Bug 1433345 - part 4: Make EditorBase derived from nsISelectionListener and notify its owning classes of selection change r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 26 Jan 2018 17:25:45 +0900
changeset 748212 437ebf52fe2ee796a1664fbb2d693bf304ae9246
parent 748211 421d672ccb1d3b0ae09cb583795b6750d2213b96
push id97089
push usermasayuki@d-toybox.com
push dateMon, 29 Jan 2018 07:02:13 +0000
reviewersm_kato
bugs1433345
milestone60.0a1
Bug 1433345 - part 4: Make EditorBase derived from nsISelectionListener and notify its owning classes of selection change r?m_kato This patch makes EditorBase derived from nsISelectionListener. Then, we can make IMEContentObserver, TextInputListener, ComposerCommandsUpdater, TypeInState not derived from nsISelectionListener since EditorBase or HTMLEditor can notify them of selection change directly. Additionally, ResizerSelectionListener is not necessary anymore since it just implements nsISelectionListener and calls only a method of HTMLEditor. So, HTMLEditor can call it directly. Note that the order of selection listeners may be different. However, according to what each selection listener does, changing the order isn't problem. MozReview-Commit-ID: 1JXZxQcS0tP
dom/events/IMEContentObserver.cpp
dom/events/IMEContentObserver.h
dom/html/TextInputListener.h
dom/html/nsTextEditorState.cpp
editor/composer/ComposerCommandsUpdater.cpp
editor/composer/ComposerCommandsUpdater.h
editor/composer/nsEditingSession.cpp
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/HTMLEditorObjectResizer.cpp
editor/libeditor/HTMLEditorObjectResizerUtils.h
editor/libeditor/TypeInState.cpp
editor/libeditor/TypeInState.h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -181,22 +181,21 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentObserver)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
     mStartOfRemovingTextRangeCache.mContainerNode)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMEContentObserver)
- NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
  NS_INTERFACE_MAP_ENTRY(nsIReflowObserver)
  NS_INTERFACE_MAP_ENTRY(nsIScrollObserver)
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIReflowObserver)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
 
 IMEContentObserver::IMEContentObserver()
   : mESM(nullptr)
   , mIMENotificationRequests(nullptr)
