tor-browser

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

PresShell.cpp (475539B)


      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 /* a presentation of a document, part 2 */
      8 
      9 #include "PresShell.h"
     10 
     11 #include <algorithm>
     12 
     13 #include "AnchorPositioningUtils.h"
     14 #include "AutoProfilerStyleMarker.h"
     15 #include "ChildIterator.h"
     16 #include "MobileViewportManager.h"
     17 #include "OverflowChangedTracker.h"
     18 #include "PLDHashTable.h"
     19 #include "PositionedEventTargeting.h"
     20 #include "PresShellWidgetListener.h"
     21 #include "ScrollSnap.h"
     22 #include "StickyScrollContainer.h"
     23 #include "Units.h"
     24 #include "VisualViewport.h"
     25 #include "XULTreeElement.h"
     26 #include "ZoomConstraintsClient.h"
     27 #include "gfxContext.h"
     28 #include "gfxPlatform.h"
     29 #include "gfxUserFontSet.h"
     30 #include "gfxUtils.h"
     31 #include "js/GCAPI.h"
     32 #include "mozilla/AccessibleCaretEventHub.h"
     33 #include "mozilla/AnimationEventDispatcher.h"
     34 #include "mozilla/Assertions.h"
     35 #include "mozilla/Attributes.h"
     36 #include "mozilla/CaretAssociationHint.h"
     37 #include "mozilla/ConnectedAncestorTracker.h"
     38 #include "mozilla/ContentIterator.h"
     39 #include "mozilla/DisplayPortUtils.h"
     40 #include "mozilla/EditorBase.h"
     41 #include "mozilla/ErrorResult.h"
     42 #include "mozilla/EventDispatcher.h"
     43 #include "mozilla/EventForwards.h"
     44 #include "mozilla/EventStateManager.h"
     45 #include "mozilla/GeckoMVMContext.h"
     46 #include "mozilla/GlobalStyleSheetCache.h"
     47 #include "mozilla/IMEStateManager.h"
     48 #include "mozilla/InputTaskManager.h"
     49 #include "mozilla/IntegerRange.h"
     50 #include "mozilla/Likely.h"
     51 #include "mozilla/Logging.h"
     52 #include "mozilla/MemoryReporting.h"
     53 #include "mozilla/MouseEvents.h"
     54 #include "mozilla/PerfStats.h"
     55 #include "mozilla/PointerLockManager.h"
     56 #include "mozilla/Preferences.h"
     57 #include "mozilla/PresShellInlines.h"
     58 #include "mozilla/ProfilerLabels.h"
     59 #include "mozilla/ProfilerMarkers.h"
     60 #include "mozilla/RangeUtils.h"
     61 #include "mozilla/RefPtr.h"
     62 #include "mozilla/RestyleManager.h"
     63 #include "mozilla/SMILAnimationController.h"
     64 #include "mozilla/SVGFragmentIdentifier.h"
     65 #include "mozilla/SVGObserverUtils.h"
     66 #include "mozilla/ScopeExit.h"
     67 #include "mozilla/ScrollContainerFrame.h"
     68 #include "mozilla/ScrollTimelineAnimationTracker.h"
     69 #include "mozilla/ScrollTypes.h"
     70 #include "mozilla/ServoBindings.h"
     71 #include "mozilla/ServoStyleSet.h"
     72 #include "mozilla/Sprintf.h"
     73 #include "mozilla/StartupTimeline.h"
     74 #include "mozilla/StaticAnalysisFunctions.h"
     75 #include "mozilla/StaticPrefs_apz.h"
     76 #include "mozilla/StaticPrefs_dom.h"
     77 #include "mozilla/StaticPrefs_font.h"
     78 #include "mozilla/StaticPrefs_image.h"
     79 #include "mozilla/StaticPrefs_layout.h"
     80 #include "mozilla/StaticPrefs_test.h"
     81 #include "mozilla/StaticPrefs_toolkit.h"
     82 #include "mozilla/StyleSheet.h"
     83 #include "mozilla/StyleSheetInlines.h"
     84 #include "mozilla/Telemetry.h"
     85 #include "mozilla/TextComposition.h"
     86 #include "mozilla/TextEvents.h"
     87 #include "mozilla/TimeStamp.h"
     88 #include "mozilla/TouchEvents.h"
     89 #include "mozilla/Try.h"
     90 #include "mozilla/UniquePtr.h"
     91 #include "mozilla/ViewportFrame.h"
     92 #include "mozilla/ViewportUtils.h"
     93 #include "mozilla/css/ImageLoader.h"
     94 #include "mozilla/dom/AncestorIterator.h"
     95 #include "mozilla/dom/AnimationTimelinesController.h"
     96 #include "mozilla/dom/BrowserBridgeChild.h"
     97 #include "mozilla/dom/BrowserChild.h"
     98 #include "mozilla/dom/BrowsingContext.h"
     99 #include "mozilla/dom/CanonicalBrowsingContext.h"
    100 #include "mozilla/dom/ContentChild.h"
    101 #include "mozilla/dom/ContentParent.h"
    102 #include "mozilla/dom/DOMIntersectionObserver.h"
    103 #include "mozilla/dom/Document.h"
    104 #include "mozilla/dom/DocumentInlines.h"
    105 #include "mozilla/dom/DocumentTimeline.h"
    106 #include "mozilla/dom/Element.h"
    107 #include "mozilla/dom/ElementBinding.h"
    108 #include "mozilla/dom/ElementInlines.h"
    109 #include "mozilla/dom/FontFaceSet.h"
    110 #include "mozilla/dom/FragmentDirective.h"
    111 #include "mozilla/dom/HTMLAreaElement.h"
    112 #include "mozilla/dom/LargestContentfulPaint.h"
    113 #include "mozilla/dom/MouseEventBinding.h"
    114 #include "mozilla/dom/Performance.h"
    115 #include "mozilla/dom/PerformanceMainThread.h"
    116 #include "mozilla/dom/PointerEventBinding.h"
    117 #include "mozilla/dom/PointerEventHandler.h"
    118 #include "mozilla/dom/PopupBlocker.h"
    119 #include "mozilla/dom/SVGAnimationElement.h"
    120 #include "mozilla/dom/ScriptSettings.h"
    121 #include "mozilla/dom/Selection.h"
    122 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
    123 #include "mozilla/dom/Touch.h"
    124 #include "mozilla/dom/TouchEvent.h"
    125 #include "mozilla/dom/UserActivation.h"
    126 #include "mozilla/gfx/2D.h"
    127 #include "mozilla/gfx/Types.h"
    128 #include "mozilla/glean/GfxMetrics.h"
    129 #include "mozilla/glean/LayoutMetrics.h"
    130 #include "mozilla/glue/Debug.h"
    131 #include "mozilla/layers/APZPublicUtils.h"
    132 #include "mozilla/layers/CompositorBridgeChild.h"
    133 #include "mozilla/layers/FocusTarget.h"
    134 #include "mozilla/layers/InputAPZContext.h"
    135 #include "mozilla/layers/ScrollingInteractionContext.h"
    136 #include "mozilla/layers/WebRenderLayerManager.h"
    137 #include "mozilla/layers/WebRenderUserData.h"
    138 #include "mozilla/layout/ScrollAnchorContainer.h"
    139 #include "nsAnimationManager.h"
    140 #include "nsAutoLayoutPhase.h"
    141 #include "nsCOMArray.h"
    142 #include "nsCOMPtr.h"
    143 #include "nsCRTGlue.h"
    144 #include "nsCSSFrameConstructor.h"
    145 #include "nsCSSRendering.h"
    146 #include "nsCanvasFrame.h"
    147 #include "nsCaret.h"
    148 #include "nsClassHashtable.h"
    149 #include "nsContainerFrame.h"
    150 #include "nsContentList.h"
    151 #include "nsDOMNavigationTiming.h"
    152 #include "nsDisplayList.h"
    153 #include "nsDocShell.h"  // for reflow observation
    154 #include "nsError.h"
    155 #include "nsFlexContainerFrame.h"
    156 #include "nsFocusManager.h"
    157 #include "nsFrameSelection.h"
    158 #include "nsGkAtoms.h"
    159 #include "nsGlobalWindowOuter.h"
    160 #include "nsHashKeys.h"
    161 #include "nsIBaseWindow.h"
    162 #include "nsIContent.h"
    163 #include "nsIDOMXULMenuListElement.h"
    164 #include "nsIDOMXULMultSelectCntrlEl.h"
    165 #include "nsIDOMXULSelectCntrlItemEl.h"
    166 #include "nsIDocShellTreeItem.h"
    167 #include "nsIDocShellTreeOwner.h"
    168 #include "nsIDragSession.h"
    169 #include "nsIFrame.h"
    170 #include "nsIFrameInlines.h"
    171 #include "nsILayoutHistoryState.h"
    172 #include "nsILineIterator.h"  // for ScrollContentIntoView
    173 #include "nsIMutationObserver.h"
    174 #include "nsIObserverService.h"
    175 #include "nsIReflowCallback.h"
    176 #include "nsIScreen.h"
    177 #include "nsIScreenManager.h"
    178 #include "nsITimer.h"
    179 #include "nsIURI.h"
    180 #include "nsImageFrame.h"
    181 #include "nsLayoutUtils.h"
    182 #include "nsMenuPopupFrame.h"
    183 #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
    184 #include "nsNetUtil.h"
    185 #include "nsPIDOMWindow.h"
    186 #include "nsPageSequenceFrame.h"
    187 #include "nsPlaceholderFrame.h"
    188 #include "nsPresContext.h"
    189 #include "nsQueryObject.h"
    190 #include "nsRange.h"
    191 #include "nsReadableUtils.h"
    192 #include "nsRefreshDriver.h"
    193 #include "nsRegion.h"
    194 #include "nsStyleChangeList.h"
    195 #include "nsStyleSheetService.h"
    196 #include "nsSubDocumentFrame.h"
    197 #include "nsTArray.h"
    198 #include "nsThreadUtils.h"
    199 #include "nsTransitionManager.h"
    200 #include "nsTreeBodyFrame.h"
    201 #include "nsTreeColumns.h"
    202 #include "nsViewportInfo.h"
    203 #include "nsWindowSizes.h"
    204 #include "nsXPCOM.h"
    205 #include "nsXULElement.h"
    206 #include "prenv.h"
    207 #include "prinrval.h"
    208 
    209 #ifdef XP_WIN
    210 #  include "winuser.h"
    211 #endif
    212 
    213 #ifdef MOZ_REFLOW_PERF
    214 #  include "nsFontMetrics.h"
    215 #endif
    216 
    217 #ifdef ACCESSIBILITY
    218 #  include "mozilla/a11y/DocAccessible.h"
    219 #  ifdef DEBUG
    220 #    include "mozilla/a11y/Logging.h"
    221 #  endif
    222 #endif
    223 
    224 // define the scalfactor of drag and drop images
    225 // relative to the max screen height/width
    226 #define RELATIVE_SCALEFACTOR 0.0925f
    227 
    228 using namespace mozilla;
    229 using namespace mozilla::css;
    230 using namespace mozilla::dom;
    231 using namespace mozilla::gfx;
    232 using namespace mozilla::layers;
    233 using namespace mozilla::gfx;
    234 using namespace mozilla::layout;
    235 using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
    236 typedef ScrollableLayerGuid::ViewID ViewID;
    237 
    238 constinit PresShell::CapturingContentInfo PresShell::sCapturingContentInfo;
    239 
    240 // RangePaintInfo is used to paint ranges to offscreen buffers
    241 struct RangePaintInfo {
    242  nsDisplayListBuilder mBuilder;
    243  nsDisplayList mList;
    244 
    245  // offset of builder's reference frame to the root frame
    246  nsPoint mRootOffset;
    247 
    248  // Resolution at which the items are normally painted. So if we're painting
    249  // these items in a range separately from the "full display list", we may want
    250  // to paint them at this resolution.
    251  float mResolution = 1.0;
    252 
    253  explicit RangePaintInfo(nsIFrame* aFrame)
    254      : mBuilder(aFrame, nsDisplayListBuilderMode::Painting, false),
    255        mList(&mBuilder) {
    256    MOZ_COUNT_CTOR(RangePaintInfo);
    257    mBuilder.BeginFrame();
    258  }
    259 
    260  ~RangePaintInfo() {
    261    mList.DeleteAll(&mBuilder);
    262    mBuilder.EndFrame();
    263    MOZ_COUNT_DTOR(RangePaintInfo);
    264  }
    265 };
    266 
    267 #undef NOISY
    268 
    269 #ifdef MOZ_REFLOW_PERF
    270 class ReflowCountMgr;
    271 
    272 static const char kGrandTotalsStr[] = "Grand Totals";
    273 
    274 // Counting Class
    275 class ReflowCounter {
    276 public:
    277  explicit ReflowCounter(ReflowCountMgr* aMgr = nullptr);
    278  ~ReflowCounter();
    279 
    280  void ClearTotals();
    281  void DisplayTotals(const char* aStr);
    282  void DisplayDiffTotals(const char* aStr);
    283  void DisplayHTMLTotals(const char* aStr);
    284 
    285  void Add() { mTotal++; }
    286  void Add(uint32_t aTotal) { mTotal += aTotal; }
    287 
    288  void CalcDiffInTotals();
    289  void SetTotalsCache();
    290 
    291  void SetMgr(ReflowCountMgr* aMgr) { mMgr = aMgr; }
    292 
    293  uint32_t GetTotal() { return mTotal; }
    294 
    295 protected:
    296  void DisplayTotals(uint32_t aTotal, const char* aTitle);
    297  void DisplayHTMLTotals(uint32_t aTotal, const char* aTitle);
    298 
    299  uint32_t mTotal;
    300  uint32_t mCacheTotal;
    301 
    302  ReflowCountMgr* mMgr;  // weak reference (don't delete)
    303 };
    304 
    305 // Counting Class
    306 class IndiReflowCounter {
    307 public:
    308  explicit IndiReflowCounter(ReflowCountMgr* aMgr = nullptr)
    309      : mFrame(nullptr),
    310        mCount(0),
    311        mMgr(aMgr),
    312        mCounter(aMgr),
    313        mHasBeenOutput(false) {}
    314  virtual ~IndiReflowCounter() = default;
    315 
    316  nsAutoString mName;
    317  nsIFrame* mFrame;  // weak reference (don't delete)
    318  int32_t mCount;
    319 
    320  ReflowCountMgr* mMgr;  // weak reference (don't delete)
    321 
    322  ReflowCounter mCounter;
    323  bool mHasBeenOutput;
    324 };
    325 
    326 //--------------------
    327 // Manager Class
    328 //--------------------
    329 class ReflowCountMgr {
    330 public:
    331  ReflowCountMgr();
    332  virtual ~ReflowCountMgr();
    333 
    334  void ClearTotals();
    335  void ClearGrandTotals();
    336  void DisplayTotals(const char* aStr);
    337  void DisplayHTMLTotals(const char* aStr);
    338  void DisplayDiffsInTotals();
    339 
    340  void Add(const char* aName, nsIFrame* aFrame);
    341  ReflowCounter* LookUp(const char* aName);
    342 
    343  void PaintCount(const char* aName, gfxContext* aRenderingContext,
    344                  nsPresContext* aPresContext, nsIFrame* aFrame,
    345                  const nsPoint& aOffset, uint32_t aColor);
    346 
    347  FILE* GetOutFile() { return mFD; }
    348 
    349  void SetPresContext(nsPresContext* aPresContext) {
    350    mPresContext = aPresContext;  // weak reference
    351  }
    352  void SetPresShell(PresShell* aPresShell) {
    353    mPresShell = aPresShell;  // weak reference
    354  }
    355 
    356  void SetDumpFrameCounts(bool aVal) { mDumpFrameCounts = aVal; }
    357  void SetDumpFrameByFrameCounts(bool aVal) { mDumpFrameByFrameCounts = aVal; }
    358  void SetPaintFrameCounts(bool aVal) { mPaintFrameByFrameCounts = aVal; }
    359 
    360  bool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
    361 
    362 protected:
    363  void DisplayTotals(uint32_t aTotal, uint32_t* aDupArray, char* aTitle);
    364  void DisplayHTMLTotals(uint32_t aTotal, uint32_t* aDupArray, char* aTitle);
    365 
    366  void DoGrandTotals();
    367  void DoIndiTotalsTree();
    368 
    369  // HTML Output Methods
    370  void DoGrandHTMLTotals();
    371 
    372  nsClassHashtable<nsCharPtrHashKey, ReflowCounter> mCounts;
    373  nsClassHashtable<nsCharPtrHashKey, IndiReflowCounter> mIndiFrameCounts;
    374  FILE* mFD;
    375 
    376  bool mDumpFrameCounts;
    377  bool mDumpFrameByFrameCounts;
    378  bool mPaintFrameByFrameCounts;
    379 
    380  bool mCycledOnce;
    381 
    382  // Root Frame for Individual Tracking
    383  nsPresContext* mPresContext;
    384  PresShell* mPresShell;
    385 
    386  // ReflowCountMgr gReflowCountMgr;
    387 };
    388 #endif
    389 //========================================================================
    390 
    391 // comment out to hide caret
    392 #define SHOW_CARET
    393 
    394 // The upper bound on the amount of time to spend reflowing, in
    395 // microseconds.  When this bound is exceeded and reflow commands are
    396 // still queued up, a reflow event is posted.  The idea is for reflow
    397 // to not hog the processor beyond the time specifed in
    398 // gMaxRCProcessingTime.  This data member is initialized from the
    399 // layout.reflow.timeslice pref.
    400 #define NS_MAX_REFLOW_TIME 1000000
    401 static int32_t gMaxRCProcessingTime = -1;
    402 
    403 struct nsCallbackEventRequest {
    404  nsIReflowCallback* callback;
    405  nsCallbackEventRequest* next;
    406 };
    407 
    408 // ----------------------------------------------------------------------------
    409 
    410 class nsAutoCauseReflowNotifier {
    411 public:
    412  MOZ_CAN_RUN_SCRIPT explicit nsAutoCauseReflowNotifier(PresShell* aPresShell)
    413      : mPresShell(aPresShell) {
    414    mPresShell->WillCauseReflow();
    415  }
    416  MOZ_CAN_RUN_SCRIPT ~nsAutoCauseReflowNotifier() {
    417    // This check should not be needed. Currently the only place that seem
    418    // to need it is the code that deals with bug 337586.
    419    if (!mPresShell->mHaveShutDown) {
    420      RefPtr<PresShell> presShell(mPresShell);
    421      presShell->DidCauseReflow();
    422    } else {
    423      nsContentUtils::RemoveScriptBlocker();
    424    }
    425  }
    426 
    427  PresShell* mPresShell;
    428 };
    429 
    430 class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback {
    431 public:
    432  explicit nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
    433 
    434  MOZ_CAN_RUN_SCRIPT
    435  virtual void HandleEvent(EventChainPostVisitor& aVisitor) override {
    436    if (aVisitor.mPresContext && aVisitor.mEvent->mClass != eBasicEventClass) {
    437      if (aVisitor.mEvent->mMessage == eMouseDown ||
    438          aVisitor.mEvent->mMessage == eMouseUp) {
    439        // Mouse-up and mouse-down events call nsIFrame::HandlePress/Release
    440        // which call GetContentOffsetsFromPoint which requires up-to-date
    441        // layout. Bring layout up-to-date now so that GetCurrentEventFrame()
    442        // below will return a real frame and we don't have to worry about
    443        // destroying it by flushing later.
    444        MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Layout);
    445      } else if (aVisitor.mEvent->mMessage == eWheel &&
    446                 aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
    447        nsIFrame* frame = mPresShell->GetCurrentEventFrame();
    448        if (frame) {
    449          // chrome (including addons) should be able to know if content
    450          // handles both D3E "wheel" event and legacy mouse scroll events.
    451          // We should dispatch legacy mouse events before dispatching the
    452          // "wheel" event into system group.
    453          RefPtr<EventStateManager> esm =
    454              aVisitor.mPresContext->EventStateManager();
    455          esm->DispatchLegacyMouseScrollEvents(
    456              frame, aVisitor.mEvent->AsWheelEvent(), &aVisitor.mEventStatus);
    457        }
    458      }
    459      nsIFrame* frame = mPresShell->GetCurrentEventFrame();
    460      if (!frame && (aVisitor.mEvent->mMessage == eMouseUp ||
    461                     aVisitor.mEvent->mMessage == eTouchEnd)) {
    462        // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
    463        // that capturing is released.
    464        frame = mPresShell->GetRootFrame();
    465      }
    466      if (frame) {
    467        frame->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent->AsGUIEvent(),
    468                           &aVisitor.mEventStatus);
    469      }
    470    }
    471  }
    472 
    473  RefPtr<PresShell> mPresShell;
    474 };
    475 
    476 class nsBeforeFirstPaintDispatcher : public Runnable {
    477 public:
    478  explicit nsBeforeFirstPaintDispatcher(Document* aDocument)
    479      : mozilla::Runnable("nsBeforeFirstPaintDispatcher"),
    480        mDocument(aDocument) {}
    481 
    482  // Fires the "before-first-paint" event so that interested parties (right now,
    483  // the mobile browser) are aware of it.
    484  NS_IMETHOD Run() override {
    485    nsCOMPtr<nsIObserverService> observerService =
    486        mozilla::services::GetObserverService();
    487    if (observerService) {
    488      observerService->NotifyObservers(ToSupports(mDocument),
    489                                       "before-first-paint", nullptr);
    490    }
    491    return NS_OK;
    492  }
    493 
    494 private:
    495  RefPtr<Document> mDocument;
    496 };
    497 
    498 // This is a helper class to track whether the targeted frame is destroyed after
    499 // dispatching pointer events. In that case, we need the original targeted
    500 // content so that we can dispatch the mouse events to it.
    501 class MOZ_RAII AutoPointerEventTargetUpdater final {
    502 public:
    503  AutoPointerEventTargetUpdater(PresShell* aShell, WidgetEvent* aEvent,
    504                                nsIFrame* aFrame, nsIContent* aTargetContent,
    505                                nsIContent** aOutTargetContent) {
    506    MOZ_ASSERT(aEvent);
    507    if (!aOutTargetContent || aEvent->mClass != ePointerEventClass) {
    508      // Make the destructor happy.
    509      mOutTargetContent = nullptr;
    510      return;
    511    }
    512    MOZ_ASSERT(aShell);
    513    MOZ_ASSERT_IF(aFrame && aFrame->GetContent(),
    514                  aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
    515 
    516    mShell = aShell;
    517    mWeakFrame = aFrame;
    518    mOutTargetContent = aOutTargetContent;
    519    mFromTouch = aEvent->AsPointerEvent()->mFromTouchEvent;
    520    // Touch event target may have no frame, e.g., removed from the DOM
    521    MOZ_ASSERT_IF(!mFromTouch, aFrame);
    522    // The frame may be a text frame, but the event target should be an element
    523    // node.  Therefore, refer aTargetContent first, then, if we have only a
    524    // frame, we should use inclusive ancestor of the content.
    525    mOriginalPointerEventTarget = [&]() -> nsIContent* {
    526      nsIContent* const target =
    527          aTargetContent ? aTargetContent
    528                         : (aFrame ? aFrame->GetContent() : nullptr);
    529      if (MOZ_UNLIKELY(!target)) {
    530        return nullptr;
    531      }
    532      if (target->IsElement() ||
    533          !IsForbiddenDispatchingToNonElementContent(aEvent->mMessage)) {
    534        return target;
    535      }
    536      return target->GetInclusiveFlattenedTreeAncestorElement();
    537    }();
    538    if (mOriginalPointerEventTarget &&
    539        mOriginalPointerEventTarget->IsInComposedDoc()) {
    540      mPointerEventTargetTracker.emplace(*mOriginalPointerEventTarget);
    541    }
    542  }
    543 
    544  ~AutoPointerEventTargetUpdater() {
    545    if (!mOutTargetContent || !mShell || mWeakFrame.IsAlive()) {
    546      return;
    547    }
    548    if (mFromTouch) {
    549      // If the source event is a touch event, the touch event target should
    550      // always be same target as preceding ePointerDown.  Therefore, we should
    551      // always set it back to the original event target.
    552      mOriginalPointerEventTarget.swap(*mOutTargetContent);
    553    } else {
    554      // If the source event is not a touch event (must be a mouse event in
    555      // this case), the event should be fired on the closest inclusive ancestor
    556      // of the pointer event target which is still connected.  The mutations
    557      // are tracked by PresShell::ContentRemoved.  Therefore, we should set it.
    558      if (!mPointerEventTargetTracker ||
    559          !mPointerEventTargetTracker->ContentWasRemoved()) {
    560        mOriginalPointerEventTarget.swap(*mOutTargetContent);
    561      } else {
    562        nsCOMPtr<nsIContent> connectedAncestor =
    563            mPointerEventTargetTracker->GetConnectedContent();
    564        connectedAncestor.swap(*mOutTargetContent);
    565      }
    566    }
    567  }
    568 
    569 private:
    570  RefPtr<PresShell> mShell;
    571  nsCOMPtr<nsIContent> mOriginalPointerEventTarget;
    572  AutoWeakFrame mWeakFrame;
    573  Maybe<AutoConnectedAncestorTracker> mPointerEventTargetTracker;
    574  nsIContent** mOutTargetContent;
    575  bool mFromTouch = false;
    576 };
    577 
    578 bool PresShell::sDisableNonTestMouseEvents = false;
    579 
    580 LazyLogModule PresShell::gLog("PresShell");
    581 
    582 TimeStamp PresShell::EventHandler::sLastInputCreated;
    583 TimeStamp PresShell::EventHandler::sLastInputProcessed;
    584 StaticRefPtr<Element> PresShell::EventHandler::sLastKeyDownEventTargetElement;
    585 
    586 bool PresShell::sProcessInteractable = false;
    587 
    588 Modifiers PresShell::sCurrentModifiers = MODIFIER_NONE;
    589 
    590 void PresShell::AddAutoWeakFrame(AutoWeakFrame* aWeakFrame) {
    591  if (aWeakFrame->GetFrame()) {
    592    aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
    593  }
    594  aWeakFrame->SetPreviousWeakFrame(mAutoWeakFrames);
    595  mAutoWeakFrames = aWeakFrame;
    596 }
    597 
    598 void PresShell::AddWeakFrame(WeakFrame* aWeakFrame) {
    599  if (aWeakFrame->GetFrame()) {
    600    aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
    601  }
    602  MOZ_ASSERT(!mWeakFrames.Contains(aWeakFrame));
    603  mWeakFrames.Insert(aWeakFrame);
    604 }
    605 
    606 void PresShell::AddConnectedAncestorTracker(
    607    AutoConnectedAncestorTracker& aTracker) {
    608  aTracker.mPreviousTracker = mLastConnectedAncestorTracker;
    609  mLastConnectedAncestorTracker = &aTracker;
    610 }
    611 
    612 void PresShell::RemoveAutoWeakFrame(AutoWeakFrame* aWeakFrame) {
    613  if (mAutoWeakFrames == aWeakFrame) {
    614    mAutoWeakFrames = aWeakFrame->GetPreviousWeakFrame();
    615    return;
    616  }
    617  AutoWeakFrame* nextWeak = mAutoWeakFrames;
    618  while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
    619    nextWeak = nextWeak->GetPreviousWeakFrame();
    620  }
    621  if (nextWeak) {
    622    nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
    623  }
    624 }
    625 
    626 void PresShell::RemoveWeakFrame(WeakFrame* aWeakFrame) {
    627  MOZ_ASSERT(mWeakFrames.Contains(aWeakFrame));
    628  mWeakFrames.Remove(aWeakFrame);
    629 }
    630 
    631 void PresShell::RemoveConnectedAncestorTracker(
    632    const AutoConnectedAncestorTracker& aTracker) {
    633  if (mLastConnectedAncestorTracker == &aTracker) {
    634    mLastConnectedAncestorTracker = aTracker.mPreviousTracker;
    635    return;
    636  }
    637  AutoConnectedAncestorTracker* nextTracker = mLastConnectedAncestorTracker;
    638  while (nextTracker && nextTracker->mPreviousTracker != &aTracker) {
    639    nextTracker = nextTracker->mPreviousTracker;
    640  }
    641  if (nextTracker) {
    642    nextTracker->mPreviousTracker = aTracker.mPreviousTracker;
    643  }
    644 }
    645 
    646 already_AddRefed<nsFrameSelection> PresShell::FrameSelection() {
    647  RefPtr<nsFrameSelection> ret = mSelection;
    648  return ret.forget();
    649 }
    650 
    651 //----------------------------------------------------------------------
    652 
    653 static uint32_t sNextPresShellId = 0;
    654 
    655 /* static */
    656 bool PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell) {
    657  // If the pref forces it on, then enable it.
    658  if (StaticPrefs::layout_accessiblecaret_enabled()) {
    659    return true;
    660  }
    661  // If the touch pref is on, and touch events are enabled (this depends
    662  // on the specific device running), then enable it.
    663  if (StaticPrefs::layout_accessiblecaret_enabled_on_touch() &&
    664      dom::TouchEvent::PrefEnabled(aDocShell)) {
    665    return true;
    666  }
    667  // Otherwise, disabled.
    668  return false;
    669 }
    670 
    671 PresShell::PresShell(Document* aDocument)
    672    : mDocument(aDocument),
    673      mLastSelectionForToString(nullptr),
    674 #ifdef ACCESSIBILITY
    675      mDocAccessible(nullptr),
    676 #endif  // ACCESSIBILITY
    677      mLastResolutionChangeOrigin(ResolutionChangeOrigin::Apz),
    678      mPaintCount(0),
    679      mAPZFocusSequenceNumber(0),
    680      mActiveSuppressDisplayport(0),
    681      mPresShellId(++sNextPresShellId),
    682      mFontSizeInflationEmPerLine(0),
    683      mFontSizeInflationMinTwips(0),
    684      mFontSizeInflationLineThreshold(0),
    685      mSelectionFlags(nsISelectionDisplay::DISPLAY_TEXT |
    686                      nsISelectionDisplay::DISPLAY_IMAGES),
    687      mChangeNestCount(0),
    688      mRenderingStateFlags(RenderingStateFlags::None),
    689      mCaretEnabled(false),
    690      mNeedLayoutFlush(true),
    691      mNeedStyleFlush(true),
    692      mNeedThrottledAnimationFlush(true),
    693      mVisualViewportSizeSet(false),
    694      mDidInitialize(false),
    695      mIsDestroying(false),
    696      mIsReflowing(false),
    697      mIsObservingDocument(false),
    698      mForbiddenToFlush(false),
    699      mIsDocumentGone(false),
    700      mHaveShutDown(false),
    701      mPaintingSuppressed(false),
    702      mShouldUnsuppressPainting(false),
    703      mIgnoreFrameDestruction(false),
    704      mIsActive(true),
    705      mFrozen(false),
    706      mIsFirstPaint(true),
    707      mObservesMutationsForPrint(false),
    708      mWasLastReflowInterrupted(false),
    709      mResizeEventPending(false),
    710      mVisualViewportResizeEventPending(false),
    711      mFontSizeInflationForceEnabled(false),
    712      mFontSizeInflationEnabled(false),
    713      mIsNeverPainting(false),
    714      mResolutionUpdated(false),
    715      mResolutionUpdatedByApz(false),
    716      mUnderHiddenEmbedderElement(false),
    717      mDocumentLoading(false),
    718      mNoDelayedMouseEvents(false),
    719      mNoDelayedKeyEvents(false),
    720      mNoDelayedSingleTap(false),
    721      mApproximateFrameVisibilityVisited(false),
    722      mIsLastChromeOnlyEscapeKeyConsumed(false),
    723      mHasReceivedPaintMessage(false),
    724      mIsLastKeyDownCanceled(false),
    725      mHasHandledUserInput(false),
    726      mForceDispatchKeyPressEventsForNonPrintableKeys(false),
    727      mForceUseLegacyKeyCodeAndCharCodeValues(false),
    728      mInitializedWithKeyPressEventDispatchingBlacklist(false),
    729      mHasTriedFastUnsuppress(false),
    730      mProcessingReflowCommands(false),
    731      mPendingDidDoReflow(false),
    732      mHasSeenAnchorPos(false) {
    733  MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::PresShell this=%p", this));
    734  MOZ_ASSERT(aDocument);
    735 
    736 #ifdef MOZ_REFLOW_PERF
    737  mReflowCountMgr = MakeUnique<ReflowCountMgr>();
    738  mReflowCountMgr->SetPresContext(mPresContext);
    739  mReflowCountMgr->SetPresShell(this);
    740 #endif
    741  mLastOSWake = mLoadBegin = TimeStamp::Now();
    742 }
    743 
    744 NS_INTERFACE_TABLE_HEAD(PresShell)
    745  NS_INTERFACE_TABLE_BEGIN
    746    // In most cases, PresShell should be treated as concrete class, but need to
    747    // QI for weak reference.  Therefore, the case needed by do_QueryReferent()
    748    // should be tested first.
    749    NS_INTERFACE_TABLE_ENTRY(PresShell, PresShell)
    750    NS_INTERFACE_TABLE_ENTRY(PresShell, nsIDocumentObserver)
    751    NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionController)
    752    NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionDisplay)
    753    NS_INTERFACE_TABLE_ENTRY(PresShell, nsIObserver)
    754    NS_INTERFACE_TABLE_ENTRY(PresShell, nsISupportsWeakReference)
    755    NS_INTERFACE_TABLE_ENTRY(PresShell, nsIMutationObserver)
    756    NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(PresShell, nsISupports, nsIObserver)
    757  NS_INTERFACE_TABLE_END
    758  NS_INTERFACE_TABLE_TO_MAP_SEGUE
    759 NS_INTERFACE_MAP_END
    760 
    761 NS_IMPL_ADDREF(PresShell)
    762 NS_IMPL_RELEASE(PresShell)
    763 
    764 PresShell::~PresShell() {
    765  MOZ_RELEASE_ASSERT(!mForbiddenToFlush,
    766                     "Flag should only be set temporarily, while doing things "
    767                     "that shouldn't cause destruction");
    768  MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::~PresShell this=%p", this));
    769 
    770  if (!mHaveShutDown) {
    771    MOZ_ASSERT_UNREACHABLE("Someone did not call PresShell::Destroy()");
    772    Destroy();
    773  }
    774 
    775  NS_ASSERTION(mCurrentEventTargetStack.IsEmpty(),
    776               "Huh, event content left on the stack in pres shell dtor!");
    777  NS_ASSERTION(mFirstCallbackEventRequest == nullptr &&
    778                   mLastCallbackEventRequest == nullptr,
    779               "post-reflow queues not empty.  This means we're leaking");
    780 
    781  MOZ_ASSERT(!mAllocatedPointers || mAllocatedPointers->IsEmpty(),
    782             "Some pres arena objects were not freed");
    783 
    784  mFrameConstructor = nullptr;
    785 }
    786 
    787 /**
    788 * Initialize the presentation shell.
    789 * Note this can't be merged into our constructor because caret initialization
    790 * calls AddRef() on us.
    791 */
    792 void PresShell::Init(nsPresContext* aPresContext) {
    793  MOZ_ASSERT(mDocument);
    794  MOZ_ASSERT(aPresContext);
    795  MOZ_ASSERT(!mWidgetListener, "Already initialized");
    796 
    797  mWidgetListener = MakeUnique<PresShellWidgetListener>(this);
    798 
    799  // mDocument is now set.  It might have a display document whose "need layout/
    800  // style" flush flags are not set, but ours will be set.  To keep these
    801  // consistent, call the flag setting functions to propagate those flags up
    802  // to the display document.
    803  SetNeedLayoutFlush();
    804  SetNeedStyleFlush();
    805 
    806  // Create our frame constructor.
    807  mFrameConstructor = MakeUnique<nsCSSFrameConstructor>(mDocument, this);
    808 
    809  // Bind the context to the presentation shell.
    810  // FYI: We cannot initialize mPresContext in the constructor because we
    811  //      cannot call AttachPresShell() in it and once we initialize
    812  //      mPresContext, other objects may refer refresh driver or restyle
    813  //      manager via mPresContext and that causes hitting MOZ_ASSERT in some
    814  //      places.  Therefore, we should initialize mPresContext here with
    815  //      const_cast hack since we want to guarantee that mPresContext lives
    816  //      as long as the PresShell.
    817  const_cast<RefPtr<nsPresContext>&>(mPresContext) = aPresContext;
    818  mPresContext->AttachPresShell(this);
    819 
    820  mPresContext->InitFontCache();
    821 
    822  // FIXME(emilio, bug 1544185): Some Android code somehow depends on the shell
    823  // being eagerly registered as a style flush observer. This shouldn't be
    824  // needed otherwise.
    825  EnsureStyleFlush();
    826 
    827  const bool accessibleCaretEnabled =
    828      AccessibleCaretEnabled(mDocument->GetDocShell());
    829  if (accessibleCaretEnabled) {
    830    // Need to happen before nsFrameSelection has been set up.
    831    mAccessibleCaretEventHub = new AccessibleCaretEventHub(this);
    832    mAccessibleCaretEventHub->Init();
    833  }
    834 
    835  mSelection = new nsFrameSelection(this, accessibleCaretEnabled);
    836 
    837  // Important: this has to happen after the selection has been set up
    838 #ifdef SHOW_CARET
    839  // make the caret
    840  mCaret = new nsCaret();
    841  mCaret->Init(this);
    842  mOriginalCaret = mCaret;
    843 
    844  // SetCaretEnabled(true);       // make it show in browser windows
    845 #endif
    846  // set up selection to be displayed in document
    847  // Don't enable selection for print media
    848  nsPresContext::nsPresContextType type = mPresContext->Type();
    849  if (type != nsPresContext::eContext_PrintPreview &&
    850      type != nsPresContext::eContext_Print) {
    851    SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
    852  }
    853 
    854  if (gMaxRCProcessingTime == -1) {
    855    gMaxRCProcessingTime =
    856        Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME);
    857  }
    858 
    859  if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
    860    ss->RegisterPresShell(this);
    861  }
    862 
    863  {
    864    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    865    if (os) {
    866      os->AddObserver(this, "memory-pressure", false);
    867      os->AddObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC, false);
    868      if (XRE_IsParentProcess() && !sProcessInteractable) {
    869        os->AddObserver(this, "sessionstore-one-or-no-tab-restored", false);
    870      }
    871      os->AddObserver(this, "font-info-updated", false);
    872      os->AddObserver(this, "internal-look-and-feel-changed", false);
    873    }
    874  }
    875 
    876 #ifdef MOZ_REFLOW_PERF
    877  if (mReflowCountMgr) {
    878    bool paintFrameCounts =
    879        Preferences::GetBool("layout.reflow.showframecounts");
    880 
    881    bool dumpFrameCounts =
    882        Preferences::GetBool("layout.reflow.dumpframecounts");
    883 
    884    bool dumpFrameByFrameCounts =
    885        Preferences::GetBool("layout.reflow.dumpframebyframecounts");
    886 
    887    mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
    888    mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
    889    mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
    890  }
    891 #endif
    892 
    893  mDocument->TimelinesController().UpdateLastRefreshDriverTime();
    894 
    895  // Get our activeness from the docShell.
    896  ActivenessMaybeChanged();
    897 
    898  // Setup our font inflation preferences.
    899  mFontSizeInflationEmPerLine = StaticPrefs::font_size_inflation_emPerLine();
    900  mFontSizeInflationMinTwips = StaticPrefs::font_size_inflation_minTwips();
    901  mFontSizeInflationLineThreshold =
    902      StaticPrefs::font_size_inflation_lineThreshold();
    903  mFontSizeInflationForceEnabled =
    904      StaticPrefs::font_size_inflation_forceEnabled();
    905  // We'll compute the font size inflation state in Initialize(), when we know
    906  // the document type.
    907 
    908  mTouchManager.Init(this, mDocument);
    909 
    910  if (mPresContext->IsRootContentDocumentCrossProcess()) {
    911    mZoomConstraintsClient = new ZoomConstraintsClient();
    912    mZoomConstraintsClient->Init(this, mDocument);
    913 
    914    // We call this to create mMobileViewportManager, if it is needed.
    915    MaybeRecreateMobileViewportManager(false);
    916  }
    917 
    918  if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
    919    if (BrowsingContext* bc = docShell->GetBrowsingContext()) {
    920      mUnderHiddenEmbedderElement = bc->IsUnderHiddenEmbedderElement();
    921    }
    922  }
    923 }
    924 
    925 enum TextPerfLogType { eLog_reflow, eLog_loaddone, eLog_totals };
    926 
    927 static void LogTextPerfStats(gfxTextPerfMetrics* aTextPerf,
    928                             PresShell* aPresShell,
    929                             const gfxTextPerfMetrics::TextCounts& aCounts,
    930                             float aTime, TextPerfLogType aLogType,
    931                             const char* aURL) {
    932  LogModule* tpLog = gfxPlatform::GetLog(eGfxLog_textperf);
    933 
    934  // ignore XUL contexts unless at debug level
    935  mozilla::LogLevel logLevel = LogLevel::Warning;
    936  if (aCounts.numContentTextRuns == 0) {
    937    logLevel = LogLevel::Debug;
    938  }
    939 
    940  if (!MOZ_LOG_TEST(tpLog, logLevel)) {
    941    return;
    942  }
    943 
    944  char prefix[256];
    945 
    946  switch (aLogType) {
    947    case eLog_reflow:
    948      SprintfLiteral(prefix, "(textperf-reflow) %p time-ms: %7.0f", aPresShell,
    949                     aTime);
    950      break;
    951    case eLog_loaddone:
    952      SprintfLiteral(prefix, "(textperf-loaddone) %p time-ms: %7.0f",
    953                     aPresShell, aTime);
    954      break;
    955    default:
    956      MOZ_ASSERT(aLogType == eLog_totals, "unknown textperf log type");
    957      SprintfLiteral(prefix, "(textperf-totals) %p", aPresShell);
    958  }
    959 
    960  double hitRatio = 0.0;
    961  uint32_t lookups = aCounts.wordCacheHit + aCounts.wordCacheMiss;
    962  if (lookups) {
    963    hitRatio = double(aCounts.wordCacheHit) / double(lookups);
    964  }
    965 
    966  if (aLogType == eLog_loaddone) {
    967    MOZ_LOG(
    968        tpLog, logLevel,
    969        ("%s reflow: %d chars: %d "
    970         "[%s] "
    971         "content-textruns: %d chrome-textruns: %d "
    972         "max-textrun-len: %d "
    973         "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
    974         "word-cache-space: %d word-cache-long: %d "
    975         "pref-fallbacks: %d system-fallbacks: %d "
    976         "textruns-const: %d textruns-destr: %d "
    977         "generic-lookups: %d "
    978         "cumulative-textruns-destr: %d\n",
    979         prefix, aTextPerf->reflowCount, aCounts.numChars, (aURL ? aURL : ""),
    980         aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
    981         aCounts.maxTextRunLen, lookups, hitRatio, aCounts.wordCacheSpaceRules,
    982         aCounts.wordCacheLong, aCounts.fallbackPrefs, aCounts.fallbackSystem,
    983         aCounts.textrunConst, aCounts.textrunDestr, aCounts.genericLookups,
    984         aTextPerf->cumulative.textrunDestr));
    985  } else {
    986    MOZ_LOG(
    987        tpLog, logLevel,
    988        ("%s reflow: %d chars: %d "
    989         "content-textruns: %d chrome-textruns: %d "
    990         "max-textrun-len: %d "
    991         "word-cache-lookups: %d word-cache-hit-ratio: %4.3f "
    992         "word-cache-space: %d word-cache-long: %d "
    993         "pref-fallbacks: %d system-fallbacks: %d "
    994         "textruns-const: %d textruns-destr: %d "
    995         "generic-lookups: %d "
    996         "cumulative-textruns-destr: %d\n",
    997         prefix, aTextPerf->reflowCount, aCounts.numChars,
    998         aCounts.numContentTextRuns, aCounts.numChromeTextRuns,
    999         aCounts.maxTextRunLen, lookups, hitRatio, aCounts.wordCacheSpaceRules,
   1000         aCounts.wordCacheLong, aCounts.fallbackPrefs, aCounts.fallbackSystem,
   1001         aCounts.textrunConst, aCounts.textrunDestr, aCounts.genericLookups,
   1002         aTextPerf->cumulative.textrunDestr));
   1003  }
   1004 }
   1005 
   1006 bool PresShell::InRDMPane() {
   1007  if (Document* doc = GetDocument()) {
   1008    if (BrowsingContext* bc = doc->GetBrowsingContext()) {
   1009      return bc->InRDMPane();
   1010    }
   1011  }
   1012  return false;
   1013 }
   1014 
   1015 #if defined(MOZ_WIDGET_ANDROID)
   1016 void PresShell::MaybeNotifyShowDynamicToolbar() {
   1017  const DynamicToolbarState dynToolbarState = GetDynamicToolbarState();
   1018  if ((dynToolbarState == DynamicToolbarState::Collapsed ||
   1019       dynToolbarState == DynamicToolbarState::InTransition)) {
   1020    MOZ_ASSERT(mPresContext &&
   1021               mPresContext->IsRootContentDocumentCrossProcess());
   1022    if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
   1023      browserChild->SendShowDynamicToolbar();
   1024    }
   1025  }
   1026 }
   1027 #endif  // defined(MOZ_WIDGET_ANDROID)
   1028 
   1029 void PresShell::Destroy() {
   1030  // Do not add code before this line please!
   1031  if (mHaveShutDown) {
   1032    return;
   1033  }
   1034 
   1035  NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
   1036               "destroy called on presshell while scripts not blocked");
   1037 
   1038  nsIURI* uri = mDocument->GetDocumentURI();
   1039  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS(
   1040      "Layout tree destruction", LAYOUT_Destroy,
   1041      uri ? uri->GetSpecOrDefault() : "N/A"_ns);
   1042 
   1043  // Try to determine if the page is the user had a meaningful opportunity to
   1044  // zoom this page. This is not 100% accurate but should be "good enough" for
   1045  // telemetry purposes.
   1046  auto isUserZoomablePage = [&]() -> bool {
   1047    if (mIsFirstPaint) {
   1048      // Page was never painted, so it wasn't zoomable by the user. We get a
   1049      // handful of these "transient" presShells.
   1050      return false;
   1051    }
   1052    if (!mPresContext->IsRootContentDocumentCrossProcess()) {
   1053      // Not a root content document, so APZ doesn't support zooming it.
   1054      return false;
   1055    }
   1056    if (InRDMPane()) {
   1057      // Responsive design mode is a special case that we want to ignore here.
   1058      return false;
   1059    }
   1060    if (mDocument && mDocument->IsInitialDocument()) {
   1061      // Ignore initial about:blank page loads
   1062      return false;
   1063    }
   1064    if (XRE_IsContentProcess() &&
   1065        IsExtensionRemoteType(ContentChild::GetSingleton()->GetRemoteType())) {
   1066      // Also omit presShells from the extension process because they sometimes
   1067      // can't be zoomed by the user.
   1068      return false;
   1069    }
   1070    // Otherwise assume the page is user-zoomable.
   1071    return true;
   1072  };
   1073  if (isUserZoomablePage()) {
   1074    glean::apz_zoom::activity
   1075        .EnumGet(static_cast<glean::apz_zoom::ActivityLabel>(
   1076            IsResolutionUpdatedByApz()))
   1077        .Add();
   1078  }
   1079 
   1080  // dump out cumulative text perf metrics
   1081  gfxTextPerfMetrics* tp;
   1082  if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) {
   1083    tp->Accumulate();
   1084    if (tp->cumulative.numChars > 0) {
   1085      LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr);
   1086    }
   1087  }
   1088  if (mPresContext) {
   1089    if (gfxUserFontSet* fs = mPresContext->GetUserFontSet()) {
   1090      uint32_t fontCount;
   1091      uint64_t fontSize;
   1092      fs->GetLoadStatistics(fontCount, fontSize);
   1093      glean::webfont::per_page.Add(fontCount);
   1094      glean::webfont::size_per_page.Accumulate(uint32_t(fontSize / 1024));
   1095    } else {
   1096      glean::webfont::per_page.Add(0);
   1097      glean::webfont::size_per_page.Accumulate(0);
   1098    }
   1099  }
   1100 
   1101 #ifdef MOZ_REFLOW_PERF
   1102  DumpReflows();
   1103  mReflowCountMgr = nullptr;
   1104 #endif
   1105 
   1106  if (mZoomConstraintsClient) {
   1107    mZoomConstraintsClient->Destroy();
   1108    mZoomConstraintsClient = nullptr;
   1109  }
   1110  if (mMobileViewportManager) {
   1111    mMobileViewportManager->Destroy();
   1112    mMobileViewportManager = nullptr;
   1113    mMVMContext = nullptr;
   1114  }
   1115 
   1116  MaybeReleaseCapturingContent();
   1117 
   1118  EventHandler::OnPresShellDestroy(mDocument);
   1119 
   1120  if (mContentToScrollTo) {
   1121    mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
   1122    mContentToScrollTo = nullptr;
   1123  }
   1124 
   1125  if (mPresContext) {
   1126    // We need to notify the destroying the nsPresContext to ESM for
   1127    // suppressing to use from ESM.
   1128    mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
   1129  }
   1130 
   1131  if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
   1132    ss->UnregisterPresShell(this);
   1133  }
   1134 
   1135  {
   1136    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1137    if (os) {
   1138      os->RemoveObserver(this, "memory-pressure");
   1139      os->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC);
   1140      if (XRE_IsParentProcess()) {
   1141        os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
   1142      }
   1143      os->RemoveObserver(this, "font-info-updated");
   1144      os->RemoveObserver(this, "internal-look-and-feel-changed");
   1145    }
   1146  }
   1147 
   1148  // If our paint suppression timer is still active, kill it.
   1149  CancelPaintSuppressionTimer();
   1150 
   1151  mSynthMouseMoveEvent.Revoke();
   1152 
   1153  mUpdateApproximateFrameVisibilityEvent.Revoke();
   1154 
   1155  ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages));
   1156 
   1157  if (mOriginalCaret) {
   1158    mOriginalCaret->Terminate();
   1159  }
   1160  if (mCaret && mCaret != mOriginalCaret) {
   1161    mCaret->Terminate();
   1162  }
   1163  mCaret = mOriginalCaret = nullptr;
   1164 
   1165  mFocusedFrameSelection = nullptr;
   1166 
   1167  if (mSelection) {
   1168    RefPtr<nsFrameSelection> frameSelection = mSelection;
   1169    frameSelection->DisconnectFromPresShell();
   1170  }
   1171 
   1172  mIsDestroying = true;
   1173 
   1174 #ifdef ACCESSIBILITY
   1175  if (mDocAccessible) {
   1176 #  ifdef DEBUG
   1177    if (a11y::logging::IsEnabled(a11y::logging::eDocDestroy))
   1178      a11y::logging::DocDestroy("presshell destroyed", mDocument);
   1179 #  endif
   1180 
   1181    mDocAccessible->Shutdown();
   1182    mDocAccessible = nullptr;
   1183  }
   1184 #endif  // ACCESSIBILITY
   1185 
   1186  // We can't release all the event content in
   1187  // mCurrentEventContentStack here since there might be code on the
   1188  // stack that will release the event content too. Double release
   1189  // bad!
   1190 
   1191  // The frames will be torn down, so remove them from the current
   1192  // event frame stack (since they'd be dangling references if we'd
   1193  // leave them in) and null out the mCurrentEventFrame pointer as
   1194  // well.
   1195 
   1196  mCurrentEventTarget.ClearFrame();
   1197 
   1198  for (EventTargetInfo& eventTargetInfo : mCurrentEventTargetStack) {
   1199    eventTargetInfo.ClearFrame();
   1200  }
   1201 
   1202  mFramesToDirty.Clear();
   1203  mPendingScrollAnchorSelection.Clear();
   1204  mPendingScrollAnchorAdjustment.Clear();
   1205  mPendingScrollResnap.Clear();
   1206 
   1207  // This shell must be removed from the document before the frame
   1208  // hierarchy is torn down to avoid finding deleted frames through
   1209  // this presshell while the frames are being torn down
   1210  if (mDocument) {
   1211    NS_ASSERTION(mDocument->GetPresShell() == this, "Wrong shell?");
   1212    mDocument->ClearServoRestyleRoot();
   1213    mDocument->DeletePresShell();
   1214  }
   1215 
   1216  if (mPresContext) {
   1217    mPresContext->AnimationEventDispatcher()->ClearEventQueue();
   1218  }
   1219 
   1220  // Revoke any pending events.  We need to do this and cancel pending reflows
   1221  // before we destroy the frame constructor, since apparently frame destruction
   1222  // sometimes spins the event queue when plug-ins are involved(!).
   1223  // XXXmats is this still needed now that plugins are gone?
   1224  CancelAllPendingReflows();
   1225  CancelPostedReflowCallbacks();
   1226 
   1227  // Destroy the frame constructor. This will destroy the frame hierarchy
   1228  mFrameConstructor->WillDestroyFrameTree();
   1229 
   1230  NS_WARNING_ASSERTION(!mAutoWeakFrames && mWeakFrames.IsEmpty(),
   1231                       "Weak frames alive after destroying FrameManager");
   1232  while (mAutoWeakFrames) {
   1233    mAutoWeakFrames->Clear(this);
   1234  }
   1235  const nsTArray<WeakFrame*> weakFrames = ToArray(mWeakFrames);
   1236  for (WeakFrame* weakFrame : weakFrames) {
   1237    weakFrame->Clear(this);
   1238  }
   1239 
   1240  // Clear the embedding frame only after tearing down the frame tree, since we
   1241  // rely on reaching the display root frame from frame destruction.
   1242  if (nsSubDocumentFrame* f = GetInProcessEmbedderFrame()) {
   1243    f->RemoveEmbeddingPresShell(this);
   1244  }
   1245  mEmbedderFrame = nullptr;
   1246 
   1247  // Terminate AccessibleCaretEventHub after tearing down the frame tree so that
   1248  // we don't need to remove caret element's frame in
   1249  // AccessibleCaret::RemoveCaretElement().
   1250  if (mAccessibleCaretEventHub) {
   1251    mAccessibleCaretEventHub->Terminate();
   1252    mAccessibleCaretEventHub = nullptr;
   1253  }
   1254 
   1255  mWidgetListener = nullptr;
   1256 
   1257  if (mPresContext) {
   1258    // We hold a reference to the pres context, and it holds a weak link back
   1259    // to us. To avoid the pres context having a dangling reference, set its
   1260    // pres shell to nullptr
   1261    mPresContext->DetachPresShell();
   1262  }
   1263 
   1264  mHaveShutDown = true;
   1265 
   1266  mTouchManager.Destroy();
   1267 }
   1268 
   1269 void PresShell::StartObservingRefreshDriver() {
   1270  nsRefreshDriver* rd = mPresContext->RefreshDriver();
   1271  if (mResizeEventPending || mVisualViewportResizeEventPending) {
   1272    rd->ScheduleRenderingPhase(mozilla::RenderingPhase::ResizeSteps);
   1273  }
   1274  if (mNeedLayoutFlush || mNeedStyleFlush) {
   1275    rd->ScheduleRenderingPhase(mozilla::RenderingPhase::Layout);
   1276  }
   1277 }
   1278 
   1279 nsRefreshDriver* PresShell::GetRefreshDriver() const {
   1280  return mPresContext ? mPresContext->RefreshDriver() : nullptr;
   1281 }
   1282 
   1283 // NOTE(emilio): It'd be ideal if instead of this explicit tracking we could
   1284 // rely on mDocument->GetEmbedderElement()->GetPrimaryFrame() + relevant
   1285 // null-checks. However, given how things are set up now, the embedder element
   1286 // in BrowsingContext / Window get cleared before tearing down the pres shell,
   1287 // and RDL relies on getting ahold of it to get the display root.
   1288 void PresShell::SetInProcessEmbedderFrame(nsSubDocumentFrame* aFrame) {
   1289  mEmbedderFrame = aFrame;
   1290 }
   1291 
   1292 void PresShell::SetAuthorStyleDisabled(bool aStyleDisabled) {
   1293  if (aStyleDisabled != StyleSet()->GetAuthorStyleDisabled()) {
   1294    StyleSet()->SetAuthorStyleDisabled(aStyleDisabled);
   1295    mDocument->ApplicableStylesChanged();
   1296 
   1297    nsCOMPtr<nsIObserverService> observerService =
   1298        mozilla::services::GetObserverService();
   1299    if (observerService) {
   1300      observerService->NotifyObservers(
   1301          ToSupports(mDocument), "author-style-disabled-changed", nullptr);
   1302    }
   1303  }
   1304 }
   1305 
   1306 bool PresShell::GetAuthorStyleDisabled() const {
   1307  return StyleSet()->GetAuthorStyleDisabled();
   1308 }
   1309 
   1310 void PresShell::AddUserSheet(StyleSheet* aSheet) {
   1311  // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
   1312  // ordering. We want this new sheet to come after all the existing stylesheet
   1313  // service sheets (which are at the start), but before other user sheets; see
   1314  // nsIStyleSheetService.idl for the ordering.
   1315 
   1316  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
   1317  nsTArray<RefPtr<StyleSheet>>& userSheets = *sheetService->UserStyleSheets();
   1318 
   1319  // Search for the place to insert the new user sheet. Since all of the
   1320  // stylesheet service provided user sheets should be at the start of the style
   1321  // set's list, and aSheet should be at the end of userSheets. Given that, we
   1322  // can find the right place to insert the new sheet based on the length of
   1323  // userSheets.
   1324  MOZ_ASSERT(aSheet);
   1325  MOZ_ASSERT(userSheets.LastElement() == aSheet);
   1326 
   1327  size_t index = userSheets.Length() - 1;
   1328 
   1329  // Assert that all of userSheets (except for the last, new element) matches up
   1330  // with what's in the style set.
   1331  for (size_t i = 0; i < index; ++i) {
   1332    MOZ_ASSERT(StyleSet()->SheetAt(StyleOrigin::User, i) == userSheets[i]);
   1333  }
   1334 
   1335  if (index == static_cast<size_t>(StyleSet()->SheetCount(StyleOrigin::User))) {
   1336    StyleSet()->AppendStyleSheet(*aSheet);
   1337  } else {
   1338    StyleSheet* ref = StyleSet()->SheetAt(StyleOrigin::User, index);
   1339    StyleSet()->InsertStyleSheetBefore(*aSheet, *ref);
   1340  }
   1341 
   1342  mDocument->ApplicableStylesChanged();
   1343 }
   1344 
   1345 void PresShell::AddAgentSheet(StyleSheet* aSheet) {
   1346  // Make sure this does what nsDocumentViewer::CreateStyleSet does
   1347  // wrt ordering.
   1348  StyleSet()->AppendStyleSheet(*aSheet);
   1349  mDocument->ApplicableStylesChanged();
   1350 }
   1351 
   1352 void PresShell::AddAuthorSheet(StyleSheet* aSheet) {
   1353  // Document specific "additional" Author sheets should be stronger than the
   1354  // ones added with the StyleSheetService.
   1355  StyleSheet* firstAuthorSheet = mDocument->GetFirstAdditionalAuthorSheet();
   1356  if (firstAuthorSheet) {
   1357    StyleSet()->InsertStyleSheetBefore(*aSheet, *firstAuthorSheet);
   1358  } else {
   1359    StyleSet()->AppendStyleSheet(*aSheet);
   1360  }
   1361 
   1362  mDocument->ApplicableStylesChanged();
   1363 }
   1364 
   1365 bool PresShell::NeedsFocusFixUp() const {
   1366  if (NS_WARN_IF(!mDocument)) {
   1367    return false;
   1368  }
   1369 
   1370  nsIContent* currentFocus = mDocument->GetUnretargetedFocusedContent(
   1371      Document::IncludeChromeOnly::Yes);
   1372  if (!currentFocus) {
   1373    return false;
   1374  }
   1375 
   1376  // If focus target is an area element with one or more shapes that are
   1377  // focusable areas.
   1378  if (auto* area = HTMLAreaElement::FromNode(currentFocus)) {
   1379    if (nsFocusManager::IsAreaElementFocusable(*area)) {
   1380      return false;
   1381    }
   1382  }
   1383 
   1384  nsIFrame* f = currentFocus->GetPrimaryFrame();
   1385  if (f && f->IsFocusable()) {
   1386    return false;
   1387  }
   1388 
   1389  if (currentFocus == mDocument->GetBody() ||
   1390      currentFocus == mDocument->GetRootElement()) {
   1391    return false;
   1392  }
   1393 
   1394  return true;
   1395 }
   1396 
   1397 bool PresShell::FixUpFocus() {
   1398  if (!NeedsFocusFixUp()) {
   1399    return false;
   1400  }
   1401  RefPtr fm = nsFocusManager::GetFocusManager();
   1402  nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
   1403  if (NS_WARN_IF(!window)) {
   1404    return false;
   1405  }
   1406  fm->ClearFocus(window);
   1407  return true;
   1408 }
   1409 
   1410 void PresShell::SelectionWillTakeFocus() {
   1411  if (mSelection) {
   1412    FrameSelectionWillTakeFocus(*mSelection,
   1413                                CanMoveLastSelectionForToString::No);
   1414  }
   1415 }
   1416 
   1417 void PresShell::SelectionWillLoseFocus() {
   1418  // Do nothing, the main selection is the default focused selection.
   1419 }
   1420 
   1421 // Selection repainting code relies on selection offsets being properly
   1422 // adjusted (see bug 1626291), so we need to wait until the DOM is finished
   1423 // notifying.
   1424 static void RepaintNormalSelectionWhenSafe(nsFrameSelection& aFrameSelection) {
   1425  if (nsContentUtils::IsSafeToRunScript()) {
   1426    aFrameSelection.RepaintSelection(SelectionType::eNormal);
   1427    return;
   1428  }
   1429 
   1430  // Note that importantly we don't defer changing the DisplaySelection. That'd
   1431  // be potentially racy with other code that may change it.
   1432  nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
   1433      "RepaintNormalSelectionWhenSafe",
   1434      [sel = RefPtr<nsFrameSelection>(&aFrameSelection)] {
   1435        sel->RepaintSelection(SelectionType::eNormal);
   1436      }));
   1437 }
   1438 
   1439 void PresShell::FrameSelectionWillLoseFocus(nsFrameSelection& aFrameSelection) {
   1440  if (mFocusedFrameSelection != &aFrameSelection) {
   1441    return;
   1442  }
   1443 
   1444  // Do nothing, the main selection is the default focused selection.
   1445  if (&aFrameSelection == mSelection) {
   1446    return;
   1447  }
   1448 
   1449  RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
   1450  MOZ_ASSERT(!mFocusedFrameSelection);
   1451 
   1452  if (old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
   1453    old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
   1454    RepaintNormalSelectionWhenSafe(*old);
   1455  }
   1456 
   1457  if (mSelection) {
   1458    FrameSelectionWillTakeFocus(*mSelection,
   1459                                CanMoveLastSelectionForToString::No);
   1460  }
   1461 }
   1462 
   1463 void PresShell::FrameSelectionWillTakeFocus(
   1464    nsFrameSelection& aFrameSelection,
   1465    CanMoveLastSelectionForToString aCanMoveLastSelectionForToString) {
   1466  if (StaticPrefs::dom_selection_mimic_chrome_tostring_enabled()) {
   1467    if (aCanMoveLastSelectionForToString ==
   1468        CanMoveLastSelectionForToString::Yes) {
   1469      UpdateLastSelectionForToString(&aFrameSelection);
   1470    }
   1471  }
   1472  if (mFocusedFrameSelection == &aFrameSelection) {
   1473 #ifdef XP_MACOSX
   1474    // FIXME: Mac needs to update the global selection cache, even if the
   1475    // document's focused selection doesn't change, and this is currently done
   1476    // from RepaintSelection. Maybe we should move part of the global selection
   1477    // handling here, or something of that sort, unclear.
   1478    RepaintNormalSelectionWhenSafe(aFrameSelection);
   1479 #endif
   1480    return;
   1481  }
   1482 
   1483  RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
   1484  mFocusedFrameSelection = &aFrameSelection;
   1485 
   1486  if (old &&
   1487      old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
   1488    old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
   1489    RepaintNormalSelectionWhenSafe(*old);
   1490  }
   1491 
   1492  if (aFrameSelection.GetDisplaySelection() !=
   1493      nsISelectionController::SELECTION_ON) {
   1494    aFrameSelection.SetDisplaySelection(nsISelectionController::SELECTION_ON);
   1495    RepaintNormalSelectionWhenSafe(aFrameSelection);
   1496  }
   1497 }
   1498 
   1499 void PresShell::UpdateLastSelectionForToString(
   1500    const nsFrameSelection* aFrameSelection) {
   1501  if (mLastSelectionForToString != aFrameSelection) {
   1502    mLastSelectionForToString = aFrameSelection;
   1503  }
   1504 }
   1505 
   1506 NS_IMETHODIMP
   1507 PresShell::SetDisplaySelection(int16_t aToggle) {
   1508  mSelection->SetDisplaySelection(aToggle);
   1509  return NS_OK;
   1510 }
   1511 
   1512 NS_IMETHODIMP
   1513 PresShell::GetDisplaySelection(int16_t* aToggle) {
   1514  *aToggle = mSelection->GetDisplaySelection();
   1515  return NS_OK;
   1516 }
   1517 
   1518 NS_IMETHODIMP
   1519 PresShell::GetSelectionFromScript(RawSelectionType aRawSelectionType,
   1520                                  Selection** aSelection) {
   1521  if (!aSelection || !mSelection) {
   1522    return NS_ERROR_NULL_POINTER;
   1523  }
   1524 
   1525  RefPtr<Selection> selection =
   1526      mSelection->GetSelection(ToSelectionType(aRawSelectionType));
   1527 
   1528  if (!selection) {
   1529    return NS_ERROR_INVALID_ARG;
   1530  }
   1531 
   1532  selection.forget(aSelection);
   1533  return NS_OK;
   1534 }
   1535 
   1536 Selection* PresShell::GetSelection(RawSelectionType aRawSelectionType) {
   1537  if (!mSelection) {
   1538    return nullptr;
   1539  }
   1540 
   1541  return mSelection->GetSelection(ToSelectionType(aRawSelectionType));
   1542 }
   1543 
   1544 Selection* PresShell::GetCurrentSelection(SelectionType aSelectionType) {
   1545  if (!mSelection) {
   1546    return nullptr;
   1547  }
   1548 
   1549  return mSelection->GetSelection(aSelectionType);
   1550 }
   1551 
   1552 nsFrameSelection* PresShell::GetLastFocusedFrameSelection() {
   1553  return mFocusedFrameSelection ? mFocusedFrameSelection : mSelection;
   1554 }
   1555 
   1556 NS_IMETHODIMP
   1557 PresShell::ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
   1558                                   SelectionRegion aRegion,
   1559                                   ControllerScrollFlags aFlags) {
   1560  if (!mSelection) {
   1561    return NS_ERROR_NULL_POINTER;
   1562  }
   1563 
   1564  RefPtr<nsFrameSelection> frameSelection = mSelection;
   1565  return frameSelection->ScrollSelectionIntoView(
   1566      ToSelectionType(aRawSelectionType), aRegion, aFlags);
   1567 }
   1568 
   1569 NS_IMETHODIMP
   1570 PresShell::RepaintSelection(RawSelectionType aRawSelectionType) {
   1571  if (!mSelection) {
   1572    return NS_ERROR_NULL_POINTER;
   1573  }
   1574 
   1575  if (MOZ_UNLIKELY(mIsDestroying)) {
   1576    return NS_OK;
   1577  }
   1578 
   1579  RefPtr<nsFrameSelection> frameSelection = mSelection;
   1580  return frameSelection->RepaintSelection(ToSelectionType(aRawSelectionType));
   1581 }
   1582 
   1583 // Make shell be a document observer
   1584 void PresShell::BeginObservingDocument() {
   1585  if (mDocument && !mIsDestroying) {
   1586    mIsObservingDocument = true;
   1587    if (mIsDocumentGone) {
   1588      NS_WARNING(
   1589          "Adding a presshell that was disconnected from the document "
   1590          "as a document observer?  Sounds wrong...");
   1591      mIsDocumentGone = false;
   1592    }
   1593  }
   1594 }
   1595 
   1596 // Make shell stop being a document observer
   1597 void PresShell::EndObservingDocument() {
   1598  // XXXbz do we need to tell the frame constructor that the document
   1599  // is gone, perhaps?  Except for printing it's NOT gone, sometimes.
   1600  mIsDocumentGone = true;
   1601  mIsObservingDocument = false;
   1602 }
   1603 
   1604 void PresShell::InitPaintSuppressionTimer() {
   1605  // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
   1606  Document* doc = mDocument->GetDisplayDocument()
   1607                      ? mDocument->GetDisplayDocument()
   1608                      : mDocument.get();
   1609  const bool inProcess = !doc->GetBrowsingContext() ||
   1610                         doc->GetBrowsingContext()->Top()->IsInProcess();
   1611  int32_t delay = inProcess
   1612                      ? StaticPrefs::nglayout_initialpaint_delay()
   1613                      : StaticPrefs::nglayout_initialpaint_delay_in_oopif();
   1614  mPaintSuppressionTimer->InitWithNamedFuncCallback(
   1615      [](nsITimer* aTimer, void* aPresShell) {
   1616        RefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
   1617        self->UnsuppressPainting();
   1618      },
   1619      this, delay, nsITimer::TYPE_ONE_SHOT,
   1620      "PresShell::sPaintSuppressionCallback"_ns);
   1621 }
   1622 
   1623 nsresult PresShell::Initialize() {
   1624  if (mIsDestroying) {
   1625    return NS_OK;
   1626  }
   1627 
   1628  if (!mDocument) {
   1629    // Nothing to do
   1630    return NS_OK;
   1631  }
   1632 
   1633  MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::Initialize this=%p", this));
   1634 
   1635  NS_ASSERTION(!mDidInitialize, "Why are we being called?");
   1636 
   1637  RefPtr<PresShell> kungFuDeathGrip(this);
   1638 
   1639  RecomputeFontSizeInflationEnabled();
   1640  MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
   1641 
   1642  // Ensure the pres context doesn't think it has changed, since we haven't even
   1643  // started layout. This avoids spurious restyles / reflows afterwards.
   1644  //
   1645  // Note that this is very intentionally before setting mDidInitialize so it
   1646  // doesn't notify the document, or run media query change events.
   1647  mPresContext->FlushPendingMediaFeatureValuesChanged();
   1648  MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
   1649 
   1650  mDidInitialize = true;
   1651 
   1652  // Get the root frame from the frame constructor.
   1653  // XXXbz it would be nice to move this somewhere else... like frame manager
   1654  // Init(), say.  But we need to make sure our views are all set up by the
   1655  // time we do this!
   1656  MOZ_ASSERT(!mFrameConstructor->GetRootFrame(),
   1657             "How did that happen, exactly?");
   1658  ViewportFrame* rootFrame;
   1659  {
   1660    nsAutoScriptBlocker scriptBlocker;
   1661    rootFrame = mFrameConstructor->ConstructRootFrame();
   1662    mFrameConstructor->SetRootFrame(rootFrame);
   1663  }
   1664 
   1665  NS_ENSURE_STATE(!mHaveShutDown);
   1666 
   1667  if (!rootFrame) {
   1668    return NS_ERROR_OUT_OF_MEMORY;
   1669  }
   1670 
   1671  if (Element* root = mDocument->GetRootElement()) {
   1672    {
   1673      nsAutoCauseReflowNotifier reflowNotifier(this);
   1674      // Have the style sheet processor construct frame for the root
   1675      // content object down
   1676      mFrameConstructor->ContentInserted(
   1677          root, nsCSSFrameConstructor::InsertionKind::Sync);
   1678    }
   1679    // Something in mFrameConstructor->ContentInserted may have caused
   1680    // Destroy() to get called, bug 337586.  Or, nsAutoCauseReflowNotifier
   1681    // (which sets up a script blocker) going out of scope may have killed us
   1682    // too
   1683    NS_ENSURE_STATE(!mHaveShutDown);
   1684  }
   1685 
   1686  mDocument->MaybeScheduleRendering();
   1687 
   1688  NS_ASSERTION(rootFrame, "How did that happen?");
   1689 
   1690  // Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit
   1691  // set, but XBL processing could have caused a reflow which clears it.
   1692  if (MOZ_LIKELY(rootFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY))) {
   1693    // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
   1694    rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
   1695    NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
   1696                 "Why is the root in mDirtyRoots already?");
   1697    FrameNeedsReflow(rootFrame, IntrinsicDirty::None, NS_FRAME_IS_DIRTY);
   1698    NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
   1699                 "Should be in mDirtyRoots now");
   1700    NS_ASSERTION(mNeedStyleFlush || mNeedLayoutFlush,
   1701                 "Why no reflow scheduled?");
   1702  }
   1703 
   1704  // Restore our root scroll position now if we're getting here after EndLoad
   1705  // got called, since this is our one chance to do it.  Note that we need not
   1706  // have reflowed for this to work; when the scrollframe is finally reflowed
   1707  // it'll pick up the position we store in it here.
   1708  if (!mDocumentLoading) {
   1709    RestoreRootScrollPosition();
   1710  }
   1711 
   1712  // For printing, we just immediately unsuppress.
   1713  if (!mPresContext->IsPaginated()) {
   1714    // Kick off a one-shot timer based off our pref value.  When this timer
   1715    // fires, if painting is still locked down, then we will go ahead and
   1716    // trigger a full invalidate and allow painting to proceed normally.
   1717    mPaintingSuppressed = true;
   1718    // Don't suppress painting if the document isn't loading. However,
   1719    // the initial about:blank appears not to be loading, but we still
   1720    // want to suppress painting.
   1721    nsIDocShell* docShell = mDocument->GetDocShell();
   1722    if ((docShell &&
   1723         !nsDocShell::Cast(docShell)
   1724              ->HasStartedLoadingOtherThanInitialBlankURI() &&
   1725         mDocument->IsInitialDocument()) ||
   1726        mDocument->GetReadyStateEnum() != Document::READYSTATE_COMPLETE) {
   1727      mPaintSuppressionTimer = NS_NewTimer();
   1728    }
   1729    if (!mPaintSuppressionTimer) {
   1730      mPaintingSuppressed = false;
   1731    } else {
   1732      // Initialize the timer.
   1733      mPaintSuppressionTimer->SetTarget(GetMainThreadSerialEventTarget());
   1734      InitPaintSuppressionTimer();
   1735      if (mHasTriedFastUnsuppress) {
   1736        // Someone tried to unsuppress painting before Initialize was called so
   1737        // unsuppress painting rather soon.
   1738        mHasTriedFastUnsuppress = false;
   1739        TryUnsuppressPaintingSoon();
   1740        MOZ_ASSERT(mHasTriedFastUnsuppress);
   1741      }
   1742    }
   1743  }
   1744 
   1745  // If we get here and painting is not suppressed, we still want to run the
   1746  // unsuppression logic, so set mShouldUnsuppressPainting to true.
   1747  if (!mPaintingSuppressed) {
   1748    mShouldUnsuppressPainting = true;
   1749  }
   1750 
   1751  return NS_OK;  // XXX this needs to be real. MMP
   1752 }
   1753 
   1754 void PresShell::TryUnsuppressPaintingSoon() {
   1755  if (mHasTriedFastUnsuppress) {
   1756    return;
   1757  }
   1758  mHasTriedFastUnsuppress = true;
   1759 
   1760  if (!mDidInitialize || !IsPaintingSuppressed() || !XRE_IsContentProcess()) {
   1761    return;
   1762  }
   1763 
   1764  if (!mDocument->IsInitialDocument() &&
   1765      mDocument->DidHitCompleteSheetCache() &&
   1766      mPresContext->IsRootContentDocumentCrossProcess()) {
   1767    // Try to unsuppress faster on a top level page if it uses stylesheet
   1768    // cache, since that hints that many resources can be painted sooner than
   1769    // in a cold page load case.
   1770    NS_DispatchToCurrentThreadQueue(
   1771        NS_NewRunnableFunction("PresShell::TryUnsuppressPaintingSoon",
   1772                               [self = RefPtr{this}]() -> void {
   1773                                 if (self->IsPaintingSuppressed()) {
   1774                                   PROFILER_MARKER_UNTYPED(
   1775                                       "Fast paint unsuppression", GRAPHICS);
   1776                                   self->UnsuppressPainting();
   1777                                 }
   1778                               }),
   1779        EventQueuePriority::Control);
   1780  }
   1781 }
   1782 
   1783 void PresShell::RefreshZoomConstraintsForScreenSizeChange() {
   1784  if (mZoomConstraintsClient) {
   1785    mZoomConstraintsClient->ScreenSizeChanged();
   1786  }
   1787 }
   1788 
   1789 nsSize PresShell::MaybePendingLayoutViewportSize() const {
   1790  if (mPendingLayoutViewportSize) {
   1791    return *mPendingLayoutViewportSize;
   1792  }
   1793  return mPresContext ? mPresContext->GetVisibleArea().Size() : nsSize();
   1794 }
   1795 
   1796 bool PresShell::ShouldDelayResize() const {
   1797  if (!IsVisible()) {
   1798    return true;
   1799  }
   1800  nsRefreshDriver* rd = GetRefreshDriver();
   1801  return rd && rd->IsResizeSuppressed();
   1802 }
   1803 
   1804 void PresShell::FlushDelayedResize() {
   1805  if (!mPendingLayoutViewportSize) {
   1806    return;
   1807  }
   1808  auto size = mPendingLayoutViewportSize.extract();
   1809  if (!mPresContext || size == mPresContext->GetVisibleArea().Size()) {
   1810    return;
   1811  }
   1812  ResizeReflow(size);
   1813 }
   1814 
   1815 void PresShell::SetLayoutViewportSize(const nsSize& aSize, bool aDelay) {
   1816  mPendingLayoutViewportSize = Some(aSize);
   1817  if (aDelay || ShouldDelayResize()) {
   1818    SetNeedStyleFlush();
   1819    SetNeedLayoutFlush();
   1820    return;
   1821  }
   1822  FlushDelayedResize();
   1823 }
   1824 
   1825 void PresShell::ForceResizeReflowWithCurrentDimensions() {
   1826  ResizeReflow(MaybePendingLayoutViewportSize());
   1827 }
   1828 
   1829 void PresShell::ResizeReflow(const nsSize& aSize,
   1830                             ResizeReflowOptions aOptions) {
   1831  if (mZoomConstraintsClient) {
   1832    // If we have a ZoomConstraintsClient and the available screen area
   1833    // changed, then we might need to disable double-tap-to-zoom, so notify
   1834    // the ZCC to update itself.
   1835    mZoomConstraintsClient->ScreenSizeChanged();
   1836  }
   1837  if (UsesMobileViewportSizing()) {
   1838    // If we are using mobile viewport sizing, request a reflow from the MVM.
   1839    // It can recompute the final CSS viewport and trigger a call to
   1840    // ResizeReflowIgnoreOverride if it changed. We don't force adjusting
   1841    // of resolution, because that is only necessary when we are destroying
   1842    // the MVM.
   1843    MOZ_ASSERT(mMobileViewportManager);
   1844    mMobileViewportManager->RequestReflow(false);
   1845    return;
   1846  }
   1847  ResizeReflowIgnoreOverride(aSize, aOptions);
   1848 }
   1849 
   1850 bool PresShell::SimpleResizeReflow(const nsSize& aSize) {
   1851  MOZ_ASSERT(aSize.width != NS_UNCONSTRAINEDSIZE);
   1852  MOZ_ASSERT(aSize.height != NS_UNCONSTRAINEDSIZE);
   1853  nsSize oldSize = mPresContext->GetVisibleArea().Size();
   1854  mPresContext->SetVisibleArea(nsRect(nsPoint(), aSize));
   1855  nsIFrame* rootFrame = GetRootFrame();
   1856  if (!rootFrame) {
   1857    return false;
   1858  }
   1859  WritingMode wm = rootFrame->GetWritingMode();
   1860  bool isBSizeChanging = wm.IsVertical() ? oldSize.width != aSize.width
   1861                                         : oldSize.height != aSize.height;
   1862  if (isBSizeChanging) {
   1863    nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
   1864    rootFrame->SetHasBSizeChange(true);
   1865  }
   1866  FrameNeedsReflow(rootFrame, IntrinsicDirty::None,
   1867                   NS_FRAME_HAS_DIRTY_CHILDREN);
   1868 
   1869  if (mMobileViewportManager) {
   1870    mMobileViewportManager->UpdateSizesBeforeReflow();
   1871  }
   1872  return true;
   1873 }
   1874 
   1875 bool PresShell::CanHandleUserInputEvents(WidgetGUIEvent* aGUIEvent) {
   1876  if (XRE_IsParentProcess()) {
   1877    return true;
   1878  }
   1879 
   1880  if (aGUIEvent->mFlags.mIsSynthesizedForTests &&
   1881      !StaticPrefs::dom_input_events_security_isUserInputHandlingDelayTest()) {
   1882    return true;
   1883  }
   1884 
   1885  if (!aGUIEvent->IsUserAction()) {
   1886    return true;
   1887  }
   1888 
   1889  if (nsPresContext* rootPresContext = mPresContext->GetRootPresContext()) {
   1890    return rootPresContext->UserInputEventsAllowed();
   1891  }
   1892 
   1893  return true;
   1894 }
   1895 
   1896 void PresShell::PostScrollEvent(Runnable* aEvent) {
   1897  MOZ_ASSERT(aEvent);
   1898  mPendingScrollEvents.AppendElement(aEvent);
   1899 
   1900  // If we (or any descendant docs) have any content visibility: auto elements,
   1901  // we also need to run its proximity to the viewport on scroll. Same for
   1902  // intersection observers.
   1903  //
   1904  // We don't need to mark ourselves as needing a layout flush. We don't need to
   1905  // get flushed, we just need the viewport relevancy / content-visibility: auto
   1906  // viewport proximity phases to run.
   1907  mPresContext->RefreshDriver()->ScheduleRenderingPhases(
   1908      {RenderingPhase::ScrollSteps, RenderingPhase::Layout,
   1909       RenderingPhase::UpdateIntersectionObservations});
   1910 }
   1911 
   1912 void PresShell::ScheduleResizeEventIfNeeded(ResizeEventKind aKind) {
   1913  if (mIsDestroying) {
   1914    return;
   1915  }
   1916  if (MOZ_UNLIKELY(mDocument->GetBFCacheEntry())) {
   1917    return;
   1918  }
   1919  if (aKind == ResizeEventKind::Regular) {
   1920    mResizeEventPending = true;
   1921  } else {
   1922    MOZ_ASSERT(aKind == ResizeEventKind::Visual);
   1923    mVisualViewportResizeEventPending = true;
   1924  }
   1925  mPresContext->RefreshDriver()->ScheduleRenderingPhase(
   1926      RenderingPhase::ResizeSteps);
   1927 }
   1928 
   1929 bool PresShell::ResizeReflowIgnoreOverride(const nsSize& aSize,
   1930                                           ResizeReflowOptions aOptions) {
   1931  MOZ_ASSERT(!mIsReflowing, "Shouldn't be in reflow here!");
   1932 
   1933  // Historically we never fired resize events if there was no root frame by the
   1934  // time this function got called.
   1935  const bool initialized = mDidInitialize;
   1936  RefPtr<PresShell> kungFuDeathGrip(this);
   1937 
   1938  auto postResizeEventIfNeeded = [this, initialized]() {
   1939    if (initialized) {
   1940      ScheduleResizeEventIfNeeded(ResizeEventKind::Regular);
   1941    }
   1942  };
   1943 
   1944  // If there are orthogonal flows that were dependent on the ICB size, mark
   1945  // them as dirty to ensure they will be reflowed.
   1946  for (auto* frame : mOrthogonalFlows) {
   1947    FrameNeedsReflow(frame, IntrinsicDirty::None, NS_FRAME_HAS_DIRTY_CHILDREN);
   1948  }
   1949  mOrthogonalFlows.Clear();
   1950 
   1951  if (!(aOptions & ResizeReflowOptions::BSizeLimit)) {
   1952    nsSize oldSize = mPresContext->GetVisibleArea().Size();
   1953    if (oldSize == aSize) {
   1954      return false;
   1955    }
   1956 
   1957    bool changed = SimpleResizeReflow(aSize);
   1958    postResizeEventIfNeeded();
   1959    return changed;
   1960  }
   1961 
   1962  // Make sure that style is flushed before setting the pres context
   1963  // VisibleArea.
   1964  //
   1965  // Otherwise we may end up with bogus viewport units resolved against the
   1966  // unconstrained bsize, or restyling the whole document resolving viewport
   1967  // units against targetWidth, which may end up doing wasteful work.
   1968  mDocument->FlushPendingNotifications(FlushType::Frames);
   1969 
   1970  nsIFrame* rootFrame = GetRootFrame();
   1971  if (mIsDestroying || !rootFrame) {
   1972    // If we don't have a root frame yet, that means we haven't had our initial
   1973    // reflow... If that's the case, and aWidth or aHeight is unconstrained,
   1974    // ignore them altogether.
   1975    if (aSize.height == NS_UNCONSTRAINEDSIZE ||
   1976        aSize.width == NS_UNCONSTRAINEDSIZE) {
   1977      // We can't do the work needed for SizeToContent without a root
   1978      // frame, and we want to return before setting the visible area.
   1979      return false;
   1980    }
   1981 
   1982    mPresContext->SetVisibleArea(nsRect(nsPoint(), aSize));
   1983    // There isn't anything useful we can do if the initial reflow hasn't
   1984    // happened.
   1985    return true;
   1986  }
   1987 
   1988  WritingMode wm = rootFrame->GetWritingMode();
   1989  MOZ_ASSERT(
   1990      (wm.IsVertical() ? aSize.height : aSize.width) != NS_UNCONSTRAINEDSIZE,
   1991      "unconstrained isize not allowed");
   1992 
   1993  nsSize targetSize = aSize;
   1994  if (wm.IsVertical()) {
   1995    targetSize.width = NS_UNCONSTRAINEDSIZE;
   1996  } else {
   1997    targetSize.height = NS_UNCONSTRAINEDSIZE;
   1998  }
   1999 
   2000  mPresContext->SetVisibleArea(nsRect(nsPoint(), targetSize));
   2001  // XXX Do a full invalidate at the beginning so that invalidates along
   2002  // the way don't have region accumulation issues?
   2003 
   2004  // For height:auto BSizes (i.e. layout-controlled), descendant
   2005  // intrinsic sizes can't depend on them. So the only other case is
   2006  // viewport-controlled BSizes which we handle here.
   2007  nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(rootFrame);
   2008  rootFrame->SetHasBSizeChange(true);
   2009  FrameNeedsReflow(rootFrame, IntrinsicDirty::None,
   2010                   NS_FRAME_HAS_DIRTY_CHILDREN);
   2011 
   2012  {
   2013    nsAutoCauseReflowNotifier crNotifier(this);
   2014    WillDoReflow();
   2015 
   2016    // Kick off a top-down reflow
   2017    AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
   2018 
   2019    mDirtyRoots.Remove(rootFrame);
   2020    DoReflow(rootFrame, true, nullptr);
   2021 
   2022    const bool reflowAgain =
   2023        wm.IsVertical() ? mPresContext->GetVisibleArea().width > aSize.width
   2024                        : mPresContext->GetVisibleArea().height > aSize.height;
   2025 
   2026    if (reflowAgain) {
   2027      mPresContext->SetVisibleArea(nsRect(nsPoint(), aSize));
   2028      rootFrame->SetHasBSizeChange(true);
   2029      DoReflow(rootFrame, true, nullptr);
   2030    }
   2031  }
   2032 
   2033  // Now, we may have been destroyed by the destructor of
   2034  // `nsAutoCauseReflowNotifier`.
   2035 
   2036  mPendingDidDoReflow = true;
   2037  DidDoReflow(true);
   2038 
   2039  // the reflow above should've set our bsize if it was NS_UNCONSTRAINEDSIZE,
   2040  // and the isize shouldn't be NS_UNCONSTRAINEDSIZE anyway.
   2041  MOZ_DIAGNOSTIC_ASSERT(
   2042      mPresContext->GetVisibleArea().width != NS_UNCONSTRAINEDSIZE,
   2043      "width should not be NS_UNCONSTRAINEDSIZE after reflow");
   2044  MOZ_DIAGNOSTIC_ASSERT(
   2045      mPresContext->GetVisibleArea().height != NS_UNCONSTRAINEDSIZE,
   2046      "height should not be NS_UNCONSTRAINEDSIZE after reflow");
   2047 
   2048  postResizeEventIfNeeded();
   2049  return true;
   2050 }
   2051 
   2052 // https://drafts.csswg.org/cssom-view/#document-run-the-resize-steps
   2053 void PresShell::RunResizeSteps() {
   2054  if (!mResizeEventPending && !mVisualViewportResizeEventPending) {
   2055    return;
   2056  }
   2057  if (mIsDocumentGone) {
   2058    return;
   2059  }
   2060 
   2061  RefPtr window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow());
   2062  if (!window) {
   2063    return;
   2064  }
   2065 
   2066  if (mResizeEventPending) {
   2067    // Clear it before firing, just in case the event triggers another resize
   2068    // event. Such event will fire next tick.
   2069    mResizeEventPending = false;
   2070    WidgetEvent event(true, mozilla::eResize);
   2071    nsEventStatus status = nsEventStatus_eIgnore;
   2072 
   2073    if (RefPtr<nsPIDOMWindowOuter> outer = window->GetOuterWindow()) {
   2074      // MOZ_KnownLive due to bug 1506441
   2075      EventDispatcher::Dispatch(MOZ_KnownLive(nsGlobalWindowOuter::Cast(outer)),
   2076                                mPresContext, &event, nullptr, &status);
   2077    }
   2078  }
   2079 
   2080  if (mVisualViewportResizeEventPending) {
   2081    mVisualViewportResizeEventPending = false;
   2082    RefPtr vv = window->VisualViewport();
   2083    vv->FireResizeEvent();
   2084  }
   2085 }
   2086 
   2087 // https://drafts.csswg.org/cssom-view/#document-run-the-scroll-steps
   2088 // But note: https://github.com/w3c/csswg-drafts/issues/11164
   2089 void PresShell::RunScrollSteps() {
   2090  // Scroll events are one-shot, so after running them we can drop them.
   2091  // However, dispatching a scroll event can potentially cause more scroll
   2092  // events to be posted, so we move the initial set into a temporary array
   2093  // first. (Newly posted scroll events will be dispatched on the next tick.)
   2094  auto events = std::move(mPendingScrollEvents);
   2095  for (auto& event : events) {
   2096    event->Run();
   2097  }
   2098 }
   2099 
   2100 static nsIContent* GetNativeAnonymousSubtreeRoot(nsIContent* aContent) {
   2101  if (!aContent) {
   2102    return nullptr;
   2103  }
   2104  return aContent->GetClosestNativeAnonymousSubtreeRoot();
   2105 }
   2106 
   2107 void PresShell::NativeAnonymousContentWillBeRemoved(nsIContent* aAnonContent) {
   2108  MOZ_ASSERT(aAnonContent->IsRootOfNativeAnonymousSubtree());
   2109  mPresContext->EventStateManager()->NativeAnonymousContentRemoved(
   2110      aAnonContent);
   2111 #ifdef ACCESSIBILITY
   2112  if (nsAccessibilityService* accService = GetAccService()) {
   2113    accService->ContentRemoved(this, aAnonContent);
   2114  }
   2115 #endif
   2116  if (mDocument->DevToolsAnonymousAndShadowEventsEnabled()) {
   2117    aAnonContent->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ true);
   2118  }
   2119  if (nsIContent* root =
   2120          GetNativeAnonymousSubtreeRoot(mCurrentEventTarget.mContent)) {
   2121    if (aAnonContent == root) {
   2122      mCurrentEventTarget.UpdateFrameAndContent(
   2123          nullptr, aAnonContent->GetFlattenedTreeParent());
   2124    }
   2125  }
   2126 
   2127  for (EventTargetInfo& eventTargetInfo : mCurrentEventTargetStack) {
   2128    nsIContent* anon = GetNativeAnonymousSubtreeRoot(eventTargetInfo.mContent);
   2129    if (aAnonContent == anon) {
   2130      eventTargetInfo.UpdateFrameAndContent(
   2131          nullptr, aAnonContent->GetFlattenedTreeParent());
   2132    }
   2133  }
   2134 }
   2135 
   2136 void PresShell::SetIgnoreFrameDestruction(bool aIgnore) {
   2137  if (mDocument) {
   2138    // We need to tell the ImageLoader to drop all its references to frames
   2139    // because they're about to go away and it won't get notifications of that.
   2140    mDocument->EnsureStyleImageLoader().ClearFrames(mPresContext);
   2141  }
   2142  mIgnoreFrameDestruction = aIgnore;
   2143 }
   2144 
   2145 void PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) {
   2146  // We must remove these from FrameLayerBuilder::DisplayItemData::mFrameList
   2147  // here, otherwise the DisplayItemData destructor will use the destroyed frame
   2148  // when it tries to remove it from the (array) value of this property.
   2149  aFrame->RemoveDisplayItemDataForDeletion();
   2150 
   2151  if (!mIgnoreFrameDestruction) {
   2152    if (aFrame->HasImageRequest()) {
   2153      mDocument->EnsureStyleImageLoader().DropRequestsForFrame(aFrame);
   2154    }
   2155 
   2156    mFrameConstructor->NotifyDestroyingFrame(aFrame);
   2157 
   2158    mDirtyRoots.Remove(aFrame);
   2159 
   2160    // Remove frame properties
   2161    aFrame->RemoveAllProperties();
   2162 
   2163    const auto ComputeTargetContent =
   2164        [&aFrame](const EventTargetInfo& aEventTargetInfo) -> nsIContent* {
   2165      if (!IsForbiddenDispatchingToNonElementContent(
   2166              aEventTargetInfo.mEventMessage)) {
   2167        return aFrame->GetContent();
   2168      }
   2169      return aFrame->GetContent()
   2170                 ? aFrame->GetContent()
   2171                       ->GetInclusiveFlattenedTreeAncestorElement()
   2172                 : nullptr;
   2173    };
   2174 
   2175    if (aFrame == mCurrentEventTarget.mFrame) {
   2176      mCurrentEventTarget.UpdateFrameAndContent(
   2177          nullptr, ComputeTargetContent(mCurrentEventTarget));
   2178    }
   2179 
   2180    for (EventTargetInfo& eventTargetInfo : mCurrentEventTargetStack) {
   2181      if (aFrame == eventTargetInfo.mFrame) {
   2182        // One of our stack frames was deleted.  Get its content so that when we
   2183        // pop it we can still get its new frame from its content
   2184        eventTargetInfo.UpdateFrameAndContent(
   2185            nullptr, ComputeTargetContent(eventTargetInfo));
   2186      }
   2187    }
   2188 
   2189    mFramesToDirty.Remove(aFrame);
   2190    mOrthogonalFlows.Remove(aFrame);
   2191 
   2192    if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(aFrame)) {
   2193      mPendingScrollAnchorSelection.Remove(scrollContainerFrame);
   2194      mPendingScrollAnchorAdjustment.Remove(scrollContainerFrame);
   2195      mPendingScrollResnap.Remove(scrollContainerFrame);
   2196    }
   2197  }
   2198 }
   2199 
   2200 already_AddRefed<nsCaret> PresShell::GetCaret() const {
   2201  RefPtr<nsCaret> caret = mCaret;
   2202  return caret.forget();
   2203 }
   2204 
   2205 already_AddRefed<AccessibleCaretEventHub>
   2206 PresShell::GetAccessibleCaretEventHub() const {
   2207  RefPtr<AccessibleCaretEventHub> eventHub = mAccessibleCaretEventHub;
   2208  return eventHub.forget();
   2209 }
   2210 
   2211 void PresShell::SetCaret(nsCaret* aNewCaret) {
   2212  if (mCaret == aNewCaret) {
   2213    return;
   2214  }
   2215  if (mCaret) {
   2216    mCaret->SchedulePaint();
   2217  }
   2218  mCaret = aNewCaret;
   2219  if (aNewCaret) {
   2220    aNewCaret->SchedulePaint();
   2221  }
   2222 }
   2223 
   2224 void PresShell::RestoreCaret() { SetCaret(mOriginalCaret); }
   2225 
   2226 NS_IMETHODIMP PresShell::SetCaretEnabled(bool aInEnable) {
   2227  bool oldEnabled = mCaretEnabled;
   2228 
   2229  mCaretEnabled = aInEnable;
   2230 
   2231  if (mCaretEnabled != oldEnabled) {
   2232    MOZ_ASSERT(mCaret);
   2233    if (mCaret) {
   2234      mCaret->SetVisible(mCaretEnabled);
   2235    }
   2236  }
   2237 
   2238  return NS_OK;
   2239 }
   2240 
   2241 NS_IMETHODIMP PresShell::SetCaretReadOnly(bool aReadOnly) {
   2242  if (mCaret) {
   2243    mCaret->SetCaretReadOnly(aReadOnly);
   2244  }
   2245  return NS_OK;
   2246 }
   2247 
   2248 NS_IMETHODIMP PresShell::GetCaretEnabled(bool* aOutEnabled) {
   2249  NS_ENSURE_ARG_POINTER(aOutEnabled);
   2250  *aOutEnabled = mCaretEnabled;
   2251  return NS_OK;
   2252 }
   2253 
   2254 NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(bool aVisibility) {
   2255  if (mCaret) {
   2256    mCaret->SetVisibilityDuringSelection(aVisibility);
   2257  }
   2258  return NS_OK;
   2259 }
   2260 
   2261 NS_IMETHODIMP PresShell::GetCaretVisible(bool* aOutIsVisible) {
   2262  *aOutIsVisible = false;
   2263  if (mCaret) {
   2264    *aOutIsVisible = mCaret->IsVisible();
   2265  }
   2266  return NS_OK;
   2267 }
   2268 
   2269 NS_IMETHODIMP PresShell::SetSelectionFlags(int16_t aFlags) {
   2270  mSelectionFlags = aFlags;
   2271  return NS_OK;
   2272 }
   2273 
   2274 NS_IMETHODIMP PresShell::GetSelectionFlags(int16_t* aFlags) {
   2275  if (!aFlags) {
   2276    return NS_ERROR_INVALID_ARG;
   2277  }
   2278 
   2279  *aFlags = mSelectionFlags;
   2280  return NS_OK;
   2281 }
   2282 
   2283 // implementation of nsISelectionController
   2284 
   2285 NS_IMETHODIMP
   2286 PresShell::PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend) {
   2287  RefPtr<nsFrameSelection> frameSelection = mSelection;
   2288  return frameSelection->PhysicalMove(aDirection, aAmount, aExtend);
   2289 }
   2290 
   2291 NS_IMETHODIMP
   2292 PresShell::CharacterMove(bool aForward, bool aExtend) {
   2293  RefPtr<nsFrameSelection> frameSelection = mSelection;
   2294  return frameSelection->CharacterMove(aForward, aExtend);
   2295 }
   2296 
   2297 NS_IMETHODIMP
   2298 PresShell::WordMove(bool aForward, bool aExtend) {
   2299  RefPtr<nsFrameSelection> frameSelection = mSelection;
   2300  nsresult result = frameSelection->WordMove(aForward, aExtend);
   2301  // if we can't go down/up any more we must then move caret completely to
   2302  // end/beginning respectively.
   2303  if (NS_FAILED(result)) {
   2304    result = CompleteMove(aForward, aExtend);
   2305  }
   2306  return result;
   2307 }
   2308 
   2309 NS_IMETHODIMP
   2310 PresShell::LineMove(bool aForward, bool aExtend) {
   2311  RefPtr<nsFrameSelection> frameSelection = mSelection;
   2312  nsresult result = frameSelection->LineMove(aForward, aExtend);
   2313  // if we can't go down/up any more we must then move caret completely to
   2314  // end/beginning respectively.
   2315  if (NS_FAILED(result)) {
   2316    result = CompleteMove(aForward, aExtend);
   2317  }
   2318  return result;
   2319 }
   2320 
   2321 NS_IMETHODIMP
   2322 PresShell::IntraLineMove(bool aForward, bool aExtend) {
   2323  RefPtr<nsFrameSelection> frameSelection = mSelection;
   2324  return frameSelection->IntraLineMove(aForward, aExtend);
   2325 }
   2326 
   2327 NS_IMETHODIMP
   2328 PresShell::PageMove(bool aForward, bool aExtend) {
   2329  nsIFrame* frame = nullptr;
   2330  if (!aExtend) {
   2331    frame = GetScrollContainerFrameToScroll(VerticalScrollDirection);
   2332    // If there is no scrollable frame, get the frame to move caret instead.
   2333  }
   2334  if (!frame || frame->PresContext() != mPresContext) {
   2335    frame = mSelection->GetFrameToPageSelect();
   2336    if (!frame) {
   2337      return NS_OK;
   2338    }
   2339  }
   2340  // We may scroll parent scrollable element of current selection limiter.
   2341  // In such case, we don't want to scroll selection into view unless
   2342  // selection is changed.
   2343  RefPtr<nsFrameSelection> frameSelection = mSelection;
   2344  return frameSelection->PageMove(
   2345      aForward, aExtend, frame, nsFrameSelection::SelectionIntoView::IfChanged);
   2346 }
   2347 
   2348 NS_IMETHODIMP
   2349 PresShell::ScrollPage(bool aForward) {
   2350  ScrollContainerFrame* scrollContainerFrame =
   2351      GetScrollContainerFrameToScroll(VerticalScrollDirection);
   2352  ScrollMode scrollMode = apz::GetScrollModeForOrigin(ScrollOrigin::Pages);
   2353  if (scrollContainerFrame) {
   2354    scrollContainerFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
   2355                                   ScrollUnit::PAGES, scrollMode, nullptr,
   2356                                   mozilla::ScrollOrigin::NotSpecified,
   2357                                   ScrollContainerFrame::NOT_MOMENTUM,
   2358                                   ScrollSnapFlags::IntendedDirection |
   2359                                       ScrollSnapFlags::IntendedEndPosition);
   2360  }
   2361  return NS_OK;
   2362 }
   2363 
   2364 NS_IMETHODIMP
   2365 PresShell::ScrollLine(bool aForward) {
   2366  ScrollContainerFrame* scrollContainerFrame =
   2367      GetScrollContainerFrameToScroll(VerticalScrollDirection);
   2368  ScrollMode scrollMode = apz::GetScrollModeForOrigin(ScrollOrigin::Lines);
   2369  if (scrollContainerFrame) {
   2370    nsRect scrollPort = scrollContainerFrame->GetScrollPortRect();
   2371    nsSize lineSize = scrollContainerFrame->GetLineScrollAmount();
   2372    int32_t lineCount = StaticPrefs::toolkit_scrollbox_verticalScrollDistance();
   2373    if (lineCount * lineSize.height > scrollPort.Height()) {
   2374      return ScrollPage(aForward);
   2375    }
   2376    scrollContainerFrame->ScrollBy(
   2377        nsIntPoint(0, aForward ? lineCount : -lineCount), ScrollUnit::LINES,
   2378        scrollMode, nullptr, mozilla::ScrollOrigin::NotSpecified,
   2379        ScrollContainerFrame::NOT_MOMENTUM, ScrollSnapFlags::IntendedDirection);
   2380  }
   2381  return NS_OK;
   2382 }
   2383 
   2384 NS_IMETHODIMP
   2385 PresShell::ScrollCharacter(bool aRight) {
   2386  ScrollContainerFrame* scrollContainerFrame =
   2387      GetScrollContainerFrameToScroll(HorizontalScrollDirection);
   2388  ScrollMode scrollMode = apz::GetScrollModeForOrigin(ScrollOrigin::Lines);
   2389  if (scrollContainerFrame) {
   2390    int32_t h = StaticPrefs::toolkit_scrollbox_horizontalScrollDistance();
   2391    scrollContainerFrame->ScrollBy(
   2392        nsIntPoint(aRight ? h : -h, 0), ScrollUnit::LINES, scrollMode, nullptr,
   2393        mozilla::ScrollOrigin::NotSpecified, ScrollContainerFrame::NOT_MOMENTUM,
   2394        ScrollSnapFlags::IntendedDirection);
   2395  }
   2396  return NS_OK;
   2397 }
   2398 
   2399 NS_IMETHODIMP
   2400 PresShell::CompleteScroll(bool aForward) {
   2401  ScrollContainerFrame* scrollContainerFrame =
   2402      GetScrollContainerFrameToScroll(VerticalScrollDirection);
   2403  ScrollMode scrollMode = apz::GetScrollModeForOrigin(ScrollOrigin::Other);
   2404  if (scrollContainerFrame) {
   2405    scrollContainerFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
   2406                                   ScrollUnit::WHOLE, scrollMode, nullptr,
   2407                                   mozilla::ScrollOrigin::NotSpecified,
   2408                                   ScrollContainerFrame::NOT_MOMENTUM,
   2409                                   ScrollSnapFlags::IntendedEndPosition);
   2410  }
   2411  return NS_OK;
   2412 }
   2413 
   2414 NS_IMETHODIMP
   2415 PresShell::CompleteMove(bool aForward, bool aExtend) {
   2416  // Beware! This may flush notifications via synchronous
   2417  // ScrollSelectionIntoView.
   2418  RefPtr<nsFrameSelection> frameSelection = mSelection;
   2419  Element* const limiter = frameSelection->GetAncestorLimiter();
   2420  nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
   2421                            : FrameConstructor()->GetRootElementFrame();
   2422  if (!frame) {
   2423    return NS_ERROR_FAILURE;
   2424  }
   2425  nsIFrame::CaretPosition pos = frame->GetExtremeCaretPosition(!aForward);
   2426 
   2427  const nsFrameSelection::FocusMode focusMode =
   2428      aExtend ? nsFrameSelection::FocusMode::kExtendSelection
   2429              : nsFrameSelection::FocusMode::kCollapseToNewPoint;
   2430  frameSelection->HandleClick(
   2431      MOZ_KnownLive(pos.mResultContent) /* bug 1636889 */, pos.mContentOffset,
   2432      pos.mContentOffset, focusMode,
   2433      aForward ? CaretAssociationHint::After : CaretAssociationHint::Before);
   2434  if (limiter) {
   2435    // HandleClick resets ancestorLimiter, so set it again.
   2436    frameSelection->SetAncestorLimiter(limiter);
   2437  }
   2438 
   2439  // After ScrollSelectionIntoView(), the pending notifications might be
   2440  // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
   2441  return ScrollSelectionIntoView(SelectionType::eNormal,
   2442                                 nsISelectionController::SELECTION_FOCUS_REGION,
   2443                                 SelectionScrollMode::SyncFlush);
   2444 }
   2445 
   2446 // end implementations nsISelectionController
   2447 
   2448 ScrollContainerFrame* PresShell::GetRootScrollContainerFrame() const {
   2449  if (!mFrameConstructor) {
   2450    return nullptr;
   2451  }
   2452  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   2453  if (!rootFrame) {
   2454    return nullptr;
   2455  }
   2456  nsIFrame* theFrame = rootFrame->PrincipalChildList().FirstChild();
   2457  if (!theFrame || !theFrame->IsScrollContainerFrame()) {
   2458    return nullptr;
   2459  }
   2460  return static_cast<ScrollContainerFrame*>(theFrame);
   2461 }
   2462 
   2463 nsPageSequenceFrame* PresShell::GetPageSequenceFrame() const {
   2464  return mFrameConstructor->GetPageSequenceFrame();
   2465 }
   2466 
   2467 nsCanvasFrame* PresShell::GetCanvasFrame() const {
   2468  return mFrameConstructor->GetCanvasFrame();
   2469 }
   2470 
   2471 void PresShell::RestoreRootScrollPosition() {
   2472  if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
   2473    sf->ScrollToRestoredPosition();
   2474  }
   2475 }
   2476 
   2477 void PresShell::MaybeReleaseCapturingContent() {
   2478  RefPtr<nsFrameSelection> frameSelection = FrameSelection();
   2479  if (frameSelection) {
   2480    frameSelection->SetDragState(false);
   2481  }
   2482  if (sCapturingContentInfo.mContent &&
   2483      sCapturingContentInfo.mContent->OwnerDoc() == mDocument) {
   2484    PresShell::ReleaseCapturingContent();
   2485  }
   2486 }
   2487 
   2488 void PresShell::BeginLoad(Document* aDocument) {
   2489  mDocumentLoading = true;
   2490 
   2491  SuppressDisplayport(true);
   2492 
   2493  gfxTextPerfMetrics* tp = nullptr;
   2494  if (mPresContext) {
   2495    tp = mPresContext->GetTextPerfMetrics();
   2496  }
   2497 
   2498  bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
   2499  if (shouldLog || tp) {
   2500    mLoadBegin = TimeStamp::Now();
   2501  }
   2502 
   2503  if (shouldLog) {
   2504    nsIURI* uri = mDocument->GetDocumentURI();
   2505    MOZ_LOG(gLog, LogLevel::Debug,
   2506            ("(presshell) %p load begin [%s]\n", this,
   2507             uri ? uri->GetSpecOrDefault().get() : ""));
   2508  }
   2509 }
   2510 
   2511 void PresShell::EndLoad(Document* aDocument) {
   2512  MOZ_ASSERT(aDocument == mDocument, "Wrong document");
   2513 
   2514  SuppressDisplayport(false);
   2515  RestoreRootScrollPosition();
   2516 
   2517  mDocumentLoading = false;
   2518 }
   2519 
   2520 void PresShell::LoadComplete() {
   2521  gfxTextPerfMetrics* tp = nullptr;
   2522  if (mPresContext) {
   2523    tp = mPresContext->GetTextPerfMetrics();
   2524  }
   2525 
   2526  // log load
   2527  bool shouldLog = MOZ_LOG_TEST(gLog, LogLevel::Debug);
   2528  if (shouldLog || tp) {
   2529    TimeDuration loadTime = TimeStamp::Now() - mLoadBegin;
   2530    nsIURI* uri = mDocument->GetDocumentURI();
   2531    nsAutoCString spec;
   2532    if (uri) {
   2533      spec = uri->GetSpecOrDefault();
   2534    }
   2535    if (shouldLog) {
   2536      MOZ_LOG(gLog, LogLevel::Debug,
   2537              ("(presshell) %p load done time-ms: %9.2f [%s]\n", this,
   2538               loadTime.ToMilliseconds(), spec.get()));
   2539    }
   2540    if (tp) {
   2541      tp->Accumulate();
   2542      if (tp->cumulative.numChars > 0) {
   2543        LogTextPerfStats(tp, this, tp->cumulative, loadTime.ToMilliseconds(),
   2544                         eLog_loaddone, spec.get());
   2545      }
   2546    }
   2547  }
   2548 }
   2549 
   2550 #ifdef DEBUG
   2551 void PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame) {
   2552  // XXXbz due to bug 372769, can't actually assert anything here...
   2553  // XXX Since bug 372769 is now fixed, the assertion is being enabled in bug
   2554  //     1758104.
   2555 #  if 0
   2556  // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
   2557  // handles the root frame correctly.
   2558  if (!aFrame->GetParent()) {
   2559    return;
   2560  }
   2561 
   2562  // Make sure that there is a reflow root ancestor of |aFrame| that's
   2563  // in mDirtyRoots already.
   2564  while (aFrame && aFrame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN)) {
   2565    if ((aFrame->HasAnyStateBits(NS_FRAME_REFLOW_ROOT |
   2566                                 NS_FRAME_DYNAMIC_REFLOW_ROOT) ||
   2567         !aFrame->GetParent()) &&
   2568        mDirtyRoots.Contains(aFrame)) {
   2569      return;
   2570    }
   2571 
   2572    aFrame = aFrame->GetParent();
   2573  }
   2574 
   2575  MOZ_ASSERT_UNREACHABLE(
   2576      "Frame has dirty bits set but isn't scheduled to be "
   2577      "reflowed?");
   2578 #  endif
   2579 }
   2580 #endif
   2581 
   2582 void PresShell::PostPendingScrollAnchorSelection(
   2583    mozilla::layout::ScrollAnchorContainer* aContainer) {
   2584  mPendingScrollAnchorSelection.Insert(aContainer->ScrollContainer());
   2585 }
   2586 
   2587 void PresShell::FlushPendingScrollAnchorSelections() {
   2588  for (ScrollContainerFrame* scroll : mPendingScrollAnchorSelection) {
   2589    scroll->Anchor()->SelectAnchor();
   2590  }
   2591  mPendingScrollAnchorSelection.Clear();
   2592 }
   2593 
   2594 void PresShell::PostPendingScrollAnchorAdjustment(
   2595    ScrollAnchorContainer* aContainer) {
   2596  mPendingScrollAnchorAdjustment.Insert(aContainer->ScrollContainer());
   2597 }
   2598 
   2599 void PresShell::FlushPendingScrollAnchorAdjustments() {
   2600  for (ScrollContainerFrame* scroll : mPendingScrollAnchorAdjustment) {
   2601    scroll->Anchor()->ApplyAdjustments();
   2602  }
   2603  mPendingScrollAnchorAdjustment.Clear();
   2604 }
   2605 
   2606 void PresShell::PostPendingScrollResnap(
   2607    ScrollContainerFrame* aScrollContainerFrame) {
   2608  mPendingScrollResnap.Insert(aScrollContainerFrame);
   2609 }
   2610 
   2611 void PresShell::FlushPendingScrollResnap() {
   2612  for (ScrollContainerFrame* scrollContainerFrame : mPendingScrollResnap) {
   2613    scrollContainerFrame->TryResnap();
   2614  }
   2615  mPendingScrollResnap.Clear();
   2616 }
   2617 
   2618 void PresShell::FrameNeedsReflow(nsIFrame* aFrame,
   2619                                 IntrinsicDirty aIntrinsicDirty,
   2620                                 nsFrameState aBitToAdd,
   2621                                 ReflowRootHandling aRootHandling) {
   2622  MOZ_ASSERT(aBitToAdd == NS_FRAME_IS_DIRTY ||
   2623                 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN || !aBitToAdd,
   2624             "Unexpected bits being added");
   2625 
   2626  // FIXME bug 478135
   2627  NS_ASSERTION(
   2628      aIntrinsicDirty != IntrinsicDirty::FrameAncestorsAndDescendants ||
   2629          aBitToAdd != NS_FRAME_HAS_DIRTY_CHILDREN,
   2630      "bits don't correspond to style change reason");
   2631 
   2632  // FIXME bug 457400
   2633  NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
   2634 
   2635  // If we've not yet done the initial reflow, then don't bother
   2636  // enqueuing a reflow command yet.
   2637  if (!mDidInitialize) {
   2638    return;
   2639  }
   2640 
   2641  // If we're already destroying, don't bother with this either.
   2642  if (mIsDestroying) {
   2643    return;
   2644  }
   2645 
   2646  AutoTArray<nsIFrame*, 4> subtrees;
   2647  subtrees.AppendElement(aFrame);
   2648 
   2649  do {
   2650    nsIFrame* subtreeRoot = subtrees.PopLastElement();
   2651 
   2652    // Grab |wasDirty| now so we can go ahead and update the bits on
   2653    // subtreeRoot.
   2654    bool wasDirty = subtreeRoot->IsSubtreeDirty();
   2655    subtreeRoot->AddStateBits(aBitToAdd);
   2656 
   2657    // Determine whether we need to keep looking for the next ancestor
   2658    // reflow root if subtreeRoot itself is a reflow root.
   2659    bool targetNeedsReflowFromParent;
   2660    switch (aRootHandling) {
   2661      case ReflowRootHandling::PositionOrSizeChange:
   2662        targetNeedsReflowFromParent = true;
   2663        break;
   2664      case ReflowRootHandling::NoPositionOrSizeChange:
   2665        targetNeedsReflowFromParent = false;
   2666        break;
   2667      case ReflowRootHandling::InferFromBitToAdd:
   2668        targetNeedsReflowFromParent = (aBitToAdd == NS_FRAME_IS_DIRTY);
   2669        break;
   2670    }
   2671 
   2672    auto FrameIsReflowRoot = [](const nsIFrame* aFrame) {
   2673      return aFrame->HasAnyStateBits(NS_FRAME_REFLOW_ROOT |
   2674                                     NS_FRAME_DYNAMIC_REFLOW_ROOT);
   2675    };
   2676 
   2677    auto CanStopClearingAncestorIntrinsics = [&](const nsIFrame* aFrame) {
   2678      return FrameIsReflowRoot(aFrame) && aFrame != subtreeRoot;
   2679    };
   2680 
   2681    auto IsReflowBoundary = [&](const nsIFrame* aFrame) {
   2682      return FrameIsReflowRoot(aFrame) &&
   2683             (aFrame != subtreeRoot || !targetNeedsReflowFromParent);
   2684    };
   2685 
   2686    // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
   2687    // and all of its descendants, if needed:
   2688 
   2689    if (aIntrinsicDirty != IntrinsicDirty::None) {
   2690      // Mark argument and all ancestors dirty. (Unless we hit a reflow root
   2691      // that should contain the reflow.
   2692      for (nsIFrame* a = subtreeRoot;
   2693           a && !CanStopClearingAncestorIntrinsics(a); a = a->GetParent()) {
   2694        a->MarkIntrinsicISizesDirty();
   2695        if (a->IsAbsolutelyPositioned()) {
   2696          // If we get here, 'a' is abspos, so its subtree's intrinsic sizing
   2697          // has no effect on its ancestors' intrinsic sizing. So, don't loop
   2698          // upwards any further.
   2699          break;
   2700        }
   2701      }
   2702    }
   2703 
   2704    const bool frameAncestorAndDescendantISizesDirty =
   2705        (aIntrinsicDirty == IntrinsicDirty::FrameAncestorsAndDescendants);
   2706    const bool dirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
   2707    if (frameAncestorAndDescendantISizesDirty || dirty) {
   2708      // Mark all descendants dirty (using an nsTArray stack rather than
   2709      // recursion).
   2710      // Note that ReflowInput::InitResizeFlags has some similar
   2711      // code; see comments there for how and why it differs.
   2712      AutoTArray<nsIFrame*, 32> stack;
   2713      stack.AppendElement(subtreeRoot);
   2714 
   2715      do {
   2716        nsIFrame* f = stack.PopLastElement();
   2717 
   2718        if (frameAncestorAndDescendantISizesDirty && f->IsPlaceholderFrame()) {
   2719          // Call `GetOutOfFlowFrame` directly because we can get here from
   2720          // frame destruction and the placeholder might be already torn down.
   2721          if (nsIFrame* oof =
   2722                  static_cast<nsPlaceholderFrame*>(f)->GetOutOfFlowFrame()) {
   2723            if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
   2724              // We have another distinct subtree we need to mark.
   2725              subtrees.AppendElement(oof);
   2726            }
   2727          }
   2728        }
   2729 
   2730        for (const auto& childList : f->ChildLists()) {
   2731          for (nsIFrame* kid : childList.mList) {
   2732            if (frameAncestorAndDescendantISizesDirty) {
   2733              kid->MarkIntrinsicISizesDirty();
   2734            }
   2735            if (dirty) {
   2736              kid->AddStateBits(NS_FRAME_IS_DIRTY);
   2737            }
   2738            stack.AppendElement(kid);
   2739          }
   2740        }
   2741      } while (stack.Length() != 0);
   2742    }
   2743 
   2744    // Skip setting dirty bits up the tree if we weren't given a bit to add.
   2745    if (!aBitToAdd) {
   2746      continue;
   2747    }
   2748 
   2749    // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
   2750    // up the tree until we reach either a frame that's already dirty or
   2751    // a reflow root.
   2752    nsIFrame* f = subtreeRoot;
   2753    for (;;) {
   2754      if (IsReflowBoundary(f) || !f->GetParent()) {
   2755        // we've hit a reflow root or the root frame
   2756        if (!wasDirty) {
   2757          mDirtyRoots.Add(f);
   2758          SetNeedLayoutFlush();
   2759        }
   2760 #ifdef DEBUG
   2761        else {
   2762          VerifyHasDirtyRootAncestor(f);
   2763        }
   2764 #endif
   2765 
   2766        break;
   2767      }
   2768 
   2769      nsIFrame* child = f;
   2770      f = f->GetParent();
   2771      wasDirty = f->IsSubtreeDirty();
   2772      f->ChildIsDirty(child);
   2773      NS_ASSERTION(f->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN),
   2774                   "ChildIsDirty didn't do its job");
   2775      if (wasDirty) {
   2776        // This frame was already marked dirty.
   2777 #ifdef DEBUG
   2778        VerifyHasDirtyRootAncestor(f);
   2779 #endif
   2780        break;
   2781      }
   2782    }
   2783  } while (subtrees.Length() != 0);
   2784 
   2785  EnsureLayoutFlush();
   2786 }
   2787 
   2788 void PresShell::FrameNeedsToContinueReflow(nsIFrame* aFrame) {
   2789  NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
   2790  MOZ_ASSERT(mCurrentReflowRoot, "Must have a current reflow root here");
   2791  NS_ASSERTION(
   2792      aFrame == mCurrentReflowRoot ||
   2793          nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
   2794      "Frame passed in is not the descendant of mCurrentReflowRoot");
   2795  NS_ASSERTION(aFrame->HasAnyStateBits(NS_FRAME_IN_REFLOW),
   2796               "Frame passed in not in reflow?");
   2797 
   2798  mFramesToDirty.Insert(aFrame);
   2799 }
   2800 
   2801 already_AddRefed<nsIContent> PresShell::GetContentForScrolling() const {
   2802  if (nsCOMPtr<nsIContent> focused = GetFocusedContentInOurWindow()) {
   2803    return focused.forget();
   2804  }
   2805  return GetSelectedContentForScrolling();
   2806 }
   2807 
   2808 already_AddRefed<nsIContent> PresShell::GetSelectedContentForScrolling() const {
   2809  nsCOMPtr<nsIContent> selectedContent;
   2810  if (mSelection) {
   2811    Selection& domSelection = mSelection->NormalSelection();
   2812    selectedContent = nsIContent::FromNodeOrNull(domSelection.GetFocusNode());
   2813  }
   2814  return selectedContent.forget();
   2815 }
   2816 
   2817 ScrollContainerFrame* PresShell::GetScrollContainerFrameToScrollForContent(
   2818    nsIContent* aContent, ScrollDirections aDirections) {
   2819  ScrollContainerFrame* scrollContainerFrame = nullptr;
   2820  if (aContent) {
   2821    nsIFrame* startFrame = aContent->GetPrimaryFrame();
   2822    if (startFrame) {
   2823      scrollContainerFrame = startFrame->GetScrollTargetFrame();
   2824      if (scrollContainerFrame) {
   2825        startFrame = scrollContainerFrame->GetScrolledFrame();
   2826      }
   2827      scrollContainerFrame =
   2828          nsLayoutUtils::GetNearestScrollableFrameForDirection(startFrame,
   2829                                                               aDirections);
   2830    }
   2831  }
   2832  if (!scrollContainerFrame) {
   2833    scrollContainerFrame = GetRootScrollContainerFrame();
   2834    if (!scrollContainerFrame || !scrollContainerFrame->GetScrolledFrame()) {
   2835      return nullptr;
   2836    }
   2837    scrollContainerFrame = nsLayoutUtils::GetNearestScrollableFrameForDirection(
   2838        scrollContainerFrame->GetScrolledFrame(), aDirections);
   2839  }
   2840  return scrollContainerFrame;
   2841 }
   2842 
   2843 ScrollContainerFrame* PresShell::GetScrollContainerFrameToScroll(
   2844    ScrollDirections aDirections) {
   2845  nsCOMPtr<nsIContent> content = GetContentForScrolling();
   2846  return GetScrollContainerFrameToScrollForContent(content.get(), aDirections);
   2847 }
   2848 
   2849 void PresShell::CancelAllPendingReflows() { mDirtyRoots.Clear(); }
   2850 
   2851 static bool DestroyFramesAndStyleDataFor(
   2852    Element* aElement, nsPresContext& aPresContext,
   2853    RestyleManager::IncludeRoot aIncludeRoot) {
   2854  bool didReconstruct =
   2855      aPresContext.FrameConstructor()->DestroyFramesFor(aElement);
   2856  RestyleManager::ClearServoDataFromSubtree(aElement, aIncludeRoot);
   2857  return didReconstruct;
   2858 }
   2859 
   2860 void PresShell::SlotAssignmentWillChange(Element& aElement,
   2861                                         HTMLSlotElement* aOldSlot,
   2862                                         HTMLSlotElement* aNewSlot) {
   2863  MOZ_ASSERT(aOldSlot != aNewSlot);
   2864 
   2865  if (MOZ_UNLIKELY(!mDidInitialize)) {
   2866    return;
   2867  }
   2868 
   2869  // If the old slot is about to become empty and show fallback, let layout know
   2870  // that it needs to do work.
   2871  if (aOldSlot && aOldSlot->AssignedNodes().Length() == 1 &&
   2872      aOldSlot->HasChildren()) {
   2873    DestroyFramesForAndRestyle(aOldSlot);
   2874  }
   2875 
   2876  // Ensure the new element starts off clean.
   2877  DestroyFramesAndStyleDataFor(&aElement, *mPresContext,
   2878                               RestyleManager::IncludeRoot::Yes);
   2879 
   2880  if (aNewSlot) {
   2881    // If the new slot will stop showing fallback content, we need to reframe it
   2882    // altogether.
   2883    if (aNewSlot->AssignedNodes().IsEmpty() && aNewSlot->HasChildren()) {
   2884      DestroyFramesForAndRestyle(aNewSlot);
   2885      // Otherwise we just care about the element, but we need to ensure that
   2886      // something takes care of traversing to the relevant slot, if needed.
   2887    } else if (aNewSlot->HasServoData() &&
   2888               !Servo_Element_IsDisplayNone(aNewSlot)) {
   2889      // Set the reframe bits...
   2890      aNewSlot->NoteDescendantsNeedFramesForServo();
   2891      aElement.SetFlags(NODE_NEEDS_FRAME);
   2892      // Now the style dirty bits. Note that we can't just do
   2893      // aElement.NoteDirtyForServo(), because the new slot is not setup yet.
   2894      aNewSlot->SetHasDirtyDescendantsForServo();
   2895      aNewSlot->NoteDirtySubtreeForServo();
   2896    }
   2897  }
   2898 }
   2899 
   2900 #ifdef DEBUG
   2901 static void AssertNoFramesOrStyleDataInDescendants(Element& aElement) {
   2902  for (nsINode* node : ShadowIncludingTreeIterator(aElement)) {
   2903    nsIContent* c = nsIContent::FromNode(node);
   2904    if (c == &aElement) {
   2905      continue;
   2906    }
   2907    // FIXME(emilio): The <area> check is needed because of bug 135040.
   2908    MOZ_ASSERT(!c->GetPrimaryFrame() || c->IsHTMLElement(nsGkAtoms::area));
   2909    MOZ_ASSERT(!c->IsElement() || !c->AsElement()->HasServoData());
   2910  }
   2911 }
   2912 #endif
   2913 
   2914 void PresShell::DestroyFramesForAndRestyle(Element* aElement) {
   2915 #ifdef DEBUG
   2916  auto postCondition = MakeScopeExit([&]() {
   2917    MOZ_ASSERT(!aElement->GetPrimaryFrame());
   2918    AssertNoFramesOrStyleDataInDescendants(*aElement);
   2919  });
   2920 #endif
   2921 
   2922  MOZ_ASSERT(aElement);
   2923  if (!aElement->HasServoData()) {
   2924    // Nothing to do here, the element already is out of the flat tree or is not
   2925    // styled.
   2926    return;
   2927  }
   2928 
   2929  // Mark ourselves as not safe to flush while we're doing frame destruction.
   2930  nsAutoScriptBlocker scriptBlocker;
   2931  ++mChangeNestCount;
   2932 
   2933  const bool didReconstruct = FrameConstructor()->DestroyFramesFor(aElement);
   2934  // Clear the style data from all the flattened tree descendants, but _not_
   2935  // from us, since otherwise we wouldn't see the reframe.
   2936  RestyleManager::ClearServoDataFromSubtree(aElement,
   2937                                            RestyleManager::IncludeRoot::No);
   2938  auto changeHint =
   2939      didReconstruct ? nsChangeHint(0) : nsChangeHint_ReconstructFrame;
   2940  mPresContext->RestyleManager()->PostRestyleEvent(
   2941      aElement, RestyleHint::RestyleSubtree(), changeHint);
   2942 
   2943  --mChangeNestCount;
   2944 }
   2945 
   2946 void PresShell::ShadowRootWillBeAttached(Element& aElement) {
   2947 #ifdef DEBUG
   2948  auto postCondition = MakeScopeExit(
   2949      [&]() { AssertNoFramesOrStyleDataInDescendants(aElement); });
   2950 #endif
   2951 
   2952  if (!aElement.HasServoData()) {
   2953    // Nothing to do here, the element already is out of the flat tree or is not
   2954    // styled.
   2955    return;
   2956  }
   2957 
   2958  if (!aElement.HasChildren()) {
   2959    // The element has no children, just avoid the work.
   2960    return;
   2961  }
   2962 
   2963  // Mark ourselves as not safe to flush while we're doing frame destruction.
   2964  nsAutoScriptBlocker scriptBlocker;
   2965  ++mChangeNestCount;
   2966 
   2967  // NOTE(emilio): We use FlattenedChildIterator intentionally here (rather than
   2968  // StyleChildrenIterator), since we don't want to remove ::before / ::after
   2969  // content.
   2970  FlattenedChildIterator iter(&aElement);
   2971  nsCSSFrameConstructor* fc = FrameConstructor();
   2972  for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
   2973    fc->DestroyFramesFor(c);
   2974    if (c->IsElement()) {
   2975      RestyleManager::ClearServoDataFromSubtree(c->AsElement());
   2976    }
   2977  }
   2978 
   2979 #ifdef ACCESSIBILITY
   2980  if (nsAccessibilityService* accService = GetAccService()) {
   2981    accService->ScheduleAccessibilitySubtreeUpdate(this, &aElement);
   2982  }
   2983 #endif
   2984 
   2985  --mChangeNestCount;
   2986 }
   2987 
   2988 void PresShell::PostRecreateFramesFor(Element* aElement) {
   2989  if (MOZ_UNLIKELY(!mDidInitialize)) {
   2990    // Nothing to do here. In fact, if we proceed and aElement is the root, we
   2991    // will crash.
   2992    return;
   2993  }
   2994 
   2995  mPresContext->RestyleManager()->PostRestyleEvent(
   2996      aElement, RestyleHint{0}, nsChangeHint_ReconstructFrame);
   2997 }
   2998 
   2999 void PresShell::RestyleForAnimation(Element* aElement, RestyleHint aHint) {
   3000  // Now that we no longer have separate non-animation and animation
   3001  // restyles, this method having a distinct identity is less important,
   3002  // but it still seems useful to offer as a "more public" API and as a
   3003  // checkpoint for these restyles to go through.
   3004  mPresContext->RestyleManager()->PostRestyleEvent(aElement, aHint,
   3005                                                   nsChangeHint(0));
   3006 }
   3007 
   3008 void PresShell::SetForwardingContainer(const WeakPtr<nsDocShell>& aContainer) {
   3009  mForwardingContainer = aContainer;
   3010 }
   3011 
   3012 void PresShell::ClearFrameRefs(nsIFrame* aFrame) {
   3013  mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
   3014 
   3015  AutoWeakFrame* weakFrame = mAutoWeakFrames;
   3016  while (weakFrame) {
   3017    AutoWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
   3018    if (weakFrame->GetFrame() == aFrame) {
   3019      // This removes weakFrame from mAutoWeakFrames.
   3020      weakFrame->Clear(this);
   3021    }
   3022    weakFrame = prev;
   3023  }
   3024 
   3025  AutoTArray<WeakFrame*, 4> toRemove;
   3026  for (WeakFrame* weakFrame : mWeakFrames) {
   3027    if (weakFrame->GetFrame() == aFrame) {
   3028      toRemove.AppendElement(weakFrame);
   3029    }
   3030  }
   3031  for (WeakFrame* weakFrame : toRemove) {
   3032    weakFrame->Clear(this);
   3033  }
   3034 }
   3035 
   3036 UniquePtr<gfxContext> PresShell::CreateReferenceRenderingContext() {
   3037  if (mPresContext->IsScreen()) {
   3038    return gfxContext::CreateOrNull(
   3039        gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
   3040  }
   3041 
   3042  // We assume the devCtx has positive width and height for this call.
   3043  // However, width and height, may be outside of the reasonable range
   3044  // so rc may still be null.
   3045  nsDeviceContext* devCtx = mPresContext->DeviceContext();
   3046  return devCtx->CreateReferenceRenderingContext();
   3047 }
   3048 
   3049 // https://html.spec.whatwg.org/#scroll-to-the-fragment-identifier
   3050 nsresult PresShell::GoToAnchor(const nsAString& aAnchorName,
   3051                               const nsRange* aFirstTextDirective, bool aScroll,
   3052                               ScrollFlags aAdditionalScrollFlags) {
   3053  if (!mDocument) {
   3054    return NS_ERROR_FAILURE;
   3055  }
   3056 
   3057  if (mDocument->GetSVGRootElement()) {
   3058    // We need to execute this even if there is an empty anchor name
   3059    // so that any existing SVG fragment identifier effect is removed
   3060    if (SVGFragmentIdentifier::ProcessFragmentIdentifier(mDocument,
   3061                                                         aAnchorName)) {
   3062      return NS_OK;
   3063    }
   3064  }
   3065 
   3066  // Hold a reference to the ESM in case event dispatch tears us down.
   3067  RefPtr<EventStateManager> esm = mPresContext->EventStateManager();
   3068 
   3069  // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
   3070  // From "Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:"
   3071  // 3.4. If target is a range, then:
   3072  // 3.4.1 Set target to be the first common ancestor of target's start node and
   3073  //       end node.
   3074  // 3.4.2 While target is non-null and is not an element, set target to
   3075  //       target's parent.
   3076  // ------
   3077  // Common closest ancestor is not suitable here, as it can scroll to positions
   3078  // where no text directive is visible. Instead, scroll to the start container
   3079  // of the text directive.
   3080  // see https://bugzil.la/1906895 and
   3081  // https://github.com/WICG/scroll-to-text-fragment/issues/259
   3082  Element* textFragmentTargetElement = [&aFirstTextDirective]() -> Element* {
   3083    nsINode* node = aFirstTextDirective
   3084                        ? aFirstTextDirective->GetStartContainer()
   3085                        : nullptr;
   3086    while (node && !node->IsElement()) {
   3087      node = node->GetParent();
   3088    }
   3089    return Element::FromNodeOrNull(node);
   3090  }();
   3091  const bool thereIsATextFragment = !!textFragmentTargetElement;
   3092 
   3093  // 1. If there is no indicated part of the document, set the Document's target
   3094  //    element to null.
   3095  //
   3096  // FIXME(emilio): Per spec empty fragment string should take the same
   3097  // code-path as "top"!
   3098  if (aAnchorName.IsEmpty() && !thereIsATextFragment) {
   3099    NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
   3100    esm->SetContentState(nullptr, ElementState::URLTARGET);
   3101    return NS_OK;
   3102  }
   3103 
   3104  // 2. If the indicated part of the document is the top of the document,
   3105  // then:
   3106  // (handled below when `target` is null, and anchor is `top`)
   3107 
   3108  // 3.1. Let target be element that is the indicated part of the document.
   3109  //
   3110  // https://html.spec.whatwg.org/#target-element
   3111  // https://html.spec.whatwg.org/#find-a-potential-indicated-element
   3112  RefPtr<Element> target = textFragmentTargetElement;
   3113  if (!target) {
   3114    target = nsContentUtils::GetTargetElement(mDocument, aAnchorName);
   3115  }
   3116 
   3117  // 1. If there is no indicated part of the document, set the Document's
   3118  //    target element to null.
   3119  // 2.1. Set the Document's target element to null.
   3120  // 3.2. Set the Document's target element to target.
   3121  esm->SetContentState(target, ElementState::URLTARGET);
   3122 
   3123  // TODO: Spec probably needs a section to account for this.
   3124  if (ScrollContainerFrame* rootScroll = GetRootScrollContainerFrame()) {
   3125    if (rootScroll->DidHistoryRestore()) {
   3126      // Scroll position restored from history trumps scrolling to anchor.
   3127      aScroll = false;
   3128      rootScroll->ClearDidHistoryRestore();
   3129    }
   3130  }
   3131 
   3132  if (target) {
   3133    // 3.4 Run the ancestor revealing algorithm on target.
   3134    ErrorResult rv;
   3135    target->AncestorRevealingAlgorithm(rv);
   3136    if (MOZ_UNLIKELY(rv.Failed())) {
   3137      return rv.StealNSResult();
   3138    }
   3139 
   3140    if (aScroll) {
   3141      // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
   3142      // From "Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:"
   3143      // 3.9 Let blockPosition be "center" if scrollTarget is a range, "start"
   3144      //     otherwise.
   3145      // Implementation note: Use `ScrollSelectionIntoView` for text fragment,
   3146      // since the text fragment is stored as a `eTargetText` selection.
   3147      //
   3148      // 3.4. Scroll target into view, with behavior set to "auto", block set to
   3149      //      "start", and inline set to "nearest".
   3150      // FIXME(emilio): Not all callers pass ScrollSmoothAuto (but we use auto
   3151      // smooth scroll for `top` regardless below, so maybe they should!).
   3152      ScrollingInteractionContext scrollToAnchorContext(true);
   3153      if (thereIsATextFragment) {
   3154        MOZ_TRY(ScrollSelectionIntoView(
   3155            SelectionType::eTargetText,
   3156            nsISelectionController::SELECTION_ANCHOR_REGION,
   3157            ScrollAxis(WhereToScroll::Center, WhenToScroll::Always),
   3158            ScrollAxis(),
   3159            ScrollFlags::AnchorScrollFlags | aAdditionalScrollFlags,
   3160            SelectionScrollMode::SyncFlush));
   3161      } else {
   3162        MOZ_TRY(ScrollContentIntoView(
   3163            target, ScrollAxis(WhereToScroll::Start, WhenToScroll::Always),
   3164            ScrollAxis(),
   3165            ScrollFlags::AnchorScrollFlags | aAdditionalScrollFlags));
   3166      }
   3167      if (ScrollContainerFrame* rootScroll = GetRootScrollContainerFrame()) {
   3168        mLastAnchorScrolledTo = target;
   3169        mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
   3170        mLastAnchorScrollType = thereIsATextFragment
   3171                                    ? AnchorScrollType::TextDirective
   3172                                    : AnchorScrollType::Anchor;
   3173      }
   3174    }
   3175 
   3176    {
   3177      // 3.6. Move the sequential focus navigation starting point to target.
   3178      //
   3179      // Move the caret to the anchor. That way tabbing will start from the new
   3180      // location.
   3181      //
   3182      // TODO(emilio): Do we want to do this even if aScroll is false?
   3183      //
   3184      // NOTE: Intentionally out of order for now with the focus steps, see
   3185      // https://github.com/whatwg/html/issues/7759
   3186      RefPtr<nsRange> jumpToRange = nsRange::Create(mDocument);
   3187      nsCOMPtr<nsIContent> nodeToSelect = target.get();
   3188      while (nodeToSelect->GetFirstChild()) {
   3189        nodeToSelect = nodeToSelect->GetFirstChild();
   3190      }
   3191      jumpToRange->SelectNodeContents(*nodeToSelect, IgnoreErrors());
   3192      RefPtr sel = &mSelection->NormalSelection();
   3193      MOZ_ASSERT(sel);
   3194      sel->RemoveAllRanges(IgnoreErrors());
   3195      sel->AddRangeAndSelectFramesAndNotifyListeners(*jumpToRange,
   3196                                                     IgnoreErrors());
   3197      if (!StaticPrefs::layout_selectanchor()) {
   3198        // Use a caret (collapsed selection) at the start of the anchor.
   3199        sel->CollapseToStart(IgnoreErrors());
   3200      }
   3201    }
   3202 
   3203    // 3.5. Run the focusing steps for target, with the Document's viewport as
   3204    // the fallback target.
   3205    //
   3206    // Note that ScrollContentIntoView flushes, so we don't need to do that
   3207    // again here. We also don't need to scroll again either.
   3208    //
   3209    // We intentionally focus the target only when aScroll is true, we need to
   3210    // sort out if the spec needs to differentiate these cases. When aScroll is
   3211    // false we still clear the focus unconditionally, that's legacy behavior,
   3212    // maybe we shouldn't do it.
   3213    //
   3214    // TODO(emilio): Do we really want to clear the focus even if aScroll is
   3215    // false?
   3216    const bool shouldFocusTarget = [&] {
   3217      if (!aScroll || thereIsATextFragment) {
   3218        return false;
   3219      }
   3220      nsIFrame* targetFrame = target->GetPrimaryFrame();
   3221      return targetFrame && targetFrame->IsFocusable();
   3222    }();
   3223 
   3224    if (shouldFocusTarget) {
   3225      FocusOptions options;
   3226      options.mPreventScroll = true;
   3227      target->Focus(options, CallerType::NonSystem, IgnoreErrors());
   3228    } else if (RefPtr<nsIFocusManager> fm = nsFocusManager::GetFocusManager()) {
   3229      if (nsPIDOMWindowOuter* win = mDocument->GetWindow()) {
   3230        // Now focus the document itself if focus is on an element within it.
   3231        nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
   3232        fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
   3233        if (SameCOMIdentity(win, focusedWindow)) {
   3234          fm->ClearFocus(focusedWindow);
   3235        }
   3236      }
   3237    }
   3238 
   3239    // If the target is an animation element, activate the animation
   3240    if (auto* animationElement = SVGAnimationElement::FromNode(target.get())) {
   3241      animationElement->ActivateByHyperlink();
   3242    }
   3243 
   3244 #ifdef ACCESSIBILITY
   3245    if (nsAccessibilityService* accService = GetAccService()) {
   3246      nsIContent* a11yTarget = target;
   3247      if (thereIsATextFragment) {
   3248        // A text fragment starts in a text leaf node. `target` is the element
   3249        // parent, but there may be many other children of that element before
   3250        // the start of the text fragment. Explicitly use the start leaf node
   3251        // here to get a11y clients as close as possible to the fragment (on
   3252        // platforms which support this).
   3253        a11yTarget = nsIContent::FromNodeOrNull(
   3254            aFirstTextDirective->GetStartContainer());
   3255        if (!a11yTarget) {
   3256          a11yTarget = target;
   3257        }
   3258      }
   3259      accService->NotifyOfAnchorJumpTo(a11yTarget);
   3260    }
   3261 #endif
   3262  } else if (nsContentUtils::EqualsIgnoreASCIICase(aAnchorName, u"top"_ns)) {
   3263    // 2.2. Scroll to the beginning of the document for the Document.
   3264    ScrollContainerFrame* sf = GetRootScrollContainerFrame();
   3265    // Check |aScroll| after setting |rv| so we set |rv| to the same
   3266    // thing whether or not |aScroll| is true.
   3267    if (aScroll && sf) {
   3268      ScrollMode scrollMode = sf->ScrollModeForScrollBehavior();
   3269      // Scroll to the top of the page
   3270      sf->ScrollTo(nsPoint(0, 0), scrollMode);
   3271    }
   3272  } else {
   3273    return NS_ERROR_FAILURE;
   3274  }
   3275 
   3276  return NS_OK;
   3277 }
   3278 
   3279 nsresult PresShell::ScrollToAnchor() {
   3280  NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
   3281  if (mLastAnchorScrollType == AnchorScrollType::Anchor) {
   3282    nsCOMPtr<nsIContent> lastAnchor = std::move(mLastAnchorScrolledTo);
   3283    if (!lastAnchor) {
   3284      return NS_OK;
   3285    }
   3286 
   3287    ScrollContainerFrame* rootScroll = GetRootScrollContainerFrame();
   3288    if (!rootScroll ||
   3289        mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) {
   3290      return NS_OK;
   3291    }
   3292    return ScrollContentIntoView(
   3293        lastAnchor, ScrollAxis(WhereToScroll::Start, WhenToScroll::Always),
   3294        ScrollAxis(), ScrollFlags::AnchorScrollFlags);
   3295  }
   3296 
   3297  return ScrollSelectionIntoView(
   3298      SelectionType::eTargetText,
   3299      nsISelectionController::SELECTION_ANCHOR_REGION,
   3300      ScrollAxis(WhereToScroll::Center, WhenToScroll::Always), ScrollAxis(),
   3301      ScrollFlags::AnchorScrollFlags, SelectionScrollMode::SyncFlush);
   3302 }
   3303 
   3304 /*
   3305 * Helper (per-continuation) for ScrollContentIntoView.
   3306 *
   3307 * @param aContainerFrame [in] the frame which aRect is relative to
   3308 * @param aFrame [in] Frame whose bounds should be unioned
   3309 * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
   3310 * we should include the top of the line in the added rectangle
   3311 * @param aRect [inout] rect into which its bounds should be unioned
   3312 * @param aHaveRect [inout] whether aRect contains data yet
   3313 * @param aPrevBlock [inout] the block aLines is a line iterator for
   3314 * @param aLines [inout] the line iterator we're using
   3315 * @param aCurLine [inout] the line to start looking from in this iterator
   3316 */
   3317 static void AccumulateFrameBounds(nsIFrame* aContainerFrame, nsIFrame* aFrame,
   3318                                  bool aUseWholeLineHeightForInlines,
   3319                                  nsRect& aRect, bool& aHaveRect,
   3320                                  nsIFrame*& aPrevBlock,
   3321                                  nsILineIterator*& aLines, int32_t& aCurLine) {
   3322  nsIFrame* frame = aFrame;
   3323  nsRect frameBounds = nsRect(nsPoint(0, 0), aFrame->GetSize());
   3324 
   3325  // If this is an inline frame and either the bounds height is 0 (quirks
   3326  // layout model) or aUseWholeLineHeightForInlines is set, we need to
   3327  // change the top of the bounds to include the whole line.
   3328  if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
   3329    nsIFrame* prevFrame = aFrame;
   3330    nsIFrame* f = aFrame;
   3331 
   3332    while (f && f->IsLineParticipant() && !f->IsTransformed() &&
   3333           !f->IsAbsPosContainingBlock()) {
   3334      prevFrame = f;
   3335      f = prevFrame->GetParent();
   3336    }
   3337 
   3338    if (f != aFrame && f && f->IsBlockFrame()) {
   3339      // find the line containing aFrame and increase the top of |offset|.
   3340      if (f != aPrevBlock) {
   3341        aLines = f->GetLineIterator();
   3342        aPrevBlock = f;
   3343        aCurLine = 0;
   3344      }
   3345      if (aLines) {
   3346        int32_t index = aLines->FindLineContaining(prevFrame, aCurLine);
   3347        if (index >= 0) {
   3348          auto line = aLines->GetLine(index).unwrap();
   3349          frameBounds += frame->GetOffsetTo(f);
   3350          frame = f;
   3351          if (line.mLineBounds.y < frameBounds.y) {
   3352            frameBounds.height = frameBounds.YMost() - line.mLineBounds.y;
   3353            frameBounds.y = line.mLineBounds.y;
   3354          }
   3355        }
   3356      }
   3357    }
   3358  }
   3359 
   3360  nsRect transformedBounds = nsLayoutUtils::TransformFrameRectToAncestor(
   3361      frame, frameBounds, aContainerFrame);
   3362 
   3363  if (aHaveRect) {
   3364    // We can't use nsRect::UnionRect since it drops empty rects on
   3365    // the floor, and we need to include them.  (Thus we need
   3366    // aHaveRect to know when to drop the initial value on the floor.)
   3367    aRect = aRect.UnionEdges(transformedBounds);
   3368  } else {
   3369    aHaveRect = true;
   3370    aRect = transformedBounds;
   3371  }
   3372 }
   3373 
   3374 static bool ComputeNeedToScroll(WhenToScroll aWhenToScroll, nscoord aLineSize,
   3375                                nscoord aRectMin, nscoord aRectMax,
   3376                                nscoord aViewMin, nscoord aViewMax) {
   3377  // See how the rect should be positioned in a given axis.
   3378  switch (aWhenToScroll) {
   3379    case WhenToScroll::Always:
   3380      // The caller wants the frame as visible as possible
   3381      return true;
   3382    case WhenToScroll::IfNotVisible:
   3383      if (aLineSize > (aRectMax - aRectMin)) {
   3384        // If the line size is greater than the size of the rect
   3385        // to scroll into view, do not use the line size to determine
   3386        // if we need to scroll.
   3387        aLineSize = 0;
   3388      }
   3389 
   3390      // Scroll only if no part of the frame is visible in this view.
   3391      return aRectMax - aLineSize <= aViewMin ||
   3392             aRectMin + aLineSize >= aViewMax;
   3393    case WhenToScroll::IfNotFullyVisible:
   3394      // Scroll only if part of the frame is hidden and more can fit in view
   3395      return !(aRectMin >= aViewMin && aRectMax <= aViewMax) &&
   3396             std::min(aViewMax, aRectMax) - std::max(aRectMin, aViewMin) <
   3397                 aViewMax - aViewMin;
   3398  }
   3399  return false;
   3400 }
   3401 
   3402 static nscoord ComputeWhereToScroll(WhereToScroll aWhereToScroll,
   3403                                    nscoord aOriginalCoord, nscoord aRectMin,
   3404                                    nscoord aRectMax, nscoord aViewMin,
   3405                                    nscoord aViewMax, nscoord* aRangeMin,
   3406                                    nscoord* aRangeMax) {
   3407  nscoord resultCoord = aOriginalCoord;
   3408  nscoord scrollPortLength = aViewMax - aViewMin;
   3409  if (!aWhereToScroll.mPercentage) {
   3410    // Scroll the minimum amount necessary to show as much as possible of the
   3411    // frame. If the frame is too large, don't hide any initially visible part
   3412    // of it.
   3413    nscoord min = std::min(aRectMin, aRectMax - scrollPortLength);
   3414    nscoord max = std::max(aRectMin, aRectMax - scrollPortLength);
   3415    resultCoord = std::clamp(aOriginalCoord, min, max);
   3416  } else {
   3417    float percent = aWhereToScroll.mPercentage.value() / 100.0f;
   3418    nscoord frameAlignCoord =
   3419        NSToCoordRound(aRectMin + (aRectMax - aRectMin) * percent);
   3420    resultCoord = NSToCoordRound(frameAlignCoord - scrollPortLength * percent);
   3421  }
   3422  // Force the scroll range to extend to include resultCoord.
   3423  *aRangeMin = std::min(resultCoord, aRectMax - scrollPortLength);
   3424  *aRangeMax = std::max(resultCoord, aRectMin);
   3425  return resultCoord;
   3426 }
   3427 
   3428 static WhereToScroll GetApplicableWhereToScroll(
   3429    const ScrollContainerFrame* aScrollContainerFrame,
   3430    const nsIFrame* aScrollableFrame, const nsIFrame* aTarget,
   3431    ScrollDirection aScrollDirection, WhereToScroll aOriginal) {
   3432  MOZ_ASSERT(do_QueryFrame(aScrollContainerFrame) == aScrollableFrame);
   3433  if (aTarget == aScrollableFrame) {
   3434    return aOriginal;
   3435  }
   3436 
   3437  StyleScrollSnapAlignKeyword align =
   3438      aScrollDirection == ScrollDirection::eHorizontal
   3439          ? aScrollContainerFrame->GetScrollSnapAlignFor(aTarget).first
   3440          : aScrollContainerFrame->GetScrollSnapAlignFor(aTarget).second;
   3441 
   3442  switch (align) {
   3443    case StyleScrollSnapAlignKeyword::None:
   3444      return aOriginal;
   3445    case StyleScrollSnapAlignKeyword::Start:
   3446      return WhereToScroll::Start;
   3447    case StyleScrollSnapAlignKeyword::Center:
   3448      return WhereToScroll::Center;
   3449    case StyleScrollSnapAlignKeyword::End:
   3450      return WhereToScroll::End;
   3451  }
   3452  return aOriginal;
   3453 }
   3454 
   3455 static ScrollMode GetScrollModeForScrollIntoView(
   3456    const ScrollContainerFrame* aScrollContainerFrame,
   3457    ScrollFlags aScrollFlags) {
   3458  // Default to an instant scroll, but if the scroll behavior given is "auto"
   3459  // or "smooth", use that as the specified behavior. If the user has disabled
   3460  // smooth scrolls, a given mode of "auto" or "smooth" should not result in
   3461  // a smooth scroll.
   3462  ScrollBehavior behavior = ScrollBehavior::Instant;
   3463  if (aScrollFlags & ScrollFlags::ScrollSmooth) {
   3464    behavior = ScrollBehavior::Smooth;
   3465  } else if (aScrollFlags & ScrollFlags::ScrollSmoothAuto) {
   3466    behavior = ScrollBehavior::Auto;
   3467  }
   3468  return aScrollContainerFrame->ScrollModeForScrollBehavior(behavior);
   3469 }
   3470 
   3471 /**
   3472 * This function takes a scroll container frame, a rect in the coordinate system
   3473 * of the scrolled frame, and a desired percentage-based scroll
   3474 * position and attempts to scroll the rect to that position in the
   3475 * visual viewport.
   3476 *
   3477 * This needs to work even if aRect has a width or height of zero.
   3478 */
   3479 static Maybe<nsPoint> ScrollToShowRect(
   3480    ScrollContainerFrame* aScrollContainerFrame,
   3481    const nsIFrame* aScrollableFrame, const nsIFrame* aTarget,
   3482    const nsRect& aRect, const Sides aScrollPaddingSkipSides,
   3483    const nsMargin& aMargin, ScrollAxis aVertical, ScrollAxis aHorizontal,
   3484    ScrollFlags aScrollFlags) {
   3485  nsPoint scrollPt = aScrollContainerFrame->GetVisualViewportOffset();
   3486  const nsPoint originalScrollPt = scrollPt;
   3487  const nsRect visibleRect(scrollPt,
   3488                           aScrollContainerFrame->GetVisualViewportSize());
   3489 
   3490  const nsMargin padding = [&] {
   3491    nsMargin p = aScrollContainerFrame->GetScrollPadding();
   3492    p.ApplySkipSides(aScrollPaddingSkipSides);
   3493    return p + aMargin;
   3494  }();
   3495 
   3496  const nsRect rectToScrollIntoView = [&] {
   3497    nsRect r(aRect);
   3498    r.Inflate(padding);
   3499    return r.Intersect(aScrollContainerFrame->GetScrolledRect());
   3500  }();
   3501 
   3502  nsSize lineSize;
   3503  // Don't call GetLineScrollAmount unless we actually need it. Not only
   3504  // does this save time, but it's not safe to call GetLineScrollAmount
   3505  // during reflow (because it depends on font size inflation and doesn't
   3506  // use the in-reflow-safe font-size inflation path). If we did call it,
   3507  // it would assert and possible give the wrong result.
   3508  if (aVertical.mWhenToScroll == WhenToScroll::IfNotVisible ||
   3509      aHorizontal.mWhenToScroll == WhenToScroll::IfNotVisible) {
   3510    lineSize = aScrollContainerFrame->GetLineScrollAmount();
   3511  }
   3512  ScrollStyles ss = aScrollContainerFrame->GetScrollStyles();
   3513  nsRect allowedRange(scrollPt, nsSize(0, 0));
   3514 
   3515  if ((aScrollFlags & ScrollFlags::ScrollOverflowHidden) ||
   3516      ss.mVertical != StyleOverflow::Hidden) {
   3517    if (ComputeNeedToScroll(aVertical.mWhenToScroll, lineSize.height, aRect.y,
   3518                            aRect.YMost(), visibleRect.y + padding.top,
   3519                            visibleRect.YMost() - padding.bottom)) {
   3520      // If the scroll-snap-align on the frame is valid, we need to respect it.
   3521      WhereToScroll whereToScroll = GetApplicableWhereToScroll(
   3522          aScrollContainerFrame, aScrollableFrame, aTarget,
   3523          ScrollDirection::eVertical, aVertical.mWhereToScroll);
   3524 
   3525      nscoord maxHeight;
   3526      scrollPt.y = ComputeWhereToScroll(
   3527          whereToScroll, scrollPt.y, rectToScrollIntoView.y,
   3528          rectToScrollIntoView.YMost(), visibleRect.y, visibleRect.YMost(),
   3529          &allowedRange.y, &maxHeight);
   3530      allowedRange.height = maxHeight - allowedRange.y;
   3531    }
   3532  }
   3533 
   3534  if ((aScrollFlags & ScrollFlags::ScrollOverflowHidden) ||
   3535      ss.mHorizontal != StyleOverflow::Hidden) {
   3536    if (ComputeNeedToScroll(aHorizontal.mWhenToScroll, lineSize.width, aRect.x,
   3537                            aRect.XMost(), visibleRect.x + padding.left,
   3538                            visibleRect.XMost() - padding.right)) {
   3539      // If the scroll-snap-align on the frame is valid, we need to respect it.
   3540      WhereToScroll whereToScroll = GetApplicableWhereToScroll(
   3541          aScrollContainerFrame, aScrollableFrame, aTarget,
   3542          ScrollDirection::eHorizontal, aHorizontal.mWhereToScroll);
   3543 
   3544      nscoord maxWidth;
   3545      scrollPt.x = ComputeWhereToScroll(
   3546          whereToScroll, scrollPt.x, rectToScrollIntoView.x,
   3547          rectToScrollIntoView.XMost(), visibleRect.x, visibleRect.XMost(),
   3548          &allowedRange.x, &maxWidth);
   3549      allowedRange.width = maxWidth - allowedRange.x;
   3550    }
   3551  }
   3552 
   3553  // If we don't need to scroll, then don't try since it might cancel
   3554  // a current smooth scroll operation.
   3555  if (scrollPt == originalScrollPt) {
   3556    return Nothing();
   3557  }
   3558 
   3559  ScrollMode scrollMode =
   3560      GetScrollModeForScrollIntoView(aScrollContainerFrame, aScrollFlags);
   3561  nsIFrame* frame = do_QueryFrame(aScrollContainerFrame);
   3562  AutoWeakFrame weakFrame(frame);
   3563  aScrollContainerFrame->ScrollTo(scrollPt, scrollMode, &allowedRange,
   3564                                  ScrollSnapFlags::IntendedEndPosition,
   3565                                  aScrollFlags & ScrollFlags::TriggeredByScript
   3566                                      ? ScrollTriggeredByScript::Yes
   3567                                      : ScrollTriggeredByScript::No);
   3568  return Some(scrollPt);
   3569 }
   3570 
   3571 nsresult PresShell::ScrollContentIntoView(nsIContent* aContent,
   3572                                          ScrollAxis aVertical,
   3573                                          ScrollAxis aHorizontal,
   3574                                          ScrollFlags aScrollFlags) {
   3575  NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
   3576  RefPtr<Document> composedDoc = aContent->GetComposedDoc();
   3577  NS_ENSURE_STATE(composedDoc);
   3578 
   3579  NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
   3580 
   3581  if (mContentToScrollTo) {
   3582    mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
   3583  }
   3584  mContentToScrollTo = aContent;
   3585  ScrollIntoViewData* data = new ScrollIntoViewData();
   3586  data->mContentScrollVAxis = aVertical;
   3587  data->mContentScrollHAxis = aHorizontal;
   3588  data->mContentToScrollToFlags = aScrollFlags;
   3589  if (NS_FAILED(mContentToScrollTo->SetProperty(
   3590          nsGkAtoms::scrolling, data,
   3591          nsINode::DeleteProperty<PresShell::ScrollIntoViewData>))) {
   3592    mContentToScrollTo = nullptr;
   3593  }
   3594 
   3595  // If the target frame has an ancestor of a `content-visibility: auto`
   3596  // element ensure that it is laid out, so that the boundary rectangle is
   3597  // correct.
   3598  // Additionally, ensure that all ancestor elements with 'content-visibility:
   3599  // auto' are set to 'visible'. so that they are laid out as visible before
   3600  // scrolling, improving the accuracy of the scroll position, especially when
   3601  // the scroll target is within the overflow area. And here invoking
   3602  // 'SetTemporarilyVisibleForScrolledIntoViewDescendant' would make the
   3603  // intersection observer knows that it should generate entries for these
   3604  // c-v:auto ancestors, so that the content relevancy could be checked again
   3605  // after scrolling. https://drafts.csswg.org/css-contain-2/#cv-notes
   3606  bool reflowedForHiddenContent = false;
   3607  if (mContentToScrollTo) {
   3608    if (nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame()) {
   3609      bool hasContentVisibilityAutoAncestor = false;
   3610      auto* ancestor = frame->GetClosestContentVisibilityAncestor(
   3611          nsIFrame::IncludeContentVisibility::Auto);
   3612      while (ancestor) {
   3613        if (auto* element = Element::FromNodeOrNull(ancestor->GetContent())) {
   3614          hasContentVisibilityAutoAncestor = true;
   3615          element->SetTemporarilyVisibleForScrolledIntoViewDescendant(true);
   3616          element->SetVisibleForContentVisibility(true);
   3617        }
   3618        ancestor = ancestor->GetClosestContentVisibilityAncestor(
   3619            nsIFrame::IncludeContentVisibility::Auto);
   3620      }
   3621      if (hasContentVisibilityAutoAncestor) {
   3622        UpdateHiddenContentInForcedLayout(frame);
   3623        // TODO: There might be the other already scheduled relevancy updates,
   3624        // other than caused be scrollIntoView.
   3625        UpdateContentRelevancyImmediately(ContentRelevancyReason::Visible);
   3626        reflowedForHiddenContent = ReflowForHiddenContentIfNeeded();
   3627      }
   3628    }
   3629  }
   3630 
   3631  if (!reflowedForHiddenContent) {
   3632    // Flush layout and attempt to scroll in the process.
   3633    if (PresShell* presShell = composedDoc->GetPresShell()) {
   3634      presShell->SetNeedLayoutFlush();
   3635    }
   3636    composedDoc->FlushPendingNotifications(FlushType::InterruptibleLayout);
   3637  }
   3638 
   3639  // If mContentToScrollTo is non-null, that means we interrupted the reflow
   3640  // (or suppressed it altogether because we're suppressing interruptible
   3641  // flushes right now) and won't necessarily get the position correct, but do
   3642  // a best-effort scroll here.  The other option would be to do this inside
   3643  // FlushPendingNotifications, but I'm not sure the repeated scrolling that
   3644  // could trigger if reflows keep getting interrupted would be more desirable
   3645  // than a single best-effort scroll followed by one final scroll on the first
   3646  // completed reflow.
   3647  if (mContentToScrollTo) {
   3648    DoScrollContentIntoView();
   3649  }
   3650  return NS_OK;
   3651 }
   3652 
   3653 static nsMargin GetScrollMargin(const nsIFrame* aFrame) {
   3654  MOZ_ASSERT(aFrame);
   3655  // If we're focusing something that can't be targeted by content, allow
   3656  // content to customize the margin.
   3657  //
   3658  // TODO: This is also a bit of an issue for delegated focus, see
   3659  // https://github.com/whatwg/html/issues/7033.
   3660  if (aFrame->GetContent() && aFrame->GetContent()->ChromeOnlyAccess()) {
   3661    // XXX Should we use nsIContent::FindFirstNonChromeOnlyAccessContent()
   3662    // instead of nsINode::GetClosestNativeAnonymousSubtreeRootParentOrHost()?
   3663    if (const nsIContent* userContent =
   3664            aFrame->GetContent()
   3665                ->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
   3666      if (const nsIFrame* frame = userContent->GetPrimaryFrame()) {
   3667        return frame->StyleMargin()->GetScrollMargin();
   3668      }
   3669    }
   3670  }
   3671  return aFrame->StyleMargin()->GetScrollMargin();
   3672 }
   3673 
   3674 void PresShell::DoScrollContentIntoView() {
   3675  NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
   3676 
   3677  nsIFrame* frame = mContentToScrollTo->GetPrimaryFrame();
   3678 
   3679  if (!frame || frame->IsHiddenByContentVisibilityOnAnyAncestor(
   3680                    nsIFrame::IncludeContentVisibility::Hidden)) {
   3681    mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
   3682    mContentToScrollTo = nullptr;
   3683    return;
   3684  }
   3685 
   3686  if (frame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
   3687    // The reflow flush before this scroll got interrupted, and this frame's
   3688    // coords and size are all zero, and it has no content showing anyway.
   3689    // Don't bother scrolling to it.  We'll try again when we finish up layout.
   3690    return;
   3691  }
   3692 
   3693  auto* data = static_cast<ScrollIntoViewData*>(
   3694      mContentToScrollTo->GetProperty(nsGkAtoms::scrolling));
   3695  if (MOZ_UNLIKELY(!data)) {
   3696    mContentToScrollTo = nullptr;
   3697    return;
   3698  }
   3699 
   3700  ScrollFrameIntoView(frame, Nothing(), data->mContentScrollVAxis,
   3701                      data->mContentScrollHAxis, data->mContentToScrollToFlags);
   3702 }
   3703 
   3704 static bool NeedToVisuallyScroll(const nsSize& aLayoutViewportSize,
   3705                                 const nsRect& aPositionFixedRect) {
   3706  // position:fixed elements are fixed to the layout viewport, thus the
   3707  // coordinate system is (0, 0) origin.
   3708  // (and the maximum visible position is the layout viewport size, elements
   3709  // outside of the size will never be laid out)
   3710  const nsRect layoutViewport = nsRect(nsPoint(), aLayoutViewportSize);
   3711 
   3712  // `BaseRect::Intersects(const Sub& aRect)` does return false if `aRect` is
   3713  // empty, but we do want to visually scroll to empty position:fixed elements
   3714  // if the elements are inside the layout viewport.
   3715  if (aPositionFixedRect.IsEmpty()) {
   3716    if (aPositionFixedRect.x > layoutViewport.XMost() ||
   3717        aPositionFixedRect.XMost() < layoutViewport.x ||
   3718        aPositionFixedRect.y > layoutViewport.YMost() ||
   3719        aPositionFixedRect.YMost() < layoutViewport.y) {
   3720      return false;
   3721    }
   3722    return true;
   3723  }
   3724 
   3725  if (!layoutViewport.Intersects(aPositionFixedRect)) {
   3726    return false;
   3727  }
   3728  return true;
   3729 }
   3730 
   3731 void PresShell::ScrollFrameIntoVisualViewport(
   3732    Maybe<nsPoint>& aDestination, const nsRect& aPositionFixedRect,
   3733    const nsIFrame* aPositionFixedFrame, ScrollAxis aVertical,
   3734    ScrollAxis aHorizontal, ScrollFlags aScrollFlags) {
   3735  PresShell* root = GetRootPresShell();
   3736  if (!root) {
   3737    return;
   3738  }
   3739 
   3740  if (!root->GetPresContext()->IsRootContentDocumentCrossProcess()) {
   3741    return;
   3742  }
   3743 
   3744  ScrollContainerFrame* rootScrollContainer =
   3745      root->GetRootScrollContainerFrame();
   3746  if (!rootScrollContainer) {
   3747    return;
   3748  }
   3749 
   3750  if (!aDestination) {
   3751    MOZ_ASSERT(aPositionFixedFrame);
   3752    // If we have in the top level content document but we didn't reach to
   3753    // the root scroll container in the frame tree walking up loop in
   3754    // ScrollFrameIntoView, it means the target element is inside a
   3755    // position:fixed subtree.
   3756    if (!StaticPrefs::layout_scroll_fixed_content_into_view_visually()) {
   3757      return;
   3758    }
   3759 
   3760    const nsSize visualViewportSize =
   3761        rootScrollContainer->GetVisualViewportSize();
   3762 
   3763    const nsSize layoutViewportSize = root->GetLayoutViewportSize();
   3764    const nsRect layoutViewport = nsRect(nsPoint(), layoutViewportSize);
   3765    // `positon:fixed` element are attached/fixed to the ViewportFrame, which is
   3766    // the parent of the root scroll container frame, thus what we need here is
   3767    // the visible area of the position:fixed element inside the root scroll
   3768    // container frame.
   3769    // For example, if the top left position of the fixed element is (-100,
   3770    // -100), it's outside of the scrollable range either in the layout viewport
   3771    // or the visual viewport. Likewise, if the right bottom position of the
   3772    // fixed element is (110vw, 110vh), it's also outside of the scrollable
   3773    // range.
   3774    const nsRect clampedPositionFixedRect =
   3775        aPositionFixedRect.MoveInsideAndClamp(layoutViewport);
   3776    // If the position:fixed element is already inside the visual viewport, we
   3777    // don't need to scroll visually.
   3778    if (clampedPositionFixedRect.y >= 0 &&
   3779        clampedPositionFixedRect.YMost() <= visualViewportSize.height &&
   3780        clampedPositionFixedRect.x >= 0 &&
   3781        clampedPositionFixedRect.XMost() <= visualViewportSize.width) {
   3782      return;
   3783    }
   3784 
   3785    // If the position:fixed element is totally outside of the the layout
   3786    // viewport, it will never be in the viewport.
   3787    if (!NeedToVisuallyScroll(layoutViewportSize, aPositionFixedRect)) {
   3788      return;
   3789    }
   3790    // Offset the position:fixed element position by the layout scroll
   3791    // position because the position:fixed origin (0, 0) is the layout scroll
   3792    // position. Otherwise if we've already scrolled, this scrollIntoView
   3793    // operation will jump back to near (0, 0) position.
   3794    nsPoint layoutOffset = rootScrollContainer->GetScrollPosition();
   3795 
   3796    const nsRect visibleRect(layoutOffset, visualViewportSize);
   3797    nscoord unusedRangeMinOutparam;
   3798    nscoord unusedRangeMaxOutparam;
   3799    nscoord x = ComputeWhereToScroll(
   3800        aScrollFlags & ScrollFlags::ForZoomToFocusedInput
   3801            ? WhereToScroll::Nearest
   3802            : aHorizontal.mWhereToScroll,
   3803        layoutOffset.x, aPositionFixedRect.x, aPositionFixedRect.XMost(),
   3804        visibleRect.x, visibleRect.XMost(), &unusedRangeMinOutparam,
   3805        &unusedRangeMaxOutparam);
   3806    nscoord y = ComputeWhereToScroll(
   3807        aScrollFlags & ScrollFlags::ForZoomToFocusedInput
   3808            ? WhereToScroll::Nearest
   3809            : aVertical.mWhereToScroll,
   3810        layoutOffset.y, aPositionFixedRect.y, aPositionFixedRect.YMost(),
   3811        visibleRect.y, visibleRect.YMost(), &unusedRangeMinOutparam,
   3812        &unusedRangeMaxOutparam);
   3813 
   3814    layoutOffset.x += x;
   3815    layoutOffset.y += y;
   3816    aDestination = Some(layoutOffset);
   3817  }
   3818 
   3819  // NOTE: It seems chrome doesn't respect the root element's
   3820  // scroll-behavior for visual scrolling.
   3821  ScrollMode scrollMode =
   3822      GetScrollModeForScrollIntoView(rootScrollContainer, aScrollFlags);
   3823  root->ScrollToVisual(*aDestination, FrameMetrics::eMainThread, scrollMode);
   3824 }
   3825 
   3826 bool PresShell::ScrollFrameIntoView(
   3827    nsIFrame* aTargetFrame, const Maybe<nsRect>& aKnownRectRelativeToTarget,
   3828    ScrollAxis aVertical, ScrollAxis aHorizontal, ScrollFlags aScrollFlags) {
   3829  // If the AxesAreLogical flag is set, the aVertical and aHorizontal params
   3830  // actually refer to block and inline axes respectively, so we resolve them
   3831  // to physical axes/directions here.
   3832  // XXX Maybe we should convert more of the following code to logical axes,
   3833  // if it's convenient for more callers to work that way?
   3834  if (aScrollFlags & ScrollFlags::AxesAreLogical) {
   3835    // The aVertical parameter actually refers to the element's block axis,
   3836    // and aHorizontal to its inline axis. Potentially reverse/swap them,
   3837    // according to its writing mode and directionality.
   3838    WritingMode wm = aTargetFrame->GetWritingMode();
   3839    if (wm.IsVerticalRL()) {
   3840      // Reverse the block-axis percentage.
   3841      if (aVertical.mWhereToScroll.mPercentage) {
   3842        aVertical.mWhereToScroll.mPercentage =
   3843            Some(100 - aVertical.mWhereToScroll.mPercentage.value());
   3844      }
   3845    }
   3846    if (wm.IsInlineReversed()) {
   3847      // Reverse the inline-axis percentage.
   3848      if (aHorizontal.mWhereToScroll.mPercentage) {
   3849        aHorizontal.mWhereToScroll.mPercentage =
   3850            Some(100 - aHorizontal.mWhereToScroll.mPercentage.value());
   3851      }
   3852    }
   3853    if (wm.IsVertical()) {
   3854      std::swap(aVertical, aHorizontal);
   3855    }
   3856    // Remove the AxesAreLogical flag, to make it clear that methods we call
   3857    // always get physical axes from here on.
   3858    aScrollFlags &= ~ScrollFlags::AxesAreLogical;
   3859  }
   3860 
   3861  // The scroll margin only applies to the whole bounds of the element, so don't
   3862  // apply it if we get an arbitrary rect / point to scroll to.
   3863  const nsMargin scrollMargin =
   3864      aKnownRectRelativeToTarget ? nsMargin() : GetScrollMargin(aTargetFrame);
   3865 
   3866  Sides skipPaddingSides;
   3867  const auto MaybeSkipPaddingSides = [&](nsIFrame* aFrame) {
   3868    if (!aFrame->IsStickyPositioned()) {
   3869      return;
   3870    }
   3871    const nsPoint pos = aFrame->GetPosition();
   3872    const nsPoint normalPos = aFrame->GetNormalPosition();
   3873    if (pos == normalPos) {
   3874      return;  // Frame is not stuck.
   3875    }
   3876    // If we're targetting a sticky element, make sure not to apply
   3877    // scroll-padding on the direction we're stuck.
   3878    const auto* stylePosition = aFrame->StylePosition();
   3879    const auto anchorResolutionParams =
   3880        AnchorPosOffsetResolutionParams::UseCBFrameSize(
   3881            AnchorPosResolutionParams::From(aFrame));
   3882    for (auto side : AllPhysicalSides()) {
   3883      if (stylePosition->GetAnchorResolvedInset(side, anchorResolutionParams)
   3884              ->IsAuto()) {
   3885        continue;
   3886      }
   3887      // See if this axis is stuck.
   3888      const bool yAxis = side == eSideTop || side == eSideBottom;
   3889      const bool stuck = yAxis ? pos.y != normalPos.y : pos.x != normalPos.x;
   3890      if (!stuck) {
   3891        continue;
   3892      }
   3893      skipPaddingSides |= SideToSideBit(side);
   3894    }
   3895  };
   3896 
   3897  nsIFrame* container = aTargetFrame;
   3898 
   3899  const nsIFrame* positionFixedFrame = nullptr;
   3900  auto isPositionFixed = [&](const nsIFrame* aFrame) -> bool {
   3901    return aFrame->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
   3902           nsLayoutUtils::IsReallyFixedPos(aFrame);
   3903  };
   3904  // This function needs to work even if rect has a width or height of 0.
   3905  nsRect rect = [&] {
   3906    if (aKnownRectRelativeToTarget) {
   3907      return *aKnownRectRelativeToTarget;
   3908    }
   3909    MaybeSkipPaddingSides(aTargetFrame);
   3910    while (nsIFrame* parent = container->GetParent()) {
   3911      if (isPositionFixed(container)) {
   3912        positionFixedFrame = container;
   3913      }
   3914      container = parent;
   3915      if (container->IsScrollContainerOrSubclass()) {
   3916        // We really just need a non-fragmented frame so that we can accumulate
   3917        // the bounds of all our continuations relative to it. We shouldn't jump
   3918        // out of our nearest scrollable frame, and that's an ok reference
   3919        // frame, so try to use that, or the root frame if there's nothing to
   3920        // scroll in this document.
   3921        break;
   3922      }
   3923      MaybeSkipPaddingSides(container);
   3924    }
   3925    MOZ_DIAGNOSTIC_ASSERT(container);
   3926 
   3927    nsRect targetFrameBounds;
   3928    {
   3929      bool haveRect = false;
   3930      const bool useWholeLineHeightForInlines =
   3931          aVertical.mWhenToScroll != WhenToScroll::IfNotFullyVisible;
   3932      AutoAssertNoDomMutations
   3933          guard;  // Ensure use of nsILineIterators is safe.
   3934      nsIFrame* prevBlock = nullptr;
   3935      // Reuse the same line iterator across calls to AccumulateFrameBounds.
   3936      // We set it every time we detect a new block (stored in prevBlock).
   3937      nsILineIterator* lines = nullptr;
   3938      // The last line we found a continuation on in |lines|.  We assume that
   3939      // later continuations cannot come on earlier lines.
   3940      int32_t curLine = 0;
   3941      nsIFrame* frame = aTargetFrame;
   3942      do {
   3943        AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
   3944                              targetFrameBounds, haveRect, prevBlock, lines,
   3945                              curLine);
   3946      } while ((frame = frame->GetNextContinuation()));
   3947    }
   3948 
   3949    return targetFrameBounds;
   3950  }();
   3951  bool didScroll = false;
   3952  const nsIFrame* target = aTargetFrame;
   3953  Maybe<nsPoint> rootScrollDestination;
   3954  // Walk up the frame hierarchy scrolling the rect into view and
   3955  // keeping rect relative to container
   3956  do {
   3957    if (isPositionFixed(container)) {
   3958      positionFixedFrame = container;
   3959    }
   3960 
   3961    if (ScrollContainerFrame* sf = do_QueryFrame(container)) {
   3962      nsPoint oldPosition = sf->GetScrollPosition();
   3963      nsRect targetRect = rect - sf->GetScrolledFrame()->GetPosition();
   3964 
   3965      {
   3966        AutoWeakFrame wf(container);
   3967        Maybe<nsPoint> destination = ScrollToShowRect(
   3968            sf, container, target, targetRect, skipPaddingSides, scrollMargin,
   3969            aVertical, aHorizontal, aScrollFlags);
   3970        if (!wf.IsAlive()) {
   3971          return didScroll;
   3972        }
   3973 
   3974        if (sf->IsRootScrollFrameOfDocument() &&
   3975            sf->PresContext()->IsRootContentDocumentCrossProcess()) {
   3976          rootScrollDestination = destination;
   3977        }
   3978      }
   3979 
   3980      nsPoint newPosition = sf->LastScrollDestination();
   3981      // If the scroll position increased, that means our content moved up,
   3982      // so our rect's offset should decrease
   3983      rect += oldPosition - newPosition;
   3984 
   3985      if (oldPosition != newPosition) {
   3986        didScroll = true;
   3987      }
   3988 
   3989      // only scroll one container when this flag is set
   3990      if (aScrollFlags & ScrollFlags::ScrollFirstAncestorOnly) {
   3991        break;
   3992      }
   3993 
   3994      // This scroll container will be the next target element in the nearest
   3995      // ancestor scroll container.
   3996      target = container;
   3997      // We found a sticky scroll container, we shouldn't skip that side
   3998      // anymore.
   3999      skipPaddingSides = {};
   4000    }
   4001 
   4002    MaybeSkipPaddingSides(container);
   4003 
   4004    nsIFrame* parent = container->GetParent();
   4005    NS_ASSERTION(parent || !container->IsTransformed(),
   4006                 "viewport shouldnt be transformed");
   4007    if (parent && container->IsTransformed()) {
   4008      rect =
   4009          nsLayoutUtils::TransformFrameRectToAncestor(container, rect, parent);
   4010    } else {
   4011      rect += container->GetPosition();
   4012    }
   4013    if (!parent && !(aScrollFlags & ScrollFlags::ScrollNoParentFrames)) {
   4014      nsPoint extraOffset(0, 0);
   4015      int32_t APD = container->PresContext()->AppUnitsPerDevPixel();
   4016      parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(container,
   4017                                                              &extraOffset);
   4018      if (parent) {
   4019        int32_t parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
   4020        rect = rect.ScaleToOtherAppUnitsRoundOut(APD, parentAPD);
   4021        rect += extraOffset;
   4022      } else {
   4023        nsCOMPtr<nsIDocShell> docShell =
   4024            container->PresContext()->GetDocShell();
   4025        if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
   4026          // Defer to the parent document if this is an out-of-process iframe.
   4027          (void)browserChild->SendScrollRectIntoView(
   4028              rect, aVertical, aHorizontal, aScrollFlags, APD);
   4029        }
   4030      }
   4031    }
   4032    container = parent;
   4033  } while (container);
   4034 
   4035  // If this is inside the top level content document process (and a direct
   4036  // descendant of it), also call ScrollToVisual() since we want to
   4037  // scroll the rect into view visually, and that may require scrolling
   4038  // the visual viewport in scenarios where there is not enough layout
   4039  // scroll range.
   4040  if (!rootScrollDestination && !positionFixedFrame) {
   4041    return didScroll;
   4042  }
   4043 
   4044  ScrollFrameIntoVisualViewport(rootScrollDestination, rect, positionFixedFrame,
   4045                                aVertical, aHorizontal, aScrollFlags);
   4046 
   4047  return didScroll;
   4048 }
   4049 
   4050 void PresShell::SchedulePaint() {
   4051  if (MOZ_UNLIKELY(mIsDestroying)) {
   4052    return;
   4053  }
   4054  if (nsPresContext* presContext = GetPresContext()) {
   4055    presContext->RefreshDriver()->SchedulePaint();
   4056  }
   4057 }
   4058 
   4059 void PresShell::ClearMouseCapture() {
   4060  ReleaseCapturingContent();
   4061  AllowMouseCapture(false);
   4062 }
   4063 
   4064 void PresShell::ClearMouseCapture(nsIFrame* aFrame) {
   4065  MOZ_ASSERT(aFrame);
   4066 
   4067  nsIContent* capturingContent = GetCapturingContent();
   4068  if (!capturingContent) {
   4069    return;
   4070  }
   4071 
   4072  nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
   4073  const bool shouldClear =
   4074      !capturingFrame ||
   4075      nsLayoutUtils::IsAncestorFrameCrossDocInProcess(aFrame, capturingFrame);
   4076  if (shouldClear) {
   4077    ClearMouseCapture();
   4078  }
   4079 }
   4080 
   4081 nsresult PresShell::CaptureHistoryState(nsILayoutHistoryState** aState) {
   4082  MOZ_ASSERT(nullptr != aState, "null state pointer");
   4083 
   4084  // We actually have to mess with the docshell here, since we want to
   4085  // store the state back in it.
   4086  // XXXbz this isn't really right, since this is being called in the
   4087  // content viewer's Hide() method...  by that point the docshell's
   4088  // state could be wrong.  We should sort out a better ownership
   4089  // model for the layout history state.
   4090  nsCOMPtr<nsIDocShell> docShell(mPresContext->GetDocShell());
   4091  if (!docShell) {
   4092    return NS_ERROR_FAILURE;
   4093  }
   4094 
   4095  nsCOMPtr<nsILayoutHistoryState> historyState;
   4096  docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
   4097  if (!historyState) {
   4098    // Create the document state object
   4099    historyState = NS_NewLayoutHistoryState();
   4100    docShell->SetLayoutHistoryState(historyState);
   4101  }
   4102 
   4103  *aState = historyState;
   4104  NS_IF_ADDREF(*aState);
   4105 
   4106  // Capture frame state for the entire frame hierarchy
   4107  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   4108  if (!rootFrame) {
   4109    return NS_OK;
   4110  }
   4111 
   4112  mFrameConstructor->CaptureFrameState(rootFrame, historyState);
   4113 
   4114  return NS_OK;
   4115 }
   4116 
   4117 void PresShell::ScheduleBeforeFirstPaint() {
   4118  if (!mDocument->IsResourceDoc()) {
   4119    // Notify observers that a new page is about to be drawn. Execute this
   4120    // as soon as it is safe to run JS, which is guaranteed to be before we
   4121    // go back to the event loop and actually draw the page.
   4122    MOZ_LOG(gLog, LogLevel::Debug,
   4123            ("PresShell::ScheduleBeforeFirstPaint this=%p", this));
   4124 
   4125    nsContentUtils::AddScriptRunner(
   4126        new nsBeforeFirstPaintDispatcher(mDocument));
   4127  }
   4128 }
   4129 
   4130 void PresShell::UnsuppressAndInvalidate() {
   4131  // Note: We ignore the EnsureVisible check for resource documents, because
   4132  // they won't have a docshell, so they'll always fail EnsureVisible.
   4133  if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) ||
   4134      mHaveShutDown) {
   4135    // No point; we're about to be torn down anyway.
   4136    return;
   4137  }
   4138 
   4139  ScheduleBeforeFirstPaint();
   4140 
   4141  PROFILER_MARKER_UNTYPED("UnsuppressAndInvalidate", GRAPHICS);
   4142 
   4143  mPaintingSuppressed = false;
   4144  if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
   4145    // let's assume that outline on a root frame is not supported
   4146    rootFrame->InvalidateFrame();
   4147  }
   4148 
   4149  if (mPresContext->IsRootContentDocumentCrossProcess()) {
   4150    if (auto* bc = BrowserChild::GetFrom(mDocument->GetDocShell())) {
   4151      if (mDocument->IsInitialDocument()) {
   4152        bc->SendDidUnsuppressPaintingNormalPriority();
   4153      } else {
   4154        bc->SendDidUnsuppressPainting();
   4155      }
   4156    }
   4157  }
   4158 
   4159  // now that painting is unsuppressed, focus may be set on the document
   4160  if (nsPIDOMWindowOuter* win = mDocument->GetWindow()) {
   4161    win->SetReadyForFocus();
   4162  }
   4163 
   4164  if (!mHaveShutDown) {
   4165    SynthesizeMouseMove(false);
   4166    ScheduleApproximateFrameVisibilityUpdateNow();
   4167  }
   4168 }
   4169 
   4170 void PresShell::CancelPaintSuppressionTimer() {
   4171  if (mPaintSuppressionTimer) {
   4172    mPaintSuppressionTimer->Cancel();
   4173    mPaintSuppressionTimer = nullptr;
   4174  }
   4175 }
   4176 
   4177 void PresShell::UnsuppressPainting() {
   4178  CancelPaintSuppressionTimer();
   4179 
   4180  if (mIsDocumentGone || !mPaintingSuppressed) {
   4181    return;
   4182  }
   4183 
   4184  // If we have reflows pending, just wait until we process
   4185  // the reflows and get all the frames where we want them
   4186  // before actually unlocking the painting.  Otherwise
   4187  // go ahead and unlock now.
   4188  if (!mDirtyRoots.IsEmpty()) {
   4189    mShouldUnsuppressPainting = true;
   4190  } else {
   4191    UnsuppressAndInvalidate();
   4192  }
   4193 }
   4194 
   4195 // Post a request to handle an arbitrary callback after reflow has finished.
   4196 nsresult PresShell::PostReflowCallback(nsIReflowCallback* aCallback) {
   4197  void* result = AllocateByObjectID(eArenaObjectID_nsCallbackEventRequest,
   4198                                    sizeof(nsCallbackEventRequest));
   4199  nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
   4200 
   4201  request->callback = aCallback;
   4202  request->next = nullptr;
   4203 
   4204  if (mLastCallbackEventRequest) {
   4205    mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
   4206  } else {
   4207    mFirstCallbackEventRequest = request;
   4208    mLastCallbackEventRequest = request;
   4209  }
   4210 
   4211  return NS_OK;
   4212 }
   4213 
   4214 void PresShell::CancelReflowCallback(nsIReflowCallback* aCallback) {
   4215  nsCallbackEventRequest* before = nullptr;
   4216  nsCallbackEventRequest* node = mFirstCallbackEventRequest;
   4217  while (node) {
   4218    nsIReflowCallback* callback = node->callback;
   4219 
   4220    if (callback == aCallback) {
   4221      nsCallbackEventRequest* toFree = node;
   4222      if (node == mFirstCallbackEventRequest) {
   4223        node = node->next;
   4224        mFirstCallbackEventRequest = node;
   4225        NS_ASSERTION(before == nullptr, "impossible");
   4226      } else {
   4227        node = node->next;
   4228        before->next = node;
   4229      }
   4230 
   4231      if (toFree == mLastCallbackEventRequest) {
   4232        mLastCallbackEventRequest = before;
   4233      }
   4234 
   4235      FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, toFree);
   4236    } else {
   4237      before = node;
   4238      node = node->next;
   4239    }
   4240  }
   4241 }
   4242 
   4243 void PresShell::CancelPostedReflowCallbacks() {
   4244  while (mFirstCallbackEventRequest) {
   4245    nsCallbackEventRequest* node = mFirstCallbackEventRequest;
   4246    mFirstCallbackEventRequest = node->next;
   4247    if (!mFirstCallbackEventRequest) {
   4248      mLastCallbackEventRequest = nullptr;
   4249    }
   4250    nsIReflowCallback* callback = node->callback;
   4251    FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
   4252    if (callback) {
   4253      callback->ReflowCallbackCanceled();
   4254    }
   4255  }
   4256 }
   4257 
   4258 void PresShell::HandlePostedReflowCallbacks(bool aInterruptible) {
   4259  while (true) {
   4260    // Call all our callbacks, tell us if we need to flush again.
   4261    bool shouldFlush = false;
   4262    while (mFirstCallbackEventRequest) {
   4263      nsCallbackEventRequest* node = mFirstCallbackEventRequest;
   4264      mFirstCallbackEventRequest = node->next;
   4265      if (!mFirstCallbackEventRequest) {
   4266        mLastCallbackEventRequest = nullptr;
   4267      }
   4268      nsIReflowCallback* callback = node->callback;
   4269      FreeByObjectID(eArenaObjectID_nsCallbackEventRequest, node);
   4270      if (callback && callback->ReflowFinished()) {
   4271        shouldFlush = true;
   4272      }
   4273    }
   4274 
   4275    if (!shouldFlush || mIsDestroying) {
   4276      return;
   4277    }
   4278 
   4279    // The flush might cause us to have more callbacks.
   4280    const auto flushType =
   4281        aInterruptible ? FlushType::InterruptibleLayout : FlushType::Layout;
   4282    FlushPendingNotifications(flushType);
   4283  }
   4284 }
   4285 
   4286 bool PresShell::IsSafeToFlush() const {
   4287  // Not safe if we are getting torn down, reflowing, or in the middle of frame
   4288  // construction.
   4289  if (mIsReflowing || mChangeNestCount || mIsDestroying) {
   4290    return false;
   4291  }
   4292  // Not safe either if we're painting.
   4293  // TODO(emilio): What could call us while painting now?
   4294  return !mIsPainting;
   4295 }
   4296 
   4297 void PresShell::NotifyFontFaceSetOnRefresh() {
   4298  if (FontFaceSet* set = mDocument->GetFonts()) {
   4299    set->DidRefresh();
   4300  }
   4301 }
   4302 
   4303 void PresShell::DoFlushPendingNotifications(FlushType aType) {
   4304  // by default, flush animations if aType >= FlushType::Style
   4305  mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style,
   4306                                aType >= FlushType::Layout);
   4307  FlushPendingNotifications(flush);
   4308 }
   4309 
   4310 #ifdef DEBUG
   4311 static void AssertFrameSubtreeIsSane(const nsIFrame& aRoot) {
   4312  if (const nsIContent* content = aRoot.GetContent()) {
   4313    MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle(),
   4314               "Node not in the flattened tree still has a frame?");
   4315  }
   4316 
   4317  for (const auto& childList : aRoot.ChildLists()) {
   4318    for (const nsIFrame* child : childList.mList) {
   4319      AssertFrameSubtreeIsSane(*child);
   4320    }
   4321  }
   4322 }
   4323 #endif
   4324 
   4325 static inline void AssertFrameTreeIsSane(const PresShell& aPresShell) {
   4326 #ifdef DEBUG
   4327  if (const nsIFrame* root = aPresShell.GetRootFrame()) {
   4328    AssertFrameSubtreeIsSane(*root);
   4329  }
   4330 #endif
   4331 }
   4332 
   4333 static void TriggerPendingScrollTimelineAnimations(Document* aDocument) {
   4334  auto* tracker = aDocument->GetScrollTimelineAnimationTracker();
   4335  if (!tracker || !tracker->HasPendingAnimations()) {
   4336    return;
   4337  }
   4338  tracker->TriggerPendingAnimations();
   4339 }
   4340 
   4341 void PresShell::DoFlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
   4342  // FIXME(emilio, bug 1530177): Turn into a release assert when bug 1530188 and
   4343  // bug 1530190 are fixed.
   4344  MOZ_DIAGNOSTIC_ASSERT(!mForbiddenToFlush, "This is bad!");
   4345 
   4346  // Per our API contract, hold a strong ref to ourselves until we return.
   4347  RefPtr<PresShell> kungFuDeathGrip = this;
   4348 
   4349  /**
   4350   * VERY IMPORTANT: If you add some sort of new flushing to this
   4351   * method, make sure to add the relevant SetNeedLayoutFlush or
   4352   * SetNeedStyleFlush calls on the shell.
   4353   */
   4354  FlushType flushType = aFlush.mFlushType;
   4355 
   4356  if (aFlush.mUpdateRelevancy) {
   4357    // If needed, first update the relevancy of any content of elements with
   4358    // `content-visibility: auto` so that the values returned from e.g. script
   4359    // queries are up-to-date.
   4360    UpdateRelevancyOfContentVisibilityAutoFrames();
   4361  }
   4362 
   4363  MOZ_ASSERT(NeedFlush(flushType), "Why did we get called?");
   4364 
   4365  AUTO_PROFILER_MARKER_TEXT(
   4366      "DoFlushPendingNotifications", LAYOUT,
   4367      MarkerOptions(MarkerStack::Capture(), MarkerInnerWindowIdFromDocShell(
   4368                                                mPresContext->GetDocShell())),
   4369      nsDependentCString(kFlushTypeNames[flushType]));
   4370  AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE(
   4371      "PresShell::DoFlushPendingNotifications", LAYOUT,
   4372      kFlushTypeNames[flushType]);
   4373 
   4374 #ifdef ACCESSIBILITY
   4375 #  ifdef DEBUG
   4376  if (nsAccessibilityService* accService = GetAccService()) {
   4377    NS_ASSERTION(!accService->IsProcessingRefreshDriverNotification(),
   4378                 "Flush during accessible tree update!");
   4379  }
   4380 #  endif
   4381 #endif
   4382 
   4383  NS_ASSERTION(flushType >= FlushType::Style, "Why did we get called?");
   4384 
   4385  bool isSafeToFlush = IsSafeToFlush();
   4386 
   4387  // If layout could possibly trigger scripts, then it's only safe to flush if
   4388  // it's safe to run script.
   4389  bool hasHadScriptObject;
   4390  if (mDocument->GetScriptHandlingObject(hasHadScriptObject) ||
   4391      hasHadScriptObject) {
   4392    isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
   4393  }
   4394 
   4395  // Don't flush if the doc is already in the bfcache.
   4396  if (MOZ_UNLIKELY(mDocument->GetPresShell() != this)) {
   4397    MOZ_DIAGNOSTIC_ASSERT(!mDocument->GetPresShell(),
   4398                          "Where did this shell come from?");
   4399    isSafeToFlush = false;
   4400  }
   4401 
   4402  MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying || !isSafeToFlush);
   4403  MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mWidgetListener);
   4404  MOZ_DIAGNOSTIC_ASSERT(mIsDestroying || mDocument->HasShellOrBFCacheEntry());
   4405 
   4406  if (!isSafeToFlush) {
   4407    return;
   4408  }
   4409 
   4410  // We need to make sure external resource documents are flushed too (for
   4411  // example, svg filters that reference a filter in an external document
   4412  // need the frames in the external document to be constructed for the
   4413  // filter to work). We only need external resources to be flushed when the
   4414  // main document is flushing >= FlushType::Frames, so we flush external
   4415  // resources here instead of Document::FlushPendingNotifications.
   4416  mDocument->FlushExternalResources(flushType);
   4417 
   4418  // Force flushing of any pending content notifications that might have
   4419  // queued up while our event was pending.  That will ensure that we don't
   4420  // construct frames for content right now that's still waiting to be
   4421  // notified on,
   4422  mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
   4423 
   4424  mDocument->UpdateSVGUseElementShadowTrees();
   4425 
   4426  // Process pending restyles, since any flush of the presshell wants
   4427  // up-to-date style data.
   4428  if (MOZ_LIKELY(!mIsDestroying)) {
   4429    FlushDelayedResize();
   4430    mPresContext->FlushPendingMediaFeatureValuesChanged();
   4431  }
   4432 
   4433  if (MOZ_LIKELY(!mIsDestroying)) {
   4434    // Now that we have flushed media queries, update the rules before looking
   4435    // up @font-face / @counter-style / @font-feature-values rules.
   4436    StyleSet()->UpdateStylistIfNeeded();
   4437 
   4438    // Flush any pending update of the user font set, since that could
   4439    // cause style changes (for updating ex/ch units, and to cause a
   4440    // reflow).
   4441    mDocument->FlushUserFontSet();
   4442 
   4443    mPresContext->FlushCounterStyles();
   4444 
   4445    mPresContext->FlushFontFeatureValues();
   4446 
   4447    mPresContext->FlushFontPaletteValues();
   4448 
   4449    // Flush any requested SMIL samples.
   4450    if (mDocument->HasAnimationController()) {
   4451      mDocument->GetAnimationController()->FlushResampleRequests();
   4452    }
   4453  }
   4454 
   4455  // The FlushResampleRequests() above might have flushed style changes.
   4456  if (MOZ_LIKELY(!mIsDestroying)) {
   4457    if (aFlush.mFlushAnimations) {
   4458      mPresContext->EffectCompositor()->PostRestyleForThrottledAnimations();
   4459      mNeedThrottledAnimationFlush = false;
   4460    }
   4461 
   4462    nsAutoScriptBlocker scriptBlocker;
   4463    Maybe<uint64_t> innerWindowID;
   4464    if (auto* window = mDocument->GetInnerWindow()) {
   4465      innerWindowID = Some(window->WindowID());
   4466    }
   4467    AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause),
   4468                                              innerWindowID);
   4469    PerfStats::AutoMetricRecording<PerfStats::Metric::Styling> autoRecording;
   4470 
   4471    mPresContext->RestyleManager()->ProcessPendingRestyles();
   4472    mNeedStyleFlush = false;
   4473  }
   4474 
   4475  AssertFrameTreeIsSane(*this);
   4476 
   4477  if (flushType >= (SuppressInterruptibleReflows()
   4478                        ? FlushType::Layout
   4479                        : FlushType::InterruptibleLayout) &&
   4480      !mIsDestroying) {
   4481    if (DoFlushLayout(/* aInterruptible = */ flushType < FlushType::Layout)) {
   4482      if (mContentToScrollTo) {
   4483        DoScrollContentIntoView();
   4484        if (mContentToScrollTo) {
   4485          mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
   4486          mContentToScrollTo = nullptr;
   4487        }
   4488      }
   4489    }
   4490    // FIXME(emilio): Maybe we should assert here but it's not 100% sure it'd
   4491    // hold right now, UnsuppressAndInvalidate and so on can run script...
   4492    if (MOZ_LIKELY(mDirtyRoots.IsEmpty())) {
   4493      mNeedLayoutFlush = false;
   4494    }
   4495  }
   4496 
   4497  FlushPendingScrollResnap();
   4498 
   4499  if (MOZ_LIKELY(!mIsDestroying)) {
   4500    // Try to trigger pending scroll-driven animations after we flush
   4501    // style and layout (if any). If we try to trigger them after flushing
   4502    // style but the frame tree is not ready, we will check them again after
   4503    // we flush layout because the requirement to trigger scroll-driven
   4504    // animations is that the associated scroll containers are ready (i.e. the
   4505    // scroll-timeline is active), and this depends on the readiness of the
   4506    // scrollable frame and the primary frame of the scroll container.
   4507    TriggerPendingScrollTimelineAnimations(mDocument);
   4508  }
   4509 }
   4510 
   4511 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::CharacterDataChanged(
   4512    nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
   4513  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   4514  MOZ_ASSERT(!mIsDocumentGone, "Unexpected CharacterDataChanged");
   4515  MOZ_ASSERT(aContent->OwnerDoc() == mDocument, "Unexpected document");
   4516 
   4517  nsAutoCauseReflowNotifier crNotifier(this);
   4518 
   4519  mPresContext->RestyleManager()->CharacterDataChanged(aContent, aInfo);
   4520  mFrameConstructor->CharacterDataChanged(aContent, aInfo);
   4521 }
   4522 
   4523 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ElementStateChanged(
   4524    Document* aDocument, Element* aElement, ElementState aStateMask) {
   4525  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   4526  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentStateChanged");
   4527  MOZ_ASSERT(aDocument == mDocument, "Unexpected aDocument");
   4528 
   4529  if (mDidInitialize) {
   4530    nsAutoCauseReflowNotifier crNotifier(this);
   4531    mPresContext->RestyleManager()->ElementStateChanged(aElement, aStateMask);
   4532  }
   4533 }
   4534 
   4535 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::CustomStatesWillChange(
   4536    Element& aElement) {
   4537  if (MOZ_UNLIKELY(!mDidInitialize)) {
   4538    return;
   4539  }
   4540 
   4541  mPresContext->RestyleManager()->CustomStatesWillChange(aElement);
   4542 }
   4543 
   4544 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::CustomStateChanged(
   4545    Element& aElement, nsAtom* aState) {
   4546  MOZ_ASSERT(!mIsDocumentGone, "Unexpected CustomStateChanged");
   4547  MOZ_ASSERT(aState, "Unexpected empty state");
   4548 
   4549  if (mDidInitialize) {
   4550    nsAutoCauseReflowNotifier crNotifier(this);
   4551    mPresContext->RestyleManager()->CustomStateChanged(aElement, aState);
   4552  }
   4553 }
   4554 
   4555 void PresShell::DocumentStatesChanged(DocumentState aStateMask) {
   4556  MOZ_ASSERT(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
   4557  MOZ_ASSERT(mDocument);
   4558  MOZ_ASSERT(!aStateMask.IsEmpty());
   4559 
   4560  if (mDidInitialize) {
   4561    StyleSet()->InvalidateStyleForDocumentStateChanges(aStateMask);
   4562  }
   4563 
   4564  if (aStateMask.HasState(DocumentState::WINDOW_INACTIVE)) {
   4565    if (nsIFrame* root = mFrameConstructor->GetRootFrame()) {
   4566      root->SchedulePaint();
   4567    }
   4568  }
   4569 }
   4570 
   4571 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::AttributeWillChange(
   4572    Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute,
   4573    AttrModType aModType) {
   4574  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   4575  MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeWillChange");
   4576  MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
   4577 
   4578  // XXXwaterson it might be more elegant to wait until after the
   4579  // initial reflow to begin observing the document. That would
   4580  // squelch any other inappropriate notifications as well.
   4581  if (mDidInitialize) {
   4582    nsAutoCauseReflowNotifier crNotifier(this);
   4583    mPresContext->RestyleManager()->AttributeWillChange(aElement, aNameSpaceID,
   4584                                                        aAttribute, aModType);
   4585  }
   4586 }
   4587 
   4588 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::AttributeChanged(
   4589    Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute,
   4590    AttrModType aModType, const nsAttrValue* aOldValue) {
   4591  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   4592  MOZ_ASSERT(!mIsDocumentGone, "Unexpected AttributeChanged");
   4593  MOZ_ASSERT(aElement->OwnerDoc() == mDocument, "Unexpected document");
   4594 
   4595  // XXXwaterson it might be more elegant to wait until after the
   4596  // initial reflow to begin observing the document. That would
   4597  // squelch any other inappropriate notifications as well.
   4598  if (mDidInitialize) {
   4599    nsAutoCauseReflowNotifier crNotifier(this);
   4600    mPresContext->RestyleManager()->AttributeChanged(
   4601        aElement, aNameSpaceID, aAttribute, aModType, aOldValue);
   4602  }
   4603 }
   4604 
   4605 static void MaybeDestroyFramesAndStyles(nsIContent* aContent,
   4606                                        nsPresContext& aPresContext) {
   4607  if (!aContent->IsElement()) {
   4608    return;
   4609  }
   4610 
   4611  Element* element = aContent->AsElement();
   4612  if (!element->HasServoData()) {
   4613    return;
   4614  }
   4615 
   4616  Element* parent =
   4617      Element::FromNodeOrNull(element->GetFlattenedTreeParentNode());
   4618  if (!parent || !parent->HasServoData() ||
   4619      Servo_Element_IsDisplayNone(parent)) {
   4620    DestroyFramesAndStyleDataFor(element, aPresContext,
   4621                                 RestyleManager::IncludeRoot::Yes);
   4622  }
   4623 }
   4624 
   4625 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ContentAppended(
   4626    nsIContent* aFirstNewContent, const ContentAppendInfo& aInfo) {
   4627  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   4628  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentAppended");
   4629  MOZ_ASSERT(aFirstNewContent->OwnerDoc() == mDocument, "Unexpected document");
   4630 
   4631  // We never call ContentAppended with a document as the container, so we can
   4632  // assert that we have an nsIContent parent.
   4633  MOZ_ASSERT(aFirstNewContent->GetParent());
   4634  MOZ_ASSERT(aFirstNewContent->GetParent()->IsElement() ||
   4635             aFirstNewContent->GetParent()->IsShadowRoot());
   4636 
   4637  if (!mDidInitialize) {
   4638    return;
   4639  }
   4640 
   4641  mPresContext->EventStateManager()->ContentAppended(aFirstNewContent, aInfo);
   4642 
   4643  if (aInfo.mOldParent) {
   4644    MaybeDestroyFramesAndStyles(aFirstNewContent, *mPresContext);
   4645  }
   4646 
   4647  nsAutoCauseReflowNotifier crNotifier(this);
   4648 
   4649  // Call this here so it only happens for real content mutations and
   4650  // not cases when the frame constructor calls its own methods to force
   4651  // frame reconstruction.
   4652  mPresContext->RestyleManager()->ContentAppended(aFirstNewContent);
   4653 
   4654  mFrameConstructor->ContentAppended(
   4655      aFirstNewContent, nsCSSFrameConstructor::InsertionKind::Async);
   4656 }
   4657 
   4658 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ContentInserted(
   4659    nsIContent* aChild, const ContentInsertInfo& aInfo) {
   4660  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   4661  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentInserted");
   4662  MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
   4663 
   4664  if (!mDidInitialize) {
   4665    return;
   4666  }
   4667 
   4668  mPresContext->EventStateManager()->ContentInserted(aChild, aInfo);
   4669 
   4670  if (aInfo.mOldParent) {
   4671    MaybeDestroyFramesAndStyles(aChild, *mPresContext);
   4672  }
   4673 
   4674  nsAutoCauseReflowNotifier crNotifier(this);
   4675 
   4676  // Call this here so it only happens for real content mutations and
   4677  // not cases when the frame constructor calls its own methods to force
   4678  // frame reconstruction.
   4679  mPresContext->RestyleManager()->ContentInserted(aChild);
   4680 
   4681  mFrameConstructor->ContentInserted(
   4682      aChild, nsCSSFrameConstructor::InsertionKind::Async);
   4683 }
   4684 
   4685 MOZ_CAN_RUN_SCRIPT_BOUNDARY void PresShell::ContentWillBeRemoved(
   4686    nsIContent* aChild, const ContentRemoveInfo& aInfo) {
   4687  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript());
   4688  MOZ_ASSERT(!mIsDocumentGone, "Unexpected ContentRemoved");
   4689  MOZ_ASSERT(aChild->OwnerDoc() == mDocument, "Unexpected document");
   4690  // Notify the ESM that the content has been removed, so that
   4691  // it can clean up any state related to the content.
   4692 
   4693  mPresContext->EventStateManager()->ContentRemoved(mDocument, aChild, aInfo);
   4694 
   4695  nsAutoCauseReflowNotifier crNotifier(this);
   4696 
   4697  for (AutoConnectedAncestorTracker* tracker = mLastConnectedAncestorTracker;
   4698       tracker; tracker = tracker->mPreviousTracker) {
   4699    if (tracker->ConnectedNode().IsInclusiveFlatTreeDescendantOf(aChild)) {
   4700      tracker->mConnectedAncestor = aChild->GetFlattenedTreeParentElement();
   4701    }
   4702  }
   4703 
   4704  if (aInfo.mNewParent && aChild->IsElement()) {
   4705    if (aInfo.mNewParent->IsElement() &&
   4706        aInfo.mNewParent->AsElement()->HasServoData() &&
   4707        !Servo_Element_IsDisplayNone(aInfo.mNewParent->AsElement())) {
   4708      DestroyFramesForAndRestyle(aChild->AsElement());
   4709      return;
   4710    }
   4711  }
   4712 
   4713  mFrameConstructor->ContentWillBeRemoved(
   4714      aChild, nsCSSFrameConstructor::RemovalKind::Dom);
   4715 
   4716  // NOTE(emilio): It's important that this goes after the frame constructor
   4717  // stuff, otherwise the frame constructor can't see elements which are
   4718  // display: contents / display: none, because we'd have cleared all the style
   4719  // data from there.
   4720  mPresContext->RestyleManager()->ContentWillBeRemoved(aChild);
   4721 }
   4722 
   4723 void PresShell::NotifyCounterStylesAreDirty() {
   4724  // TODO: Looks like that nsFrameConstructor::NotifyCounterStylesAreDirty()
   4725  //       does not run script.  If so, we don't need to block script with
   4726  //       nsAutoCauseReflowNotifier here.  Instead, there should be methods
   4727  //       and stack only class which manages only mChangeNestCount for
   4728  //       avoiding unnecessary `MOZ_CAN_RUN_SCRIPT` marking.
   4729  nsAutoCauseReflowNotifier reflowNotifier(this);
   4730  mFrameConstructor->NotifyCounterStylesAreDirty();
   4731 }
   4732 
   4733 bool PresShell::FrameIsAncestorOfDirtyRoot(nsIFrame* aFrame) const {
   4734  return mDirtyRoots.FrameIsAncestorOfAnyElement(aFrame);
   4735 }
   4736 
   4737 void PresShell::ReconstructFrames() {
   4738  MOZ_ASSERT(!mFrameConstructor->GetRootFrame() || mDidInitialize,
   4739             "Must not have root frame before initial reflow");
   4740  if (!mDidInitialize || mIsDestroying) {
   4741    // Nothing to do here
   4742    return;
   4743  }
   4744 
   4745  if (Element* root = mDocument->GetRootElement()) {
   4746    PostRecreateFramesFor(root);
   4747  }
   4748 
   4749  mDocument->FlushPendingNotifications(FlushType::Frames);
   4750 }
   4751 
   4752 nsresult PresShell::RenderDocument(const nsRect& aRect,
   4753                                   RenderDocumentFlags aFlags,
   4754                                   nscolor aBackgroundColor,
   4755                                   gfxContext* aThebesContext) {
   4756  NS_ENSURE_TRUE(!(aFlags & RenderDocumentFlags::IsUntrusted),
   4757                 NS_ERROR_NOT_IMPLEMENTED);
   4758 
   4759  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
   4760  if (rootPresContext) {
   4761    rootPresContext->FlushWillPaintObservers();
   4762    if (mIsDestroying) {
   4763      return NS_OK;
   4764    }
   4765  }
   4766 
   4767  nsAutoScriptBlocker blockScripts;
   4768 
   4769  // Set up the rectangle as the path in aThebesContext
   4770  gfxRect r(0, 0, nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
   4771            nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
   4772  aThebesContext->NewPath();
   4773 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   4774  aThebesContext->SnappedRectangle(r);
   4775 #else
   4776  aThebesContext->Rectangle(r);
   4777 #endif
   4778 
   4779  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   4780  if (!rootFrame) {
   4781    // Nothing to paint, just fill the rect
   4782    aThebesContext->SetColor(sRGBColor::FromABGR(aBackgroundColor));
   4783    aThebesContext->Fill();
   4784    return NS_OK;
   4785  }
   4786 
   4787  gfxContextAutoSaveRestore save(aThebesContext);
   4788 
   4789  MOZ_ASSERT(aThebesContext->CurrentOp() == CompositionOp::OP_OVER);
   4790 
   4791  aThebesContext->Clip();
   4792 
   4793  nsDeviceContext* devCtx = mPresContext->DeviceContext();
   4794 
   4795  gfxPoint offset(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
   4796                  -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y));
   4797  gfxFloat scale =
   4798      gfxFloat(devCtx->AppUnitsPerDevPixel()) / AppUnitsPerCSSPixel();
   4799 
   4800  // Since canvas APIs use floats to set up their matrices, we may have some
   4801  // slight rounding errors here.  We use NudgeToIntegers() here to adjust
   4802  // matrix components that are integers up to the accuracy of floats to be
   4803  // those integers.
   4804  gfxMatrix newTM = aThebesContext->CurrentMatrixDouble()
   4805                        .PreTranslate(offset)
   4806                        .PreScale(scale, scale)
   4807                        .NudgeToIntegers();
   4808  aThebesContext->SetMatrixDouble(newTM);
   4809 
   4810  AutoSaveRestoreRenderingState _(this);
   4811 
   4812  bool wouldFlushRetainedLayers = false;
   4813  PaintFrameFlags flags = PaintFrameFlags::IgnoreSuppression;
   4814  if (aThebesContext->CurrentMatrix().HasNonIntegerTranslation()) {
   4815    flags |= PaintFrameFlags::InTransform;
   4816  }
   4817  if (!(aFlags & RenderDocumentFlags::AsyncDecodeImages)) {
   4818    flags |= PaintFrameFlags::SyncDecodeImages;
   4819  }
   4820  if (aFlags & RenderDocumentFlags::UseHighQualityScaling) {
   4821    flags |= PaintFrameFlags::UseHighQualityScaling;
   4822  }
   4823  if (aFlags & RenderDocumentFlags::UseWidgetLayers) {
   4824    // We only support using widget layers on display root's with widgets.
   4825    nsIWidget* widget = rootFrame->GetOwnWidget();
   4826    if (widget && nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
   4827      WindowRenderer* renderer = widget->GetWindowRenderer();
   4828      // WebRenderLayerManagers in content processes
   4829      // don't support taking snapshots.
   4830      if (renderer &&
   4831          (!renderer->AsKnowsCompositor() || XRE_IsParentProcess())) {
   4832        flags |= PaintFrameFlags::WidgetLayers;
   4833      }
   4834    }
   4835  }
   4836  if (!(aFlags & RenderDocumentFlags::DrawCaret)) {
   4837    wouldFlushRetainedLayers = true;
   4838    flags |= PaintFrameFlags::HideCaret;
   4839  }
   4840  if (aFlags & RenderDocumentFlags::IgnoreViewportScrolling) {
   4841    wouldFlushRetainedLayers = !IgnoringViewportScrolling();
   4842    mRenderingStateFlags |= RenderingStateFlags::IgnoringViewportScrolling;
   4843  }
   4844  if (aFlags & RenderDocumentFlags::ResetViewportScrolling) {
   4845    wouldFlushRetainedLayers = true;
   4846    flags |= PaintFrameFlags::ResetViewportScrolling;
   4847  }
   4848  if (aFlags & RenderDocumentFlags::DrawWindowNotFlushing) {
   4849    mRenderingStateFlags |= RenderingStateFlags::DrawWindowNotFlushing;
   4850  }
   4851  if (aFlags & RenderDocumentFlags::DocumentRelative) {
   4852    // XXX be smarter about this ... drawWindow might want a rect
   4853    // that's "pretty close" to what our retained layer tree covers.
   4854    // In that case, it wouldn't disturb normal rendering too much,
   4855    // and we should allow it.
   4856    wouldFlushRetainedLayers = true;
   4857    flags |= PaintFrameFlags::DocumentRelative;
   4858  }
   4859 
   4860  // Don't let drawWindow blow away our retained layer tree
   4861  if ((flags & PaintFrameFlags::WidgetLayers) && wouldFlushRetainedLayers) {
   4862    flags &= ~PaintFrameFlags::WidgetLayers;
   4863  }
   4864 
   4865  nsLayoutUtils::PaintFrame(aThebesContext, rootFrame, nsRegion(aRect),
   4866                            aBackgroundColor,
   4867                            nsDisplayListBuilderMode::Painting, flags);
   4868 
   4869  return NS_OK;
   4870 }
   4871 
   4872 /*
   4873 * Clip the display list aList to a range. Returns the clipped
   4874 * rectangle surrounding the range.
   4875 */
   4876 nsRect PresShell::ClipListToRange(nsDisplayListBuilder* aBuilder,
   4877                                  nsDisplayList* aList, nsRange* aRange) {
   4878  // iterate though the display items and add up the bounding boxes of each.
   4879  // This will allow the total area of the frames within the range to be
   4880  // determined. To do this, remove an item from the bottom of the list, check
   4881  // whether it should be part of the range, and if so, append it to the top
   4882  // of the temporary list tmpList. If the item is a text frame at the end of
   4883  // the selection range, clip it to the portion of the text frame that is
   4884  // part of the selection. Then, append the wrapper to the top of the list.
   4885  // Otherwise, just delete the item and don't append it.
   4886  nsRect surfaceRect;
   4887 
   4888  for (nsDisplayItem* i : aList->TakeItems()) {
   4889    if (i->GetType() == DisplayItemType::TYPE_CONTAINER) {
   4890      aList->AppendToTop(i);
   4891      surfaceRect.UnionRect(
   4892          surfaceRect, ClipListToRange(aBuilder, i->GetChildren(), aRange));
   4893      continue;
   4894    }
   4895 
   4896    // itemToInsert indiciates the item that should be inserted into the
   4897    // temporary list. If null, no item should be inserted.
   4898    nsDisplayItem* itemToInsert = nullptr;
   4899    nsIFrame* frame = i->Frame();
   4900    nsIContent* content = frame->GetContent();
   4901    if (content) {
   4902      bool atStart =
   4903          content == aRange->GetMayCrossShadowBoundaryStartContainer();
   4904      bool atEnd = content == aRange->GetMayCrossShadowBoundaryEndContainer();
   4905      if ((atStart || atEnd) && frame->IsTextFrame()) {
   4906        auto [frameStartOffset, frameEndOffset] = frame->GetOffsets();
   4907 
   4908        int32_t highlightStart =
   4909            atStart ? std::max(static_cast<int32_t>(
   4910                                   aRange->MayCrossShadowBoundaryStartOffset()),
   4911                               frameStartOffset)
   4912                    : frameStartOffset;
   4913        int32_t highlightEnd =
   4914            atEnd ? std::min(static_cast<int32_t>(
   4915                                 aRange->MayCrossShadowBoundaryEndOffset()),
   4916                             frameEndOffset)
   4917                  : frameEndOffset;
   4918        if (highlightStart < highlightEnd) {
   4919          // determine the location of the start and end edges of the range.
   4920          nsPoint startPoint, endPoint;
   4921          frame->GetPointFromOffset(highlightStart, &startPoint);
   4922          frame->GetPointFromOffset(highlightEnd, &endPoint);
   4923 
   4924          // The clip rectangle is determined by taking the the start and
   4925          // end points of the range, offset from the reference frame.
   4926          // Because of rtl, the end point may be to the left of (or above,
   4927          // in vertical mode) the start point, so x (or y) is set to the
   4928          // lower of the values.
   4929          nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
   4930          if (frame->GetWritingMode().IsVertical()) {
   4931            nscoord y = std::min(startPoint.y, endPoint.y);
   4932            textRect.y += y;
   4933            textRect.height = std::max(startPoint.y, endPoint.y) - y;
   4934          } else {
   4935            nscoord x = std::min(startPoint.x, endPoint.x);
   4936            textRect.x += x;
   4937            textRect.width = std::max(startPoint.x, endPoint.x) - x;
   4938          }
   4939          surfaceRect.UnionRect(surfaceRect, textRect);
   4940 
   4941          const ActiveScrolledRoot* asr = i->GetActiveScrolledRoot();
   4942 
   4943          DisplayItemClip newClip;
   4944          newClip.SetTo(textRect);
   4945 
   4946          const DisplayItemClipChain* newClipChain =
   4947              aBuilder->AllocateDisplayItemClipChain(newClip, asr, nullptr);
   4948 
   4949          i->IntersectClip(aBuilder, newClipChain, true);
   4950          itemToInsert = i;
   4951        }
   4952      }
   4953      // Don't try to descend into subdocuments.
   4954      // If this ever changes we'd need to add handling for subdocuments with
   4955      // different zoom levels.
   4956      else if (content->GetComposedDoc() ==
   4957               aRange->GetMayCrossShadowBoundaryStartContainer()
   4958                   ->GetComposedDoc()) {
   4959        // if the node is within the range, append it to the temporary list
   4960        bool before, after;
   4961        nsresult rv =
   4962            RangeUtils::CompareNodeToRange<TreeKind::ShadowIncludingDOM>(
   4963                content, aRange, &before, &after);
   4964        if (NS_SUCCEEDED(rv) && !before && !after) {
   4965          itemToInsert = i;
   4966          bool snap;
   4967          surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder, &snap));
   4968        }
   4969      }
   4970    }
   4971 
   4972    // insert the item into the list if necessary. If the item has a child
   4973    // list, insert that as well
   4974    nsDisplayList* sublist = i->GetSameCoordinateSystemChildren();
   4975    if (itemToInsert || sublist) {
   4976      aList->AppendToTop(itemToInsert ? itemToInsert : i);
   4977      // if the item is a list, iterate over it as well
   4978      if (sublist) {
   4979        surfaceRect.UnionRect(surfaceRect,
   4980                              ClipListToRange(aBuilder, sublist, aRange));
   4981      }
   4982    } else {
   4983      // otherwise, just delete the item and don't readd it to the list
   4984      i->Destroy(aBuilder);
   4985    }
   4986  }
   4987 
   4988  return surfaceRect;
   4989 }
   4990 
   4991 #ifdef DEBUG
   4992 #  include <stdio.h>
   4993 
   4994 static bool gDumpRangePaintList = false;
   4995 #endif
   4996 
   4997 UniquePtr<RangePaintInfo> PresShell::CreateRangePaintInfo(
   4998    nsRange* aRange, nsRect& aSurfaceRect, bool aForPrimarySelection) {
   4999  nsIFrame* ancestorFrame = nullptr;
   5000  nsIFrame* rootFrame = GetRootFrame();
   5001 
   5002  // If the start or end of the range is the document, just use the root
   5003  // frame, otherwise get the common ancestor of the two endpoints of the
   5004  // range.
   5005  nsINode* startContainer = aRange->GetMayCrossShadowBoundaryStartContainer();
   5006  nsINode* endContainer = aRange->GetMayCrossShadowBoundaryEndContainer();
   5007  Document* doc = startContainer->GetComposedDoc();
   5008  if (startContainer == doc || endContainer == doc) {
   5009    ancestorFrame = rootFrame;
   5010  } else {
   5011    nsINode* ancestor =
   5012        StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
   5013            ? nsContentUtils::GetClosestCommonShadowIncludingInclusiveAncestor(
   5014                  startContainer, endContainer)
   5015            : nsContentUtils::GetClosestCommonInclusiveAncestor(startContainer,
   5016                                                                endContainer);
   5017    NS_ASSERTION(!ancestor || ancestor->IsContent(),
   5018                 "common ancestor is not content");
   5019 
   5020    while (ancestor && ancestor->IsContent()) {
   5021      ancestorFrame = ancestor->AsContent()->GetPrimaryFrame();
   5022      if (ancestorFrame) {
   5023        break;
   5024      }
   5025 
   5026      ancestor = ancestor->GetParentOrShadowHostNode();
   5027    }
   5028 
   5029    // use the nearest ancestor frame that includes all continuations as the
   5030    // root for building the display list
   5031    while (ancestorFrame &&
   5032           nsLayoutUtils::GetNextContinuationOrIBSplitSibling(ancestorFrame)) {
   5033      ancestorFrame = ancestorFrame->GetParent();
   5034    }
   5035  }
   5036 
   5037  if (!ancestorFrame) {
   5038    return nullptr;
   5039  }
   5040 
   5041  // get a display list containing the range
   5042  auto info = MakeUnique<RangePaintInfo>(ancestorFrame);
   5043  info->mBuilder.SetIncludeAllOutOfFlows();
   5044  if (aForPrimarySelection) {
   5045    info->mBuilder.SetSelectedFramesOnly();
   5046  }
   5047  info->mBuilder.EnterPresShell(ancestorFrame);
   5048 
   5049  ContentSubtreeIterator subtreeIter;
   5050  nsresult rv = StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
   5051                    ? subtreeIter.InitWithAllowCrossShadowBoundary(aRange)
   5052                    : subtreeIter.Init(aRange);
   5053  if (NS_FAILED(rv)) {
   5054    return nullptr;
   5055  }
   5056 
   5057  auto BuildDisplayListForNode = [&](nsINode* aNode) {
   5058    if (MOZ_UNLIKELY(!aNode->IsContent())) {
   5059      return;
   5060    }
   5061    nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
   5062    // XXX deal with frame being null due to display:contents
   5063    for (; frame;
   5064         frame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame)) {
   5065      info->mBuilder.SetVisibleRect(frame->InkOverflowRect());
   5066      info->mBuilder.SetDirtyRect(frame->InkOverflowRect());
   5067      frame->BuildDisplayListForStackingContext(&info->mBuilder, &info->mList);
   5068    }
   5069  };
   5070  if (startContainer->NodeType() == nsINode::TEXT_NODE) {
   5071    BuildDisplayListForNode(startContainer);
   5072  }
   5073  for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
   5074    nsCOMPtr<nsINode> node = subtreeIter.GetCurrentNode();
   5075    BuildDisplayListForNode(node);
   5076  }
   5077  if (endContainer != startContainer &&
   5078      endContainer->NodeType() == nsINode::TEXT_NODE) {
   5079    BuildDisplayListForNode(endContainer);
   5080  }
   5081 
   5082  // If one of the ancestor presShells (including this one) has a resolution
   5083  // set, we may have some APZ zoom applied. That means we may want to rasterize
   5084  // the nodes at that zoom level. Populate `info` with the relevant information
   5085  // so that the caller can decide what to do. Also wrap the display list in
   5086  // appropriate nsDisplayAsyncZoom display items. This code handles the general
   5087  // case with nested async zooms (even though that never actually happens),
   5088  // because it fell out of the implementation for free.
   5089  //
   5090  // TODO: Do we need to do the same for ancestor transforms?
   5091  for (nsPresContext* ctx = GetPresContext(); ctx;
   5092       ctx = ctx->GetParentPresContext()) {
   5093    PresShell* shell = ctx->PresShell();
   5094    float resolution = shell->GetResolution();
   5095 
   5096    // If we are at the root document in the process, try to see if documents
   5097    // in enclosing processes have a resolution and include that as well.
   5098    if (!ctx->GetParentPresContext()) {
   5099      // xScale is an arbitrary choice. Outside of edge cases involving CSS
   5100      // transforms, xScale == yScale so it doesn't matter.
   5101      resolution *= ViewportUtils::TryInferEnclosingResolution(shell).xScale;
   5102    }
   5103 
   5104    if (resolution == 1.0) {
   5105      continue;
   5106    }
   5107 
   5108    info->mResolution *= resolution;
   5109    nsIFrame* rootScrollContainerFrame = shell->GetRootScrollContainerFrame();
   5110    ViewID zoomedId = nsLayoutUtils::FindOrCreateIDFor(
   5111        rootScrollContainerFrame->GetContent());
   5112 
   5113    nsDisplayList wrapped(&info->mBuilder);
   5114    wrapped.AppendNewToTop<nsDisplayAsyncZoom>(
   5115        &info->mBuilder, rootScrollContainerFrame, &info->mList, nullptr,
   5116        nsDisplayItem::ContainerASRType::Constant, zoomedId);
   5117    info->mList.AppendToTop(&wrapped);
   5118  }
   5119 
   5120 #ifdef DEBUG
   5121  if (gDumpRangePaintList) {
   5122    fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
   5123    nsIFrame::PrintDisplayList(&(info->mBuilder), info->mList);
   5124  }
   5125 #endif
   5126 
   5127  nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, aRange);
   5128 
   5129  info->mBuilder.LeavePresShell(ancestorFrame, &info->mList);
   5130 
   5131 #ifdef DEBUG
   5132  if (gDumpRangePaintList) {
   5133    fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
   5134    nsIFrame::PrintDisplayList(&(info->mBuilder), info->mList);
   5135  }
   5136 #endif
   5137 
   5138  // determine the offset of the reference frame for the display list
   5139  // to the root frame. This will allow the coordinates used when painting
   5140  // to all be offset from the same point
   5141  info->mRootOffset = ancestorFrame->GetBoundingClientRect().TopLeft();
   5142  rangeRect.MoveBy(info->mRootOffset);
   5143  aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
   5144 
   5145  return info;
   5146 }
   5147 
   5148 already_AddRefed<SourceSurface> PresShell::PaintRangePaintInfo(
   5149    const nsTArray<UniquePtr<RangePaintInfo>>& aItems, Selection* aSelection,
   5150    const Maybe<CSSIntRegion>& aRegion, nsRect aArea,
   5151    const LayoutDeviceIntPoint aPoint, LayoutDeviceIntRect* aScreenRect,
   5152    RenderImageFlags aFlags) {
   5153  nsPresContext* pc = GetPresContext();
   5154  if (!pc || aArea.width == 0 || aArea.height == 0) {
   5155    return nullptr;
   5156  }
   5157 
   5158  // use the rectangle to create the surface
   5159  LayoutDeviceIntRect pixelArea = LayoutDeviceIntRect::FromAppUnitsToOutside(
   5160      aArea, pc->AppUnitsPerDevPixel());
   5161 
   5162  // if the image should not be resized, scale must be 1
   5163  float scale = 1.0;
   5164 
   5165  const nsRect maxSize = pc->DeviceContext()->GetClientRect();
   5166 
   5167  // check if the image should be resized
   5168  bool resize = !!(aFlags & RenderImageFlags::AutoScale);
   5169 
   5170  if (resize) {
   5171    // check if image-resizing-algorithm should be used
   5172    if (aFlags & RenderImageFlags::IsImage) {
   5173      // get max screensize
   5174      int32_t maxWidth = pc->AppUnitsToDevPixels(maxSize.width);
   5175      int32_t maxHeight = pc->AppUnitsToDevPixels(maxSize.height);
   5176      // resize image relative to the screensize
   5177      // get best height/width relative to screensize
   5178      float bestHeight = float(maxHeight) * RELATIVE_SCALEFACTOR;
   5179      float bestWidth = float(maxWidth) * RELATIVE_SCALEFACTOR;
   5180      // calculate scale for bestWidth
   5181      float adjustedScale = bestWidth / float(pixelArea.width);
   5182      // get the worst height (height when width is perfect)
   5183      float worstHeight = float(pixelArea.height) * adjustedScale;
   5184      // get the difference of best and worst height
   5185      float difference = bestHeight - worstHeight;
   5186      // halve the difference and add it to worstHeight to get
   5187      // the best compromise between bestHeight and bestWidth,
   5188      // then calculate the corresponding scale factor
   5189      adjustedScale = (worstHeight + difference / 2) / float(pixelArea.height);
   5190      // prevent upscaling
   5191      scale = std::min(scale, adjustedScale);
   5192    } else {
   5193      // get half of max screensize
   5194      int32_t maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
   5195      int32_t maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
   5196      if (pixelArea.width > maxWidth || pixelArea.height > maxHeight) {
   5197        // divide the maximum size by the image size in both directions.
   5198        // Whichever direction produces the smallest result determines how much
   5199        // should be scaled.
   5200        if (pixelArea.width > maxWidth) {
   5201          scale = std::min(scale, float(maxWidth) / pixelArea.width);
   5202        }
   5203        if (pixelArea.height > maxHeight) {
   5204          scale = std::min(scale, float(maxHeight) / pixelArea.height);
   5205        }
   5206      }
   5207    }
   5208 
   5209    // Pick a resolution scale factor that is the highest we need for any of
   5210    // the items. This means some items may get rendered at a higher-than-needed
   5211    // resolution but at least nothing will be avoidably blurry.
   5212    float resolutionScale = 1.0;
   5213    for (const UniquePtr<RangePaintInfo>& rangeInfo : aItems) {
   5214      resolutionScale = std::max(resolutionScale, rangeInfo->mResolution);
   5215    }
   5216    float unclampedResolution = resolutionScale;
   5217    // Clamp the resolution scale so that `pixelArea` when scaled by `scale` and
   5218    // `resolutionScale` isn't bigger than `maxSize`. This prevents creating
   5219    // giant/unbounded images.
   5220    resolutionScale =
   5221        std::min(resolutionScale, maxSize.width / (scale * pixelArea.width));
   5222    resolutionScale =
   5223        std::min(resolutionScale, maxSize.height / (scale * pixelArea.height));
   5224    // The following assert should only get hit if pixelArea scaled by `scale`
   5225    // alone would already have been bigger than `maxSize`, which should never
   5226    // be the case. For release builds we handle gracefully by reverting
   5227    // resolutionScale to 1.0 to avoid unexpected consequences.
   5228    MOZ_ASSERT(resolutionScale >= 1.0);
   5229    resolutionScale = std::max(1.0f, resolutionScale);
   5230 
   5231    scale *= resolutionScale;
   5232 
   5233    // Now we need adjust the output screen position of the surface based on the
   5234    // scaling factor and any APZ zoom that may be in effect. The goal is here
   5235    // to set `aScreenRect`'s top-left corner (in screen-relative LD pixels)
   5236    // such that the scaling effect on the surface appears anchored  at `aPoint`
   5237    // ("anchor" here is like "transform-origin"). When this code is e.g. used
   5238    // to generate a drag image for dragging operations, `aPoint` refers to the
   5239    // position of the mouse cursor (also in screen-relative LD pixels), and the
   5240    // user-visible effect of doing this is that the point at which the user
   5241    // clicked to start the drag remains under the mouse during the drag.
   5242 
   5243    // In order to do this we first compute the top-left corner of the
   5244    // pixelArea is screen-relative LD pixels.
   5245    LayoutDevicePoint visualPoint = ViewportUtils::ToScreenRelativeVisual(
   5246        LayoutDevicePoint(pixelArea.TopLeft()), pc);
   5247    // And then adjust the output screen position based on that, which we can do
   5248    // since everything here is screen-relative LD pixels. Note that the scale
   5249    // factor we use here is the effective "transform" scale applied to the
   5250    // content we're painting, relative to the scale at which it would normally
   5251    // get painted at as part of page rendering (`unclampedResolution`).
   5252    float scaleRelativeToNormalContent = scale / unclampedResolution;
   5253    aScreenRect->x =
   5254        NSToIntFloor(aPoint.x - float(aPoint.x.value - visualPoint.x.value) *
   5255                                    scaleRelativeToNormalContent);
   5256    aScreenRect->y =
   5257        NSToIntFloor(aPoint.y - float(aPoint.y.value - visualPoint.y.value) *
   5258                                    scaleRelativeToNormalContent);
   5259 
   5260    pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
   5261    pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
   5262    if (!pixelArea.width || !pixelArea.height) {
   5263      return nullptr;
   5264    }
   5265  } else {
   5266    // move aScreenRect to the position of the surface in screen coordinates
   5267    LayoutDevicePoint visualPoint = ViewportUtils::ToScreenRelativeVisual(
   5268        LayoutDevicePoint(pixelArea.TopLeft()), pc);
   5269    aScreenRect->MoveTo(RoundedToInt(visualPoint));
   5270  }
   5271  aScreenRect->width = pixelArea.width;
   5272  aScreenRect->height = pixelArea.height;
   5273 
   5274  RefPtr<DrawTarget> dt =
   5275      gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
   5276          IntSize(pixelArea.width, pixelArea.height), SurfaceFormat::B8G8R8A8);
   5277  if (!dt || !dt->IsValid()) {
   5278    return nullptr;
   5279  }
   5280 
   5281  gfxContext ctx(dt);
   5282 
   5283  if (aRegion) {
   5284    RefPtr<PathBuilder> builder = dt->CreatePathBuilder(FillRule::FILL_WINDING);
   5285 
   5286    // Convert aRegion from CSS pixels to dev pixels
   5287    nsIntRegion region = aRegion->ToAppUnits(AppUnitsPerCSSPixel())
   5288                             .ToOutsidePixels(pc->AppUnitsPerDevPixel());
   5289    for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
   5290      const IntRect& rect = iter.Get();
   5291 
   5292      builder->MoveTo(rect.TopLeft());
   5293      builder->LineTo(rect.TopRight());
   5294      builder->LineTo(rect.BottomRight());
   5295      builder->LineTo(rect.BottomLeft());
   5296      builder->LineTo(rect.TopLeft());
   5297    }
   5298 
   5299    RefPtr<Path> path = builder->Finish();
   5300    ctx.Clip(path);
   5301  }
   5302 
   5303  gfxMatrix initialTM = ctx.CurrentMatrixDouble();
   5304 
   5305  if (resize) {
   5306    initialTM.PreScale(scale, scale);
   5307  }
   5308 
   5309  // translate so that points are relative to the surface area
   5310  gfxPoint surfaceOffset = nsLayoutUtils::PointToGfxPoint(
   5311      -aArea.TopLeft(), pc->AppUnitsPerDevPixel());
   5312  initialTM.PreTranslate(surfaceOffset);
   5313 
   5314  // temporarily hide the selection so that text is drawn normally. If a
   5315  // selection is being rendered, use that, otherwise use the presshell's
   5316  // selection.
   5317  RefPtr<nsFrameSelection> frameSelection;
   5318  if (aSelection) {
   5319    frameSelection = aSelection->GetFrameSelection();
   5320  } else {
   5321    frameSelection = FrameSelection();
   5322  }
   5323  int16_t oldDisplaySelection = frameSelection->GetDisplaySelection();
   5324  frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
   5325 
   5326  // next, paint each range in the selection
   5327  for (const UniquePtr<RangePaintInfo>& rangeInfo : aItems) {
   5328    // the display lists paint relative to the offset from the reference
   5329    // frame, so account for that translation too:
   5330    gfxPoint rootOffset = nsLayoutUtils::PointToGfxPoint(
   5331        rangeInfo->mRootOffset, pc->AppUnitsPerDevPixel());
   5332    ctx.SetMatrixDouble(initialTM.PreTranslate(rootOffset));
   5333    aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
   5334    nsRegion visible(aArea);
   5335    rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, &ctx,
   5336                               nsDisplayList::PAINT_DEFAULT, Nothing());
   5337    aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
   5338  }
   5339 
   5340  // restore the old selection display state
   5341  frameSelection->SetDisplaySelection(oldDisplaySelection);
   5342 
   5343  return dt->Snapshot();
   5344 }
   5345 
   5346 already_AddRefed<SourceSurface> PresShell::RenderNode(
   5347    nsINode* aNode, const Maybe<CSSIntRegion>& aRegion,
   5348    const LayoutDeviceIntPoint aPoint, LayoutDeviceIntRect* aScreenRect,
   5349    RenderImageFlags aFlags) {
   5350  // area will hold the size of the surface needed to draw the node, measured
   5351  // from the root frame.
   5352  nsRect area;
   5353  nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
   5354 
   5355  // nothing to draw if the node isn't in a document
   5356  if (!aNode->IsInComposedDoc()) {
   5357    return nullptr;
   5358  }
   5359 
   5360  RefPtr<nsRange> range = nsRange::Create(aNode);
   5361  IgnoredErrorResult rv;
   5362  range->SelectNode(*aNode, rv);
   5363  if (rv.Failed()) {
   5364    return nullptr;
   5365  }
   5366 
   5367  UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, false);
   5368  if (info) {
   5369    // XXX(Bug 1631371) Check if this should use a fallible operation as it
   5370    // pretended earlier, or change the return type to void.
   5371    rangeItems.AppendElement(std::move(info));
   5372  }
   5373 
   5374  Maybe<CSSIntRegion> region = aRegion;
   5375  if (region) {
   5376    // combine the area with the supplied region
   5377    CSSIntRect rrectPixels = region->GetBounds();
   5378 
   5379    nsRect rrect = ToAppUnits(rrectPixels, AppUnitsPerCSSPixel());
   5380    area.IntersectRect(area, rrect);
   5381 
   5382    nsPresContext* pc = GetPresContext();
   5383    if (!pc) {
   5384      return nullptr;
   5385    }
   5386 
   5387    // move the region so that it is offset from the topleft corner of the
   5388    // surface
   5389    region->MoveBy(-nsPresContext::AppUnitsToIntCSSPixels(area.x),
   5390                   -nsPresContext::AppUnitsToIntCSSPixels(area.y));
   5391  }
   5392 
   5393  return PaintRangePaintInfo(rangeItems, nullptr, region, area, aPoint,
   5394                             aScreenRect, aFlags);
   5395 }
   5396 
   5397 already_AddRefed<SourceSurface> PresShell::RenderSelection(
   5398    Selection* aSelection, const LayoutDeviceIntPoint aPoint,
   5399    LayoutDeviceIntRect* aScreenRect, RenderImageFlags aFlags) {
   5400  // area will hold the size of the surface needed to draw the selection,
   5401  // measured from the root frame.
   5402  nsRect area;
   5403  nsTArray<UniquePtr<RangePaintInfo>> rangeItems;
   5404 
   5405  // iterate over each range and collect them into the rangeItems array.
   5406  // This is done so that the size of selection can be determined so as
   5407  // to allocate a surface area
   5408  const uint32_t rangeCount = aSelection->RangeCount();
   5409  NS_ASSERTION(rangeCount > 0, "RenderSelection called with no selection");
   5410  for (const uint32_t r : IntegerRange(rangeCount)) {
   5411    MOZ_ASSERT(aSelection->RangeCount() == rangeCount);
   5412    RefPtr<nsRange> range = aSelection->GetRangeAt(r);
   5413 
   5414    UniquePtr<RangePaintInfo> info = CreateRangePaintInfo(range, area, true);
   5415    if (info) {
   5416      // XXX(Bug 1631371) Check if this should use a fallible operation as it
   5417      // pretended earlier.
   5418      rangeItems.AppendElement(std::move(info));
   5419    }
   5420  }
   5421 
   5422  return PaintRangePaintInfo(rangeItems, aSelection, Nothing(), area, aPoint,
   5423                             aScreenRect, aFlags);
   5424 }
   5425 
   5426 static void AddDisplayItemToBottom(nsDisplayListBuilder* aBuilder,
   5427                                   nsDisplayList* aList, nsDisplayItem* aItem) {
   5428  nsDisplayList list(aBuilder);
   5429  list.AppendToTop(aItem);
   5430  list.AppendToTop(aList);
   5431  aList->AppendToTop(&list);
   5432 }
   5433 
   5434 void PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder* aBuilder,
   5435                                             nsDisplayList* aList,
   5436                                             nsIFrame* aFrame,
   5437                                             const nsRect& aBounds,
   5438                                             nscolor aBackstopColor) {
   5439  if (aBounds.IsEmpty() || !aFrame->IsViewportFrame()) {
   5440    // We don't want to add an item for the canvas background color if the frame
   5441    // (sub)tree we are painting doesn't include any canvas frames.
   5442    return;
   5443  }
   5444 
   5445  const SingleCanvasBackground& canvasBg = mCanvasBackground.mViewport;
   5446  const nscolor bgcolor = NS_ComposeColors(aBackstopColor, canvasBg.mColor);
   5447  if (NS_GET_A(bgcolor) == 0) {
   5448    return;
   5449  }
   5450 
   5451  // With async scrolling, we'd like to have two instances of the background
   5452  // color: one that scrolls with the content and one underneath which does not
   5453  // scroll with the content, but which can be shown during checkerboarding and
   5454  // overscroll and the dynamic toolbar movement. We can only do that if the
   5455  // color is opaque.
   5456  //
   5457  // We also need to paint the background if CSS hasn't specified it (since
   5458  // otherwise nsCanvasFrame might not paint it). Note that non-CSS-specified
   5459  // backgrounds shouldn't ever be semi-transparent.
   5460  const bool forceUnscrolledItem =
   5461      nsLayoutUtils::UsesAsyncScrolling(aFrame) && NS_GET_A(bgcolor) == 255;
   5462  if (canvasBg.mCSSSpecified && !forceUnscrolledItem) {
   5463    return;
   5464  }
   5465 
   5466  MOZ_ASSERT(NS_GET_A(bgcolor) == 255);
   5467  const bool isRootContentDocumentCrossProcess =
   5468      mPresContext->IsRootContentDocumentCrossProcess();
   5469  MOZ_ASSERT_IF(
   5470      !aFrame->GetParent() && isRootContentDocumentCrossProcess &&
   5471          mPresContext->HasDynamicToolbar(),
   5472      aBounds.Size() ==
   5473          nsLayoutUtils::ExpandHeightForDynamicToolbar(
   5474              mPresContext, aFrame->InkOverflowRectRelativeToSelf().Size()));
   5475 
   5476  nsDisplaySolidColor* item =
   5477      MakeDisplayItem<nsDisplaySolidColor>(aBuilder, aFrame, aBounds, bgcolor);
   5478  if (canvasBg.mCSSSpecified && isRootContentDocumentCrossProcess) {
   5479    item->SetIsCheckerboardBackground();
   5480  }
   5481  AddDisplayItemToBottom(aBuilder, aList, item);
   5482 }
   5483 
   5484 bool PresShell::IsTransparentContainerElement() const {
   5485  if (mDocument->IsInitialDocument()) {
   5486    switch (StaticPrefs::layout_css_initial_document_transparency()) {
   5487      case 3:
   5488        return true;
   5489      case 2:
   5490        if (!mDocument->IsTopLevelContentDocument()) {
   5491          return true;
   5492        }
   5493        [[fallthrough]];
   5494      case 1:
   5495        if (mDocument->IsLikelyContentInaccessibleTopLevelAboutBlank()) {
   5496          return true;
   5497        }
   5498        [[fallthrough]];
   5499      default:
   5500        break;
   5501    }
   5502  }
   5503 
   5504  nsPresContext* pc = GetPresContext();
   5505  if (!pc->IsRootContentDocumentCrossProcess()) {
   5506    if (mDocument->IsInChromeDocShell()) {
   5507      return true;
   5508    }
   5509    // Frames are transparent except if their used embedder color-scheme is
   5510    // mismatched, in which case we use an opaque background to avoid
   5511    // black-on-black or white-on-white text, see
   5512    // https://github.com/w3c/csswg-drafts/issues/4772
   5513    if (BrowsingContext* bc = mDocument->GetBrowsingContext()) {
   5514      switch (bc->GetEmbedderColorSchemes().mUsed) {
   5515        case dom::PrefersColorSchemeOverride::Light:
   5516          return pc->DefaultBackgroundColorScheme() == ColorScheme::Light;
   5517        case dom::PrefersColorSchemeOverride::Dark:
   5518          return pc->DefaultBackgroundColorScheme() == ColorScheme::Dark;
   5519        case dom::PrefersColorSchemeOverride::None:
   5520          break;
   5521      }
   5522    }
   5523    return true;
   5524  }
   5525 
   5526  nsIDocShell* docShell = pc->GetDocShell();
   5527  if (!docShell) {
   5528    return false;
   5529  }
   5530  nsPIDOMWindowOuter* pwin = docShell->GetWindow();
   5531  if (!pwin) {
   5532    return false;
   5533  }
   5534  if (Element* containerElement = pwin->GetFrameElementInternal()) {
   5535    return containerElement->HasAttr(nsGkAtoms::transparent);
   5536  }
   5537  if (BrowserChild* tab = BrowserChild::GetFrom(docShell)) {
   5538    // Check if presShell is the top PresShell. Only the top can influence the
   5539    // canvas background color.
   5540    return this == tab->GetTopLevelPresShell() && tab->IsTransparent();
   5541  }
   5542  return false;
   5543 }
   5544 
   5545 nscolor PresShell::GetDefaultBackgroundColorToDraw() const {
   5546  if (!mPresContext) {
   5547    return NS_RGB(255, 255, 255);
   5548  }
   5549  return mPresContext->DefaultBackgroundColor();
   5550 }
   5551 
   5552 void PresShell::UpdateCanvasBackground() {
   5553  mCanvasBackground = ComputeCanvasBackground();
   5554 }
   5555 
   5556 static SingleCanvasBackground ComputeSingleCanvasBackground(nsIFrame* aCanvas) {
   5557  MOZ_ASSERT(aCanvas->IsCanvasFrame());
   5558  const nsIFrame* bgFrame = nsCSSRendering::FindBackgroundFrame(aCanvas);
   5559  static constexpr nscolor kTransparent = NS_RGBA(0, 0, 0, 0);
   5560  if (bgFrame->IsThemed()) {
   5561    // Ignore the CSS background-color if `appearance` is used on the root.
   5562    return {kTransparent, false};
   5563  }
   5564  bool drawBackgroundImage = false;
   5565  bool drawBackgroundColor = false;
   5566  nscolor color = nsCSSRendering::DetermineBackgroundColor(
   5567      aCanvas->PresContext(), bgFrame->Style(), aCanvas, drawBackgroundImage,
   5568      drawBackgroundColor);
   5569  if (!drawBackgroundColor) {
   5570    // No need to draw the CSS-specified background (or no CSS-specified
   5571    // background at all).
   5572    return {kTransparent, false};
   5573  }
   5574  return {color, true};
   5575 }
   5576 
   5577 PresShell::CanvasBackground PresShell::ComputeCanvasBackground() const {
   5578  // If we have a frame tree and it has style information that
   5579  // specifies the background color of the canvas, update our local
   5580  // cache of that color.
   5581  nsIFrame* canvas = GetCanvasFrame();
   5582  if (!canvas) {
   5583    nscolor color = GetDefaultBackgroundColorToDraw();
   5584    // If the root element of the document (ie html) has style 'display: none'
   5585    // then the document's background color does not get drawn; return the color
   5586    // we actually draw.
   5587    return {{color, false}, {color, false}};
   5588  }
   5589 
   5590  auto viewportBg = ComputeSingleCanvasBackground(canvas);
   5591  if (!IsTransparentContainerElement()) {
   5592    viewportBg.mColor =
   5593        NS_ComposeColors(GetDefaultBackgroundColorToDraw(), viewportBg.mColor);
   5594  }
   5595  auto pageBg = viewportBg;
   5596  nsCanvasFrame* docElementCb =
   5597      mFrameConstructor->GetDocElementContainingBlock();
   5598  if (canvas != docElementCb) {
   5599    // We're in paged mode / print / print-preview, and just computed the "root"
   5600    // canvas background. Compute the doc element containing block background
   5601    // too.
   5602    MOZ_ASSERT(mPresContext->IsRootPaginatedDocument());
   5603    pageBg = ComputeSingleCanvasBackground(docElementCb);
   5604  }
   5605  return {viewportBg, pageBg};
   5606 }
   5607 
   5608 nscolor PresShell::ComputeBackstopColor(nsIFrame* aDisplayRoot) {
   5609  nsIWidget* widget =
   5610      aDisplayRoot ? aDisplayRoot->GetNearestWidget() : GetNearestWidget();
   5611  if (widget &&
   5612      (widget->GetTransparencyMode() != widget::TransparencyMode::Opaque ||
   5613       widget->WidgetPaintsBackground())) {
   5614    // Within a transparent widget, so the backstop color must be
   5615    // totally transparent.
   5616    return NS_RGBA(0, 0, 0, 0);
   5617  }
   5618  // Within an opaque widget (or no widget at all), so the backstop
   5619  // color must be totally opaque. The user's default background
   5620  // as reported by the prescontext is guaranteed to be opaque.
   5621  return GetDefaultBackgroundColorToDraw();
   5622 }
   5623 
   5624 struct PaintParams {
   5625  nscolor mBackgroundColor;
   5626 };
   5627 
   5628 WindowRenderer* PresShell::GetWindowRenderer() {
   5629  if (nsIWidget* widget = GetOwnWidget()) {
   5630    return widget->GetWindowRenderer();
   5631  }
   5632  return nullptr;
   5633 }
   5634 
   5635 nsIWidget* PresShell::GetNearestWidget() const {
   5636  if (auto* widget = GetOwnWidget()) {
   5637    return widget;
   5638  }
   5639  if (auto* embedder = GetInProcessEmbedderFrame()) {
   5640    return embedder->GetNearestWidget();
   5641  }
   5642  return GetRootWidget();
   5643 }
   5644 
   5645 nsIWidget* PresShell::GetOwnWidget() const {
   5646  return mWidgetListener ? mWidgetListener->GetWidget() : nullptr;
   5647 }
   5648 
   5649 bool PresShell::AsyncPanZoomEnabled() {
   5650  if (nsIWidget* widget = GetOwnWidget()) {
   5651    return widget->AsyncPanZoomEnabled();
   5652  }
   5653  return gfxPlatform::AsyncPanZoomEnabled();
   5654 }
   5655 
   5656 nsresult PresShell::SetResolutionAndScaleTo(float aResolution,
   5657                                            ResolutionChangeOrigin aOrigin) {
   5658  if (!(aResolution > 0.0)) {
   5659    return NS_ERROR_ILLEGAL_VALUE;
   5660  }
   5661  if (aResolution == mResolution.valueOr(0.0)) {
   5662    MOZ_ASSERT(mResolution.isSome());
   5663    return NS_OK;
   5664  }
   5665 
   5666  // GetResolution handles mResolution being nothing by returning 1 so this
   5667  // is checking that the resolution is actually changing.
   5668  bool resolutionUpdated = aResolution != GetResolution();
   5669 
   5670  mLastResolutionChangeOrigin = aOrigin;
   5671 
   5672  RenderingState state(this);
   5673  state.mResolution = Some(aResolution);
   5674  SetRenderingState(state);
   5675  if (mMobileViewportManager) {
   5676    mMobileViewportManager->ResolutionUpdated(aOrigin);
   5677  }
   5678  // Changing the resolution changes the visual viewport size which may
   5679  // make the current visual viewport offset out-of-bounds (if the size
   5680  // increased). APZ will reconcile this by sending a clamped visual
   5681  // viewport offset on the next repaint, but to avoid main-thread code
   5682  // observing an out-of-bounds offset until then, reclamp it here.
   5683  if (IsVisualViewportOffsetSet()) {
   5684    SetVisualViewportOffset(GetVisualViewportOffset(),
   5685                            GetLayoutViewportOffset());
   5686  }
   5687  if (aOrigin == ResolutionChangeOrigin::Apz) {
   5688    mResolutionUpdatedByApz = true;
   5689  } else if (resolutionUpdated) {
   5690    mResolutionUpdated = true;
   5691  }
   5692 
   5693  if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
   5694    window->VisualViewport()->PostResizeEvent();
   5695  }
   5696 
   5697  return NS_OK;
   5698 }
   5699 
   5700 float PresShell::GetCumulativeResolution() const {
   5701  float resolution = GetResolution();
   5702  nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
   5703  if (parentCtx) {
   5704    resolution *= parentCtx->PresShell()->GetCumulativeResolution();
   5705  }
   5706  return resolution;
   5707 }
   5708 
   5709 void PresShell::SetRestoreResolution(float aResolution,
   5710                                     LayoutDeviceIntSize aDisplaySize) {
   5711  if (mMobileViewportManager) {
   5712    mMobileViewportManager->SetRestoreResolution(aResolution, aDisplaySize);
   5713  }
   5714 }
   5715 
   5716 void PresShell::SetRenderingState(const RenderingState& aState) {
   5717  if (GetResolution() != aState.mResolution.valueOr(1.f)) {
   5718    if (nsIFrame* frame = GetRootFrame()) {
   5719      frame->SchedulePaint();
   5720    }
   5721  }
   5722 
   5723  mRenderingStateFlags = aState.mRenderingStateFlags;
   5724  mResolution = aState.mResolution;
   5725 #ifdef ACCESSIBILITY
   5726  if (nsAccessibilityService* accService = GetAccService()) {
   5727    accService->NotifyOfResolutionChange(this, GetResolution());
   5728  }
   5729 #endif
   5730 }
   5731 
   5732 void PresShell::SynthesizeMouseMove(bool aFromScroll) {
   5733  if (!StaticPrefs::layout_reflow_synthMouseMove()) {
   5734    return;
   5735  }
   5736 
   5737  if (mPaintingSuppressed || !mIsActive || !mPresContext) {
   5738    return;
   5739  }
   5740 
   5741  if (!IsRoot()) {
   5742    if (PresShell* rootPresShell = GetRootPresShell()) {
   5743      rootPresShell->SynthesizeMouseMove(aFromScroll);
   5744    }
   5745    return;
   5746  }
   5747 
   5748  if (mLastMousePointerId.isNothing() && mPointerIds.IsEmpty()) {
   5749    return;
   5750  }
   5751 
   5752  if (!mSynthMouseMoveEvent.IsPending()) {
   5753    auto ev = MakeRefPtr<nsSynthMouseMoveEvent>(this, aFromScroll);
   5754 
   5755    GetPresContext()->RefreshDriver()->AddRefreshObserver(
   5756        ev, FlushType::Display, "Synthetic mouse move event");
   5757    mSynthMouseMoveEvent = std::move(ev);
   5758  }
   5759 }
   5760 
   5761 static nsMenuPopupFrame* FindPopupFrame(nsPresContext* aRootPresContext,
   5762                                        nsIWidget* aRootWidget,
   5763                                        const LayoutDeviceIntPoint& aPt) {
   5764  return nsLayoutUtils::GetPopupFrameForPoint(
   5765      aRootPresContext, aRootWidget, aPt,
   5766      nsLayoutUtils::GetPopupFrameForPointFlags::OnlyReturnFramesWithWidgets);
   5767 }
   5768 
   5769 void PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) {
   5770  auto forgetMouseMove = MakeScopeExit([&]() {
   5771    // Must be safe to refer `this` without grabbing it with a RefPtr since this
   5772    // method is marked as MOZ_CAN_RUN_SCRIPT, and we don't want to use RefPtr
   5773    // here because of a hot path
   5774    mSynthMouseMoveEvent.Forget();
   5775  });
   5776  // If drag session has started, we shouldn't synthesize mousemove event.
   5777  nsIWidget* widget = GetOwnWidget();
   5778  if (!widget) {
   5779    return;
   5780  }
   5781  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(widget);
   5782  if (dragSession) {
   5783    // Don't forget it.  We need to synthesize a mouse move when the drag
   5784    // session ends.
   5785    forgetMouseMove.release();
   5786    return;
   5787  }
   5788 
   5789  if (!mPresContext) {
   5790    return;
   5791  }
   5792 
   5793  // allow new event to be posted while handling this one only if the
   5794  // source of the event is a scroll (to prevent infinite reflow loops)
   5795  if (aFromScroll) {
   5796    mSynthMouseMoveEvent.Forget();
   5797    forgetMouseMove.release();
   5798  }
   5799 
   5800  NS_ASSERTION(IsRoot(), "Only a root pres shell should be here");
   5801 
   5802  if (StaticPrefs::dom_event_pointer_boundary_dispatch_when_layout_change()) {
   5803    const AutoTArray<uint32_t, 16> pointerIds(mPointerIds.Clone());
   5804    for (const uint32_t pointerId : pointerIds) {
   5805      const PointerInfo* const pointerInfo =
   5806          PointerEventHandler::GetPointerInfo(pointerId);
   5807      if (MOZ_UNLIKELY(!pointerInfo) || !pointerInfo->HasLastState() ||
   5808          // We shouldn't dispatch pointer boundary events when a layout change
   5809          // if the pointer is not a stationary device.
   5810          !pointerInfo->InputSourceSupportsHover()) {
   5811        continue;
   5812      }
   5813      // If the pointer is captured, we don't need to dispatch pointer boundary
   5814      // events since pointer boundary events should be fired before
   5815      // gotpointercapture.
   5816      PointerCaptureInfo* const captureInfo =
   5817          PointerEventHandler::GetPointerCaptureInfo(pointerId);
   5818      if (captureInfo && captureInfo->mOverrideElement) {
   5819        continue;
   5820      }
   5821      ProcessSynthMouseOrPointerMoveEvent(ePointerMove, pointerId,
   5822                                          *pointerInfo);
   5823    }
   5824  }
   5825 
   5826  if (mLastMousePointerId.isSome()) {
   5827    if (const PointerInfo* const lastMouseInfo =
   5828            PointerEventHandler::GetLastMouseInfo(this)) {
   5829      if (lastMouseInfo->HasLastState()) {
   5830        ProcessSynthMouseOrPointerMoveEvent(eMouseMove, *mLastMousePointerId,
   5831                                            *lastMouseInfo);
   5832      }
   5833    }
   5834  }
   5835 }
   5836 
   5837 void PresShell::ProcessSynthMouseOrPointerMoveEvent(
   5838    EventMessage aMoveMessage, uint32_t aPointerId,
   5839    const PointerInfo& aPointerInfo) {
   5840  MOZ_ASSERT(aMoveMessage == eMouseMove || aMoveMessage == ePointerMove);
   5841  NS_ASSERTION(IsRoot(), "Only a root pres shell should be here");
   5842 
   5843 #ifdef DEBUG
   5844  if (aMoveMessage == eMouseMove || aMoveMessage == ePointerMove) {
   5845    MOZ_LOG(aMoveMessage == eMouseMove
   5846                ? PointerEventHandler::MouseLocationLogRef()
   5847                : PointerEventHandler::PointerLocationLogRef(),
   5848            LogLevel::Info,
   5849            ("[ps=%p]synthesizing %s to (%d,%d) (pointerId=%u, source=%s)\n",
   5850             this, ToChar(aMoveMessage), aPointerInfo.mLastRefPointInRootDoc.x,
   5851             aPointerInfo.mLastRefPointInRootDoc.y, aPointerId,
   5852             InputSourceToString(aPointerInfo.mInputSource).get()));
   5853  }
   5854 #endif
   5855 
   5856  int32_t APD = mPresContext->AppUnitsPerDevPixel();
   5857 
   5858  // mRefPoint will be mMouseLocation relative to the widget of |view|, the
   5859  // widget we will put in the event we dispatch, in widgetAPD appunits
   5860  nsPoint refpoint(0, 0);
   5861 
   5862  nsIWidget* ownWidget = GetOwnWidget();
   5863  if (!ownWidget) {
   5864    return;
   5865  }
   5866  MOZ_ASSERT(!nsCOMPtr{nsContentUtils::GetDragSession(ownWidget)});
   5867 
   5868  // We need a widget to put in the event we are going to dispatch so we look
   5869  // for a view that has a widget and the mouse location is over. We first look
   5870  // for floating views, if there isn't one we use the root view. |view| holds
   5871  // that view.
   5872  nsCOMPtr<nsIWidget> widget;
   5873  // We always dispatch the event to the pres shell that contains the view that
   5874  // the mouse is over. pointShell is that.
   5875  RefPtr<PresShell> pointShell;
   5876  // The appunits per devpixel ratio of |widget|.
   5877  int32_t widgetAPD;
   5878  // If we're in a child process and the view points to an OOP iframe, this is
   5879  // its BrowserBridgeChild.
   5880  RefPtr<BrowserBridgeChild> bbc;
   5881 
   5882  // We either dispatch the event to a popup, or a view.
   5883  nsMenuPopupFrame* popupFrame =
   5884      FindPopupFrame(mPresContext, ownWidget,
   5885                     LayoutDeviceIntPoint::FromAppUnitsToNearest(
   5886                         aPointerInfo.mLastRefPointInRootDoc, APD));
   5887  if (popupFrame) {
   5888    pointShell = popupFrame->PresShell();
   5889    widget = popupFrame->GetWidget();
   5890    widgetAPD = popupFrame->PresContext()->AppUnitsPerDevPixel();
   5891    refpoint = aPointerInfo.mLastRefPointInRootDoc;
   5892    DebugOnly<nsLayoutUtils::TransformResult> result =
   5893        nsLayoutUtils::TransformPoint(
   5894            RelativeTo{GetRootFrame(), ViewportType::Visual},
   5895            RelativeTo{popupFrame, ViewportType::Layout}, refpoint);
   5896    MOZ_ASSERT(result == nsLayoutUtils::TRANSFORM_SUCCEEDED);
   5897  }
   5898  if (!widget) {
   5899    widget = ownWidget;
   5900    widgetAPD = APD;
   5901    pointShell = this;
   5902    refpoint = aPointerInfo.mLastRefPointInRootDoc;
   5903  }
   5904  NS_ASSERTION(widget, "view should have a widget here");
   5905  Maybe<WidgetMouseEvent> mouseMoveEvent;
   5906  Maybe<WidgetPointerEvent> pointerMoveEvent;
   5907  if (aMoveMessage == eMouseMove) {
   5908    mouseMoveEvent.emplace(true, eMouseMove, widget,
   5909                           WidgetMouseEvent::eSynthesized);
   5910    mouseMoveEvent->mButton = MouseButton::ePrimary;
   5911    // We don't want to dispatch preceding pointer event since the caller
   5912    // should've already been dispatched it.  However, if the target is an OOP
   5913    // iframe, we'll set this to true again below.
   5914    mouseMoveEvent->convertToPointer = false;
   5915  } else {
   5916    pointerMoveEvent.emplace(true, ePointerMove, widget);
   5917    pointerMoveEvent->mButton = MouseButton::eNotPressed;
   5918    pointerMoveEvent->mReason = WidgetMouseEvent::eSynthesized;
   5919  }
   5920  WidgetMouseEvent& event =
   5921      mouseMoveEvent ? mouseMoveEvent.ref() : pointerMoveEvent.ref();
   5922 
   5923  // If the last cursor location was set by a synthesized mouse event for tests,
   5924  // running test should expect a restyle or a DOM mutation under the cursor may
   5925  // cause mouse boundary events in a remote process if the cursor is over a
   5926  // remote content.  Therefore, the events should not be ignored by
   5927  // PresShell::HandleEvent in the remote process.  So we need to mark the
   5928  // synthesized eMouseMove as "synthesized for tests".
   5929  event.mFlags.mIsSynthesizedForTests = aPointerInfo.mIsSynthesizedForTests;
   5930 
   5931  event.mRefPoint =
   5932      LayoutDeviceIntPoint::FromAppUnitsToNearest(refpoint, widgetAPD);
   5933  event.mButtons = aPointerInfo.mLastButtons;
   5934  event.mInputSource = aPointerInfo.mInputSource;
   5935  event.pointerId = aPointerId;
   5936  event.mModifiers = PresShell::GetCurrentModifiers();
   5937 
   5938  MOZ_ASSERT(pointShell);
   5939  // Since this gets run in a refresh tick there isn't an InputAPZContext on
   5940  // the stack from the nsIWidget. We need to simulate one with at least
   5941  // the correct target guid, so that the correct callback transform gets
   5942  // applied if this event goes to a child process. The input block id is set
   5943  // to 0 because this is a synthetic event which doesn't really belong to any
   5944  // input block. Same for the APZ response field.
   5945  InputAPZContext apzContext(aPointerInfo.mLastTargetGuid, 0,
   5946                             nsEventStatus_eIgnore);
   5947  AUTO_PROFILER_MARKER_DOCSHELL("DispatchSynthMouseOrPointerMove", GRAPHICS,
   5948                                pointShell->GetPresContext()->GetDocShell());
   5949  nsEventStatus status = nsEventStatus_eIgnore;
   5950  if (auto* eventFrame = popupFrame ? popupFrame : GetRootFrame()) {
   5951    pointShell->HandleEvent(eventFrame, &event, false, &status);
   5952  }
   5953 }
   5954 
   5955 /* static */
   5956 void PresShell::MarkFramesInListApproximatelyVisible(
   5957    const nsDisplayList& aList) {
   5958  for (nsDisplayItem* item : aList) {
   5959    nsDisplayList* sublist = item->GetChildren();
   5960    if (sublist) {
   5961      MarkFramesInListApproximatelyVisible(*sublist);
   5962      continue;
   5963    }
   5964 
   5965    nsIFrame* frame = item->Frame();
   5966    MOZ_ASSERT(frame);
   5967 
   5968    if (!frame->TrackingVisibility()) {
   5969      continue;
   5970    }
   5971 
   5972    // Use the presshell containing the frame.
   5973    PresShell* presShell = frame->PresShell();
   5974    MOZ_ASSERT(!presShell->AssumeAllFramesVisible());
   5975    if (presShell->mApproximatelyVisibleFrames.EnsureInserted(frame)) {
   5976      // The frame was added to mApproximatelyVisibleFrames, so increment its
   5977      // visible count.
   5978      frame->IncApproximateVisibleCount();
   5979    }
   5980  }
   5981 }
   5982 
   5983 /* static */
   5984 void PresShell::DecApproximateVisibleCount(
   5985    VisibleFrames& aFrames, const Maybe<OnNonvisible>& aNonvisibleAction
   5986    /* = Nothing() */) {
   5987  for (nsIFrame* frame : aFrames) {
   5988    // Decrement the frame's visible count if we're still tracking its
   5989    // visibility. (We may not be, if the frame disabled visibility tracking
   5990    // after we added it to the visible frames list.)
   5991    if (frame->TrackingVisibility()) {
   5992      frame->DecApproximateVisibleCount(aNonvisibleAction);
   5993    }
   5994  }
   5995 }
   5996 
   5997 void PresShell::RebuildApproximateFrameVisibilityDisplayList(
   5998    const nsDisplayList& aList) {
   5999  MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
   6000  mApproximateFrameVisibilityVisited = true;
   6001 
   6002  // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
   6003  // them in oldApproxVisibleFrames.
   6004  VisibleFrames oldApproximatelyVisibleFrames =
   6005      std::move(mApproximatelyVisibleFrames);
   6006 
   6007  MarkFramesInListApproximatelyVisible(aList);
   6008 
   6009  DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
   6010 }
   6011 
   6012 void PresShell::ClearApproximateFrameVisibilityVisited() {
   6013  if (!mApproximateFrameVisibilityVisited) {
   6014    ClearApproximatelyVisibleFramesList();
   6015  }
   6016  mApproximateFrameVisibilityVisited = false;
   6017  mDocument->EnumerateSubDocuments([](Document& aSubdoc) {
   6018    if (auto* ps = aSubdoc.GetPresShell()) {
   6019      ps->ClearApproximateFrameVisibilityVisited();
   6020    }
   6021    return CallState::Continue;
   6022  });
   6023 }
   6024 
   6025 void PresShell::ClearApproximatelyVisibleFramesList(
   6026    const Maybe<OnNonvisible>& aNonvisibleAction
   6027    /* = Nothing() */) {
   6028  DecApproximateVisibleCount(mApproximatelyVisibleFrames, aNonvisibleAction);
   6029  mApproximatelyVisibleFrames.Clear();
   6030 }
   6031 
   6032 // aRect is relative to aFrame
   6033 // aPreserve3DRect is set upon entering a preserve3d context and it doesn't
   6034 // change, it stays relative to the root frame in the preserve3d context. Any
   6035 // frame that is in a preserve3d context ignores aRect but takes aPreserve3DRect
   6036 // and transforms it from the root of the preserve3d context to itself
   6037 // (nsDisplayTransform::UntransformRect does this by default), and passes the
   6038 // result down as aRect (leaving aPreserve3DRect untouched). Additionally, we
   6039 // descend into every frame inside the preserve3d context (we skip the rect
   6040 // intersection test). Any frame that is not in a preserve3d context just uses
   6041 // aRect and doesn't need to know about any of this, even if it's parent frame
   6042 // is in the preserve3d context. Any frame that is extend3d (ie has preserve3d
   6043 // transform style) but not combines3d (ie its either transformed or backface
   6044 // visibility hidden and its parent has preserve3d style) forms the root of a
   6045 // preserve3d context. And any frame that is combines3d is in a preserve3d
   6046 // context.
   6047 void PresShell::MarkFramesInSubtreeApproximatelyVisible(
   6048    nsIFrame* aFrame, const nsRect& aRect, const nsRect& aPreserve3DRect,
   6049    bool aRemoveOnly /* = false */) {
   6050  MOZ_DIAGNOSTIC_ASSERT(aFrame, "aFrame arg should be a valid frame pointer");
   6051  MOZ_ASSERT(aFrame->PresShell() == this, "wrong presshell");
   6052 
   6053  if (aFrame->TrackingVisibility() && aFrame->StyleVisibility()->IsVisible() &&
   6054      (!aRemoveOnly ||
   6055       aFrame->GetVisibility() == Visibility::ApproximatelyVisible)) {
   6056    MOZ_ASSERT(!AssumeAllFramesVisible());
   6057    if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
   6058      // The frame was added to mApproximatelyVisibleFrames, so increment its
   6059      // visible count.
   6060      aFrame->IncApproximateVisibleCount();
   6061    }
   6062  }
   6063 
   6064  nsSubDocumentFrame* subdocFrame = do_QueryFrame(aFrame);
   6065  if (subdocFrame) {
   6066    PresShell* presShell = subdocFrame->GetSubdocumentPresShellForPainting(
   6067        nsSubDocumentFrame::IGNORE_PAINT_SUPPRESSION);
   6068    if (presShell && !presShell->AssumeAllFramesVisible()) {
   6069      nsRect rect = aRect;
   6070      nsIFrame* root = presShell->GetRootFrame();
   6071      if (root) {
   6072        rect.MoveBy(aFrame->GetOffsetToCrossDoc(root));
   6073      } else {
   6074        rect.MoveBy(-aFrame->GetContentRectRelativeToSelf().TopLeft());
   6075      }
   6076      rect = rect.ScaleToOtherAppUnitsRoundOut(
   6077          aFrame->PresContext()->AppUnitsPerDevPixel(),
   6078          presShell->GetPresContext()->AppUnitsPerDevPixel());
   6079 
   6080      presShell->RebuildApproximateFrameVisibility(&rect);
   6081    }
   6082    return;
   6083  }
   6084 
   6085  nsRect rect = aRect;
   6086 
   6087  if (ScrollContainerFrame* scrollFrame = do_QueryFrame(aFrame)) {
   6088    bool ignoreDisplayPort = false;
   6089    if (DisplayPortUtils::IsMissingDisplayPortBaseRect(aFrame->GetContent())) {
   6090      // We can properly set the base rect for root scroll frames on top level
   6091      // and root content documents. Otherwise the base rect we compute might
   6092      // be way too big without the limiting that
   6093      // ScrollContainerFrame::DecideScrollableLayer does, so we just ignore the
   6094      // displayport in that case.
   6095      nsPresContext* pc = aFrame->PresContext();
   6096      if (scrollFrame->IsRootScrollFrameOfDocument() &&
   6097          (pc->IsRootContentDocumentCrossProcess() ||
   6098           (pc->IsChrome() && !pc->GetParentPresContext()))) {
   6099        nsRect baseRect(
   6100            nsPoint(), nsLayoutUtils::CalculateCompositionSizeForFrame(aFrame));
   6101        DisplayPortUtils::SetDisplayPortBase(aFrame->GetContent(), baseRect);
   6102      } else {
   6103        ignoreDisplayPort = true;
   6104      }
   6105    }
   6106 
   6107    nsRect displayPort;
   6108    bool usingDisplayport =
   6109        !ignoreDisplayPort &&
   6110        DisplayPortUtils::GetDisplayPortForVisibilityTesting(
   6111            aFrame->GetContent(), &displayPort);
   6112 
   6113    scrollFrame->NotifyApproximateFrameVisibilityUpdate(!usingDisplayport);
   6114 
   6115    if (usingDisplayport) {
   6116      rect = displayPort;
   6117    } else {
   6118      rect = rect.Intersect(scrollFrame->GetScrollPortRect());
   6119    }
   6120    rect = scrollFrame->ExpandRectToNearlyVisible(rect);
   6121  }
   6122 
   6123  for (const auto& [list, listID] : aFrame->ChildLists()) {
   6124    for (nsIFrame* child : list) {
   6125      // Note: This assert should be trivially satisfied, just by virtue of how
   6126      // nsFrameList and its iterator works (with nullptr being an end-of-list
   6127      // sentinel which should terminate the loop).  But we do somehow get
   6128      // crash reports inside this loop that suggest `child` is null...
   6129      MOZ_DIAGNOSTIC_ASSERT(child, "shouldn't have null values in child lists");
   6130 
   6131      const bool extend3DContext = child->Extend3DContext();
   6132      const bool combines3DTransformWithAncestors =
   6133          (extend3DContext || child->IsTransformed()) &&
   6134          child->Combines3DTransformWithAncestors();
   6135 
   6136      nsRect r = rect - child->GetPosition();
   6137      if (!combines3DTransformWithAncestors) {
   6138        if (!r.IntersectRect(r, child->InkOverflowRect())) {
   6139          continue;
   6140        }
   6141      }
   6142 
   6143      nsRect newPreserve3DRect = aPreserve3DRect;
   6144      if (extend3DContext && !combines3DTransformWithAncestors) {
   6145        newPreserve3DRect = r;
   6146      }
   6147 
   6148      if (child->IsTransformed()) {
   6149        if (combines3DTransformWithAncestors) {
   6150          r = newPreserve3DRect;
   6151        }
   6152        const nsRect overflow = child->InkOverflowRectRelativeToSelf();
   6153        nsRect out;
   6154        if (nsDisplayTransform::UntransformRect(r, overflow, child, &out)) {
   6155          r = out;
   6156        } else {
   6157          r.SetEmpty();
   6158        }
   6159      }
   6160      MarkFramesInSubtreeApproximatelyVisible(child, r, newPreserve3DRect,
   6161                                              aRemoveOnly);
   6162    }
   6163  }
   6164 }
   6165 
   6166 void PresShell::RebuildApproximateFrameVisibility(
   6167    nsRect* aRect, bool aRemoveOnly /* = false */) {
   6168  MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "already visited?");
   6169  mApproximateFrameVisibilityVisited = true;
   6170 
   6171  nsIFrame* rootFrame = GetRootFrame();
   6172  if (!rootFrame) {
   6173    return;
   6174  }
   6175 
   6176  // Remove the entries of the mApproximatelyVisibleFrames hashtable and put
   6177  // them in oldApproximatelyVisibleFrames.
   6178  VisibleFrames oldApproximatelyVisibleFrames =
   6179      std::move(mApproximatelyVisibleFrames);
   6180 
   6181  nsRect vis(nsPoint(0, 0), rootFrame->GetSize());
   6182  if (aRect) {
   6183    vis = *aRect;
   6184  }
   6185 
   6186  // If we are in-process root but not the top level content, we need to take
   6187  // the intersection with the iframe visible rect.
   6188  if (mPresContext->IsRootContentDocumentInProcess() &&
   6189      !mPresContext->IsRootContentDocumentCrossProcess()) {
   6190    // There are two possibilities that we can't get the iframe's visible
   6191    // rect other than the iframe is out side of ancestors' display ports.
   6192    // a) the BrowserChild is being torn down
   6193    // b) the visible rect hasn't been delivered the BrowserChild
   6194    // In both cases we consider the visible rect is empty.
   6195    Maybe<nsRect> visibleRect;
   6196    if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
   6197      visibleRect = browserChild->GetVisibleRect();
   6198    }
   6199    vis = vis.Intersect(visibleRect.valueOr(nsRect()));
   6200  }
   6201 
   6202  MarkFramesInSubtreeApproximatelyVisible(rootFrame, vis, vis, aRemoveOnly);
   6203 
   6204  DecApproximateVisibleCount(oldApproximatelyVisibleFrames);
   6205 }
   6206 
   6207 void PresShell::UpdateApproximateFrameVisibility() {
   6208  DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ false);
   6209 }
   6210 
   6211 void PresShell::DoUpdateApproximateFrameVisibility(bool aRemoveOnly) {
   6212  MOZ_ASSERT(
   6213      !mPresContext || mPresContext->IsRootContentDocumentInProcess(),
   6214      "Updating approximate frame visibility on a non-root content document?");
   6215 
   6216  mUpdateApproximateFrameVisibilityEvent.Revoke();
   6217 
   6218  if (mHaveShutDown || mIsDestroying) {
   6219    return;
   6220  }
   6221 
   6222  // call update on that frame
   6223  nsIFrame* rootFrame = GetRootFrame();
   6224  if (!rootFrame) {
   6225    ClearApproximatelyVisibleFramesList(Some(OnNonvisible::DiscardImages));
   6226    return;
   6227  }
   6228 
   6229  RebuildApproximateFrameVisibility(/* aRect = */ nullptr, aRemoveOnly);
   6230  ClearApproximateFrameVisibilityVisited();
   6231 
   6232 #ifdef DEBUG_FRAME_VISIBILITY_DISPLAY_LIST
   6233  // This can be used to debug the frame walker by comparing beforeFrameList
   6234  // and mApproximatelyVisibleFrames in RebuildFrameVisibilityDisplayList to see
   6235  // if they produce the same results (mApproximatelyVisibleFrames holds the
   6236  // frames the display list thinks are visible, beforeFrameList holds the
   6237  // frames the frame walker thinks are visible).
   6238  nsDisplayListBuilder builder(
   6239      rootFrame, nsDisplayListBuilderMode::FRAME_VISIBILITY, false);
   6240  nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize());
   6241  nsIFrame* rootScroll = GetRootScrollFrame();
   6242  if (rootScroll) {
   6243    nsIContent* content = rootScroll->GetContent();
   6244    if (content) {
   6245      (void)nsLayoutUtils::GetDisplayPortForVisibilityTesting(
   6246          content, &updateRect, RelativeTo::ScrollFrame);
   6247    }
   6248 
   6249    if (IgnoringViewportScrolling()) {
   6250      builder.SetIgnoreScrollFrame(rootScroll);
   6251    }
   6252  }
   6253  builder.IgnorePaintSuppression();
   6254  builder.EnterPresShell(rootFrame);
   6255  nsDisplayList list;
   6256  rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list);
   6257  builder.LeavePresShell(rootFrame, &list);
   6258 
   6259  RebuildApproximateFrameVisibilityDisplayList(list);
   6260 
   6261  ClearApproximateFrameVisibilityVisited();
   6262 
   6263  list.DeleteAll(&builder);
   6264 #endif
   6265 }
   6266 
   6267 bool PresShell::AssumeAllFramesVisible() {
   6268  if (!StaticPrefs::layout_framevisibility_enabled() || !mPresContext ||
   6269      !mDocument) {
   6270    return true;
   6271  }
   6272 
   6273  // We assume all frames are visible in print, print preview, chrome, and
   6274  // resource docs and don't keep track of them.
   6275  if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
   6276      mPresContext->Type() == nsPresContext::eContext_Print ||
   6277      mPresContext->IsChrome() || mDocument->IsResourceDoc()) {
   6278    return true;
   6279  }
   6280 
   6281  // If we're assuming all frames are visible in the top level content
   6282  // document, we need to in subdocuments as well. Otherwise we can get in a
   6283  // situation where things like animations won't work in subdocuments because
   6284  // their frames appear not to be visible, since we won't schedule an image
   6285  // visibility update if the top level content document is assuming all
   6286  // frames are visible.
   6287  //
   6288  // Note that it's not safe to call IsRootContentDocumentInProcess() if we're
   6289  // currently being destroyed, so we have to check that first.
   6290  if (!mHaveShutDown && !mIsDestroying &&
   6291      !mPresContext->IsRootContentDocumentInProcess()) {
   6292    nsPresContext* presContext =
   6293        mPresContext->GetInProcessRootContentDocumentPresContext();
   6294    if (presContext && presContext->PresShell()->AssumeAllFramesVisible()) {
   6295      return true;
   6296    }
   6297  }
   6298 
   6299  return false;
   6300 }
   6301 
   6302 void PresShell::ScheduleApproximateFrameVisibilityUpdateSoon() {
   6303  if (AssumeAllFramesVisible()) {
   6304    return;
   6305  }
   6306 
   6307  if (!mPresContext) {
   6308    return;
   6309  }
   6310 
   6311  nsRefreshDriver* refreshDriver = mPresContext->RefreshDriver();
   6312  if (!refreshDriver) {
   6313    return;
   6314  }
   6315 
   6316  // Ask the refresh driver to update frame visibility soon.
   6317  refreshDriver->ScheduleFrameVisibilityUpdate();
   6318 }
   6319 
   6320 void PresShell::ScheduleApproximateFrameVisibilityUpdateNow() {
   6321  if (AssumeAllFramesVisible()) {
   6322    return;
   6323  }
   6324 
   6325  if (!mPresContext->IsRootContentDocumentInProcess()) {
   6326    nsPresContext* presContext =
   6327        mPresContext->GetInProcessRootContentDocumentPresContext();
   6328    if (!presContext) {
   6329      return;
   6330    }
   6331    MOZ_ASSERT(presContext->IsRootContentDocumentInProcess(),
   6332               "Didn't get a root prescontext from "
   6333               "GetInProcessRootContentDocumentPresContext?");
   6334    presContext->PresShell()->ScheduleApproximateFrameVisibilityUpdateNow();
   6335    return;
   6336  }
   6337 
   6338  if (mHaveShutDown || mIsDestroying) {
   6339    return;
   6340  }
   6341 
   6342  if (mUpdateApproximateFrameVisibilityEvent.IsPending()) {
   6343    return;
   6344  }
   6345 
   6346  RefPtr<nsRunnableMethod<PresShell>> event =
   6347      NewRunnableMethod("PresShell::UpdateApproximateFrameVisibility", this,
   6348                        &PresShell::UpdateApproximateFrameVisibility);
   6349  nsresult rv = mDocument->Dispatch(do_AddRef(event));
   6350 
   6351  if (NS_SUCCEEDED(rv)) {
   6352    mUpdateApproximateFrameVisibilityEvent = std::move(event);
   6353  }
   6354 }
   6355 
   6356 void PresShell::EnsureFrameInApproximatelyVisibleList(nsIFrame* aFrame) {
   6357  if (!aFrame->TrackingVisibility()) {
   6358    return;
   6359  }
   6360 
   6361  if (AssumeAllFramesVisible()) {
   6362    aFrame->IncApproximateVisibleCount();
   6363    return;
   6364  }
   6365 
   6366 #ifdef DEBUG
   6367  // Make sure it's in this pres shell.
   6368  nsCOMPtr<nsIContent> content = aFrame->GetContent();
   6369  if (content) {
   6370    PresShell* presShell = content->OwnerDoc()->GetPresShell();
   6371    MOZ_ASSERT(!presShell || presShell == this, "wrong shell");
   6372  }
   6373 #endif
   6374 
   6375  if (mApproximatelyVisibleFrames.EnsureInserted(aFrame)) {
   6376    // We inserted a new entry.
   6377    aFrame->IncApproximateVisibleCount();
   6378  }
   6379 }
   6380 
   6381 void PresShell::RemoveFrameFromApproximatelyVisibleList(nsIFrame* aFrame) {
   6382 #ifdef DEBUG
   6383  // Make sure it's in this pres shell.
   6384  nsCOMPtr<nsIContent> content = aFrame->GetContent();
   6385  if (content) {
   6386    PresShell* presShell = content->OwnerDoc()->GetPresShell();
   6387    MOZ_ASSERT(!presShell || presShell == this, "wrong shell");
   6388  }
   6389 #endif
   6390 
   6391  if (AssumeAllFramesVisible()) {
   6392    MOZ_ASSERT(mApproximatelyVisibleFrames.Count() == 0,
   6393               "Shouldn't have any frames in the table");
   6394    return;
   6395  }
   6396 
   6397  if (mApproximatelyVisibleFrames.EnsureRemoved(aFrame) &&
   6398      aFrame->TrackingVisibility()) {
   6399    // aFrame was in the hashtable, and we're still tracking its visibility,
   6400    // so we need to decrement its visible count.
   6401    aFrame->DecApproximateVisibleCount();
   6402  }
   6403 }
   6404 
   6405 void PresShell::PaintAndRequestComposite(nsIFrame* aFrame,
   6406                                         WindowRenderer* aRenderer,
   6407                                         PaintFlags aFlags) {
   6408  if (!mIsActive) {
   6409    return;
   6410  }
   6411 
   6412  NS_ASSERTION(aRenderer, "Must be in paint event");
   6413  if (aRenderer->AsFallback()) {
   6414    // The fallback renderer doesn't do any retaining, so we just need to
   6415    // notify the view and widget that we're invalid, and we'll do a
   6416    // paint+composite from the PaintWindow callback.
   6417    if (nsIWidget* widget = aFrame ? aFrame->GetOwnWidget() : nullptr) {
   6418      auto bounds = widget->GetBounds();
   6419      widget->Invalidate(LayoutDeviceIntRect({}, bounds.Size()));
   6420    }
   6421    return;
   6422  }
   6423 
   6424  // Otherwise we're a retained WebRenderLayerManager, so we want to call
   6425  // Paint to update with any changes and push those to WR.
   6426  PaintInternalFlags flags = PaintInternalFlags::None;
   6427  if (aFlags & PaintFlags::PaintSyncDecodeImages) {
   6428    flags |= PaintInternalFlags::PaintSyncDecodeImages;
   6429  }
   6430  if (aFlags & PaintFlags::PaintCompositeOffscreen) {
   6431    flags |= PaintInternalFlags::PaintCompositeOffscreen;
   6432  }
   6433  PaintInternal(aFrame, aRenderer, flags);
   6434 }
   6435 
   6436 void PresShell::SyncPaintFallback(nsIFrame* aFrame, WindowRenderer* aRenderer) {
   6437  if (!mIsActive) {
   6438    return;
   6439  }
   6440 
   6441  NS_ASSERTION(aRenderer->AsFallback(),
   6442               "Can't do Sync paint for remote renderers");
   6443  if (!aRenderer->AsFallback()) {
   6444    return;
   6445  }
   6446 
   6447  PaintInternal(aFrame, aRenderer, PaintInternalFlags::PaintComposite);
   6448  GetPresContext()->NotifyDidPaintForSubtree();
   6449 }
   6450 
   6451 void PresShell::PaintInternal(nsIFrame* aFrame, WindowRenderer* aRenderer,
   6452                              PaintInternalFlags aFlags) {
   6453  MOZ_ASSERT_IF(aFrame,
   6454                aFrame->IsViewportFrame() || aFrame->IsMenuPopupFrame());
   6455  nsCString url;
   6456  nsIURI* uri = mDocument->GetDocumentURI();
   6457  Document* contentRoot = GetPrimaryContentDocument();
   6458  if (contentRoot) {
   6459    uri = contentRoot->GetDocumentURI();
   6460  }
   6461  url = uri ? uri->GetSpecOrDefault() : "N/A"_ns;
   6462  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS(
   6463      "Paint", GRAPHICS, Substring(url, std::min(size_t(128), url.Length())));
   6464 
   6465  Maybe<js::AutoAssertNoContentJS> nojs;
   6466 
   6467  // On Android, Flash can call into content JS during painting, so we can't
   6468  // assert there. However, we don't rely on this assertion on Android because
   6469  // we don't paint while JS is running.
   6470 #if !defined(MOZ_WIDGET_ANDROID)
   6471  if (!(aFlags & PaintInternalFlags::PaintComposite)) {
   6472    // We need to allow content JS when the flag is set since we may trigger
   6473    // MozAfterPaint events in content in those cases.
   6474    nojs.emplace(dom::danger::GetJSContext());
   6475  }
   6476 #endif
   6477 
   6478  NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
   6479  NS_ASSERTION(aRenderer, "null renderer");
   6480 
   6481  MOZ_ASSERT(!mApproximateFrameVisibilityVisited, "Should have been cleared");
   6482 
   6483  if (!mIsActive) {
   6484    return;
   6485  }
   6486 
   6487  FocusTarget focusTarget;
   6488  if (StaticPrefs::apz_keyboard_enabled_AtStartup()) {
   6489    // Update the focus target for async keyboard scrolling. This will be
   6490    // forwarded to APZ by nsDisplayList::PaintRoot. We need to to do this
   6491    // before we enter the paint phase because dispatching eVoid events can
   6492    // cause layout to happen.
   6493    uint64_t focusSequenceNumber = mAPZFocusSequenceNumber;
   6494    if (nsMenuPopupFrame* popup = do_QueryFrame(aFrame)) {
   6495      focusSequenceNumber = popup->GetAPZFocusSequenceNumber();
   6496    }
   6497    focusTarget = FocusTarget(this, focusSequenceNumber);
   6498  }
   6499 
   6500  nsPresContext* presContext = GetPresContext();
   6501  AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
   6502 
   6503  WebRenderLayerManager* layerManager = aRenderer->AsWebRender();
   6504 
   6505  // Whether or not we should set first paint when painting is suppressed
   6506  // is debatable. For now we'll do it because B2G relied on first paint
   6507  // to configure the viewport and we only want to do that when we have
   6508  // real content to paint. See Bug 798245
   6509  if (mIsFirstPaint && !mPaintingSuppressed) {
   6510    MOZ_LOG(gLog, LogLevel::Debug,
   6511            ("PresShell::Paint, first paint, this=%p", this));
   6512 
   6513    if (layerManager) {
   6514      layerManager->SetIsFirstPaint();
   6515    }
   6516    mIsFirstPaint = false;
   6517  }
   6518 
   6519  const bool offscreen =
   6520      bool(aFlags & PaintInternalFlags::PaintCompositeOffscreen);
   6521 
   6522  if (!aRenderer->BeginTransaction(url)) {
   6523    return;
   6524  }
   6525 
   6526  // Send an updated focus target with this transaction. Be sure to do this
   6527  // before we paint in the case this is an empty transaction.
   6528  if (layerManager) {
   6529    layerManager->SetFocusTarget(focusTarget);
   6530  }
   6531 
   6532  if (aFrame) {
   6533    if (!(aFlags & PaintInternalFlags::PaintSyncDecodeImages) &&
   6534        !aFrame->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)) {
   6535      if (layerManager) {
   6536        layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
   6537      }
   6538 
   6539      if (aRenderer->EndEmptyTransaction(
   6540              (aFlags & PaintInternalFlags::PaintComposite)
   6541                  ? WindowRenderer::END_DEFAULT
   6542                  : WindowRenderer::END_NO_COMPOSITE)) {
   6543        return;
   6544      }
   6545    }
   6546    aFrame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
   6547  }
   6548 
   6549  nscolor bgcolor = ComputeBackstopColor(aFrame);
   6550  PaintFrameFlags flags =
   6551      PaintFrameFlags::WidgetLayers | PaintFrameFlags::ExistingTransaction;
   6552 
   6553  // We force sync-decode for printing / print-preview (printing already does
   6554  // this from nsPageSequenceFrame::PrintNextSheet).
   6555  // We also force sync-decoding via pref for reftests.
   6556  if (aFlags & PaintInternalFlags::PaintSyncDecodeImages ||
   6557      mDocument->IsStaticDocument() ||
   6558      StaticPrefs::image_testing_decode_sync_enabled()) {
   6559    flags |= PaintFrameFlags::SyncDecodeImages;
   6560  }
   6561  if (aFlags & PaintInternalFlags::PaintCompositeOffscreen) {
   6562    flags |= PaintFrameFlags::CompositeOffscreen;
   6563  }
   6564  if (aRenderer->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
   6565    flags |= PaintFrameFlags::ForWebRender;
   6566  }
   6567 
   6568  if (aFrame) {
   6569    // We can paint directly into the widget using its layer manager.
   6570    SelectionNodeCache cache(*this);
   6571    nsLayoutUtils::PaintFrame(nullptr, aFrame, nsRegion(), bgcolor,
   6572                              nsDisplayListBuilderMode::Painting, flags);
   6573    return;
   6574  }
   6575 
   6576  bgcolor = NS_ComposeColors(bgcolor, mCanvasBackground.mViewport.mColor);
   6577 
   6578  if (aRenderer->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
   6579    LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
   6580        presContext->GetVisibleArea(), presContext->AppUnitsPerDevPixel());
   6581    WebRenderBackgroundData data(wr::ToLayoutRect(bounds),
   6582                                 wr::ToColorF(ToDeviceColor(bgcolor)));
   6583    WrFiltersHolder wrFilters;
   6584 
   6585    layerManager->SetTransactionIdAllocator(presContext->RefreshDriver());
   6586    layerManager->EndTransactionWithoutLayer(
   6587        nullptr, nullptr, std::move(wrFilters), &data, 0, offscreen);
   6588    return;
   6589  }
   6590 
   6591  FallbackRenderer* fallback = aRenderer->AsFallback();
   6592  MOZ_ASSERT(fallback);
   6593 
   6594  if (aFlags & PaintInternalFlags::PaintComposite) {
   6595    nsIntRect bounds = presContext->GetVisibleArea().ToOutsidePixels(
   6596        presContext->AppUnitsPerDevPixel());
   6597    fallback->EndTransactionWithColor(bounds, ToDeviceColor(bgcolor));
   6598  }
   6599 }
   6600 
   6601 // static
   6602 void PresShell::SetCapturingContent(nsIContent* aContent, CaptureFlags aFlags,
   6603                                    WidgetEvent* aEvent) {
   6604  // If capture was set for pointer lock, don't unlock unless we are coming
   6605  // out of pointer lock explicitly.
   6606  if (!aContent && sCapturingContentInfo.mPointerLock &&
   6607      !(aFlags & CaptureFlags::PointerLock)) {
   6608    return;
   6609  }
   6610 
   6611  sCapturingContentInfo.mContent = nullptr;
   6612  sCapturingContentInfo.mRemoteTarget = nullptr;
   6613 
   6614  // only set capturing content if allowed or the
   6615  // CaptureFlags::IgnoreAllowedState or CaptureFlags::PointerLock are used.
   6616  if ((aFlags & CaptureFlags::IgnoreAllowedState) ||
   6617      sCapturingContentInfo.mAllowed || (aFlags & CaptureFlags::PointerLock)) {
   6618    if (aContent) {
   6619      sCapturingContentInfo.mContent = aContent;
   6620    }
   6621    if (aEvent) {
   6622      MOZ_ASSERT(XRE_IsParentProcess());
   6623      MOZ_ASSERT(aEvent->mMessage == eMouseDown);
   6624      MOZ_ASSERT(aEvent->HasBeenPostedToRemoteProcess());
   6625      sCapturingContentInfo.mRemoteTarget =
   6626          BrowserParent::GetLastMouseRemoteTarget();
   6627      MOZ_ASSERT(sCapturingContentInfo.mRemoteTarget);
   6628    }
   6629    // CaptureFlags::PointerLock is the same as
   6630    // CaptureFlags::RetargetToElement & CaptureFlags::IgnoreAllowedState.
   6631    sCapturingContentInfo.mRetargetToElement =
   6632        !!(aFlags & CaptureFlags::RetargetToElement) ||
   6633        !!(aFlags & CaptureFlags::PointerLock);
   6634    sCapturingContentInfo.mPreventDrag =
   6635        !!(aFlags & CaptureFlags::PreventDragStart);
   6636    sCapturingContentInfo.mPointerLock = !!(aFlags & CaptureFlags::PointerLock);
   6637  }
   6638 }
   6639 
   6640 nsIContent* PresShell::GetCurrentEventContent() {
   6641  if (mCurrentEventTarget.mContent &&
   6642      mCurrentEventTarget.mContent->GetComposedDoc() != mDocument) {
   6643    mCurrentEventTarget.Clear();
   6644  }
   6645  return mCurrentEventTarget.mContent;
   6646 }
   6647 
   6648 nsIFrame* PresShell::GetCurrentEventFrame() {
   6649  if (MOZ_UNLIKELY(mIsDestroying)) {
   6650    return nullptr;
   6651  }
   6652 
   6653  // GetCurrentEventContent() makes sure the content is still in the
   6654  // same document that this pres shell belongs to. If not, then the
   6655  // frame shouldn't get an event, nor should we even assume its safe
   6656  // to try and find the frame.
   6657  nsIContent* content = GetCurrentEventContent();
   6658  if (!mCurrentEventTarget.mFrame && content) {
   6659    mCurrentEventTarget.mFrame = content->GetPrimaryFrame();
   6660    MOZ_ASSERT_IF(
   6661        mCurrentEventTarget.mFrame,
   6662        mCurrentEventTarget.mFrame->PresContext()->GetPresShell() == this);
   6663  }
   6664  return mCurrentEventTarget.mFrame;
   6665 }
   6666 
   6667 already_AddRefed<nsIContent> PresShell::GetEventTargetContent(
   6668    WidgetEvent* aEvent) {
   6669  nsCOMPtr<nsIContent> content = GetCurrentEventContent();
   6670  if (!content) {
   6671    if (nsIFrame* currentEventFrame = GetCurrentEventFrame()) {
   6672      content = currentEventFrame->GetContentForEvent(aEvent);
   6673      NS_ASSERTION(!content || content->GetComposedDoc() == mDocument,
   6674                   "handing out content from a different doc");
   6675    }
   6676  }
   6677  return content.forget();
   6678 }
   6679 
   6680 void PresShell::PushCurrentEventInfo(const EventTargetInfo& aInfo) {
   6681  if (mCurrentEventTarget.IsSet()) {
   6682    // XXX Why do we insert first item instead of append it? This requires to
   6683    // move the previous items...
   6684    mCurrentEventTargetStack.InsertElementAt(0, std::move(mCurrentEventTarget));
   6685  }
   6686  mCurrentEventTarget = aInfo;
   6687 }
   6688 
   6689 void PresShell::PushCurrentEventInfo(EventTargetInfo&& aInfo) {
   6690  if (mCurrentEventTarget.IsSet()) {
   6691    mCurrentEventTargetStack.InsertElementAt(0, std::move(mCurrentEventTarget));
   6692  }
   6693  mCurrentEventTarget = std::move(aInfo);
   6694 }
   6695 
   6696 void PresShell::PopCurrentEventInfo() {
   6697  mCurrentEventTarget.Clear();
   6698 
   6699  if (!mCurrentEventTargetStack.IsEmpty()) {
   6700    mCurrentEventTarget = std::move(mCurrentEventTargetStack[0]);
   6701    mCurrentEventTargetStack.RemoveElementAt(0);
   6702 
   6703    // Don't use it if it has moved to a different document.
   6704    if (mCurrentEventTarget.mContent &&
   6705        mCurrentEventTarget.mContent->GetComposedDoc() != mDocument) {
   6706      mCurrentEventTarget.Clear();
   6707    }
   6708  }
   6709 }
   6710 
   6711 // static
   6712 bool PresShell::EventHandler::InZombieDocument(nsIContent* aContent) {
   6713  // If a content node points to a null document, or the document is not
   6714  // attached to a window, then it is possibly in a zombie document,
   6715  // about to be replaced by a newly loading document.
   6716  // Such documents cannot handle DOM events.
   6717  // It might actually be in a node not attached to any document,
   6718  // in which case there is not parent presshell to retarget it to.
   6719  Document* doc = aContent->GetComposedDoc();
   6720  return !doc || !doc->GetWindow();
   6721 }
   6722 
   6723 already_AddRefed<nsPIDOMWindowOuter> PresShell::GetRootWindow() {
   6724  nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
   6725  if (window) {
   6726    nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
   6727    NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
   6728    return rootWindow.forget();
   6729  }
   6730 
   6731  // If we don't have DOM window, we're zombie, we should find the root window
   6732  // with our parent shell.
   6733  RefPtr<PresShell> parentPresShell = GetParentPresShellForEventHandling();
   6734  NS_ENSURE_TRUE(parentPresShell, nullptr);
   6735  return parentPresShell->GetRootWindow();
   6736 }
   6737 
   6738 already_AddRefed<nsPIDOMWindowOuter>
   6739 PresShell::GetFocusedDOMWindowInOurWindow() {
   6740  nsCOMPtr<nsPIDOMWindowOuter> rootWindow = GetRootWindow();
   6741  NS_ENSURE_TRUE(rootWindow, nullptr);
   6742  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   6743  nsFocusManager::GetFocusedDescendant(rootWindow,
   6744                                       nsFocusManager::eIncludeAllDescendants,
   6745                                       getter_AddRefs(focusedWindow));
   6746  return focusedWindow.forget();
   6747 }
   6748 
   6749 already_AddRefed<nsIContent> PresShell::GetFocusedContentInOurWindow() const {
   6750  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   6751  if (fm && mDocument) {
   6752    RefPtr<Element> focusedElement;
   6753    fm->GetFocusedElementForWindow(mDocument->GetWindow(), false, nullptr,
   6754                                   getter_AddRefs(focusedElement));
   6755    return focusedElement.forget();
   6756  }
   6757  return nullptr;
   6758 }
   6759 
   6760 already_AddRefed<PresShell> PresShell::GetParentPresShellForEventHandling() {
   6761  if (!mPresContext) {
   6762    return nullptr;
   6763  }
   6764 
   6765  // Now, find the parent pres shell and send the event there
   6766  RefPtr<nsDocShell> docShell = mPresContext->GetDocShell();
   6767  if (!docShell) {
   6768    docShell = mForwardingContainer.get();
   6769  }
   6770 
   6771  // Might have gone away, or never been around to start with
   6772  if (!docShell) {
   6773    return nullptr;
   6774  }
   6775 
   6776  BrowsingContext* bc = docShell->GetBrowsingContext();
   6777  if (!bc) {
   6778    return nullptr;
   6779  }
   6780 
   6781  RefPtr<BrowsingContext> parentBC;
   6782  if (XRE_IsParentProcess()) {
   6783    parentBC = bc->Canonical()->GetParentCrossChromeBoundary();
   6784  } else {
   6785    parentBC = bc->GetParent();
   6786  }
   6787 
   6788  RefPtr<nsIDocShell> parentDocShell =
   6789      parentBC ? parentBC->GetDocShell() : nullptr;
   6790  if (!parentDocShell) {
   6791    return nullptr;
   6792  }
   6793 
   6794  RefPtr<PresShell> parentPresShell = parentDocShell->GetPresShell();
   6795  return parentPresShell.forget();
   6796 }
   6797 
   6798 nsresult PresShell::EventHandler::RetargetEventToParent(
   6799    WidgetGUIEvent* aGUIEvent, nsEventStatus* aEventStatus) {
   6800  // Send this events straight up to the parent pres shell.
   6801  // We do this for keystroke events in zombie documents or if either a frame
   6802  // or a root content is not present.
   6803  // That way at least the UI key bindings can work.
   6804 
   6805  RefPtr<PresShell> parentPresShell = GetParentPresShellForEventHandling();
   6806  NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
   6807 
   6808  // Fake the event as though it's from the parent pres shell's root frame.
   6809  return parentPresShell->HandleEvent(parentPresShell->GetRootFrame(),
   6810                                      aGUIEvent, true, aEventStatus);
   6811 }
   6812 
   6813 void PresShell::DisableNonTestMouseEvents(bool aDisable) {
   6814  sDisableNonTestMouseEvents = aDisable;
   6815 }
   6816 
   6817 nsPoint PresShell::GetEventLocation(const WidgetMouseEvent& aEvent) const {
   6818  nsIFrame* rootFrame = GetRootFrame();
   6819  if (!rootFrame) {
   6820    // Matches old TranslateWidgetToView behavior
   6821    return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   6822  }
   6823 
   6824  RelativeTo relativeTo{rootFrame};
   6825  if (rootFrame->PresContext()->IsRootContentDocumentCrossProcess()) {
   6826    relativeTo.mViewportType = ViewportType::Visual;
   6827  }
   6828  return nsLayoutUtils::GetEventCoordinatesRelativeTo(&aEvent, relativeTo);
   6829 }
   6830 
   6831 void PresShell::RecordPointerLocation(WidgetGUIEvent* aEvent) {
   6832  if (!mPresContext) {
   6833    return;
   6834  }
   6835 
   6836  if (!IsRoot()) {
   6837    PresShell* rootPresShell = GetRootPresShell();
   6838    if (rootPresShell) {
   6839      rootPresShell->RecordPointerLocation(aEvent);
   6840    }
   6841    return;
   6842  }
   6843 
   6844  const auto StoreMouseLocation = [&](const WidgetMouseEvent& aMouseEvent) {
   6845    if (aMouseEvent.mMessage == eMouseMove && aMouseEvent.IsSynthesized()) {
   6846      return false;
   6847    }
   6848    PointerEventHandler::RecordMouseState(*this, aMouseEvent);
   6849    mLastMousePointerId = Some(aMouseEvent.pointerId);
   6850    return true;
   6851  };
   6852 
   6853  const auto ClearMouseLocation = [&](const WidgetMouseEvent& aMouseEvent) {
   6854    PointerEventHandler::ClearMouseState(*this, aMouseEvent);
   6855    mLastMousePointerId.reset();
   6856  };
   6857 
   6858  const auto ClearMouseLocationIfSetByTouch =
   6859      [&](const WidgetPointerEvent& aPointerEvent) {
   6860        const PointerInfo* lastMouseInfo =
   6861            PointerEventHandler::GetLastMouseInfo(this);
   6862        if (lastMouseInfo && lastMouseInfo->HasLastState() &&
   6863            lastMouseInfo->mInputSource ==
   6864                MouseEvent_Binding::MOZ_SOURCE_TOUCH &&
   6865            aPointerEvent.mInputSource ==
   6866                MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
   6867          ClearMouseLocation(aPointerEvent);
   6868        }
   6869      };
   6870 
   6871  const auto StorePointerLocation =
   6872      [&](const WidgetMouseEvent& aMouseOrPointerEvent) {
   6873        if (!mPointerIds.Contains(aMouseOrPointerEvent.pointerId)) {
   6874          mPointerIds.AppendElement(aMouseOrPointerEvent.pointerId);
   6875        }
   6876        PointerEventHandler::RecordPointerState(
   6877            GetEventLocation(aMouseOrPointerEvent), aMouseOrPointerEvent);
   6878      };
   6879 
   6880  const auto ClearPointerLocation =
   6881      [&](const WidgetMouseEvent& aMouseOrPointerEvent) {
   6882        mPointerIds.RemoveElement(aMouseOrPointerEvent.pointerId);
   6883        PointerEventHandler::RecordPointerState(
   6884            nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE),
   6885            aMouseOrPointerEvent);
   6886      };
   6887 
   6888  const auto StoreLastPointerEventLocation =
   6889      [&](const WidgetMouseEvent& aMouseOrPointerEvent) {
   6890        // TODO: instead, encapsulate mMouseLocation and
   6891        // `mLastOverWindowPointerLocation` in a struct.
   6892        mLastOverWindowPointerLocation = GetEventLocation(aMouseOrPointerEvent);
   6893      };
   6894 
   6895  switch (aEvent->mMessage) {
   6896    case eMouseMove:
   6897    case eMouseEnterIntoWidget:
   6898    case eMouseDown:
   6899    case eMouseUp:
   6900    case eDragEnter:
   6901    case eDragStart:
   6902    case eDragOver:
   6903    case eDrop: {
   6904      const WidgetMouseEvent& mouseEvent = *aEvent->AsMouseEvent();
   6905      if (StoreMouseLocation(mouseEvent) &&
   6906          (aEvent->mMessage == eMouseEnterIntoWidget ||
   6907           aEvent->mClass == eDragEventClass)) {
   6908        SynthesizeMouseMove(false);
   6909      }
   6910      // In a drag session, we won't dispatch pointer events except
   6911      // ePointerCancel immediately after eDragStart.  However, once the drag
   6912      // session ends, we want to synthesize ePointerMove at the dropped point.
   6913      // Therefore, we should update the last state of the pointer when we start
   6914      // handling a drag event.
   6915      if (aEvent->mClass == eDragEventClass) {
   6916        StorePointerLocation(mouseEvent);
   6917      }
   6918      break;
   6919    }
   6920    case eDragExit: {
   6921      const WidgetMouseEvent& mouseEvent = *aEvent->AsMouseEvent();
   6922      if (aEvent->mRelatedTarget) {
   6923        // not exit from the widget
   6924        break;
   6925      }
   6926      ClearMouseLocation(mouseEvent);
   6927      ClearPointerLocation(mouseEvent);
   6928      break;
   6929    }
   6930    case eMouseExitFromWidget: {
   6931      const WidgetMouseEvent& mouseEvent = *aEvent->AsMouseEvent();
   6932      // Although we only care about the mouse moving into an area for which
   6933      // this pres shell doesn't receive mouse move events, we don't check which
   6934      // widget the mouse exit was for since this seems to vary by platform.
   6935      // Hopefully this won't matter at all since we'll get the mouse move or
   6936      // enter after the mouse exit when the mouse moves from one of our widgets
   6937      // into another.
   6938      ClearMouseLocation(mouseEvent);
   6939      ClearPointerLocation(mouseEvent);
   6940      break;
   6941    }
   6942    case ePointerMove:
   6943    case ePointerRawUpdate:
   6944    case eMouseRawUpdate: {
   6945      const WidgetMouseEvent& mouseEvent = *aEvent->AsMouseEvent();
   6946      if (!mouseEvent.IsReal()) {
   6947        break;
   6948      }
   6949      StoreLastPointerEventLocation(mouseEvent);
   6950      if (const WidgetPointerEvent* const pointerEvent =
   6951              mouseEvent.AsPointerEvent()) {
   6952        StorePointerLocation(*pointerEvent);
   6953      }
   6954      break;
   6955    }
   6956    case ePointerDown: {
   6957      const WidgetPointerEvent& pointerEvent = *aEvent->AsPointerEvent();
   6958      StoreLastPointerEventLocation(pointerEvent);
   6959      StorePointerLocation(pointerEvent);
   6960      break;
   6961    }
   6962    case ePointerUp: {
   6963      const WidgetPointerEvent& pointerEvent = *aEvent->AsPointerEvent();
   6964      StoreLastPointerEventLocation(pointerEvent);
   6965      // If the pointer supports hover, we need to keep storing the last
   6966      // position to synthesize ePointerMove after layout changes.
   6967      if (pointerEvent.InputSourceSupportsHover()) {
   6968        StorePointerLocation(pointerEvent);
   6969      }
   6970      // If the pointer does not support hover, we won't synthesize ePointerMove
   6971      // for that.  So, we can clear the pointer location.
   6972      else {
   6973        ClearPointerLocation(pointerEvent);
   6974      }
   6975      // If the pointer is for a touch, we need to forget the last state of
   6976      // mMouseLocation if it was set by a touch because the touch is being
   6977      // removed from the active pointers.
   6978      ClearMouseLocationIfSetByTouch(pointerEvent);
   6979      break;
   6980    }
   6981    case ePointerCancel: {
   6982      // If a touch is canceled, it means that the touch input is tracked by a
   6983      // gesture like swipe to scroll, pinch to zoom or DnD.  So, it means that
   6984      // a normal touch sequence finished.  Then, we shouldn't give `:hover`
   6985      // state to the element underneath the last touch point anymore.  For
   6986      // example, it's odd that new element which comes underneath the first
   6987      // touch position gets `:hover` style even though the scroll is caused
   6988      // by swipe (i.e., has moved the touch position).
   6989      ClearMouseLocationIfSetByTouch(*aEvent->AsPointerEvent());
   6990      break;
   6991    }
   6992    default:
   6993      break;
   6994  }
   6995 }
   6996 
   6997 // static
   6998 void PresShell::RecordModifiers(WidgetGUIEvent* aEvent) {
   6999  switch (aEvent->mMessage) {
   7000    case eKeyPress:
   7001    case eKeyUp:
   7002    case eKeyDown:
   7003    case eMouseMove:
   7004    case eMouseUp:
   7005    case eMouseDown:
   7006    case eMouseEnterIntoWidget:
   7007    case eMouseExitFromWidget:
   7008    case eMouseActivate:
   7009    case eMouseTouchDrag:
   7010    case eMouseLongTap:
   7011    case eMouseRawUpdate:
   7012    case eMouseExploreByTouch:
   7013    case ePointerCancel:
   7014    case eContextMenu:
   7015    case eTouchStart:
   7016    case eTouchMove:
   7017    case eTouchEnd:
   7018    case eTouchCancel:
   7019    case eTouchPointerCancel:
   7020    case eTouchRawUpdate:
   7021    case eWheel:
   7022      sCurrentModifiers = aEvent->AsInputEvent()->mModifiers;
   7023      break;
   7024    default:
   7025      break;
   7026  }
   7027 }
   7028 
   7029 void PresShell::nsSynthMouseMoveEvent::Revoke() {
   7030  if (mPresShell) {
   7031    mPresShell->GetPresContext()->RefreshDriver()->RemoveRefreshObserver(
   7032        this, FlushType::Display);
   7033    mPresShell = nullptr;
   7034  }
   7035 }
   7036 
   7037 static CallState FlushThrottledStyles(Document& aDocument) {
   7038  PresShell* presShell = aDocument.GetPresShell();
   7039  if (presShell && presShell->IsVisible()) {
   7040    if (nsPresContext* presContext = presShell->GetPresContext()) {
   7041      presContext->RestyleManager()->UpdateOnlyAnimationStyles();
   7042    }
   7043  }
   7044 
   7045  aDocument.EnumerateSubDocuments(FlushThrottledStyles);
   7046  return CallState::Continue;
   7047 }
   7048 
   7049 bool PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const {
   7050  bool rv =
   7051      mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
   7052  if (aEvent) {
   7053    rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
   7054  }
   7055  return rv;
   7056 }
   7057 
   7058 /* static */
   7059 PresShell* PresShell::GetShellForEventTarget(nsIFrame* aFrame,
   7060                                             nsIContent* aContent) {
   7061  if (aFrame) {
   7062    return aFrame->PresShell();
   7063  }
   7064  if (aContent) {
   7065    Document* doc = aContent->GetComposedDoc();
   7066    if (!doc) {
   7067      return nullptr;
   7068    }
   7069    return doc->GetPresShell();
   7070  }
   7071  return nullptr;
   7072 }
   7073 
   7074 /* static */
   7075 PresShell* PresShell::GetShellForTouchEvent(WidgetGUIEvent* aEvent) {
   7076  switch (aEvent->mMessage) {
   7077    case eTouchMove:
   7078    case eTouchRawUpdate:
   7079    case eTouchCancel:
   7080    case eTouchEnd: {
   7081      // get the correct shell to dispatch to
   7082      WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
   7083      for (dom::Touch* touch : touchEvent->mTouches) {
   7084        if (!touch) {
   7085          return nullptr;
   7086        }
   7087 
   7088        RefPtr<dom::Touch> oldTouch =
   7089            TouchManager::GetCapturedTouch(touch->Identifier());
   7090        if (!oldTouch) {
   7091          return nullptr;
   7092        }
   7093 
   7094        nsIContent* const content =
   7095            nsIContent::FromEventTargetOrNull(oldTouch->GetTarget());
   7096        if (!content) {
   7097          return nullptr;
   7098        }
   7099 
   7100        if (PresShell* const presShell = content->OwnerDoc()->GetPresShell()) {
   7101          return presShell;
   7102        }
   7103      }
   7104      return nullptr;
   7105    }
   7106    default:
   7107      return nullptr;
   7108  }
   7109 }
   7110 
   7111 nsresult PresShell::HandleEvent(nsIFrame* aFrameForPresShell,
   7112                                WidgetGUIEvent* aGUIEvent,
   7113                                bool aDontRetargetEvents,
   7114                                nsEventStatus* aEventStatus) {
   7115  MOZ_ASSERT(aGUIEvent);
   7116 
   7117  RecordModifiers(aGUIEvent);
   7118 
   7119  AutoWeakFrame weakFrameForPresShell(aFrameForPresShell);
   7120 
   7121  // Running tests must not expect that some mouse boundary events are fired
   7122  // when something occurs in the parent process, e.g., when a popup is
   7123  // opened/closed at the last mouse cursor position in the parent process (the
   7124  // position may be different from the position which stored in this process).
   7125  // Therefore, let's ignore synthesized mouse events coming form another
   7126  // process if and only if they are not caused by the API.
   7127  if (aGUIEvent->CameFromAnotherProcess() && XRE_IsContentProcess() &&
   7128      !aGUIEvent->mFlags.mIsSynthesizedForTests) {
   7129    const PointerInfo* const lastMouseInfo =
   7130        PointerEventHandler::GetLastMouseInfo();
   7131    if (lastMouseInfo && lastMouseInfo->mIsSynthesizedForTests) {
   7132      switch (aGUIEvent->mMessage) {
   7133        // Synthesized eMouseMove will case mouse boundary events like
   7134        // mouseover, mouseout, and :hover state is changed at dispatching the
   7135        // events.
   7136        case eMouseMove:
   7137        // eMouseExitFromWidget comes from the parent process if the cursor
   7138        // crosses a puppet widget boundary.  Then, the event will be handled as
   7139        // a synthesized eMouseMove in this process and may cause unexpected
   7140        // `mouseout` and `mouseleave`.
   7141        case eMouseExitFromWidget:
   7142        // eMouseEnterIntoWidget causes updating the hover state under the event
   7143        // position which may be different from the last cursor position
   7144        // synthesized in this process.
   7145        case eMouseEnterIntoWidget:
   7146          if (!aGUIEvent->AsMouseEvent()->IsReal()) {
   7147            return NS_OK;
   7148          }
   7149          break;
   7150        default:
   7151          break;
   7152      }
   7153    }
   7154  }
   7155 
   7156  // Here we are granting some delays to ensure that user input events are
   7157  // created while the page content may not be visible to the user are not
   7158  // processed.
   7159  // The main purpose of this is to avoid user inputs are handled in the
   7160  // new document where as the user inputs were originally targeting some
   7161  // content in the old document.
   7162  if (!CanHandleUserInputEvents(aGUIEvent)) {
   7163    return NS_OK;
   7164  }
   7165 
   7166  // If there is a composition and we got a pointing device events which may not
   7167  // impossible to continue the composition, we should notify the editor of the
   7168  // event before dispatching it.  Then, composition will be commited before
   7169  // the editor loses focus.  This behavior is compatible with Chrome.
   7170  // FIXME: Perhaps, we should do same thing before dispatching touch events.
   7171  switch (aGUIEvent->mMessage) {
   7172    case eMouseDown:
   7173    case eMouseUp: {
   7174      nsPIDOMWindowOuter* const focusedWindow =
   7175          nsFocusManager::GetFocusedWindowStatic();
   7176      if (!focusedWindow) {
   7177        break;
   7178      }
   7179      Document* const focusedDocument = focusedWindow->GetExtantDoc();
   7180      if (!focusedDocument) {
   7181        break;
   7182      }
   7183      nsPresContext* const focusedPresContext =
   7184          focusedDocument->GetPresContext();
   7185      if (!focusedPresContext) {
   7186        break;
   7187      }
   7188      const RefPtr<TextComposition> textComposition =
   7189          IMEStateManager::GetTextCompositionFor(focusedPresContext);
   7190      if (!textComposition) {
   7191        break;
   7192      }
   7193      // If there is a composition and it's managed by an editor, let's notify
   7194      // the editor of mouse button event.  The editor commits the composition
   7195      // unless IME consumes the event.
   7196      if (RefPtr<EditorBase> editorBase = textComposition->GetEditorBase()) {
   7197        MOZ_ASSERT(aGUIEvent->AsMouseEvent());
   7198        editorBase->WillHandleMouseButtonEvent(*aGUIEvent->AsMouseEvent());
   7199      }
   7200      // Otherwise, we should commit the orphan composition instead.
   7201      else if (nsCOMPtr<nsIWidget> widget = textComposition->GetWidget()) {
   7202        textComposition->RequestToCommit(widget, false);
   7203      }
   7204      if (!CanHandleUserInputEvents(aGUIEvent)) {
   7205        return NS_OK;
   7206      }
   7207      // Before bug 1945988, we dispatched the mouse button event without
   7208      // committing composition and the editor will commit composition.
   7209      // However, for compatibility with the other browsers, we started
   7210      // committing composition before dispatching the mouse event.  To keep
   7211      // the traditional behavior, we should compute aFrameForPresShell if it's
   7212      // reframed by dispatching the composition events (and input events)
   7213      // above.  Otherwise, we stop dispatching the mouse button events newly in
   7214      // such case.
   7215      if (MOZ_UNLIKELY(!weakFrameForPresShell.IsAlive())) {
   7216        FlushPendingNotifications(FlushType::Layout);
   7217        if (MOZ_UNLIKELY(IsDestroying())) {
   7218          return NS_OK;
   7219        }
   7220        nsIFrame* const newFrameForPresShell = GetRootFrame();
   7221        if (MOZ_UNLIKELY(!newFrameForPresShell)) {
   7222          return NS_OK;
   7223        }
   7224        weakFrameForPresShell = newFrameForPresShell;
   7225      }
   7226      break;
   7227    }
   7228    default:
   7229      break;
   7230  }
   7231 
   7232  if (mPresContext) {
   7233    switch (aGUIEvent->mMessage) {
   7234      case eMouseMove:
   7235      case eMouseRawUpdate:
   7236        if (!aGUIEvent->AsMouseEvent()->IsReal()) {
   7237          break;
   7238        }
   7239        [[fallthrough]];
   7240      case eMouseDown:
   7241      case eMouseUp: {
   7242        // We should flush pending mousemove event now because some mouse
   7243        // boundary events which should've already been dispatched before a user
   7244        // input may have not been dispatched.  E.g., if a mousedown event
   7245        // listener removed or appended an element under the cursor and mouseup
   7246        // event comes immediately after that, mouseover or mouseout may have
   7247        // not been dispatched on the new element yet.
   7248        // XXX If eMouseMove is not propery dispatched before eMouseDown and
   7249        // a `mousedown` event listener removes the event target or its
   7250        // ancestor, eMouseOver will be dispatched between eMouseDown and
   7251        // eMouseUp.  That could cause unexpected behavior if a `mouseover`
   7252        // event listener assumes it's always disptached before `mousedown`.
   7253        // However, we're not sure whether it could happen with users' input.
   7254        // FIXME: Perhaps, we need to do this for all events which are directly
   7255        // caused by user input, e.g., eKeyDown, etc.
   7256        const RefPtr<PresShell> rootPresShell =
   7257            IsRoot() ? this : GetRootPresShell();
   7258        if (rootPresShell && rootPresShell->mSynthMouseMoveEvent.IsPending()) {
   7259          RefPtr<nsSynthMouseMoveEvent> synthMouseMoveEvent =
   7260              rootPresShell->mSynthMouseMoveEvent.get();
   7261          synthMouseMoveEvent->Run();
   7262          if (IsDestroying()) {
   7263            return NS_OK;
   7264          }
   7265          // XXX If the frame or "this" is reframed, it might be better to
   7266          // recompute the frame.  However, it could treat the user input on
   7267          // unexpected element.  Therefore, we should not do that until we'd
   7268          // get a bug report caused by that.
   7269          if (MOZ_UNLIKELY(!weakFrameForPresShell.IsAlive())) {
   7270            return NS_OK;
   7271          }
   7272        }
   7273        break;
   7274      }
   7275      default:
   7276        break;
   7277    }
   7278  }
   7279 
   7280  // If the event may cause ePointerMove, we need to dispatch ePointerRawUpdate
   7281  // before that if and only if there are some `pointerrawupdate` event
   7282  // listeners.  Note that if a `pointerrawupdate` event listener destroys its
   7283  // document/window, we need to dispatch the following pointer event (e.g.,
   7284  // ePointerMove) in the parent document/window with the parent PresShell.
   7285  // Therefore, we need to consider the target PresShell for each event
   7286  // (ePointerRawUpdate and the following pointer event) in
   7287  // EventHandler::HandleEvent().  Thus, we need to dispatch the internal event
   7288  // for ePointerRawUpdate before calling EventHandler::HandleEvent() below.
   7289  if (!aDontRetargetEvents &&
   7290      StaticPrefs::dom_event_pointer_rawupdate_enabled()) {
   7291    nsresult rv = EnsurePrecedingPointerRawUpdate(
   7292        weakFrameForPresShell, *aGUIEvent, aDontRetargetEvents);
   7293    if (NS_FAILED(rv)) {
   7294      return rv;
   7295    }
   7296    if (!CanHandleUserInputEvents(aGUIEvent)) {
   7297      return NS_OK;
   7298    }
   7299  }
   7300 
   7301  EventHandler eventHandler(*this);
   7302  return eventHandler.HandleEvent(weakFrameForPresShell, aGUIEvent,
   7303                                  aDontRetargetEvents, aEventStatus);
   7304 }
   7305 
   7306 nsresult PresShell::EnsurePrecedingPointerRawUpdate(
   7307    AutoWeakFrame& aWeakFrameForPresShell, const WidgetGUIEvent& aSourceEvent,
   7308    bool aDontRetargetEvents) {
   7309  MOZ_ASSERT(StaticPrefs::dom_event_pointer_rawupdate_enabled());
   7310  if (PointerEventHandler::ToPointerEventMessage(&aSourceEvent) !=
   7311      ePointerMove) {
   7312    return NS_OK;
   7313  }
   7314 
   7315  // We should not dispatch ePointerRawUpdate directly because dispatching
   7316  // it requires some steps which are defined by "fire a pointer event" section
   7317  // in the spec.  https://w3c.github.io/pointerevents/#dfn-fire-a-pointer-event
   7318  // We handle the steps when we call DispatchPrecedingPointerEvent().
   7319  // Therefore, this method dispatches eMouseRawUpdate or eTouchRawUpdate event
   7320  // if the event should follow a ePointerRawUpdate.  Then,
   7321  // HandleEventUsingCoordinates() will stop handling the internal events after
   7322  // calling DispatchPrecedingPointerEvent().
   7323 
   7324  MOZ_ASSERT(aSourceEvent.mMessage != eMouseRawUpdate);
   7325  MOZ_ASSERT(aSourceEvent.mMessage != eTouchRawUpdate);
   7326 
   7327  // If no window in the browser child has `pointerrawupdate` event listener,
   7328  // we should do nothing.
   7329  if (auto* const browserChild = BrowserChild::GetFrom(this)) {
   7330    if (!browserChild->HasPointerRawUpdateEventListeners()) {
   7331      return NS_OK;
   7332    }
   7333  }
   7334 
   7335  if (const WidgetMouseEvent* const mouseEvent = aSourceEvent.AsMouseEvent()) {
   7336    // If `convertToPointer` is `false`, it means that we've already handled the
   7337    // event to dispatch a preceding pointer event.  Therefore, its preceding
   7338    // event should've already been handled.
   7339    // If `convertToPointerRawUpdate` is `false`, it means that the event was in
   7340    // the queue in BrowserChild and BrowserChild has already dispatched
   7341    // `eMouseRawUpdate`. Therefore, we don't need to dispatch it again here.
   7342    if (mouseEvent->IsSynthesized() || !mouseEvent->convertToPointer ||
   7343        !mouseEvent->convertToPointerRawUpdate) {
   7344      return NS_OK;
   7345    }
   7346    WidgetMouseEvent mouseRawUpdateEvent(*mouseEvent);
   7347    mouseRawUpdateEvent.mMessage = eMouseRawUpdate;
   7348    mouseRawUpdateEvent.mCoalescedWidgetEvents = nullptr;
   7349    // PointerEvent.button of `pointerrawupdate` should always be -1 if the
   7350    // source event is not eMouseDown nor eMouseUp.  PointerEventHandler cannot
   7351    // distinguish whether eMouseRawUpdate is caused by eMouseDown/eMouseUp or
   7352    // not.  Therefore, we need to set the proper value in the latter case here
   7353    // (In the former case, the copy constructor did it already).
   7354    if (mouseEvent->mMessage != eMouseDown &&
   7355        mouseEvent->mMessage != eMouseUp) {
   7356      mouseRawUpdateEvent.mButton = MouseButton::eNotPressed;
   7357    }
   7358    nsEventStatus rawUpdateStatus = nsEventStatus_eIgnore;
   7359    EventHandler eventHandler(*this);
   7360    return eventHandler.HandleEvent(aWeakFrameForPresShell,
   7361                                    &mouseRawUpdateEvent, aDontRetargetEvents,
   7362                                    &rawUpdateStatus);
   7363  }
   7364  if (const WidgetTouchEvent* const touchEvent = aSourceEvent.AsTouchEvent()) {
   7365    WidgetTouchEvent touchRawUpdate(*touchEvent,
   7366                                    WidgetTouchEvent::CloneTouches::No);
   7367    touchRawUpdate.mMessage = eTouchRawUpdate;
   7368    touchRawUpdate.mTouches.Clear();
   7369    for (const RefPtr<Touch>& touch : touchEvent->mTouches) {
   7370      // If `convertToPointer` is `false`, it means that we've already handled
   7371      // the event to dispatch a preceding pointer event.  Therefore, its
   7372      // preceding event should've already been handled.
   7373      // If ShouldConvertTouchToPointer() returns `false`, the touch is not an
   7374      // active pointer or the touch hasn't been changed from the previous
   7375      // state.  Therefore, we don't need to dispatch ePointerRawUpdate for the
   7376      // touch.
   7377      if (!touch->convertToPointerRawUpdate ||
   7378          !TouchManager::ShouldConvertTouchToPointer(touch, &touchRawUpdate)) {
   7379        continue;
   7380      }
   7381      RefPtr<Touch> newTouch = new Touch(*touch);
   7382      newTouch->mMessage = eTouchRawUpdate;
   7383      newTouch->mCoalescedWidgetEvents = nullptr;
   7384      touchRawUpdate.mTouches.AppendElement(std::move(newTouch));
   7385    }
   7386    nsEventStatus rawUpdateStatus = nsEventStatus_eIgnore;
   7387    if (touchRawUpdate.mTouches.IsEmpty()) {
   7388      return NS_OK;
   7389    }
   7390    EventHandler eventHandler(*this);
   7391    return eventHandler.HandleEvent(aWeakFrameForPresShell, &touchRawUpdate,
   7392                                    aDontRetargetEvents, &rawUpdateStatus);
   7393  }
   7394  MOZ_ASSERT_UNREACHABLE("Handle the event to dispatch ePointerRawUpdate");
   7395  return NS_OK;
   7396 }
   7397 
   7398 bool PresShell::EventHandler::UpdateFocusSequenceNumber(
   7399    nsIFrame* aFrameForPresShell, uint64_t aEventFocusSequenceNumber) {
   7400  uint64_t focusSequenceNumber;
   7401  nsMenuPopupFrame* popup = do_QueryFrame(aFrameForPresShell);
   7402  if (popup) {
   7403    focusSequenceNumber = popup->GetAPZFocusSequenceNumber();
   7404  } else {
   7405    focusSequenceNumber = mPresShell->mAPZFocusSequenceNumber;
   7406  }
   7407  if (focusSequenceNumber >= aEventFocusSequenceNumber) {
   7408    return false;
   7409  }
   7410 
   7411  if (popup) {
   7412    popup->UpdateAPZFocusSequenceNumber(aEventFocusSequenceNumber);
   7413  } else {
   7414    mPresShell->mAPZFocusSequenceNumber = aEventFocusSequenceNumber;
   7415  }
   7416  return true;
   7417 }
   7418 
   7419 nsresult PresShell::EventHandler::HandleEvent(
   7420    AutoWeakFrame& aWeakFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   7421    bool aDontRetargetEvents, nsEventStatus* aEventStatus) {
   7422  MOZ_ASSERT(aGUIEvent);
   7423  MOZ_DIAGNOSTIC_ASSERT(aGUIEvent->IsTrusted());
   7424  MOZ_ASSERT(aEventStatus);
   7425 
   7426  NS_ASSERTION(aWeakFrameForPresShell.IsAlive(),
   7427               "aWeakFrameForPresShell should refer a frame");
   7428 
   7429  // Update the latest focus sequence number with this new sequence number;
   7430  // the next transasction that gets sent to the compositor will carry this over
   7431  if (UpdateFocusSequenceNumber(aWeakFrameForPresShell.GetFrame(),
   7432                                aGUIEvent->mFocusSequenceNumber)) {
   7433    if (aWeakFrameForPresShell.IsAlive() &&
   7434        StaticPrefs::apz_keyboard_focus_optimization()) {
   7435      aWeakFrameForPresShell->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
   7436    }
   7437  }
   7438 
   7439  if (mPresShell->IsDestroying() ||
   7440      (PresShell::sDisableNonTestMouseEvents &&
   7441       !aGUIEvent->mFlags.mIsSynthesizedForTests &&
   7442       aGUIEvent->HasMouseEventMessage())) {
   7443    return NS_OK;
   7444  }
   7445 
   7446  mPresShell->RecordPointerLocation(aGUIEvent);
   7447 
   7448  const bool wasFrameForPresShellNull = !aWeakFrameForPresShell.GetFrame();
   7449  if (MaybeHandleEventWithAccessibleCaret(aWeakFrameForPresShell, aGUIEvent,
   7450                                          aEventStatus)) {
   7451    // Handled by AccessibleCaretEventHub.
   7452    return NS_OK;
   7453  }
   7454 
   7455  if (MaybeDiscardEvent(aGUIEvent)) {
   7456    // Cannot handle the event for now.
   7457    return NS_OK;
   7458  }
   7459 
   7460  if (!aDontRetargetEvents) {
   7461    const DebugOnly<bool> wasFrameForPresShellAlive =
   7462        aWeakFrameForPresShell.IsAlive();
   7463    // If aGUIEvent should be handled in another PresShell, we should call its
   7464    // HandleEvent() and do nothing here.
   7465    nsresult rv = NS_OK;
   7466    if (MaybeHandleEventWithAnotherPresShell(aWeakFrameForPresShell, aGUIEvent,
   7467                                             aEventStatus, &rv)) {
   7468      // Handled by another PresShell or nobody can handle the event.
   7469      return rv;
   7470    }
   7471    // If MaybeHandleEventWithAnotherPresShell() returns false, it shouldn't
   7472    // have run any script.  So, aWeakFrameForPresShell must be alive.
   7473    MOZ_ASSERT_IF(wasFrameForPresShellAlive, aWeakFrameForPresShell.IsAlive());
   7474  }
   7475 
   7476  if (MaybeDiscardOrDelayKeyboardEvent(aGUIEvent)) {
   7477    // The event is discarded or put into the delayed event queue.
   7478    return NS_OK;
   7479  }
   7480 
   7481  if (aGUIEvent->IsUsingCoordinates()) {
   7482    return HandleEventUsingCoordinates(aWeakFrameForPresShell, aGUIEvent,
   7483                                       aEventStatus, aDontRetargetEvents);
   7484  }
   7485 
   7486  // Activation events need to be dispatched even if no frame was found, since
   7487  // we don't want the focus to be out of sync.
   7488  if (MOZ_UNLIKELY(wasFrameForPresShellNull)) {
   7489    if (!NS_EVENT_NEEDS_FRAME(aGUIEvent)) {
   7490      // Push nullptr for both current event target content and frame since
   7491      // there is no frame but the event does not require a frame.
   7492      AutoCurrentEventInfoSetter eventInfoSetter(*this);
   7493      return HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true,
   7494                                             nullptr);
   7495    }
   7496 
   7497    if (aGUIEvent->HasKeyEventMessage()) {
   7498      // Keypress events in new blank tabs should not be completely thrown away.
   7499      // Retarget them -- the parent chrome shell might make use of them.
   7500      return RetargetEventToParent(aGUIEvent, aEventStatus);
   7501    }
   7502 
   7503    return NS_OK;
   7504  }
   7505 
   7506  if (aGUIEvent->IsTargetedAtFocusedContent()) {
   7507    return HandleEventAtFocusedContent(aGUIEvent, aEventStatus);
   7508  }
   7509 
   7510  return HandleEventWithFrameForPresShell(aWeakFrameForPresShell, aGUIEvent,
   7511                                          aEventStatus);
   7512 }
   7513 
   7514 nsresult PresShell::EventHandler::HandleEventUsingCoordinates(
   7515    AutoWeakFrame& aWeakFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   7516    nsEventStatus* aEventStatus, bool aDontRetargetEvents) {
   7517  MOZ_ASSERT(aGUIEvent);
   7518  MOZ_ASSERT(aGUIEvent->IsUsingCoordinates());
   7519  MOZ_ASSERT(aEventStatus);
   7520 
   7521  // Flush pending notifications to handle the event with the latest layout.
   7522  // But if it causes destroying the frame for mPresShell, stop handling the
   7523  // event. (why?)
   7524  MaybeFlushPendingNotifications(aGUIEvent);
   7525  if (MOZ_UNLIKELY(!aWeakFrameForPresShell.IsAlive())) {
   7526    *aEventStatus = nsEventStatus_eIgnore;
   7527    return NS_OK;
   7528  }
   7529 
   7530  // If we are trying to dispatch an ePointerRawUpdate but it's not allowed in
   7531  // the (maybe retargetted) document, we should not flush the capture state
   7532  // below.
   7533  if (aGUIEvent->mMessage == eMouseRawUpdate ||
   7534      aGUIEvent->mMessage == eTouchRawUpdate) {
   7535    EventTargetDataWithCapture eventTargetData =
   7536        EventTargetDataWithCapture::QueryEventTargetUsingCoordinates(
   7537            *this, aWeakFrameForPresShell,
   7538            EventTargetDataWithCapture::Query::PendingState, aGUIEvent);
   7539    if (!PointerEventHandler::NeedToDispatchPointerRawUpdate(
   7540            eventTargetData.GetDocument())) {
   7541      return NS_OK;
   7542    }
   7543    // Then, we need to recompute the target with processing the pending pointer
   7544    // capture.  Note that the result may be differnet since `gotpointercapture`
   7545    // event listener does something tricky things.
   7546  }
   7547 
   7548  EventTargetDataWithCapture eventTargetData =
   7549      EventTargetDataWithCapture::QueryEventTargetUsingCoordinates(
   7550          *this, aWeakFrameForPresShell,
   7551          EventTargetDataWithCapture::Query::LatestState, aGUIEvent,
   7552          aEventStatus);
   7553  if (MOZ_UNLIKELY(!eventTargetData.CanHandleEvent())) {
   7554    // We cannot handle the event within the PresShell anymore.  Let's stop
   7555    // handling the event without returning error since it's not illegal
   7556    // case.
   7557    return NS_OK;
   7558  }
   7559  if (MOZ_UNLIKELY(!eventTargetData.GetFrame())) {
   7560    if (eventTargetData.mPointerCapturingElement &&
   7561        aWeakFrameForPresShell.IsAlive()) {
   7562      return HandleEventWithPointerCapturingContentWithoutItsFrame(
   7563          aWeakFrameForPresShell, aGUIEvent,
   7564          MOZ_KnownLive(eventTargetData.mPointerCapturingElement),
   7565          aEventStatus);
   7566    }
   7567    return NS_OK;
   7568  }
   7569 
   7570  // Suppress mouse event if it's being targeted at an element inside
   7571  // a document which needs events suppressed
   7572  if (MaybeDiscardOrDelayMouseEvent(eventTargetData.GetFrame(), aGUIEvent)) {
   7573    return NS_OK;
   7574  }
   7575 
   7576  // Check if we have an active EventStateManager which isn't the
   7577  // EventStateManager of the current PresContext.  If that is the case, and
   7578  // mouse is over some ancestor document, forward event handling to the
   7579  // active document.  This way content can get mouse events even when mouse
   7580  // is over the chrome or outside the window.
   7581  if (eventTargetData.MaybeRetargetToActiveDocument(aGUIEvent) &&
   7582      NS_WARN_IF(!eventTargetData.GetFrame())) {
   7583    return NS_OK;
   7584  }
   7585 
   7586  // Wheel events only apply to elements. If this is a wheel event, attempt to
   7587  // update the event target from the current wheel transaction before we
   7588  // compute the element from the target frame.
   7589  eventTargetData.UpdateWheelEventTarget(aGUIEvent);
   7590 
   7591  if (!eventTargetData.ComputeElementFromFrame(aGUIEvent)) {
   7592    return NS_OK;
   7593  }
   7594  // Note that even if ComputeElementFromFrame() returns true,
   7595  // eventTargetData.mContent can be nullptr here.
   7596 
   7597  // Dispatch a pointer event if Pointer Events is enabled.  Note that if
   7598  // pointer event listeners change the layout, eventTargetData is
   7599  // automatically updated.
   7600  if (!DispatchPrecedingPointerEvent(
   7601          aWeakFrameForPresShell, aGUIEvent,
   7602          MOZ_KnownLive(eventTargetData.mPointerCapturingElement),
   7603          aDontRetargetEvents, &eventTargetData, aEventStatus)) {
   7604    return NS_OK;
   7605  }
   7606 
   7607  // Handle the event in the correct shell.
   7608  // We pass the subshell's root frame as the frame to start from. This is
   7609  // the only correct alternative; if the event was captured then it
   7610  // must have been captured by us or some ancestor shell and we
   7611  // now ask the subshell to dispatch it normally.
   7612  EventHandler eventHandler(*eventTargetData.mPresShell);
   7613  AutoCurrentEventInfoSetter eventInfoSetter(eventHandler, aGUIEvent->mMessage,
   7614                                             eventTargetData);
   7615  // eventTargetData is on the stack and is guaranteed to keep its
   7616  // mOverrideClickTarget alive, so we can just use MOZ_KnownLive here.
   7617  nsresult rv = eventHandler.HandleEventWithCurrentEventInfo(
   7618      aGUIEvent, aEventStatus, true,
   7619      MOZ_KnownLive(eventTargetData.mOverrideClickTarget));
   7620  if (NS_FAILED(rv) ||
   7621      MOZ_UNLIKELY(eventTargetData.mPresShell->IsDestroying())) {
   7622    return rv;
   7623  }
   7624 
   7625  if (aGUIEvent->mMessage == eTouchEnd) {
   7626    MaybeSynthesizeCompatMouseEventsForTouchEnd(aGUIEvent->AsTouchEvent(),
   7627                                                aEventStatus);
   7628  }
   7629 
   7630  return NS_OK;
   7631 }
   7632 
   7633 // static
   7634 PresShell::EventHandler::EventTargetDataWithCapture::EventTargetDataWithCapture(
   7635    EventHandler& aEventHandler, AutoWeakFrame& aWeakFrameForPresShell,
   7636    Query aQueryState, WidgetGUIEvent* aGUIEvent,
   7637    nsEventStatus* aEventStatus /* = nullptr*/)
   7638    : EventTargetData(aWeakFrameForPresShell.GetFrame()) {
   7639  MOZ_ASSERT(aGUIEvent);
   7640  MOZ_ASSERT(aGUIEvent->IsUsingCoordinates());
   7641  // EventHandler::GetFrameToHandleNonTouchEvent() may need to flush pending
   7642  // notifications of the target child document if eMouseDown or eMouseUp.
   7643  // Currently, this class does not support the case with Query::PendingState.
   7644  MOZ_ASSERT_IF(aQueryState == Query::PendingState,
   7645                aGUIEvent->mMessage != eMouseDown);
   7646  MOZ_ASSERT_IF(aQueryState == Query::PendingState,
   7647                aGUIEvent->mMessage != eMouseUp);
   7648 
   7649  const bool queryLatestState = aQueryState == Query::LatestState;
   7650 
   7651 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   7652  nsMutationGuard mutationGuard;
   7653  const auto assertMutation = MakeScopeExit([&]() {
   7654    if (!queryLatestState) {
   7655      MOZ_DIAGNOSTIC_ASSERT(!mutationGuard.Mutated(0));
   7656    }
   7657  });
   7658  Maybe<JS::AutoAssertNoGC> assertNoGC;
   7659  if (!queryLatestState) {
   7660    assertNoGC.emplace();
   7661  }
   7662 #endif  // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   7663 
   7664  // XXX Retrieving capturing content here.  However, some of the following
   7665  //     methods allow to run script.  So, isn't it possible the capturing
   7666  //     content outdated?
   7667  mCapturingContent = EventHandler::GetCapturingContentFor(aGUIEvent);
   7668  if (queryLatestState) {
   7669    if (GetDocument() && aGUIEvent->mClass == eTouchEventClass) {
   7670      PointerLockManager::Unlock("TouchEvent");
   7671    }
   7672    // XXX If aGUIEvent is eMouseRawUpdate or eTouchRawUpdate and it's
   7673    // dispatched by BrowserChild, i.e., the event won't cause ePointerMove
   7674    // immediately after ePointerRawUpdate, should we skip fluhsing pending
   7675    // animations here? Doing this could cause different animation result while
   7676    // the user moves mouse cursor during a long animation whether there is a
   7677    // `pointerrawupdate` event listener or not.
   7678    aEventHandler.MaybeFlushThrottledStyles(aWeakFrameForPresShell);
   7679    // Previously, MaybeFlushThrottledStyles() recomputed the closest ancestor
   7680    // frame for view of mPresShell if it's reframed.  Therefore, we should keep
   7681    // computing it here.
   7682    // FIXME: GetFrame() may be target content's frame if aGUIEvent is a touch
   7683    // event.  So, we need to use different computation for such cases.
   7684    if (MOZ_UNLIKELY(!aWeakFrameForPresShell.IsAlive())) {
   7685      Clear();
   7686      MOZ_ASSERT(!CanHandleEvent());
   7687      return;
   7688    }
   7689  }
   7690 
   7691  AutoWeakFrame weakRootFrameToHandleEvent =
   7692      aEventHandler.ComputeRootFrameToHandleEvent(
   7693          aWeakFrameForPresShell.GetFrame(), aGUIEvent, mCapturingContent,
   7694          &mCapturingContentIgnored, &mCaptureRetargeted);
   7695  if (mCapturingContentIgnored) {
   7696    mCapturingContent = nullptr;
   7697  }
   7698 
   7699  // The order to generate pointer event is
   7700  // 1. check pending pointer capture.
   7701  // 2. check if there is a capturing content.
   7702  // 3. hit test
   7703  // 4. dispatch pointer events
   7704  // 5. check whether the targets of all Touch instances are in the same
   7705  //    document and suppress invalid instances.
   7706  // 6. dispatch mouse or touch events.
   7707 
   7708  // Try to keep frame for following check, because frame can be damaged
   7709  // during MaybeProcessPointerCapture.
   7710  if (queryLatestState) {
   7711    PointerEventHandler::MaybeProcessPointerCapture(aGUIEvent);
   7712    // Prevent application crashes, in case damaged frame.
   7713    if (NS_WARN_IF(!weakRootFrameToHandleEvent.IsAlive())) {
   7714      Clear();
   7715      MOZ_ASSERT(!CanHandleEvent());
   7716      return;
   7717    }
   7718  }
   7719 
   7720  // We want to query the pointer capture element which **will** capture the
   7721  // following pointer event.  If we've already processed the pointer capture
   7722  // above, current override element is it.  Otherwise, we will process the
   7723  // pending pointer capture before dispatching a pointer event.  Therefore,
   7724  // the pending pointer capture element will be the next override element
   7725  // if and only if they are different.  (If they are the same element, the
   7726  // element will keep capturing the pointer.  So, referring to the pending
   7727  // element is also fine in the case.)
   7728  mPointerCapturingElement =
   7729      queryLatestState
   7730          ? PointerEventHandler::GetPointerCapturingElement(aGUIEvent)
   7731          : PointerEventHandler::GetPendingPointerCapturingElement(aGUIEvent);
   7732 
   7733  if (mPointerCapturingElement) {
   7734    weakRootFrameToHandleEvent = mPointerCapturingElement->GetPrimaryFrame();
   7735    if (!weakRootFrameToHandleEvent.IsAlive()) {
   7736      // The caller should not keep handling the event with the frame stored by
   7737      // the super class.  Therefore, we need to clear the frame.
   7738      ClearFrameToHandleEvent();
   7739      // Although the pointer capturing element does not have a frame, the event
   7740      // should be handled on the element.
   7741      MOZ_ASSERT(CanHandleEvent());
   7742      return;
   7743    }
   7744  }
   7745 
   7746  const WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
   7747  const bool isWindowLevelMouseExit =
   7748      (aGUIEvent->mMessage == eMouseExitFromWidget) &&
   7749      (mouseEvent &&
   7750       (mouseEvent->mExitFrom.value() == WidgetMouseEvent::ePlatformTopLevel ||
   7751        mouseEvent->mExitFrom.value() == WidgetMouseEvent::ePuppet));
   7752 
   7753  // Get the frame at the event point. However, don't do this if we're
   7754  // capturing and retargeting the event because the captured frame will
   7755  // be used instead below. Also keep using the root frame if we're dealing
   7756  // with a window-level mouse exit event since we want to start sending
   7757  // mouse out events at the root EventStateManager.
   7758  SetFrameAndComputePresShell(weakRootFrameToHandleEvent.GetFrame());
   7759  if (!mCaptureRetargeted && !isWindowLevelMouseExit &&
   7760      !mPointerCapturingElement) {
   7761    if (!aEventHandler.ComputeEventTargetFrameAndPresShellAtEventPoint(
   7762            weakRootFrameToHandleEvent, aGUIEvent, this)) {
   7763      Clear();
   7764      MOZ_ASSERT(!CanHandleEvent());
   7765      if (aEventStatus) {
   7766        *aEventStatus = nsEventStatus_eIgnore;
   7767      }
   7768      return;
   7769    }
   7770  }
   7771 
   7772  // if a node is capturing the mouse, check if the event needs to be
   7773  // retargeted at the capturing content instead. This will be the case when
   7774  // capture retargeting is being used, no frame was found or the frame's
   7775  // content is not a descendant of the capturing content.
   7776  if (mCapturingContent && !mPointerCapturingElement &&
   7777      (PresShell::sCapturingContentInfo.mRetargetToElement ||
   7778       !GetFrameContent() ||
   7779       !nsContentUtils::ContentIsCrossDocDescendantOf(GetFrameContent(),
   7780                                                      mCapturingContent))) {
   7781    if (nsIFrame* const capturingFrame = mCapturingContent->GetPrimaryFrame()) {
   7782      SetFrameAndComputePresShell(capturingFrame);
   7783    }
   7784  }
   7785 
   7786  MOZ_ASSERT(CanHandleEvent());
   7787 }
   7788 
   7789 bool PresShell::EventHandler::MaybeFlushPendingNotifications(
   7790    WidgetGUIEvent* aGUIEvent) {
   7791  MOZ_ASSERT(aGUIEvent);
   7792 
   7793  switch (aGUIEvent->mMessage) {
   7794    case eMouseDown:
   7795    case eMouseUp: {
   7796      RefPtr<nsPresContext> presContext = mPresShell->GetPresContext();
   7797      if (NS_WARN_IF(!presContext)) {
   7798        return false;
   7799      }
   7800      uint64_t framesConstructedCount = presContext->FramesConstructedCount();
   7801      uint64_t framesReflowedCount = presContext->FramesReflowedCount();
   7802 
   7803      MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Layout);
   7804      return framesConstructedCount != presContext->FramesConstructedCount() ||
   7805             framesReflowedCount != presContext->FramesReflowedCount();
   7806    }
   7807    default:
   7808      return false;
   7809  }
   7810 }
   7811 
   7812 // The type of coordinates to use for hit-testing input events
   7813 // that are relative to the RCD's viewport frame.
   7814 // On most platforms, use visual coordinates so that scrollbars
   7815 // can be targeted.
   7816 // On mobile, use layout coordinates because hit-testing in
   7817 // visual coordinates clashes with mobile viewport sizing, where
   7818 // the ViewportFrame is sized to the initial containing block
   7819 // (ICB) size, which is in layout coordinates. This is fine
   7820 // because we don't need to be able to target scrollbars on mobile
   7821 // (scrollbar dragging isn't supported).
   7822 static ViewportType ViewportTypeForInputEventsRelativeToRoot() {
   7823 #ifdef MOZ_WIDGET_ANDROID
   7824  return ViewportType::Layout;
   7825 #else
   7826  return ViewportType::Visual;
   7827 #endif
   7828 }
   7829 
   7830 nsIFrame* PresShell::EventHandler::GetFrameToHandleNonTouchEvent(
   7831    AutoWeakFrame& aWeakRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent) {
   7832  MOZ_ASSERT(aGUIEvent);
   7833  MOZ_ASSERT(aGUIEvent->mClass != eTouchEventClass);
   7834 
   7835  if (MOZ_UNLIKELY(!aWeakRootFrameToHandleEvent.IsAlive())) {
   7836    return nullptr;
   7837  }
   7838 
   7839  ViewportType viewportType = ViewportType::Layout;
   7840  if (aWeakRootFrameToHandleEvent->Type() == LayoutFrameType::Viewport) {
   7841    nsPresContext* pc = aWeakRootFrameToHandleEvent->PresContext();
   7842    if (pc->IsChrome()) {
   7843      viewportType = ViewportType::Visual;
   7844    } else if (pc->IsRootContentDocumentCrossProcess()) {
   7845      viewportType = ViewportTypeForInputEventsRelativeToRoot();
   7846    }
   7847  }
   7848  RelativeTo relativeTo{aWeakRootFrameToHandleEvent.GetFrame(), viewportType};
   7849  nsPoint eventPoint =
   7850      nsLayoutUtils::GetEventCoordinatesRelativeTo(aGUIEvent, relativeTo);
   7851 
   7852  uint32_t flags = 0;
   7853  if (aGUIEvent->IsMouseEventClassOrHasClickRelatedPointerEvent()) {
   7854    WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
   7855    if (mouseEvent && mouseEvent->mIgnoreRootScrollFrame) {
   7856      flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
   7857    }
   7858  }
   7859 
   7860  nsIFrame* targetFrame =
   7861      FindFrameTargetedByInputEvent(aGUIEvent, relativeTo, eventPoint, flags);
   7862  if (!targetFrame) {
   7863    return aWeakRootFrameToHandleEvent.GetFrame();
   7864  }
   7865 
   7866  if (targetFrame->PresShell() == mPresShell) {
   7867    // If found target is in mPresShell, we've already found it in the latest
   7868    // layout so that we can use it.
   7869    return targetFrame;
   7870  }
   7871 
   7872  // If target is in a child document, we've not flushed its layout yet.
   7873  PresShell* childPresShell = targetFrame->PresShell();
   7874  EventHandler childEventHandler(*childPresShell);
   7875  bool layoutChanged =
   7876      childEventHandler.MaybeFlushPendingNotifications(aGUIEvent);
   7877  if (!aWeakRootFrameToHandleEvent.IsAlive()) {
   7878    // Stop handling the event if the root frame to handle event is destroyed
   7879    // by the reflow. (but why?)
   7880    return nullptr;
   7881  }
   7882  if (!layoutChanged) {
   7883    // If the layout in the child PresShell hasn't been changed, we don't
   7884    // need to recompute the target.
   7885    return targetFrame;
   7886  }
   7887 
   7888  // Finally, we need to recompute the target with the latest layout.
   7889  targetFrame =
   7890      FindFrameTargetedByInputEvent(aGUIEvent, relativeTo, eventPoint, flags);
   7891 
   7892  return targetFrame ? targetFrame : aWeakRootFrameToHandleEvent.GetFrame();
   7893 }
   7894 
   7895 bool PresShell::EventHandler::ComputeEventTargetFrameAndPresShellAtEventPoint(
   7896    AutoWeakFrame& aWeakRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent,
   7897    EventTargetData* aEventTargetData) {
   7898  MOZ_ASSERT(aGUIEvent);
   7899  MOZ_ASSERT(aEventTargetData);
   7900 
   7901  if (aGUIEvent->mClass == eTouchEventClass) {
   7902    nsIFrame* targetFrameAtTouchEvent = TouchManager::SetupTarget(
   7903        aGUIEvent->AsTouchEvent(), aWeakRootFrameToHandleEvent.GetFrame());
   7904    aEventTargetData->SetFrameAndComputePresShell(targetFrameAtTouchEvent);
   7905    return true;
   7906  }
   7907 
   7908  nsIFrame* targetFrame =
   7909      GetFrameToHandleNonTouchEvent(aWeakRootFrameToHandleEvent, aGUIEvent);
   7910  aEventTargetData->SetFrameAndComputePresShell(targetFrame);
   7911  return !!aEventTargetData->GetFrame();
   7912 }
   7913 
   7914 bool PresShell::EventHandler::DispatchPrecedingPointerEvent(
   7915    AutoWeakFrame& aWeakFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   7916    Element* aPointerCapturingElement, bool aDontRetargetEvents,
   7917    EventTargetData* aEventTargetData, nsEventStatus* aEventStatus) {
   7918  MOZ_ASSERT(aGUIEvent);
   7919  MOZ_ASSERT(aEventTargetData);
   7920  MOZ_ASSERT(aEventStatus);
   7921 
   7922  // Dispatch pointer events from the mouse or touch events. Regarding
   7923  // pointer events from mouse, we should dispatch those pointer events to
   7924  // the same target as the source mouse events. We pass the frame found
   7925  // in hit test to PointerEventHandler and dispatch pointer events to it.
   7926  //
   7927  // Regarding pointer events from touch, the behavior is different. Touch
   7928  // events are dispatched to the same target as the target of touchstart.
   7929  // Multiple touch points must be dispatched to the same document. Pointer
   7930  // events from touch can be dispatched to different documents. We Pass the
   7931  // original frame to PointerEventHandler, reentry PresShell::HandleEvent,
   7932  // and do hit test for each point.
   7933  auto targetFrameOrError = [&]() -> Result<nsIFrame*, nsresult> {
   7934    if (aGUIEvent->mClass == eTouchEventClass) {
   7935      // If aWeakFrameForPresShell has already been reframed before this is
   7936      // called, we don't need to handle the event.
   7937      if (MOZ_UNLIKELY(!aWeakFrameForPresShell.IsAlive())) {
   7938        return Err(NS_ERROR_FAILURE);
   7939      }
   7940      return aWeakFrameForPresShell.GetFrame();
   7941    }
   7942    return aEventTargetData->GetFrame();
   7943  }();
   7944  if (MOZ_UNLIKELY(targetFrameOrError.isErr())) {
   7945    return false;
   7946  }
   7947  nsIFrame* targetFrame = targetFrameOrError.unwrap();
   7948 
   7949  if (aPointerCapturingElement) {
   7950    Result<nsIContent*, nsresult> overrideClickTargetOrError =
   7951        GetOverrideClickTarget(aGUIEvent, aWeakFrameForPresShell.GetFrame(),
   7952                               aPointerCapturingElement);
   7953    if (MOZ_UNLIKELY(overrideClickTargetOrError.isErr())) {
   7954      return false;
   7955    }
   7956    aEventTargetData->mOverrideClickTarget =
   7957        overrideClickTargetOrError.unwrap();
   7958    aEventTargetData->mPresShell =
   7959        PresShell::GetShellForEventTarget(nullptr, aPointerCapturingElement);
   7960    if (!aEventTargetData->mPresShell) {
   7961      // If we can't process event for the capturing content, release
   7962      // the capture.
   7963      PointerEventHandler::ReleaseIfCaptureByDescendant(
   7964          aPointerCapturingElement);
   7965      return false;
   7966    }
   7967 
   7968    targetFrame = aPointerCapturingElement->GetPrimaryFrame();
   7969    aEventTargetData->SetFrameAndContent(targetFrame, aPointerCapturingElement);
   7970  }
   7971 
   7972  AutoWeakFrame weakTargetFrame(targetFrame);
   7973  AutoWeakFrame weakFrame(aEventTargetData->GetFrame());
   7974  nsCOMPtr<nsIContent> pointerEventTargetContent(
   7975      aEventTargetData->GetContent());
   7976  RefPtr<PresShell> presShell(aEventTargetData->mPresShell);
   7977  nsCOMPtr<nsIContent> mouseOrTouchEventTargetContent;
   7978  PointerEventHandler::DispatchPointerFromMouseOrTouch(
   7979      presShell, aEventTargetData->GetFrame(), pointerEventTargetContent,
   7980      aPointerCapturingElement, aGUIEvent, aDontRetargetEvents, aEventStatus,
   7981      getter_AddRefs(mouseOrTouchEventTargetContent));
   7982 
   7983  const bool maybeCallerCanHandleEvent =
   7984      aGUIEvent->mMessage != eMouseRawUpdate &&
   7985      aGUIEvent->mMessage != eTouchRawUpdate;
   7986 
   7987  // If the target frame is alive, the caller should keep handling the event
   7988  // unless event target frame is destroyed.
   7989  if (weakTargetFrame.IsAlive() && weakFrame.IsAlive()) {
   7990    aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
   7991    return maybeCallerCanHandleEvent;
   7992  }
   7993 
   7994  presShell->FlushPendingNotifications(FlushType::Layout);
   7995  if (MOZ_UNLIKELY(mPresShell->IsDestroying())) {
   7996    return false;
   7997  }
   7998 
   7999  // The spec defines that mouse events must be dispatched to the same target as
   8000  // the pointer event.
   8001  // The Touch Events spec defines that touch events must be dispatched to the
   8002  // same target as touch start and the other browsers dispatch touch events
   8003  // even if the touch event target is not connected to the document.
   8004  // Retargetting the event is handled by AutoPointerEventTargetUpdater and
   8005  // mouseOrTouchEventTargetContent stores the result.
   8006 
   8007  // If the target is no longer participating in its ownerDocument's tree,
   8008  // fire the event at the original target's nearest ancestor node.
   8009  if (!mouseOrTouchEventTargetContent) {
   8010    MOZ_ASSERT(aGUIEvent->IsMouseEventClassOrHasClickRelatedPointerEvent());
   8011    return false;
   8012  }
   8013 
   8014  aEventTargetData->SetFrameAndContent(
   8015      mouseOrTouchEventTargetContent->GetPrimaryFrame(),
   8016      mouseOrTouchEventTargetContent);
   8017  aEventTargetData->mPresShell =
   8018      mouseOrTouchEventTargetContent->IsInComposedDoc()
   8019          ? PresShell::GetShellForEventTarget(aEventTargetData->GetFrame(),
   8020                                              aEventTargetData->GetContent())
   8021          : mouseOrTouchEventTargetContent->OwnerDoc()->GetPresShell();
   8022 
   8023  // If new target PresShel is not found, we cannot keep handling the event.
   8024  if (!aEventTargetData->mPresShell) {
   8025    return false;
   8026  }
   8027 
   8028  aEventTargetData->UpdateTouchEventTarget(aGUIEvent);
   8029  return maybeCallerCanHandleEvent;
   8030 }
   8031 
   8032 /**
   8033 * Event retargetting may retarget a mouse event and change the reference point.
   8034 * If event retargetting changes the reference point of a event that accessible
   8035 * caret will not handle, restore the original reference point.
   8036 */
   8037 class AutoEventTargetPointResetter {
   8038 public:
   8039  explicit AutoEventTargetPointResetter(WidgetGUIEvent* aGUIEvent)
   8040      : mGUIEvent(aGUIEvent),
   8041        mRefPoint(aGUIEvent->mRefPoint),
   8042        mHandledByAccessibleCaret(false) {}
   8043 
   8044  void SetHandledByAccessibleCaret() { mHandledByAccessibleCaret = true; }
   8045 
   8046  ~AutoEventTargetPointResetter() {
   8047    if (!mHandledByAccessibleCaret) {
   8048      mGUIEvent->mRefPoint = mRefPoint;
   8049    }
   8050  }
   8051 
   8052 private:
   8053  WidgetGUIEvent* mGUIEvent;
   8054  LayoutDeviceIntPoint mRefPoint;
   8055  bool mHandledByAccessibleCaret;
   8056 };
   8057 
   8058 bool PresShell::EventHandler::MaybeHandleEventWithAccessibleCaret(
   8059    AutoWeakFrame& aWeakFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   8060    nsEventStatus* aEventStatus) {
   8061  MOZ_ASSERT(aGUIEvent);
   8062  MOZ_ASSERT(aEventStatus);
   8063 
   8064  // Don't dispatch event to AccessibleCaretEventHub when the event status
   8065  // is nsEventStatus_eConsumeNoDefault. This might be happened when content
   8066  // preventDefault on the pointer events. In such case, we also call
   8067  // preventDefault on mouse events to stop default behaviors.
   8068  if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
   8069    return false;
   8070  }
   8071 
   8072  if (!AccessibleCaretEnabled(GetDocument()->GetDocShell())) {
   8073    return false;
   8074  }
   8075 
   8076  // AccessibleCaretEventHub handles only mouse, touch, and keyboard events.
   8077  if (!aGUIEvent->IsMouseEventClassOrHasClickRelatedPointerEvent() &&
   8078      aGUIEvent->mClass != eTouchEventClass &&
   8079      aGUIEvent->mClass != eKeyboardEventClass) {
   8080    return false;
   8081  }
   8082 
   8083  // AccessibleCaretEventHub::HandleEvent may return nsEventStatus_eIgnore to
   8084  // allow the context menu to appear correctly on desktop Firefox (see
   8085  // AccessibleCaretEventHub::LongTapState::OnLongTap). When this happens, and
   8086  // the event hub at the event location is the same as the hub attached to
   8087  // the currently‑focused window, HandleEvent would be invoked a second time.
   8088  // Avoid the duplicate call by explicitly checking for this condition.
   8089  AccessibleCaretEventHub* alreadyHandledEventHub = nullptr;
   8090 
   8091  AutoEventTargetPointResetter autoEventTargetPointResetter(aGUIEvent);
   8092  // First, try the event hub at the event point to handle a long press to
   8093  // select a word in an unfocused window.
   8094  do {
   8095    EventTargetData eventTargetData(nullptr);
   8096    if (!ComputeEventTargetFrameAndPresShellAtEventPoint(
   8097            aWeakFrameForPresShell, aGUIEvent, &eventTargetData)) {
   8098      break;
   8099    }
   8100 
   8101    if (!eventTargetData.mPresShell) {
   8102      break;
   8103    }
   8104 
   8105    RefPtr<AccessibleCaretEventHub> eventHub =
   8106        eventTargetData.mPresShell->GetAccessibleCaretEventHub();
   8107    if (!eventHub) {
   8108      break;
   8109    }
   8110    alreadyHandledEventHub = eventHub.get();
   8111 
   8112    *aEventStatus = eventHub->HandleEvent(aGUIEvent);
   8113    if (*aEventStatus != nsEventStatus_eConsumeNoDefault) {
   8114      break;
   8115    }
   8116 
   8117    // If the event is consumed, cancel APZC panning by setting
   8118    // mMultipleActionsPrevented.
   8119    aGUIEvent->mFlags.mMultipleActionsPrevented = true;
   8120    autoEventTargetPointResetter.SetHandledByAccessibleCaret();
   8121    return true;
   8122  } while (false);
   8123 
   8124  // Then, we target the event to the event hub at the focused window.
   8125  nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
   8126  if (!window) {
   8127    return false;
   8128  }
   8129  RefPtr<Document> retargetEventDoc = window->GetExtantDoc();
   8130  if (!retargetEventDoc) {
   8131    return false;
   8132  }
   8133  RefPtr<PresShell> presShell = retargetEventDoc->GetPresShell();
   8134  if (!presShell) {
   8135    return false;
   8136  }
   8137 
   8138  RefPtr<AccessibleCaretEventHub> eventHub =
   8139      presShell->GetAccessibleCaretEventHub();
   8140  if (!eventHub || eventHub.get() == alreadyHandledEventHub) {
   8141    return false;
   8142  }
   8143  *aEventStatus = eventHub->HandleEvent(aGUIEvent);
   8144  if (*aEventStatus != nsEventStatus_eConsumeNoDefault) {
   8145    return false;
   8146  }
   8147  // If the event is consumed, cancel APZC panning by setting
   8148  // mMultipleActionsPrevented.
   8149  aGUIEvent->mFlags.mMultipleActionsPrevented = true;
   8150  autoEventTargetPointResetter.SetHandledByAccessibleCaret();
   8151  return true;
   8152 }
   8153 
   8154 void PresShell::EventHandler::MaybeSynthesizeCompatMouseEventsForTouchEnd(
   8155    const WidgetTouchEvent* aTouchEndEvent,
   8156    const nsEventStatus* aStatus) const {
   8157  MOZ_ASSERT(aTouchEndEvent->mMessage == eTouchEnd);
   8158 
   8159  // If the eTouchEnd event is dispatched via APZ, APZCCallbackHelper dispatches
   8160  // a set of mouse events with better handling.  Therefore, we don't need to
   8161  // handle that here.
   8162  if (!aTouchEndEvent->mFlags.mIsSynthesizedForTests ||
   8163      StaticPrefs::test_events_async_enabled()) {
   8164    return;
   8165  }
   8166 
   8167  auto cleanUpPointerCapturingElementAtLastPointerUp = MakeScopeExit([]() {
   8168    PointerEventHandler::ReleasePointerCapturingElementAtLastPointerUp();
   8169  });
   8170 
   8171  // If the tap was consumed or 2 or more touches occurred, we don't need the
   8172  // compatibility mouse events.
   8173  if (*aStatus == nsEventStatus_eConsumeNoDefault ||
   8174      !TouchManager::IsSingleTapEndToDoDefault(aTouchEndEvent)) {
   8175    return;
   8176  }
   8177 
   8178  if (NS_WARN_IF(!aTouchEndEvent->mWidget)) {
   8179    return;
   8180  }
   8181 
   8182  nsCOMPtr<nsIWidget> widget = aTouchEndEvent->mWidget;
   8183 
   8184  // NOTE: I think that we don't need to implement a double click here becase
   8185  // WebDriver does not support a way to synthesize a double click and Chrome
   8186  // does not fire "dblclick" even if doing `pointerDown().pointerUp()` twice.
   8187  // FIXME: Currently we don't support long tap.
   8188  RefPtr<PresShell> presShell = mPresShell;
   8189  for (const EventMessage message : {eMouseMove, eMouseDown, eMouseUp}) {
   8190    if (MOZ_UNLIKELY(presShell->IsDestroying())) {
   8191      break;
   8192    }
   8193    nsIFrame* const frameForPresShell = presShell->GetRootFrame();
   8194    if (!frameForPresShell) {
   8195      break;
   8196    }
   8197    WidgetMouseEvent event(true, message, widget, WidgetMouseEvent::eReal);
   8198    event.mFlags.mIsSynthesizedForTests =
   8199        aTouchEndEvent->mFlags.mIsSynthesizedForTests;
   8200    event.mRefPoint = aTouchEndEvent->mTouches[0]->mRefPoint;
   8201    event.mButton = MouseButton::ePrimary;
   8202    event.mButtons = message == eMouseDown ? MouseButtonsFlag::ePrimaryFlag
   8203                                           : MouseButtonsFlag::eNoButtons;
   8204    event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
   8205    event.mClickCount = message == eMouseMove ? 0 : 1;
   8206    event.mModifiers = aTouchEndEvent->mModifiers;
   8207    event.pointerId = aTouchEndEvent->mTouches[0]->mIdentifier;
   8208    event.convertToPointer = false;
   8209    if (TouchManager::IsPrecedingTouchPointerDownConsumedByContent()) {
   8210      event.PreventDefault(false);
   8211      event.mFlags.mOnlyChromeDispatch = true;
   8212    }
   8213    nsEventStatus mouseEventStatus = nsEventStatus_eIgnore;
   8214    presShell->HandleEvent(frameForPresShell, &event, false, &mouseEventStatus);
   8215  }
   8216 }
   8217 
   8218 bool PresShell::EventHandler::MaybeDiscardEvent(WidgetGUIEvent* aGUIEvent) {
   8219  MOZ_ASSERT(aGUIEvent);
   8220 
   8221  // If it is safe to dispatch events now, don't discard the event.
   8222  if (nsContentUtils::IsSafeToRunScript()) {
   8223    return false;
   8224  }
   8225 
   8226  // If the event does not cause dispatching DOM event (i.e., internal event),
   8227  // we can keep handling it even when it's not safe to run script.
   8228  if (!aGUIEvent->IsAllowedToDispatchDOMEvent()) {
   8229    return false;
   8230  }
   8231 
   8232  // If the event is a composition event, we need to let IMEStateManager know
   8233  // it's discarded because it needs to listen all composition events to manage
   8234  // TextComposition instance.
   8235  if (aGUIEvent->mClass == eCompositionEventClass) {
   8236    IMEStateManager::OnCompositionEventDiscarded(
   8237        aGUIEvent->AsCompositionEvent());
   8238  }
   8239 
   8240 #ifdef DEBUG
   8241  if (aGUIEvent->IsIMERelatedEvent()) {
   8242    nsPrintfCString warning("%s event is discarded",
   8243                            ToChar(aGUIEvent->mMessage));
   8244    NS_WARNING(warning.get());
   8245  }
   8246 #endif  // #ifdef DEBUG
   8247 
   8248  nsContentUtils::WarnScriptWasIgnored(GetDocument());
   8249  return true;
   8250 }
   8251 
   8252 // static
   8253 nsIContent* PresShell::EventHandler::GetCapturingContentFor(
   8254    WidgetGUIEvent* aGUIEvent) {
   8255  if (aGUIEvent->mClass != ePointerEventClass &&
   8256      aGUIEvent->mClass != eWheelEventClass &&
   8257      !aGUIEvent->HasMouseEventMessage()) {
   8258    return nullptr;
   8259  }
   8260 
   8261  // PointerEventHandler may synthesize ePointerMove event before releasing the
   8262  // mouse capture (it's done by a default handler of eMouseUp) after handling
   8263  // ePointerUp.  Then, we need to dispatch pointer boundary events for the
   8264  // element under the pointer to emulate a pointer move after a pointer
   8265  // capture.  Therefore, we need to ignore the capturing element if the event
   8266  // dispatcher requests it.
   8267  if (aGUIEvent->ShouldIgnoreCapturingContent()) {
   8268    return nullptr;
   8269  }
   8270 
   8271  return PresShell::GetCapturingContent();
   8272 }
   8273 
   8274 bool PresShell::EventHandler::GetRetargetEventDocument(
   8275    WidgetGUIEvent* aGUIEvent, Document** aRetargetEventDocument) {
   8276  MOZ_ASSERT(aGUIEvent);
   8277  MOZ_ASSERT(aRetargetEventDocument);
   8278 
   8279  *aRetargetEventDocument = nullptr;
   8280 
   8281  // key and IME related events should not cross top level window boundary.
   8282  // Basically, such input events should be fired only on focused widget.
   8283  // However, some IMEs might need to clean up composition after focused
   8284  // window is deactivated.  And also some tests on MozMill want to test key
   8285  // handling on deactivated window because MozMill window can be activated
   8286  // during tests.  So, there is no merit the events should be redirected to
   8287  // active window.  So, the events should be handled on the last focused
   8288  // content in the last focused DOM window in same top level window.
   8289  // Note, if no DOM window has been focused yet, we can discard the events.
   8290  if (aGUIEvent->IsTargetedAtFocusedWindow()) {
   8291    nsCOMPtr<nsPIDOMWindowOuter> window = GetFocusedDOMWindowInOurWindow();
   8292    // No DOM window in same top level window has not been focused yet,
   8293    // discard the events.
   8294    if (!window) {
   8295      return false;
   8296    }
   8297 
   8298    RefPtr<Document> retargetEventDoc = window->GetExtantDoc();
   8299    if (!retargetEventDoc) {
   8300      return false;
   8301    }
   8302    retargetEventDoc.forget(aRetargetEventDocument);
   8303    return true;
   8304  }
   8305 
   8306  const nsIContent* const capturingContent =
   8307      aGUIEvent->ShouldIgnoreCapturingContent()
   8308          ? nullptr
   8309          : EventHandler::GetCapturingContentFor(aGUIEvent);
   8310  if (capturingContent) {
   8311    // if the mouse is being captured then retarget the mouse event at the
   8312    // document that is being captured.
   8313    RefPtr<Document> retargetEventDoc = capturingContent->GetComposedDoc();
   8314    retargetEventDoc.forget(aRetargetEventDocument);
   8315    return true;
   8316  }
   8317 
   8318 #ifdef ANDROID
   8319  if (aGUIEvent->mClass == eTouchEventClass ||
   8320      aGUIEvent->IsMouseEventClassOrHasClickRelatedPointerEvent() ||
   8321      aGUIEvent->mClass == eWheelEventClass) {
   8322    RefPtr<Document> retargetEventDoc = mPresShell->GetPrimaryContentDocument();
   8323    retargetEventDoc.forget(aRetargetEventDocument);
   8324    return true;
   8325  }
   8326 #endif  // #ifdef ANDROID
   8327 
   8328  // When we don't find another document to handle the event, we need to keep
   8329  // handling the event by ourselves.
   8330  return true;
   8331 }
   8332 
   8333 nsIFrame* PresShell::EventHandler::GetFrameForHandlingEventWith(
   8334    WidgetGUIEvent* aGUIEvent, Document* aRetargetDocument,
   8335    nsIFrame* aFrameForPresShell) {
   8336  MOZ_ASSERT(aGUIEvent);
   8337  MOZ_ASSERT(aRetargetDocument);
   8338 
   8339  RefPtr<PresShell> retargetPresShell = aRetargetDocument->GetPresShell();
   8340  // Even if the document doesn't have PresShell, i.e., it's invisible, we
   8341  // need to dispatch only KeyboardEvent in its nearest visible document
   8342  // because key focus shouldn't be caught by invisible document.
   8343  if (!retargetPresShell) {
   8344    if (!aGUIEvent->HasKeyEventMessage()) {
   8345      return nullptr;
   8346    }
   8347    Document* retargetEventDoc = aRetargetDocument;
   8348    while (!retargetPresShell) {
   8349      retargetEventDoc = retargetEventDoc->GetInProcessParentDocument();
   8350      if (!retargetEventDoc) {
   8351        return nullptr;
   8352      }
   8353      retargetPresShell = retargetEventDoc->GetPresShell();
   8354    }
   8355  }
   8356 
   8357  // If the found PresShell is this instance, caller needs to keep handling
   8358  // aGUIEvent by itself.  Therefore, return the given frame which was set
   8359  // to aFrame of HandleEvent().
   8360  if (retargetPresShell == mPresShell) {
   8361    return aFrameForPresShell;
   8362  }
   8363 
   8364  // Use root frame of the new PresShell if there is.
   8365  nsIFrame* rootFrame = retargetPresShell->GetRootFrame();
   8366  if (rootFrame) {
   8367    return rootFrame;
   8368  }
   8369 
   8370  // Otherwise, and if aGUIEvent requires content of PresShell, caller should
   8371  // stop handling the event.
   8372  if (aGUIEvent->mMessage == eQueryTextContent ||
   8373      aGUIEvent->IsContentCommandEvent()) {
   8374    return nullptr;
   8375  }
   8376 
   8377  // Otherwise, use nearest ancestor frame which includes the PresShell.
   8378  return retargetPresShell->GetRootFrame();
   8379 }
   8380 
   8381 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
   8382    AutoWeakFrame& aWeakFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   8383    nsEventStatus* aEventStatus, nsresult* aRv) {
   8384  MOZ_ASSERT(aGUIEvent);
   8385  MOZ_ASSERT(aEventStatus);
   8386  MOZ_ASSERT(aRv);
   8387 
   8388  *aRv = NS_OK;
   8389 
   8390  RefPtr<Document> retargetEventDoc;
   8391  if (!GetRetargetEventDocument(aGUIEvent, getter_AddRefs(retargetEventDoc))) {
   8392    // Nobody can handle this event.  So, treat as handled by somebody to make
   8393    // caller do nothing anymore.
   8394    return true;
   8395  }
   8396 
   8397  // If there is no proper retarget document, the caller should handle the
   8398  // event by itself.
   8399  if (!retargetEventDoc) {
   8400    return false;
   8401  }
   8402 
   8403  nsIFrame* frame = GetFrameForHandlingEventWith(
   8404      aGUIEvent, retargetEventDoc, aWeakFrameForPresShell.GetFrame());
   8405  if (!frame) {
   8406    // Nobody can handle this event.  So, treat as handled by somebody to make
   8407    // caller do nothing anymore.
   8408    // NOTE: If aWeakFrameForPresShell does not refer to a frame (i.e., it's
   8409    // already been reframed) and aGUIEvent needs to be handled in mPresShell,
   8410    // we are here because GetFrameForHandlingEventWith() returns
   8411    // aWeakFrameForPresShell.GetFrame() as-is. In the case, we don't need to
   8412    // handle aGUIEvent, so, it's fine to return true from this method.
   8413    return true;
   8414  }
   8415 
   8416  // If we reached same frame as set to HandleEvent(), the caller should handle
   8417  // the event by itself.
   8418  if (frame == aWeakFrameForPresShell.GetFrame()) {
   8419    return false;
   8420  }
   8421 
   8422  // We need to handle aGUIEvent with another PresShell.
   8423  RefPtr<PresShell> presShell = frame->PresContext()->PresShell();
   8424  *aRv = presShell->HandleEvent(frame, aGUIEvent, true, aEventStatus);
   8425  return true;
   8426 }
   8427 
   8428 bool PresShell::EventHandler::MaybeDiscardOrDelayKeyboardEvent(
   8429    WidgetGUIEvent* aGUIEvent) {
   8430  MOZ_ASSERT(aGUIEvent);
   8431 
   8432  if (aGUIEvent->mClass != eKeyboardEventClass) {
   8433    return false;
   8434  }
   8435 
   8436  Document* document = GetDocument();
   8437  if (!document || !document->EventHandlingSuppressed()) {
   8438    return false;
   8439  }
   8440 
   8441  MOZ_ASSERT_IF(InputTaskManager::CanSuspendInputEvent(),
   8442                !InputTaskManager::Get()->IsSuspended());
   8443 
   8444  if (aGUIEvent->mMessage == eKeyDown) {
   8445    mPresShell->mNoDelayedKeyEvents = true;
   8446  } else if (!mPresShell->mNoDelayedKeyEvents) {
   8447    UniquePtr<DelayedKeyEvent> delayedKeyEvent =
   8448        MakeUnique<DelayedKeyEvent>(aGUIEvent->AsKeyboardEvent());
   8449    mPresShell->mDelayedEvents.AppendElement(std::move(delayedKeyEvent));
   8450  }
   8451  aGUIEvent->mFlags.mIsSuppressedOrDelayed = true;
   8452  return true;
   8453 }
   8454 
   8455 bool PresShell::EventHandler::MaybeDiscardOrDelayMouseEvent(
   8456    nsIFrame* aFrameToHandleEvent, WidgetGUIEvent* aGUIEvent) {
   8457  MOZ_ASSERT(aFrameToHandleEvent);
   8458  MOZ_ASSERT(aGUIEvent);
   8459 
   8460  // We must not need to let suspend listeners know ePointerRawUpdate events.
   8461  // And also the delayed events will be dispatched via widget.  Therefore,
   8462  // ePointerRawUpdate event will be dispatched by PresShell::HandleEvent()
   8463  // again.
   8464  if (aGUIEvent->mMessage == eMouseRawUpdate ||
   8465      aGUIEvent->mMessage == eTouchRawUpdate ||
   8466      aGUIEvent->mMessage == ePointerRawUpdate) {
   8467    return false;
   8468  }
   8469 
   8470  if (!aGUIEvent->IsMouseEventClassOrHasClickRelatedPointerEvent() &&
   8471      aGUIEvent->mMessage != eTouchStart) {
   8472    return false;
   8473  }
   8474 
   8475  if (!aFrameToHandleEvent->PresContext()
   8476           ->Document()
   8477           ->EventHandlingSuppressed()) {
   8478    return false;
   8479  }
   8480 
   8481  MOZ_ASSERT_IF(InputTaskManager::CanSuspendInputEvent() &&
   8482                    aGUIEvent->mMessage != eMouseMove,
   8483                !InputTaskManager::Get()->IsSuspended());
   8484 
   8485  RefPtr<PresShell> ps = aFrameToHandleEvent->PresShell();
   8486 
   8487  switch (aGUIEvent->mMessage) {
   8488    case eTouchStart: {
   8489      // If we receive a single touch start during the suppression, its
   8490      // compatibility mouse events should not be fired later because the single
   8491      // tap sequence has not been sent to the web app.
   8492      const WidgetTouchEvent* const touchEvent = aGUIEvent->AsTouchEvent();
   8493      if (touchEvent->mTouches.Length() == 1) {
   8494        ps->mNoDelayedSingleTap = true;
   8495      }
   8496      // We won't dispatch eTouchStart as a delayed event later so that return
   8497      // false.
   8498      return false;
   8499    }
   8500    case eMouseDown: {
   8501      // If we receive a click sequence start during the suppression, we should
   8502      // not fire `click` event later because its sequence has not been send to
   8503      // the web app.  Note that if the eMouseDown is caused by a touch, we may
   8504      // have already sent the touch sequence to the web app.  In such case,
   8505      // the eMouseDown is NOT start of the click sequence.
   8506      const WidgetMouseEvent* const mouseEvent = aGUIEvent->AsMouseEvent();
   8507      if (ps->mNoDelayedSingleTap ||
   8508          mouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
   8509        ps->mNoDelayedMouseEvents = true;
   8510        break;
   8511      }
   8512      // Otherwise, put the event into the queue.
   8513      [[fallthrough]];
   8514    }
   8515    case eMouseUp:
   8516    case eMouseExitFromWidget: {
   8517      if (ps->mNoDelayedMouseEvents) {
   8518        break;
   8519      }
   8520      UniquePtr<DelayedMouseEvent> delayedMouseEvent =
   8521          MakeUnique<DelayedMouseEvent>(aGUIEvent->AsMouseEvent());
   8522      ps->mDelayedEvents.AppendElement(std::move(delayedMouseEvent));
   8523      break;
   8524    }
   8525    case eContextMenu: {
   8526      if (ps->mNoDelayedMouseEvents) {
   8527        break;
   8528      }
   8529      // contextmenu is triggered after right mouseup on Windows and right
   8530      // mousedown on other platforms.
   8531      UniquePtr<DelayedPointerEvent> delayedPointerEvent =
   8532          MakeUnique<DelayedPointerEvent>(aGUIEvent->AsPointerEvent());
   8533      ps->mDelayedEvents.AppendElement(std::move(delayedPointerEvent));
   8534      break;
   8535    }
   8536    default:
   8537      break;
   8538  }
   8539 
   8540  // If there is a suppressed event listener associated with the document,
   8541  // notify it about the suppressed mouse event. This allows devtools
   8542  // features to continue receiving mouse events even when the devtools
   8543  // debugger has paused execution in a page.
   8544  RefPtr<EventListener> suppressedListener = aFrameToHandleEvent->PresContext()
   8545                                                 ->Document()
   8546                                                 ->GetSuppressedEventListener();
   8547  if (!suppressedListener ||
   8548      aGUIEvent->AsMouseEvent()->mReason == WidgetMouseEvent::eSynthesized) {
   8549    return true;
   8550  }
   8551 
   8552  if (auto* target = aFrameToHandleEvent->GetContentForEvent(aGUIEvent)) {
   8553    aGUIEvent->mTarget = target;
   8554  }
   8555 
   8556  nsCOMPtr<EventTarget> eventTarget = aGUIEvent->mTarget;
   8557  RefPtr<Event> event = EventDispatcher::CreateEvent(
   8558      eventTarget, aFrameToHandleEvent->PresContext(), aGUIEvent, u""_ns);
   8559 
   8560  suppressedListener->HandleEvent(*event);
   8561  return true;
   8562 }
   8563 
   8564 void PresShell::EventHandler::MaybeFlushThrottledStyles(
   8565    AutoWeakFrame& aWeakFrameForPresShell) {
   8566  if (!GetDocument()) {
   8567    return;
   8568  }
   8569 
   8570  PresShell* rootPresShell = mPresShell->GetRootPresShell();
   8571  if (NS_WARN_IF(!rootPresShell)) {
   8572    return;
   8573  }
   8574  Document* rootDocument = rootPresShell->GetDocument();
   8575  if (NS_WARN_IF(!rootDocument)) {
   8576    return;
   8577  }
   8578 
   8579  {  // scope for scriptBlocker.
   8580    nsAutoScriptBlocker scriptBlocker;
   8581    FlushThrottledStyles(*rootDocument);
   8582  }
   8583 
   8584  if (MOZ_UNLIKELY(!aWeakFrameForPresShell.IsAlive()) &&
   8585      MOZ_LIKELY(!mPresShell->IsDestroying())) {
   8586    // FIXME: aWeakFrameForPresShell may be target content's frame if aGUIEvent
   8587    // of the caller is a touch event.  So, we need to use different computation
   8588    // for such cases.
   8589    aWeakFrameForPresShell = mPresShell->GetRootFrame();
   8590  }
   8591 }
   8592 
   8593 nsIFrame* PresShell::EventHandler::ComputeRootFrameToHandleEvent(
   8594    nsIFrame* aFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   8595    nsIContent* aCapturingContent, bool* aIsCapturingContentIgnored,
   8596    bool* aIsCaptureRetargeted) {
   8597  MOZ_ASSERT(aFrameForPresShell);
   8598  MOZ_ASSERT(aGUIEvent);
   8599  MOZ_ASSERT(aIsCapturingContentIgnored);
   8600  MOZ_ASSERT(aIsCaptureRetargeted);
   8601 
   8602  nsIFrame* rootFrameToHandleEvent = ComputeRootFrameToHandleEventWithPopup(
   8603      aFrameForPresShell, aGUIEvent, aCapturingContent,
   8604      aIsCapturingContentIgnored);
   8605  if (*aIsCapturingContentIgnored) {
   8606    // If the capturing content is ignored, we don't need to respect it.
   8607    return rootFrameToHandleEvent;
   8608  }
   8609 
   8610  if (!aCapturingContent) {
   8611    return rootFrameToHandleEvent;
   8612  }
   8613 
   8614  // If we have capturing content, let's compute root frame with it again.
   8615  return ComputeRootFrameToHandleEventWithCapturingContent(
   8616      rootFrameToHandleEvent, aCapturingContent, aIsCapturingContentIgnored,
   8617      aIsCaptureRetargeted);
   8618 }
   8619 
   8620 nsIFrame* PresShell::EventHandler::ComputeRootFrameToHandleEventWithPopup(
   8621    nsIFrame* aRootFrameToHandleEvent, WidgetGUIEvent* aGUIEvent,
   8622    nsIContent* aCapturingContent, bool* aIsCapturingContentIgnored) {
   8623  MOZ_ASSERT(aRootFrameToHandleEvent);
   8624  MOZ_ASSERT(aGUIEvent);
   8625  MOZ_ASSERT(aIsCapturingContentIgnored);
   8626 
   8627  *aIsCapturingContentIgnored = false;
   8628 
   8629  nsPresContext* framePresContext = aRootFrameToHandleEvent->PresContext();
   8630  nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
   8631  NS_ASSERTION(rootPresContext == GetPresContext()->GetRootPresContext(),
   8632               "How did we end up outside the connected "
   8633               "prescontext hierarchy?");
   8634  nsIFrame* popupFrame = nsLayoutUtils::GetPopupFrameForEventCoordinates(
   8635      rootPresContext, aGUIEvent);
   8636  if (!popupFrame) {
   8637    return aRootFrameToHandleEvent;
   8638  }
   8639 
   8640  // If a remote browser is currently capturing input break out if we
   8641  // detect a chrome generated popup.
   8642  // XXXedgar, do we need to check fission OOP iframe?
   8643  if (aCapturingContent &&
   8644      EventStateManager::IsTopLevelRemoteTarget(aCapturingContent)) {
   8645    *aIsCapturingContentIgnored = true;
   8646  }
   8647 
   8648  // If the popupFrame is an ancestor of the 'frame', the frame should
   8649  // handle the event, otherwise, the popup should handle it.
   8650  if (nsContentUtils::ContentIsCrossDocDescendantOf(
   8651          framePresContext->GetPresShell()->GetDocument(),
   8652          popupFrame->GetContent())) {
   8653    return aRootFrameToHandleEvent;
   8654  }
   8655 
   8656  // If we aren't starting our event dispatch from the root frame of the
   8657  // root prescontext, then someone must be capturing the mouse. In that
   8658  // case we only want to use the popup list if the capture is
   8659  // inside the popup.
   8660  if (framePresContext == rootPresContext &&
   8661      aRootFrameToHandleEvent == FrameConstructor()->GetRootFrame()) {
   8662    return popupFrame;
   8663  }
   8664 
   8665  if (aCapturingContent && !*aIsCapturingContentIgnored &&
   8666      aCapturingContent->IsInclusiveDescendantOf(popupFrame->GetContent())) {
   8667    return popupFrame;
   8668  }
   8669 
   8670  return aRootFrameToHandleEvent;
   8671 }
   8672 
   8673 nsIFrame*
   8674 PresShell::EventHandler::ComputeRootFrameToHandleEventWithCapturingContent(
   8675    nsIFrame* aRootFrameToHandleEvent, nsIContent* aCapturingContent,
   8676    bool* aIsCapturingContentIgnored, bool* aIsCaptureRetargeted) {
   8677  MOZ_ASSERT(aRootFrameToHandleEvent);
   8678  MOZ_ASSERT(aCapturingContent);
   8679  MOZ_ASSERT(aIsCapturingContentIgnored);
   8680  MOZ_ASSERT(aIsCaptureRetargeted);
   8681 
   8682  *aIsCapturingContentIgnored = false;
   8683  *aIsCaptureRetargeted = false;
   8684 
   8685  // If a capture is active, determine if the BrowsingContext is active. If
   8686  // not, clear the capture and target the mouse event normally instead. This
   8687  // would occur if the mouse button is held down while a tab change occurs.
   8688  // If the BrowsingContext is active, look for a scrolling container.
   8689  BrowsingContext* bc = GetPresContext()->Document()->GetBrowsingContext();
   8690  if (!bc || !bc->IsActive()) {
   8691    ClearMouseCapture();
   8692    *aIsCapturingContentIgnored = true;
   8693    return aRootFrameToHandleEvent;
   8694  }
   8695 
   8696  if (PresShell::sCapturingContentInfo.mRetargetToElement) {
   8697    *aIsCaptureRetargeted = true;
   8698    return aRootFrameToHandleEvent;
   8699  }
   8700 
   8701  nsIFrame* captureFrame = aCapturingContent->GetPrimaryFrame();
   8702  if (!captureFrame) {
   8703    return aRootFrameToHandleEvent;
   8704  }
   8705 
   8706  // scrollable frames should use the scrolling container as the root instead
   8707  // of the document
   8708  ScrollContainerFrame* scrollFrame = do_QueryFrame(captureFrame);
   8709  return scrollFrame ? scrollFrame->GetScrolledFrame()
   8710                     : aRootFrameToHandleEvent;
   8711 }
   8712 
   8713 nsresult
   8714 PresShell::EventHandler::HandleEventWithPointerCapturingContentWithoutItsFrame(
   8715    AutoWeakFrame& aWeakFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   8716    Element* aPointerCapturingElement, nsEventStatus* aEventStatus) {
   8717  MOZ_ASSERT(aGUIEvent);
   8718  MOZ_ASSERT(aPointerCapturingElement);
   8719  MOZ_ASSERT(!aPointerCapturingElement->GetPrimaryFrame(),
   8720             "Handle the event with frame rather than only with the content");
   8721  MOZ_ASSERT(aEventStatus);
   8722 
   8723  RefPtr<PresShell> presShellForCapturingContent =
   8724      PresShell::GetShellForEventTarget(nullptr, aPointerCapturingElement);
   8725  if (!presShellForCapturingContent) {
   8726    // If we can't process event for the capturing content, release
   8727    // the capture.
   8728    PointerEventHandler::ReleaseIfCaptureByDescendant(aPointerCapturingElement);
   8729    // Since we don't dispatch ePointeUp nor ePointerCancel in this case,
   8730    // EventStateManager::PostHandleEvent does not have a chance to dispatch
   8731    // ePointerLostCapture event.  Therefore, we need to dispatch it here.
   8732    PointerEventHandler::MaybeImplicitlyReleasePointerCapture(aGUIEvent);
   8733    return NS_OK;
   8734  }
   8735 
   8736  Result<nsIContent*, nsresult> overrideClickTargetOrError =
   8737      GetOverrideClickTarget(aGUIEvent, aWeakFrameForPresShell.GetFrame(),
   8738                             aPointerCapturingElement);
   8739  if (MOZ_UNLIKELY(overrideClickTargetOrError.isErr())) {
   8740    return NS_OK;
   8741  }
   8742  nsCOMPtr<nsIContent> overrideClickTarget =
   8743      overrideClickTargetOrError.unwrap();
   8744 
   8745  // Dispatch events to the capturing content even it's frame is
   8746  // destroyed.
   8747  PointerEventHandler::DispatchPointerFromMouseOrTouch(
   8748      presShellForCapturingContent, nullptr, aPointerCapturingElement,
   8749      aPointerCapturingElement, aGUIEvent, false, aEventStatus, nullptr);
   8750 
   8751  if (presShellForCapturingContent == mPresShell) {
   8752    return HandleEventWithTarget(aGUIEvent, nullptr, aPointerCapturingElement,
   8753                                 aEventStatus, true, nullptr,
   8754                                 overrideClickTarget);
   8755  }
   8756 
   8757  EventHandler eventHandlerForCapturingContent(
   8758      std::move(presShellForCapturingContent));
   8759  return eventHandlerForCapturingContent.HandleEventWithTarget(
   8760      aGUIEvent, nullptr, aPointerCapturingElement, aEventStatus, true, nullptr,
   8761      overrideClickTarget);
   8762 }
   8763 
   8764 nsresult PresShell::EventHandler::HandleEventAtFocusedContent(
   8765    WidgetGUIEvent* aGUIEvent, nsEventStatus* aEventStatus) {
   8766  MOZ_ASSERT(aGUIEvent);
   8767  MOZ_ASSERT(aGUIEvent->IsTargetedAtFocusedContent());
   8768  MOZ_ASSERT(aEventStatus);
   8769 
   8770  AutoCurrentEventInfoSetter eventInfoSetter(*this);
   8771 
   8772  RefPtr<Element> eventTargetElement =
   8773      ComputeFocusedEventTargetElement(aGUIEvent);
   8774 
   8775  // mCurrentEventTarget is cleared by eventInfoSetter and
   8776  // ComputeFocusedEventTargetElement shouldn't set it again.
   8777  MOZ_ASSERT(!mPresShell->mCurrentEventTarget.IsSet());
   8778 
   8779  if (eventTargetElement) {
   8780    nsresult rv = NS_OK;
   8781    if (MaybeHandleEventWithAnotherPresShell(eventTargetElement, aGUIEvent,
   8782                                             aEventStatus, &rv)) {
   8783      return rv;
   8784    }
   8785  }
   8786 
   8787  // If we cannot handle the event with mPresShell, let's try to handle it
   8788  // with parent PresShell.
   8789  // However, we don't want to handle IME related events with parent document
   8790  // because it may leak the content of parent document and the IME state was
   8791  // set for the empty document.  So, dispatching on the parent document may be
   8792  // handled by nobody. Additionally, IMEContentObserver may send notifications
   8793  // to PuppetWidget in a content process while document which is in the design
   8794  // mode but does not have content nodes has focus.  At that time, PuppetWidget
   8795  // makes ContentCacheInChild collect the latest content data with dispatching
   8796  // query content events.  Therefore, we want they handle in the empty document
   8797  // rather than the parent document.  So, we must not retarget in this case
   8798  // anyway.
   8799  mPresShell->mCurrentEventTarget.SetFrameAndContent(
   8800      aGUIEvent->mMessage, nullptr, eventTargetElement);
   8801  if (aGUIEvent->mClass != eCompositionEventClass &&
   8802      aGUIEvent->mClass != eQueryContentEventClass &&
   8803      aGUIEvent->mClass != eSelectionEventClass &&
   8804      (!mPresShell->GetCurrentEventContent() ||
   8805       !mPresShell->GetCurrentEventFrame() ||
   8806       InZombieDocument(mPresShell->mCurrentEventTarget.mContent))) {
   8807    return RetargetEventToParent(aGUIEvent, aEventStatus);
   8808  }
   8809 
   8810  nsresult rv =
   8811      HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
   8812  return rv;
   8813 }
   8814 
   8815 Element* PresShell::EventHandler::ComputeFocusedEventTargetElement(
   8816    WidgetGUIEvent* aGUIEvent) {
   8817  MOZ_ASSERT(aGUIEvent);
   8818  MOZ_ASSERT(aGUIEvent->IsTargetedAtFocusedContent());
   8819 
   8820  // key and IME related events go to the focused frame in this DOM window.
   8821  nsPIDOMWindowOuter* window = GetDocument()->GetWindow();
   8822  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   8823  Element* eventTargetElement = nsFocusManager::GetFocusedDescendant(
   8824      window, nsFocusManager::eOnlyCurrentWindow,
   8825      getter_AddRefs(focusedWindow));
   8826 
   8827  // otherwise, if there is no focused content or the focused content has
   8828  // no frame, just use the root content. This ensures that key events
   8829  // still get sent to the window properly if nothing is focused or if a
   8830  // frame goes away while it is focused.
   8831  if (!eventTargetElement || !eventTargetElement->GetPrimaryFrame()) {
   8832    eventTargetElement = GetDocument()->GetUnfocusedKeyEventTarget();
   8833  }
   8834 
   8835  switch (aGUIEvent->mMessage) {
   8836    case eKeyDown:
   8837      sLastKeyDownEventTargetElement = eventTargetElement;
   8838      return eventTargetElement;
   8839    case eKeyPress:
   8840    case eKeyUp:
   8841      if (!sLastKeyDownEventTargetElement) {
   8842        return eventTargetElement;
   8843      }
   8844      // If a different element is now focused for the keypress/keyup event
   8845      // than what was focused during the keydown event, check if the new
   8846      // focused element is not in a chrome document any more, and if so,
   8847      // retarget the event back at the keydown target. This prevents a
   8848      // content area from grabbing the focus from chrome in-between key
   8849      // events.
   8850      if (eventTargetElement) {
   8851        bool keyDownIsChrome = nsContentUtils::IsChromeDoc(
   8852            sLastKeyDownEventTargetElement->GetComposedDoc());
   8853        if (keyDownIsChrome != nsContentUtils::IsChromeDoc(
   8854                                   eventTargetElement->GetComposedDoc()) ||
   8855            (keyDownIsChrome && BrowserParent::GetFrom(eventTargetElement))) {
   8856          eventTargetElement = sLastKeyDownEventTargetElement;
   8857        }
   8858      }
   8859 
   8860      if (aGUIEvent->mMessage == eKeyUp) {
   8861        sLastKeyDownEventTargetElement = nullptr;
   8862      }
   8863      [[fallthrough]];
   8864    default:
   8865      return eventTargetElement;
   8866  }
   8867 }
   8868 
   8869 bool PresShell::EventHandler::MaybeHandleEventWithAnotherPresShell(
   8870    Element* aEventTargetElement, WidgetGUIEvent* aGUIEvent,
   8871    nsEventStatus* aEventStatus, nsresult* aRv) {
   8872  MOZ_ASSERT(aEventTargetElement);
   8873  MOZ_ASSERT(aGUIEvent);
   8874  MOZ_ASSERT(!aGUIEvent->IsUsingCoordinates());
   8875  MOZ_ASSERT(aEventStatus);
   8876  MOZ_ASSERT(aRv);
   8877 
   8878  Document* eventTargetDocument = aEventTargetElement->OwnerDoc();
   8879  if (!eventTargetDocument || eventTargetDocument == GetDocument()) {
   8880    *aRv = NS_OK;
   8881    return false;
   8882  }
   8883 
   8884  RefPtr<PresShell> eventTargetPresShell = eventTargetDocument->GetPresShell();
   8885  if (!eventTargetPresShell) {
   8886    *aRv = NS_OK;
   8887    return true;  // No PresShell can handle the event.
   8888  }
   8889 
   8890  EventHandler eventHandler(std::move(eventTargetPresShell));
   8891  *aRv = eventHandler.HandleRetargetedEvent(aGUIEvent, aEventStatus,
   8892                                            aEventTargetElement);
   8893  return true;
   8894 }
   8895 
   8896 nsresult PresShell::EventHandler::HandleEventWithFrameForPresShell(
   8897    AutoWeakFrame& aWeakFrameForPresShell, WidgetGUIEvent* aGUIEvent,
   8898    nsEventStatus* aEventStatus) {
   8899  MOZ_ASSERT(aGUIEvent);
   8900  MOZ_ASSERT(!aGUIEvent->IsUsingCoordinates());
   8901  MOZ_ASSERT(!aGUIEvent->IsTargetedAtFocusedContent());
   8902  MOZ_ASSERT(aEventStatus);
   8903 
   8904  AutoCurrentEventInfoSetter eventInfoSetter(
   8905      *this, EventTargetInfo(aGUIEvent->mMessage,
   8906                             aWeakFrameForPresShell.GetFrame(), nullptr));
   8907 
   8908  nsresult rv = NS_OK;
   8909  if (mPresShell->GetCurrentEventFrame()) {
   8910    rv =
   8911        HandleEventWithCurrentEventInfo(aGUIEvent, aEventStatus, true, nullptr);
   8912  }
   8913 
   8914  return rv;
   8915 }
   8916 
   8917 Document* PresShell::GetPrimaryContentDocument() {
   8918  nsPresContext* context = GetPresContext();
   8919  if (!context || !context->IsRoot()) {
   8920    return nullptr;
   8921  }
   8922 
   8923  nsCOMPtr<nsIDocShellTreeItem> shellAsTreeItem = context->GetDocShell();
   8924  if (!shellAsTreeItem) {
   8925    return nullptr;
   8926  }
   8927 
   8928  nsCOMPtr<nsIDocShellTreeOwner> owner;
   8929  shellAsTreeItem->GetTreeOwner(getter_AddRefs(owner));
   8930  if (!owner) {
   8931    return nullptr;
   8932  }
   8933 
   8934  // now get the primary content shell (active tab)
   8935  nsCOMPtr<nsIDocShellTreeItem> item;
   8936  owner->GetPrimaryContentShell(getter_AddRefs(item));
   8937  nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(item);
   8938  if (!childDocShell) {
   8939    return nullptr;
   8940  }
   8941 
   8942  return childDocShell->GetExtantDocument();
   8943 }
   8944 
   8945 nsresult PresShell::EventHandler::HandleEventWithTarget(
   8946    WidgetEvent* aEvent, nsIFrame* aNewEventFrame, nsIContent* aNewEventContent,
   8947    nsEventStatus* aEventStatus, bool aIsHandlingNativeEvent,
   8948    nsIContent** aTargetContent, nsIContent* aOverrideClickTarget) {
   8949  MOZ_ASSERT(aEvent);
   8950  MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
   8951  MOZ_ASSERT(!aNewEventFrame || aNewEventFrame->PresShell() == mPresShell,
   8952             "wrong shell");
   8953  // NOTE: We don't require that the document still have a PresShell.
   8954  // See bug 1375940.
   8955  NS_ASSERTION(!aNewEventContent || aNewEventContent->IsInComposedDoc(),
   8956               "event for content that isn't in a document");
   8957  NS_ENSURE_STATE(!aNewEventContent ||
   8958                  aNewEventContent->GetComposedDoc() == GetDocument());
   8959  if (aEvent->mClass == ePointerEventClass ||
   8960      aEvent->mClass == eDragEventClass) {
   8961    mPresShell->RecordPointerLocation(aEvent->AsMouseEvent());
   8962  }
   8963  AutoPointerEventTargetUpdater updater(mPresShell, aEvent, aNewEventFrame,
   8964                                        aNewEventContent, aTargetContent);
   8965  AutoCurrentEventInfoSetter eventInfoSetter(
   8966      *this,
   8967      EventTargetInfo(aEvent->mMessage, aNewEventFrame, aNewEventContent));
   8968  nsresult rv = HandleEventWithCurrentEventInfo(aEvent, aEventStatus, false,
   8969                                                aOverrideClickTarget);
   8970  return rv;
   8971 }
   8972 
   8973 namespace {
   8974 
   8975 class MOZ_RAII AutoEventHandler final {
   8976 public:
   8977  AutoEventHandler(WidgetEvent* aEvent, Document* aDocument) : mEvent(aEvent) {
   8978    MOZ_ASSERT(mEvent);
   8979    MOZ_ASSERT(mEvent->IsTrusted());
   8980 
   8981    if (mEvent->mMessage == eMouseDown) {
   8982      PresShell::ReleaseCapturingContent();
   8983      PresShell::AllowMouseCapture(true);
   8984    }
   8985    if (NeedsToUpdateCurrentMouseBtnState()) {
   8986      WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent();
   8987      if (mouseEvent) {
   8988        EventStateManager::sCurrentMouseBtn = mouseEvent->mButton;
   8989      }
   8990    }
   8991  }
   8992 
   8993  ~AutoEventHandler() {
   8994    if (mEvent->mMessage == eMouseDown) {
   8995      PresShell::AllowMouseCapture(false);
   8996    }
   8997    if (NeedsToUpdateCurrentMouseBtnState()) {
   8998      EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed;
   8999    }
   9000  }
   9001 
   9002 protected:
   9003  bool NeedsToUpdateCurrentMouseBtnState() const {
   9004    return mEvent->mMessage == eMouseDown || mEvent->mMessage == eMouseUp ||
   9005           mEvent->mMessage == ePointerDown || mEvent->mMessage == ePointerUp;
   9006  }
   9007 
   9008  WidgetEvent* mEvent;
   9009 };
   9010 
   9011 }  // anonymous namespace
   9012 
   9013 nsresult PresShell::EventHandler::HandleEventWithCurrentEventInfo(
   9014    WidgetEvent* aEvent, nsEventStatus* aEventStatus,
   9015    bool aIsHandlingNativeEvent, nsIContent* aOverrideClickTarget) {
   9016  MOZ_ASSERT(aEvent);
   9017  MOZ_ASSERT(aEventStatus);
   9018 
   9019  RefPtr<EventStateManager> manager = GetPresContext()->EventStateManager();
   9020 
   9021  // If we cannot handle the event with mPresShell because of no target,
   9022  // just record the response time.
   9023  // XXX Is this intentional?  In such case, the score is really good because
   9024  //     of nothing to do.  So, it may make average and median better.
   9025  if (NS_EVENT_NEEDS_FRAME(aEvent) && !mPresShell->GetCurrentEventFrame() &&
   9026      !mPresShell->GetCurrentEventContent()) {
   9027    RecordEventHandlingResponsePerformance(aEvent);
   9028    return NS_OK;
   9029  }
   9030 
   9031  if (mPresShell->mCurrentEventTarget.mContent &&
   9032      aEvent->IsTargetedAtFocusedWindow() &&
   9033      aEvent->AllowFlushingPendingNotifications()) {
   9034    if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   9035      // This may run script now.  So, mPresShell might be destroyed after here.
   9036      nsCOMPtr<nsIContent> currentEventContent =
   9037          mPresShell->mCurrentEventTarget.mContent;
   9038      fm->FlushBeforeEventHandlingIfNeeded(currentEventContent);
   9039    }
   9040  }
   9041 
   9042  bool touchIsNew = false;
   9043  if (!PrepareToDispatchEvent(aEvent, aEventStatus, &touchIsNew)) {
   9044    return NS_OK;
   9045  }
   9046 
   9047  // We finished preparing to dispatch the event.  So, let's record the
   9048  // performance.
   9049  RecordEventPreparationPerformance(aEvent);
   9050 
   9051  AutoHandlingUserInputStatePusher userInpStatePusher(
   9052      UserActivation::IsUserInteractionEvent(aEvent), aEvent);
   9053  AutoEventHandler eventHandler(aEvent, GetDocument());
   9054  AutoPopupStatePusher popupStatePusher(
   9055      PopupBlocker::GetEventPopupControlState(aEvent));
   9056 
   9057  // FIXME. If the event was reused, we need to clear the old target,
   9058  // bug 329430
   9059  aEvent->mTarget = nullptr;
   9060 
   9061  nsresult rv = DispatchEvent(manager, aEvent, touchIsNew, aEventStatus,
   9062                              aOverrideClickTarget);
   9063 
   9064  if (!mPresShell->IsDestroying() && aIsHandlingNativeEvent &&
   9065      aEvent->mClass != eQueryContentEventClass) {
   9066    // Ensure that notifications to IME should be sent before getting next
   9067    // native event from the event queue.
   9068    // XXX Should we check the event message or event class instead of
   9069    //     using aIsHandlingNativeEvent?
   9070    manager->TryToFlushPendingNotificationsToIME();
   9071  }
   9072 
   9073  FinalizeHandlingEvent(aEvent, aEventStatus);
   9074 
   9075  RecordEventHandlingResponsePerformance(aEvent);
   9076 
   9077  return rv;  // Result of DispatchEvent()
   9078 }
   9079 
   9080 nsresult PresShell::EventHandler::DispatchEvent(
   9081    EventStateManager* aEventStateManager, WidgetEvent* aEvent,
   9082    bool aTouchIsNew, nsEventStatus* aEventStatus,
   9083    nsIContent* aOverrideClickTarget) {
   9084  MOZ_ASSERT(aEventStateManager);
   9085  MOZ_ASSERT(aEvent);
   9086  MOZ_ASSERT(aEventStatus);
   9087 
   9088  // 1. Give event to event manager for pre event state changes and
   9089  //    generation of synthetic events.
   9090  {  // Scope for presContext
   9091    RefPtr<nsPresContext> presContext = GetPresContext();
   9092    nsCOMPtr<nsIContent> eventContent =
   9093        mPresShell->mCurrentEventTarget.mContent;
   9094    nsresult rv = aEventStateManager->PreHandleEvent(
   9095        presContext, aEvent, mPresShell->mCurrentEventTarget.mFrame,
   9096        eventContent, aEventStatus, aOverrideClickTarget);
   9097    if (NS_FAILED(rv)) {
   9098      return rv;
   9099    }
   9100    // Let's retarget eMouseMove target if the preceding mouse boundary events
   9101    // caused removing the target from the tree and EventStateManager knows that
   9102    // the deepest connected mouseenter target which was an ancestor of the
   9103    // removed target.  This matches with Chrome Canary with enabling the
   9104    // new mouse/pointer boundary event feature.  However, they stop dispatching
   9105    // "pointermove" in the same case.  Therefore, for now, we should do this
   9106    // only for eMouseMove.
   9107    if (eventContent && aEvent->mMessage == eMouseMove &&
   9108        (!eventContent->IsInComposedDoc() ||
   9109         eventContent->OwnerDoc() != mPresShell->GetDocument())) {
   9110      const OverOutElementsWrapper* const boundaryEventTargets =
   9111          aEventStateManager->GetExtantMouseBoundaryEventTarget();
   9112      const nsIContent* outEventTarget =
   9113          boundaryEventTargets ? boundaryEventTargets->GetOutEventTarget()
   9114                               : nullptr;
   9115      nsIContent* const deepestLeaveEventTarget =
   9116          boundaryEventTargets
   9117              ? boundaryEventTargets->GetDeepestLeaveEventTarget()
   9118              : nullptr;
   9119      // If the last "over" target (next "out" target) is there, it means that
   9120      // it was temporarily removed.  In such case, EventStateManager treats
   9121      // it as never disconnected.  Therefore, we need to do nothing here.
   9122      // Additionally, if there is no last deepest "enter" event target, we
   9123      // lost the target.  Therefore, we should keep the traditional behavior,
   9124      // to dispatch it on the Document node.
   9125      if (!outEventTarget && deepestLeaveEventTarget) {
   9126        nsIFrame* const frame =
   9127            deepestLeaveEventTarget->GetPrimaryFrame(FlushType::Layout);
   9128        if (MOZ_UNLIKELY(mPresShell->IsDestroying())) {
   9129          return NS_OK;
   9130        }
   9131        if (frame) {
   9132          mPresShell->mCurrentEventTarget.mFrame = frame;
   9133          mPresShell->mCurrentEventTarget.mContent = deepestLeaveEventTarget;
   9134        }
   9135      }
   9136    }
   9137  }
   9138 
   9139  // 2. Give event to the DOM for third party and JS use.
   9140  bool wasHandlingKeyBoardEvent = nsContentUtils::IsHandlingKeyBoardEvent();
   9141  if (aEvent->mClass == eKeyboardEventClass) {
   9142    nsContentUtils::SetIsHandlingKeyBoardEvent(true);
   9143  }
   9144  // If EventStateManager or something wants reply from remote process and
   9145  // needs to win any other event listeners in chrome, the event is both
   9146  // stopped its propagation and marked as "waiting reply from remote
   9147  // process".  In this case, PresShell shouldn't dispatch the event into
   9148  // the DOM tree because they don't have a chance to stop propagation in
   9149  // the system event group.  On the other hand, if its propagation is not
   9150  // stopped, that means that the event may be reserved by chrome.  If it's
   9151  // reserved by chrome, the event shouldn't be sent to any remote
   9152  // processes.  In this case, PresShell needs to dispatch the event to
   9153  // the DOM tree for checking if it's reserved.
   9154  if (aEvent->IsAllowedToDispatchDOMEvent() &&
   9155      !(aEvent->PropagationStopped() &&
   9156        aEvent->IsWaitingReplyFromRemoteProcess())) {
   9157    MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
   9158               "Somebody changed aEvent to cause a DOM event!");
   9159    nsPresShellEventCB eventCB(mPresShell);
   9160    if (nsIFrame* target = mPresShell->GetCurrentEventFrame()) {
   9161      if (target->OnlySystemGroupDispatch(aEvent->mMessage)) {
   9162        aEvent->StopPropagation();
   9163      }
   9164    }
   9165    if (aEvent->mClass == eTouchEventClass) {
   9166      DispatchTouchEventToDOM(aEvent, aEventStatus, &eventCB, aTouchIsNew);
   9167    } else {
   9168      DispatchEventToDOM(aEvent, aEventStatus, &eventCB);
   9169    }
   9170  }
   9171 
   9172  nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
   9173 
   9174  if (mPresShell->IsDestroying()) {
   9175    return NS_OK;
   9176  }
   9177 
   9178  // 3. Give event to event manager for post event state changes and
   9179  //    generation of synthetic events.
   9180  // Refetch the prescontext, in case it changed.
   9181  RefPtr<nsPresContext> presContext = GetPresContext();
   9182  return aEventStateManager->PostHandleEvent(
   9183      presContext, aEvent, mPresShell->GetCurrentEventFrame(), aEventStatus,
   9184      aOverrideClickTarget);
   9185 }
   9186 
   9187 bool PresShell::EventHandler::PrepareToDispatchEvent(
   9188    WidgetEvent* aEvent, nsEventStatus* aEventStatus, bool* aTouchIsNew) {
   9189  MOZ_ASSERT(aEvent->IsTrusted());
   9190  MOZ_ASSERT(aEventStatus);
   9191  MOZ_ASSERT(aTouchIsNew);
   9192 
   9193  *aTouchIsNew = false;
   9194  if (aEvent->IsUserAction()) {
   9195    mPresShell->mHasHandledUserInput = true;
   9196  }
   9197 
   9198  switch (aEvent->mMessage) {
   9199    case eKeyPress:
   9200    case eKeyDown:
   9201    case eKeyUp: {
   9202      WidgetKeyboardEvent* keyboardEvent = aEvent->AsKeyboardEvent();
   9203      MaybeHandleKeyboardEventBeforeDispatch(keyboardEvent);
   9204      return true;
   9205    }
   9206    case eMouseRawUpdate:
   9207      MOZ_ASSERT_UNREACHABLE(
   9208          "eMouseRawUpdate shouldn't be handled as a DOM event");
   9209      return false;
   9210 
   9211    case eMouseMove: {
   9212      bool allowCapture = EventStateManager::GetActiveEventStateManager() &&
   9213                          GetPresContext() &&
   9214                          GetPresContext()->EventStateManager() ==
   9215                              EventStateManager::GetActiveEventStateManager();
   9216      PresShell::AllowMouseCapture(allowCapture);
   9217      return true;
   9218    }
   9219    case eDrop: {
   9220      nsCOMPtr<nsIDragSession> session =
   9221          nsContentUtils::GetDragSession(GetPresContext());
   9222      if (session) {
   9223        bool onlyChromeDrop = false;
   9224        session->GetOnlyChromeDrop(&onlyChromeDrop);
   9225        if (onlyChromeDrop) {
   9226          aEvent->mFlags.mOnlyChromeDispatch = true;
   9227        }
   9228      }
   9229      return true;
   9230    }
   9231    case eDragExit: {
   9232      if (!StaticPrefs::dom_event_dragexit_enabled()) {
   9233        aEvent->mFlags.mOnlyChromeDispatch = true;
   9234      }
   9235      return true;
   9236    }
   9237    case eContextMenu: {
   9238      // If we cannot open context menu even though eContextMenu is fired, we
   9239      // should stop dispatching it into the DOM.
   9240      WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   9241      if (mouseEvent->IsContextMenuKeyEvent() &&
   9242          !AdjustContextMenuKeyEvent(mouseEvent)) {
   9243        return false;
   9244      }
   9245 
   9246      // If "Shift" state is active, context menu should be forcibly opened even
   9247      // if web apps want to prevent it since we respect our users' intention.
   9248      // In this case, we don't fire "contextmenu" event on web content because
   9249      // of not cancelable.
   9250      if (mouseEvent->IsShift() &&
   9251          StaticPrefs::dom_event_contextmenu_shift_suppresses_event()) {
   9252        aEvent->mFlags.mOnlyChromeDispatch = true;
   9253        aEvent->mFlags.mRetargetToNonNativeAnonymous = true;
   9254      }
   9255      return true;
   9256    }
   9257    case eTouchStart:
   9258    case eTouchMove:
   9259    case eTouchEnd:
   9260    case eTouchCancel:
   9261    case eTouchPointerCancel:
   9262      return mPresShell->mTouchManager.PreHandleEvent(
   9263          aEvent, aEventStatus, *aTouchIsNew,
   9264          mPresShell->mCurrentEventTarget.mContent);
   9265    case eTouchRawUpdate:
   9266      MOZ_ASSERT_UNREACHABLE(
   9267          "eTouchRawUpdate shouldn't be handled as a DOM event");
   9268      return false;
   9269    default:
   9270      return true;
   9271  }
   9272 }
   9273 
   9274 void PresShell::EventHandler::FinalizeHandlingEvent(
   9275    WidgetEvent* aEvent, const nsEventStatus* aStatus) {
   9276  switch (aEvent->mMessage) {
   9277    case eKeyPress:
   9278    case eKeyDown:
   9279    case eKeyUp: {
   9280      if (aEvent->AsKeyboardEvent()->mKeyCode == NS_VK_ESCAPE) {
   9281        if (aEvent->mMessage == eKeyUp) {
   9282          // Reset this flag after key up is handled.
   9283          mPresShell->mIsLastChromeOnlyEscapeKeyConsumed = false;
   9284        } else {
   9285          if (aEvent->mFlags.mOnlyChromeDispatch &&
   9286              aEvent->mFlags.mDefaultPreventedByChrome) {
   9287            mPresShell->mIsLastChromeOnlyEscapeKeyConsumed = true;
   9288          }
   9289          if (aEvent->mMessage == eKeyDown &&
   9290              !aEvent->mFlags.mDefaultPrevented) {
   9291            if (RefPtr<Document> doc = GetDocument()) {
   9292              if (StaticPrefs::dom_closewatcher_enabled()) {
   9293                doc->ProcessCloseRequest();
   9294              } else {
   9295                doc->HandleEscKey();
   9296              }
   9297            }
   9298          }
   9299        }
   9300      }
   9301      if (aEvent->mMessage == eKeyDown) {
   9302        mPresShell->mIsLastKeyDownCanceled = aEvent->mFlags.mDefaultPrevented;
   9303      }
   9304      break;
   9305    }
   9306    case eMouseUp:
   9307      // reset the capturing content now that the mouse button is up
   9308      PresShell::ReleaseCapturingContent();
   9309      break;
   9310    case eMouseRawUpdate:
   9311      MOZ_ASSERT_UNREACHABLE(
   9312          "eMouseRawUpdate shouldn't be handled as a DOM event");
   9313      break;
   9314    case eMouseMove:
   9315      PresShell::AllowMouseCapture(false);
   9316      break;
   9317    case eDrag:
   9318    case eDragEnd:
   9319    case eDragEnter:
   9320    case eDragExit:
   9321    case eDragLeave:
   9322    case eDragOver:
   9323    case eDrop: {
   9324      // After any drag event other than dragstart (which is handled
   9325      // separately, as we need to collect the data first), the DataTransfer
   9326      // needs to be made protected, and then disconnected.
   9327      DataTransfer* dataTransfer = aEvent->AsDragEvent()->mDataTransfer;
   9328      if (dataTransfer) {
   9329        dataTransfer->Disconnect();
   9330      }
   9331      break;
   9332    }
   9333    case eTouchStart:
   9334    case eTouchMove:
   9335    case eTouchEnd:
   9336    case eTouchCancel:
   9337    case eTouchPointerCancel:
   9338    case eMouseLongTap:
   9339    case eContextMenu: {
   9340      mPresShell->mTouchManager.PostHandleEvent(aEvent, aStatus);
   9341      break;
   9342    }
   9343    case eTouchRawUpdate:
   9344      MOZ_ASSERT_UNREACHABLE(
   9345          "eTouchRawUpdate shouldn't be handled as a DOM event");
   9346      break;
   9347    default:
   9348      break;
   9349  }
   9350 
   9351  if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
   9352    if (mouseEvent->mSynthesizeMoveAfterDispatch) {
   9353      PointerEventHandler::SynthesizeMoveToDispatchBoundaryEvents(mouseEvent);
   9354    }
   9355  }
   9356 }
   9357 
   9358 void PresShell::EventHandler::MaybeHandleKeyboardEventBeforeDispatch(
   9359    WidgetKeyboardEvent* aKeyboardEvent) {
   9360  MOZ_ASSERT(aKeyboardEvent);
   9361 
   9362  if (aKeyboardEvent->mKeyCode != NS_VK_ESCAPE) {
   9363    return;
   9364  }
   9365 
   9366  // If we're in fullscreen mode, exit from it forcibly when Escape key is
   9367  // pressed.
   9368  Document* doc = mPresShell->GetCurrentEventContent()
   9369                      ? mPresShell->mCurrentEventTarget.mContent->OwnerDoc()
   9370                      : nullptr;
   9371  Document* root = nsContentUtils::GetInProcessSubtreeRootDocument(doc);
   9372  if (root && root->GetFullscreenElement()) {
   9373    // Prevent default action on ESC key press when exiting
   9374    // DOM fullscreen mode. This prevents the browser ESC key
   9375    // handler from stopping all loads in the document, which
   9376    // would cause <video> loads to stop.
   9377    // XXX We need to claim the Escape key event which will be
   9378    //     dispatched only into chrome is already consumed by
   9379    //     content because we need to prevent its default here
   9380    //     for some reasons (not sure) but we need to detect
   9381    //     if a chrome event handler will call PreventDefault()
   9382    //     again and check it later.
   9383    aKeyboardEvent->PreventDefaultBeforeDispatch(CrossProcessForwarding::eStop);
   9384    aKeyboardEvent->mFlags.mOnlyChromeDispatch = true;
   9385 
   9386    // The event listeners in chrome can prevent this ESC behavior by
   9387    // calling prevent default on the preceding keydown/press events.
   9388    if (aKeyboardEvent->mMessage == eKeyUp) {
   9389      bool shouldExitFullscreen =
   9390          !mPresShell->mIsLastChromeOnlyEscapeKeyConsumed;
   9391      if (!shouldExitFullscreen) {
   9392        if (mPresShell->mLastConsumedEscapeKeyUpForFullscreen &&
   9393            (aKeyboardEvent->mTimeStamp -
   9394             mPresShell->mLastConsumedEscapeKeyUpForFullscreen) <=
   9395                TimeDuration::FromMilliseconds(
   9396                    StaticPrefs::
   9397                        dom_fullscreen_force_exit_on_multiple_escape_interval())) {
   9398          shouldExitFullscreen = true;
   9399          mPresShell->mLastConsumedEscapeKeyUpForFullscreen = TimeStamp();
   9400        } else {
   9401          mPresShell->mLastConsumedEscapeKeyUpForFullscreen =
   9402              aKeyboardEvent->mTimeStamp;
   9403        }
   9404      }
   9405 
   9406      if (shouldExitFullscreen) {
   9407        // ESC key released while in DOM fullscreen mode.
   9408        // Fully exit fullscreen mode for the browser window and documents that
   9409        // received the event.
   9410        Document::AsyncExitFullscreen(root);
   9411      }
   9412    }
   9413  }
   9414 
   9415  if (XRE_IsParentProcess() &&
   9416      !mPresShell->mIsLastChromeOnlyEscapeKeyConsumed) {
   9417    if (PointerLockManager::GetLockedRemoteTarget() ||
   9418        PointerLockManager::IsLocked()) {
   9419      // XXX See above comment to understand the reason why this needs
   9420      //     to claim that the Escape key event is consumed by content
   9421      //     even though it will be dispatched only into chrome.
   9422      aKeyboardEvent->PreventDefaultBeforeDispatch(
   9423          CrossProcessForwarding::eStop);
   9424      aKeyboardEvent->mFlags.mOnlyChromeDispatch = true;
   9425      if (aKeyboardEvent->mMessage == eKeyUp) {
   9426        PointerLockManager::Unlock("EscapeKey");
   9427      }
   9428    }
   9429  }
   9430 }
   9431 
   9432 void PresShell::EventHandler::RecordEventPreparationPerformance(
   9433    const WidgetEvent* aEvent) {
   9434  MOZ_ASSERT(aEvent);
   9435 
   9436  switch (aEvent->mMessage) {
   9437    case eKeyPress:
   9438    case eKeyDown:
   9439    case eKeyUp:
   9440      if (aEvent->AsKeyboardEvent()->ShouldInteractionTimeRecorded()) {
   9441        GetPresContext()->RecordInteractionTime(
   9442            nsPresContext::InteractionType::KeyInteraction, aEvent->mTimeStamp);
   9443      }
   9444      glean::layout::input_event_queued_keyboard.AccumulateRawDuration(
   9445          TimeStamp::Now() - aEvent->mTimeStamp);
   9446      return;
   9447 
   9448    case eMouseDown:
   9449    case eMouseUp:
   9450      glean::layout::input_event_queued_click.AccumulateRawDuration(
   9451          TimeStamp::Now() - aEvent->mTimeStamp);
   9452      [[fallthrough]];
   9453    case ePointerDown:
   9454    case ePointerUp:
   9455      GetPresContext()->RecordInteractionTime(
   9456          nsPresContext::InteractionType::ClickInteraction, aEvent->mTimeStamp);
   9457      return;
   9458 
   9459    case eMouseRawUpdate:
   9460      MOZ_ASSERT_UNREACHABLE(
   9461          "eMouseRawUpdate shouldn't be handled as a DOM event");
   9462      break;
   9463    case eMouseMove:
   9464      GetPresContext()->RecordInteractionTime(
   9465          nsPresContext::InteractionType::MouseMoveInteraction,
   9466          aEvent->mTimeStamp);
   9467      return;
   9468 
   9469    default:
   9470      return;
   9471  }
   9472 }
   9473 
   9474 void PresShell::EventHandler::RecordEventHandlingResponsePerformance(
   9475    const WidgetEvent* aEvent) {
   9476  if (!Telemetry::CanRecordBase() || aEvent->mTimeStamp.IsNull() ||
   9477      aEvent->mTimeStamp <= mPresShell->mLastOSWake ||
   9478      !aEvent->AsInputEvent()) {
   9479    return;
   9480  }
   9481 
   9482  TimeStamp now = TimeStamp::Now();
   9483  TimeDuration duration = now - aEvent->mTimeStamp;
   9484  glean::layout::input_event_response.AccumulateRawDuration(duration);
   9485  if (GetDocument() &&
   9486      GetDocument()->GetReadyStateEnum() != Document::READYSTATE_COMPLETE) {
   9487    glean::layout::load_input_event_response.AccumulateRawDuration(duration);
   9488  }
   9489 
   9490  if (!sLastInputProcessed || sLastInputProcessed < aEvent->mTimeStamp) {
   9491    if (sLastInputProcessed) {
   9492      // This input event was created after we handled the last one.
   9493      // Accumulate the previous events' coalesced duration.
   9494      glean::layout::input_event_response_coalesced.AccumulateRawDuration(
   9495          sLastInputProcessed - sLastInputCreated);
   9496 
   9497      if (MOZ_UNLIKELY(!PresShell::sProcessInteractable)) {
   9498        // For content process, we use the ready state of
   9499        // top-level-content-document to know if the process has finished the
   9500        // start-up.
   9501        // For parent process, see the topic
   9502        // 'sessionstore-one-or-no-tab-restored' in PresShell::Observe.
   9503        if (XRE_IsContentProcess() && GetDocument() &&
   9504            GetDocument()->IsTopLevelContentDocument()) {
   9505          switch (GetDocument()->GetReadyStateEnum()) {
   9506            case Document::READYSTATE_INTERACTIVE:
   9507            case Document::READYSTATE_COMPLETE:
   9508              PresShell::sProcessInteractable = true;
   9509              break;
   9510            default:
   9511              break;
   9512          }
   9513        }
   9514      }
   9515    }
   9516    sLastInputCreated = aEvent->mTimeStamp;
   9517  } else if (aEvent->mTimeStamp < sLastInputCreated) {
   9518    // This event was created before the last input. May be processing out
   9519    // of order, so coalesce backwards, too.
   9520    sLastInputCreated = aEvent->mTimeStamp;
   9521  }
   9522  sLastInputProcessed = now;
   9523 }
   9524 
   9525 // static
   9526 nsIPrincipal*
   9527 PresShell::EventHandler::GetDocumentPrincipalToCompareWithBlacklist(
   9528    PresShell& aPresShell) {
   9529  nsPresContext* presContext = aPresShell.GetPresContext();
   9530  if (NS_WARN_IF(!presContext)) {
   9531    return nullptr;
   9532  }
   9533  return presContext->Document()->GetPrincipalForPrefBasedHacks();
   9534 }
   9535 
   9536 nsresult PresShell::EventHandler::DispatchEventToDOM(
   9537    WidgetEvent* aEvent, nsEventStatus* aEventStatus,
   9538    nsPresShellEventCB* aEventCB) {
   9539  nsresult rv = NS_OK;
   9540  nsCOMPtr<nsINode> eventTarget = mPresShell->mCurrentEventTarget.mContent;
   9541  nsPresShellEventCB* eventCBPtr = aEventCB;
   9542  if (!eventTarget) {
   9543    nsCOMPtr<nsIContent> targetContent;
   9544    if (mPresShell->mCurrentEventTarget.mFrame) {
   9545      targetContent =
   9546          mPresShell->mCurrentEventTarget.mFrame->GetContentForEvent(aEvent);
   9547      if (targetContent && !targetContent->IsElement() &&
   9548          IsForbiddenDispatchingToNonElementContent(aEvent->mMessage)) {
   9549        targetContent =
   9550            targetContent->GetInclusiveFlattenedTreeAncestorElement();
   9551      }
   9552    }
   9553    if (targetContent) {
   9554      eventTarget = targetContent;
   9555    } else if (GetDocument()) {
   9556      eventTarget = GetDocument();
   9557      // If we don't have any content, the callback wouldn't probably
   9558      // do nothing.
   9559      eventCBPtr = nullptr;
   9560    }
   9561  }
   9562  if (eventTarget) {
   9563    if (eventTarget->OwnerDoc()->ShouldResistFingerprinting(
   9564            RFPTarget::WidgetEvents) &&
   9565        aEvent->IsBlockedForFingerprintingResistance()) {
   9566      aEvent->mFlags.mOnlySystemGroupDispatchInContent = true;
   9567    } else if (aEvent->mMessage == eKeyPress) {
   9568      // If eKeyPress event is marked as not dispatched in the default event
   9569      // group in web content, it's caused by non-printable key or key
   9570      // combination.  In this case, UI Events declares that browsers
   9571      // shouldn't dispatch keypress event.  However, some web apps may be
   9572      // broken with this strict behavior due to historical issue.
   9573      // Therefore, we need to keep dispatching keypress event for such keys
   9574      // even with breaking the standard.
   9575      // Similarly, the other browsers sets non-zero value of keyCode or
   9576      // charCode of keypress event to the other.  Therefore, we should
   9577      // behave so, however, some web apps may be broken.  On such web apps,
   9578      // we should keep using legacy our behavior.
   9579      if (!mPresShell->mInitializedWithKeyPressEventDispatchingBlacklist) {
   9580        mPresShell->mInitializedWithKeyPressEventDispatchingBlacklist = true;
   9581        nsCOMPtr<nsIPrincipal> principal =
   9582            GetDocumentPrincipalToCompareWithBlacklist(*mPresShell);
   9583        if (principal) {
   9584          mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys =
   9585              principal->IsURIInPrefList(
   9586                  "dom.keyboardevent.keypress.hack.dispatch_non_printable_"
   9587                  "keys") ||
   9588              principal->IsURIInPrefList(
   9589                  "dom.keyboardevent.keypress.hack."
   9590                  "dispatch_non_printable_keys.addl");
   9591 
   9592          mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues |=
   9593              principal->IsURIInPrefList(
   9594                  "dom.keyboardevent.keypress.hack."
   9595                  "use_legacy_keycode_and_charcode") ||
   9596              principal->IsURIInPrefList(
   9597                  "dom.keyboardevent.keypress.hack."
   9598                  "use_legacy_keycode_and_charcode.addl");
   9599        }
   9600      }
   9601      if (mPresShell->mForceDispatchKeyPressEventsForNonPrintableKeys) {
   9602        aEvent->mFlags.mOnlySystemGroupDispatchInContent = false;
   9603      }
   9604      if (mPresShell->mForceUseLegacyKeyCodeAndCharCodeValues) {
   9605        aEvent->AsKeyboardEvent()->mUseLegacyKeyCodeAndCharCodeValues = true;
   9606      }
   9607    }
   9608 
   9609    if (aEvent->mClass == eCompositionEventClass) {
   9610      RefPtr<nsPresContext> presContext = GetPresContext();
   9611      RefPtr<BrowserParent> browserParent =
   9612          IMEStateManager::GetActiveBrowserParent();
   9613      IMEStateManager::DispatchCompositionEvent(
   9614          eventTarget, presContext, browserParent, aEvent->AsCompositionEvent(),
   9615          aEventStatus, eventCBPtr);
   9616    } else {
   9617      if (aEvent->IsMouseEventClassOrHasClickRelatedPointerEvent()) {
   9618        PointerEventHandler::RecordMouseButtons(*aEvent->AsMouseEvent());
   9619 #ifdef DEBUG
   9620        if (eventTarget->IsContent() && !eventTarget->IsElement()) {
   9621          NS_WARNING(nsPrintfCString(
   9622                         "%s (IsReal()=%s) target is not an elemnet content "
   9623                         "node, %s\n",
   9624                         ToChar(aEvent->mMessage),
   9625                         aEvent->AsMouseEvent()->IsReal() ? "true" : "false",
   9626                         ToString(*eventTarget).c_str())
   9627                         .get());
   9628          MOZ_CRASH("MouseEvent target must be an element");
   9629        }
   9630 #endif  // #ifdef DEBUG
   9631      }
   9632      RefPtr<nsPresContext> presContext = GetPresContext();
   9633      EventDispatcher::Dispatch(eventTarget, presContext, aEvent, nullptr,
   9634                                aEventStatus, eventCBPtr);
   9635    }
   9636  }
   9637  return rv;
   9638 }
   9639 
   9640 void PresShell::EventHandler::DispatchTouchEventToDOM(
   9641    WidgetEvent* aEvent, nsEventStatus* aEventStatus,
   9642    nsPresShellEventCB* aEventCB, bool aTouchIsNew) {
   9643  MOZ_ASSERT(aEvent->mMessage != eTouchRawUpdate);
   9644  // calling preventDefault on touchstart or the first touchmove for a
   9645  // point prevents mouse events. calling it on the touchend should
   9646  // prevent click dispatching.
   9647  bool canPrevent = (aEvent->mMessage == eTouchStart) ||
   9648                    (aEvent->mMessage == eTouchMove && aTouchIsNew) ||
   9649                    (aEvent->mMessage == eTouchEnd);
   9650  bool preventDefault = false;
   9651  nsEventStatus tmpStatus = nsEventStatus_eIgnore;
   9652  WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
   9653 
   9654  // loop over all touches and dispatch events on any that have changed
   9655  for (dom::Touch* touch : touchEvent->mTouches) {
   9656    // We should remove all suppressed touch instances in
   9657    // TouchManager::PreHandleEvent.
   9658    MOZ_ASSERT(!touch->mIsTouchEventSuppressed);
   9659 
   9660    if (!touch || !touch->mChanged) {
   9661      continue;
   9662    }
   9663 
   9664    nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
   9665    nsCOMPtr<nsIContent> content = do_QueryInterface(targetPtr);
   9666    if (!content) {
   9667      continue;
   9668    }
   9669 
   9670    Document* doc = content->OwnerDoc();
   9671    nsIContent* capturingContent = PresShell::GetCapturingContent();
   9672    if (capturingContent) {
   9673      if (capturingContent->OwnerDoc() != doc) {
   9674        // Wrong document, don't dispatch anything.
   9675        continue;
   9676      }
   9677      content = capturingContent;
   9678    }
   9679    // copy the event
   9680    MOZ_ASSERT(touchEvent->IsTrusted());
   9681    WidgetTouchEvent newEvent(true, touchEvent->mMessage, touchEvent->mWidget);
   9682    newEvent.AssignTouchEventData(*touchEvent, false);
   9683    newEvent.mTarget = targetPtr;
   9684    newEvent.mFlags.mHandledByAPZ = touchEvent->mFlags.mHandledByAPZ;
   9685 
   9686    RefPtr<PresShell> contentPresShell;
   9687    if (doc == GetDocument()) {
   9688      contentPresShell = doc->GetPresShell();
   9689      if (contentPresShell) {
   9690        // XXXsmaug huge hack. Pushing possibly capturing content,
   9691        //         even though event target is something else.
   9692        contentPresShell->PushCurrentEventInfo(EventTargetInfo(
   9693            newEvent.mMessage, content->GetPrimaryFrame(), content));
   9694      }
   9695    }
   9696 
   9697    RefPtr<nsPresContext> presContext = doc->GetPresContext();
   9698    if (!presContext) {
   9699      if (contentPresShell) {
   9700        contentPresShell->PopCurrentEventInfo();
   9701      }
   9702      continue;
   9703    }
   9704 
   9705    tmpStatus = nsEventStatus_eIgnore;
   9706    EventDispatcher::Dispatch(targetPtr, presContext, &newEvent, nullptr,
   9707                              &tmpStatus, aEventCB);
   9708    if (nsEventStatus_eConsumeNoDefault == tmpStatus ||
   9709        newEvent.mFlags.mMultipleActionsPrevented) {
   9710      preventDefault = true;
   9711    }
   9712 
   9713    if (newEvent.mFlags.mMultipleActionsPrevented) {
   9714      touchEvent->mFlags.mMultipleActionsPrevented = true;
   9715    }
   9716 
   9717    if (contentPresShell) {
   9718      contentPresShell->PopCurrentEventInfo();
   9719    }
   9720  }
   9721 
   9722  if (preventDefault && canPrevent) {
   9723    *aEventStatus = nsEventStatus_eConsumeNoDefault;
   9724  } else {
   9725    *aEventStatus = nsEventStatus_eIgnore;
   9726  }
   9727 }
   9728 
   9729 // Dispatch event to content only (NOT full processing)
   9730 // See also HandleEventWithTarget which does full event processing.
   9731 nsresult PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
   9732                                             WidgetEvent* aEvent,
   9733                                             nsEventStatus* aStatus) {
   9734  nsresult rv = NS_OK;
   9735 
   9736  PushCurrentEventInfo(
   9737      EventTargetInfo(aEvent->mMessage, nullptr, aTargetContent));
   9738 
   9739  // Bug 41013: Check if the event should be dispatched to content.
   9740  // It's possible that we are in the middle of destroying the window
   9741  // and the js context is out of date. This check detects the case
   9742  // that caused a crash in bug 41013, but there may be a better way
   9743  // to handle this situation!
   9744  nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
   9745  if (container) {
   9746    // Dispatch event to content
   9747    rv = EventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent,
   9748                                   nullptr, aStatus);
   9749  }
   9750 
   9751  PopCurrentEventInfo();
   9752  return rv;
   9753 }
   9754 
   9755 // See the method above.
   9756 nsresult PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
   9757                                             Event* aEvent,
   9758                                             nsEventStatus* aStatus) {
   9759  nsresult rv = NS_OK;
   9760 
   9761  PushCurrentEventInfo(EventTargetInfo(aEvent->WidgetEventPtr()->mMessage,
   9762                                       nullptr, aTargetContent));
   9763  nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
   9764  if (container) {
   9765    rv = EventDispatcher::DispatchDOMEvent(aTargetContent, nullptr, aEvent,
   9766                                           mPresContext, aStatus);
   9767  }
   9768 
   9769  PopCurrentEventInfo();
   9770  return rv;
   9771 }
   9772 
   9773 bool PresShell::EventHandler::AdjustContextMenuKeyEvent(
   9774    WidgetMouseEvent* aMouseEvent) {
   9775  // if a menu is open, open the context menu relative to the active item on the
   9776  // menu.
   9777  if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
   9778    nsIFrame* popupFrame = pm->GetTopPopup(widget::PopupType::Menu);
   9779    if (popupFrame) {
   9780      nsIFrame* itemFrame = (static_cast<nsMenuPopupFrame*>(popupFrame))
   9781                                ->GetCurrentMenuItemFrame();
   9782      if (!itemFrame) {
   9783        itemFrame = popupFrame;
   9784      }
   9785 
   9786      nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
   9787      aMouseEvent->mWidget = widget;
   9788      LayoutDeviceIntPoint widgetPoint = widget->WidgetToScreenOffset();
   9789      aMouseEvent->mRefPoint =
   9790          LayoutDeviceIntPoint::FromAppUnitsToNearest(
   9791              itemFrame->GetScreenRectInAppUnits().BottomLeft(),
   9792              itemFrame->PresContext()->AppUnitsPerDevPixel()) -
   9793          widgetPoint;
   9794 
   9795      mPresShell->mCurrentEventTarget.SetFrameAndContent(
   9796          aMouseEvent->mMessage, itemFrame,
   9797          itemFrame->GetContent()
   9798              ? itemFrame->GetContent()
   9799                    ->GetInclusiveFlattenedTreeAncestorElement()
   9800              : nullptr);
   9801 
   9802      return true;
   9803    }
   9804  }
   9805 
   9806  // If we're here because of the key-equiv for showing context menus, we
   9807  // have to twiddle with the NS event to make sure the context menu comes
   9808  // up in the upper left of the relevant content area before we create
   9809  // the DOM event. Since we never call InitMouseEvent() on the event,
   9810  // the client X/Y will be 0,0. We can make use of that if the widget is null.
   9811  // Use the root widget since it's most likely to exist, and the coordinates
   9812  // returned by GetCurrentItemAndPositionForElement are relative to it.
   9813  nsRootPresContext* rootPC = GetPresContext()->GetRootPresContext();
   9814  aMouseEvent->mRefPoint = LayoutDeviceIntPoint();
   9815  if (rootPC) {
   9816    aMouseEvent->mWidget = rootPC->PresShell()->GetRootWidget();
   9817    if (aMouseEvent->mWidget) {
   9818      // default the refpoint to the topleft of our document
   9819      if (nsIFrame* rootFrame = FrameConstructor()->GetRootFrame()) {
   9820        auto frameToWidgetOffset =
   9821            nsLayoutUtils::FrameToWidgetOffset(rootFrame, aMouseEvent->mWidget);
   9822        MOZ_ASSERT(frameToWidgetOffset, "If rootPC has a widget, so should we");
   9823        if (frameToWidgetOffset) {
   9824          aMouseEvent->mRefPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(
   9825              *frameToWidgetOffset, GetPresContext()->AppUnitsPerDevPixel());
   9826        }
   9827      }
   9828    }
   9829  } else {
   9830    aMouseEvent->mWidget = nullptr;
   9831  }
   9832 
   9833  // see if we should use the caret position for the popup
   9834  LayoutDeviceIntPoint caretPoint;
   9835  // Beware! This may flush notifications via synchronous
   9836  // ScrollSelectionIntoView.
   9837  if (PrepareToUseCaretPosition(MOZ_KnownLive(aMouseEvent->mWidget),
   9838                                caretPoint)) {
   9839    // caret position is good
   9840    int32_t devPixelRatio = GetPresContext()->AppUnitsPerDevPixel();
   9841    caretPoint = LayoutDeviceIntPoint::FromAppUnitsToNearest(
   9842        ViewportUtils::LayoutToVisual(
   9843            LayoutDeviceIntPoint::ToAppUnits(caretPoint, devPixelRatio),
   9844            GetPresContext()->PresShell()),
   9845        devPixelRatio);
   9846    aMouseEvent->mRefPoint = caretPoint;
   9847    return true;
   9848  }
   9849 
   9850  // If we're here because of the key-equiv for showing context menus, we
   9851  // have to reset the event target to the currently focused element. Get it
   9852  // from the focus controller.
   9853  RefPtr<Element> currentFocus = nsFocusManager::GetFocusedElementStatic();
   9854 
   9855  // Reset event coordinates relative to focused frame in view
   9856  if (currentFocus) {
   9857    nsCOMPtr<nsIContent> currentPointElement;
   9858    GetCurrentItemAndPositionForElement(
   9859        currentFocus, getter_AddRefs(currentPointElement),
   9860        aMouseEvent->mRefPoint, MOZ_KnownLive(aMouseEvent->mWidget));
   9861    if (currentPointElement) {
   9862      mPresShell->mCurrentEventTarget.SetFrameAndContent(
   9863          aMouseEvent->mMessage, nullptr, currentPointElement);
   9864      mPresShell->GetCurrentEventFrame();
   9865    }
   9866  }
   9867 
   9868  return true;
   9869 }
   9870 
   9871 // PresShell::EventHandler::PrepareToUseCaretPosition
   9872 //
   9873 //    This checks to see if we should use the caret position for popup context
   9874 //    menus. Returns true if the caret position should be used, and the
   9875 //    coordinates of that position is returned in aTargetPt. This function
   9876 //    will also scroll the window as needed to make the caret visible.
   9877 //
   9878 //    The event widget should be the widget that generated the event, and
   9879 //    whose coordinate system the resulting event's mRefPoint should be
   9880 //    relative to.  The returned point is in device pixels realtive to the
   9881 //    widget passed in.
   9882 bool PresShell::EventHandler::PrepareToUseCaretPosition(
   9883    nsIWidget* aEventWidget, LayoutDeviceIntPoint& aTargetPt) {
   9884  nsresult rv;
   9885 
   9886  // check caret visibility
   9887  RefPtr<nsCaret> caret = mPresShell->GetCaret();
   9888  NS_ENSURE_TRUE(caret, false);
   9889 
   9890  bool caretVisible = caret->IsVisible();
   9891  if (!caretVisible) {
   9892    return false;
   9893  }
   9894 
   9895  // caret selection, this is a temporary weak reference, so no refcounting is
   9896  // needed
   9897  Selection* domSelection = caret->GetSelection();
   9898  NS_ENSURE_TRUE(domSelection, false);
   9899 
   9900  // since the match could be an anonymous textnode inside a
   9901  // <textarea> or text <input>, we need to get the outer frame
   9902  // note: frames are not refcounted
   9903  nsIFrame* frame = nullptr;  // may be nullptr
   9904  nsINode* node = domSelection->GetFocusNode();
   9905  NS_ENSURE_TRUE(node, false);
   9906  nsCOMPtr<nsIContent> content = nsIContent::FromNode(node);
   9907  if (content) {
   9908    nsIContent* nonNative = content->FindFirstNonChromeOnlyAccessContent();
   9909    content = nonNative;
   9910  }
   9911 
   9912  if (content) {
   9913    // It seems like ScrollSelectionIntoView should be enough, but it's
   9914    // not. The problem is that scrolling the selection into view when it is
   9915    // below the current viewport will align the top line of the frame exactly
   9916    // with the bottom of the window. This is fine, BUT, the popup event causes
   9917    // the control to be re-focused which does this exact call to
   9918    // ScrollContentIntoView, which has a one-pixel disagreement of whether the
   9919    // frame is actually in view. The result is that the frame is aligned with
   9920    // the top of the window, but the menu is still at the bottom.
   9921    //
   9922    // Doing this call first forces the frame to be in view, eliminating the
   9923    // problem. The only difference in the result is that if your cursor is in
   9924    // an edit box below the current view, you'll get the edit box aligned with
   9925    // the top of the window. This is arguably better behavior anyway.
   9926    rv = MOZ_KnownLive(mPresShell)
   9927             ->ScrollContentIntoView(
   9928                 content,
   9929                 ScrollAxis(WhereToScroll::Nearest, WhenToScroll::IfNotVisible),
   9930                 ScrollAxis(WhereToScroll::Nearest, WhenToScroll::IfNotVisible),
   9931                 ScrollFlags::ScrollOverflowHidden);
   9932    NS_ENSURE_SUCCESS(rv, false);
   9933    frame = content->GetPrimaryFrame();
   9934    NS_WARNING_ASSERTION(frame, "No frame for focused content?");
   9935  }
   9936 
   9937  // Actually scroll the selection (ie caret) into view. Note that this must
   9938  // be synchronous since we will be checking the caret position on the screen.
   9939  //
   9940  // Be easy about errors, and just don't scroll in those cases. Better to have
   9941  // the correct menu at a weird place than the wrong menu.
   9942  // After ScrollSelectionIntoView(), the pending notifications might be
   9943  // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
   9944  const nsCOMPtr<nsISelectionController> selCon =
   9945      frame ? frame->GetSelectionController()
   9946            : static_cast<nsISelectionController*>(mPresShell);
   9947  if (selCon) {
   9948    rv = selCon->ScrollSelectionIntoView(
   9949        SelectionType::eNormal, nsISelectionController::SELECTION_FOCUS_REGION,
   9950        SelectionScrollMode::SyncFlush);
   9951    NS_ENSURE_SUCCESS(rv, false);
   9952  }
   9953 
   9954  nsPresContext* presContext = GetPresContext();
   9955 
   9956  if (!aEventWidget) {
   9957    return false;
   9958  }
   9959  // get caret position relative to the closest widget
   9960  nsRect caretCoords;
   9961  nsIFrame* caretFrame = caret->GetGeometry(&caretCoords);
   9962  if (!caretFrame) {
   9963    return false;
   9964  }
   9965 
   9966  if (aEventWidget) {
   9967    if (auto offset =
   9968            nsLayoutUtils::FrameToWidgetOffset(caretFrame, aEventWidget)) {
   9969      caretCoords.MoveBy(*offset);
   9970    }
   9971  }
   9972 
   9973  // caret coordinates are in app units, convert to pixels
   9974  aTargetPt.x =
   9975      presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
   9976  aTargetPt.y =
   9977      presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
   9978 
   9979  // make sure rounding doesn't return a pixel which is outside the caret
   9980  // (e.g. one line lower)
   9981  aTargetPt.y -= 1;
   9982 
   9983  return true;
   9984 }
   9985 
   9986 void PresShell::EventHandler::GetCurrentItemAndPositionForElement(
   9987    Element* aFocusedElement, nsIContent** aTargetToUse,
   9988    LayoutDeviceIntPoint& aTargetPt, nsIWidget* aRootWidget) {
   9989  nsCOMPtr<nsIContent> focusedContent = aFocusedElement;
   9990  MOZ_KnownLive(mPresShell)
   9991      ->ScrollContentIntoView(focusedContent, ScrollAxis(), ScrollAxis(),
   9992                              ScrollFlags::ScrollOverflowHidden);
   9993 
   9994  nsPresContext* presContext = GetPresContext();
   9995 
   9996  bool istree = false, checkLineHeight = true;
   9997  nscoord extraTreeY = 0;
   9998 
   9999  // Set the position to just underneath the current item for multi-select
  10000  // lists or just underneath the selected item for single-select lists. If
  10001  // the element is not a list, or there is no selection, leave the position
  10002  // as is.
  10003  nsCOMPtr<Element> item;
  10004  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
  10005      aFocusedElement->AsXULMultiSelectControl();
  10006  if (multiSelect) {
  10007    checkLineHeight = false;
  10008 
  10009    int32_t currentIndex;
  10010    multiSelect->GetCurrentIndex(&currentIndex);
  10011    if (currentIndex >= 0) {
  10012      RefPtr<XULTreeElement> tree = XULTreeElement::FromNode(focusedContent);
  10013      // Tree view special case (tree items have no frames)
  10014      // Get the focused row and add its coordinates, which are already in
  10015      // pixels
  10016      // XXX Boris, should we create a new interface so that this doesn't
  10017      // need to know about trees? Something like nsINodelessChildCreator
  10018      // which could provide the current focus coordinates?
  10019      if (tree) {
  10020        tree->EnsureRowIsVisible(currentIndex);
  10021        int32_t firstVisibleRow = tree->GetFirstVisibleRow();
  10022        int32_t rowHeight = tree->RowHeight();
  10023 
  10024        extraTreeY += nsPresContext::CSSPixelsToAppUnits(
  10025            (currentIndex - firstVisibleRow + 1) * rowHeight);
  10026        istree = true;
  10027 
  10028        RefPtr<nsTreeColumns> cols = tree->GetColumns();
  10029        if (cols) {
  10030          nsTreeColumn* col = cols->GetFirstColumn();
  10031          if (col) {
  10032            RefPtr<Element> colElement = col->Element();
  10033            nsIFrame* frame = colElement->GetPrimaryFrame();
  10034            if (frame) {
  10035              extraTreeY += frame->GetSize().height;
  10036            }
  10037          }
  10038        }
  10039      } else {
  10040        multiSelect->GetCurrentItem(getter_AddRefs(item));
  10041      }
  10042    }
  10043  } else {
  10044    // don't check menulists as the selected item will be inside a popup.
  10045    nsCOMPtr<nsIDOMXULMenuListElement> menulist =
  10046        aFocusedElement->AsXULMenuList();
  10047    if (!menulist) {
  10048      nsCOMPtr<nsIDOMXULSelectControlElement> select =
  10049          aFocusedElement->AsXULSelectControl();
  10050      if (select) {
  10051        checkLineHeight = false;
  10052        select->GetSelectedItem(getter_AddRefs(item));
  10053      }
  10054    }
  10055  }
  10056 
  10057  if (item) {
  10058    focusedContent = item;
  10059  }
  10060 
  10061  if (nsIFrame* frame = focusedContent->GetPrimaryFrame()) {
  10062    NS_ASSERTION(
  10063        frame->PresContext() == GetPresContext(),
  10064        "handling event for focused content that is not in our document?");
  10065 
  10066    nsPoint widgetOffset;
  10067    if (aRootWidget) {
  10068      if (auto offset =
  10069              nsLayoutUtils::FrameToWidgetOffset(frame, aRootWidget)) {
  10070        widgetOffset = *offset;
  10071      }
  10072    }
  10073 
  10074    // Start context menu down and to the right from top left of frame
  10075    // use the lineheight. This is a good distance to move the context
  10076    // menu away from the top left corner of the frame. If we always
  10077    // used the frame height, the context menu could end up far away,
  10078    // for example when we're focused on linked images.
  10079    // On the other hand, we want to use the frame height if it's less
  10080    // than the current line height, so that the context menu appears
  10081    // associated with the correct frame.
  10082    nscoord extra = 0;
  10083    if (!istree) {
  10084      extra = frame->GetSize().height;
  10085      if (checkLineHeight) {
  10086        ScrollContainerFrame* scrollContainerFrame =
  10087            nsLayoutUtils::GetNearestScrollContainerFrame(
  10088                frame, nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN |
  10089                           nsLayoutUtils::SCROLLABLE_FIXEDPOS_FINDS_ROOT);
  10090        if (scrollContainerFrame) {
  10091          nsSize scrollAmount = scrollContainerFrame->GetLineScrollAmount();
  10092          int32_t APD = presContext->AppUnitsPerDevPixel();
  10093          int32_t scrollAPD =
  10094              scrollContainerFrame->PresContext()->AppUnitsPerDevPixel();
  10095          scrollAmount = scrollAmount.ScaleToOtherAppUnits(scrollAPD, APD);
  10096          if (extra > scrollAmount.height) {
  10097            extra = scrollAmount.height;
  10098          }
  10099        }
  10100      }
  10101    }
  10102 
  10103    aTargetPt.x = presContext->AppUnitsToDevPixels(widgetOffset.x);
  10104    aTargetPt.y =
  10105        presContext->AppUnitsToDevPixels(widgetOffset.y + extra + extraTreeY);
  10106  }
  10107 
  10108  NS_IF_ADDREF(*aTargetToUse = focusedContent);
  10109 }
  10110 
  10111 bool PresShell::ShouldIgnoreInvalidation() {
  10112  return mPaintingSuppressed || !mIsActive || mIsNeverPainting;
  10113 }
  10114 
  10115 void PresShell::WillPaint() {
  10116  // Check the simplest things first.  In particular, it's important to
  10117  // check mIsActive before making any of the more expensive calls such
  10118  // as GetRootPresContext, for the case of a browser with a large
  10119  // number of tabs.
  10120  // Don't bother doing anything if we still have painting suppressed or we are
  10121  // not active.
  10122  if (!mIsActive || mPaintingSuppressed || !IsVisible()) {
  10123    return;
  10124  }
  10125 
  10126  nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
  10127  if (!rootPresContext) {
  10128    // In some edge cases, such as when we don't have a root frame yet,
  10129    // we can't find the root prescontext. There's nothing to do in that
  10130    // case.
  10131    return;
  10132  }
  10133 
  10134  rootPresContext->FlushWillPaintObservers();
  10135  if (mIsDestroying) {
  10136    return;
  10137  }
  10138 
  10139  // Process reflows, if we have them, to reduce flicker due to invalidates and
  10140  // reflow being interspersed.  Note that we _do_ allow this to be
  10141  // interruptible; if we can't do all the reflows it's better to flicker a bit
  10142  // than to freeze up.
  10143  FlushPendingNotifications(ChangesToFlush(FlushType::InterruptibleLayout,
  10144                                           /* aFlushAnimations = */ false,
  10145                                           /* aUpdateRelevancy = */ false));
  10146  if (mIsDestroying) {
  10147    return;
  10148  }
  10149  mDocument->EnumerateSubDocuments(
  10150      [](Document& aSubdoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
  10151        if (RefPtr ps = aSubdoc.GetPresShell()) {
  10152          if (!ps->IsUnderHiddenEmbedderElement()) {
  10153            ps->WillPaint();
  10154          }
  10155        }
  10156        return CallState::Continue;
  10157      });
  10158 }
  10159 
  10160 void PresShell::DidPaintWindow() {
  10161  if (mHasReceivedPaintMessage) {
  10162    return;
  10163  }
  10164  mHasReceivedPaintMessage = true;
  10165  nsPIDOMWindowOuter* win = mDocument->GetWindow();
  10166  if (!win || !nsGlobalWindowOuter::Cast(win)->IsChromeWindow()) {
  10167    return;
  10168  }
  10169  if (nsCOMPtr<nsIObserverService> obsvc = services::GetObserverService()) {
  10170    obsvc->NotifyObservers(win, "widget-first-paint", nullptr);
  10171  }
  10172 }
  10173 
  10174 nsSubDocumentFrame* PresShell::GetInProcessEmbedderFrame() const {
  10175  nsIFrame* f = mEmbedderFrame.GetFrame();
  10176  MOZ_ASSERT_IF(f, f->IsSubDocumentFrame());
  10177  return static_cast<nsSubDocumentFrame*>(f);
  10178 }
  10179 
  10180 bool PresShell::IsVisible() const {
  10181  return mIsActive && !IsUnderHiddenEmbedderElement();
  10182 }
  10183 
  10184 void PresShell::SuppressDisplayport(bool aEnabled) {
  10185  if (aEnabled) {
  10186    mActiveSuppressDisplayport++;
  10187  } else if (mActiveSuppressDisplayport > 0) {
  10188    bool isSuppressed = IsDisplayportSuppressed();
  10189    mActiveSuppressDisplayport--;
  10190    if (isSuppressed && !IsDisplayportSuppressed()) {
  10191      // We unsuppressed the displayport, trigger a paint
  10192      if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
  10193        rootFrame->SchedulePaint();
  10194      }
  10195    }
  10196  }
  10197 }
  10198 
  10199 static bool sDisplayPortSuppressionRespected = true;
  10200 
  10201 void PresShell::RespectDisplayportSuppression(bool aEnabled) {
  10202  bool isSuppressed = IsDisplayportSuppressed();
  10203  sDisplayPortSuppressionRespected = aEnabled;
  10204  if (isSuppressed && !IsDisplayportSuppressed()) {
  10205    // We unsuppressed the displayport, trigger a paint
  10206    if (nsIFrame* rootFrame = mFrameConstructor->GetRootFrame()) {
  10207      rootFrame->SchedulePaint();
  10208    }
  10209  }
  10210 }
  10211 
  10212 bool PresShell::IsDisplayportSuppressed() {
  10213  return sDisplayPortSuppressionRespected && mActiveSuppressDisplayport > 0;
  10214 }
  10215 
  10216 static CallState FreezeSubDocument(Document& aDocument) {
  10217  if (PresShell* presShell = aDocument.GetPresShell()) {
  10218    presShell->Freeze();
  10219  }
  10220  return CallState::Continue;
  10221 }
  10222 
  10223 void PresShell::Freeze(bool aIncludeSubDocuments) {
  10224  mUpdateApproximateFrameVisibilityEvent.Revoke();
  10225 
  10226  MaybeReleaseCapturingContent();
  10227 
  10228  if (mCaret) {
  10229    SetCaretEnabled(false);
  10230  }
  10231 
  10232  mPaintingSuppressed = true;
  10233 
  10234  if (aIncludeSubDocuments && mDocument) {
  10235    mDocument->EnumerateSubDocuments(FreezeSubDocument);
  10236  }
  10237 
  10238  nsPresContext* presContext = GetPresContext();
  10239  if (presContext) {
  10240    presContext->DisableInteractionTimeRecording();
  10241    if (presContext->RefreshDriver()->GetPresContext() == presContext) {
  10242      presContext->RefreshDriver()->Freeze();
  10243    }
  10244 
  10245    if (nsPresContext* rootPresContext = presContext->GetRootPresContext()) {
  10246      rootPresContext->ResetUserInputEventsAllowed();
  10247    }
  10248  }
  10249 
  10250  mFrozen = true;
  10251  if (mDocument) {
  10252    UpdateImageLockingState();
  10253  }
  10254 }
  10255 
  10256 void PresShell::FireOrClearDelayedEvents(bool aFireEvents) {
  10257  mNoDelayedMouseEvents = false;
  10258  mNoDelayedKeyEvents = false;
  10259  mNoDelayedSingleTap = false;
  10260  if (!aFireEvents) {
  10261    mDelayedEvents.Clear();
  10262    return;
  10263  }
  10264 
  10265  if (mDocument) {
  10266    RefPtr<Document> doc = mDocument;
  10267    while (!mIsDestroying && mDelayedEvents.Length() &&
  10268           !doc->EventHandlingSuppressed()) {
  10269      UniquePtr<DelayedEvent> ev = std::move(mDelayedEvents[0]);
  10270      mDelayedEvents.RemoveElementAt(0);
  10271      if (ev->IsKeyPressEvent() && mIsLastKeyDownCanceled) {
  10272        continue;
  10273      }
  10274      ev->Dispatch();
  10275    }
  10276    if (!doc->EventHandlingSuppressed()) {
  10277      mDelayedEvents.Clear();
  10278    }
  10279  }
  10280 }
  10281 
  10282 void PresShell::Thaw(bool aIncludeSubDocuments) {
  10283  nsPresContext* presContext = GetPresContext();
  10284  if (presContext &&
  10285      presContext->RefreshDriver()->GetPresContext() == presContext) {
  10286    presContext->RefreshDriver()->Thaw();
  10287  }
  10288 
  10289  if (aIncludeSubDocuments && mDocument) {
  10290    mDocument->EnumerateSubDocuments([](Document& aSubDoc) {
  10291      if (PresShell* presShell = aSubDoc.GetPresShell()) {
  10292        presShell->Thaw();
  10293      }
  10294      return CallState::Continue;
  10295    });
  10296  }
  10297 
  10298  // Get the activeness of our presshell, as this might have changed
  10299  // while we were in the bfcache
  10300  ActivenessMaybeChanged();
  10301 
  10302  // We're now unfrozen
  10303  mFrozen = false;
  10304  UpdateImageLockingState();
  10305 
  10306  UnsuppressPainting();
  10307 
  10308  // In case the above UnsuppressPainting call didn't start the
  10309  // refresh driver, we manually start the refresh driver to
  10310  // ensure nsPresContext::MaybeIncreaseMeasuredTicksSinceLoading
  10311  // can be called for user input events handling.
  10312  if (presContext && presContext->IsRoot()) {
  10313    if (!presContext->RefreshDriver()->HasPendingTick()) {
  10314      presContext->RefreshDriver()->InitializeTimer();
  10315    }
  10316  }
  10317 }
  10318 
  10319 //--------------------------------------------------------
  10320 // Start of protected and private methods on the PresShell
  10321 //--------------------------------------------------------
  10322 
  10323 void PresShell::WillCauseReflow() {
  10324  nsContentUtils::AddScriptBlocker();
  10325  ++mChangeNestCount;
  10326 }
  10327 
  10328 void PresShell::DidCauseReflow() {
  10329  NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
  10330  --mChangeNestCount;
  10331  nsContentUtils::RemoveScriptBlocker();
  10332 }
  10333 
  10334 void PresShell::WillDoReflow() {
  10335  mDocument->FlushUserFontSet();
  10336 
  10337  mPresContext->FlushCounterStyles();
  10338 
  10339  mPresContext->FlushFontFeatureValues();
  10340 
  10341  mPresContext->FlushFontPaletteValues();
  10342 
  10343  mLastReflowStart = GetPerformanceNowUnclamped();
  10344 }
  10345 
  10346 void PresShell::DidDoReflow(bool aInterruptible) {
  10347  MOZ_ASSERT(mPendingDidDoReflow);
  10348  if (!nsContentUtils::IsSafeToRunScript()) {
  10349    // If we're reflowing while script-blocked (e.g. from container query
  10350    // updates), defer our reflow callbacks until the end of our next layout
  10351    // flush.
  10352    SetNeedLayoutFlush();
  10353    return;
  10354  }
  10355 
  10356  auto clearPendingDidDoReflow =
  10357      MakeScopeExit([&] { mPendingDidDoReflow = false; });
  10358 
  10359  mHiddenContentInForcedLayout.Clear();
  10360 
  10361  HandlePostedReflowCallbacks(aInterruptible);
  10362 
  10363  if (mIsDestroying) {
  10364    return;
  10365  }
  10366 
  10367  {
  10368    nsAutoScriptBlocker scriptBlocker;
  10369    AutoAssertNoFlush noReentrantFlush(*this);
  10370    if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
  10371      DOMHighResTimeStamp now = GetPerformanceNowUnclamped();
  10372      docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
  10373    }
  10374 
  10375    SynthesizeMouseMove(false);
  10376 
  10377    mPresContext->NotifyMissingFonts();
  10378  }
  10379 
  10380  if (mIsDestroying) {
  10381    return;
  10382  }
  10383 
  10384  if (mDirtyRoots.IsEmpty()) {
  10385    // We only unsuppress painting if we're out of reflows.  It's pointless to
  10386    // do so if reflows are still pending, since reflows are just going to
  10387    // thrash the frames around some more.  By waiting we avoid an overeager
  10388    // "jitter" effect.
  10389    if (mShouldUnsuppressPainting) {
  10390      mShouldUnsuppressPainting = false;
  10391      UnsuppressAndInvalidate();
  10392    }
  10393  } else {
  10394    // If any new reflow commands were enqueued during the reflow (or we didn't
  10395    // reflow everything because we were interrupted), schedule another reflow
  10396    // event to process them.
  10397    //
  10398    // Note that we want to do this after DidDoReflow(), since that method can
  10399    // change whether there are dirty roots around by flushing, and there's no
  10400    // point in posting a reflow event just to have the flush revoke it.
  10401    EnsureLayoutFlush();
  10402  }
  10403 }
  10404 
  10405 DOMHighResTimeStamp PresShell::GetPerformanceNowUnclamped() {
  10406  DOMHighResTimeStamp now = 0;
  10407 
  10408  if (nsPIDOMWindowInner* window = mDocument->GetInnerWindow()) {
  10409    Performance* perf = window->GetPerformance();
  10410 
  10411    if (perf) {
  10412      now = perf->NowUnclamped();
  10413    }
  10414  }
  10415 
  10416  return now;
  10417 }
  10418 
  10419 bool PresShell::DoReflow(nsIFrame* target, bool aInterruptible,
  10420                         OverflowChangedTracker* aOverflowTracker) {
  10421  nsIURI* uri = mDocument->GetDocumentURI();
  10422  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS(
  10423      "Reflow", LAYOUT_Reflow, uri ? uri->GetSpecOrDefault() : "N/A"_ns);
  10424 
  10425  PerfStats::AutoMetricRecording<PerfStats::Metric::Reflowing> autoRecording;
  10426 
  10427  gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
  10428  TimeStamp timeStart;
  10429  if (tp) {
  10430    tp->Accumulate();
  10431    tp->reflowCount++;
  10432    timeStart = TimeStamp::Now();
  10433  }
  10434 
  10435  // set up a cache that saves all nodes contained in each selection,
  10436  // allowing a fast lookup in `nsTextFrame::IsFrameSelected()`.
  10437  // This cache only lives throughout this reflow call.
  10438  SelectionNodeCache cache(*this);
  10439 
  10440  // Schedule a paint, but don't actually mark this frame as changed for
  10441  // retained DL building purposes. If any child frames get moved, then
  10442  // they will schedule paint again. We could probaby skip this, and just
  10443  // schedule a similar paint when a frame is deleted.
  10444  target->SchedulePaint(nsIFrame::PAINT_DEFAULT, false);
  10445 
  10446  Maybe<uint64_t> innerWindowID;
  10447  if (auto* window = mDocument->GetInnerWindow()) {
  10448    innerWindowID = Some(window->WindowID());
  10449  }
  10450  AutoProfilerTracing tracingLayoutFlush(
  10451      aInterruptible ? "Reflow (interruptible)" : "Reflow (sync)",
  10452      geckoprofiler::category::LAYOUT, std::move(mReflowCause), innerWindowID);
  10453  mReflowCause = nullptr;
  10454 
  10455  FlushPendingScrollAnchorSelections();
  10456 
  10457  const bool isRoot = target == mFrameConstructor->GetRootFrame();
  10458 
  10459  MOZ_ASSERT(isRoot || aOverflowTracker,
  10460             "caller must provide overflow tracker when reflowing "
  10461             "non-root frames");
  10462 
  10463  // CreateReferenceRenderingContext can return nullptr
  10464  UniquePtr<gfxContext> rcx(CreateReferenceRenderingContext());
  10465 
  10466 #ifdef DEBUG
  10467  mCurrentReflowRoot = target;
  10468 #endif
  10469 
  10470  // If the target frame is the root of the frame hierarchy, then
  10471  // use all the available space. If it's simply a `reflow root',
  10472  // then use the target frame's size as the available space.
  10473  WritingMode wm = target->GetWritingMode();
  10474  LogicalSize size(wm);
  10475  if (isRoot) {
  10476    size = LogicalSize(wm, mPresContext->GetVisibleArea().Size());
  10477  } else {
  10478    size = target->GetLogicalSize();
  10479  }
  10480 
  10481  OverflowAreas oldOverflow;  // initialized and used only when !isRoot
  10482  if (!isRoot) {
  10483    oldOverflow = target->GetOverflowAreas();
  10484  }
  10485 
  10486  NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
  10487               "reflow roots should never split");
  10488 
  10489  // Don't pass size directly to the reflow input, since a
  10490  // constrained height implies page/column breaking.
  10491  LogicalSize reflowSize(wm, size.ISize(wm), NS_UNCONSTRAINEDSIZE);
  10492  ReflowInput reflowInput(mPresContext, target, rcx.get(), reflowSize,
  10493                          ReflowInput::InitFlag::CallerWillInit);
  10494 
  10495  if (isRoot) {
  10496    reflowInput.Init(mPresContext);
  10497  } else {
  10498    // Initialize reflow input with current used border and padding,
  10499    // in case this was set specially by the parent frame when the reflow root
  10500    // was reflowed by its parent.
  10501    reflowInput.Init(mPresContext, Nothing(),
  10502                     Some(target->GetLogicalUsedBorder(wm)),
  10503                     Some(target->GetLogicalUsedPadding(wm)));
  10504  }
  10505 
  10506  // fix the computed height
  10507  NS_ASSERTION(reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
  10508               "reflow input should not set margin for reflow roots");
  10509  if (size.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
  10510    nscoord computedBSize =
  10511        size.BSize(wm) -
  10512        reflowInput.ComputedLogicalBorderPadding(wm).BStartEnd(wm);
  10513    computedBSize = std::max(computedBSize, 0);
  10514    reflowInput.SetComputedBSize(computedBSize);
  10515  }
  10516  NS_ASSERTION(
  10517      reflowInput.ComputedISize() ==
  10518          size.ISize(wm) -
  10519              reflowInput.ComputedLogicalBorderPadding(wm).IStartEnd(wm),
  10520      "reflow input computed incorrect inline size");
  10521 
  10522  mPresContext->ReflowStarted(aInterruptible);
  10523  mIsReflowing = true;
  10524 
  10525  nsReflowStatus status;
  10526  ReflowOutput desiredSize(reflowInput);
  10527  target->Reflow(mPresContext, desiredSize, reflowInput, status);
  10528 
  10529  // If an incremental reflow is initiated at a frame other than the
  10530  // root frame, then its desired size had better not change!  If it's
  10531  // initiated at the root, then the size better not change unless its
  10532  // height was unconstrained to start with.
  10533  nsRect boundsRelativeToTarget =
  10534      nsRect(0, 0, desiredSize.Width(), desiredSize.Height());
  10535  const bool isBSizeLimitReflow =
  10536      isRoot && size.BSize(wm) == NS_UNCONSTRAINEDSIZE;
  10537  NS_ASSERTION(isBSizeLimitReflow || desiredSize.Size(wm) == size,
  10538               "non-root frame's desired size changed during an "
  10539               "incremental reflow");
  10540  NS_ASSERTION(status.IsEmpty(), "reflow roots should never split");
  10541 
  10542  target->SetSize(boundsRelativeToTarget.Size());
  10543  target->DidReflow(mPresContext, nullptr);
  10544  if (target->IsInScrollAnchorChain()) {
  10545    ScrollAnchorContainer* container = ScrollAnchorContainer::FindFor(target);
  10546    PostPendingScrollAnchorAdjustment(container);
  10547  }
  10548  if (MOZ_UNLIKELY(isBSizeLimitReflow)) {
  10549    mPresContext->SetVisibleArea(boundsRelativeToTarget);
  10550  }
  10551 
  10552 #ifdef DEBUG
  10553  mCurrentReflowRoot = nullptr;
  10554 #endif
  10555 
  10556  if (!isRoot && oldOverflow != target->GetOverflowAreas()) {
  10557    // The overflow area changed.  Propagate this change to ancestors.
  10558    aOverflowTracker->AddFrame(target->GetParent(),
  10559                               OverflowChangedTracker::CHILDREN_CHANGED);
  10560  }
  10561 
  10562  NS_ASSERTION(
  10563      mPresContext->HasPendingInterrupt() || mFramesToDirty.Count() == 0,
  10564      "Why do we need to dirty anything if not interrupted?");
  10565 
  10566  mIsReflowing = false;
  10567  bool interrupted = mPresContext->HasPendingInterrupt();
  10568  if (interrupted) {
  10569    // Make sure target gets reflowed again.
  10570    for (const auto& key : mFramesToDirty) {
  10571      // Mark frames dirty until target frame.
  10572      for (nsIFrame* f = key; f && !f->IsSubtreeDirty(); f = f->GetParent()) {
  10573        f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
  10574        if (f->IsFlexItem()) {
  10575          nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(f);
  10576        }
  10577 
  10578        if (f == target) {
  10579          break;
  10580        }
  10581      }
  10582    }
  10583 
  10584    NS_ASSERTION(target->IsSubtreeDirty(), "Why is the target not dirty?");
  10585    mDirtyRoots.Add(target);
  10586    SetNeedLayoutFlush();
  10587 
  10588    // Clear mFramesToDirty after we've done the target->IsSubtreeDirty()
  10589    // assertion so that if it fails it's easier to see what's going on.
  10590 #ifdef NOISY_INTERRUPTIBLE_REFLOW
  10591    printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
  10592 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
  10593    mFramesToDirty.Clear();
  10594 
  10595    // Any FlushPendingNotifications with interruptible reflows
  10596    // should be suppressed now. We don't want to do extra reflow work
  10597    // before our reflow event happens.
  10598    mWasLastReflowInterrupted = true;
  10599    EnsureLayoutFlush();
  10600  }
  10601 
  10602  // dump text perf metrics for reflows with significant text processing
  10603  if (tp) {
  10604    if (tp->current.numChars > 100) {
  10605      TimeDuration reflowTime = TimeStamp::Now() - timeStart;
  10606      LogTextPerfStats(tp, this, tp->current, reflowTime.ToMilliseconds(),
  10607                       eLog_reflow, nullptr);
  10608    }
  10609    tp->Accumulate();
  10610  }
  10611 
  10612  return !interrupted;
  10613 }
  10614 
  10615 // used with Telemetry metrics
  10616 #define NS_LONG_REFLOW_TIME_MS 5000
  10617 
  10618 bool PresShell::ProcessReflowCommands(bool aInterruptible) {
  10619  if (mDirtyRoots.IsEmpty() && !mShouldUnsuppressPainting &&
  10620      !mPendingDidDoReflow) {
  10621    // Nothing to do; bail out
  10622    return true;
  10623  }
  10624 
  10625  const bool wasProcessingReflowCommands = mProcessingReflowCommands;
  10626  auto restoreProcessingReflowCommands = MakeScopeExit(
  10627      [&] { mProcessingReflowCommands = wasProcessingReflowCommands; });
  10628  mProcessingReflowCommands = true;
  10629 
  10630  auto timerStart = mozilla::TimeStamp::Now();
  10631  bool interrupted = false;
  10632  if (!mDirtyRoots.IsEmpty()) {
  10633    // If reflow is interruptible, then make a note of our deadline.
  10634    const PRIntervalTime deadline =
  10635        aInterruptible
  10636            ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
  10637            : (PRIntervalTime)0;
  10638 
  10639    // Scope for the reflow entry point
  10640    nsAutoScriptBlocker scriptBlocker;
  10641    WillDoReflow();
  10642    AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
  10643 
  10644    OverflowChangedTracker overflowTracker;
  10645 
  10646    do {
  10647      // Send an incremental reflow notification to the target frame.
  10648      nsIFrame* target = mDirtyRoots.PopShallowestRoot();
  10649 
  10650      if (!target->IsSubtreeDirty()) {
  10651        // It's not dirty anymore, which probably means the notification
  10652        // was posted in the middle of a reflow (perhaps with a reflow
  10653        // root in the middle).  Don't do anything.
  10654        continue;
  10655      }
  10656 
  10657      interrupted = !DoReflow(target, aInterruptible, &overflowTracker);
  10658 
  10659      // Keep going until we're out of reflow commands, or we've run
  10660      // past our deadline, or we're interrupted.
  10661    } while (!interrupted && !mDirtyRoots.IsEmpty() &&
  10662             (!aInterruptible || PR_IntervalNow() < deadline));
  10663 
  10664    interrupted = !mDirtyRoots.IsEmpty();
  10665 
  10666    overflowTracker.Flush();
  10667 
  10668    if (!interrupted) {
  10669      // We didn't get interrupted. Go ahead and perform scroll anchor
  10670      // adjustments.
  10671      FlushPendingScrollAnchorAdjustments();
  10672    }
  10673    mPendingDidDoReflow = true;
  10674  }
  10675 
  10676  // Exiting the scriptblocker might have killed us. If we were processing
  10677  // scroll commands, let the outermost call deal with it.
  10678  if (!mIsDestroying && mPendingDidDoReflow && !wasProcessingReflowCommands) {
  10679    DidDoReflow(aInterruptible);
  10680  }
  10681 
  10682  {
  10683    TimeDuration elapsed = TimeStamp::Now() - timerStart;
  10684    int32_t intElapsed = int32_t(elapsed.ToMilliseconds());
  10685    if (intElapsed > NS_LONG_REFLOW_TIME_MS) {
  10686      glean::layout::long_reflow_interruptible
  10687          .EnumGet(aInterruptible
  10688                       ? glean::layout::LongReflowInterruptibleLabel::eTrue
  10689                       : glean::layout::LongReflowInterruptibleLabel::eFalse)
  10690          .Add();
  10691    }
  10692  }
  10693 
  10694  return !interrupted;
  10695 }
  10696 
  10697 bool PresShell::DoFlushLayout(bool aInterruptible) {
  10698  mFrameConstructor->RecalcQuotesAndCounters();
  10699  return ProcessReflowCommands(aInterruptible);
  10700 }
  10701 
  10702 void PresShell::WindowSizeMoveDone() {
  10703  if (mPresContext) {
  10704    EventStateManager::ClearGlobalActiveContent(nullptr);
  10705    ClearMouseCapture();
  10706  }
  10707 }
  10708 
  10709 NS_IMETHODIMP
  10710 PresShell::Observe(nsISupports* aSubject, const char* aTopic,
  10711                   const char16_t* aData) {
  10712  if (mIsDestroying) {
  10713    NS_WARNING("our observers should have been unregistered by now");
  10714    return NS_OK;
  10715  }
  10716 
  10717  if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
  10718    if (!AssumeAllFramesVisible() &&
  10719        mPresContext->IsRootContentDocumentInProcess()) {
  10720      DoUpdateApproximateFrameVisibility(/* aRemoveOnly = */ true);
  10721    }
  10722    return NS_OK;
  10723  }
  10724 
  10725  if (!nsCRT::strcmp(aTopic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
  10726    mLastOSWake = TimeStamp::Now();
  10727    return NS_OK;
  10728  }
  10729 
  10730  // For parent process, user may expect the UI is interactable after a
  10731  // tab (previously opened page or home page) has restored.
  10732  if (!nsCRT::strcmp(aTopic, "sessionstore-one-or-no-tab-restored")) {
  10733    MOZ_ASSERT(XRE_IsParentProcess());
  10734    sProcessInteractable = true;
  10735 
  10736    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  10737    if (os) {
  10738      os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
  10739    }
  10740    return NS_OK;
  10741  }
  10742 
  10743  if (!nsCRT::strcmp(aTopic, "font-info-updated")) {
  10744    // See how gfxPlatform::ForceGlobalReflow encodes this.
  10745    bool needsReframe = aData && !!aData[0];
  10746    mPresContext->ForceReflowForFontInfoUpdate(needsReframe);
  10747    return NS_OK;
  10748  }
  10749 
  10750  // The "look-and-feel-changed" notification for JS observers will be
  10751  // dispatched HandleGlobalThemeChange once LookAndFeel caches are cleared.
  10752  if (!nsCRT::strcmp(aTopic, "internal-look-and-feel-changed")) {
  10753    // See how LookAndFeel::NotifyChangedAllWindows encodes this.
  10754    auto kind = widget::ThemeChangeKind(aData[0]);
  10755    mPresContext->ThemeChanged(kind);
  10756    return NS_OK;
  10757  }
  10758 
  10759  NS_WARNING(nsPrintfCString("unrecognized topic %s", aTopic).get());
  10760  return NS_ERROR_FAILURE;
  10761 }
  10762 
  10763 bool PresShell::AddRefreshObserver(nsARefreshObserver* aObserver,
  10764                                   FlushType aFlushType,
  10765                                   const char* aObserverDescription) {
  10766  nsPresContext* presContext = GetPresContext();
  10767  if (MOZ_UNLIKELY(!presContext)) {
  10768    return false;
  10769  }
  10770  presContext->RefreshDriver()->AddRefreshObserver(aObserver, aFlushType,
  10771                                                   aObserverDescription);
  10772  return true;
  10773 }
  10774 
  10775 bool PresShell::RemoveRefreshObserver(nsARefreshObserver* aObserver,
  10776                                      FlushType aFlushType) {
  10777  nsPresContext* presContext = GetPresContext();
  10778  return presContext && presContext->RefreshDriver()->RemoveRefreshObserver(
  10779                            aObserver, aFlushType);
  10780 }
  10781 
  10782 bool PresShell::AddPostRefreshObserver(nsAPostRefreshObserver* aObserver) {
  10783  nsPresContext* presContext = GetPresContext();
  10784  if (!presContext) {
  10785    return false;
  10786  }
  10787  presContext->RefreshDriver()->AddPostRefreshObserver(aObserver);
  10788  return true;
  10789 }
  10790 
  10791 bool PresShell::RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver) {
  10792  nsPresContext* presContext = GetPresContext();
  10793  if (!presContext) {
  10794    return false;
  10795  }
  10796  presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver);
  10797  return true;
  10798 }
  10799 
  10800 void PresShell::ScheduleFlush() {
  10801  if (MOZ_UNLIKELY(IsDestroying()) ||
  10802      MOZ_UNLIKELY(mDocument->GetBFCacheEntry())) {
  10803    return;
  10804  }
  10805  mPresContext->RefreshDriver()->ScheduleRenderingPhase(RenderingPhase::Layout);
  10806 }
  10807 
  10808 //------------------------------------------------------
  10809 // End of protected and private methods on the PresShell
  10810 //------------------------------------------------------
  10811 
  10812 //------------------------------------------------------------------
  10813 //-- Delayed event Classes Impls
  10814 //------------------------------------------------------------------
  10815 
  10816 PresShell::DelayedInputEvent::DelayedInputEvent()
  10817    : DelayedEvent(), mEvent(nullptr) {}
  10818 
  10819 PresShell::DelayedInputEvent::~DelayedInputEvent() { delete mEvent; }
  10820 
  10821 void PresShell::DelayedInputEvent::Dispatch() {
  10822  if (!mEvent || !mEvent->mWidget) {
  10823    return;
  10824  }
  10825  nsCOMPtr<nsIWidget> widget = mEvent->mWidget;
  10826  widget->DispatchEvent(mEvent);
  10827 }
  10828 
  10829 PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) {
  10830  MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
  10831  WidgetMouseEvent* mouseEvent =
  10832      new WidgetMouseEvent(true, aEvent->mMessage, aEvent->mWidget,
  10833                           aEvent->mReason, aEvent->mContextMenuTrigger);
  10834  mouseEvent->AssignMouseEventData(*aEvent, false);
  10835  mEvent = mouseEvent;
  10836 }
  10837 
  10838 PresShell::DelayedPointerEvent::DelayedPointerEvent(
  10839    WidgetPointerEvent* aEvent) {
  10840  MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
  10841  MOZ_ASSERT(aEvent->mMessage == eContextMenu);
  10842  WidgetPointerEvent* pointerEvent = new WidgetPointerEvent(
  10843      true, aEvent->mMessage, aEvent->mWidget, aEvent->mContextMenuTrigger);
  10844  pointerEvent->AssignPointerEventData(*aEvent, false);
  10845  mEvent = pointerEvent;
  10846 }
  10847 
  10848 PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) {
  10849  MOZ_DIAGNOSTIC_ASSERT(aEvent->IsTrusted());
  10850  WidgetKeyboardEvent* keyEvent =
  10851      new WidgetKeyboardEvent(true, aEvent->mMessage, aEvent->mWidget);
  10852  keyEvent->AssignKeyEventData(*aEvent, false);
  10853  keyEvent->mFlags.mIsSynthesizedForTests =
  10854      aEvent->mFlags.mIsSynthesizedForTests;
  10855  keyEvent->mFlags.mIsSuppressedOrDelayed = true;
  10856  mEvent = keyEvent;
  10857 }
  10858 
  10859 bool PresShell::DelayedKeyEvent::IsKeyPressEvent() {
  10860  return mEvent->mMessage == eKeyPress;
  10861 }
  10862 
  10863 #ifdef DEBUG
  10864 // Layout debugging hooks
  10865 void PresShell::ListComputedStyles(FILE* out, int32_t aIndent) {
  10866  nsIFrame* rootFrame = GetRootFrame();
  10867  if (rootFrame) {
  10868    rootFrame->Style()->List(out, aIndent);
  10869  }
  10870 
  10871  // The root element's frame's ComputedStyle is the root of a separate tree.
  10872  Element* rootElement = mDocument->GetRootElement();
  10873  if (rootElement) {
  10874    nsIFrame* rootElementFrame = rootElement->GetPrimaryFrame();
  10875    if (rootElementFrame) {
  10876      rootElementFrame->Style()->List(out, aIndent);
  10877    }
  10878  }
  10879 }
  10880 #endif
  10881 
  10882 #if defined(DEBUG) || defined(MOZ_LAYOUT_DEBUGGER)
  10883 void PresShell::ListStyleSheets(FILE* out, int32_t aIndent) {
  10884  auto ListStyleSheetsAtOrigin = [this, out, aIndent](StyleOrigin origin) {
  10885    int32_t sheetCount = StyleSet()->SheetCount(origin);
  10886    for (int32_t i = 0; i < sheetCount; ++i) {
  10887      StyleSet()->SheetAt(origin, i)->List(out, aIndent);
  10888    }
  10889  };
  10890 
  10891  ListStyleSheetsAtOrigin(StyleOrigin::UserAgent);
  10892  ListStyleSheetsAtOrigin(StyleOrigin::User);
  10893  ListStyleSheetsAtOrigin(StyleOrigin::Author);
  10894 }
  10895 #endif
  10896 
  10897 //=============================================================
  10898 //=============================================================
  10899 //-- Debug Reflow Counts
  10900 //=============================================================
  10901 //=============================================================
  10902 #ifdef MOZ_REFLOW_PERF
  10903 //-------------------------------------------------------------
  10904 void PresShell::DumpReflows() {
  10905  if (mReflowCountMgr) {
  10906    nsAutoCString uriStr;
  10907    if (mDocument) {
  10908      nsIURI* uri = mDocument->GetDocumentURI();
  10909      if (uri) {
  10910        uri->GetPathQueryRef(uriStr);
  10911      }
  10912    }
  10913    mReflowCountMgr->DisplayTotals(uriStr.get());
  10914    mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
  10915    mReflowCountMgr->DisplayDiffsInTotals();
  10916  }
  10917 }
  10918 
  10919 //-------------------------------------------------------------
  10920 void PresShell::CountReflows(const char* aName, nsIFrame* aFrame) {
  10921  if (mReflowCountMgr) {
  10922    mReflowCountMgr->Add(aName, aFrame);
  10923  }
  10924 }
  10925 
  10926 //-------------------------------------------------------------
  10927 void PresShell::PaintCount(const char* aName, gfxContext* aRenderingContext,
  10928                           nsPresContext* aPresContext, nsIFrame* aFrame,
  10929                           const nsPoint& aOffset, uint32_t aColor) {
  10930  if (mReflowCountMgr) {
  10931    mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext, aFrame,
  10932                                aOffset, aColor);
  10933  }
  10934 }
  10935 
  10936 //-------------------------------------------------------------
  10937 void PresShell::SetPaintFrameCount(bool aPaintFrameCounts) {
  10938  if (mReflowCountMgr) {
  10939    mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
  10940  }
  10941 }
  10942 
  10943 bool PresShell::IsPaintingFrameCounts() {
  10944  if (mReflowCountMgr) return mReflowCountMgr->IsPaintingFrameCounts();
  10945  return false;
  10946 }
  10947 
  10948 //------------------------------------------------------------------
  10949 //-- Reflow Counter Classes Impls
  10950 //------------------------------------------------------------------
  10951 
  10952 //------------------------------------------------------------------
  10953 ReflowCounter::ReflowCounter(ReflowCountMgr* aMgr) : mMgr(aMgr) {
  10954  ClearTotals();
  10955  SetTotalsCache();
  10956 }
  10957 
  10958 //------------------------------------------------------------------
  10959 ReflowCounter::~ReflowCounter() = default;
  10960 
  10961 //------------------------------------------------------------------
  10962 void ReflowCounter::ClearTotals() { mTotal = 0; }
  10963 
  10964 //------------------------------------------------------------------
  10965 void ReflowCounter::SetTotalsCache() { mCacheTotal = mTotal; }
  10966 
  10967 //------------------------------------------------------------------
  10968 void ReflowCounter::CalcDiffInTotals() { mCacheTotal = mTotal - mCacheTotal; }
  10969 
  10970 //------------------------------------------------------------------
  10971 void ReflowCounter::DisplayTotals(const char* aStr) {
  10972  DisplayTotals(mTotal, aStr ? aStr : "Totals");
  10973 }
  10974 
  10975 //------------------------------------------------------------------
  10976 void ReflowCounter::DisplayDiffTotals(const char* aStr) {
  10977  DisplayTotals(mCacheTotal, aStr ? aStr : "Diff Totals");
  10978 }
  10979 
  10980 //------------------------------------------------------------------
  10981 void ReflowCounter::DisplayHTMLTotals(const char* aStr) {
  10982  DisplayHTMLTotals(mTotal, aStr ? aStr : "Totals");
  10983 }
  10984 
  10985 //------------------------------------------------------------------
  10986 void ReflowCounter::DisplayTotals(uint32_t aTotal, const char* aTitle) {
  10987  // figure total
  10988  if (aTotal == 0) {
  10989    return;
  10990  }
  10991  ReflowCounter* gTots = (ReflowCounter*)mMgr->LookUp(kGrandTotalsStr);
  10992 
  10993  printf("%25s\t", aTitle);
  10994  printf("%d\t", aTotal);
  10995  if (gTots != this && aTotal > 0) {
  10996    gTots->Add(aTotal);
  10997  }
  10998 }
  10999 
  11000 //------------------------------------------------------------------
  11001 void ReflowCounter::DisplayHTMLTotals(uint32_t aTotal, const char* aTitle) {
  11002  if (aTotal == 0) {
  11003    return;
  11004  }
  11005 
  11006  ReflowCounter* gTots = (ReflowCounter*)mMgr->LookUp(kGrandTotalsStr);
  11007  FILE* fd = mMgr->GetOutFile();
  11008  if (!fd) {
  11009    return;
  11010  }
  11011 
  11012  fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
  11013  fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
  11014 
  11015  if (gTots != this && aTotal > 0) {
  11016    gTots->Add(aTotal);
  11017  }
  11018 }
  11019 
  11020 //------------------------------------------------------------------
  11021 //-- ReflowCountMgr
  11022 //------------------------------------------------------------------
  11023 
  11024 #  define KEY_BUF_SIZE_FOR_PTR \
  11025    24  // adequate char[] buffer to sprintf a pointer
  11026 
  11027 ReflowCountMgr::ReflowCountMgr() : mCounts(10), mIndiFrameCounts(10) {
  11028  mCycledOnce = false;
  11029  mDumpFrameCounts = false;
  11030  mDumpFrameByFrameCounts = false;
  11031  mPaintFrameByFrameCounts = false;
  11032 }
  11033 
  11034 //------------------------------------------------------------------
  11035 ReflowCountMgr::~ReflowCountMgr() = default;
  11036 
  11037 //------------------------------------------------------------------
  11038 ReflowCounter* ReflowCountMgr::LookUp(const char* aName) {
  11039  return mCounts.Get(aName);
  11040 }
  11041 
  11042 //------------------------------------------------------------------
  11043 void ReflowCountMgr::Add(const char* aName, nsIFrame* aFrame) {
  11044  NS_ASSERTION(aName != nullptr, "Name shouldn't be null!");
  11045 
  11046  if (mDumpFrameCounts) {
  11047    auto* const counter = mCounts.GetOrInsertNew(aName, this);
  11048    counter->Add();
  11049  }
  11050 
  11051  if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
  11052      aFrame != nullptr) {
  11053    char key[KEY_BUF_SIZE_FOR_PTR];
  11054    SprintfLiteral(key, "%p", (void*)aFrame);
  11055    auto* const counter =
  11056        mIndiFrameCounts
  11057            .LookupOrInsertWith(key,
  11058                                [&aName, &aFrame, this]() {
  11059                                  auto counter =
  11060                                      MakeUnique<IndiReflowCounter>(this);
  11061                                  counter->mFrame = aFrame;
  11062                                  counter->mName.AssignASCII(aName);
  11063                                  return counter;
  11064                                })
  11065            .get();
  11066    // this eliminates extra counts from super classes
  11067    if (counter && counter->mName.EqualsASCII(aName)) {
  11068      counter->mCount++;
  11069      counter->mCounter.Add(1);
  11070    }
  11071  }
  11072 }
  11073 
  11074 //------------------------------------------------------------------
  11075 void ReflowCountMgr::PaintCount(const char* aName,
  11076                                gfxContext* aRenderingContext,
  11077                                nsPresContext* aPresContext, nsIFrame* aFrame,
  11078                                const nsPoint& aOffset, uint32_t aColor) {
  11079  if (mPaintFrameByFrameCounts && aFrame != nullptr) {
  11080    char key[KEY_BUF_SIZE_FOR_PTR];
  11081    SprintfLiteral(key, "%p", (void*)aFrame);
  11082    IndiReflowCounter* counter = mIndiFrameCounts.Get(key);
  11083    if (counter != nullptr && counter->mName.EqualsASCII(aName)) {
  11084      DrawTarget* drawTarget = aRenderingContext->GetDrawTarget();
  11085      int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
  11086 
  11087      aRenderingContext->Save();
  11088      gfxPoint devPixelOffset =
  11089          nsLayoutUtils::PointToGfxPoint(aOffset, appUnitsPerDevPixel);
  11090      aRenderingContext->SetMatrixDouble(
  11091          aRenderingContext->CurrentMatrixDouble().PreTranslate(
  11092              devPixelOffset));
  11093 
  11094      // We don't care about the document language or user fonts here;
  11095      // just get a default Latin font.
  11096      nsFont font(StyleGenericFontFamily::Serif, Length::FromPixels(11));
  11097      nsFontMetrics::Params params;
  11098      params.language = nsGkAtoms::x_western;
  11099      params.textPerf = aPresContext->GetTextPerfMetrics();
  11100      params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
  11101      RefPtr<nsFontMetrics> fm = aPresContext->GetMetricsFor(font, params);
  11102 
  11103      char buf[16];
  11104      int len = SprintfLiteral(buf, "%d", counter->mCount);
  11105      nscoord x = 0, y = fm->MaxAscent();
  11106      nscoord width, height = fm->MaxHeight();
  11107      fm->SetTextRunRTL(false);
  11108      width = fm->GetWidth(buf, len, drawTarget);
  11109 
  11110      sRGBColor color;
  11111      sRGBColor color2;
  11112      if (aColor != 0) {
  11113        color = sRGBColor::FromABGR(aColor);
  11114        color2 = sRGBColor(0.f, 0.f, 0.f);
  11115      } else {
  11116        gfx::Float rc = 0.f, gc = 0.f, bc = 0.f;
  11117        if (counter->mCount < 5) {
  11118          rc = 1.f;
  11119          gc = 1.f;
  11120        } else if (counter->mCount < 11) {
  11121          gc = 1.f;
  11122        } else {
  11123          rc = 1.f;
  11124        }
  11125        color = sRGBColor(rc, gc, bc);
  11126        color2 = sRGBColor(rc / 2, gc / 2, bc / 2);
  11127      }
  11128 
  11129      nsRect rect(0, 0, width + 15, height + 15);
  11130      Rect devPxRect =
  11131          NSRectToSnappedRect(rect, appUnitsPerDevPixel, *drawTarget);
  11132      ColorPattern black(ToDeviceColor(sRGBColor::OpaqueBlack()));
  11133      drawTarget->FillRect(devPxRect, black);
  11134 
  11135      aRenderingContext->SetColor(color2);
  11136      fm->DrawString(buf, len, x + 15, y + 15, aRenderingContext);
  11137      aRenderingContext->SetColor(color);
  11138      fm->DrawString(buf, len, x, y, aRenderingContext);
  11139 
  11140      aRenderingContext->Restore();
  11141    }
  11142  }
  11143 }
  11144 
  11145 //------------------------------------------------------------------
  11146 void ReflowCountMgr::DoGrandTotals() {
  11147  mCounts.WithEntryHandle(kGrandTotalsStr, [this](auto&& entry) {
  11148    if (!entry) {
  11149      entry.Insert(MakeUnique<ReflowCounter>(this));
  11150    } else {
  11151      entry.Data()->ClearTotals();
  11152    }
  11153  });
  11154 
  11155  printf("\t\t\t\tTotal\n");
  11156  for (uint32_t i = 0; i < 78; i++) {
  11157    printf("-");
  11158  }
  11159  printf("\n");
  11160  for (const auto& entry : mCounts) {
  11161    entry.GetData()->DisplayTotals(entry.GetKey());
  11162  }
  11163 }
  11164 
  11165 static void RecurseIndiTotals(
  11166    nsPresContext* aPresContext,
  11167    nsClassHashtable<nsCharPtrHashKey, IndiReflowCounter>& aHT,
  11168    nsIFrame* aParentFrame, int32_t aLevel) {
  11169  if (aParentFrame == nullptr) {
  11170    return;
  11171  }
  11172 
  11173  char key[KEY_BUF_SIZE_FOR_PTR];
  11174  SprintfLiteral(key, "%p", (void*)aParentFrame);
  11175  IndiReflowCounter* counter = aHT.Get(key);
  11176  if (counter) {
  11177    counter->mHasBeenOutput = true;
  11178    char* name = ToNewCString(counter->mName);
  11179    for (int32_t i = 0; i < aLevel; i++) printf(" ");
  11180    printf("%s - %p   [%d][", name, (void*)aParentFrame, counter->mCount);
  11181    printf("%d", counter->mCounter.GetTotal());
  11182    printf("]\n");
  11183    free(name);
  11184  }
  11185 
  11186  for (nsIFrame* child : aParentFrame->PrincipalChildList()) {
  11187    RecurseIndiTotals(aPresContext, aHT, child, aLevel + 1);
  11188  }
  11189 }
  11190 
  11191 //------------------------------------------------------------------
  11192 void ReflowCountMgr::DoIndiTotalsTree() {
  11193  printf("\n------------------------------------------------\n");
  11194  printf("-- Individual Frame Counts\n");
  11195  printf("------------------------------------------------\n");
  11196 
  11197  if (mPresShell) {
  11198    nsIFrame* rootFrame = mPresShell->GetRootFrame();
  11199    RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
  11200    printf("------------------------------------------------\n");
  11201    printf("-- Individual Counts of Frames not in Root Tree\n");
  11202    printf("------------------------------------------------\n");
  11203    for (const auto& counter : mIndiFrameCounts.Values()) {
  11204      if (!counter->mHasBeenOutput) {
  11205        char* name = ToNewCString(counter->mName);
  11206        printf("%s - %p   [%d][", name, (void*)counter->mFrame,
  11207               counter->mCount);
  11208        printf("%d", counter->mCounter.GetTotal());
  11209        printf("]\n");
  11210        free(name);
  11211      }
  11212    }
  11213  }
  11214 }
  11215 
  11216 //------------------------------------------------------------------
  11217 void ReflowCountMgr::DoGrandHTMLTotals() {
  11218  mCounts.WithEntryHandle(kGrandTotalsStr, [this](auto&& entry) {
  11219    if (!entry) {
  11220      entry.Insert(MakeUnique<ReflowCounter>(this));
  11221    } else {
  11222      entry.Data()->ClearTotals();
  11223    }
  11224  });
  11225 
  11226  static const char* title[] = {"Class", "Reflows"};
  11227  fprintf(mFD, "<tr>");
  11228  for (uint32_t i = 0; i < std::size(title); i++) {
  11229    fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
  11230  }
  11231  fprintf(mFD, "</tr>\n");
  11232 
  11233  for (const auto& entry : mCounts) {
  11234    entry.GetData()->DisplayHTMLTotals(entry.GetKey());
  11235  }
  11236 }
  11237 
  11238 //------------------------------------
  11239 void ReflowCountMgr::DisplayTotals(const char* aStr) {
  11240  if (mDumpFrameCounts) {
  11241    DoGrandTotals();
  11242  }
  11243  if (mDumpFrameByFrameCounts) {
  11244    DoIndiTotalsTree();
  11245  }
  11246 }
  11247 //------------------------------------
  11248 void ReflowCountMgr::DisplayHTMLTotals(const char* aStr) {
  11249 #  ifdef WIN32x  // XXX NOT XP!
  11250  char name[1024];
  11251 
  11252  char* sptr = strrchr(aStr, '/');
  11253  if (sptr) {
  11254    sptr++;
  11255    strcpy(name, sptr);
  11256    char* eptr = strrchr(name, '.');
  11257    if (eptr) {
  11258      *eptr = 0;
  11259    }
  11260    strcat(name, "_stats.html");
  11261  }
  11262  mFD = fopen(name, "w");
  11263  if (mFD) {
  11264    fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
  11265    const char* title = aStr ? aStr : "No name";
  11266    fprintf(mFD,
  11267            "<center><b>%s</b><br><table border=1 "
  11268            "style=\"background-color:#e0e0e0\">",
  11269            title);
  11270    DoGrandHTMLTotals();
  11271    fprintf(mFD, "</center></table>\n");
  11272    fprintf(mFD, "</body></html>\n");
  11273    fclose(mFD);
  11274    mFD = nullptr;
  11275  }
  11276 #  endif  // not XP!
  11277 }
  11278 
  11279 //------------------------------------------------------------------
  11280 void ReflowCountMgr::ClearTotals() {
  11281  for (const auto& data : mCounts.Values()) {
  11282    data->ClearTotals();
  11283  }
  11284 }
  11285 
  11286 //------------------------------------------------------------------
  11287 void ReflowCountMgr::ClearGrandTotals() {
  11288  mCounts.WithEntryHandle(kGrandTotalsStr, [&](auto&& entry) {
  11289    if (!entry) {
  11290      entry.Insert(MakeUnique<ReflowCounter>(this));
  11291    } else {
  11292      entry.Data()->ClearTotals();
  11293      entry.Data()->SetTotalsCache();
  11294    }
  11295  });
  11296 }
  11297 
  11298 //------------------------------------------------------------------
  11299 void ReflowCountMgr::DisplayDiffsInTotals() {
  11300  if (mCycledOnce) {
  11301    printf("Differences\n");
  11302    for (int32_t i = 0; i < 78; i++) {
  11303      printf("-");
  11304    }
  11305    printf("\n");
  11306    ClearGrandTotals();
  11307  }
  11308 
  11309  for (const auto& entry : mCounts) {
  11310    if (mCycledOnce) {
  11311      entry.GetData()->CalcDiffInTotals();
  11312      entry.GetData()->DisplayDiffTotals(entry.GetKey());
  11313    }
  11314    entry.GetData()->SetTotalsCache();
  11315  }
  11316 
  11317  mCycledOnce = true;
  11318 }
  11319 
  11320 #endif  // MOZ_REFLOW_PERF
  11321 
  11322 nsIFrame* PresShell::GetAbsoluteContainingBlock(nsIFrame* aFrame) {
  11323  return FrameConstructor()->GetAbsoluteContainingBlock(
  11324      aFrame, nsCSSFrameConstructor::ABS_POS);
  11325 }
  11326 
  11327 nsIFrame* PresShell::GetAnchorPosAnchor(
  11328    const nsAtom* aName, const nsIFrame* aPositionedFrame) const {
  11329  MOZ_ASSERT(aName);
  11330  MOZ_ASSERT(!aName->IsEmpty());
  11331  MOZ_ASSERT(mLazyAnchorPosAnchorChanges.IsEmpty());
  11332  if (aName == nsGkAtoms::AnchorPosImplicitAnchor) {
  11333    return AnchorPositioningUtils::GetAnchorPosImplicitAnchor(aPositionedFrame);
  11334  }
  11335  if (const auto& entry = mAnchorPosAnchors.Lookup(aName)) {
  11336    return AnchorPositioningUtils::FindFirstAcceptableAnchor(
  11337        aName, aPositionedFrame, entry.Data());
  11338  }
  11339  return nullptr;
  11340 }
  11341 
  11342 void PresShell::AddAnchorPosAnchorImpl(const nsAtom* aName, nsIFrame* aFrame,
  11343                                       bool aForMerge) {
  11344  MOZ_ASSERT(aName);
  11345 
  11346  auto& entry = mAnchorPosAnchors.LookupOrInsertWith(
  11347      aName, []() { return nsTArray<nsIFrame*>(); });
  11348 
  11349  if (entry.IsEmpty()) {
  11350    entry.AppendElement(aFrame);
  11351    return;
  11352  }
  11353 
  11354  struct FrameTreeComparator {
  11355    nsIFrame* mFrame;
  11356 
  11357    int32_t operator()(nsIFrame* aOther) const {
  11358      return nsLayoutUtils::CompareTreePosition(mFrame, aOther, nullptr);
  11359    }
  11360  };
  11361 
  11362  FrameTreeComparator cmp{aFrame};
  11363 
  11364  size_t matchOrInsertionIdx = entry.Length();
  11365  // If the same element is already in the array,
  11366  // someone forgot to call RemoveAnchorPosAnchor.
  11367  if (BinarySearchIf(entry, 0, entry.Length(), cmp, &matchOrInsertionIdx)) {
  11368    if (entry.ElementAt(matchOrInsertionIdx) == aFrame) {
  11369      // nsLayoutUtils::CompareTreePosition() returns 0 when the frames are
  11370      // in different documents or child lists. This could indicate that
  11371      // the tree is being restructured and we can defer anchor insertion
  11372      // to a MergeAnchorPosAnchors call after the restructuring is complete.
  11373      MOZ_ASSERT_UNREACHABLE("Attempt to insert a frame twice was made");
  11374      return;
  11375    }
  11376    MOZ_ASSERT(!entry.Contains(aFrame));
  11377 
  11378    if (!aForMerge) {
  11379      // nsLayoutUtils::CompareTreePosition() returns 0 when the frames are
  11380      // in different documents or child lists. This could indicate that
  11381      // the tree is being restructured and we can defer anchor insertion
  11382      // to a MergeAnchorPosAnchors call after the restructuring is complete.
  11383      mLazyAnchorPosAnchorChanges.AppendElement(
  11384          AnchorPosAnchorChange{RefPtr<const nsAtom>(aName), aFrame});
  11385      return;
  11386    }
  11387  }
  11388 
  11389  MOZ_ASSERT(!entry.Contains(aFrame));
  11390  entry.InsertElementAt(matchOrInsertionIdx, aFrame);
  11391 }
  11392 
  11393 void PresShell::AddAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
  11394  AddAnchorPosAnchorImpl(aName, aFrame, /* aForMerge = */ false);
  11395 }
  11396 
  11397 void PresShell::RemoveAnchorPosAnchor(const nsAtom* aName, nsIFrame* aFrame) {
  11398  MOZ_ASSERT(aName);
  11399 
  11400  if (!mLazyAnchorPosAnchorChanges.IsEmpty()) {
  11401    mLazyAnchorPosAnchorChanges.RemoveElementsBy(
  11402        [&](const AnchorPosAnchorChange& change) {
  11403          return change.mFrame == aFrame;
  11404        });
  11405  }
  11406 
  11407  auto entry = mAnchorPosAnchors.Lookup(aName);
  11408  if (!entry) {
  11409    return;  // Nothing to remove.
  11410  }
  11411 
  11412 #ifdef ACCESSIBILITY
  11413  if (nsAccessibilityService* accService = GetAccService()) {
  11414    accService->NotifyAnchorRemoved(this, aFrame);
  11415  }
  11416 #endif
  11417 
  11418  auto& anchorArray = entry.Data();
  11419 
  11420  // XXX: Once the implementation is more complete,
  11421  // we should probably assert here that anchorArray
  11422  // is not empty and aFrame is in it.
  11423 
  11424  anchorArray.RemoveElement(aFrame);
  11425  if (anchorArray.IsEmpty()) {
  11426    entry.Remove();
  11427  }
  11428 }
  11429 
  11430 void PresShell::MergeAnchorPosAnchorChanges() {
  11431  for (const auto& [name, frame] : mLazyAnchorPosAnchorChanges) {
  11432    AddAnchorPosAnchorImpl(name, frame, /* aForMerge = */ true);
  11433  }
  11434 
  11435  mLazyAnchorPosAnchorChanges.Clear();
  11436 }
  11437 
  11438 static bool NeedReflowForAnchorPos(
  11439    const nsIFrame* aAnchor, const nsIFrame* aPositioned,
  11440    const Maybe<AnchorPosResolutionData>& aData) {
  11441  const bool validityChanged = (aAnchor && !aData) || (!aAnchor && aData);
  11442  if (validityChanged) {
  11443    return true;
  11444  }
  11445  if (!aData) {
  11446    // Was invalid, still invalid. No more consideration needed.
  11447    return false;
  11448  }
  11449  // Was valid, still valid Did the referenced value change?
  11450  if (!aAnchor) {
  11451    MOZ_ASSERT_UNREACHABLE("Anchor is supposed to be valid");
  11452    return false;
  11453  }
  11454  const auto& anchorReference = aData.ref();
  11455  const auto anchorSize = aAnchor->GetSize();
  11456  if (anchorReference.mSize != anchorSize) {
  11457    // Size changed, needs reflow.
  11458    return true;
  11459  }
  11460  if (!anchorReference.mOffsetData) {
  11461    // Didn't resolve offsets, no need to reflow based on it.
  11462    return false;
  11463  }
  11464 
  11465  const auto nearestScrollFrameInfo =
  11466      AnchorPositioningUtils::GetNearestScrollFrame(aAnchor);
  11467  if (anchorReference.mOffsetData->mDistanceToNearestScrollContainer !=
  11468      nearestScrollFrameInfo.mDistance) {
  11469    // Scroll container relationship changed, need to reflow.
  11470    return true;
  11471  }
  11472 
  11473  const auto posInfo = AnchorPositioningUtils::GetAnchorPosRect(
  11474      aPositioned->GetParent(), aAnchor, true);
  11475  MOZ_ASSERT(posInfo, "Can't resolve anchor rect?");
  11476  const auto newOrigin = posInfo.ref().TopLeft();
  11477  const auto& prevOrigin = anchorReference.mOffsetData.ref().mOrigin;
  11478  // Did the offset change?
  11479  return newOrigin != prevOrigin;
  11480 }
  11481 
  11482 struct DefaultAnchorInfo {
  11483  const nsAtom* mName;
  11484  const nsIFrame* mAnchor;
  11485  DistanceToNearestScrollContainer mDistanceToNearestScrollContainer;
  11486 };
  11487 
  11488 PresShell::AnchorPosUpdateResult PresShell::UpdateAnchorPosLayout() {
  11489  if (mAnchorPosPositioned.IsEmpty()) {
  11490    return AnchorPosUpdateResult::NotApplicable;
  11491  }
  11492 
  11493  // Flush the layout, so that positioned frames' anchor references are valid.
  11494  DoFlushLayout(/* aInterruptible = */ false);
  11495 
  11496  auto result = AnchorPosUpdateResult::Flushed;
  11497  AUTO_PROFILER_MARKER_UNTYPED("UpdateAnchorPosLayout", LAYOUT, {});
  11498  for (auto* positioned : mAnchorPosPositioned) {
  11499    MOZ_ASSERT(positioned->IsAbsolutelyPositioned(),
  11500               "Anchor positioned frame is not absolutely positioned?");
  11501    const auto* anchorPosReferenceData =
  11502        positioned->GetProperty(nsIFrame::AnchorPosReferences());
  11503    // Note that it's possible (Though unlikely) to register as anchor
  11504    // positioned but not actually make any anchor resolution - e.g.
  11505    // `position-anchor` is set, but no other anchor positioning property is
  11506    // used.
  11507    if (!anchorPosReferenceData || anchorPosReferenceData->IsEmpty()) {
  11508      continue;
  11509    }
  11510    if (positioned->HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
  11511      // Already marked for reflow.
  11512      continue;
  11513    }
  11514    const auto defaultAnchorInfo = [&]() -> Maybe<DefaultAnchorInfo> {
  11515      const auto* anchorName =
  11516          AnchorPositioningUtils::GetUsedAnchorName(positioned, nullptr);
  11517      if (!anchorName) {
  11518        return Nothing{};
  11519      }
  11520      const auto* anchor = GetAnchorPosAnchor(anchorName, positioned);
  11521      if (!anchor) {
  11522        return Nothing{};
  11523      }
  11524      const auto nearestScrollFrame =
  11525          AnchorPositioningUtils::GetNearestScrollFrame(anchor);
  11526      return Some(
  11527          DefaultAnchorInfo{anchorName, anchor, nearestScrollFrame.mDistance});
  11528    }();
  11529    bool shouldReflow = false;
  11530    if (defaultAnchorInfo &&
  11531        defaultAnchorInfo->mDistanceToNearestScrollContainer !=
  11532            anchorPosReferenceData->mDistanceToDefaultScrollContainer) {
  11533      // Default anchor's nearest scroller changed, reflow.
  11534      shouldReflow = true;
  11535    } else {
  11536      const auto GetAnchor =
  11537          [&](const nsAtom* aName,
  11538              const nsIFrame* aPositioned) -> const nsIFrame* {
  11539        if (!defaultAnchorInfo) {
  11540          return GetAnchorPosAnchor(aName, aPositioned);
  11541        }
  11542        const auto* defaultAnchorName = defaultAnchorInfo->mName;
  11543        if (aName != defaultAnchorName) {
  11544          return GetAnchorPosAnchor(aName, aPositioned);
  11545        }
  11546        return defaultAnchorInfo->mAnchor;
  11547      };
  11548      for (const auto& kv : *anchorPosReferenceData) {
  11549        const auto& data = kv.GetData();
  11550        const auto& anchorName = kv.GetKey();
  11551        const auto* anchor = GetAnchor(anchorName, positioned);
  11552        if (NeedReflowForAnchorPos(anchor, positioned, data)) {
  11553          shouldReflow = true;
  11554          break;
  11555        }
  11556      }
  11557    }
  11558    if (shouldReflow) {
  11559      result = AnchorPosUpdateResult::NeedReflow;
  11560      MarkPositionedFrameForReflow(positioned);
  11561    }
  11562  }
  11563  return result;
  11564 }
  11565 
  11566 using AffectedAnchor = AnchorPosDefaultAnchorCache;
  11567 struct AffectedAnchorGroup {
  11568  const nsAtom* mAnchorName;
  11569  nsTArray<AffectedAnchor> mFrames;
  11570 };
  11571 
  11572 static const nsIFrame* NearestScrollContainerOfAffectedAnchor(
  11573    const nsIFrame* aAnchor, const ScrollContainerFrame* aScrollContainer) {
  11574  const auto* scrollContainer =
  11575      AnchorPositioningUtils::GetNearestScrollFrame(aAnchor).mScrollContainer;
  11576  if (!scrollContainer) {
  11577    // Fixed-pos anchor, likely
  11578    return nullptr;
  11579  }
  11580  // Does this scroll container match a anchor's nearest scroll container,
  11581  // or contain it?
  11582  if (scrollContainer == aScrollContainer ||
  11583      nsLayoutUtils::IsProperAncestorFrame(aScrollContainer, scrollContainer)) {
  11584    return scrollContainer;
  11585  }
  11586  return nullptr;
  11587 }
  11588 
  11589 static nsTArray<AffectedAnchorGroup> FindAnchorsAffectedByScroll(
  11590    const nsTHashMap<RefPtr<const nsAtom>, nsTArray<nsIFrame*>>& aAnchors,
  11591    const ScrollContainerFrame* aScrollContainer) {
  11592  nsTArray<AffectedAnchorGroup> affectedAnchors;
  11593  // We keep only referenced anchors' name in positioned frames to avoid dealing
  11594  // with lifetime issues associated with it. Now we need to re-establish that
  11595  // association.
  11596  for (const auto& kv : aAnchors) {
  11597    const auto& anchorFrames = kv.GetData();
  11598    Maybe<nsTArray<AffectedAnchor>> affected;
  11599    for (const auto& frame : anchorFrames) {
  11600      const auto* scrollContainer =
  11601          NearestScrollContainerOfAffectedAnchor(frame, aScrollContainer);
  11602      if (!scrollContainer) {
  11603        continue;
  11604      }
  11605      if (affected.isNothing()) {
  11606        affected = Some(nsTArray<AffectedAnchor>{anchorFrames.Length()});
  11607      }
  11608      affected.ref().AppendElement(AffectedAnchor{frame, scrollContainer});
  11609    }
  11610    if (affected.isSome()) {
  11611      affectedAnchors.AppendElement(
  11612          AffectedAnchorGroup{kv.GetKey(), std::move(*affected)});
  11613    }
  11614  }
  11615  return affectedAnchors;
  11616 }
  11617 
  11618 // Given a list of anchors affected by scrolling, find one that the given
  11619 // positioned frame need to compensate scroll for.
  11620 static Maybe<AffectedAnchor> FindScrollCompensatedAnchor(
  11621    const PresShell* aPresShell,
  11622    const ScrollContainerFrame* aScrolledScrollContainer,
  11623    const nsTArray<AffectedAnchorGroup>& aAffectedAnchors,
  11624    const nsIFrame* aPositioned, const AnchorPosReferenceData& aReferenceData,
  11625    const nsIFrame** aResolvedDefaultAnchor) {
  11626  MOZ_ASSERT(aPositioned->IsAbsolutelyPositioned(),
  11627             "Anchor positioned frame is not absolutely positioned?");
  11628  if (aResolvedDefaultAnchor) {
  11629    *aResolvedDefaultAnchor = nullptr;
  11630  }
  11631 
  11632  if (aReferenceData.IsEmpty()) {
  11633    return Nothing{};
  11634  }
  11635 
  11636  const auto* defaultAnchorName = aReferenceData.mDefaultAnchorName.get();
  11637  if (!defaultAnchorName) {
  11638    return Nothing{};
  11639  }
  11640 
  11641  const auto* defaultAnchor =
  11642      aPresShell->GetAnchorPosAnchor(defaultAnchorName, aPositioned);
  11643  if (!defaultAnchor) {
  11644    return Nothing{};
  11645  }
  11646  if (aResolvedDefaultAnchor) {
  11647    *aResolvedDefaultAnchor = defaultAnchor;
  11648  }
  11649 
  11650  const auto compensatingForScroll = aReferenceData.CompensatingForScrollAxes();
  11651  if (compensatingForScroll.isEmpty()) {
  11652    return Nothing{};
  11653  }
  11654 
  11655  if (defaultAnchorName == nsGkAtoms::AnchorPosImplicitAnchor) {
  11656    // We're not going to find this in `aAffectedAnchors`, which works off of
  11657    // `PresShell::mAnchorPosAnchors`, which doesn't store implicit anchors.
  11658    const auto* anchor =
  11659        AnchorPositioningUtils::GetAnchorPosImplicitAnchor(aPositioned);
  11660    if (!anchor) {
  11661      return Nothing{};
  11662    }
  11663    const auto* scrollContainer = NearestScrollContainerOfAffectedAnchor(
  11664        anchor, aScrolledScrollContainer);
  11665    if (!scrollContainer) {
  11666      return Nothing{};
  11667    }
  11668    return Some(AffectedAnchor{anchor, scrollContainer});
  11669  }
  11670 
  11671  struct Comparator {
  11672    bool Equals(const AffectedAnchor& aEntry, const nsIFrame* aFrame) const {
  11673      return aEntry.mAnchor == aFrame;
  11674    }
  11675  };
  11676 
  11677  // Find the relevant default anchor.
  11678  for (const auto& group : aAffectedAnchors) {
  11679    if (group.mAnchorName != defaultAnchorName) {
  11680      // Default anchor has a name, and it's different from this affected
  11681      // anchor group.
  11682      continue;
  11683    }
  11684    const auto& anchors = group.mFrames;
  11685    // Find the affected anchor that not only matches in name, but in actual
  11686    // frame.
  11687    const auto idx = anchors.IndexOf(defaultAnchor, 0, Comparator{});
  11688    if (idx == anchors.NoIndex) {
  11689      // We found the default anchor, but it wasn't correct.
  11690      break;
  11691    }
  11692    const auto& info = anchors.ElementAt(idx);
  11693    return Some(info);
  11694  }
  11695 
  11696  return Nothing{};
  11697 }
  11698 
  11699 static bool CheckOverflow(nsIFrame* aPositioned,
  11700                          const AnchorPosReferenceData& aData) {
  11701  const auto* stylePos = aPositioned->StylePosition();
  11702  const auto hasFallbacks = !stylePos->mPositionTryFallbacks._0.IsEmpty();
  11703  if (!hasFallbacks) {
  11704    return false;
  11705  }
  11706  return !AnchorPositioningUtils::FitsInContainingBlock(aPositioned, aData);
  11707 }
  11708 
  11709 // HACK(dshin, Bug 1999954): This is a workaround. While we try to lay out
  11710 // against the scroll-ignored position of an anchor, sticky and chain anchor
  11711 // positioned frames actually end up containing scroll offset in their position.
  11712 // Additionally, scroll offset collection does not do any special handling for
  11713 // such frames (Which is impossible unless we can cleanly separate the
  11714 // scroll-ignored position).
  11715 // For now, we detect such frames and just trigger a reflow.
  11716 // Bug 2002789 tracks the proper fix.
  11717 static bool AnchorIsStickyOrChainedToScrollCompensatedAnchor(
  11718    const nsIFrame* aAnchor) {
  11719  if (!aAnchor) {
  11720    return false;
  11721  }
  11722 
  11723  if (aAnchor->IsStickyPositioned()) {
  11724    return true;
  11725  }
  11726 
  11727  if (aAnchor->StylePosition()->mPositionAnchor.IsNone()) {
  11728    // Not anchored, or anchored but not scroll compensated.
  11729    return false;
  11730  }
  11731 
  11732  const auto* referenceData =
  11733      aAnchor->GetProperty(nsIFrame::AnchorPosReferences());
  11734  if (!referenceData) {
  11735    return false;
  11736  }
  11737 
  11738  // Theoretically, we should look at the entire anchor chain to see if this
  11739  // anchor will be affected by scroll compensation. However, that does not seem
  11740  // worth it - A long anchor chain seems like an edge case anyway.
  11741  return !referenceData->CompensatingForScrollAxes().isEmpty();
  11742 }
  11743 
  11744 // https://drafts.csswg.org/css-anchor-position-1/#default-scroll-shift
  11745 void PresShell::UpdateAnchorPosForScroll(
  11746    const ScrollContainerFrame* aScrollContainer) {
  11747  if (mAnchorPosAnchors.IsEmpty() && mAnchorPosPositioned.IsEmpty()) {
  11748    return;
  11749  }
  11750 
  11751  AUTO_PROFILER_MARKER_UNTYPED("UpdateAnchorPosForScroll", LAYOUT, {});
  11752 
  11753  // First, find all anchors under this scroll container. Can look at positioned
  11754  // frames' anchor references first, but we want to avoid anchor lookups if we
  11755  // can.
  11756  nsTArray<AffectedAnchorGroup> affectedAnchors =
  11757      FindAnchorsAffectedByScroll(mAnchorPosAnchors, aScrollContainer);
  11758  // Affected anchors may be empty, an implicit anchor may have scrolled.
  11759 
  11760  // Now, update all affected positioned elements' scroll offsets.
  11761  for (auto* positioned : mAnchorPosPositioned) {
  11762    auto* referenceData =
  11763        positioned->GetProperty(nsIFrame::AnchorPosReferences());
  11764    if (!referenceData) {
  11765      continue;
  11766    }
  11767    const nsIFrame* defaultAnchor = nullptr;
  11768    const auto scrollDependency =
  11769        FindScrollCompensatedAnchor(this, aScrollContainer, affectedAnchors,
  11770                                    positioned, *referenceData, &defaultAnchor);
  11771    const bool offsetChanged = [&]() {
  11772      if (!scrollDependency) {
  11773        return false;
  11774      }
  11775      const auto offset = AnchorPositioningUtils::GetScrollOffsetFor(
  11776          referenceData->CompensatingForScrollAxes(), positioned,
  11777          *scrollDependency);
  11778      if (referenceData->mDefaultScrollShift == offset) {
  11779        return false;
  11780      }
  11781      positioned->SetPosition(positioned->GetNormalPosition() - offset);
  11782      // Update positioned frame's overflow, then the absolute containing
  11783      // block's.
  11784      positioned->UpdateOverflow();
  11785      positioned->GetParent()->UpdateOverflow();
  11786      // APZ-handled scrolling may skip scheduling of paint for the relevant
  11787      // scroll container - We need to ensure that we schedule a paint for this
  11788      // positioned frame. Could theoretically do this when deciding to skip
  11789      // painting in `ScrollContainerFrame::ScrollToImpl`, that'd be conditional
  11790      // on finding a dependent anchor anyway, we should be as specific as
  11791      // possible as to what gets scheduled to paint.
  11792      positioned->SchedulePaint();
  11793      referenceData->mDefaultScrollShift = offset;
  11794      return true;
  11795    }();
  11796    const bool cbScrolls =
  11797        positioned->GetParent() == aScrollContainer->GetScrolledFrame();
  11798    // HACK(dshin): Check if this positioned frame is anchoring to a sticky or
  11799    // another anchor positioned frame, even if we may not be scroll
  11800    // compensating against it. Such frames, even when in the same scroll
  11801    // container, as the positioned element, don't (always) scroll with the
  11802    // scroll container. Also see comment for
  11803    // `AnchorIsStickyOrChainedToScrollCompensatedAnchor`.
  11804    const bool anchorIsStickyOrScrollCompensatedAnchor =
  11805        defaultAnchor &&
  11806        AnchorIsStickyOrChainedToScrollCompensatedAnchor(defaultAnchor);
  11807    if (offsetChanged || cbScrolls || anchorIsStickyOrScrollCompensatedAnchor) {
  11808      if (CheckOverflow(positioned, *referenceData) ||
  11809          anchorIsStickyOrScrollCompensatedAnchor) {
  11810 #ifdef ACCESSIBILITY
  11811        if (nsAccessibilityService* accService = GetAccService()) {
  11812          accService->NotifyAnchorPositionedScrollUpdate(this, positioned);
  11813        }
  11814 #endif
  11815        MarkPositionedFrameForReflow(positioned);
  11816      }
  11817    }
  11818  }
  11819 }
  11820 
  11821 void PresShell::ActivenessMaybeChanged() {
  11822  if (!mDocument) {
  11823    return;
  11824  }
  11825  SetIsActive(ComputeActiveness());
  11826 }
  11827 
  11828 // A PresShell being active means that it is visible (or close to be visible, if
  11829 // the front-end is warming it). That means that when it is active we always
  11830 // tick its refresh driver at full speed if needed.
  11831 //
  11832 // Image documents behave specially in the sense that they are always "active"
  11833 // and never "in the active tab". However these documents tick manually so
  11834 // there's not much to worry about there.
  11835 bool PresShell::ComputeActiveness() const {
  11836  MOZ_LOG(gLog, LogLevel::Debug,
  11837          ("PresShell::ComputeActiveness(%s, %d)\n",
  11838           mDocument->GetDocumentURI()
  11839               ? mDocument->GetDocumentURI()->GetSpecOrDefault().get()
  11840               : "(no uri)",
  11841           mIsActive));
  11842 
  11843  Document* doc = mDocument;
  11844 
  11845  if (doc->IsBeingUsedAsImage()) {
  11846    // Documents used as an image can remain active. They do not tick their
  11847    // refresh driver if not painted, and they can't run script or such so they
  11848    // can't really observe much else.
  11849    //
  11850    // Image docs can be displayed in multiple docs at the same time so the "in
  11851    // active tab" bool doesn't make much sense for them.
  11852    return true;
  11853  }
  11854 
  11855  if (Document* displayDoc = doc->GetDisplayDocument()) {
  11856    // Ok, we're an external resource document -- we need to use our display
  11857    // document's docshell to determine "IsActive" status, since we lack
  11858    // a browsing context of our own.
  11859    MOZ_ASSERT(!doc->GetBrowsingContext(),
  11860               "external resource doc shouldn't have its own BC");
  11861    doc = displayDoc;
  11862  }
  11863 
  11864  BrowsingContext* bc = doc->GetBrowsingContext();
  11865  const bool inActiveTab = bc && bc->IsActive();
  11866 
  11867  MOZ_LOG(gLog, LogLevel::Debug,
  11868          (" > BrowsingContext %p  active: %d", bc, inActiveTab));
  11869 
  11870  if (StaticPrefs::layout_testing_top_level_always_active() && bc &&
  11871      bc->IsTop()) {
  11872    MOZ_LOG(gLog, LogLevel::Debug, (" > Activeness overridden by pref"));
  11873    return true;
  11874  }
  11875 
  11876  Document* root = nsContentUtils::GetInProcessSubtreeRootDocument(doc);
  11877  if (auto* browserChild = BrowserChild::GetFrom(root->GetDocShell())) {
  11878    // We might want to activate a tab even though the browsing-context is not
  11879    // active if the BrowserChild is considered visible. This serves two
  11880    // purposes:
  11881    //
  11882    //  * For top-level tabs, we use this for tab warming. The browsing-context
  11883    //    might still be inactive, but we want to activate the pres shell and
  11884    //    the refresh driver.
  11885    //
  11886    //  * For oop iframes, we do want to throttle them if they're not visible.
  11887    //
  11888    // TODO(emilio): Consider unifying the in-process vs. fission iframe
  11889    // throttling code (in-process throttling for non-visible iframes lives
  11890    // right now in Document::ShouldThrottleFrameRequests(), but that only
  11891    // throttles rAF).
  11892    if (!browserChild->IsVisible()) {
  11893      MOZ_LOG(gLog, LogLevel::Debug,
  11894              (" > BrowserChild %p is not visible", browserChild));
  11895      return false;
  11896    }
  11897 
  11898    // If the browser is visible but just due to be preserving layers
  11899    // artificially, we do want to fall back to the browsing context activeness
  11900    // instead. Otherwise we do want to be active for the use cases above.
  11901    if (!browserChild->IsPreservingLayers()) {
  11902      MOZ_LOG(gLog, LogLevel::Debug,
  11903              (" > BrowserChild %p is visible and not preserving layers",
  11904               browserChild));
  11905      return true;
  11906    }
  11907    MOZ_LOG(
  11908        gLog, LogLevel::Debug,
  11909        (" > BrowserChild %p is visible and preserving layers", browserChild));
  11910  }
  11911  return inActiveTab;
  11912 }
  11913 
  11914 void PresShell::SetIsActive(bool aIsActive) {
  11915  MOZ_ASSERT(mDocument, "should only be called with a document");
  11916 
  11917  const bool activityChanged = mIsActive != aIsActive;
  11918 
  11919  mIsActive = aIsActive;
  11920 
  11921  nsPresContext* presContext = GetPresContext();
  11922  if (presContext &&
  11923      presContext->RefreshDriver()->GetPresContext() == presContext) {
  11924    presContext->RefreshDriver()->SetActivity(aIsActive);
  11925  }
  11926 
  11927  if (activityChanged) {
  11928    // Propagate state-change to my resource documents' PresShells and other
  11929    // subdocuments.
  11930    //
  11931    // Note that it is fine to not propagate to fission iframes. Those will
  11932    // become active / inactive as needed as a result of they getting painted /
  11933    // not painted eventually.
  11934    auto recurse = [aIsActive](Document& aSubDoc) {
  11935      if (PresShell* presShell = aSubDoc.GetPresShell()) {
  11936        presShell->SetIsActive(aIsActive);
  11937      }
  11938      return CallState::Continue;
  11939    };
  11940    mDocument->EnumerateExternalResources(recurse);
  11941    mDocument->EnumerateSubDocuments(recurse);
  11942  }
  11943 
  11944  UpdateImageLockingState();
  11945 
  11946  if (activityChanged) {
  11947 #if defined(MOZ_WIDGET_ANDROID)
  11948    if (!aIsActive && presContext &&
  11949        presContext->IsRootContentDocumentCrossProcess()) {
  11950      // Reset the dynamic toolbar offset state.
  11951      presContext->UpdateDynamicToolbarOffset(0);
  11952    }
  11953 #endif
  11954  }
  11955 
  11956  if (aIsActive) {
  11957 #ifdef ACCESSIBILITY
  11958    if (nsAccessibilityService* accService = GetAccService()) {
  11959      accService->PresShellActivated(this);
  11960    }
  11961 #endif
  11962    if (nsIFrame* rootFrame = GetRootFrame()) {
  11963      rootFrame->SchedulePaint();
  11964    }
  11965  }
  11966 }
  11967 
  11968 MobileViewportManager* PresShell::GetMobileViewportManager() const {
  11969  return mMobileViewportManager;
  11970 }
  11971 
  11972 Maybe<MobileViewportManager::ManagerType> UseMobileViewportManager(
  11973    PresShell* aPresShell, Document* aDocument) {
  11974  // If we're not using APZ, we won't be able to zoom, so there is no
  11975  // point in having an MVM.
  11976  if (nsIWidget* widget = aPresShell->GetNearestWidget()) {
  11977    if (!widget->AsyncPanZoomEnabled()) {
  11978      return Nothing();
  11979    }
  11980  }
  11981  if (nsLayoutUtils::ShouldHandleMetaViewport(aDocument)) {
  11982    return Some(MobileViewportManager::ManagerType::VisualAndMetaViewport);
  11983  }
  11984  if (nsLayoutUtils::AllowZoomingForDocument(aDocument)) {
  11985    return Some(MobileViewportManager::ManagerType::VisualViewportOnly);
  11986  }
  11987  return Nothing();
  11988 }
  11989 
  11990 void PresShell::MaybeRecreateMobileViewportManager(bool aAfterInitialization) {
  11991  // Determine if we require a MobileViewportManager, and what kind if so. We
  11992  // need one any time we allow resolution zooming for a document, and any time
  11993  // we want to obey <meta name="viewport"> tags for it.
  11994  Maybe<MobileViewportManager::ManagerType> mvmType =
  11995      UseMobileViewportManager(this, mDocument);
  11996 
  11997  if (mvmType.isNothing() && !mMobileViewportManager) {
  11998    // We don't need one and don't have it. So we're done.
  11999    return;
  12000  }
  12001  if (mvmType && mMobileViewportManager &&
  12002      *mvmType == mMobileViewportManager->GetManagerType()) {
  12003    // We need one and we have one of the correct type, so we're done.
  12004    return;
  12005  }
  12006 
  12007  if (!mPresContext->IsRootContentDocumentCrossProcess()) {
  12008    MOZ_ASSERT(!mMobileViewportManager, "We never create MVMs for subframes");
  12009    return;
  12010  }
  12011 
  12012  if (mMobileViewportManager) {
  12013    // We have one, but we need to either destroy it completely to replace it
  12014    // with another one of the correct type. So either way, let's destroy the
  12015    // one we have.
  12016    mMobileViewportManager->Destroy();
  12017    mMobileViewportManager = nullptr;
  12018    mMVMContext = nullptr;
  12019 
  12020    ResetVisualViewportSize();
  12021  }
  12022 
  12023  if (mvmType) {
  12024    // Let's create the MVM of the type that we need. At this point we shouldn't
  12025    // have one.
  12026    MOZ_ASSERT(!mMobileViewportManager);
  12027 
  12028    mMVMContext = new GeckoMVMContext(mDocument, this);
  12029    mMobileViewportManager = new MobileViewportManager(mMVMContext, *mvmType);
  12030    if (MOZ_UNLIKELY(
  12031            MOZ_LOG_TEST(MobileViewportManager::gLog, LogLevel::Debug))) {
  12032      nsIURI* uri = mDocument->GetDocumentURI();
  12033      MOZ_LOG(
  12034          MobileViewportManager::gLog, LogLevel::Debug,
  12035          ("Created MVM %p (type %d) for URI %s", mMobileViewportManager.get(),
  12036           (int)*mvmType, uri ? uri->GetSpecOrDefault().get() : "(null)"));
  12037    }
  12038    if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
  12039      mMobileViewportManager->UpdateKeyboardHeight(
  12040          browserChild->GetKeyboardHeight());
  12041    }
  12042  }
  12043 
  12044  if (aAfterInitialization) {
  12045    // Setting the initial viewport will trigger a reflow.
  12046    if (mMobileViewportManager) {
  12047      mMobileViewportManager->SetInitialViewport();
  12048    } else {
  12049      // Force a reflow to our correct layout viewport size.
  12050      ForceResizeReflowWithCurrentDimensions();
  12051    }
  12052    // After we clear out the MVM and the MVMContext, also reset the
  12053    // resolution to 1.
  12054    SetResolutionAndScaleTo(1.0f, ResolutionChangeOrigin::MainThreadRestore);
  12055  }
  12056 }
  12057 
  12058 bool PresShell::UsesMobileViewportSizing() const {
  12059  return mMobileViewportManager &&
  12060         nsLayoutUtils::ShouldHandleMetaViewport(mDocument);
  12061 }
  12062 
  12063 /*
  12064 * Determines the current image locking state. Called when one of the
  12065 * dependent factors changes.
  12066 */
  12067 void PresShell::UpdateImageLockingState() {
  12068  // We're locked if we're both thawed and active.
  12069  const bool locked = !mFrozen && mIsActive;
  12070  if (locked == mDocument->GetLockingImages()) {
  12071    return;
  12072  }
  12073  mDocument->SetLockingImages(locked);
  12074  if (locked) {
  12075    // Request decodes for visible image frames; we want to start decoding as
  12076    // quickly as possible when we get foregrounded to minimize flashing.
  12077    for (const auto& key : mApproximatelyVisibleFrames) {
  12078      if (nsImageFrame* imageFrame = do_QueryFrame(key)) {
  12079        imageFrame->MaybeDecodeForPredictedSize();
  12080      }
  12081    }
  12082  }
  12083 }
  12084 
  12085 nsIWidget* PresShell::GetRootWidget() const {
  12086  if (!mPresContext) {
  12087    return nullptr;
  12088  }
  12089  for (nsPresContext* pc = mPresContext; pc; pc = pc->GetParentPresContext()) {
  12090    if (auto* widget = pc->PresShell()->GetOwnWidget()) {
  12091      return widget;
  12092    }
  12093  }
  12094  return nullptr;
  12095 }
  12096 
  12097 PresShell* PresShell::GetRootPresShell() const {
  12098  if (mPresContext) {
  12099    nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
  12100    if (rootPresContext) {
  12101      return rootPresContext->PresShell();
  12102    }
  12103  }
  12104  return nullptr;
  12105 }
  12106 
  12107 void PresShell::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const {
  12108  MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
  12109  mFrameArena.AddSizeOfExcludingThis(aSizes, Arena::ArenaKind::PresShell);
  12110  aSizes.mLayoutPresShellSize += mallocSizeOf(this);
  12111  if (mCaret) {
  12112    aSizes.mLayoutPresShellSize += mCaret->SizeOfIncludingThis(mallocSizeOf);
  12113  }
  12114  aSizes.mLayoutPresShellSize +=
  12115      mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(mallocSizeOf) +
  12116      mFramesToDirty.ShallowSizeOfExcludingThis(mallocSizeOf) +
  12117      mPendingScrollAnchorSelection.ShallowSizeOfExcludingThis(mallocSizeOf) +
  12118      mPendingScrollAnchorAdjustment.ShallowSizeOfExcludingThis(mallocSizeOf);
  12119 
  12120  aSizes.mLayoutTextRunsSize += SizeOfTextRuns(mallocSizeOf);
  12121 
  12122  aSizes.mLayoutPresContextSize +=
  12123      mPresContext->SizeOfIncludingThis(mallocSizeOf);
  12124 
  12125  mFrameConstructor->AddSizeOfIncludingThis(aSizes);
  12126 }
  12127 
  12128 size_t PresShell::SizeOfTextRuns(MallocSizeOf aMallocSizeOf) const {
  12129  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  12130  if (!rootFrame) {
  12131    return 0;
  12132  }
  12133 
  12134  // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
  12135  nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, nullptr,
  12136                                         /* clear = */ true);
  12137 
  12138  // collect the total memory in use for textruns
  12139  return nsLayoutUtils::SizeOfTextRunsForFrames(rootFrame, aMallocSizeOf,
  12140                                                /* clear = */ false);
  12141 }
  12142 
  12143 void PresShell::MarkPositionedFrameForReflow(nsIFrame* aFrame) {
  12144  // Abspos frames don't affect intrinsic sizes of ancestors.
  12145  FrameNeedsReflow(aFrame, IntrinsicDirty::None, NS_FRAME_HAS_DIRTY_CHILDREN,
  12146                   ReflowRootHandling::PositionOrSizeChange);
  12147 }
  12148 
  12149 void PresShell::MarkFixedFramesForReflow() {
  12150  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
  12151  if (rootFrame) {
  12152    const nsFrameList& childList =
  12153        rootFrame->GetChildList(FrameChildListID::Fixed);
  12154    for (nsIFrame* childFrame : childList) {
  12155      MarkPositionedFrameForReflow(childFrame);
  12156    }
  12157  }
  12158 }
  12159 
  12160 void PresShell::MarkStickyFramesForReflow() {
  12161  ScrollContainerFrame* sc = GetRootScrollContainerFrame();
  12162  if (!sc) {
  12163    return;
  12164  }
  12165 
  12166  StickyScrollContainer* ssc = sc->GetStickyContainer();
  12167  if (!ssc) {
  12168    return;
  12169  }
  12170 
  12171  ssc->MarkFramesForReflow();
  12172 }
  12173 
  12174 static void AppendSubtree(nsIDocShell* aDocShell,
  12175                          nsTArray<nsCOMPtr<nsIDocumentViewer>>& aArray) {
  12176  if (nsCOMPtr<nsIDocumentViewer> viewer = aDocShell->GetDocViewer()) {
  12177    aArray.AppendElement(viewer);
  12178  }
  12179 
  12180  int32_t n = aDocShell->GetInProcessChildCount();
  12181  for (int32_t i = 0; i < n; i++) {
  12182    nsCOMPtr<nsIDocShellTreeItem> childItem;
  12183    aDocShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
  12184    if (childItem) {
  12185      nsCOMPtr<nsIDocShell> child(do_QueryInterface(childItem));
  12186      AppendSubtree(child, aArray);
  12187    }
  12188  }
  12189 }
  12190 
  12191 void PresShell::MaybeReflowForInflationScreenSizeChange() {
  12192  nsPresContext* pc = GetPresContext();
  12193  const bool fontInflationWasEnabled = FontSizeInflationEnabled();
  12194  RecomputeFontSizeInflationEnabled();
  12195  bool changed = false;
  12196  if (FontSizeInflationEnabled() && FontSizeInflationMinTwips() != 0) {
  12197    pc->ScreenSizeInchesForFontInflation(&changed);
  12198  }
  12199 
  12200  changed = changed || fontInflationWasEnabled != FontSizeInflationEnabled();
  12201  if (!changed) {
  12202    return;
  12203  }
  12204  if (nsCOMPtr<nsIDocShell> docShell = pc->GetDocShell()) {
  12205    nsTArray<nsCOMPtr<nsIDocumentViewer>> array;
  12206    AppendSubtree(docShell, array);
  12207    for (uint32_t i = 0, iEnd = array.Length(); i < iEnd; ++i) {
  12208      nsCOMPtr<nsIDocumentViewer> viewer = array[i];
  12209      if (RefPtr<PresShell> descendantPresShell = viewer->GetPresShell()) {
  12210        nsIFrame* rootFrame = descendantPresShell->GetRootFrame();
  12211        if (rootFrame) {
  12212          descendantPresShell->FrameNeedsReflow(
  12213              rootFrame, IntrinsicDirty::FrameAncestorsAndDescendants,
  12214              NS_FRAME_IS_DIRTY);
  12215        }
  12216      }
  12217    }
  12218  }
  12219 }
  12220 
  12221 void PresShell::CompleteChangeToVisualViewportSize() {
  12222  // This can get called during reflow, if the caller wants to get the latest
  12223  // visual viewport size after scrollbars have been added/removed. In such a
  12224  // case, we don't need to mark things as dirty because the things that we
  12225  // would mark dirty either just got updated (the root scrollframe's
  12226  // scrollbars), or will be laid out later during this reflow cycle (fixed-pos
  12227  // items). Callers that update the visual viewport during a reflow are
  12228  // responsible for maintaining these invariants.
  12229  if (!mIsReflowing) {
  12230    if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
  12231      sf->MarkScrollbarsDirtyForReflow();
  12232    }
  12233    MarkFixedFramesForReflow();
  12234  }
  12235 
  12236  MaybeReflowForInflationScreenSizeChange();
  12237 
  12238  if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
  12239    window->VisualViewport()->PostResizeEvent();
  12240  }
  12241 }
  12242 
  12243 void PresShell::SetVisualViewportSize(nscoord aWidth, nscoord aHeight) {
  12244  MOZ_ASSERT(aWidth >= 0.0 && aHeight >= 0.0);
  12245 
  12246  if (!mVisualViewportSizeSet || mVisualViewportSize.width != aWidth ||
  12247      mVisualViewportSize.height != aHeight) {
  12248    mVisualViewportSizeSet = true;
  12249    mVisualViewportSize.width = aWidth;
  12250    mVisualViewportSize.height = aHeight;
  12251 
  12252    CompleteChangeToVisualViewportSize();
  12253  }
  12254 }
  12255 
  12256 void PresShell::ResetVisualViewportSize() {
  12257  if (mVisualViewportSizeSet) {
  12258    mVisualViewportSizeSet = false;
  12259    mVisualViewportSize.width = 0;
  12260    mVisualViewportSize.height = 0;
  12261 
  12262    CompleteChangeToVisualViewportSize();
  12263  }
  12264 }
  12265 
  12266 void PresShell::SetNeedsWindowPropertiesSync() {
  12267  if (XRE_IsContentProcess() || !IsRoot()) {
  12268    // Window properties are only relevant to top level widgets in the parent
  12269    // process
  12270    return;
  12271  }
  12272  mNeedsWindowPropertiesSync = true;
  12273  SchedulePaint();
  12274 }
  12275 
  12276 bool PresShell::SetVisualViewportOffset(const nsPoint& aScrollOffset,
  12277                                        const nsPoint& aPrevLayoutScrollPos) {
  12278  nsPoint newOffset = aScrollOffset;
  12279  ScrollContainerFrame* rootScrollContainerFrame =
  12280      GetRootScrollContainerFrame();
  12281  if (rootScrollContainerFrame) {
  12282    // See the comment in ScrollContainerFrame::Reflow above the call to
  12283    // SetVisualViewportOffset for why we need to do this.
  12284    nsRect scrollRange =
  12285        rootScrollContainerFrame->GetScrollRangeForUserInputEvents();
  12286    if (!scrollRange.Contains(newOffset)) {
  12287      newOffset.x = std::min(newOffset.x, scrollRange.XMost());
  12288      newOffset.x = std::max(newOffset.x, scrollRange.x);
  12289      newOffset.y = std::min(newOffset.y, scrollRange.YMost());
  12290      newOffset.y = std::max(newOffset.y, scrollRange.y);
  12291    }
  12292  }
  12293 
  12294  // Careful here not to call GetVisualViewportOffset to get the previous visual
  12295  // viewport offset because if mVisualViewportOffset is nothing then we'll get
  12296  // the layout scroll position directly from the scroll frame and it has likely
  12297  // already been updated.
  12298  nsPoint prevOffset = aPrevLayoutScrollPos;
  12299  if (mVisualViewportOffset.isSome()) {
  12300    prevOffset = *mVisualViewportOffset;
  12301  }
  12302  if (prevOffset == newOffset) {
  12303    return false;
  12304  }
  12305 
  12306  mVisualViewportOffset = Some(newOffset);
  12307 
  12308  if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
  12309    window->VisualViewport()->PostScrollEvent(prevOffset, aPrevLayoutScrollPos);
  12310  }
  12311 
  12312  if (IsVisualViewportSizeSet() && rootScrollContainerFrame) {
  12313    rootScrollContainerFrame->Anchor()->UserScrolled();
  12314  }
  12315 
  12316  if (gfxPlatform::UseDesktopZoomingScrollbars()) {
  12317    if (rootScrollContainerFrame) {
  12318      rootScrollContainerFrame->UpdateScrollbarPosition();
  12319    }
  12320  }
  12321 
  12322  return true;
  12323 }
  12324 
  12325 void PresShell::ResetVisualViewportOffset() { mVisualViewportOffset.reset(); }
  12326 
  12327 void PresShell::RefreshViewportSize() {
  12328  if (mMobileViewportManager) {
  12329    mMobileViewportManager->RefreshViewportSize(false);
  12330  }
  12331 }
  12332 
  12333 void PresShell::ScrollToVisual(const nsPoint& aVisualViewportOffset,
  12334                               FrameMetrics::ScrollOffsetUpdateType aUpdateType,
  12335                               ScrollMode aMode) {
  12336  if (aMode == ScrollMode::Smooth || aMode == ScrollMode::SmoothMsd) {
  12337    if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
  12338      if (sf->SmoothScrollVisual(aVisualViewportOffset, aUpdateType, aMode)) {
  12339        return;
  12340      }
  12341    }
  12342  }
  12343 
  12344  // If the caller asked for instant scroll, or if we failed
  12345  // to do a smooth scroll, do an instant scroll.
  12346  SetPendingVisualScrollUpdate(aVisualViewportOffset, aUpdateType);
  12347 }
  12348 
  12349 void PresShell::SetPendingVisualScrollUpdate(
  12350    const nsPoint& aVisualViewportOffset,
  12351    FrameMetrics::ScrollOffsetUpdateType aUpdateType) {
  12352  mPendingVisualScrollUpdate =
  12353      Some(VisualScrollUpdate{aVisualViewportOffset, aUpdateType});
  12354 
  12355  // The pending update is picked up during the next paint.
  12356  // Schedule a paint to make sure one will happen.
  12357  if (nsIFrame* rootFrame = GetRootFrame()) {
  12358    rootFrame->SchedulePaint();
  12359  }
  12360 }
  12361 
  12362 void PresShell::ClearPendingVisualScrollUpdate() {
  12363  if (mPendingVisualScrollUpdate && mPendingVisualScrollUpdate->mAcknowledged) {
  12364    mPendingVisualScrollUpdate = mozilla::Nothing();
  12365  }
  12366 }
  12367 
  12368 void PresShell::AcknowledgePendingVisualScrollUpdate() {
  12369  MOZ_ASSERT(mPendingVisualScrollUpdate);
  12370  mPendingVisualScrollUpdate->mAcknowledged = true;
  12371 }
  12372 
  12373 nsPoint PresShell::GetVisualViewportOffsetRelativeToLayoutViewport() const {
  12374  return GetVisualViewportOffset() - GetLayoutViewportOffset();
  12375 }
  12376 
  12377 nsPoint PresShell::GetLayoutViewportOffset() const {
  12378  nsPoint result;
  12379  if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
  12380    result = sf->GetScrollPosition();
  12381  }
  12382  return result;
  12383 }
  12384 
  12385 nsSize PresShell::GetLayoutViewportSize() const {
  12386  nsSize result;
  12387  if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
  12388    result = sf->GetScrollPortRect().Size();
  12389  }
  12390  return result;
  12391 }
  12392 
  12393 nsSize PresShell::GetInnerSize() const {
  12394  if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
  12395    return sf->GetSizeForWindowInnerSize();
  12396  }
  12397  return mPresContext->GetVisibleArea().Size();
  12398 }
  12399 
  12400 nsSize PresShell::GetVisualViewportSizeUpdatedByDynamicToolbar() const {
  12401  NS_ASSERTION(mVisualViewportSizeSet,
  12402               "asking for visual viewport size when its not set?");
  12403  if (!mMobileViewportManager) {
  12404    return mVisualViewportSize;
  12405  }
  12406 
  12407  MOZ_ASSERT(GetDynamicToolbarState() == DynamicToolbarState::InTransition ||
  12408             GetDynamicToolbarState() == DynamicToolbarState::Collapsed);
  12409 
  12410  nsSize sizeUpdatedByDynamicToolbar =
  12411      mMobileViewportManager->GetVisualViewportSizeUpdatedByDynamicToolbar();
  12412  return sizeUpdatedByDynamicToolbar == nsSize() ? mVisualViewportSize
  12413                                                 : sizeUpdatedByDynamicToolbar;
  12414 }
  12415 
  12416 void PresShell::RecomputeFontSizeInflationEnabled() {
  12417  mFontSizeInflationEnabled = DetermineFontSizeInflationState();
  12418 }
  12419 
  12420 bool PresShell::DetermineFontSizeInflationState() {
  12421  MOZ_ASSERT(mPresContext, "our pres context should not be null");
  12422  if (mPresContext->IsChrome()) {
  12423    return false;
  12424  }
  12425 
  12426  if (FontSizeInflationEmPerLine() == 0 && FontSizeInflationMinTwips() == 0) {
  12427    return false;
  12428  }
  12429 
  12430  // Force-enabling font inflation always trumps the heuristics here.
  12431  if (!FontSizeInflationForceEnabled()) {
  12432    if (BrowserChild* tab = BrowserChild::GetFrom(this)) {
  12433      // We're in a child process.  Cancel inflation if we're not
  12434      // async-pan zoomed.
  12435      if (!tab->AsyncPanZoomEnabled()) {
  12436        return false;
  12437      }
  12438    }
  12439  }
  12440 
  12441  Maybe<LayoutDeviceIntSize> displaySize;
  12442  // The MVM already caches the top-level content viewer size and is therefore
  12443  // the fastest way of getting that data.
  12444  if (mPresContext->IsRootContentDocumentCrossProcess()) {
  12445    if (mMobileViewportManager) {
  12446      displaySize = Some(mMobileViewportManager->DisplaySize());
  12447    }
  12448  } else if (PresShell* rootPresShell = GetRootPresShell()) {
  12449    // With any luck, we can get at the root content document without any cross-
  12450    // process shenanigans.
  12451    if (auto mvm = rootPresShell->GetMobileViewportManager()) {
  12452      displaySize = Some(mvm->DisplaySize());
  12453    }
  12454  }
  12455 
  12456  if (!displaySize) {
  12457    // Unfortunately, it looks like the root content document lives in a
  12458    // different process. For consistency's sake it would be best to always use
  12459    // the content viewer size of the root content document, but it's not worth
  12460    // the effort, because this only makes a difference in the case of pages
  12461    // with an explicitly sized viewport (neither "width=device-width" nor a
  12462    // completely missing viewport tag) being loaded within a frame, which is
  12463    // hopefully a relatively exotic case.
  12464    // More to the point, these viewport size and zoom-based calculations don't
  12465    // really make sense for frames anyway, so instead of creating a way to
  12466    // access the content viewer size of the top level document cross-process,
  12467    // we probably rather want frames to simply inherit the font inflation state
  12468    // of their top-level parent and should therefore invest any time spent on
  12469    // getting things to work cross-process into that (bug 1724311).
  12470 
  12471    // Until we get around to that though, we just use the content viewer size
  12472    // of however high we can get within the same process.
  12473 
  12474    // (This also serves as a fallback code path if the MVM isn't available,
  12475    // e.g. when debugging in non-e10s mode on Desktop.)
  12476    nsPresContext* topContext =
  12477        mPresContext->GetInProcessRootContentDocumentPresContext();
  12478    LayoutDeviceIntSize result;
  12479    if (!nsLayoutUtils::GetDocumentViewerSize(topContext, result)) {
  12480      return false;
  12481    }
  12482    displaySize = Some(result);
  12483  }
  12484 
  12485  ScreenIntSize screenSize = ViewAs<ScreenPixel>(
  12486      displaySize.value(),
  12487      PixelCastJustification::LayoutDeviceIsScreenForBounds);
  12488  nsViewportInfo vInf = GetDocument()->GetViewportInfo(screenSize);
  12489 
  12490  CSSToScreenScale defaultScale =
  12491      mPresContext->CSSToDevPixelScale() * LayoutDeviceToScreenScale(1.0);
  12492 
  12493  if (vInf.GetDefaultZoom() >= defaultScale || vInf.IsAutoSizeEnabled()) {
  12494    return false;
  12495  }
  12496 
  12497  return true;
  12498 }
  12499 
  12500 static nsIWidget* GetPresContextContainerWidget(nsPresContext* aPresContext) {
  12501  nsCOMPtr<nsISupports> container = aPresContext->Document()->GetContainer();
  12502  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
  12503  if (!baseWindow) {
  12504    return nullptr;
  12505  }
  12506 
  12507  nsCOMPtr<nsIWidget> mainWidget;
  12508  baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
  12509  return mainWidget;
  12510 }
  12511 
  12512 static bool IsTopLevelWidget(nsIWidget* aWidget) {
  12513  using WindowType = mozilla::widget::WindowType;
  12514 
  12515  auto windowType = aWidget->GetWindowType();
  12516  return windowType == WindowType::TopLevel ||
  12517         windowType == WindowType::Dialog || windowType == WindowType::Popup;
  12518 }
  12519 
  12520 PresShell::WindowSizeConstraints PresShell::GetWindowSizeConstraints() {
  12521  nsSize minSize(0, 0);
  12522  nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
  12523  nsIFrame* rootFrame = FrameConstructor()->GetRootElementStyleFrame();
  12524  if (!rootFrame || !mPresContext) {
  12525    return {minSize, maxSize};
  12526  }
  12527  const auto* pos = rootFrame->StylePosition();
  12528  const auto anchorResolutionParams =
  12529      AnchorPosResolutionParams::From(rootFrame);
  12530  if (const auto styleMinWidth = pos->GetMinWidth(anchorResolutionParams);
  12531      styleMinWidth->ConvertsToLength()) {
  12532    minSize.width = styleMinWidth->ToLength();
  12533  }
  12534  if (const auto styleMinHeight = pos->GetMinHeight(anchorResolutionParams);
  12535      styleMinHeight->ConvertsToLength()) {
  12536    minSize.height = styleMinHeight->ToLength();
  12537  }
  12538  if (const auto maxWidth = pos->GetMaxWidth(anchorResolutionParams);
  12539      maxWidth->ConvertsToLength()) {
  12540    maxSize.width = maxWidth->ToLength();
  12541  }
  12542  if (const auto maxHeight = pos->GetMaxHeight(anchorResolutionParams);
  12543      maxHeight->ConvertsToLength()) {
  12544    maxSize.height = maxHeight->ToLength();
  12545  }
  12546  return {minSize, maxSize};
  12547 }
  12548 
  12549 void PresShell::PaintSynchronously() {
  12550  MOZ_ASSERT(!mIsPainting, "re-entrant paint?");
  12551  if (IsNeverPainting() || IsPaintingSuppressed() || !IsVisible() ||
  12552      MOZ_UNLIKELY(NS_WARN_IF(mIsPainting))) {
  12553    return;
  12554  }
  12555  RefPtr widget = GetOwnWidget();
  12556  if (!widget) {
  12557    // We were asked to paint a non-root pres shell, or an already-detached
  12558    // shell.
  12559    return;
  12560  }
  12561  MOZ_ASSERT(widget->IsTopLevelWidget());
  12562  if (!widget->NeedsPaint()) {
  12563    return;
  12564  }
  12565 
  12566  // FIXME: This might not be needed now except for widget paints
  12567  // (WillPaintWindow) and maybe FlushWillPaintObservers.
  12568  WillPaint();
  12569 
  12570  if (MOZ_UNLIKELY(mIsDestroying)) {
  12571    return;
  12572  }
  12573 
  12574  FlushDelayedResize();
  12575 
  12576  mIsPainting = true;
  12577  auto cleanUpPaintingBit = MakeScopeExit([&] { mIsPainting = false; });
  12578  nsAutoScriptBlocker blocker;
  12579  RefPtr<WindowRenderer> renderer = widget->GetWindowRenderer();
  12580  PaintAndRequestComposite(GetRootFrame(), renderer, PaintFlags::None);
  12581 }
  12582 
  12583 void PresShell::SyncWindowPropertiesIfNeeded() {
  12584  if (!mNeedsWindowPropertiesSync) {
  12585    return;
  12586  }
  12587 
  12588  mNeedsWindowPropertiesSync = false;
  12589 
  12590  RefPtr pc = mPresContext;
  12591  if (!pc) {
  12592    return;
  12593  }
  12594 
  12595  nsCOMPtr<nsIWidget> windowWidget = GetPresContextContainerWidget(pc);
  12596  if (!windowWidget || !IsTopLevelWidget(windowWidget)) {
  12597    return;
  12598  }
  12599 
  12600  nsIFrame* rootFrame = FrameConstructor()->GetRootElementStyleFrame();
  12601  if (!rootFrame) {
  12602    return;
  12603  }
  12604 
  12605  // Apply color scheme to the top level window widget.
  12606  windowWidget->SetColorScheme(
  12607      Some(LookAndFeel::ColorSchemeForFrame(rootFrame)));
  12608 
  12609  AutoWeakFrame weak(rootFrame);
  12610  auto* canvas = GetCanvasFrame();
  12611  windowWidget->SetTransparencyMode(nsLayoutUtils::GetFrameTransparency(
  12612      canvas ? canvas : rootFrame, rootFrame));
  12613  if (!weak.IsAlive()) {
  12614    return;
  12615  }
  12616 
  12617  const auto& constraints = GetWindowSizeConstraints();
  12618  nsContainerFrame::SetSizeConstraints(pc, windowWidget, constraints.mMinSize,
  12619                                       constraints.mMaxSize);
  12620 }
  12621 
  12622 nsresult PresShell::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
  12623                                                            bool* aRetVal) {
  12624  *aRetVal = false;
  12625  return NS_OK;
  12626 }
  12627 
  12628 void PresShell::NotifyStyleSheetServiceSheetAdded(StyleSheet* aSheet,
  12629                                                  uint32_t aSheetType) {
  12630  switch (aSheetType) {
  12631    case nsIStyleSheetService::AGENT_SHEET:
  12632      AddAgentSheet(aSheet);
  12633      break;
  12634    case nsIStyleSheetService::USER_SHEET:
  12635      AddUserSheet(aSheet);
  12636      break;
  12637    case nsIStyleSheetService::AUTHOR_SHEET:
  12638      AddAuthorSheet(aSheet);
  12639      break;
  12640    default:
  12641      MOZ_ASSERT_UNREACHABLE("unexpected aSheetType value");
  12642      break;
  12643  }
  12644 }
  12645 
  12646 void PresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet* aSheet,
  12647                                                    uint32_t aSheetType) {
  12648  StyleSet()->RemoveStyleSheet(*aSheet);
  12649  mDocument->ApplicableStylesChanged();
  12650 }
  12651 
  12652 Result<nsIContent*, nsresult> PresShell::EventHandler::GetOverrideClickTarget(
  12653    WidgetGUIEvent* aGUIEvent, nsIFrame* aFrameForPresShell,
  12654    nsIContent* aPointerCapturingContent) {
  12655  if (aGUIEvent->mMessage != eMouseUp) {
  12656    return nullptr;
  12657  }
  12658 
  12659  // If aFrameForPresShell has already been reframed before this is called,
  12660  // we cannot keep handling aGUIEvent.
  12661  auto overrideClickTargetOrError = [&]() -> Result<nsIContent*, nsresult> {
  12662    if (PointerEventHandler::ShouldDispatchClickEventOnCapturingElement() &&
  12663        aPointerCapturingContent) {
  12664      return aGUIEvent->AsMouseEvent()->mInputSource ==
  12665                     MouseEvent_Binding::MOZ_SOURCE_TOUCH
  12666                 // If the event is a compatibility mouse event of Touch Events,
  12667                 // `click` event target should be the element capturing the
  12668                 // touch (Note that eTouchStart caused implicit pointer capture
  12669                 // by default when the web app does not use the pointer capture
  12670                 // API).  However, if the web app released the pointer capture,
  12671                 // the target should be the closest common ancestor of
  12672                 // ePointerDown and ePointerUp.  These things will be handled
  12673                 // by EventStateManager::SetClickCount().  Therefore, we should
  12674                 // not override the click event target for a single tap here.
  12675                 ? nullptr
  12676                 // On the other hand, we want to use the pointer capturing
  12677                 // element as the target of `click` event caused by other input
  12678                 // devices.
  12679                 : aPointerCapturingContent;
  12680    }
  12681 
  12682    if (MOZ_UNLIKELY(!aFrameForPresShell)) {
  12683      return Err(NS_ERROR_FAILURE);
  12684    }
  12685 
  12686    MOZ_ASSERT(aGUIEvent->mClass == eMouseEventClass);
  12687    WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
  12688 
  12689    uint32_t flags = 0;
  12690    RelativeTo relativeTo{aFrameForPresShell};
  12691    nsPoint eventPoint =
  12692        nsLayoutUtils::GetEventCoordinatesRelativeTo(aGUIEvent, relativeTo);
  12693    if (mouseEvent->mIgnoreRootScrollFrame) {
  12694      flags |= INPUT_IGNORE_ROOT_SCROLL_FRAME;
  12695    }
  12696 
  12697    nsIFrame* target =
  12698        FindFrameTargetedByInputEvent(aGUIEvent, relativeTo, eventPoint, flags);
  12699    if (!target) {
  12700      return nullptr;
  12701    }
  12702    return target->GetContent();
  12703  }();
  12704  if (MOZ_UNLIKELY(overrideClickTargetOrError.isErr())) {
  12705    return overrideClickTargetOrError;
  12706  }
  12707  return overrideClickTargetOrError.inspect()
  12708             ? overrideClickTargetOrError.inspect()
  12709                   ->GetInclusiveFlattenedTreeAncestorElement()
  12710             : nullptr;
  12711 }
  12712 
  12713 /******************************************************************************
  12714 * PresShell::EventHandler::EventTargetData
  12715 ******************************************************************************/
  12716 
  12717 void PresShell::EventHandler::EventTargetData::SetFrameAndComputePresShell(
  12718    nsIFrame* aFrameToHandleEvent) {
  12719  if (aFrameToHandleEvent) {
  12720    mFrame = aFrameToHandleEvent;
  12721    mPresShell = aFrameToHandleEvent->PresShell();
  12722  } else {
  12723    mFrame = nullptr;
  12724    mPresShell = nullptr;
  12725  }
  12726 }
  12727 
  12728 void PresShell::EventHandler::EventTargetData::
  12729    SetFrameAndComputePresShellAndContent(nsIFrame* aFrameToHandleEvent,
  12730                                          WidgetGUIEvent* aGUIEvent) {
  12731  MOZ_ASSERT(aFrameToHandleEvent);
  12732  MOZ_ASSERT(aGUIEvent);
  12733 
  12734  SetFrameAndComputePresShell(aFrameToHandleEvent);
  12735  SetContentForEventFromFrame(aGUIEvent);
  12736 }
  12737 
  12738 void PresShell::EventHandler::EventTargetData::SetContentForEventFromFrame(
  12739    WidgetGUIEvent* aGUIEvent) {
  12740  MOZ_ASSERT(mFrame);
  12741  mContent = mFrame->GetContentForEvent(aGUIEvent);
  12742  AssertIfEventTargetContentAndFrameContentMismatch(aGUIEvent);
  12743 }
  12744 
  12745 nsIContent* PresShell::EventHandler::EventTargetData::GetFrameContent() const {
  12746  return mFrame ? mFrame->GetContent() : nullptr;
  12747 }
  12748 
  12749 void PresShell::EventHandler::EventTargetData::
  12750    AssertIfEventTargetContentAndFrameContentMismatch(
  12751        const WidgetGUIEvent* aGUIEvent) const {
  12752 #ifdef DEBUG
  12753  if (!mContent || !mFrame || !mFrame->GetContent()) {
  12754    return;
  12755  }
  12756 
  12757  // If we know the event, we can compute the target correctly.
  12758  if (aGUIEvent) {
  12759    MOZ_ASSERT(mContent == mFrame->GetContentForEvent(aGUIEvent));
  12760    return;
  12761  }
  12762  // If clicking an image map, mFrame should be the image frame, but mContent
  12763  // should be the area element which handles the event at the position.
  12764  if (mContent->IsHTMLElement(nsGkAtoms::area)) {
  12765    MOZ_ASSERT(mContent->GetPrimaryFrame() == mFrame);
  12766    return;
  12767  }
  12768 
  12769  // Otherwise, we can check only whether mContent is an inclusive ancestor
  12770  // element or not.
  12771  if (!mContent->IsElement()) {
  12772    MOZ_ASSERT(mContent == mFrame->GetContent());
  12773    return;
  12774  }
  12775  const Element* const closestInclusiveAncestorElement =
  12776      mFrame->GetContent()->GetInclusiveFlattenedTreeAncestorElement();
  12777  if (closestInclusiveAncestorElement == mContent) {
  12778    return;
  12779  }
  12780  if (closestInclusiveAncestorElement->IsInNativeAnonymousSubtree() &&
  12781      (mContent == closestInclusiveAncestorElement
  12782                       ->FindFirstNonChromeOnlyAccessContent())) {
  12783    return;
  12784  }
  12785  NS_WARNING(nsPrintfCString("mContent=%s", ToString(*mContent).c_str()).get());
  12786  NS_WARNING(nsPrintfCString("mFrame->GetContent()=%s",
  12787                             ToString(*mFrame->GetContent()).c_str())
  12788                 .get());
  12789  MOZ_ASSERT(mContent == mFrame->GetContent());
  12790 #endif  // #ifdef DEBUG
  12791 }
  12792 
  12793 bool PresShell::EventHandler::EventTargetData::MaybeRetargetToActiveDocument(
  12794    WidgetGUIEvent* aGUIEvent) {
  12795  MOZ_ASSERT(aGUIEvent);
  12796  MOZ_ASSERT(mFrame);
  12797  MOZ_ASSERT(mPresShell);
  12798  MOZ_ASSERT(!mContent, "Doesn't support to retarget the content");
  12799 
  12800  EventStateManager* activeESM =
  12801      EventStateManager::GetActiveEventStateManager();
  12802  if (!activeESM) {
  12803    return false;
  12804  }
  12805 
  12806  if (aGUIEvent->mClass != ePointerEventClass &&
  12807      !aGUIEvent->HasMouseEventMessage()) {
  12808    return false;
  12809  }
  12810 
  12811  if (activeESM == GetEventStateManager()) {
  12812    return false;
  12813  }
  12814 
  12815  if (aGUIEvent->ShouldIgnoreCapturingContent()) {
  12816    return false;
  12817  }
  12818 
  12819  nsPresContext* activePresContext = activeESM->GetPresContext();
  12820  if (!activePresContext) {
  12821    return false;
  12822  }
  12823 
  12824  PresShell* activePresShell = activePresContext->GetPresShell();
  12825  if (!activePresShell) {
  12826    return false;
  12827  }
  12828 
  12829  // Note, currently for backwards compatibility we don't forward mouse events
  12830  // to the active document when mouse is over some subdocument.
  12831  if (!nsContentUtils::ContentIsCrossDocDescendantOf(
  12832          activePresShell->GetDocument(), GetDocument())) {
  12833    return false;
  12834  }
  12835 
  12836  SetFrameAndComputePresShell(activePresShell->GetRootFrame());
  12837  return true;
  12838 }
  12839 
  12840 bool PresShell::EventHandler::EventTargetData::ComputeElementFromFrame(
  12841    WidgetGUIEvent* aGUIEvent) {
  12842  MOZ_ASSERT(aGUIEvent);
  12843  MOZ_ASSERT(aGUIEvent->IsUsingCoordinates());
  12844  MOZ_ASSERT(mPresShell);
  12845  MOZ_ASSERT(mFrame);
  12846 
  12847  SetContentForEventFromFrame(aGUIEvent);
  12848 
  12849  // If there is no content for this frame, target it anyway.  Some frames can
  12850  // be targeted but do not have content, particularly windows with scrolling
  12851  // off.
  12852  if (!mContent) {
  12853    return true;
  12854  }
  12855 
  12856  // Bug 103055, bug 185889: mouse events apply to *elements*, not all nodes.
  12857  // Thus we get the nearest element parent here.
  12858  // XXX we leave the frame the same even if we find an element parent, so that
  12859  // the text frame will receive the event (selection and friends are the ones
  12860  // who care about that anyway)
  12861  //
  12862  // We use weak pointers because during this tight loop, the node
  12863  // will *not* go away.  And this happens on every mousemove.
  12864  mContent = mContent->GetInclusiveFlattenedTreeAncestorElement();
  12865 
  12866  // If we found an element, target it.  Otherwise, target *nothing*.
  12867  return !!mContent;
  12868 }
  12869 
  12870 void PresShell::EventHandler::EventTargetData::UpdateWheelEventTarget(
  12871    WidgetGUIEvent* aGUIEvent) {
  12872  MOZ_ASSERT(aGUIEvent);
  12873 
  12874  if (aGUIEvent->mMessage != eWheel) {
  12875    return;
  12876  }
  12877 
  12878  // If dom.event.wheel-event-groups.enabled is not set or the stored
  12879  // event target is removed, we will not get a event target frame from the
  12880  // wheel transaction here.
  12881  nsIFrame* groupFrame = WheelTransaction::GetEventTargetFrame();
  12882  if (!groupFrame) {
  12883    return;
  12884  }
  12885 
  12886  // If dom.event.wheel-event-groups.enabled is set and whe have a stored
  12887  // event target from the wheel transaction, override the event target.
  12888  SetFrameAndComputePresShellAndContent(groupFrame, aGUIEvent);
  12889 }
  12890 
  12891 void PresShell::EventHandler::EventTargetData::UpdateTouchEventTarget(
  12892    WidgetGUIEvent* aGUIEvent) {
  12893  MOZ_ASSERT(aGUIEvent);
  12894 
  12895  if (aGUIEvent->mClass != eTouchEventClass) {
  12896    return;
  12897  }
  12898 
  12899  if (aGUIEvent->mMessage == eTouchStart) {
  12900    WidgetTouchEvent* touchEvent = aGUIEvent->AsTouchEvent();
  12901    nsIFrame* newFrame =
  12902        TouchManager::SuppressInvalidPointsAndGetTargetedFrame(touchEvent);
  12903    if (!newFrame) {
  12904      return;
  12905    }
  12906    SetFrameAndComputePresShellAndContent(newFrame, aGUIEvent);
  12907    return;
  12908  }
  12909 
  12910  PresShell* newPresShell = PresShell::GetShellForTouchEvent(aGUIEvent);
  12911  if (!newPresShell) {
  12912    return;  // XXX Why don't we stop handling the event in this case?
  12913  }
  12914 
  12915  // Touch events (except touchstart) are dispatching to the captured
  12916  // element. Get correct shell from it.
  12917  mPresShell = newPresShell;
  12918 }
  12919 
  12920 void PresShell::EndPaint() {
  12921  ClearPendingVisualScrollUpdate();
  12922 
  12923  if (mDocument) {
  12924    mDocument->EnumerateSubDocuments([](Document& aSubDoc) {
  12925      if (PresShell* presShell = aSubDoc.GetPresShell()) {
  12926        presShell->EndPaint();
  12927      }
  12928      return CallState::Continue;
  12929    });
  12930 
  12931    if (nsPresContext* presContext = GetPresContext()) {
  12932      if (PerformanceMainThread* perf =
  12933              presContext->GetPerformanceMainThread()) {
  12934        perf->FinalizeLCPEntriesForText();
  12935      }
  12936    }
  12937  }
  12938 }
  12939 
  12940 bool PresShell::GetZoomableByAPZ() const {
  12941  return mZoomConstraintsClient && mZoomConstraintsClient->GetAllowZoom();
  12942 }
  12943 
  12944 bool PresShell::ReflowForHiddenContentIfNeeded() {
  12945  if (mHiddenContentInForcedLayout.IsEmpty()) {
  12946    return false;
  12947  }
  12948  mDocument->FlushPendingNotifications(FlushType::Layout);
  12949  mHiddenContentInForcedLayout.Clear();
  12950  return true;
  12951 }
  12952 
  12953 void PresShell::UpdateHiddenContentInForcedLayout(nsIFrame* aFrame) {
  12954  if (!aFrame || !aFrame->IsSubtreeDirty()) {
  12955    return;
  12956  }
  12957 
  12958  nsIFrame* topmostFrameWithContentHidden = nullptr;
  12959  for (nsIFrame* cur = aFrame->GetInFlowParent(); cur;
  12960       cur = cur->GetInFlowParent()) {
  12961    if (cur->HidesContent()) {
  12962      topmostFrameWithContentHidden = cur;
  12963      mHiddenContentInForcedLayout.Insert(cur->GetContent());
  12964    }
  12965  }
  12966 
  12967  if (mHiddenContentInForcedLayout.IsEmpty()) {
  12968    return;
  12969  }
  12970 
  12971  // Queue and immediately flush a reflow for this node.
  12972  MOZ_ASSERT(topmostFrameWithContentHidden);
  12973  FrameNeedsReflow(topmostFrameWithContentHidden, IntrinsicDirty::None,
  12974                   NS_FRAME_IS_DIRTY);
  12975 }
  12976 
  12977 void PresShell::EnsureReflowIfFrameHasHiddenContent(nsIFrame* aFrame) {
  12978  MOZ_ASSERT(mHiddenContentInForcedLayout.IsEmpty());
  12979 
  12980  UpdateHiddenContentInForcedLayout(aFrame);
  12981  ReflowForHiddenContentIfNeeded();
  12982 }
  12983 
  12984 bool PresShell::IsForcingLayoutForHiddenContent(const nsIFrame* aFrame) const {
  12985  return mHiddenContentInForcedLayout.Contains(aFrame->GetContent());
  12986 }
  12987 
  12988 void PresShell::UpdateRelevancyOfContentVisibilityAutoFrames() {
  12989  if (mContentVisibilityRelevancyToUpdate.isEmpty()) {
  12990    return;
  12991  }
  12992 
  12993  for (nsIFrame* frame : mContentVisibilityAutoFrames) {
  12994    frame->UpdateIsRelevantContent(mContentVisibilityRelevancyToUpdate);
  12995  }
  12996 
  12997  if (nsPresContext* presContext = GetPresContext()) {
  12998    presContext->UpdateHiddenByContentVisibilityForAnimationsIfNeeded();
  12999  }
  13000 
  13001  mContentVisibilityRelevancyToUpdate.clear();
  13002 }
  13003 
  13004 void PresShell::ScheduleContentRelevancyUpdate(ContentRelevancyReason aReason) {
  13005  if (MOZ_UNLIKELY(mIsDestroying)) {
  13006    return;
  13007  }
  13008  mContentVisibilityRelevancyToUpdate += aReason;
  13009  EnsureLayoutFlush();
  13010 }
  13011 
  13012 PresShell::ProximityToViewportResult PresShell::DetermineProximityToViewport() {
  13013  ProximityToViewportResult result;
  13014  if (mContentVisibilityAutoFrames.IsEmpty()) {
  13015    return result;
  13016  }
  13017 
  13018  auto margin = LengthPercentage::FromPercentage(
  13019      StaticPrefs::layout_css_content_visibility_relevant_content_margin() /
  13020      100.0f);
  13021 
  13022  auto rootMargin = StyleRect<LengthPercentage>::WithAllSides(margin);
  13023 
  13024  auto input = DOMIntersectionObserver::ComputeInput(
  13025      *mDocument, /* aRoot = */ nullptr, &rootMargin, nullptr);
  13026 
  13027  for (nsIFrame* frame : mContentVisibilityAutoFrames) {
  13028    auto* element = frame->GetContent()->AsElement();
  13029    result.mAnyScrollIntoViewFlag |=
  13030        element->TemporarilyVisibleForScrolledIntoViewDescendant();
  13031 
  13032    // 14.2.3.1
  13033    Maybe<bool> oldVisibility = element->GetVisibleForContentVisibility();
  13034    bool checkForInitialDetermination =
  13035        oldVisibility.isNothing() &&
  13036        (element->GetContentRelevancy().isNothing() ||
  13037         element->GetContentRelevancy()->isEmpty());
  13038 
  13039    // 14.2.3.2
  13040    bool intersects =
  13041        DOMIntersectionObserver::Intersect(
  13042            input, *element, DOMIntersectionObserver::BoxToUse::OverflowClip,
  13043            DOMIntersectionObserver::IsForProximityToViewport::Yes)
  13044            .Intersects();
  13045    element->SetVisibleForContentVisibility(intersects);
  13046 
  13047    // 14.2.3.3
  13048    if (checkForInitialDetermination && intersects) {
  13049      // Initial determination happens sync, otherwise on the next rendering
  13050      // opportunity.
  13051      frame->UpdateIsRelevantContent(ContentRelevancyReason::Visible);
  13052      result.mHadInitialDetermination = true;
  13053    } else if (oldVisibility.isNothing() || *oldVisibility != intersects) {
  13054      ScheduleContentRelevancyUpdate(ContentRelevancyReason::Visible);
  13055    }
  13056  }
  13057  if (nsPresContext* presContext = GetPresContext()) {
  13058    presContext->UpdateHiddenByContentVisibilityForAnimationsIfNeeded();
  13059  }
  13060 
  13061  return result;
  13062 }
  13063 
  13064 void PresShell::ClearTemporarilyVisibleForScrolledIntoViewDescendantFlags()
  13065    const {
  13066  for (nsIFrame* frame : mContentVisibilityAutoFrames) {
  13067    frame->GetContent()
  13068        ->AsElement()
  13069        ->SetTemporarilyVisibleForScrolledIntoViewDescendant(false);
  13070  }
  13071 }
  13072 
  13073 void PresShell::UpdateContentRelevancyImmediately(
  13074    ContentRelevancyReason aReason) {
  13075  if (MOZ_UNLIKELY(mIsDestroying)) {
  13076    return;
  13077  }
  13078 
  13079  mContentVisibilityRelevancyToUpdate += aReason;
  13080 
  13081  EnsureLayoutFlush();
  13082  UpdateRelevancyOfContentVisibilityAutoFrames();
  13083 }