Bug 1430021 - part 2: Make HTMLEditRules not derived from nsIEditActionListener r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 13 Jan 2018 00:21:17 +0900
changeset 722011 d536ec65bcd6b9b909971b34c74775b2b996c07c
parent 722010 522563a30bc1ed936b0d0e82a1dcfd4823c1f96a
child 722217 903a1b79c50af3c4d090d5b4ab3309469aa843d9
push id96026
push usermasayuki@d-toybox.com
push dateThu, 18 Jan 2018 09:49:18 +0000
reviewersm_kato
bugs1430021
milestone59.0a1
Bug 1430021 - part 2: Make HTMLEditRules not derived from nsIEditActionListener r?m_kato HTMLEditRules implements only some of nsIEditActionListener and this is always first edit action listener. So, if we make EditorBase treat HTMLEditRules directly before notifying edit action listeners, we can save a lot of runtime cost (virtual calls especially unnecessary, copying array of edit action listeners with strong pointer, redundant QIs), although the code becomes not beautiful. Perhaps, we should do same thing to nsTextServicesDocument and mozInlineSpellChecker in other bugs. MozReview-Commit-ID: Eveaxj398f2
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
editor/libeditor/HTMLEditor.cpp
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -14,16 +14,17 @@
 #include "ChangeAttributeTransaction.h" // for ChangeAttributeTransaction
 #include "CompositionTransaction.h"     // for CompositionTransaction
 #include "CreateElementTransaction.h"   // for CreateElementTransaction
 #include "DeleteNodeTransaction.h"      // for DeleteNodeTransaction
 #include "DeleteRangeTransaction.h"     // for DeleteRangeTransaction
 #include "DeleteTextTransaction.h"      // for DeleteTextTransaction
 #include "EditAggregateTransaction.h"   // for EditAggregateTransaction
 #include "EditorEventListener.h"        // for EditorEventListener
+#include "HTMLEditRules.h"              // for HTMLEditRules
 #include "InsertNodeTransaction.h"      // for InsertNodeTransaction
 #include "InsertTextTransaction.h"      // for InsertTextTransaction
 #include "JoinNodeTransaction.h"        // for JoinNodeTransaction
 #include "PlaceholderTransaction.h"     // for PlaceholderTransaction
 #include "SplitNodeTransaction.h"       // for SplitNodeTransaction
 #include "StyleSheetTransactions.h"     // for AddStyleSheetTransaction, etc.
 #include "TextEditUtils.h"              // for TextEditUtils
 #include "mozInlineSpellChecker.h"      // for mozInlineSpellChecker
@@ -1426,17 +1427,17 @@ EditorBase::CreateNode(nsAtom* aTag,
 
   // XXX We need offset at new node for mRangeUpdater.  Therefore, we need
   //     to compute the offset now but this is expensive.  So, if it's possible,
   //     we need to redesign mRangeUpdater as avoiding using indices.
   int32_t offset = static_cast<int32_t>(pointToInsert.Offset());
 
   AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
 
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillCreateNode(nsDependentAtomString(aTag),
                                GetAsDOMNode(pointToInsert.GetChild()));
     }
   }
 
   nsCOMPtr<Element> ret;
@@ -1450,17 +1451,22 @@ EditorBase::CreateNode(nsAtom* aTag,
     // Now, aPointToInsert may be invalid.  I.e., GetChild() keeps
     // referring the next sibling of new node but Offset() refers the
     // new node.  Let's make refer the new node.
     pointToInsert.Set(ret, offset);
   }
 
   mRangeUpdater.SelAdjCreateNode(pointToInsert.AsRaw());
 
-  {
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->DidCreateNode(ret);
+  }
+
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidCreateNode(nsDependentAtomString(aTag),
                               GetAsDOMNode(ret), rv);
     }
   }
 
   return ret.forget();