@@ -471,21 +470,16 @@ IMEContentObserver::ObserveEditableNode(
   }
 
   mIsObserving = true;
   if (mEditorBase) {
     mEditorBase->SetIMEContentObserver(this);
   }
 
   if (!WasInitializedWithPlugin()) {
-    // Add selection change listener only when this starts to observe
-    // non-plugin content since we cannot detect selection changes in
-    // plugins.
-    nsresult rv = mSelection->AddSelectionListener(this);
-    NS_ENSURE_SUCCESS_VOID(rv);
     // Add text change observer only when this starts to observe
     // non-plugin content since we cannot detect text changes in
     // plugins.
     mRootContent->AddMutationObserver(this);
     // If it's in a document (should be so), we can use document observer to
     // reduce redundant computation of text change offsets.
     nsIDocument* doc = mRootContent->GetComposedDoc();
     if (doc) {
@@ -548,17 +542,16 @@ IMEContentObserver::UnregisterObservers(
   }
   mIsObserving = false;
 
   if (mEditorBase) {
     mEditorBase->SetIMEContentObserver(nullptr);
   }
 
   if (mSelection) {
-    mSelection->RemoveSelectionListener(this);
     mSelectionData.Clear();
     mFocusedWidget = nullptr;
   }
 
   if (mRootContent) {
     mRootContent->RemoveMutationObserver(this);
   }
 
@@ -711,33 +704,31 @@ IMEContentObserver::GetSelectionAndRoot(
   }
 
   NS_ASSERTION(mSelection && mRootContent, "uninitialized content observer");
   NS_ADDREF(*aSelection = mSelection);
   NS_ADDREF(*aRootContent = mRootContent);
   return NS_OK;
 }
 
-nsresult
-IMEContentObserver::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
-                                           nsISelection* aSelection,
-                                           int16_t aReason)
+void
+IMEContentObserver::OnSelectionChange(Selection& aSelection)
 {
-  int32_t count = 0;
-  nsresult rv = aSelection->GetRangeCount(&count);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (count > 0 && mWidget) {
+  if (!mIsObserving) {
+    return;
+  }
+
+  if (aSelection.RangeCount() && mWidget) {
     bool causedByComposition = IsEditorHandlingEventForComposition();
     bool causedBySelectionEvent = TextComposition::IsHandlingSelectionEvent();
     bool duringComposition = IsEditorComposing();
     MaybeNotifyIMEOfSelectionChange(causedByComposition,
                                     causedBySelectionEvent,
                                     duringComposition);
   }
-  return NS_OK;
 }
 
 void
 IMEContentObserver::ScrollPositionChanged()
 {
   if (!NeedsPositionChangeNotification()) {
     return;
   }
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -9,17 +9,16 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EditorBase.h"
 #include "mozilla/dom/Selection.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocShell.h" // XXX Why does only this need to be included here?
 #include "nsIReflowObserver.h"
-#include "nsISelectionListener.h"
 #include "nsIScrollObserver.h"
 #include "nsIWidget.h"
 #include "nsStubDocumentObserver.h"
 #include "nsStubMutationObserver.h"
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 
 class nsIContent;
@@ -27,49 +26,56 @@ class nsINode;
 class nsISelection;
 class nsPresContext;
 
 namespace mozilla {
 
 class EventStateManager;
 class TextComposition;
 
+namespace dom {
+class Selection;
+} // namespace dom
+
 // IMEContentObserver notifies widget of any text and selection changes
 // in the currently focused editor
-class IMEContentObserver final : public nsISelectionListener
-                               , public nsStubMutationObserver
+class IMEContentObserver final : public nsStubMutationObserver
                                , public nsIReflowObserver
                                , public nsIScrollObserver
                                , public nsSupportsWeakReference
 {
 public:
   typedef widget::IMENotification::SelectionChangeData SelectionChangeData;
   typedef widget::IMENotification::TextChangeData TextChangeData;
   typedef widget::IMENotification::TextChangeDataBase TextChangeDataBase;
   typedef widget::IMENotificationRequests IMENotificationRequests;
   typedef widget::IMEMessage IMEMessage;
 
   IMEContentObserver();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IMEContentObserver,
-                                           nsISelectionListener)
-  NS_DECL_NSISELECTIONLISTENER
+                                           nsIReflowObserver)
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIREFLOWOBSERVER
 
   // nsIScrollObserver
   virtual void ScrollPositionChanged() override;
 
+  /**
+   * OnSelectionChange() is called when selection is changed in the editor.
+   */
+  void OnSelectionChange(dom::Selection& aSelection);
+
   bool OnMouseButtonEvent(nsPresContext* aPresContext,
                           WidgetMouseEvent* aMouseEvent);
 
   nsresult HandleQueryContentEvent(WidgetQueryContentEvent* aEvent);
 
   /**
    * Init() initializes the instance, i.e., retrieving necessary objects and
    * starts to observe something.
@@ -331,24 +337,24 @@ private:
       eChangeEventType_CompositionEventHandled
     };
 
     explicit AChangeEvent(const char* aName,
                           IMEContentObserver* aIMEContentObserver)
       : Runnable(aName)
       , mIMEContentObserver(
           do_GetWeakReference(
-            static_cast<nsISelectionListener*>(aIMEContentObserver)))
+            static_cast<nsIReflowObserver*>(aIMEContentObserver)))
     {
       MOZ_ASSERT(aIMEContentObserver);
     }
 
     already_AddRefed<IMEContentObserver> GetObserver() const
     {
-      nsCOMPtr<nsISelectionListener> observer =
+      nsCOMPtr<nsIReflowObserver> observer =
         do_QueryReferent(mIMEContentObserver);
       return observer.forget().downcast<IMEContentObserver>();
     }
 
     nsWeakPtr mIMEContentObserver;
 
     /**
      * CanNotifyIME() checks if mIMEContentObserver can and should notify IME.
--- a/dom/html/TextInputListener.h
+++ b/dom/html/TextInputListener.h
@@ -4,29 +4,27 @@
  * 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_TextInputListener_h
 #define mozilla_TextInputListener_h
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMEventListener.h"
-#include "nsISelectionListener.h"
 #include "nsStringFwd.h"
 #include "nsWeakReference.h"
 
 class nsIFrame;
 class nsISelection;
 class nsITextControlElement;
 class nsTextControlFrame;
 
 namespace mozilla {
 
-class TextInputListener final : public nsISelectionListener
-                              , public nsIDOMEventListener
+class TextInputListener final : public nsIDOMEventListener
                               , public nsSupportsWeakReference
 {
 public:
   explicit TextInputListener(nsITextControlElement* aTextControlElement);
 
   void SetFrame(nsIFrame* aTextControlFrame)
   {
     mFrame = aTextControlFrame;
@@ -46,20 +44,36 @@ public:
    */
   void HandleValueChanged(nsTextControlFrame* aFrame = nullptr);
 
   /**
    * OnEditActionHandled() is called when the editor handles each edit action.
    */
   void OnEditActionHandled();
 
+  /**
+   * OnSelectionChange() is called when selection is changed in the editor.
+   */
+  void OnSelectionChange(Selection& aSelection, int16_t aReason);
+
+  /**
+   * Start to listen or end listening to selection change in the editor.
+   */
+  void StartToListenToSelectionChange()
+  {
+    mListeningToSelectionChange = true;
+  }
+  void EndListeningToSelectionChange()
+  {
+    mListeningToSelectionChange = false;
+  }
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TextInputListener,
-                                           nsISelectionListener)
-  NS_DECL_NSISELECTIONLISTENER
+                                           nsIDOMEventListener)
   NS_DECL_NSIDOMEVENTLISTENER
 
 protected:
   virtual ~TextInputListener() = default;
 
   nsresult UpdateTextInputCommands(const nsAString& aCommandsToUpdate,
                                    nsISelection* aSelection = nullptr,
                                    int16_t aReason = 0);
@@ -85,13 +99,17 @@ protected:
    * refrain from calling OnValueChanged.
    */
   bool mSettingValue;
   /**
    * Whether we are in the process of a SetValue call that doesn't want
    * |SetValueChanged| to be called.
    */
   bool mSetValueChanged;
+  /**
+   * Whether we're listening to selection change in the editor.
+   */
+  bool mListeningToSelectionChange;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef mozilla_TextInputListener_h
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -797,61 +797,57 @@ nsTextInputSelectionImpl::CheckVisibilit
 TextInputListener::TextInputListener(nsITextControlElement* aTxtCtrlElement)
   : mFrame(nullptr)
   , mTxtCtrlElement(aTxtCtrlElement)
   , mSelectionWasCollapsed(true)
   , mHadUndoItems(false)
   , mHadRedoItems(false)
   , mSettingValue(false)
   , mSetValueChanged(true)
