Bug 1487591 - Make Selection treat AccessibleCaretEventHub as concrete class rather than nsISelectionListener r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 31 Aug 2018 21:19:44 +0000
changeset 434368 bdbd39b2251dd4effb6cf19fe07710118358bc4e
parent 434367 64efa7d65fd1bdf6a7326ce758469453339d7d3d
child 434369 18320a6e8444c623aadf8e1b42ef8a04ab31d203
push id34552
push usercsabou@mozilla.com
push dateSat, 01 Sep 2018 09:28:41 +0000
treeherdermozilla-central@dcae3a9fe7b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1487591
milestone63.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 1487591 - Make Selection treat AccessibleCaretEventHub as concrete class rather than nsISelectionListener r=smaug AccessibleCaretEventHub is an nsISelectionListener of Selection whose type is "normal". This is added only when nsFrameSelection::Init() is called and accessible caret is enabled. Additionally, nsFrameSelection::Init() is always called immediately after creating nsFrameSelection. Therefore, when AccessibleCaretEventHub is installed to Selection, this is always second selection listener and won't be installed multiple times. So, Selection can store pointer of AccessibleCaretEventHub directly only when it's enabled and the Selection needs to notify it of selection change. This patch makes Selection stores AccessibleCaretEventHub with RefPtr, then, makes Selection::NotifySelectionListeners() call its OnSelectionChange() immediately after AutoCopyListener. Unfortunately, this patch includes making of MOZ_CAN_RUN_SCRIPT_BOUNDARY and MOZ_CAN_RUN_SCRIPT a lot since some methods of AccessibleCaretEventHub are marked as MOZ_CAN_RUN_SCRIPT and including AccessibleCaretEventHub.h into Selection.h causes compile the compile errors. Differential Revision: https://phabricator.services.mozilla.com/D4733
docshell/base/nsDocShell.h
dom/base/Selection.cpp
dom/base/Selection.h
dom/base/nsCopySupport.h
dom/base/nsDocumentEncoder.cpp
dom/base/nsFocusManager.cpp
dom/base/nsRange.h
layout/base/AccessibleCaretEventHub.cpp
layout/base/AccessibleCaretEventHub.h
layout/base/PresShell.cpp
layout/base/gtest/TestAccessibleCaretEventHub.cpp
layout/base/moz.build
layout/base/nsLayoutUtils.h
layout/generic/nsCanvasFrame.cpp
layout/generic/nsFrameSelection.cpp
layout/generic/nsFrameSelection.h
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -253,20 +253,22 @@ public:
     FireOnLocationChange(this, nullptr, mCurrentURI,
                          LOCATION_CHANGE_SAME_DOCUMENT);
   }
 
   nsresult HistoryTransactionRemoved(int32_t aIndex);
 
   // Notify Scroll observers when an async panning/zooming transform
   // has started being applied
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void NotifyAsyncPanZoomStarted();
 
   // Notify Scroll observers when an async panning/zooming transform
   // is no longer applied
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void NotifyAsyncPanZoomStopped();
 
   void SetInFrameSwap(bool aInSwap)
   {
     mInFrameSwap = aInSwap;
   }
   bool InFrameSwap();
 
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -732,16 +732,17 @@ Selection::GetDocGroup() const
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
   // Unlink the selection listeners *before* we do RemoveAllRanges since
   // we don't want to notify the listeners during JS GC (they could be
   // in JS!).
   tmp->mNotifyAutoCopy = false;
+  tmp->StopNotifyingAccessibleCaretEventHub();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedRange)
   tmp->RemoveAllRanges(IgnoreErrors());
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
   {
@@ -2121,17 +2122,18 @@ Selection::AddRangeJS(nsRange& aRange, E
   AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
   mCalledByJS = true;
   AddRange(aRange, aRv);
 }
 
 void
 Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
 {
-  return AddRangeInternal(aRange, GetParentObject(), aRv);
+  RefPtr<nsIDocument> document(GetParentObject());
+  return AddRangeInternal(aRange, document, aRv);
 }
 
 void
 Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
                             ErrorResult& aRv)
 {
   // If the given range is part of another Selection, we need to clone the
   // range first.
@@ -3492,16 +3494,21 @@ Selection::NotifySelectionListeners()
     selectionListeners(mSelectionListeners);
 
   int16_t reason = frameSelection->PopReason();
 
   if (mNotifyAutoCopy) {
     AutoCopyListener::OnSelectionChange(doc, *this, reason);
   }
 
+  if (mAccessibleCaretEventHub) {
+    RefPtr<AccessibleCaretEventHub> hub(mAccessibleCaretEventHub);
+    hub->OnSelectionChange(doc, this, reason);
+  }
+
   for (auto& listener : selectionListeners) {
     listener->NotifySelectionChanged(doc, this, reason);
   }
   return NS_OK;
 }
 
 void
 Selection::StartBatchChanges()
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_Selection_h__
 #define mozilla_Selection_h__
 
 #include "nsIWeakReference.h"
 
