Bug 1543315 - part 15: Mark PresShell::ScrollFrameRectIntoView() as MOZ_CAN_RUN_SCRIPT r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 06 May 2019 13:57:46 +0000
changeset 472702 b4cbda10ffaf3584fcbc8df61a7c643979072891
parent 472701 e2969c920810b32d97f6dd72b1f84a94fadddc77
child 472703 801ff1a58ffc0cbfa6641b31b7a030b53d5bc6d5
push id84790
push usermasayuki@d-toybox.com
push dateMon, 06 May 2019 13:59:16 +0000
treeherderautoland@801ff1a58ffc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1543315
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1543315 - part 15: Mark PresShell::ScrollFrameRectIntoView() as MOZ_CAN_RUN_SCRIPT r=smaug Unfortunately, `EventChainVisitor` does not grab the `nsPresContext` with `RefPtr` by itself. Therefore, there is no guarantee of the lifetime without checking the origin when its subclasses are instantiated. This patch changes it and subclasses to `MOZ_STACK_CLASS` since only `EventDispatcher::Dispatch()` creates them in the stack with given `nsPresContext`. Additionally, it's already been marked as MOZ_CAN_RUN_SCRIPT_BOUNDARY`. Therefore, the `nsPresContext` instance has already been guaranteed its lifetime by the caller. For making this fact stronger, this patch marks their constructors as `MOZ_CAN_RUN_SCRIPT`. Therefore, nobody can create those instances without guaranteeing the lifetime of `nsPresContext` and `dom::Event`. Note that it may look like that `mPresContext` of `EventChainPostVisitor` is not guaranteed. However, `EventChainPreVisitor` which gives `nsPresContext` to it is also a stack only class. So, it won't be deleted before `EventChainPostVisitor` instance. Differential Revision: https://phabricator.services.mozilla.com/D30010
dom/base/Selection.cpp
dom/base/Selection.h
dom/events/EventDispatcher.cpp
dom/events/EventDispatcher.h
dom/events/EventStateManager.cpp
layout/base/AccessibleCaretManager.h
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/forms/nsListControlFrame.cpp
layout/forms/nsListControlFrame.h
layout/generic/nsFrame.h
layout/generic/nsFrameSelection.cpp
layout/generic/nsFrameSelection.h
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsMenuPopupFrame.h
layout/xul/nsScrollbarButtonFrame.h
layout/xul/nsScrollbarFrame.h
layout/xul/nsSliderFrame.h
layout/xul/nsSplitterFrame.h
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -193,17 +193,17 @@ class nsAutoScrollTimer final : public n
     return NS_OK;
   }
 
   nsresult SetDelay(uint32_t aDelay) {
     mDelay = aDelay;
     return NS_OK;
   }
 
-  NS_IMETHOD Notify(nsITimer* timer) override {
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Notify(nsITimer* timer) override {
     if (mSelection && mPresContext) {
       AutoWeakFrame frame =
           mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
       if (!frame) {
         return NS_OK;
       }
       mContent = nullptr;
 
@@ -211,17 +211,18 @@ class nsAutoScrollTimer final : public n
                                 mPresContext->PresShell()->GetRootFrame());
       RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
       frameSelection->HandleDrag(frame, pt);
       if (!frame.IsAlive()) {
         return NS_OK;
       }
 
       NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
-      mSelection->DoAutoScroll(frame, pt);
+      RefPtr<Selection> selection = mSelection;
+      selection->DoAutoScroll(frame, pt);
     }
     return NS_OK;
   }
 
   NS_IMETHOD GetName(nsACString& aName) override {
     aName.AssignLiteral("nsAutoScrollTimer");
     return NS_OK;
   }