@@ -1491,32 +1497,37 @@ EditorBase::InsertNode(nsIContent& aCont
 {
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return NS_ERROR_INVALID_ARG;
   }
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
   AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
 
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillInsertNode(
                   aContentToInsert.AsDOMNode(),
                   GetAsDOMNode(aPointToInsert.GetNextSiblingOfChild()));
     }
   }
 
   RefPtr<InsertNodeTransaction> transaction =
     InsertNodeTransaction::Create(*this, aContentToInsert, aPointToInsert);
   nsresult rv = DoTransaction(transaction);
 
   mRangeUpdater.SelAdjInsertNode(aPointToInsert.AsRaw());
 
-  {
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->DidInsertNode(aContentToInsert);
+  }
+
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidInsertNode(aContentToInsert.AsDOMNode(), rv);
     }
   }
 
   return rv;
 }
@@ -1554,40 +1565,50 @@ EditorBase::SplitNode(const EditorRawDOM
   }
   MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
 
   AutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext);
 
   // Different from CreateNode(), we need offset at start of right node only
   // for WillSplitNode() since the offset is always same as the length of new
   // left node.
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       // XXX Unfortunately, we need to compute offset here because the container
       //     may be a data node like text node.  However, nobody implements this
       //     method actually.  So, we should get rid of this in a follow up bug.
       listener->WillSplitNode(aStartOfRightNode.GetContainerAsDOMNode(),
                               aStartOfRightNode.Offset());
     }
+  } else {
+    // XXX Unfortunately, storing offset of the split point in
+    //     SplitNodeTransaction is necessary for now.  We should fix this
+    //     in a follow up bug.
+    Unused << aStartOfRightNode.Offset();
   }
 
   RefPtr<SplitNodeTransaction> transaction =
     SplitNodeTransaction::Create(*this, aStartOfRightNode);
   aError = DoTransaction(transaction);
 
   nsCOMPtr<nsIContent> newNode = transaction->GetNewNode();
   NS_WARNING_ASSERTION(newNode, "Failed to create a new left node");
 
   // XXX Some other transactions manage range updater by themselves.
   //     Why doesn't SplitNodeTransaction do it?
   mRangeUpdater.SelAdjSplitNode(*aStartOfRightNode.GetContainerAsContent(),
                                 newNode);
 
-  {
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->DidSplitNode(aStartOfRightNode.GetContainer(), newNode);
+  }
+
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidSplitNode(aStartOfRightNode.GetContainerAsDOMNode(),
                              GetAsDOMNode(newNode));
     }
   }
 
   if (NS_WARN_IF(aError.Failed())) {
@@ -1619,17 +1640,22 @@ EditorBase::JoinNodes(nsINode& aLeftNode
                                nsIEditor::ePrevious);
 
   // Remember some values; later used for saved selection updating.
   // Find the offset between the nodes to be joined.
   int32_t offset = parent->IndexOf(&aRightNode);
   // Find the number of children of the lefthand node
   uint32_t oldLeftNodeLen = aLeftNode.Length();
 
-  {
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->WillJoinNodes(aLeftNode, aRightNode);
+  }
+
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(),
                               parent->AsDOMNode());
     }
   }
 
   nsresult rv = NS_OK;
@@ -1639,17 +1665,22 @@ EditorBase::JoinNodes(nsINode& aLeftNode
     rv = DoTransaction(transaction);
   }
 
   // XXX Some other transactions manage range updater by themselves.
   //     Why doesn't JoinNodeTransaction do it?
   mRangeUpdater.SelAdjJoinNodes(aLeftNode, aRightNode, *parent, offset,
                                 (int32_t)oldLeftNodeLen);
 
-  {
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->DidJoinNodes(aLeftNode, aRightNode);
+  }
+
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidJoinNodes(aLeftNode.AsDOMNode(), aRightNode.AsDOMNode(),
                              parent->AsDOMNode(), rv);
     }
   }
 
   return rv;