+#include "mozilla/AccessibleCaretEventHub.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/RangeBoundary.h"
 #include "mozilla/TextRange.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsDirection.h"
 #include "nsIPresShell.h"  // For ScrollAxis
 #include "nsISelectionController.h"
@@ -89,16 +90,36 @@ public:
   /**
    * NotifyAutoCopy() starts to notify AutoCopyListener of selection changes.
    */
   void NotifyAutoCopy()
   {
     mNotifyAutoCopy = true;
   }
 
+  /**
+   * MaybeNotifyAccessibleCaretEventHub() starts to notify
+   * AccessibleCaretEventHub of selection change if aPresShell has it.
+   */
+  void MaybeNotifyAccessibleCaretEventHub(nsIPresShell* aPresShell)
+  {
+    if (!mAccessibleCaretEventHub && aPresShell) {
+      mAccessibleCaretEventHub = aPresShell->GetAccessibleCaretEventHub();
+    }
+  }
+
+  /**
+   * StopNotifyingAccessibleCaretEventHub() stops notifying
+   * AccessibleCaretEventHub of selection change.
+   */
+  void StopNotifyingAccessibleCaretEventHub()
+  {
+    mAccessibleCaretEventHub = nullptr;
+  }
+
   nsIDocument* GetParentObject() const;
   DocGroup* GetDocGroup() const;
 
   // utility methods for scrolling the selection into view
   nsPresContext* GetPresContext() const;
   nsIPresShell* GetPresShell() const;
   nsFrameSelection* GetFrameSelection() const { return mFrameSelection; }
   // Returns a rect containing the selection region, and frame that that
@@ -155,17 +176,20 @@ public:
     return Collapse(RawRangeBoundary(aContainer, aOffset));
   }
   nsresult      Collapse(const RawRangeBoundary& aPoint)
   {
     ErrorResult result;
     Collapse(aPoint, result);
     return result.StealNSResult();
   }
+
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult      Extend(nsINode* aContainer, int32_t aOffset);
+
   nsRange*      GetRangeAt(int32_t aIndex) const;
 
   // Get the anchor-to-focus range if we don't care which end is
   // anchor and which end is focus.
   const nsRange* GetAnchorFocusRange() const {
     return mAnchorFocusRange;
   }
 
@@ -254,16 +278,17 @@ public:
   // *JS() methods are mapped to Selection.*().
   // They may move focus only when the range represents normal selection.
   // These methods shouldn't be used by non-JS callers.
   void CollapseJS(nsINode* aContainer, uint32_t aOffset,
                   mozilla::ErrorResult& aRv);
   void CollapseToStartJS(mozilla::ErrorResult& aRv);
   void CollapseToEndJS(mozilla::ErrorResult& aRv);
 
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void ExtendJS(nsINode& aContainer, uint32_t aOffset,
                 mozilla::ErrorResult& aRv);
 
   void SelectAllChildrenJS(nsINode& aNode, mozilla::ErrorResult& aRv);
 
   /**
    * Deletes this selection from document the nodes belong to.
    */
@@ -273,18 +298,21 @@ public:
   {
     return mRanges.Length();
   }
 
   void GetType(nsAString& aOutType) const;
 
   nsRange* GetRangeAt(uint32_t aIndex, mozilla::ErrorResult& aRv);
   void AddRangeJS(nsRange& aRange, mozilla::ErrorResult& aRv);
