Bug 1543315 - part 11: Mark nsIPresShell::ScrollContentIntoView() as MOZ_CAN_RUN_SCRIPT r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 23 Apr 2019 01:34:24 +0000
changeset 470447 7d67598c90435110cb0ee046e14750306f1d32f6
parent 470446 3d921a5274f511f3da58cc0499239299f092ad96
child 470448 7b69b606bb29b260d42eea8365ddae86207324b7
push id35906
push useraciure@mozilla.com
push dateTue, 23 Apr 2019 22:14:56 +0000
treeherdermozilla-central@0ce3633f8b80 [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 11: Mark nsIPresShell::ScrollContentIntoView() as MOZ_CAN_RUN_SCRIPT r=smaug This patch marks `ScrollContentIntoView()` as `MOZ_CAN_RUN_SCRIPT` and changing some callers of them to guarantee thar their parent callers are also safe. Additionally, this patch moves it from `nsIPresShell` to `PresShell` because all callers can refer `PresShell` directly. Unfortunately, this patch changes a lot of methods in autocomplete and satchel since this patch needs to mark some interface methods as `can_run_script` and they are called each other. This means that autocomplete module is really sensitive like editor module. Perhaps, autocomplete and satchel should do scroll asynchronously and unmark some of them as `MOZ_CAN_RUN_SCRIPT` again. Differential Revision: https://phabricator.services.mozilla.com/D28320
accessible/xul/XULComboboxAccessible.h
dom/base/Element.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsFocusManager.cpp
dom/base/nsFocusManager.h
dom/interfaces/base/nsIDOMWindowUtils.idl
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/nsIPresShell.h
toolkit/components/autocomplete/nsAutoCompleteController.cpp
toolkit/components/autocomplete/nsAutoCompleteController.h
toolkit/components/autocomplete/nsIAutoCompleteController.idl
toolkit/components/autocomplete/nsIAutoCompleteInput.idl
toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
toolkit/components/satchel/nsFormFillController.cpp
toolkit/components/satchel/nsFormFillController.h
toolkit/components/satchel/nsIFormAutoComplete.idl
toolkit/components/satchel/nsIFormFillController.idl
--- a/accessible/xul/XULComboboxAccessible.h
+++ b/accessible/xul/XULComboboxAccessible.h
@@ -28,15 +28,15 @@ class XULComboboxAccessible : public Acc
 
   // ActionAccessible
   virtual uint8_t ActionCount() const override;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
   virtual bool DoAction(uint8_t aIndex) const override;
 
   // Widgets
   virtual bool IsActiveWidget() const override;
-  virtual bool AreItemsOperable() const override;
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual bool AreItemsOperable() const override;
 };
 
 }  // namespace a11y
 }  // namespace mozilla
 
 #endif
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1239,19 +1239,20 @@ class Element : public FragmentOrElement
   void GetSlot(nsAString& aName);
 
   ShadowRoot* GetShadowRoot() const {
     const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     return slots ? slots->mShadowRoot.get() : nullptr;
   }
 
  private:
-  void ScrollIntoView(const ScrollIntoViewOptions& aOptions);
+  MOZ_CAN_RUN_SCRIPT void ScrollIntoView(const ScrollIntoViewOptions& aOptions);
 
  public:
+  MOZ_CAN_RUN_SCRIPT
   void ScrollIntoView(const BooleanOrScrollIntoViewOptions& aObject);
   MOZ_CAN_RUN_SCRIPT void Scroll(double aXScroll, double aYScroll);
   MOZ_CAN_RUN_SCRIPT void Scroll(const ScrollToOptions& aOptions);
   MOZ_CAN_RUN_SCRIPT void ScrollTo(double aXScroll, double aYScroll);
   MOZ_CAN_RUN_SCRIPT void ScrollTo(const ScrollToOptions& aOptions);
   MOZ_CAN_RUN_SCRIPT void ScrollBy(double aXScrollDif, double aYScrollDif);
   MOZ_CAN_RUN_SCRIPT void ScrollBy(const ScrollToOptions& aOptions);
   MOZ_CAN_RUN_SCRIPT int32_t ScrollTop();
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2449,17 +2449,17 @@ nsDOMWindowUtils::ZoomToFocusedInput() {
     return NS_OK;
   }
 
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (!fm) {
     return NS_OK;
   }
 
-  nsIContent* content = fm->GetFocusedElement();
+  nsCOMPtr<nsIContent> content = fm->GetFocusedElement();
   if (!content) {
     return NS_OK;
   }
 
   RefPtr<PresShell> presShell =
       APZCCallbackHelper::GetRootContentDocumentPresShellForContent(content);
   if (!presShell) {
     return NS_OK;
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -516,18 +516,18 @@ nsFocusManager::MoveFocus(mozIDOMWindowP
 
   LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
 
   if (newFocus && newFocus->IsElement()) {
     // for caret movement, pass false for the aFocusChanged argument,
     // otherwise the caret will end up moving to the focus position. This
     // would be a problem because the caret would move to the beginning of the
     // focused link making it impossible to navigate the caret over a link.
-    SetFocusInner(newFocus->AsElement(), aFlags, aType != MOVEFOCUS_CARET,
-                  true);
+    SetFocusInner(MOZ_KnownLive(newFocus->AsElement()), aFlags,
+                  aType != MOVEFOCUS_CARET, true);
     *aElement = do_AddRef(newFocus->AsElement()).take();
   } else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
     // no content was found, so clear the focus for these two types.
     ClearFocus(window);
   }
 
   LOGFOCUS(("<<MoveFocus end>>"));
 
@@ -1311,18 +1311,19 @@ void nsFocusManager::SetFocusInner(Eleme
     uint32_t focusMethod =
         aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK
                       : newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
     newWindow->SetFocusedElement(elementToFocus, focusMethod);
     if (aFocusChanged) {
       nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
 
       RefPtr<PresShell> presShell = docShell->GetPresShell();
-      if (presShell && presShell->DidInitialize())
+      if (presShell && presShell->DidInitialize()) {
         ScrollIntoView(presShell, elementToFocus, aFlags);
+      }
     }
 
     // update the commands even when inactive so that the attributes for that
     // window are up to date.
     if (allowFrameSwitch)
       newWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
 
     if (aFlags & FLAG_RAISE) RaiseWindow(newRootWindow);
@@ -2115,18 +2116,18 @@ void nsFocusManager::FireFocusOrBlurEven
           aEventMessage == eFocus ? eFocusIn : eFocusOut;
       FireFocusInOrOutEvent(focusInOrOutMessage, aPresShell, aTarget,
                             currentWindow, currentFocusedContent,
                             aRelatedTarget);
     }
   }
 }
 