@@ -1668,30 +1699,35 @@ EditorBase::DeleteNode(nsINode* aNode)
 {
   if (NS_WARN_IF(!aNode)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   AutoRules beginRulesSniffing(this, EditAction::createNode,
                                nsIEditor::ePrevious);
 
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->WillDeleteNode(aNode);
+  }
+
   // save node location for selection updating code.
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillDeleteNode(aNode->AsDOMNode());
     }
   }
 
   RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
     DeleteNodeTransaction::MaybeCreate(*this, *aNode);
   nsresult rv = deleteNodeTransaction ? DoTransaction(deleteNodeTransaction) :
                                         NS_ERROR_FAILURE;
 
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidDeleteNode(aNode->AsDOMNode(), rv);
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
@@ -2807,33 +2843,39 @@ EditorBase::InsertTextIntoTextNodeImpl(c
     insertedTextNode = mComposition->GetContainerTextNode();
     insertedOffset = mComposition->XPOffsetInTextNode();
   } else {
     transaction =
       InsertTextTransaction::Create(*this, aStringToInsert, aTextNode, aOffset);
   }
 
   // Let listeners know what's up
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillInsertText(
         static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()),
         insertedOffset, aStringToInsert);
     }
   }
 
   // XXX We may not need these view batches anymore.  This is handled at a
   // higher level now I believe.
   BeginUpdateViewBatch();
   nsresult rv = DoTransaction(transaction);
   EndUpdateViewBatch();
 
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->DidInsertText(insertedTextNode, insertedOffset,
+                                 aStringToInsert);
+  }
+
   // let listeners know what happened
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidInsertText(
         static_cast<nsIDOMCharacterData*>(insertedTextNode->AsDOMNode()),
         insertedOffset, aStringToInsert, rv);
     }
   }
 
@@ -2951,17 +2993,17 @@ EditorBase::SetTextImpl(Selection& aSele
                         Text& aCharData)
 {
   const uint32_t length = aCharData.Length();
 
   AutoRules beginRulesSniffing(this, EditAction::setText,
                                nsIEditor::eNext);
 
   // Let listeners know what's up
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       if (length) {
         listener->WillDeleteText(
           static_cast<nsIDOMCharacterData*>(aCharData.AsDOMNode()), 0,
           length);
       }
       if (!aString.IsEmpty()) {
@@ -2986,18 +3028,28 @@ EditorBase::SetTextImpl(Selection& aSele
     DebugOnly<nsresult> rv = selection->Collapse(&aCharData, aString.Length());
     NS_ASSERTION(NS_SUCCEEDED(rv),
                  "Selection could not be collapsed after insert");
   }
 
   mRangeUpdater.SelAdjDeleteText(&aCharData, 0, length);
   mRangeUpdater.SelAdjInsertText(aCharData, 0, aString);
 
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    if (length) {
+      htmlEditRules->DidDeleteText(&aCharData, 0, length);
+    }
+    if (!aString.IsEmpty()) {
+      htmlEditRules->DidInsertText(&aCharData, 0, aString);
+    }
+  }
+
   // Let listeners know what happened
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       if (length) {
         listener->DidDeleteText(
           static_cast<nsIDOMCharacterData*>(aCharData.AsDOMNode()), 0,
           length, rv);
       }
       if (!aString.IsEmpty()) {
@@ -3021,29 +3073,34 @@ EditorBase::DeleteText(nsGenericDOMDataN
   if (NS_WARN_IF(!transaction)) {
     return NS_ERROR_FAILURE;
   }
 
   AutoRules beginRulesSniffing(this, EditAction::deleteText,
                                nsIEditor::ePrevious);
 
   // Let listeners know what's up
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillDeleteText(
           static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset,
           aLength);
     }
   }
 
   nsresult rv = DoTransaction(transaction);
 
+  if (mRules && mRules->AsHTMLEditRules()) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->DidDeleteText(&aCharData, aOffset, aLength);
+  }
+
   // Let listeners know what happened
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidDeleteText(
           static_cast<nsIDOMCharacterData*>(GetAsDOMNode(&aCharData)), aOffset,
           aLength, rv);
     }
   }
 