+
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void RemoveRange(nsRange& aRange, mozilla::ErrorResult& aRv);
-  void RemoveAllRanges(mozilla::ErrorResult& aRv);
+
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void RemoveAllRanges(mozilla::ErrorResult& aRv);
 
   /**
    * RemoveAllRangesTemporarily() is useful if the caller will add one or more
    * ranges later.  This tries to cache a removing range if it's possible.
    * If a range is not referred by anything else this selection, the range
    * can be reused later.  Otherwise, this works as same as RemoveAllRanges().
    */
   nsresult RemoveAllRangesTemporarily();
@@ -381,46 +409,56 @@ public:
    * is focused and editable, the caret will blink there.
    * @param aContainer The given node where the selection will be set
    * @param offset      Where in given dom node to place the selection (the offset into the given node)
    */
   void Collapse(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
   {
     Collapse(RawRangeBoundary(&aContainer, aOffset), aRv);
   }
+
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void Collapse(const RawRangeBoundary& aPoint, ErrorResult& aRv);
+
   /**
    * Collapses the whole selection to a single point at the start
    * of the current selection (irrespective of direction).  If content
    * is focused and editable, the caret will blink there.
    */
   void CollapseToStart(mozilla::ErrorResult& aRv);
+
   /**
    * Collapses the whole selection to a single point at the end
    * of the current selection (irrespective of direction).  If content
    * is focused and editable, the caret will blink there.
    */
   void CollapseToEnd(mozilla::ErrorResult& aRv);
 
   /**
    * Extends the selection by moving the selection end to the specified node and
    * offset, preserving the selection begin position. The new selection end
    * result will always be from the anchorNode to the new focusNode, regardless
    * of direction.
    *
    * @param aContainer The node where the selection will be extended to
    * @param aOffset    Where in aContainer to place the offset of the new selection end.
    */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void Extend(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv);
+
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void AddRange(nsRange& aRange, mozilla::ErrorResult& aRv);
+
   /**
    * Adds all children of the specified node to the selection.
    * @param aNode the parent of the children to be added to the selection.
    */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void SelectAllChildren(nsINode& aNode, mozilla::ErrorResult& aRv);
+
   void SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
                         nsINode& aFocusNode, uint32_t aFocusOffset,
                         mozilla::ErrorResult& aRv);
 
   void AddSelectionChangeBlocker();
   void RemoveSelectionChangeBlocker();
   bool IsBlockingSelectionChangeEvents() const;
 
@@ -484,16 +522,17 @@ private:
 
   // 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;