@@ -3039,17 +3040,17 @@ void Selection::ScrollIntoView(int16_t a
 
 nsresult Selection::ScrollIntoView(SelectionRegion aRegion,
                                    ScrollAxis aVertical, ScrollAxis aHorizontal,
                                    int32_t aFlags) {
   if (!mFrameSelection) {
     return NS_OK;
   }
 
-  PresShell* presShell = mFrameSelection->GetPresShell();
+  RefPtr<PresShell> presShell = mFrameSelection->GetPresShell();
   if (!presShell || !presShell->GetDocument()) {
     return NS_OK;
   }
 
   if (mFrameSelection->GetBatching()) return NS_OK;
 
   if (!(aFlags & Selection::SCROLL_SYNCHRONOUS))
     return PostScrollSelectionIntoViewEvent(aRegion, aFlags, aVertical,
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -148,16 +148,17 @@ class Selection final : public nsSupport
     SCROLL_OVERFLOW_HIDDEN = 1 << 5,
     SCROLL_FOR_CARET_MOVE = 1 << 6
   };
   // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
   // the scroll event fires so we make sure to scroll to the right place.
   // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
   // flush layout and you MUST hold a strong ref on 'this' for the duration
   // of this call.  This might destroy arbitrary layout objects.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult ScrollIntoView(SelectionRegion aRegion,
                           ScrollAxis aVertical = ScrollAxis(),
                           ScrollAxis aHorizontal = ScrollAxis(),
                           int32_t aFlags = 0);
   nsresult SubtractRange(RangeData* aRange, nsRange* aSubtract,
                          nsTArray<RangeData>* aOutput);
   /**
    * AddItem adds aRange to this Selection.  If mUserInitiated is true,
@@ -204,17 +205,17 @@ class Selection final : public nsSupport
 
   UniquePtr<SelectionDetails> LookUpSelection(
       nsIContent* aContent, int32_t aContentOffset, int32_t aContentLength,
       UniquePtr<SelectionDetails> aDetailsHead, SelectionType aSelectionType,
       bool aSlowCheck);
 
   NS_IMETHOD Repaint(nsPresContext* aPresContext);
 
-  // Note: StartAutoScrollTimer might destroy arbitrary frames etc.
+  MOZ_CAN_RUN_SCRIPT
   nsresult StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint,
                                 uint32_t aDelay);
 
   nsresult StopAutoScrollTimer();
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
@@ -372,17 +373,17 @@ class Selection final : public nsSupport
   }
   SelectionType Type() const { return mSelectionType; }
 
   void GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
                             nsINode& aEndNode, int32_t aEndOffset,
                             bool aAllowAdjacent,
                             nsTArray<RefPtr<nsRange>>& aReturn,
                             mozilla::ErrorResult& aRv);
-
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
                       WhereToScroll aVPercent, WhereToScroll aHPercent,
                       mozilla::ErrorResult& aRv);
 
   void SetColors(const nsAString& aForeColor, const nsAString& aBackColor,
                  const nsAString& aAltForeColor, const nsAString& aAltBackColor,
                  mozilla::ErrorResult& aRv);
 
@@ -582,18 +583,17 @@ class Selection final : public nsSupport
    * @param langRTL is true if the new language is right-to-left or
    *                false if the new language is left-to-right.
    */
   nsresult SelectionLanguageChange(bool aLangRTL);
 
  private:
   friend class ::nsAutoScrollTimer;
 
-  // Note: DoAutoScroll might destroy arbitrary frames etc.
-  nsresult DoAutoScroll(nsIFrame* aFrame, nsPoint aPoint);
+  MOZ_CAN_RUN_SCRIPT nsresult DoAutoScroll(nsIFrame* aFrame, nsPoint aPoint);
 
   // We are not allowed to be in nodes whose root is not our document
   bool HasSameRoot(nsINode& aNode);
 
   // XXX Please don't add additional uses of this method, it's only for
   // XXX supporting broken code (bug 1245883) in the following classes:
   friend class ::nsCopySupport;
   friend class ::nsHTMLCopyEncoder;
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -888,18 +888,19 @@ nsresult EventDispatcher::Dispatch(nsISu
   nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->mOriginalTarget);
   bool isInAnon = content && content->IsInAnonymousSubtree();
 
   aEvent->mFlags.mIsBeingDispatched = true;
 
   // Create visitor object and start event dispatching.
   // GetEventTargetParent for the original target.
   nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
+  nsCOMPtr<EventTarget> targetForPreVisitor = aEvent->mTarget;
   EventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
-                                  isInAnon, aEvent->mTarget);
+                                  isInAnon, targetForPreVisitor);
   targetEtci->GetEventTargetParent(preVisitor);
 
   if (!preVisitor.mCanHandle) {
     targetEtci = MayRetargetToChromeIfCanNotHandleEvent(
         chain, preVisitor, targetEtci, nullptr, content);
   }
   if (!preVisitor.mCanHandle) {
     // The original target and chrome target (mAutomaticChromeDispatch=true)
--- a/dom/events/EventDispatcher.h
+++ b/dom/events/EventDispatcher.h
@@ -45,29 +45,35 @@ class EventTarget;
  *
  * The capture, target and bubble phases of the event dispatch are handled
  * by iterating through the event target chain. Iteration happens twice,
  * first for the default event group and then for the system event group.
  * While dispatching the event for the system event group PostHandleEvent
  * is called right after calling event listener for the current event target.
  */
 
-class EventChainVisitor {
+class MOZ_STACK_CLASS EventChainVisitor {
  public:
+  // For making creators of this class instances guarantee the lifetime of
+  // aPresContext, this needs to be marked as MOZ_CAN_RUN_SCRIPT.
+  MOZ_CAN_RUN_SCRIPT
   EventChainVisitor(nsPresContext* aPresContext, WidgetEvent* aEvent,
                     dom::Event* aDOMEvent,
                     nsEventStatus aEventStatus = nsEventStatus_eIgnore)
       : mPresContext(aPresContext),
         mEvent(aEvent),
         mDOMEvent(aDOMEvent),
         mEventStatus(aEventStatus),
         mItemFlags(0) {}
 
   /**
    * The prescontext, possibly nullptr.
+   * Note that the lifetime of mPresContext is guaranteed by the creators so
+   * that you can use this with MOZ_KnownLive() when you set argument
+   * of can-run-script methods to this.
    */
   nsPresContext* const mPresContext;
 
   /**
    * The WidgetEvent which is being dispatched. Never nullptr.
    */
   WidgetEvent* const mEvent;
 
@@ -101,18 +107,19 @@ class EventChainVisitor {
    *
    * @note This data is different for each item in the event target chain.
    *       It is up to the Pre/PostHandleEvent implementation to decide how to
    *       use this.
    */
   nsCOMPtr<nsISupports> mItemData;
 };
 
-class EventChainPreVisitor : public EventChainVisitor {
+class MOZ_STACK_CLASS EventChainPreVisitor final : public EventChainVisitor {
  public:
+  MOZ_CAN_RUN_SCRIPT
   EventChainPreVisitor(nsPresContext* aPresContext, WidgetEvent* aEvent,
                        dom::Event* aDOMEvent, nsEventStatus aEventStatus,
                        bool aIsInAnon,
                        dom::EventTarget* aTargetInKnownToBeHandledScope)
       : EventChainVisitor(aPresContext, aEvent, aDOMEvent, aEventStatus),
         mCanHandle(true),
         mAutomaticChromeDispatch(true),
         mForceContentDispatch(false),
@@ -287,20 +294,26 @@ class EventChainPreVisitor : public Even
   /**
    * Set to the value of mEvent->mTarget of the previous scope in case of
    * Shadow DOM or such, and if there is no anonymous content this just points
    * to the initial target.
    */
   dom::EventTarget* mTargetInKnownToBeHandledScope;
 };
 
-class EventChainPostVisitor : public mozilla::EventChainVisitor {
+class MOZ_STACK_CLASS EventChainPostVisitor final
+    : public mozilla::EventChainVisitor {
  public:
+  // Note that for making guarantee the lifetime of mPresContext and mDOMEvent,
+  // creators should guarantee that aOther won't be deleted while the instance
+  // of this class is alive.
+  MOZ_CAN_RUN_SCRIPT
   explicit EventChainPostVisitor(EventChainVisitor& aOther)
-      : EventChainVisitor(aOther.mPresContext, aOther.mEvent, aOther.mDOMEvent,
+      : EventChainVisitor(MOZ_KnownLive(aOther.mPresContext), aOther.mEvent,
+                          MOZ_KnownLive(aOther.mDOMEvent),
                           aOther.mEventStatus) {}
 };
 
 /**
  * If an EventDispatchingCallback object is passed to Dispatch,
  * its HandleEvent method is called after handling the default event group,
  * before handling the system event group.
  * This is used in PresShell.
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -4028,17 +4028,18 @@ class MOZ_STACK_CLASS ESMEventCB : publi
  public:
   explicit ESMEventCB(nsIContent* aTarget) : mTarget(aTarget) {}
 
   MOZ_CAN_RUN_SCRIPT
   void HandleEvent(EventChainPostVisitor& aVisitor) override {
     if (aVisitor.mPresContext) {
       nsIFrame* frame = aVisitor.mPresContext->GetPrimaryFrameFor(mTarget);
       if (frame) {
-        frame->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent->AsGUIEvent(),
+        frame->HandleEvent(MOZ_KnownLive(aVisitor.mPresContext),
+                           aVisitor.mEvent->AsGUIEvent(),
                            &aVisitor.mEventStatus);
       }
     }
   }
 
   nsCOMPtr<nsIContent> mTarget;
 };
 
@@ -4729,17 +4730,19 @@ void EventStateManager::FireDragEnterOrE
     }
 
     // collect any changes to moz cursor settings stored in the event's
     // data transfer.
     UpdateDragDataTransfer(&event);
   }
 
   // Finally dispatch the event to the frame
-  if (aTargetFrame) aTargetFrame->HandleEvent(aPresContext, &event, &status);
+  if (aTargetFrame) {
+    aTargetFrame->HandleEvent(aPresContext, &event, &status);
+  }
 }
 
 void EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent) {
   NS_ASSERTION(dragEvent, "drag event is null in UpdateDragDataTransfer!");
   if (!dragEvent->mDataTransfer) {
     return;
   }
 
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -207,16 +207,17 @@ class AccessibleCaretManager {
       nsIContent** aOutContent = nullptr,
       int32_t* aOutContentOffset = nullptr) const;
 
   nsresult DragCaretInternal(const nsPoint& aPoint);
   nsPoint AdjustDragBoundary(const nsPoint& aPoint) const;
 
   // Start the selection scroll timer if the caret is being dragged out of
   // the scroll port.
+  MOZ_CAN_RUN_SCRIPT
   void StartSelectionAutoScrollTimer(const nsPoint& aPoint) const;
   void StopSelectionAutoScrollTimer() const;
 
   void ClearMaintainedSelection() const;
 
   // This method could kill the shell, so callers to methods that call
   // FlushLayout should ensure the event hub that owns us is still alive.
   //
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -496,17 +496,18 @@ class MOZ_STACK_CLASS nsPresShellEventCB
       nsIFrame* frame = mPresShell->GetCurrentEventFrame();
       if (!frame && (aVisitor.mEvent->mMessage == eMouseUp ||
                      aVisitor.mEvent->mMessage == eTouchEnd)) {
         // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure
         // that capturing is released.
         frame = mPresShell->GetRootFrame();
       }
       if (frame) {
-        frame->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent->AsGUIEvent(),
+        frame->HandleEvent(MOZ_KnownLive(aVisitor.mPresContext),
+                           aVisitor.mEvent->AsGUIEvent(),
                            &aVisitor.mEventStatus);
       }
     }
   }
 
   RefPtr<PresShell> mPresShell;
 };
 
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -554,16 +554,17 @@ class PresShell final : public nsStubDoc
    * nodes in this document, not in any parent documents which
    * contain this document in a iframe or the like.
    * If SCROLL_IGNORE_SCROLL_MARGIN_AND_PADDING is set we ignore scroll-margin
    * value specified for |aFrame| and scroll-padding value for the scroll
    * container. This option is typically used to locate poped-up frames into
    * view.
    * @return true if any scrolling happened, false if no scrolling happened
    */
+  MOZ_CAN_RUN_SCRIPT
   bool ScrollFrameRectIntoView(nsIFrame* aFrame, const nsRect& aRect,
                                ScrollAxis aVertical, ScrollAxis aHorizontal,
                                ScrollFlags aScrollFlags);
 
   /**
    * Determine if a rectangle specified in the frame's coordinate system
    * intersects "enough" with the viewport to be considered visible. This
    * is not a strict test against the viewport -- it's a test against
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -1807,17 +1807,18 @@ void nsListControlFrame::ScrollToIndex(i
     }
   }
 }
 
 void nsListControlFrame::ScrollToFrame(dom::HTMLOptionElement& aOptElement) {
   // otherwise we find the content's frame and scroll to it
   nsIFrame* childFrame = aOptElement.GetPrimaryFrame();
   if (childFrame) {
-    PresShell()->ScrollFrameRectIntoView(
+    RefPtr<mozilla::PresShell> presShell = PresShell();
+    presShell->ScrollFrameRectIntoView(
         childFrame, nsRect(nsPoint(0, 0), childFrame->GetSize()), ScrollAxis(),
         ScrollAxis(),
         ScrollFlags::ScrollOverflowHidden |
             ScrollFlags::ScrollFirstAncestorOnly |
             ScrollFlags::IgnoreMarginAndPadding);
   }
 }
 
--- a/layout/forms/nsListControlFrame.h
+++ b/layout/forms/nsListControlFrame.h
@@ -71,16 +71,17 @@ class nsListControlFrame final : public 
 
   virtual void Reflow(nsPresContext* aCX, ReflowOutput& aDesiredSize,
                       const ReflowInput& aReflowInput,
                       nsReflowStatus& aStatus) override;
 
   virtual void Init(nsIContent* aContent, nsContainerFrame* aParent,
                     nsIFrame* aPrevInFlow) override;
 
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual void DidReflow(nsPresContext* aPresContext,
                          const ReflowInput* aReflowInput) override;
   virtual void DestroyFrom(nsIFrame* aDestructRoot,
                            PostDestroyData& aPostDestroyData) override;
 
   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                 const nsDisplayListSet& aLists) override;
 
@@ -93,16 +94,17 @@ class nsListControlFrame final : public 
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   // nsIFormControlFrame
   virtual nsresult SetFormProperty(nsAtom* aName,
                                    const nsAString& aValue) override;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual void SetFocus(bool aOn = true, bool aRepaint = false) override;
 
   virtual mozilla::ScrollStyles GetScrollStyles() const override;
   virtual bool ShouldPropagateComputedBSizeToScrolledContent() const override;
 
   // for accessibility purposes
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
@@ -117,17 +119,17 @@ class nsListControlFrame final : public 
    * If the there are zero items then an empty string is returned
    * If there is nothing selected, then the 0th item's text is returned.
    */
   void GetOptionText(uint32_t aIndex, nsAString& aStr);
 
   void CaptureMouseEvents(bool aGrabMouseEvents);
   nscoord GetBSizeOfARow();
   uint32_t GetNumberOfOptions();
-  void AboutToDropDown();
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void AboutToDropDown();
 
   /**
    * @note This method might destroy the frame, pres shell and other objects.
    */
   void AboutToRollup();
 
   /**
    * Dispatch a DOM oninput and onchange event synchroniously.
@@ -135,40 +137,46 @@ class nsListControlFrame final : public 
    */
   MOZ_CAN_RUN_SCRIPT
   void FireOnInputAndOnChange();
 
   /**
    * Makes aIndex the selected option of a combobox list.
    * @note This method might destroy the frame, pres shell and other objects.
    */
-  void ComboboxFinish(int32_t aIndex);
-  void OnContentReset();
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void ComboboxFinish(int32_t aIndex);
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void OnContentReset();
 
   // nsISelectControlFrame
   NS_IMETHOD AddOption(int32_t index) override;
   NS_IMETHOD RemoveOption(int32_t index) override;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD DoneAddingChildren(bool aIsDone) override;
 
   /**
    * Gets the content (an option) by index and then set it as
    * being selected or not selected.
    */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD OnOptionSelected(int32_t aIndex, bool aSelected) override;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD_(void)
   OnSetSelectedIndex(int32_t aOldIndex, int32_t aNewIndex) override;
 
   /**
    * Mouse event listeners.
    * @note These methods might destroy the frame, pres shell and other objects.
    */
+  MOZ_CAN_RUN_SCRIPT
   nsresult MouseDown(mozilla::dom::Event* aMouseEvent);
   MOZ_CAN_RUN_SCRIPT
   nsresult MouseUp(mozilla::dom::Event* aMouseEvent);
+  MOZ_CAN_RUN_SCRIPT
   nsresult MouseMove(mozilla::dom::Event* aMouseEvent);
+  MOZ_CAN_RUN_SCRIPT
   nsresult DragMove(mozilla::dom::Event* aMouseEvent);
   MOZ_CAN_RUN_SCRIPT
   nsresult KeyDown(mozilla::dom::Event* aKeyEvent);
   MOZ_CAN_RUN_SCRIPT
   nsresult KeyPress(mozilla::dom::Event* aKeyEvent);
 
   /**
    * Returns the options collection for mContent, if any.
@@ -283,24 +291,19 @@ class nsListControlFrame final : public 
   bool IsOptionInteractivelySelectable(int32_t aIndex) const;
   /**
    * @return true if aOption in aSelect is selectable by the user.
    */
   static bool IsOptionInteractivelySelectable(
       mozilla::dom::HTMLSelectElement* aSelect,
       mozilla::dom::HTMLOptionElement* aOption);
 
-  /**
-   * @note This method might destroy the frame, pres shell and other objects.
-   */
-  void ScrollToFrame(HTMLOptionElement& aOptElement);
-  /**
-   * @note This method might destroy the frame, pres shell and other objects.
-   */
-  void ScrollToIndex(int32_t anIndex);
+  MOZ_CAN_RUN_SCRIPT void ScrollToFrame(HTMLOptionElement& aOptElement);
+
+  MOZ_CAN_RUN_SCRIPT void ScrollToIndex(int32_t anIndex);
 
   /**
    * When the user clicks on the comboboxframe to show the dropdown
    * listbox, they then have to move the mouse into the list. We don't
    * want to process those mouse events as selection events (i.e., to
    * scroll list items into view). So we ignore the events until
    * the mouse moves below our border-inner-edge, when
    * mItemSelectionStarted is set.
@@ -317,17 +320,17 @@ class nsListControlFrame final : public 
   void AdjustIndexForDisabledOpt(int32_t aStartIndex, int32_t& anNewIndex,
                                  int32_t aNumOptions, int32_t aDoAdjustInc,
                                  int32_t aDoAdjustIncNext);
 
   /**
    * Resets the select back to it's original default values;
    * those values as determined by the original HTML
    */
-  virtual void ResetList(bool aAllowScrolling);
+  MOZ_CAN_RUN_SCRIPT void ResetList(bool aAllowScrolling);
 
   explicit nsListControlFrame(ComputedStyle* aStyle,
                               nsPresContext* aPresContext);
   virtual ~nsListControlFrame();
 
   /**
    * Sets the mSelectedIndex and mOldSelectedIndex from figuring out what
    * item was selected using content
@@ -359,29 +362,24 @@ class nsListControlFrame final : public 
   void ReflowAsDropdown(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
                         const ReflowInput& aReflowInput,
                         nsReflowStatus& aStatus);
 
   // Selection
   bool SetOptionsSelectedFromFrame(int32_t aStartIndex, int32_t aEndIndex,
                                    bool aValue, bool aClearAll);
   bool ToggleOptionSelectedFromFrame(int32_t aIndex);
-  /**
-   * @note This method might destroy the frame, pres shell and other objects.
-   */
+
+  MOZ_CAN_RUN_SCRIPT
   bool SingleSelection(int32_t aClickedIndex, bool aDoToggle);
   bool ExtendedSelection(int32_t aStartIndex, int32_t aEndIndex,
                          bool aClearAll);
-  /**
-   * @note This method might destroy the frame, pres shell and other objects.
-   */
+  MOZ_CAN_RUN_SCRIPT
   bool PerformSelection(int32_t aClickedIndex, bool aIsShift, bool aIsControl);
-  /**
-   * @note This method might destroy the frame, pres shell and other objects.
-   */
+  MOZ_CAN_RUN_SCRIPT
   bool HandleListSelection(mozilla::dom::Event* aDOMEvent,
                            int32_t selectedIndex);
   void InitSelectionRange(int32_t aClickedIndex);
   MOZ_CAN_RUN_SCRIPT
   void PostHandleKeyEvent(int32_t aNewIndex, uint32_t aCharCode, bool aIsShift,
                           bool aIsControlOrMeta);
 
  public:
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -167,16 +167,17 @@ class nsFrame : public nsBox {
                    PostDestroyData& aPostDestroyData) override;
   ComputedStyle* GetAdditionalComputedStyle(int32_t aIndex) const override;
   void SetAdditionalComputedStyle(int32_t aIndex,
                                   ComputedStyle* aComputedStyle) override;
   nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
   const nsFrameList& GetChildList(ChildListID aListID) const override;
   void GetChildLists(nsTArray<ChildList>* aLists) const override;
 
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult HandleEvent(nsPresContext* aPresContext,
                        mozilla::WidgetGUIEvent* aEvent,
                        nsEventStatus* aEventStatus) override;
   nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent,
                               nsIContent** aContent) override;
 
   nsresult GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) override;
   nsresult GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
@@ -394,16 +395,17 @@ class nsFrame : public nsBox {
                          mozilla::WidgetGUIEvent* aEvent,
                          nsEventStatus* aEventStatus);
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  mozilla::WidgetGUIEvent* aEvent,
                                  nsEventStatus* aEventStatus,
                                  bool aControlHeld);
 
+  MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
                         mozilla::WidgetGUIEvent* aEvent,
                         nsEventStatus* aEventStatus);
 
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            mozilla::WidgetGUIEvent* aEvent,
                            nsEventStatus* aEventStatus);
 
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -1184,17 +1184,18 @@ void nsFrameSelection::HandleDrag(nsIFra
 nsresult nsFrameSelection::StartAutoScrollTimer(nsIFrame* aFrame,
                                                 const nsPoint& aPoint,
                                                 uint32_t aDelay) {
   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
   if (!mDomSelections[index]) {
     return NS_ERROR_NULL_POINTER;
   }
 
-  return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
+  RefPtr<Selection> selection = mDomSelections[index];
+  return selection->StartAutoScrollTimer(aFrame, aPoint, aDelay);
 }
 
 void nsFrameSelection::StopAutoScrollTimer() {
   int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
   if (!mDomSelections[index]) {
     return;
   }
 
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -362,17 +362,17 @@ class nsFrameSelection final {
    * @param aFrame is the outermost frame to use when searching for
    * the closest frame for the point, i.e. the frame that is capturing
    * the mouse
    *
    * @param aPoint is relative to aFrame.
    *
    * @param aDelay is the timer's interval.
    */
-  /*unsafe*/
+  MOZ_CAN_RUN_SCRIPT
   nsresult StartAutoScrollTimer(nsIFrame* aFrame, const nsPoint& aPoint,
                                 uint32_t aDelay);
 
   /**
    * Stops any active auto scroll timer.
    */
   void StopAutoScrollTimer();
 
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1807,17 +1807,18 @@ nsIScrollableFrame* nsMenuPopupFrame::Ge
     currFrame = currFrame->GetNextSibling();
   } while (currFrame);
 
   return nullptr;
 }
 
 void nsMenuPopupFrame::EnsureMenuItemIsVisible(nsMenuFrame* aMenuItem) {
   if (aMenuItem) {
-    aMenuItem->PresShell()->ScrollFrameRectIntoView(
+    RefPtr<mozilla::PresShell> presShell = aMenuItem->PresShell();
+    presShell->ScrollFrameRectIntoView(
         aMenuItem, nsRect(nsPoint(0, 0), aMenuItem->GetRect().Size()),
         ScrollAxis(), ScrollAxis(),
         ScrollFlags::ScrollOverflowHidden |
             ScrollFlags::ScrollFirstAncestorOnly |
             ScrollFlags::IgnoreMarginAndPadding);
   }
 }
 
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -173,16 +173,17 @@ class nsMenuPopupFrame final : public ns
  public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS(nsMenuPopupFrame)
 
   explicit nsMenuPopupFrame(ComputedStyle* aStyle, nsPresContext* aPresContext);
 
   // nsMenuParent interface
   virtual nsMenuFrame* GetCurrentMenuItem() override;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD SetCurrentMenuItem(nsMenuFrame* aMenuItem) override;
   virtual void CurrentMenuIsBeingDestroyed() override;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD ChangeMenuItem(nsMenuFrame* aMenuItem, bool aSelectFirstItem,
                             bool aFromKey) override;
 
   // as popups are opened asynchronously, the popup pending state is used to
   // prevent multiple requests from attempting to open the same popup twice
@@ -249,16 +250,17 @@ class nsMenuPopupFrame final : public ns
   nsresult CreateWidgetForView(nsView* aView);
   uint8_t GetShadowStyle();
 
   virtual bool IsLeafDynamic() const override;
 
   virtual void UpdateWidgetProperties() override;
 
   // layout, position and display the popup as needed
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
                    nsIFrame* aAnchor, bool aSizedToPopup);
 
   nsView* GetRootViewForPopup(nsIFrame* aStartFrame);
 
   // Set the position of the popup either relative to the anchor aAnchorFrame
   // (or the frame for mAnchorContent if aAnchorFrame is null), anchored at a
   // rectangle, or at a specific point if a screen position is set. The popup
@@ -342,17 +344,17 @@ class nsMenuPopupFrame final : public ns
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override {
     return MakeFrameName(NS_LITERAL_STRING("MenuPopup"), aResult);
   }
 #endif
 
-  void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame);
+  MOZ_CAN_RUN_SCRIPT void EnsureMenuItemIsVisible(nsMenuFrame* aMenuFrame);
 
   void ChangeByPage(bool aIsUp);
 
   // Move the popup to the screen coordinate |aPos| in CSS pixels.
   // If aUpdateAttrs is true, and the popup already has left or top attributes,
   // then those attributes are updated to the new location.
   // The frame may be destroyed by this method.
   void MoveTo(const mozilla::CSSIntPoint& aPos, bool aUpdateAttrs);
--- a/layout/xul/nsScrollbarButtonFrame.h
+++ b/layout/xul/nsScrollbarButtonFrame.h
@@ -54,16 +54,17 @@ class nsScrollbarButtonFrame final : pub
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  mozilla::WidgetGUIEvent* aEvent,
                                  nsEventStatus* aEventStatus,
                                  bool aControlHeld) override {
     return NS_OK;
   }
 