@@ -3455,31 +3512,16 @@ EditorBase::GetNodeLocation(nsINode* aCh
     *aOffset = GetChildOffset(aChild, parent);
     MOZ_ASSERT(*aOffset != -1);
   } else {
     *aOffset = -1;
   }
   return parent;
 }
 
-/**
- * Returns the number of things inside aNode.  If aNode is text, returns number
- * of characters. If not, returns number of children nodes.
- */
-nsresult
-EditorBase::GetLengthOfDOMNode(nsIDOMNode* aNode,
-                               uint32_t& aCount)
-{
-  aCount = 0;
-  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
-  NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
-  aCount = node->Length();
-  return NS_OK;
-}
-
 nsIContent*
 EditorBase::GetPreviousNodeInternal(nsINode& aNode,
                                     bool aFindEditableNode,
                                     bool aNoBlockCrossing)
 {
   if (!IsDescendantOfEditorRoot(&aNode)) {
     return nullptr;
   }
@@ -4378,18 +4420,29 @@ EditorBase::DeleteSelectionImpl(EDirecti
                                   &deleteCharLength);
     if (NS_WARN_IF(!deleteSelectionTransaction)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   nsCOMPtr<nsIDOMCharacterData> deleteCharData(do_QueryInterface(deleteNode));
   AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
+
+  if (mRules && mRules->AsHTMLEditRules()) {
+    if (!deleteNode) {
+      RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+      htmlEditRules->WillDeleteSelection(selection);
+    } else if (!deleteCharData) {
+      RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+      htmlEditRules->WillDeleteNode(deleteNode);
+    }
+  }
+
   // Notify nsIEditActionListener::WillDelete[Selection|Text|Node]
-  {
+  if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     if (!deleteNode) {
       for (auto& listener : listeners) {
         listener->WillDeleteSelection(selection);
       }
     } else if (deleteCharData) {
       for (auto& listener : listeners) {
         listener->WillDeleteText(deleteCharData, deleteCharOffset, 1);
@@ -4399,16 +4452,21 @@ EditorBase::DeleteSelectionImpl(EDirecti
         listener->WillDeleteNode(deleteNode->AsDOMNode());
       }
     }
   }
 
   // Delete the specified amount
   nsresult rv = DoTransaction(deleteSelectionTransaction);
 
+  if (mRules && mRules->AsHTMLEditRules() && deleteCharData) {
+    RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
+    htmlEditRules->DidDeleteText(deleteNode, deleteCharOffset, 1);
+  }
+
   // Notify nsIEditActionListener::DidDelete[Selection|Text|Node]
   {
     AutoActionListenerArray listeners(mActionListeners);
     if (!deleteNode) {
       for (auto& listener : mActionListeners) {
         listener->DidDeleteSelection(selection);
       }
     } else if (deleteCharData) {
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -751,25 +751,16 @@ public:
    * Set outOffset to the offset of aChild in the parent.
    * Returns the parent of aChild.
    */
   static already_AddRefed<nsIDOMNode> GetNodeLocation(nsIDOMNode* aChild,
                                                       int32_t* outOffset);
   static nsINode* GetNodeLocation(nsINode* aChild, int32_t* aOffset);
 
   /**
-   * Returns the number of things inside aNode in the out-param aCount.
-   * @param  aNode is the node to get the length of.
-   *         If aNode is text, returns number of characters.
-   *         If not, returns number of children nodes.
-   * @param  aCount [OUT] the result of the above calculation.
-   */
-  static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount);
-
-  /**
    * Get the previous node.
    */
   nsIContent* GetPreviousNode(const EditorRawDOMPoint& aPoint)
   {
     return GetPreviousNodeInternal(aPoint, false, false);
   }
   nsIContent* GetPreviousEditableNode(const EditorRawDOMPoint& aPoint)
   {
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -194,17 +194,16 @@ HTMLEditRules::HTMLEditRules()
   InitFields();
 }
 
 void
 HTMLEditRules::InitFields()
 {
   mHTMLEditor = nullptr;
   mDocChangeRange = nullptr;
-  mListenerEnabled = true;
   mReturnInEmptyLIKillsList = true;
   mDidDeleteSelection = false;
   mDidRangedDelete = false;
   mRestoreContentEditableCount = false;
   mUtilRange = nullptr;
   mJoinOffset = 0;
   mNewBlock = nullptr;
   mRangeItem = new RangeItem();
@@ -233,29 +232,19 @@ HTMLEditRules::InitStyleCacheArray(Style
   aStyleCache[15] = StyleCache(nsGkAtoms::acronym, nullptr);
   aStyleCache[16] = StyleCache(nsGkAtoms::backgroundColor, nullptr);
   aStyleCache[17] = StyleCache(nsGkAtoms::sub, nullptr);
   aStyleCache[18] = StyleCache(nsGkAtoms::sup, nullptr);
 }
 
 HTMLEditRules::~HTMLEditRules()
 {
-  // remove ourselves as a listener to edit actions
-  // In some cases, we have already been removed by
-  // ~HTMLEditor, in which case we will get a null pointer here
-  // which we ignore.  But this allows us to add the ability to
-  // switch rule sets on the fly if we want.
-  if (mHTMLEditor) {
-    mHTMLEditor->RemoveEditActionListener(this);
-  }
-}
-
-NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLEditRules,
-                                             TextEditRules,
-                                             nsIEditActionListener)
+}
+
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLEditRules, TextEditRules)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
                                    mDocChangeRange, mUtilRange, mNewBlock,
                                    mRangeItem)
 
 nsresult
 HTMLEditRules::Init(TextEditor* aTextEditor)
 {
@@ -303,26 +292,25 @@ HTMLEditRules::Init(TextEditor* aTextEdi
 
   if (node->IsElement()) {
     ErrorResult rv;
     mDocChangeRange->SelectNode(*node, rv);
     NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
     AdjustSpecialBreaks();
   }
 
-  // add ourselves as a listener to edit actions
-  return mHTMLEditor->AddEditActionListener(this);
+  StartToListenToEditActions();
+
+  return NS_OK;
 }
 
 nsresult
 HTMLEditRules::DetachEditor()
 {
-  if (mHTMLEditor) {
-    mHTMLEditor->RemoveEditActionListener(this);
-  }
+  EndListeningToEditActions();
   mHTMLEditor = nullptr;
   return TextEditRules::DetachEditor();
 }
 
 nsresult
 HTMLEditRules::BeforeEdit(EditAction aAction,
                           nsIEditor::EDirection aDirection)
 {
@@ -8798,217 +8786,160 @@ HTMLEditRules::InsertBRIfNeededInternal(
   RefPtr<Element> brElement =
     CreateBRInternal(EditorRawDOMPoint(&aNode, 0), aInsertMozBR);
   if (NS_WARN_IF(!brElement)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-HTMLEditRules::WillCreateNode(const nsAString& aTag,
-                              nsIDOMNode* aNextSiblingOfNewNode)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidCreateNode(const nsAString& aTag,
-                             nsIDOMNode* aNewNode,
-                             nsresult aResult)
+void
+HTMLEditRules::DidCreateNode(Element* aNewElement)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
+    return;
+  }
+  if (NS_WARN_IF(!aNewElement)) {
+    return;
   }
   // assumption that Join keeps the righthand node
-  nsresult rv = mUtilRange->SelectNode(aNewNode);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::WillInsertNode(nsIDOMNode* aNode,
-                              nsIDOMNode* aNextSiblingOfNewNode)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidInsertNode(nsIDOMNode* aNode,
-                             nsresult aResult)
-{
-  if (!mListenerEnabled) {
-    return NS_OK;
-  }
-  nsresult rv = mUtilRange->SelectNode(aNode);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::WillDeleteNode(nsIDOMNode* aChild)
+  IgnoredErrorResult error;
+  mUtilRange->SelectNode(*aNewElement, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
+}
+
+void
+HTMLEditRules::DidInsertNode(nsIContent& aContent)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
-  }
-  nsresult rv = mUtilRange->SelectNode(aChild);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidDeleteNode(nsIDOMNode* aChild,
-                             nsresult aResult)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLEditRules::WillSplitNode(nsIDOMNode* aExistingRightNode,
-                             int32_t aOffset)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidSplitNode(nsIDOMNode* aExistingRightNode,
-                            nsIDOMNode* aNewLeftNode)
+    return;
+  }
+  IgnoredErrorResult error;
+  mUtilRange->SelectNode(aContent, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
+}
+
+void
+HTMLEditRules::WillDeleteNode(nsINode* aChild)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
-  }
-  nsCOMPtr<nsINode> newLeftNode = do_QueryInterface(aNewLeftNode);
-  nsCOMPtr<nsINode> existingRightNode = do_QueryInterface(aExistingRightNode);
-  nsresult rv = mUtilRange->SetStartAndEnd(newLeftNode, 0,
-                                           existingRightNode, 0);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::WillJoinNodes(nsIDOMNode* aLeftNode,
-                             nsIDOMNode* aRightNode,
-                             nsIDOMNode* aParent)
+    return;
+  }
+  if (NS_WARN_IF(!aChild)) {
+    return;
+  }
+  IgnoredErrorResult error;
+  mUtilRange->SelectNode(*aChild, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
+}
+
+void
+HTMLEditRules::DidSplitNode(nsINode* aExistingRightNode,
+                            nsINode* aNewLeftNode)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
-  }
-  // remember split point
-  return EditorBase::GetLengthOfDOMNode(aLeftNode, mJoinOffset);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidJoinNodes(nsIDOMNode* aLeftNode,
-                            nsIDOMNode* aRightNode,
-                            nsIDOMNode* aParent,
-                            nsresult aResult)
+    return;
+  }
+  nsresult rv = mUtilRange->SetStartAndEnd(aNewLeftNode, 0,
+                                           aExistingRightNode, 0);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
+}
+
+void
+HTMLEditRules::WillJoinNodes(nsINode& aLeftNode,
+                             nsINode& aRightNode)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
-  }
-  nsCOMPtr<nsINode> rightNode = do_QueryInterface(aRightNode);
-  // assumption that Join keeps the righthand node
-  nsresult rv = mUtilRange->CollapseTo(rightNode, mJoinOffset);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::WillInsertText(nsIDOMCharacterData* aTextNode,
-                              int32_t aOffset,
-                              const nsAString& aString)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidInsertText(nsIDOMCharacterData* aTextNode,
-                             int32_t aOffset,
-                             const nsAString& aString,
-                             nsresult aResult)
+    return;
+  }
+  // remember split point
+  mJoinOffset = aLeftNode.Length();
+}
+
+void
+HTMLEditRules::DidJoinNodes(nsINode& aLeftNode,
+                            nsINode& aRightNode)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
+    return;
+  }
+  // assumption that Join keeps the righthand node
+  nsresult rv = mUtilRange->CollapseTo(&aRightNode, mJoinOffset);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
+}
+
+void
+HTMLEditRules::DidInsertText(nsINode* aTextNode,
+                             int32_t aOffset,
+                             const nsAString& aString)
+{
+  if (!mListenerEnabled) {
+    return;
   }
   int32_t length = aString.Length();
-  nsCOMPtr<nsINode> theNode = do_QueryInterface(aTextNode);
-  nsresult rv = mUtilRange->SetStartAndEnd(theNode, aOffset,
-                                           theNode, aOffset + length);
+  nsresult rv = mUtilRange->SetStartAndEnd(aTextNode, aOffset,
+                                           aTextNode, aOffset + length);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::WillDeleteText(nsIDOMCharacterData* aTextNode,
-                              int32_t aOffset,
-                              int32_t aLength)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidDeleteText(nsIDOMCharacterData* aTextNode,
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
+}
+
+void
+HTMLEditRules::DidDeleteText(nsINode* aTextNode,
                              int32_t aOffset,
-                             int32_t aLength,
-                             nsresult aResult)
+                             int32_t aLength)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
-  }
-  nsCOMPtr<nsINode> theNode = do_QueryInterface(aTextNode);
-  nsresult rv = mUtilRange->CollapseTo(theNode, aOffset);
+    return;
+  }
+  nsresult rv = mUtilRange->CollapseTo(aTextNode, aOffset);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::WillDeleteSelection(nsISelection* aSelection)
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
+}
+
+void
+HTMLEditRules::WillDeleteSelection(Selection* aSelection)
 {
   if (!mListenerEnabled) {
-    return NS_OK;
+    return;
   }
   if (NS_WARN_IF(!aSelection)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  RefPtr<Selection> selection = aSelection->AsSelection();
-  // get the (collapsed) selection location
-  nsCOMPtr<nsINode> startNode;
-  int32_t startOffset;
-  nsresult rv =
-    EditorBase::GetStartNodeAndOffset(selection,
-                                      getter_AddRefs(startNode), &startOffset);
+    return;
+  }
+  EditorRawDOMPoint startPoint = EditorBase::GetStartPoint(aSelection);
+  if (NS_WARN_IF(!startPoint.IsSet())) {
+    return;
+  }
+  EditorRawDOMPoint endPoint = EditorBase::GetEndPoint(aSelection);
+  if (NS_WARN_IF(!endPoint.IsSet())) {
+    return;
+  }
+  nsresult rv = mUtilRange->SetStartAndEnd(startPoint, endPoint);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  nsCOMPtr<nsINode> endNode;
-  int32_t endOffset;
-  rv = EditorBase::GetEndNodeAndOffset(selection,
-                                       getter_AddRefs(endNode), &endOffset);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  rv = mUtilRange->SetStartAndEnd(startNode, startOffset, endNode, endOffset);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return UpdateDocChangeRange(mUtilRange);
-}
-
-NS_IMETHODIMP
-HTMLEditRules::DidDeleteSelection(nsISelection *aSelection)
-{
-  return NS_OK;
+    return;
+  }
+  UpdateDocChangeRange(mUtilRange);
 }
 
 // Let's remove all alignment hints in the children of aNode; it can
 // be an ALIGN attribute (in case we just remove it) or a CENTER
 // element (here we have to remove the container and keep its
 // children). We break on tables and don't look at their children.
 nsresult
 HTMLEditRules::RemoveAlignment(nsINode& aNode,
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -6,17 +6,16 @@
 #ifndef HTMLEditRules_h
 #define HTMLEditRules_h
 
 #include "TypeInState.h"
 #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
 #include "mozilla/SelectionState.h"
 #include "mozilla/TextEditRules.h"
 #include "nsCOMPtr.h"
-#include "nsIEditActionListener.h"
 #include "nsIEditor.h"
 #include "nsIHTMLEditor.h"
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 #include "nscore.h"
 
 class nsAtom;
 class nsIDOMCharacterData;
@@ -73,17 +72,16 @@ struct StyleCache final : public PropIte
   {
     MOZ_COUNT_DTOR(StyleCache);
   }
 };
 
 #define SIZE_STYLE_TABLE 19
 
 class HTMLEditRules : public TextEditRules