+  MOZ_CAN_RUN_SCRIPT
   void AddRangeInternal(nsRange& aRange, nsIDocument* aDocument, ErrorResult&);
 
   // This is helper method for GetPrimaryFrameForFocusNode.
   // If aVisual is true, this returns caret frame.
   // If false, this returns primary frame.
   nsresult GetPrimaryOrCaretFrameForNodeOffset(nsIContent* aContent,
                                                uint32_t aOffset,
                                                nsIFrame** aReturnFrame,
@@ -508,18 +547,18 @@ public:
   SelectionType GetType() const { return mSelectionType; }
   void SetType(SelectionType aSelectionType)
   {
     mSelectionType = aSelectionType;
   }
 
   SelectionCustomColors* GetCustomColors() const { return mCustomColors.get(); }
 
-  nsresult NotifySelectionListeners(bool aCalledByJS);
-  nsresult NotifySelectionListeners();
+  MOZ_CAN_RUN_SCRIPT nsresult NotifySelectionListeners(bool aCalledByJS);
+  MOZ_CAN_RUN_SCRIPT nsresult NotifySelectionListeners();
 
   friend struct AutoUserInitiated;
   struct MOZ_RAII AutoUserInitiated
   {
     explicit AutoUserInitiated(Selection* aSelection
                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mSavedValue(aSelection->mUserInitiated)
     {
@@ -673,16 +712,17 @@ private:
   // Note that while the range is cached by this, we keep the range being
   // a mutation observer because it is not so cheap to register the range
   // as a mutation observer again.  On the other hand, we make it not
   // positioned because it is not so cheap to keep valid DOM point against
   // mutations.  This does not cause any problems because we will set new
   // DOM point when we treat it as a range of Selection again.
   RefPtr<nsRange> mCachedRange;
   RefPtr<nsFrameSelection> mFrameSelection;
+  RefPtr<AccessibleCaretEventHub> mAccessibleCaretEventHub;
   RefPtr<nsAutoScrollTimer> mAutoScrollTimer;
   nsTArray<nsCOMPtr<nsISelectionListener>> mSelectionListeners;
   nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
   CachedOffsetForFrame* mCachedOffsetForFrame;
   nsDirection mDirection;
   SelectionType mSelectionType;
   UniquePtr<SelectionCustomColors> mCustomColors;
 
--- a/dom/base/nsCopySupport.h
+++ b/dom/base/nsCopySupport.h
@@ -46,16 +46,17 @@ class nsCopySupport
 
     // Get the selection as a transferable. Similar to HTMLCopy except does
     // not deal with the clipboard.
   static nsresult GetTransferableForSelection(mozilla::dom::Selection* aSelection,
                                               nsIDocument* aDocument,
                                               nsITransferable** aTransferable);
 
     // Same as GetTransferableForSelection, but doesn't skip invisible content.
+    MOZ_CAN_RUN_SCRIPT_BOUNDARY
     static nsresult GetTransferableForNode(nsINode* aNode,
                                            nsIDocument* aDoc,
                                            nsITransferable** aTransferable);
     /**
      * Retrieve the selection for the given document. If the current focus
      * within the document has its own selection, aSelection will be set to it
      * and this focused content node returned. Otherwise, aSelection will be
      * set to the document's selection and null will be returned.
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -1119,16 +1119,17 @@ class nsHTMLCopyEncoder : public nsDocum
 public:
 
   nsHTMLCopyEncoder();
   virtual ~nsHTMLCopyEncoder();
 
   NS_IMETHOD Init(nsIDocument* aDocument, const nsAString& aMimeType, uint32_t aFlags) override;
 
   // overridden methods from nsDocumentEncoder
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD SetSelection(Selection* aSelection) override;
   NS_IMETHOD EncodeToStringWithContext(nsAString& aContextString,
                                        nsAString& aInfoString,
                                        nsAString& aEncodedString) override;
   NS_IMETHOD EncodeToString(nsAString& aOutputString) override;
 
 protected:
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/TabParent.h"
 
 #include "nsFocusManager.h"
 
-#include "AccessibleCaretEventHub.h"
 #include "ChildIterator.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsGkAtoms.h"
 #include "nsGlobalWindow.h"
 #include "nsContentUtils.h"
 #include "nsDocument.h"
 #include "nsIContentParent.h"
 #include "nsPIDOMWindow.h"
@@ -41,16 +40,17 @@
 #include "nsBindingManager.h"
 #include "nsStyleCoord.h"
 #include "TabChild.h"
 #include "nsFrameLoader.h"
 #include "nsNumberControlFrame.h"
 #include "nsNetUtil.h"
 #include "nsRange.h"
 
+#include "mozilla/AccessibleCaretEventHub.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLSlotElement.h"
 #include "mozilla/dom/Text.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -455,17 +455,17 @@ public:
    * will be empty.
    * @param aOutRanges the resulting set of ranges
    */
   void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
 
   /**
    * Notify the selection listeners after a range has been modified.
    */
-  void NotifySelectionListenersAfterRangeSet();
+  MOZ_CAN_RUN_SCRIPT void NotifySelectionListenersAfterRangeSet();
 
   typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
 protected:
 
   void RegisterCommonAncestor(nsINode* aNode);
   void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
   nsINode* IsValidBoundary(nsINode* aNode) const
   {
@@ -483,16 +483,17 @@ protected:
     return aOffset <= INT32_MAX;
   }
   static bool IsValidOffset(nsINode* aNode, uint32_t aOffset);
 
   // CharacterDataChanged set aNotInsertedYet to true to disable an assertion
   // and suppress re-registering a range common ancestor node since
   // the new text node of a splitText hasn't been inserted yet.
   // CharacterDataChanged does the re-registering when needed.
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void DoSetRange(const RawRangeBoundary& lowerBound,
                   const RawRangeBoundary& upperBound,
                   nsINode* aRoot, bool aNotInsertedYet = false);
 
   /**
    * For a range for which IsInSelection() is true, return the common ancestor
    * for the range, which we had to compute when the common ancestor changed or
    * IsInSelection became true, so we could register with it.  That is, it's a
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -34,17 +34,17 @@ namespace mozilla {
 #define AC_LOG(message, ...)                                                   \
   AC_LOG_BASE("AccessibleCaretEventHub (%p): " message, this, ##__VA_ARGS__);
 
 #undef AC_LOGV
 #define AC_LOGV(message, ...)                                                  \
   AC_LOGV_BASE("AccessibleCaretEventHub (%p): " message, this, ##__VA_ARGS__);
 
 NS_IMPL_ISUPPORTS(AccessibleCaretEventHub, nsIReflowObserver, nsIScrollObserver,
-                  nsISelectionListener, nsISupportsWeakReference);
+                  nsISupportsWeakReference);
 
 // -----------------------------------------------------------------------------
 // NoActionState
 //
 class AccessibleCaretEventHub::NoActionState
   : public AccessibleCaretEventHub::State
 {
 public:
@@ -721,30 +721,32 @@ AccessibleCaretEventHub::ScrollPositionC
   }
 
   MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
 
   AC_LOG("%s, state: %s", __FUNCTION__, mState->Name());
   mState->OnScrollPositionChanged(this);
 }
 
-nsresult
-AccessibleCaretEventHub::NotifySelectionChanged(nsIDocument* aDoc,
-                                                dom::Selection* aSel,
-                                                int16_t aReason)
+void
+AccessibleCaretEventHub::OnSelectionChange(nsIDocument* aDoc,
+                                           dom::Selection* aSel,
+                                           int16_t aReason)
 {
   if (!mInitialized) {
-    return NS_OK;
+    return;
   }
 
   MOZ_ASSERT(mRefCnt.get() > 1, "Expect caller holds us as well!");
 
   AC_LOG("%s, state: %s, reason: %d", __FUNCTION__, mState->Name(), aReason);
+
+  // XXX Here we may be in a hot path.  So, if we could avoid this virtual call,
+  //     we should do so.
   mState->OnSelectionChanged(this, aDoc, aSel, aReason);
-  return NS_OK;
 }
 
 void
 AccessibleCaretEventHub::NotifyBlur(bool aIsLeavingDocument)
 {
   if (!mInitialized) {
     return;
   }
--- a/layout/base/AccessibleCaretEventHub.h
+++ b/layout/base/AccessibleCaretEventHub.h
@@ -1,26 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef AccessibleCaretEventHub_h
-#define AccessibleCaretEventHub_h
+#ifndef mozilla_AccessibleCaretEventHub_h
+#define mozilla_AccessibleCaretEventHub_h
 
 #include "mozilla/EventForwards.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCOMPtr.h"
 #include "nsDocShell.h"
 #include "nsIFrame.h"
 #include "nsIReflowObserver.h"
 #include "nsIScrollObserver.h"
-#include "nsISelectionListener.h"
 #include "nsPoint.h"
 #include "mozilla/RefPtr.h"
 #include "nsWeakReference.h"
 
 class nsIPresShell;
 class nsITimer;
 class nsIDocument;
 
@@ -59,17 +58,16 @@ class WidgetTouchEvent;
 // https://hg.mozilla.org/mozilla-central/file/default/layout/base/doc/AccessibleCaretEventHubStates.dot
 //
 // Please see the wiki page for more information.
 // https://wiki.mozilla.org/AccessibleCaret
 //
 class AccessibleCaretEventHub
   : public nsIReflowObserver
   , public nsIScrollObserver
-  , public nsISelectionListener
   , public nsSupportsWeakReference
 {
 public:
   explicit AccessibleCaretEventHub(nsIPresShell* aPresShell);
   void Init();
   void Terminate();
 
   MOZ_CAN_RUN_SCRIPT
@@ -77,41 +75,40 @@ public:
 
   // Call this function to notify the blur event happened.
   MOZ_CAN_RUN_SCRIPT
   void NotifyBlur(bool aIsLeavingDocument);
 
   NS_DECL_ISUPPORTS
 
   // nsIReflowObserver
-  MOZ_CAN_RUN_SCRIPT
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD Reflow(DOMHighResTimeStamp start,
                     DOMHighResTimeStamp end) final;
-  MOZ_CAN_RUN_SCRIPT
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   NS_IMETHOD ReflowInterruptible(DOMHighResTimeStamp start,
                                  DOMHighResTimeStamp end) final;
 
-  // nsISelectionListener
-  MOZ_CAN_RUN_SCRIPT
-  NS_IMETHOD NotifySelectionChanged(nsIDocument* doc,
-                                    dom::Selection* sel,
-                                    int16_t reason) final;
-
   // Override nsIScrollObserver methods.
-  MOZ_CAN_RUN_SCRIPT
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual void ScrollPositionChanged() override;
   MOZ_CAN_RUN_SCRIPT
   virtual void AsyncPanZoomStarted() override;
   MOZ_CAN_RUN_SCRIPT
   virtual void AsyncPanZoomStopped() override;
 
   // Base state
   class State;
   State* GetState() const;
 
+  MOZ_CAN_RUN_SCRIPT
+  void OnSelectionChange(nsIDocument* aDocument,
+                         dom::Selection* aSelection,
+                         int16_t aReason);
+
 protected:
   virtual ~AccessibleCaretEventHub() = default;
 
 #define MOZ_DECL_STATE_CLASS_GETTER(aClassName)                                \
   class aClassName;                                                            \
   static State* aClassName();
 
 #define MOZ_IMPL_STATE_CLASS_GETTER(aClassName)                                \
@@ -239,9 +236,9 @@ public:
   explicit State() = default;
   virtual ~State() = default;
   State(const State&) = delete;
   State& operator=(const State&) = delete;
 };
 
 } // namespace mozilla
 
-#endif // AccessibleCaretEventHub_h
+#endif // mozilla_AccessibleCaretEventHub_h
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -63,17 +63,17 @@
 #include "nsWindowSizes.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsReadableUtils.h"
 #include "nsIPageSequenceFrame.h"
 #include "nsIPermissionManager.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsCaret.h"
-#include "AccessibleCaretEventHub.h"
+#include "mozilla/AccessibleCaretEventHub.h"
 #include "nsFrameManager.h"
 #include "nsXPCOM.h"
 #include "nsILayoutHistoryState.h"
 #include "nsILineIterator.h" // for ScrollContentIntoView
 #include "PLDHashTable.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/PointerEventBinding.h"
--- a/layout/base/gtest/TestAccessibleCaretEventHub.cpp
+++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
@@ -5,19 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 
 #include <iostream>
 #include <string>
 
-#include "AccessibleCaretEventHub.h"
 #include "AccessibleCaretManager.h"
 #include "gfxPrefs.h"
+#include "mozilla/AccessibleCaretEventHub.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 
 using ::testing::AtLeast;
 using ::testing::DefaultValue;
 using ::testing::Eq;
 using ::testing::InSequence;
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -67,16 +67,17 @@ EXPORTS += [
     'TouchManager.h',
     'Units.h',
     'UnitTransforms.h',
     'WordMovementType.h',
     'ZoomConstraintsClient.h',
 ]
 
 EXPORTS.mozilla += [
+    'AccessibleCaretEventHub.h',
     'ArenaObjectID.h',
     'ArenaRefPtr.h',
     'ArenaRefPtrInlines.h',
     'GeometryUtils.h',
     'OverflowChangedTracker.h',
     'PresShell.h',
     'RestyleManager.h',
     'ScrollStyles.h',
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2231,17 +2231,16 @@ public:
                                                      RefPtr<DrawTarget>& aTarget);
   static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::Element *aElement,
                                                      uint32_t aSurfaceFlags = 0) {
     RefPtr<DrawTarget> target = nullptr;
     return SurfaceFromElement(aElement, aSurfaceFlags, target);
   }
 
   // There are a bunch of callers of SurfaceFromElement.  Just mark it as
-  // MOZ_CAN_RUN_SCRIPT_BOUNDARY for now.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   static SurfaceFromElementResult SurfaceFromElement(nsIImageLoadingContent *aElement,
                                                      uint32_t aSurfaceFlags,
                                                      RefPtr<DrawTarget>& aTarget);
   // Need an HTMLImageElement overload, because otherwise the
   // nsIImageLoadingContent and mozilla::dom::Element overloads are ambiguous
   // for HTMLImageElement.
   static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::HTMLImageElement *aElement,
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -3,32 +3,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* rendering object that goes directly inside the document's scrollbars */
 
 #include "nsCanvasFrame.h"
 
-#include "AccessibleCaretEventHub.h"
 #include "gfxContext.h"
 #include "gfxUtils.h"
 #include "nsContainerFrame.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsCSSRendering.h"
 #include "nsPresContext.h"
 #include "nsPopupSetFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIFrameInlines.h"
 #include "nsIPresShell.h"
 #include "nsDisplayList.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsFrameManager.h"
 #include "gfxPlatform.h"
 #include "nsPrintfCString.h"
+#include "mozilla/AccessibleCaretEventHub.h"
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/dom/AnonymousContent.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/PresShell.h"
 // for focus
 #include "nsIScrollableFrame.h"
 #ifdef DEBUG_CANVAS_FOCUS
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -46,17 +46,16 @@ static NS_DEFINE_CID(kFrameTraversalCID,
 
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Preferences.h"
 
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsCaret.h"
-#include "AccessibleCaretEventHub.h"
 
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
 #include "nsITimer.h"
 // notifications
 #include "nsIDocument.h"
 
@@ -649,23 +648,18 @@ nsFrameSelection::Init(nsIPresShell *aSh
     Preferences::AddBoolVarCache(&sSelectionEventsEnabled,
                                  "dom.select_events.enabled", false);
     Preferences::AddBoolVarCache(&sSelectionEventsOnTextControlsEnabled,
                                  "dom.select_events.textcontrols.enabled", false);
   }
 
   mAccessibleCaretEnabled = aAccessibleCaretEnabled;
   if (mAccessibleCaretEnabled) {
-    RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
-    if (eventHub) {
-      int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
-      if (mDomSelections[index]) {
-        mDomSelections[index]->AddSelectionListener(eventHub);
-      }
-    }
+    int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+    mDomSelections[index]->MaybeNotifyAccessibleCaretEventHub(aShell);
   }
 
   bool plaintextControl = (aLimiter != nullptr);
   bool initSelectEvents = plaintextControl ?
                             sSelectionEventsOnTextControlsEnabled :
                             sSelectionEventsEnabled;
 
   nsIDocument* doc = aShell->GetDocument();
@@ -2863,21 +2857,18 @@ nsFrameSelection::SetDelayedCaretData(Wi
     mDelayedMouseEventValid = false;
   }
 }
 
 void
 nsFrameSelection::DisconnectFromPresShell()
 {
   if (mAccessibleCaretEnabled) {
-    RefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
-    if (eventHub) {
-      int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
-      mDomSelections[index]->RemoveSelectionListener(eventHub);
-    }
+    int8_t index = GetIndexFromSelectionType(SelectionType::eNormal);
+    mDomSelections[index]->StopNotifyingAccessibleCaretEventHub();
   }
 
   StopAutoScrollTimer();
   for (size_t i = 0; i < ArrayLength(mDomSelections); i++) {
     mDomSelections[i]->Clear(nullptr);
   }
   mShell = nullptr;
 }
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -234,17 +234,17 @@ public:
    *  @param aContentOffsetEnd is the content offset of the parent aNewFocus and is specified different
    *                           when you need to select to and include both start and end points
    *  @param aContinueSelection is the flag that tells the selection to keep the old anchor point or not.
    *  @param aMultipleSelection will tell the frame selector to replace /or not the old selection.
    *         cannot coexist with aContinueSelection
    *  @param aHint will tell the selection which direction geometrically to actually show the caret on.
    *         1 = end of this line 0 = beginning of this line
    */
-  /*unsafe*/
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult HandleClick(nsIContent *aNewFocus,
                        uint32_t aContentOffset,
                        uint32_t aContentEndOffset,
                        bool aContinueSelection,
                        bool aMultipleSelection,
                        CaretAssociateHint aHint);
 
   /** HandleDrag extends the selection to contain the frame closest to aPoint.
@@ -355,17 +355,17 @@ public:
                                                        int32_t aContentOffset,
                                                        int32_t aContentLength,
                                                        bool aSlowCheck) const;
 
   /** SetDragState(bool);
    *  sets the drag state to aState for resons of drag state.
    * @param aState is the new state of drag
    */
-  /*unsafe*/
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void SetDragState(bool aState);
 
   /** GetDragState(bool *);
    *  gets the drag state to aState for resons of drag state.
    * @param aState will hold the state of drag
    */
   bool GetDragState() const { return mDragState; }
 
@@ -519,18 +519,17 @@ public:
    * @param aExtend continue selection
    */
   /*unsafe*/
   nsresult IntraLineMove(bool aForward, bool aExtend);
 
   /** Select All will generally be called from the nsiselectioncontroller implementations.
    *  it will select the whole doc
    */
-  /*unsafe*/
-  nsresult SelectAll();
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult SelectAll();
 
   /** Sets/Gets The display selection enum.
    */
   void SetDisplaySelection(int16_t aState) { mDisplaySelection = aState; }
   int16_t GetDisplaySelection() const { return mDisplaySelection; }
 
   /** This method can be used to store the data received during a MouseDown
    *  event so that we can place the caret during the MouseUp event.
@@ -568,18 +567,17 @@ public:
   /** Get the content node that limits the selection
    *  When searching up a nodes for parents, as in a text edit field
    *    in an browser page, we must stop at this node else we reach into the
    *    parent page, which is very bad!
    */
   nsIContent* GetLimiter() const { return mLimiter; }
 
   nsIContent* GetAncestorLimiter() const { return mAncestorLimiter; }
-  /*unsafe*/
-  void SetAncestorLimiter(nsIContent *aLimiter);
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void SetAncestorLimiter(nsIContent* aLimiter);
 
   /** This will tell the frame selection that a double click has been pressed
    *  so it can track abort future drags if inside the same selection
    *  @aDoubleDown has the double click down happened
    */
   void SetMouseDoubleDown(bool aDoubleDown) { mMouseDoubleDownState = aDoubleDown; }
 
   /** This will return whether the double down flag was set.
@@ -633,29 +631,32 @@ public:
   nsresult ConstrainFrameAndPointToAnchorSubtree(nsIFrame* aFrame,
                                                  const nsPoint& aPoint,
                                                  nsIFrame** aRetFrame,
                                                  nsPoint& aRetPoint);
 
   nsFrameSelection();
 
   void StartBatchChanges();
+
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void EndBatchChanges(int16_t aReason = nsISelectionListener::NO_REASON);
 
   /*unsafe*/
   nsresult DeleteFromDocument();
 
   nsIPresShell *GetShell()const  { return mShell; }
 
   void DisconnectFromPresShell();
   nsresult ClearNormalSelection();
 
 private:
   ~nsFrameSelection();
 
+  MOZ_CAN_RUN_SCRIPT
   nsresult TakeFocus(nsIContent *aNewFocus,
                      uint32_t aContentOffset,
                      uint32_t aContentEndOffset,
                      CaretAssociateHint aHint,
                      bool aContinueSelection,
                      bool aMultipleSelection);
 
   void BidiLevelFromMove(nsIPresShell* aPresShell,
@@ -701,30 +702,32 @@ private:
 /*HELPER METHODS*/
   // Whether MoveCaret should use logical or visual movement,
   // or follow the bidi.edit.caret_movement_style preference.
   enum CaretMovementStyle {
     eLogical,
     eVisual,
     eUsePrefStyle
   };
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY
   nsresult     MoveCaret(nsDirection aDirection, bool aContinueSelection,
                          nsSelectionAmount aAmount,
                          CaretMovementStyle aMovementStyle);
 
   nsresult     FetchDesiredPos(nsPoint &aDesiredPos); //the position requested by the Key Handling for up down
   void         InvalidateDesiredPos(); //do not listen to mDesiredPos you must get another.
   void         SetDesiredPos(nsPoint aPos); //set the mDesiredPos
 
   uint32_t     GetBatching() const {return mBatching; }
   bool         GetNotifyFrames() const { return mNotifyFrames; }
   void         SetDirty(bool aDirty=true){if (mBatching) mChangesDuringBatching = aDirty;}
 
   // nsFrameSelection may get deleted when calling this,
   // so remember to use nsCOMPtr when needed.
+  MOZ_CAN_RUN_SCRIPT
   nsresult     NotifySelectionListeners(mozilla::SelectionType aSelectionType);
   // Update the selection cache on repaint when the
   // selection being repainted is not empty.
   nsresult     UpdateSelectionCacheOnRepaintSelection(mozilla::dom::
                                                       Selection* aSel);
 
   // Table selection support.
   nsITableCellLayout* GetCellLayout(nsIContent *aCellContent) const;