+  , mListeningToSelectionChange(false)
 {
 }
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextInputListener)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextInputListener)
 
 NS_INTERFACE_MAP_BEGIN(TextInputListener)
-  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(TextInputListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_0(TextInputListener)
 
-// BEGIN nsIDOMSelectionListener
-
-NS_IMETHODIMP
-TextInputListener::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
-                                          nsISelection* aSelection,
-                                          int16_t aReason)
+void
+TextInputListener::OnSelectionChange(Selection& aSelection,
+                                     int16_t aReason)
 {
-  bool collapsed;
-  AutoWeakFrame weakFrame = mFrame;
+  if (!mListeningToSelectionChange) {
+    return;
+  }
 
-  if (!aDOMDocument || !aSelection ||
-      NS_FAILED(aSelection->GetIsCollapsed(&collapsed))) {
-    return NS_OK;
-  }
+  AutoWeakFrame weakFrame = mFrame;
 
   // Fire the select event
   // The specs don't exactly say when we should fire the select event.
   // IE: Whenever you add/remove a character to/from the selection. Also
   //     each time for select all. Also if you get to the end of the text
   //     field you will get new event for each keypress or a continuous
   //     stream of events if you use the mouse. IE will fire select event
   //     when the selection collapses to nothing if you are holding down
   //     the shift or mouse button.
   // Mozilla: If we have non-empty selection we will fire a new event for each
   //          keypress (or mouseup) if the selection changed. Mozilla will also
   //          create the event each time select all is called, even if everything
   //          was previously selected, becase technically select all will first collapse
   //          and then extend. Mozilla will never create an event if the selection
   //          collapses to nothing.
+  bool collapsed = aSelection.IsCollapsed();
   if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON |
                                 nsISelectionListener::KEYPRESS_REASON |
                                 nsISelectionListener::SELECTALL_REASON))) {
     nsIContent* content = mFrame->GetContent();
     if (content) {
       nsCOMPtr<nsIDocument> doc = content->GetComposedDoc();
       if (doc) {
         nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
@@ -862,32 +858,30 @@ TextInputListener::NotifySelectionChange
           presShell->HandleEventWithTarget(&event, mFrame, content, &status);
         }
       }
     }
   }
 
   // if the collapsed state did not change, don't fire notifications
   if (collapsed == mSelectionWasCollapsed) {
-    return NS_OK;
+    return;
   }
 
   mSelectionWasCollapsed = collapsed;
 
   if (!weakFrame.IsAlive() ||
       !nsContentUtils::IsFocusedContent(mFrame->GetContent())) {
-    return NS_OK;
+    return;
   }
 
-  return UpdateTextInputCommands(NS_LITERAL_STRING("select"),
-                                 aSelection, aReason);
+  UpdateTextInputCommands(NS_LITERAL_STRING("select"),
+                          &aSelection, aReason);
 }
 
-// END nsIDOMSelectionListener
-
 static void
 DoCommandCallback(Command aCommand, void* aData)
 {
   nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
   nsIContent *content = frame->GetContent();
 
   nsCOMPtr<nsIControllers> controllers;
   nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(content);
@@ -1258,17 +1252,17 @@ nsTextEditorState::BindToFrame(nsTextCon
   //      Selection::AddSelectionListner() because it only appends the listener
   //      to its internal array.
   Selection* selection = mSelCon->GetSelection(SelectionType::eNormal);
   if (selection) {
     RefPtr<nsCaret> caret = shell->GetCaret();
     if (caret) {
       selection->AddSelectionListener(caret);
     }
-    selection->AddSelectionListener(mTextListener);
+    mTextListener->StartToListenToSelectionChange();
   }
 
   // If an editor exists from before, prepare it for usage
   if (mTextEditor) {
     nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
     NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
 
     // Set the correct direction on the newly created root node
@@ -2107,24 +2101,17 @@ nsTextEditorState::UnbindFromFrame(nsTex
           }
         }
       }
     }
   }
 
   if (mSelCon) {
     if (mTextListener) {
-      // FYI: It's safe to use raw pointer for calling
-      //      Selection::RemoveSelectionListener() because it only removes the
-      //      listener from its array.
-      Selection* selection =
-        mSelCon->GetSelection(SelectionType::eNormal);
-      if (selection) {
-        selection->RemoveSelectionListener(mTextListener);
-      }
+      mTextListener->EndListeningToSelectionChange();
     }
 
     mSelCon->SetScrollableFrame(nullptr);
     mSelCon = nullptr;
   }
 
   if (mTextListener)
   {
--- a/editor/composer/ComposerCommandsUpdater.cpp
+++ b/editor/composer/ComposerCommandsUpdater.cpp
@@ -18,17 +18,16 @@
 #include "nsIDocShell.h"                // for nsIDocShell
 #include "nsIInterfaceRequestorUtils.h"  // for do_GetInterface
 #include "nsISelection.h"               // for nsISelection
 #include "nsITransactionManager.h"      // for nsITransactionManager
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsPICommandUpdater.h"         // for nsPICommandUpdater
 #include "nsPIDOMWindow.h"              // for nsPIDOMWindow
 
-class nsIDOMDocument;
 class nsITransaction;
 
 namespace mozilla {
 
 ComposerCommandsUpdater::ComposerCommandsUpdater()
   : mDirtyState(eStateUninitialized)
   , mSelectionCollapsed(eStateUninitialized)
   , mFirstDoOfFirstUndo(true)
@@ -42,17 +41,16 @@ ComposerCommandsUpdater::~ComposerComman
     mUpdateTimer->Cancel();
   }
 }
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ComposerCommandsUpdater)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ComposerCommandsUpdater)
 
 NS_INTERFACE_MAP_BEGIN(ComposerCommandsUpdater)