-                    , public nsIEditActionListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditRules, TextEditRules)
 
   HTMLEditRules();
 
   // TextEditRules methods
@@ -105,47 +103,33 @@ public:
 
   nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
   nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
   nsresult GetIndentState(bool* aCanIndent, bool* aCanOutdent);
   nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
   nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
   nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode);
 
-  // nsIEditActionListener methods
+  void DidCreateNode(Element* aNewElement);
+  void DidInsertNode(nsIContent& aNode);
+  void WillDeleteNode(nsINode* aChild);
+  void DidSplitNode(nsINode* aExistingRightNode,
+                    nsINode* aNewLeftNode);
+  void WillJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
+  void DidJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
+  void DidInsertText(nsINode* aTextNode, int32_t aOffset,
+                     const nsAString& aString);
+  void DidDeleteText(nsINode* aTextNode, int32_t aOffset, int32_t aLength);
+  void WillDeleteSelection(Selection* aSelection);
 
-  NS_IMETHOD WillCreateNode(const nsAString& aTag,
-                            nsIDOMNode* aNextSiblingOfNewNode) override;
-  NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode* aNewNode,
-                           nsresult aResult) override;
-  NS_IMETHOD WillInsertNode(nsIDOMNode* aNode,
-                            nsIDOMNode* aNextSiblingOfNewNode) override;
-  NS_IMETHOD DidInsertNode(nsIDOMNode* aNode, nsresult aResult) override;
-  NS_IMETHOD WillDeleteNode(nsIDOMNode* aChild) override;
-  NS_IMETHOD DidDeleteNode(nsIDOMNode* aChild, nsresult aResult) override;
-  NS_IMETHOD WillSplitNode(nsIDOMNode* aExistingRightNode,
-                           int32_t aOffset) override;
-  NS_IMETHOD DidSplitNode(nsIDOMNode* aExistingRightNode,
-                          nsIDOMNode* aNewLeftNode) override;
-  NS_IMETHOD WillJoinNodes(nsIDOMNode* aLeftNode, nsIDOMNode* aRightNode,
-                           nsIDOMNode* aParent) override;
-  NS_IMETHOD DidJoinNodes(nsIDOMNode* aLeftNode, nsIDOMNode* aRightNode,
-                          nsIDOMNode* aParent, nsresult aResult) override;
-  NS_IMETHOD WillInsertText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
-                            const nsAString &aString) override;
-  NS_IMETHOD DidInsertText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
-                           const nsAString &aString, nsresult aResult) override;
-  NS_IMETHOD WillDeleteText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
-                            int32_t aLength) override;
-  NS_IMETHOD DidDeleteText(nsIDOMCharacterData* aTextNode, int32_t aOffset,
-                           int32_t aLength, nsresult aResult) override;
-  NS_IMETHOD WillDeleteSelection(nsISelection* aSelection) override;
-  NS_IMETHOD DidDeleteSelection(nsISelection* aSelection) override;
   void DeleteNodeIfCollapsedText(nsINode& aNode);
 
+  void StartToListenToEditActions() { mListenerEnabled = true; }
+  void EndListeningToEditActions() { mListenerEnabled = false; }
+
 protected:
   virtual ~HTMLEditRules();
 
   enum RulesEndpoint
   {
     kStart,
     kEnd
   };
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -130,23 +130,18 @@ HTMLEditor::HTMLEditor()
       Preferences::GetBool("editor.use_div_for_default_newlines", true)
       ? ParagraphSeparator::div : ParagraphSeparator::br)
 {
   mIsHTMLEditorClass = true;
 }
 
 HTMLEditor::~HTMLEditor()
 {
-  // remove the rules as an action listener.  Else we get a bad
-  // ownership loop later on.  it's ok if the rules aren't a listener;
-  // we ignore the error.
-  if (mRules) {
-    nsCOMPtr<nsIEditActionListener> listener =
-      static_cast<nsIEditActionListener*>(mRules->AsHTMLEditRules());
-    RemoveEditActionListener(listener);
+  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) {
     nsCOMPtr<nsISelectionListener>listener;