-void nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
-                                    nsIContent* aContent, uint32_t aFlags) {
+void nsFocusManager::ScrollIntoView(PresShell* aPresShell, nsIContent* aContent,
+                                    uint32_t aFlags) {
   // if the noscroll flag isn't set, scroll the newly focused element into view
   if (!(aFlags & FLAG_NOSCROLL)) {
     uint32_t scrollFlags = nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
     if (!(aFlags & FLAG_BYELEMENTFOCUS)) {
       scrollFlags |= nsIPresShell::SCROLL_IGNORE_SCROLL_MARGIN_AND_PADDING;
     }
     aPresShell->ScrollContentIntoView(
         aContent,
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -22,16 +22,17 @@
 
 #define FOCUSMANAGER_CONTRACTID "@mozilla.org/focus-manager;1"
 
 class nsIContent;
 class nsIDocShellTreeItem;
 class nsPIDOMWindowOuter;
 
 namespace mozilla {
+class PresShell;
 namespace dom {
 class Element;
 struct FocusOptions;
 class TabParent;
 }  // namespace dom
 }  // namespace mozilla
 
 struct nsDelayedBlurOrFocusEvent;
@@ -213,16 +214,17 @@ class nsFocusManager final : public nsIF
    * true, then the focus has actually shifted and the caret position will be
    * updated to the new focus, aNewContent will be scrolled into view (unless
    * a flag disables this) and the focus method for the window will be updated.
    * If aAdjustWidget is false, don't change the widget focus state.
    *
    * All actual focus changes must use this method to do so. (as opposed
    * to those that update the focus in an inactive window for instance).
    */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void SetFocusInner(mozilla::dom::Element* aNewContent, int32_t aFlags,
                      bool aFocusChanged, bool aAdjustWidget);
 
   /**
    * Returns true if aPossibleAncestor is the same as aWindow or an
    * ancestor of aWindow.
    */
   bool IsSameOrAncestor(nsPIDOMWindowOuter* aPossibleAncestor,
@@ -322,16 +324,17 @@ class nsFocusManager final : public nsIF
    * Otherwise, the focus is just being updated because the window was
    * raised.
    *
    * aWindowRaised should be true if the window is being raised. In this case,
    * command updaters will not be called.
    *
    * If aAdjustWidget is false, don't change the widget focus state.
    */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void Focus(nsPIDOMWindowOuter* aWindow, mozilla::dom::Element* aContent,
              uint32_t aFlags, bool aIsNewDocument, bool aFocusChanged,
              bool aWindowRaised, bool aAdjustWidget,
              nsIContent* aContentLostFocus = nullptr);
 
   /**
    * Send a focus or blur event at aTarget. It may be added to the delayed
    * event queue if the document is suppressing events.
@@ -381,17 +384,18 @@ class nsFocusManager final : public nsIF
       mozilla::EventMessage aEventMessage, nsIPresShell* aPresShell,
       nsISupports* aTarget, nsPIDOMWindowOuter* aCurrentFocusedWindow,
       nsIContent* aCurrentFocusedContent,
       mozilla::dom::EventTarget* aRelatedTarget = nullptr);
 
   /**
    * Scrolls aContent into view unless the FLAG_NOSCROLL flag is set.
    */
-  void ScrollIntoView(nsIPresShell* aPresShell, nsIContent* aContent,
+  MOZ_CAN_RUN_SCRIPT
+  void ScrollIntoView(mozilla::PresShell* aPresShell, nsIContent* aContent,
                       uint32_t aFlags);
 
   /**
    * Raises the top-level window aWindow at the widget level.
    */
   void RaiseWindow(nsPIDOMWindowOuter* aWindow);
 
   /**
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1432,17 +1432,17 @@ interface nsIDOMWindowUtils : nsISupport
    * false, an error occurred or a flush is not needed, and the notification
    * will not fire. This is intended to be used by test code only!
    */
   bool flushApzRepaints();
 
   /**
    * Ask APZ to pan and zoom to the focused input element.
    */
-  void zoomToFocusedInput();
+  [can_run_script] void zoomToFocusedInput();
 
   /**
    * Method for testing StyleAnimationValue::ComputeDistance.
    *
    * Returns the distance between the two values as reported by
    * StyleAnimationValue::ComputeDistance for the given element and
    * property.
    */
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -3170,17 +3170,18 @@ nsresult PresShell::ScrollToAnchor() {
   }
   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
 
   nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
   if (!rootScroll ||
       mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y) {
     return NS_OK;
   }
-  nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo,
+  nsCOMPtr<nsIContent> lastAnchorScrollTo = mLastAnchorScrolledTo;
+  nsresult rv = ScrollContentIntoView(lastAnchorScrollTo,
                                       ScrollAxis(SCROLL_TOP, SCROLL_ALWAYS),
                                       ScrollAxis(), ANCHOR_SCROLL_FLAGS);
   mLastAnchorScrolledTo = nullptr;
   return rv;
 }
 
 /*
  * Helper (per-continuation) for ScrollContentIntoView.
@@ -3390,19 +3391,20 @@ static void ScrollToShowRect(nsIScrollab
     }
     aFrameAsScrollable->ScrollTo(scrollPt, scrollMode, &allowedRange,
                                  aFlags & nsIPresShell::SCROLL_SNAP
                                      ? nsIScrollbarMediator::ENABLE_SNAP
                                      : nsIScrollbarMediator::DISABLE_SNAP);
   }
 }
 
-nsresult nsIPresShell::ScrollContentIntoView(
-    nsIContent* aContent, nsIPresShell::ScrollAxis aVertical,
-    nsIPresShell::ScrollAxis aHorizontal, uint32_t aFlags) {
+nsresult PresShell::ScrollContentIntoView(nsIContent* aContent,
+                                          nsIPresShell::ScrollAxis aVertical,
+                                          nsIPresShell::ScrollAxis aHorizontal,
+                                          uint32_t aFlags) {
   NS_ENSURE_TRUE(aContent, NS_ERROR_NULL_POINTER);
   RefPtr<Document> composedDoc = aContent->GetComposedDoc();
   NS_ENSURE_STATE(composedDoc);
 
   NS_ASSERTION(mDidInitialize, "should have done initial reflow by now");
 
   if (mContentToScrollTo) {
     mContentToScrollTo->DeleteProperty(nsGkAtoms::scrolling);
@@ -8410,17 +8412,18 @@ bool PresShell::EventHandler::AdjustCont
   } else {
     aMouseEvent->mWidget = nullptr;
   }
 
   // see if we should use the caret position for the popup
   LayoutDeviceIntPoint caretPoint;
   // Beware! This may flush notifications via synchronous
   // ScrollSelectionIntoView.
-  if (PrepareToUseCaretPosition(aMouseEvent->mWidget, caretPoint)) {
+  if (PrepareToUseCaretPosition(MOZ_KnownLive(aMouseEvent->mWidget),
+                                caretPoint)) {
     // caret position is good
     aMouseEvent->mRefPoint = caretPoint;
     return true;
   }
 
   // If we're here because of the key-equiv for showing context menus, we
   // have to reset the event target to the currently focused element. Get it
   // from the focus controller.
@@ -8430,17 +8433,17 @@ bool PresShell::EventHandler::AdjustCont
     currentFocus = fm->GetFocusedElement();
   }
 
   // Reset event coordinates relative to focused frame in view
   if (currentFocus) {
     nsCOMPtr<nsIContent> currentPointElement;
     GetCurrentItemAndPositionForElement(
         currentFocus, getter_AddRefs(currentPointElement),
-        aMouseEvent->mRefPoint, aMouseEvent->mWidget);
+        aMouseEvent->mRefPoint, MOZ_KnownLive(aMouseEvent->mWidget));
     if (currentPointElement) {
       mPresShell->mCurrentEventContent = currentPointElement;
       mPresShell->mCurrentEventFrame = nullptr;
       mPresShell->GetCurrentEventFrame();
     }
   }
 
   return true;
@@ -8494,24 +8497,25 @@ bool PresShell::EventHandler::PrepareToU
     // ScrollContentIntoView, which has a one-pixel disagreement of whether the
     // frame is actually in view. The result is that the frame is aligned with
     // the top of the window, but the menu is still at the bottom.
     //
     // Doing this call first forces the frame to be in view, eliminating the
     // problem. The only difference in the result is that if your cursor is in
     // an edit box below the current view, you'll get the edit box aligned with
     // the top of the window. This is arguably better behavior anyway.
-    rv = mPresShell->ScrollContentIntoView(
-        content,
-        nsIPresShell::ScrollAxis(nsIPresShell::SCROLL_MINIMUM,
-                                 nsIPresShell::SCROLL_IF_NOT_VISIBLE),
-        nsIPresShell::ScrollAxis(nsIPresShell::SCROLL_MINIMUM,
-                                 nsIPresShell::SCROLL_IF_NOT_VISIBLE),
-        nsIPresShell::SCROLL_OVERFLOW_HIDDEN |
-            nsIPresShell::SCROLL_IGNORE_SCROLL_MARGIN_AND_PADDING);
+    rv = MOZ_KnownLive(mPresShell)
+             ->ScrollContentIntoView(
+                 content,
+                 nsIPresShell::ScrollAxis(nsIPresShell::SCROLL_MINIMUM,
+                                          nsIPresShell::SCROLL_IF_NOT_VISIBLE),
+                 nsIPresShell::ScrollAxis(nsIPresShell::SCROLL_MINIMUM,
+                                          nsIPresShell::SCROLL_IF_NOT_VISIBLE),
+                 nsIPresShell::SCROLL_OVERFLOW_HIDDEN |
+                     nsIPresShell::SCROLL_IGNORE_SCROLL_MARGIN_AND_PADDING);
     NS_ENSURE_SUCCESS(rv, false);
     frame = content->GetPrimaryFrame();
     NS_WARNING_ASSERTION(frame, "No frame for focused content?");
   }
 
   // Actually scroll the selection (ie caret) into view. Note that this must
   // be synchronous since we will be checking the caret position on the screen.
   //
@@ -8559,20 +8563,21 @@ bool PresShell::EventHandler::PrepareToU
 
   return true;
 }
 
 void PresShell::EventHandler::GetCurrentItemAndPositionForElement(
     Element* aFocusedElement, nsIContent** aTargetToUse,
     LayoutDeviceIntPoint& aTargetPt, nsIWidget* aRootWidget) {
   nsCOMPtr<nsIContent> focusedContent = aFocusedElement;
-  mPresShell->ScrollContentIntoView(
-      focusedContent, ScrollAxis(), ScrollAxis(),
-      nsIPresShell::SCROLL_OVERFLOW_HIDDEN |
-          nsIPresShell::SCROLL_IGNORE_SCROLL_MARGIN_AND_PADDING);
+  MOZ_KnownLive(mPresShell)
+      ->ScrollContentIntoView(
+          focusedContent, ScrollAxis(), ScrollAxis(),
+          nsIPresShell::SCROLL_OVERFLOW_HIDDEN |
+              nsIPresShell::SCROLL_IGNORE_SCROLL_MARGIN_AND_PADDING);
 
   nsPresContext* presContext = GetPresContext();
 
   bool istree = false, checkLineHeight = true;
   nscoord extraTreeY = 0;
 
 #ifdef MOZ_XUL
   // Set the position to just underneath the current item for multi-select
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -380,16 +380,51 @@ class PresShell final : public nsIPresSh
    * GoToAnchor, if any. This scroll only happens if the scroll
    * position has not changed since the last GoToAnchor. This is called
    * by nsDocumentViewer::LoadComplete. This clears the last anchor
    * scrolled to by GoToAnchor (we don't want to keep it alive if it's
    * removed from the DOM), so don't call this more than once.
    */
   MOZ_CAN_RUN_SCRIPT nsresult ScrollToAnchor();
 
+  /**
+   * Scrolls the view of the document so that the primary frame of the content
+   * is displayed in the window. Layout is flushed before scrolling.
+   *
+   * @param aContent  The content object of which primary frame should be
+   *                  scrolled into view.
+   * @param aVertical How to align the frame vertically and when to do so.
+   *                  This is a ScrollAxis of Where and When.
+   * @param aHorizontal How to align the frame horizontally and when to do so.
+   *                  This is a ScrollAxis of Where and When.
+   * @param aFlags    If SCROLL_FIRST_ANCESTOR_ONLY is set, only the nearest
+   *                  scrollable ancestor is scrolled, otherwise all
+   *                  scrollable ancestors may be scrolled if necessary.
+   *                  If SCROLL_OVERFLOW_HIDDEN is set then we may scroll in a
+   *                  direction even if overflow:hidden is specified in that
+   *                  direction; otherwise we will not scroll in that direction
+   *                  when overflow:hidden is set for that direction.
+   *                  If SCROLL_NO_PARENT_FRAMES is set then we only scroll
+   *                  nodes in this document, not in any parent documents which
+   *                  contain this document in a iframe or the like.
+   *                  If SCROLL_SMOOTH is set and CSSOM-VIEW scroll-behavior
+   *                  is enabled, we will scroll smoothly using
+   *                  nsIScrollableFrame::ScrollMode::SMOOTH_MSD; otherwise,
+   *                  nsIScrollableFrame::ScrollMode::INSTANT will be used.
+   *                  If SCROLL_SMOOTH_AUTO is set, the CSSOM-View
+   *                  scroll-behavior attribute is set to 'smooth' on the
+   *                  scroll frame, and CSSOM-VIEW scroll-behavior is enabled,
+   *                  we will scroll smoothly using
+   *                  nsIScrollableFrame::ScrollMode::SMOOTH_MSD; otherwise,
+   *                  nsIScrollableFrame::ScrollMode::INSTANT will be used.
+   */
+  MOZ_CAN_RUN_SCRIPT
+  nsresult ScrollContentIntoView(nsIContent* aContent, ScrollAxis aVertical,
+                                 ScrollAxis aHorizontal, uint32_t aFlags);
+
  private:
   ~PresShell();
 
   friend class ::AutoPointerEventTargetUpdater;
 
   // ProcessReflowCommands returns whether we processed all our dirty roots
   // without interruptions.
   MOZ_CAN_RUN_SCRIPT bool ProcessReflowCommands(bool aInterruptible);
@@ -1196,25 +1231,28 @@ class PresShell final : public nsIPresSh
      * Otherwise, if a selectable list such as a listbox is focused, the
      * current item within the menu is opened relative to this item.
      * Otherwise, the context menu is opened at the topleft corner of the
      * view.
      *
      * Returns true if the context menu event should fire and false if it should
      * not.
      */
+    MOZ_CAN_RUN_SCRIPT
     bool AdjustContextMenuKeyEvent(WidgetMouseEvent* aMouseEvent);
 
+    MOZ_CAN_RUN_SCRIPT
     bool PrepareToUseCaretPosition(nsIWidget* aEventWidget,
                                    LayoutDeviceIntPoint& aTargetPt);
 
     /**
      * Get the selected item and coordinates in device pixels relative to root
      * document's root view for element, first ensuring the element is onscreen.
      */
+    MOZ_CAN_RUN_SCRIPT
     void GetCurrentItemAndPositionForElement(dom::Element* aFocusedElement,
                                              nsIContent** aTargetToUse,
                                              LayoutDeviceIntPoint& aTargetPt,
                                              nsIWidget* aRootWidget);
 
     nsIContent* GetOverrideClickTarget(WidgetGUIEvent* aGUIEvent,
                                        nsIFrame* aFrame);
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -725,49 +725,16 @@ class nsIPresShell : public nsStubDocume
     explicit ScrollAxis(int16_t aWhere = SCROLL_MINIMUM,
                         WhenToScroll aWhen = SCROLL_IF_NOT_FULLY_VISIBLE,
                         bool aOnlyIfPerceivedScrollableDirection = false)
         : mWhereToScroll(aWhere),
           mWhenToScroll(aWhen),
           mOnlyIfPerceivedScrollableDirection(
               aOnlyIfPerceivedScrollableDirection) {}
   } ScrollAxis;
-  /**
-   * Scrolls the view of the document so that the primary frame of the content
-   * is displayed in the window. Layout is flushed before scrolling.
-   *
-   * @param aContent  The content object of which primary frame should be
-   *                  scrolled into view.
-   * @param aVertical How to align the frame vertically and when to do so.
-   *                  This is a ScrollAxis of Where and When.
-   * @param aHorizontal How to align the frame horizontally and when to do so.
-   *                  This is a ScrollAxis of Where and When.
-   * @param aFlags    If SCROLL_FIRST_ANCESTOR_ONLY is set, only the nearest
-   *                  scrollable ancestor is scrolled, otherwise all
-   *                  scrollable ancestors may be scrolled if necessary.
-   *                  If SCROLL_OVERFLOW_HIDDEN is set then we may scroll in a
-   *                  direction even if overflow:hidden is specified in that
-   *                  direction; otherwise we will not scroll in that direction
-   *                  when overflow:hidden is set for that direction.
-   *                  If SCROLL_NO_PARENT_FRAMES is set then we only scroll
-   *                  nodes in this document, not in any parent documents which
-   *                  contain this document in a iframe or the like.
-   *                  If SCROLL_SMOOTH is set and CSSOM-VIEW scroll-behavior
-   *                  is enabled, we will scroll smoothly using
-   *                  nsIScrollableFrame::ScrollMode::SMOOTH_MSD; otherwise,
-   *                  nsIScrollableFrame::ScrollMode::INSTANT will be used.
-   *                  If SCROLL_SMOOTH_AUTO is set, the CSSOM-View
-   *                  scroll-behavior attribute is set to 'smooth' on the
-   *                  scroll frame, and CSSOM-VIEW scroll-behavior is enabled,
-   *                  we will scroll smoothly using
-   *                  nsIScrollableFrame::ScrollMode::SMOOTH_MSD; otherwise,
-   *                  nsIScrollableFrame::ScrollMode::INSTANT will be used.
-   */
-  nsresult ScrollContentIntoView(nsIContent* aContent, ScrollAxis aVertical,
-                                 ScrollAxis aHorizontal, uint32_t aFlags);
 
   enum {
     SCROLL_FIRST_ANCESTOR_ONLY = 0x01,
     SCROLL_OVERFLOW_HIDDEN = 0x02,
     SCROLL_NO_PARENT_FRAMES = 0x04,
     SCROLL_SMOOTH = 0x08,
     SCROLL_SMOOTH_AUTO = 0x10,
     SCROLL_SNAP = 0x20,
--- a/toolkit/components/autocomplete/nsAutoCompleteController.cpp
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.cpp
@@ -21,18 +21,19 @@
 
 static const char *kAutoCompleteSearchCID =
     "@mozilla.org/autocomplete/search;1?name=";
 
 using namespace mozilla;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsAutoCompleteController)
 
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAutoCompleteController)
-  tmp->SetInput(nullptr);
+  MOZ_KnownLive(tmp)->SetInput(nullptr);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAutoCompleteController)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInput)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearches)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResults)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultCache)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
@@ -830,16 +831,17 @@ nsAutoCompleteController::OnSearchResult
   }
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////
 //// nsITimerCallback
 
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
 NS_IMETHODIMP
 nsAutoCompleteController::Notify(nsITimer *timer) {
   mTimer = nullptr;
 
   if (mImmediateSearchesCount == 0) {
     // If there were no immediate searches, BeforeSearches has not yet been
     // called, so do it now.
     nsresult rv = BeforeSearches();
@@ -862,17 +864,18 @@ nsAutoCompleteController::GetName(nsACSt
 ////////////////////////////////////////////////////////////////////////
 //// nsAutoCompleteController
 
 nsresult nsAutoCompleteController::OpenPopup() {
   uint32_t minResults;
   mInput->GetMinResultsForPopup(&minResults);
 
   if (mMatchCount >= minResults) {
-    return mInput->SetPopupOpen(true);
+    nsCOMPtr<nsIAutoCompleteInput> input = mInput;
+    return input->SetPopupOpen(true);
   }
 
   return NS_OK;
 }
 
 nsresult nsAutoCompleteController::ClosePopup() {
   if (!mInput) {
     return NS_OK;
--- a/toolkit/components/autocomplete/nsAutoCompleteController.h
+++ b/toolkit/components/autocomplete/nsAutoCompleteController.h
@@ -31,47 +31,49 @@ class nsAutoCompleteController final : p
   NS_DECL_NSIAUTOCOMPLETECONTROLLER
   NS_DECL_NSIAUTOCOMPLETEOBSERVER
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSINAMED
 
   nsAutoCompleteController();
 
  protected:
-  virtual ~nsAutoCompleteController();
+  MOZ_CAN_RUN_SCRIPT virtual ~nsAutoCompleteController();
 
   /**
    * SetValueOfInputTo() sets value of mInput to aValue and notifies the input
    * of setting reason.
    */
   void SetValueOfInputTo(const nsString& aValue, uint16_t aReason);
 
   /**
    * SetSearchStringInternal() sets both mSearchString and mSetValue to
    * aSearchString.
    */
   void SetSearchStringInternal(const nsAString& aSearchString) {
     mSearchString = mSetValue = aSearchString;
   }
 
-  nsresult OpenPopup();
-  nsresult ClosePopup();
+  MOZ_CAN_RUN_SCRIPT nsresult OpenPopup();
+  MOZ_CAN_RUN_SCRIPT nsresult ClosePopup();
 
   nsresult StartSearch(uint16_t aSearchType);
 
   nsresult BeforeSearches();
-  nsresult StartSearches();
-  void AfterSearches();
+  MOZ_CAN_RUN_SCRIPT nsresult StartSearches();
+  MOZ_CAN_RUN_SCRIPT void AfterSearches();
   nsresult ClearSearchTimer();
   void MaybeCompletePlaceholder();
 
-  nsresult ProcessResult(int32_t aSearchIndex, nsIAutoCompleteResult* aResult);
-  nsresult PostSearchCleanup();
+  MOZ_CAN_RUN_SCRIPT nsresult ProcessResult(int32_t aSearchIndex,
+                                            nsIAutoCompleteResult* aResult);
+  MOZ_CAN_RUN_SCRIPT nsresult PostSearchCleanup();
 
-  nsresult EnterMatch(bool aIsPopupSelection, mozilla::dom::Event* aEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult EnterMatch(bool aIsPopupSelection,
+                                         mozilla::dom::Event* aEvent);
   nsresult RevertTextValue();
 
   nsresult CompleteDefaultIndex(int32_t aResultIndex);
   nsresult CompleteValue(nsString& aValue);
 
   nsresult GetResultAt(int32_t aIndex, nsIAutoCompleteResult** aResult,
                        int32_t* aMatchIndex);
   nsresult GetResultValueAt(int32_t aIndex, bool aGetFinalValue,
--- a/toolkit/components/autocomplete/nsIAutoCompleteController.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteController.idl
@@ -17,118 +17,119 @@ interface nsIAutoCompleteController : ns
   const unsigned short STATUS_NONE = 1;
   const unsigned short STATUS_SEARCHING = 2;
   const unsigned short STATUS_COMPLETE_NO_MATCH = 3;
   const unsigned short STATUS_COMPLETE_MATCH = 4;
 
   /*
    * The input widget that is currently being controlled.
    */
+  [can_run_script]  // Only setter.
   attribute nsIAutoCompleteInput input;
 
   /*
    * State which indicates the status of possible ongoing searches
    */
   readonly attribute unsigned short searchStatus;
 
   /*
    * The number of matches
    */
   readonly attribute unsigned long matchCount;
 
   /*
    * Start a search on a string, assuming the input property is already set.
    */
-  void startSearch(in AString searchString);
+  [can_run_script] void startSearch(in AString searchString);
 
   /*
    * Stop all asynchronous searches
    */
-  void stopSearch();
+  [can_run_script] void stopSearch();
 
   /*
    * Notify the controller that the user has changed text in the textbox.
    * This includes all means of changing the text value, including typing a
    * character, backspacing, deleting, pasting, committing composition or
    * canceling composition.
    *
    * NOTE: handleText() must be called after composition actually ends, even if
    *       the composition is canceled and the textbox value isn't changed.
    *       Then, implementation of handleText() can access the editor when
    *       it's not in composing mode. DOM compositionend event is not good
    *       timing for calling handleText(). DOM input event immediately after
    *       DOM compositionend event is the best timing to call this.
    *
    * @return whether this handler started a new search.
    */
-  boolean handleText();
+  [can_run_script] boolean handleText();
 
   /*
    * Notify the controller that the user wishes to enter the current text. If
    * aIsPopupSelection is true, then a selection was made from the popup, so
    * fill this value into the input field before continuing. If false, just
    * use the current value of the input field.
    *
    * @param aIsPopupSelection
    *        Pass true if the selection was made from the popup.
    * @param aEvent
    *        The event that triggered the enter, like a key event if the user
    *        pressed the Return key or a click event if the user clicked a popup
    *        item.
    * @return Whether the controller wishes to prevent event propagation and
    *         default event.
    */
-  boolean handleEnter(in boolean aIsPopupSelection,
-                      [optional] in Event aEvent);
+  [can_run_script] boolean handleEnter(in boolean aIsPopupSelection,
+                                       [optional] in Event aEvent);
 
   /*
    * Notify the controller that the user wishes to revert autocomplete
    *
    * @return Whether the controller wishes to prevent event propagation and
    *         default event.
    */
-  boolean handleEscape();
+  [can_run_script] boolean handleEscape();
 
   /*
    * Notify the controller that the user wishes to start composition
    *
    * NOTE: nsIAutoCompleteController implementation expects that this is called
    *       by DOM compositionstart handler.
    */
-  void handleStartComposition();
+  [can_run_script] void handleStartComposition();
 
   /*
    * Notify the controller that the user wishes to end composition
    *
    * NOTE: nsIAutoCompleteController implementation expects that this is called
    *       by DOM compositionend handler.
    */
   void handleEndComposition();
 
   /*
    * Handle tab. Just closes up.
    */
-  void handleTab();
+  [can_run_script] void handleTab();
 
   /*
    * Notify the controller of the following key navigation events:
    *   up, down, left, right, page up, page down
    *
    * @return Whether the controller wishes to prevent event propagation and
    *         default event
    */
-  boolean handleKeyNavigation(in unsigned long key);
+  [can_run_script] boolean handleKeyNavigation(in unsigned long key);
 
   /*
    * Notify the controller that the user chose to delete the current
    * auto-complete result.
    *
    * @return Whether the controller removed a result item.
    */
-  boolean handleDelete();
+  [can_run_script] boolean handleDelete();
 
   /*
    * Get the value of the result at a given index in the last completed search
    */
   AString getValueAt(in long index);
 
   /*
    * Get the label of the result at a given index in the last completed search
@@ -172,10 +173,10 @@ interface nsIAutoCompleteController : ns
    */
   void setInitiallySelectedIndex(in long index);
 
   /*
    * Reset controller internal caches for cases where the input doesn't change
    * but its context resets, thus it is about to start a completely new search
    * session.
    */
-  void resetInternalState();
+  [can_run_script] void resetInternalState();
 };
--- a/toolkit/components/autocomplete/nsIAutoCompleteInput.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteInput.idl
@@ -22,17 +22,17 @@ interface nsIAutoCompleteInput : nsISupp
   /*
    * The controller.
    */
   readonly attribute nsIAutoCompleteController controller;
 
   /*
    * Indicates if the popup is currently open
    */
-  attribute boolean popupOpen;
+  [can_run_script] attribute boolean popupOpen;
 
   /*
    * Option to disable autocomplete functionality
    */
   attribute boolean disableAutoComplete;
 
   /*
    * If a search result has its defaultIndex set, this will optionally
--- a/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
+++ b/toolkit/components/autocomplete/nsIAutoCompleteSearch.idl
@@ -34,17 +34,18 @@ interface nsIAutoCompleteSearch : nsISup
 interface nsIAutoCompleteObserver : nsISupports
 { 
   /*
    * Called when a search is complete and the results are ready
    *
    * @param search - The search object that processed this search
    * @param result - The search result object
    */
-  void onSearchResult(in nsIAutoCompleteSearch search, in nsIAutoCompleteResult result);
+  [can_run_script] void onSearchResult(in nsIAutoCompleteSearch search,
+                                       in nsIAutoCompleteResult result);
 };
 
 [scriptable, uuid(4c3e7462-fbfb-4310-8f4b-239238392b75)]
 interface nsIAutoCompleteSearchDescriptor : nsISupports
 {
   // The search is started after the timeout specified by the corresponding
   // nsIAutoCompleteInput implementation.
   const unsigned short SEARCH_TYPE_DELAYED = 0;
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -121,16 +121,17 @@ nsFormFillController::~nsFormFillControl
     RemoveWindowListeners(window);
   }
 }
 
 ////////////////////////////////////////////////////////////////////////
 //// nsIMutationObserver
 //
 
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
 void nsFormFillController::AttributeChanged(mozilla::dom::Element* aElement,
                                             int32_t aNameSpaceID,
                                             nsAtom* aAttribute,
                                             int32_t aModType,
                                             const nsAttrValue* aOldValue) {
   if ((aAttribute == nsGkAtoms::type || aAttribute == nsGkAtoms::readonly ||
        aAttribute == nsGkAtoms::autocomplete) &&
       aNameSpaceID == kNameSpaceID_None) {
@@ -147,28 +148,31 @@ void nsFormFillController::AttributeChan
     aElement->OwnerDoc()->Dispatch(TaskCategory::Other, event.forget());
   }
 
   if (mListNode && mListNode->Contains(aElement)) {
     RevalidateDataList();
   }
 }
 
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
 void nsFormFillController::ContentAppended(nsIContent* aChild) {
   if (mListNode && mListNode->Contains(aChild->GetParent())) {
     RevalidateDataList();
   }
 }
 
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
 void nsFormFillController::ContentInserted(nsIContent* aChild) {
   if (mListNode && mListNode->Contains(aChild->GetParent())) {
     RevalidateDataList();
   }
 }
 
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
 void nsFormFillController::ContentRemoved(nsIContent* aChild,
                                           nsIContent* aPreviousSibling) {
   if (mListNode && mListNode->Contains(aChild->GetParent())) {
     RevalidateDataList();
   }
 }
 
 void nsFormFillController::CharacterDataWillChange(
@@ -182,16 +186,17 @@ void nsFormFillController::AttributeWill
                                                nsAtom* aAttribute,
                                                int32_t aModType) {}
 
 void nsFormFillController::NativeAnonymousChildListChange(nsIContent* aContent,
                                                           bool aIsRemove) {}
 
 void nsFormFillController::ParentChainChanged(nsIContent* aContent) {}
 
+MOZ_CAN_RUN_SCRIPT_BOUNDARY
 void nsFormFillController::NodeWillBeDestroyed(const nsINode* aNode) {
   MOZ_LOG(sLogger, LogLevel::Verbose, ("NodeWillBeDestroyed: %p", aNode));
   mPwmgrInputs.Remove(aNode);
   mAutofillInputs.Remove(aNode);
   if (aNode == mListNode) {
     mListNode = nullptr;
     RevalidateDataList();
   } else if (aNode == mFocusedInput) {
@@ -222,17 +227,18 @@ nsFormFillController::AttachToBrowser(ns
 
   // Listen for focus events on the domWindow of the docShell
   nsCOMPtr<nsPIDOMWindowOuter> window = GetWindowForDocShell(aDocShell);
   AddWindowListeners(window);
 
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   if (fm) {
     nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedElement();
-    this->HandleFocus(HTMLInputElement::FromNodeOrNull(focusedContent));
+    HandleFocus(
+        MOZ_KnownLive(HTMLInputElement::FromNodeOrNull(focusedContent)));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFormFillController::AttachPopupElementToBrowser(nsIDocShell* aDocShell,
                                                   dom::Element* aPopupEl) {
@@ -814,17 +820,18 @@ nsresult nsFormFillController::StartQuer
 NS_IMETHODIMP
 nsFormFillController::OnSearchCompletion(nsIAutoCompleteResult* aResult) {
   nsAutoString searchString;
   aResult->GetSearchString(searchString);
 
   mLastSearchString = searchString;
 
   if (mLastListener) {
-    mLastListener->OnSearchResult(this, aResult);
+    nsCOMPtr<nsIAutoCompleteObserver> lastListener = mLastListener;
+    lastListener->OnSearchResult(this, aResult);
   }
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////
 //// nsIDOMEventListener
 
@@ -844,35 +851,39 @@ nsFormFillController::HandleEvent(Event*
       return KeyPress(aEvent);
     case eEditorInput: {
       nsCOMPtr<nsINode> input = do_QueryInterface(aEvent->GetComposedTarget());
       if (!IsTextControl(input)) {
         return NS_OK;
       }
 
       bool unused = false;
-      return (!mSuppressOnInput && IsFocusedInputControlled())
-                 ? mController->HandleText(&unused)
-                 : NS_OK;
+      if (!mSuppressOnInput && IsFocusedInputControlled()) {
+        nsCOMPtr<nsIAutoCompleteController> controller = mController;
+        return controller->HandleText(&unused);
+      }
+      return NS_OK;
     }
     case eBlur:
       if (mFocusedInput) {
         StopControllingInput();
       }
       return NS_OK;
     case eCompositionStart:
       NS_ASSERTION(mController, "should have a controller!");
       if (IsFocusedInputControlled()) {
-        mController->HandleStartComposition();
+        nsCOMPtr<nsIAutoCompleteController> controller = mController;
+        controller->HandleStartComposition();
       }
       return NS_OK;
     case eCompositionEnd:
       NS_ASSERTION(mController, "should have a controller!");
       if (IsFocusedInputControlled()) {
-        mController->HandleEndComposition();
+        nsCOMPtr<nsIAutoCompleteController> controller = mController;
+        controller->HandleEndComposition();
       }
       return NS_OK;
     case eContextMenu:
       if (mFocusedPopup) {
         mFocusedPopup->ClosePopup();
       }
       return NS_OK;
     case ePageHide: {
@@ -1010,17 +1021,17 @@ nsresult nsFormFillController::HandleFoc
   }
 #endif
 
   return NS_OK;
 }
 
 nsresult nsFormFillController::Focus(Event* aEvent) {
   nsCOMPtr<nsIContent> input = do_QueryInterface(aEvent->GetComposedTarget());
-  return HandleFocus(HTMLInputElement::FromNodeOrNull(input));
+  return HandleFocus(MOZ_KnownLive(HTMLInputElement::FromNodeOrNull(input)));
 }
 
 nsresult nsFormFillController::KeyDown(Event* aEvent) {
   NS_ASSERTION(mController, "should have a controller!");
 
   mPasswordPopupAutomaticallyOpened = false;
 
   if (!IsFocusedInputControlled()) {
@@ -1031,17 +1042,18 @@ nsresult nsFormFillController::KeyDown(E
   if (!keyEvent) {
     return NS_ERROR_FAILURE;
   }
 
   bool cancel = false;
   uint32_t k = keyEvent->KeyCode();
   switch (k) {
     case KeyboardEvent_Binding::DOM_VK_RETURN: {
-      mController->HandleEnter(false, aEvent, &cancel);
+      nsCOMPtr<nsIAutoCompleteController> controller = mController;
+      controller->HandleEnter(false, aEvent, &cancel);
       break;
     }
   }
 
   if (cancel) {
     aEvent->PreventDefault();
     // Don't let the page see the RETURN event when the popup is open
     // (indicated by cancel=true) so sites don't manually submit forms
@@ -1070,29 +1082,35 @@ nsresult nsFormFillController::KeyPress(
 
   bool cancel = false;
   bool unused = false;
 
   uint32_t k = keyEvent->KeyCode();
   switch (k) {
     case KeyboardEvent_Binding::DOM_VK_DELETE:
 #ifndef XP_MACOSX
-      mController->HandleDelete(&cancel);
+    {
+      nsCOMPtr<nsIAutoCompleteController> controller = mController;
+      controller->HandleDelete(&cancel);
       break;
-    case KeyboardEvent_Binding::DOM_VK_BACK_SPACE:
-      mController->HandleText(&unused);
+    }
+    case KeyboardEvent_Binding::DOM_VK_BACK_SPACE: {
+      nsCOMPtr<nsIAutoCompleteController> controller = mController;
+      controller->HandleText(&unused);
       break;
+    }
 #else
     case KeyboardEvent_Binding::DOM_VK_BACK_SPACE: {
       if (keyEvent->ShiftKey()) {
-        mController->HandleDelete(&cancel);
+        nsCOMPtr<nsIAutoCompleteController> controller = mController;
+        controller->HandleDelete(&cancel);
       } else {
-        mController->HandleText(&unused);
+        nsCOMPtr<nsIAutoCompleteController> controller = mController;
+        controller->HandleText(&unused);
       }
-
       break;
     }
 #endif
     case KeyboardEvent_Binding::DOM_VK_PAGE_UP:
     case KeyboardEvent_Binding::DOM_VK_PAGE_DOWN: {
       if (keyEvent->CtrlKey() || keyEvent->AltKey() || keyEvent->MetaKey()) {
         break;
       }
@@ -1124,26 +1142,31 @@ nsresult nsFormFillController::KeyPress(
           case KeyboardEvent_Binding::DOM_VK_UP:
             k = KeyboardEvent_Binding::DOM_VK_LEFT;
             break;
           case KeyboardEvent_Binding::DOM_VK_DOWN:
             k = KeyboardEvent_Binding::DOM_VK_RIGHT;
             break;
         }
       }
-    }
-      mController->HandleKeyNavigation(k, &cancel);
+      nsCOMPtr<nsIAutoCompleteController> controller = mController;
+      controller->HandleKeyNavigation(k, &cancel);
       break;
-    case KeyboardEvent_Binding::DOM_VK_ESCAPE:
-      mController->HandleEscape(&cancel);
+    }
+    case KeyboardEvent_Binding::DOM_VK_ESCAPE: {
+      nsCOMPtr<nsIAutoCompleteController> controller = mController;
+      controller->HandleEscape(&cancel);
       break;
-    case KeyboardEvent_Binding::DOM_VK_TAB:
-      mController->HandleTab();
+    }
+    case KeyboardEvent_Binding::DOM_VK_TAB: {
+      nsCOMPtr<nsIAutoCompleteController> controller = mController;
+      controller->HandleTab();
       cancel = false;
       break;
+    }
   }
 
   if (cancel) {
     aEvent->PreventDefault();
   }
 
   return NS_OK;
 }
@@ -1179,35 +1202,37 @@ nsresult nsFormFillController::MouseDown
 NS_IMETHODIMP
 nsFormFillController::ShowPopup() {
   bool isOpen = false;
   GetPopupOpen(&isOpen);
   if (isOpen) {
     return SetPopupOpen(false);
   }
 
+  nsCOMPtr<nsIAutoCompleteController> controller = mController;
+
   nsCOMPtr<nsIAutoCompleteInput> input;
-  mController->GetInput(getter_AddRefs(input));
+  controller->GetInput(getter_AddRefs(input));
   if (!input) {
     return NS_OK;
   }
 
   nsAutoString value;
   input->GetTextValue(value);
   if (value.Length() > 0) {
     // Show the popup with a filtered result set
-    mController->SetSearchString(EmptyString());
+    controller->SetSearchString(EmptyString());
     bool unused = false;
-    mController->HandleText(&unused);
+    controller->HandleText(&unused);
   } else {
     // Show the popup with the complete result set.  Can't use HandleText()
     // because it doesn't display the popup if the input is blank.
     bool cancel = false;
-    mController->HandleKeyNavigation(KeyboardEvent_Binding::DOM_VK_DOWN,
-                                     &cancel);
+    controller->HandleKeyNavigation(KeyboardEvent_Binding::DOM_VK_DOWN,
+                                    &cancel);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsFormFillController::GetPasswordPopupAutomaticallyOpened(
     bool* _retval) {
   *_retval = mPasswordPopupAutomaticallyOpened;
@@ -1328,42 +1353,43 @@ void nsFormFillController::StartControll
   mFocusedInput = aInput;
 
   if (Element* list = mFocusedInput->GetList()) {
     list->AddMutationObserverUnlessExists(this);
     mListNode = list;
   }
 
   if (!mFocusedInput->ReadOnly()) {
-    mController->SetInput(this);
+    nsCOMPtr<nsIAutoCompleteController> controller = mController;
+    controller->SetInput(this);
   }
 }
 
 bool nsFormFillController::IsFocusedInputControlled() const {
   return mFocusedInput && mController && !mFocusedInput->ReadOnly();
 }
 
 void nsFormFillController::StopControllingInput() {
   mPasswordPopupAutomaticallyOpened = false;
 
   if (mListNode) {
     mListNode->RemoveMutationObserver(this);
     mListNode = nullptr;
   }
 
-  if (mController) {
+  if (nsCOMPtr<nsIAutoCompleteController> controller = mController) {
     // Reset the controller's input, but not if it has been switched
     // to another input already, which might happen if the user switches
     // focus by clicking another autocomplete textbox
     nsCOMPtr<nsIAutoCompleteInput> input;
-    mController->GetInput(getter_AddRefs(input));
+    controller->GetInput(getter_AddRefs(input));
     if (input == this) {
       MOZ_LOG(sLogger, LogLevel::Verbose,
               ("StopControllingInput: Nulled controller input for %p", this));
-      mController->SetInput(nullptr);
+      controller->SetInput(nullptr);
     }
   }
 
   MOZ_LOG(sLogger, LogLevel::Verbose,
           ("StopControllingInput: Stopped controlling %p", mFocusedInput));
   if (mFocusedInput) {
     MaybeRemoveMutationObserver(mFocusedInput);
     mFocusedInput = nullptr;
--- a/toolkit/components/satchel/nsFormFillController.h
+++ b/toolkit/components/satchel/nsFormFillController.h
@@ -50,49 +50,52 @@ class nsFormFillController final : publi
   NS_DECL_NSIAUTOCOMPLETEINPUT
   NS_DECL_NSIFORMAUTOCOMPLETEOBSERVER
   NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_NSIMUTATIONOBSERVER
 
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFormFillController,
                                            nsIFormFillController)
 
-  nsresult Focus(mozilla::dom::Event* aEvent);
-  nsresult KeyDown(mozilla::dom::Event* aKeyEvent);
-  nsresult KeyPress(mozilla::dom::Event* aKeyEvent);
-  nsresult MouseDown(mozilla::dom::Event* aMouseEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult Focus(mozilla::dom::Event* aEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult KeyDown(mozilla::dom::Event* aKeyEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult KeyPress(mozilla::dom::Event* aKeyEvent);
+  MOZ_CAN_RUN_SCRIPT nsresult MouseDown(mozilla::dom::Event* aMouseEvent);
 
   nsFormFillController();
 
  protected:
-  virtual ~nsFormFillController();
+  MOZ_CAN_RUN_SCRIPT virtual ~nsFormFillController();
 
   void AddWindowListeners(nsPIDOMWindowOuter* aWindow);
-  void RemoveWindowListeners(nsPIDOMWindowOuter* aWindow);
+  MOZ_CAN_RUN_SCRIPT void RemoveWindowListeners(nsPIDOMWindowOuter* aWindow);
 
   void AddKeyListener(nsINode* aInput);
   void RemoveKeyListener();
 
+  MOZ_CAN_RUN_SCRIPT
   void StartControllingInput(mozilla::dom::HTMLInputElement* aInput);
-  void StopControllingInput();
+  MOZ_CAN_RUN_SCRIPT void StopControllingInput();
 
   bool IsFocusedInputControlled() const;
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult HandleFocus(mozilla::dom::HTMLInputElement* aInput);
 
   /**
    * Checks that aElement is a type of element we want to fill, then calls
    * StartControllingInput on it.
    */
+  MOZ_CAN_RUN_SCRIPT
   void MaybeStartControllingInput(mozilla::dom::HTMLInputElement* aElement);
 
   nsresult PerformInputListAutoComplete(const nsAString& aSearch,
                                         nsIAutoCompleteResult** aResult);
 
-  void RevalidateDataList();
+  MOZ_CAN_RUN_SCRIPT void RevalidateDataList();
   bool RowMatch(nsFormHistory* aHistory, uint32_t aIndex,
                 const nsAString& aInputName, const nsAString& aInputValue);
 
   inline nsIDocShell* GetDocShellForInput(
       mozilla::dom::HTMLInputElement* aInput);
   inline nsPIDOMWindowOuter* GetWindowForDocShell(nsIDocShell* aDocShell);
   inline int32_t GetIndexOfDocShell(nsIDocShell* aDocShell);
 
--- a/toolkit/components/satchel/nsIFormAutoComplete.idl
+++ b/toolkit/components/satchel/nsIFormAutoComplete.idl
@@ -34,10 +34,10 @@ interface nsIFormAutoCompleteObserver : 
 {
   /*
    * Called when a search is complete and the results are ready even if the
    * result set is empty. If the search is cancelled or a new search is
    * started, this is not called.
    *
    * @param result - The search result object
    */
-  void onSearchCompletion(in nsIAutoCompleteResult result);
+  [can_run_script] void onSearchCompletion(in nsIAutoCompleteResult result);
 };
--- a/toolkit/components/satchel/nsIFormFillController.idl
+++ b/toolkit/components/satchel/nsIFormFillController.idl
@@ -34,40 +34,42 @@ interface nsIFormFillController : nsISup
   readonly attribute boolean passwordPopupAutomaticallyOpened;
 
   /*
    * Start controlling form fill behavior for the given browser
    *
    * @param docShell - The docShell to attach to
    * @param popup - The popup to show when autocomplete results are available
    */
+  [can_run_script]
   void attachToBrowser(in nsIDocShell docShell, in nsIAutoCompletePopup popup);
+  [can_run_script]
   void attachPopupElementToBrowser(in nsIDocShell docShell, in Element popup);
 
   /*
    * Stop controlling form fill behavior for the given browser
    *
    * @param docShell - The docShell to detach from
    */
-  void detachFromBrowser(in nsIDocShell docShell);
+  [can_run_script] void detachFromBrowser(in nsIDocShell docShell);
 
   /*
    * Mark the specified <input> element as being managed by password manager.
    * Autocomplete requests will be handed off to the password manager, and will
    * not be stored in form history.
    *
    * @param aInput - The HTML <input> element to tag
    */
-  void markAsLoginManagerField(in HTMLInputElement aInput);
+  [can_run_script] void markAsLoginManagerField(in HTMLInputElement aInput);
 
   /*
    * Mark the specified <input> element as being managed by a form autofill component.
    * Autocomplete requests will be handed off to the autofill component.
    *
    * @param aInput - The HTML <input> element to mark
    */
-  void markAsAutofillField(in HTMLInputElement aInput);
+  [can_run_script] void markAsAutofillField(in HTMLInputElement aInput);
 
   /*
    * Open the autocomplete popup, if possible.
    */
-  void showPopup();
+  [can_run_script] void showPopup();
 };