-  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentStateListener)
   NS_INTERFACE_MAP_ENTRY(nsITransactionListener)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY(nsINamed)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentStateListener)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ComposerCommandsUpdater)
 NS_INTERFACE_MAP_END
 
@@ -94,24 +92,16 @@ ComposerCommandsUpdater::NotifyDocumentW
 
 NS_IMETHODIMP
 ComposerCommandsUpdater::NotifyDocumentStateChanged(bool aNowDirty)
 {
   // update document modified. We should have some other notifications for this too.
   return UpdateDirtyState(aNowDirty);
 }
 
-NS_IMETHODIMP
-ComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
-                                                nsISelection* aSelection,
-                                                int16_t aReason)
-{
-  return PrimeUpdateTimer();
-}
-
 #if 0
 #pragma mark -
 #endif
 
 NS_IMETHODIMP
 ComposerCommandsUpdater::WillDo(nsITransactionManager* aManager,
                                 nsITransaction* aTransaction,
                                 bool* aInterrupt)
--- a/editor/composer/ComposerCommandsUpdater.h
+++ b/editor/composer/ComposerCommandsUpdater.h
@@ -6,62 +6,65 @@
 
 #ifndef mozilla_ComposerCommandsUpdater_h
 #define mozilla_ComposerCommandsUpdater_h
 
 #include "nsCOMPtr.h"                   // for already_AddRefed, nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDocumentStateListener.h"
 #include "nsINamed.h"
-#include "nsISelectionListener.h"
 #include "nsISupportsImpl.h"            // for NS_DECL_ISUPPORTS
 #include "nsITimer.h"                   // for NS_DECL_NSITIMERCALLBACK, etc
 #include "nsITransactionListener.h"     // for nsITransactionListener
 #include "nsIWeakReferenceUtils.h"      // for nsWeakPtr
 #include "nscore.h"                     // for NS_IMETHOD, nsresult, etc
 
 class nsIDocShell;
 class nsITransaction;
 class nsITransactionManager;
 class nsPIDOMWindowOuter;
 class nsPICommandUpdater;
 
 namespace mozilla {
 
-class ComposerCommandsUpdater final : public nsISelectionListener
-                                    , public nsIDocumentStateListener
+class ComposerCommandsUpdater final : public nsIDocumentStateListener
                                     , public nsITransactionListener
                                     , public nsITimerCallback
                                     , public nsINamed
 {
 public:
   ComposerCommandsUpdater();
 
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ComposerCommandsUpdater,
                                            nsIDocumentStateListener)
 
-  // nsISelectionListener
-  NS_DECL_NSISELECTIONLISTENER
-
   // nsIDocumentStateListener
   NS_DECL_NSIDOCUMENTSTATELISTENER
 
   // nsITimerCallback
   NS_DECL_NSITIMERCALLBACK
 
   // nsINamed
   NS_DECL_NSINAMED
 
   // nsITransactionListener
   NS_DECL_NSITRANSACTIONLISTENER
 
   nsresult Init(nsPIDOMWindowOuter* aDOMWindow);
 
+  /**
+   * OnSelectionChange() is called when selection is changed in the editor.
+   */
+  void OnSelectionChange()
+  {
+    PrimeUpdateTimer();
+  }
+
 protected:
   virtual ~ComposerCommandsUpdater();
 
   enum
   {
     eStateUninitialized = -1,
     eStateOff = 0,
     eStateOn = 1,
--- a/editor/composer/nsEditingSession.cpp
+++ b/editor/composer/nsEditingSession.cpp
@@ -462,20 +462,16 @@ nsEditingSession::SetupEditorOnWindow(mo
                         nullptr, mEditorFlags, EmptyString());
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<Selection> selection = htmlEditor->GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
-  rv = selection->AddSelectionListener(mComposerCommandsUpdater);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
   htmlEditor->SetComposerCommandsUpdater(mComposerCommandsUpdater);
 
   // and as a transaction listener
   nsCOMPtr<nsITransactionManager> txnMgr;
   htmlEditor->GetTransactionManager(getter_AddRefs(txnMgr));
   if (txnMgr) {
     txnMgr->AddListener(mComposerCommandsUpdater);
   }
@@ -496,20 +492,16 @@ void
 nsEditingSession::RemoveListenersAndControllers(nsPIDOMWindowOuter* aWindow,
                                                 HTMLEditor* aHTMLEditor)
 {
   if (!mComposerCommandsUpdater || !aHTMLEditor) {
     return;
   }
 
   // Remove all the listeners
-  RefPtr<Selection> selection = aHTMLEditor->GetSelection();
-  if (selection) {
-    selection->RemoveSelectionListener(mComposerCommandsUpdater);
-  }
   aHTMLEditor->SetComposerCommandsUpdater(nullptr);
 
   aHTMLEditor->RemoveDocumentStateListener(mComposerCommandsUpdater);
 
   nsCOMPtr<nsITransactionManager> txnMgr;
   aHTMLEditor->GetTransactionManager(getter_AddRefs(txnMgr));
   if (txnMgr) {
     txnMgr->RemoveListener(mComposerCommandsUpdater);
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -205,16 +205,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderTransaction)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSavedSel);
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRangeUpdater);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EditorBase)
+ NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  NS_INTERFACE_MAP_ENTRY(nsIEditor)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(EditorBase)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorBase)
 