+  MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
                         mozilla::WidgetGUIEvent* aEvent,
                         nsEventStatus* aEventStatus) override {
     return NS_OK;
   }
 
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            mozilla::WidgetGUIEvent* aEvent,
--- a/layout/xul/nsScrollbarFrame.h
+++ b/layout/xul/nsScrollbarFrame.h
@@ -56,16 +56,17 @@ class nsScrollbarFrame final : public ns
                          mozilla::WidgetGUIEvent* aEvent,
                          nsEventStatus* aEventStatus) override;
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  mozilla::WidgetGUIEvent* aEvent,
                                  nsEventStatus* aEventStatus,
                                  bool aControlHeld) override;
 
+  MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
                         mozilla::WidgetGUIEvent* aEvent,
                         nsEventStatus* aEventStatus) override;
 
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            mozilla::WidgetGUIEvent* aEvent,
                            nsEventStatus* aEventStatus) override;
 
--- a/layout/xul/nsSliderFrame.h
+++ b/layout/xul/nsSliderFrame.h
@@ -111,16 +111,17 @@ class nsSliderFrame final : public nsBox
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  mozilla::WidgetGUIEvent* aEvent,
                                  nsEventStatus* aEventStatus,
                                  bool aControlHeld) override {
     return NS_OK;
   }
 
+  MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
                         mozilla::WidgetGUIEvent* aEvent,
                         nsEventStatus* aEventStatus) override {
     return NS_OK;
   }
 
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            mozilla::WidgetGUIEvent* aEvent,
--- a/layout/xul/nsSplitterFrame.h
+++ b/layout/xul/nsSplitterFrame.h
@@ -50,16 +50,17 @@ class nsSplitterFrame final : public nsB
                          mozilla::WidgetGUIEvent* aEvent,
                          nsEventStatus* aEventStatus) override;
 
   NS_IMETHOD HandleMultiplePress(nsPresContext* aPresContext,
                                  mozilla::WidgetGUIEvent* aEvent,
                                  nsEventStatus* aEventStatus,
                                  bool aControlHeld) override;
 
+  MOZ_CAN_RUN_SCRIPT
   NS_IMETHOD HandleDrag(nsPresContext* aPresContext,
                         mozilla::WidgetGUIEvent* aEvent,
                         nsEventStatus* aEventStatus) override;
 
   NS_IMETHOD HandleRelease(nsPresContext* aPresContext,
                            mozilla::WidgetGUIEvent* aEvent,
                            nsEventStatus* aEventStatus) override;