@@ -278,16 +279,21 @@ EditorBase::Init(nsIDOMDocument* aDOMDoc
   selectionController->SetCaretReadOnly(false);
   selectionController->SetDisplaySelection(
                          nsISelectionController::SELECTION_ON);
   // Show all the selection reflected to user.
   selectionController->SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
 
   MOZ_ASSERT(IsInitialized());
 
+  Selection* selection = GetSelection();
+  if (selection) {
+    selection->AddSelectionListener(this);
+  }
+
   // Make sure that the editor will be destroyed properly
   mDidPreDestroy = false;
   // Make sure that the ediotr will be created properly
   mDidPostCreate = false;
 
   return NS_OK;
 }
 
@@ -465,16 +471,21 @@ EditorBase::GetDesiredSpellCheckState()
 }
 
 NS_IMETHODIMP
 EditorBase::PreDestroy(bool aDestroyingFrames)
 {
   if (mDidPreDestroy)
     return NS_OK;
 
+  Selection* selection = GetSelection();
+  if (selection) {
+    selection->RemoveSelectionListener(this);
+  }
+
   IMEStateManager::OnEditorDestroying(*this);
 
   // Let spellchecker clean up its observers etc. It is important not to
   // actually free the spellchecker here, since the spellchecker could have
   // caused flush notifications, which could have gotten here if a textbox
   // is being removed. Setting the spellchecker to nullptr could free the
   // object that is still in use! It will be freed when the editor is
   // destroyed.
@@ -2110,16 +2121,43 @@ EditorBase::RemoveEditorObserver(nsIEdit
 
   NS_WARNING_ASSERTION(mEditorObservers.Length() != 1,
     "All nsIEditorObservers have been removed, this editor becomes faster");
   mEditorObservers.RemoveElement(aObserver);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+EditorBase::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
+                                   nsISelection* aSelection,
+                                   int16_t aReason)
+{
+  if (NS_WARN_IF(!aDOMDocument) || NS_WARN_IF(!aSelection)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  RefPtr<Selection> selection = aSelection->AsSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (mTextInputListener) {
+    RefPtr<TextInputListener> textInputListener = mTextInputListener;
+    textInputListener->OnSelectionChange(*selection, aReason);
+  }
+
+  if (mIMEContentObserver) {
+    RefPtr<IMEContentObserver> observer = mIMEContentObserver;
+    observer->OnSelectionChange(*selection);
+  }
+
+  return NS_OK;
+}
+
 class EditorInputEventDispatcher final : public Runnable
 {
 public:
   EditorInputEventDispatcher(EditorBase* aEditorBase,
                              nsIContent* aTarget,
                              bool aIsComposing)
     : Runnable("EditorInputEventDispatcher")
     , mEditorBase(aEditorBase)
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -21,16 +21,17 @@
 #include "nsCOMPtr.h"                   // for already_AddRefed, nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"                // for nsIDocument
 #include "nsIEditor.h"                  // for nsIEditor, etc.
 #include "nsIObserver.h"                // for NS_DECL_NSIOBSERVER, etc.
 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc.
 #include "nsISelectionController.h"     // for nsISelectionController constants
+#include "nsISelectionListener.h"       // for nsISelectionListener
 #include "nsISupportsImpl.h"            // for EditorBase::Release, etc.
 #include "nsIWeakReferenceUtils.h"      // for nsWeakPtr
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsString.h"                   // for nsCString
 #include "nsTArray.h"                   // for nsTArray and nsAutoTArray
 #include "nsWeakReference.h"            // for nsSupportsWeakReference
 #include "nscore.h"                     // for nsresult, nsAString, etc.
 
@@ -183,16 +184,17 @@ enum class SplitAtEdges
 /**
  * Implementation of an editor object.  it will be the controller/focal point
  * for the main editor services. i.e. the GUIManager, publishing, transaction
  * manager, event interfaces. the idea for the event interfaces is to have them
  * delegate the actual commands to the editor independent of the XPFE
  * implementation.
  */
 class EditorBase : public nsIEditor
+                 , public nsISelectionListener
                  , public nsSupportsWeakReference
 {
 public:
   typedef dom::Element Element;
   typedef dom::Selection Selection;
   typedef dom::Text Text;
 
   enum IterDirection
@@ -249,16 +251,19 @@ public:
     eNotifyEditorObserversOfBefore,
     eNotifyEditorObserversOfCancel
   };
   void NotifyEditorObservers(NotificationForEditorObservers aNotification);
 
   // nsIEditor methods
   NS_DECL_NSIEDITOR
 
+  // nsISelectionListener method
+  NS_DECL_NSISELECTIONLISTENER
+
   /**
    * Set or unset TextInputListener.  If setting non-nullptr when the editor
    * already has a TextInputListener, this will crash in debug build.
    */
   void SetTextInputListener(TextInputListener* aTextInputListener);
 
   /**
    * Set or unset IMEContentObserver.  If setting non-nullptr when the editor
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -136,31 +136,17 @@ HTMLEditor::HTMLEditor()
 }
 
 HTMLEditor::~HTMLEditor()
 {
   if (mRules && mRules->AsHTMLEditRules()) {
     mRules->AsHTMLEditRules()->EndListeningToEditActions();
   }
 
-  //the autopointers will clear themselves up.
-  //but we need to also remove the listeners or we have a leak
-  RefPtr<Selection> selection = GetSelection();
-  // if we don't get the selection, just skip this
-  if (selection) {
-    if (mTypeInState) {
-      selection->RemoveSelectionListener(mTypeInState);
-    }
-    if (mResizerSelectionListener) {
-      selection->RemoveSelectionListener(mResizerSelectionListener);
-    }
-  }
-
   mTypeInState = nullptr;
-  mResizerSelectionListener = nullptr;
 
   if (mLinkHandler && IsInitialized()) {
     nsCOMPtr<nsIPresShell> ps = GetPresShell();
 
     if (ps && ps->GetPresContext()) {
       ps->GetPresContext()->SetLinkHandler(mLinkHandler);
     }
   }
@@ -194,17 +180,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   tmp->HideAnonymousEditingUIs();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinkHandler)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLEditor, TextEditor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTypeInState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mComposerCommandsUpdater)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResizerSelectionListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLeftHandle)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopHandle)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopRightHandle)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLeftHandle)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRightHandle)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBottomLeftHandle)
@@ -294,33 +279,20 @@ HTMLEditor::Init(nsIDOMDocument* aDoc,
     if (!IsPlaintextEditor() && !IsInteractionAllowed()) {
       mLinkHandler = context->GetLinkHandler();
       context->SetLinkHandler(nullptr);
     }
 
     // init the type-in state
     mTypeInState = new TypeInState();
 
-    // init the selection listener for image resizing
-    mResizerSelectionListener = new ResizerSelectionListener(*this);
-
     if (!IsInteractionAllowed()) {
       // ignore any errors from this in case the file is missing
       AddOverrideStyleSheet(NS_LITERAL_STRING("resource://gre/res/EditorOverride.css"));
     }
-
-    RefPtr<Selection> selection = GetSelection();
-    if (selection) {
-      if (mTypeInState) {
-        selection->AddSelectionListener(mTypeInState);
-      }
-      if (mResizerSelectionListener) {
-        selection->AddSelectionListener(mResizerSelectionListener);
-      }
-    }
   }
   NS_ENSURE_SUCCESS(rulesRv, rulesRv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::PreDestroy(bool aDestroyingFrames)
@@ -340,16 +312,58 @@ HTMLEditor::PreDestroy(bool aDestroyingF
 
   // Clean up after our anonymous content -- we don't want these nodes to
   // stay around (which they would, since the frames have an owning reference).
   HideAnonymousEditingUIs();
 
   return TextEditor::PreDestroy(aDestroyingFrames);
 }
 
+NS_IMETHODIMP
+HTMLEditor::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
+                                   nsISelection* aSelection,
+                                   int16_t aReason)
+{
+  if (NS_WARN_IF(!aDOMDocument) || NS_WARN_IF(!aSelection)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  RefPtr<Selection> selection = aSelection->AsSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (mTypeInState) {
+    RefPtr<TypeInState> typeInState = mTypeInState;
+    typeInState->OnSelectionChange(*selection);
+
+    // We used a class which derived from nsISelectionListener to call
+    // HTMLEditor::CheckSelectionStateForAnonymousButtons().  The lifetime of
+    // the class was exactly same as mTypeInState.  So, call it only when
+    // mTypeInState is not nullptr.
+    if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
+                    nsISelectionListener::KEYPRESS_REASON |
+                    nsISelectionListener::SELECTALL_REASON)) && aSelection) {
+      // the selection changed and we need to check if we have to
+      // hide and/or redisplay resizing handles
+      // FYI: This is an XPCOM method.  So, the caller, Selection, guarantees
+      //      the lifetime of this instance.  So, don't need to grab this with
+      //      local variable.
+      CheckSelectionStateForAnonymousButtons(selection);
+    }
+  }
+
+  if (mComposerCommandsUpdater) {
+    RefPtr<ComposerCommandsUpdater> updater = mComposerCommandsUpdater;
+    updater->OnSelectionChange();
+  }
+
+  return EditorBase::NotifySelectionChanged(aDOMDocument, aSelection, aReason);
+}
+
 void
 HTMLEditor::UpdateRootElement()
 {
   // Use the HTML documents body element as the editor root if we didn't
   // get a root element during initialization.
 
   nsCOMPtr<nsIDOMElement> rootElement;
   nsCOMPtr<nsIDOMHTMLElement> bodyElement;
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -25,30 +25,31 @@
 #include "nsIDOMEventListener.h"
 #include "nsIEditorMailSupport.h"
 #include "nsIEditorStyleSheets.h"
 #include "nsIEditorUtils.h"
 #include "nsIHTMLAbsPosEditor.h"
 #include "nsIHTMLEditor.h"
 #include "nsIHTMLInlineTableEditor.h"
 #include "nsIHTMLObjectResizer.h"
-#include "nsISelectionListener.h"
 #include "nsITableEditor.h"
 #include "nsPoint.h"
 #include "nsStubMutationObserver.h"
 #include "nsTArray.h"
 
 class nsDocumentFragment;
 class nsITransferable;
 class nsIClipboard;
+class nsIDOMDocument;
 class nsIDOMMouseEvent;
 class nsILinkHandler;
 class nsTableWrapperFrame;
 class nsIDOMRange;
 class nsRange;
+class nsISelection;
 
 namespace mozilla {
 class AutoSelectionSetterAfterTableEdit;
 class HTMLEditorEventListener;
 class HTMLEditRules;
 class ResizerSelectionListener;
 class TypeInState;
 class WSRunObject;
@@ -103,16 +104,21 @@ public:
   HTMLEditor();
 
   bool GetReturnInParagraphCreatesNewParagraph();
   Element* GetSelectionContainer();
 
   // nsIEditor overrides
   NS_IMETHOD GetPreferredIMEState(widget::IMEState* aState) override;
 
+  // nsISelectionListener overrides
+  NS_IMETHOD NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
+                                    nsISelection* aSelection,
+                                    int16_t aReason) override;
+
   // TextEditor overrides
   NS_IMETHOD BeginningOfDocument() override;
   virtual nsresult HandleKeyPressEvent(
                      WidgetKeyboardEvent* aKeyboardEvent) override;
   virtual nsIContent* GetFocusedContent() override;
   virtual already_AddRefed<nsIContent> GetFocusedContentForIME() override;
   virtual bool IsActiveInDOMWindow() override;
   virtual dom::EventTarget* GetDOMEventTarget() override;
@@ -985,17 +991,16 @@ protected:
    */
   void SetSelectionAfterTableEdit(nsIDOMElement* aTable,
                                   int32_t aRow, int32_t aCol,
                                   int32_t aDirection, bool aSelected);
 
 protected:
   RefPtr<TypeInState> mTypeInState;
   RefPtr<ComposerCommandsUpdater> mComposerCommandsUpdater;
-  RefPtr<ResizerSelectionListener> mResizerSelectionListener;
 
   bool mCRInParagraphCreatesParagraph;
 
   bool mCSSAware;
   UniquePtr<CSSEditUtils> mCSSEditUtils;
 
   // Used by GetFirstSelectedCell and GetNextSelectedCell
   int32_t  mSelectedCellIndex;
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -63,59 +63,16 @@ DocumentResizeEventListener::HandleEvent
   RefPtr<HTMLEditor> htmlEditor = mHTMLEditorWeak.get();
   if (htmlEditor) {
     return htmlEditor->RefreshResizers();
   }
   return NS_OK;
 }
 
 /******************************************************************************
- * mozilla::ResizerSelectionListener
- ******************************************************************************/
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizerSelectionListener)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizerSelectionListener)
-
-NS_INTERFACE_MAP_BEGIN(ResizerSelectionListener)
-  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
-  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ResizerSelectionListener)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION(ResizerSelectionListener,
-                         mHTMLEditor)
-
-ResizerSelectionListener::ResizerSelectionListener(HTMLEditor& aHTMLEditor)
-  : mHTMLEditor(&aHTMLEditor)
-{
-}
-
-NS_IMETHODIMP
-ResizerSelectionListener::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
-                                                 nsISelection* aSelection,
-                                                 int16_t aReason)
-{
-  if (!mHTMLEditor) {
-    return NS_OK;
-  }
-  if ((aReason & (nsISelectionListener::MOUSEDOWN_REASON |
-                  nsISelectionListener::KEYPRESS_REASON |
-                  nsISelectionListener::SELECTALL_REASON)) && aSelection) {
-    // the selection changed and we need to check if we have to
-    // hide and/or redisplay resizing handles
-    RefPtr<HTMLEditor> htmlEditor = mHTMLEditor;
-    if (htmlEditor) {
-      htmlEditor->CheckSelectionStateForAnonymousButtons(aSelection);
-    }
-  }
-
-  return NS_OK;
-}
-
-/******************************************************************************
  * mozilla::ResizerMouseMotionListener
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(ResizerMouseMotionListener, nsIDOMEventListener)
 
 ResizerMouseMotionListener::ResizerMouseMotionListener(HTMLEditor& aHTMLEditor)
   : mHTMLEditorWeak(&aHTMLEditor)
 {
--- a/editor/libeditor/HTMLEditorObjectResizerUtils.h
+++ b/editor/libeditor/HTMLEditorObjectResizerUtils.h
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HTMLEditorObjectResizerUtils_h
 #define HTMLEditorObjectResizerUtils_h
 
 #include "mozilla/HTMLEditor.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMEventListener.h"
-#include "nsISelectionListener.h"
 #include "nsISupportsImpl.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsLiteralString.h"
 
 class nsIHTMLEditor;
 
 namespace mozilla {
 
@@ -23,37 +22,16 @@ namespace mozilla {
 #define kTopRight      NS_LITERAL_STRING("ne")
 #define kLeft          NS_LITERAL_STRING("w")
 #define kRight         NS_LITERAL_STRING("e")
 #define kBottomLeft    NS_LITERAL_STRING("sw")
 #define kBottom        NS_LITERAL_STRING("s")
 #define kBottomRight   NS_LITERAL_STRING("se")
 
 /******************************************************************************
- * mozilla::ResizerSelectionListener
- ******************************************************************************/
-
-class ResizerSelectionListener final : public nsISelectionListener
-{
-public:
-  explicit ResizerSelectionListener(HTMLEditor& aHTMLEditor);
-  void Reset();
-
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ResizerSelectionListener,
-                                           nsISelectionListener)
-
-  NS_DECL_NSISELECTIONLISTENER
-
-protected:
-  virtual ~ResizerSelectionListener() = default;
-  RefPtr<HTMLEditor> mHTMLEditor;
-};
-
-/******************************************************************************
  * mozilla::ResizerMouseMotionListener
  ******************************************************************************/
 
 class ResizerMouseMotionListener final : public nsIDOMEventListener
 {
 public:
   explicit ResizerMouseMotionListener(HTMLEditor& aHTMLEditor);
 
--- a/editor/libeditor/TypeInState.cpp
+++ b/editor/libeditor/TypeInState.cpp
@@ -31,23 +31,28 @@ class nsIDOMDocument;
 namespace mozilla {
 
 using namespace dom;
 
 /********************************************************************
  * mozilla::TypeInState
  *******************************************************************/
 
-NS_IMPL_CYCLE_COLLECTION(TypeInState, mLastSelectionContainer)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(TypeInState)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(TypeInState)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TypeInState)
-  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTION_CLASS(TypeInState)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TypeInState)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLastSelectionContainer)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TypeInState)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastSelectionContainer)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(TypeInState, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(TypeInState, Release)
 
 TypeInState::TypeInState()
   : mRelativeFontSize(0)
   , mLastSelectionOffset(0)
 {
   Reset();
 }
 
@@ -68,64 +73,56 @@ TypeInState::UpdateSelState(Selection* a
     return NS_OK;
   }
 
   return EditorBase::GetStartNodeAndOffset(
                        aSelection, getter_AddRefs(mLastSelectionContainer),
                        &mLastSelectionOffset);
 }
 
-
-NS_IMETHODIMP
-TypeInState::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
-                                    nsISelection* aSelection,
-                                    int16_t aReason)
+void
+TypeInState::OnSelectionChange(Selection& aSelection)
 {
   // XXX: Selection currently generates bogus selection changed notifications
   // XXX: (bug 140303). It can notify us when the selection hasn't actually
   // XXX: changed, and it notifies us more than once for the same change.
   // XXX:
   // XXX: The following code attempts to work around the bogus notifications,
   // XXX: and should probably be removed once bug 140303 is fixed.
   // XXX:
   // XXX: This code temporarily fixes the problem where clicking the mouse in
   // XXX: the same location clears the type-in-state.
-  RefPtr<Selection> selection =
-    aSelection ? aSelection->AsSelection() : nullptr;
 
-  if (aSelection) {
-    int32_t rangeCount = selection->RangeCount();
+  // TODO: We can make this use nsINode instead of nsIDOMNode.
+  if (aSelection.IsCollapsed() && aSelection.RangeCount()) {
+    nsCOMPtr<nsIDOMNode> selNode;
+    int32_t selOffset = 0;
 
-    if (selection->Collapsed() && rangeCount) {
-      nsCOMPtr<nsIDOMNode> selNode;
-      int32_t selOffset = 0;
-
-      nsresult rv =
-        EditorBase::GetStartNodeAndOffset(selection, getter_AddRefs(selNode),
-                                          &selOffset);
+    nsresult rv =
+      EditorBase::GetStartNodeAndOffset(&aSelection, getter_AddRefs(selNode),
+                                        &selOffset);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
 
-      NS_ENSURE_SUCCESS(rv, rv);
+    if (selNode &&
+        selNode == mLastSelectionContainer &&
+        selOffset == mLastSelectionOffset) {
+      // We got a bogus selection changed notification!
+      return;
+    }
 
-      if (selNode &&
-          selNode == mLastSelectionContainer &&
-          selOffset == mLastSelectionOffset) {
-        // We got a bogus selection changed notification!
-        return NS_OK;
-      }
-
-      mLastSelectionContainer = selNode;
-      mLastSelectionOffset = selOffset;
-    } else {
-      mLastSelectionContainer = nullptr;
-      mLastSelectionOffset = 0;
-    }
+    mLastSelectionContainer = selNode;
+    mLastSelectionOffset = selOffset;
+  } else {
+    mLastSelectionContainer = nullptr;
+    mLastSelectionOffset = 0;
   }
 
   Reset();
-  return NS_OK;
 }
 
 void
 TypeInState::Reset()
 {
   for (size_t i = 0, n = mClearedArray.Length(); i < n; i++) {
     delete mClearedArray[i];
   }
--- a/editor/libeditor/TypeInState.h
+++ b/editor/libeditor/TypeInState.h
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TypeInState_h
 #define TypeInState_h
 
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsISelectionListener.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nscore.h"
 
 // Workaround for windows headers
 #ifdef SetProp
 #undef SetProp
@@ -36,29 +35,28 @@ struct PropItem
   nsAtom* attr;
   nsString value;
 
   PropItem();
   PropItem(nsAtom* aTag, nsAtom* aAttr, const nsAString& aValue);
   ~PropItem();
 };
 
-class TypeInState final : public nsISelectionListener
+class TypeInState final
 {
 public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(TypeInState)
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TypeInState)
+  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(TypeInState)
 
   TypeInState();
   void Reset();
 
   nsresult UpdateSelState(dom::Selection* aSelection);
 
-  // nsISelectionListener
-  NS_DECL_NSISELECTIONLISTENER
+  void OnSelectionChange(dom::Selection& aSelection);
 
   void SetProp(nsAtom* aProp, nsAtom* aAttr, const nsAString& aValue);
 
   void ClearAllProps();
   void ClearProp(nsAtom* aProp, nsAtom* aAttr);
 
   /**
    * TakeClearProperty() hands back next property item on the clear list.