Bug 1393337 - Get rid of nsIEditRules r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 21 Dec 2017 14:52:32 +0900
changeset 714165 7e1f83a2c040c161775b5c90a09b55e950fb16bf
parent 714164 89be3c3fcd637f3aa5c088e8c30ee0d09c355926
child 714470 90c4e86505815193f248d27dcd048e4860a27a11
push id93878
push usermasayuki@d-toybox.com
push dateThu, 21 Dec 2017 23:01:06 +0000
reviewersm_kato
bugs1393337
milestone59.0a1
Bug 1393337 - Get rid of nsIEditRules r?m_kato nsIEditRules is a super class of only mozilla::TextEditRules and not scriptable. So, we can get rid of it. This patch merges RulesInfo with TextRulesInfo and name new class is RulesInfo for minimizing the code change. Additionally, adds two methods AsHTMLEditRules() and its const version. They make existing cast code safer. MozReview-Commit-ID: KwWH3ADj3Bv
editor/libeditor/HTMLAbsPositionEditor.cpp
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/HTMLEditorDataTransfer.cpp
editor/libeditor/HTMLStyleEditor.cpp
editor/libeditor/TextEditRules.cpp
editor/libeditor/TextEditRules.h
editor/libeditor/TextEditor.cpp
editor/libeditor/TextEditor.h
editor/libeditor/TypeInState.cpp
editor/libeditor/moz.build
editor/libeditor/nsIEditRules.h
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -7,16 +7,17 @@
 #include <math.h>
 
 #include "HTMLEditorObjectResizerUtils.h"
 #include "HTMLEditRules.h"
 #include "HTMLEditUtils.h"
 #include "TextEditUtils.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TextEditRules.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/mozalloc.h"
 #include "nsAString.h"
 #include "nsAlgorithm.h"
 #include "nsCOMPtr.h"
 #include "nsComputedDOMStyle.h"
 #include "nsDebug.h"
@@ -26,17 +27,16 @@
 #include "nsROCSSPrimitiveValue.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMNode.h"
 #include "nsDOMCSSRGBColor.h"
 #include "nsIDOMWindow.h"
-#include "nsIEditRules.h"
 #include "nsIHTMLObjectResizer.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsUtils.h"
 #include "nsLiteralString.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
@@ -59,21 +59,21 @@ HTMLEditor::AbsolutePositionSelection(bo
                                           EditAction::removeAbsolutePosition,
                                nsIEditor::eNext);
 
   // the line below does not match the code; should it be removed?
   // Find out if the selection is collapsed:
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  TextRulesInfo ruleInfo(aEnabled ? EditAction::setAbsolutePosition :
-                                    EditAction::removeAbsolutePosition);
+  RulesInfo ruleInfo(aEnabled ? EditAction::setAbsolutePosition :
+                                EditAction::removeAbsolutePosition);
   bool cancel, handled;
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (NS_FAILED(rv) || cancel) {
     return rv;
   }
 
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
 
@@ -185,21 +185,21 @@ HTMLEditor::RelativeChangeZIndex(int32_t
                                (aChange < 0) ? EditAction::decreaseZIndex :
                                                EditAction::increaseZIndex,
                                nsIEditor::eNext);
 
   // brade: can we get rid of this comment?
   // Find out if the selection is collapsed:
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  TextRulesInfo ruleInfo(aChange < 0 ? EditAction::decreaseZIndex :
-                                       EditAction::increaseZIndex);
+  RulesInfo ruleInfo(aChange < 0 ? EditAction::decreaseZIndex :
+                                   EditAction::increaseZIndex);
   bool cancel, handled;
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
 
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -184,16 +184,17 @@ HTMLEditRules::HTMLEditRules()
   : mHTMLEditor(nullptr)
   , mListenerEnabled(false)
   , mReturnInEmptyLIKillsList(false)
   , mDidDeleteSelection(false)
   , mDidRangedDelete(false)
   , mRestoreContentEditableCount(false)
   , mJoinOffset(0)
 {
+  mIsHTMLEditRules = true;
   InitFields();
 }
 
 void
 HTMLEditRules::InitFields()
 {
   mHTMLEditor = nullptr;
   mDocChangeRange = nullptr;
@@ -249,17 +250,17 @@ HTMLEditRules::~HTMLEditRules()
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLEditRules,
                                              TextEditRules,
                                              nsIEditActionListener)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
                                    mDocChangeRange, mUtilRange, mNewBlock,
                                    mRangeItem)
 
-NS_IMETHODIMP
+nsresult
 HTMLEditRules::Init(TextEditor* aTextEditor)
 {
   if (NS_WARN_IF(!aTextEditor)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   InitFields();
 
@@ -305,28 +306,28 @@ HTMLEditRules::Init(TextEditor* aTextEdi
     NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
     AdjustSpecialBreaks();
   }
 
   // add ourselves as a listener to edit actions
   return mHTMLEditor->AddEditActionListener(this);
 }
 
-NS_IMETHODIMP
+nsresult
 HTMLEditRules::DetachEditor()
 {
   if (mHTMLEditor) {
     mHTMLEditor->RemoveEditActionListener(this);
   }
   mHTMLEditor = nullptr;
   return TextEditRules::DetachEditor();
 }
 
-NS_IMETHODIMP
-HTMLEditRules::BeforeEdit(EditAction action,
+nsresult
+HTMLEditRules::BeforeEdit(EditAction aAction,
                           nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   NS_ENSURE_STATE(mHTMLEditor);
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
@@ -368,20 +369,20 @@ HTMLEditRules::BeforeEdit(EditAction act
       mDocChangeRange->Reset();
     }
     if (mUtilRange) {
       // Ditto for mUtilRange.
       mUtilRange->Reset();
     }
 
     // Remember current inline styles for deletion and normal insertion ops
-    if (action == EditAction::insertText ||
-        action == EditAction::insertIMEText ||
-        action == EditAction::deleteSelection ||
-        IsStyleCachePreservingAction(action)) {
+    if (aAction == EditAction::insertText ||
+        aAction == EditAction::insertIMEText ||
+        aAction == EditAction::deleteSelection ||
+        IsStyleCachePreservingAction(aAction)) {
       nsCOMPtr<nsINode> selNode =
         aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
       nsresult rv = CacheInlineStyles(selNode);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Stabilize the document against contenteditable count changes
     nsCOMPtr<nsIDOMDocument> doc = htmlEditor->GetDOMDocument();
@@ -391,41 +392,41 @@ HTMLEditRules::BeforeEdit(EditAction act
     if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
       htmlDoc->ChangeContentEditableCount(nullptr, +1);
       mRestoreContentEditableCount = true;
     }
 
     // Check that selection is in subtree defined by body node
     ConfirmSelectionInBody();
     // Let rules remember the top level action
-    mTheAction = action;
+    mTheAction = aAction;
   }
   return NS_OK;
 }
 
 
-NS_IMETHODIMP
-HTMLEditRules::AfterEdit(EditAction action,
+nsresult
+HTMLEditRules::AfterEdit(EditAction aAction,
                          nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   NS_ENSURE_STATE(mHTMLEditor);
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
   AutoLockRulesSniffing lockIt(this);
 
   MOZ_ASSERT(mActionNesting > 0);
   nsresult rv = NS_OK;
   mActionNesting--;
   if (!mActionNesting) {
     // Do all the tricky stuff
-    rv = AfterEditInner(action, aDirection);
+    rv = AfterEditInner(aAction, aDirection);
 
     // Free up selectionState range item
     htmlEditor->mRangeUpdater.DropRangeItem(mRangeItem);
 
     // Reset the contenteditable count to its previous value
     if (mRestoreContentEditableCount) {
       nsCOMPtr<nsIDOMDocument> doc = htmlEditor->GetDOMDocument();
       NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
@@ -439,21 +440,21 @@ HTMLEditRules::AfterEdit(EditAction acti
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
-HTMLEditRules::AfterEditInner(EditAction action,
+HTMLEditRules::AfterEditInner(EditAction aAction,
                               nsIEditor::EDirection aDirection)
 {
   ConfirmSelectionInBody();
-  if (action == EditAction::ignore) {
+  if (aAction == EditAction::ignore) {
     return NS_OK;
   }
 
   NS_ENSURE_STATE(mHTMLEditor);
   RefPtr<Selection> selection = mHTMLEditor->GetSelection();
   NS_ENSURE_STATE(selection);
 
   nsCOMPtr<nsINode> rangeStartContainer, rangeEndContainer;
@@ -465,58 +466,58 @@ HTMLEditRules::AfterEditInner(EditAction
     rangeEndContainer = mDocChangeRange->GetEndContainer();
     rangeStartOffset = mDocChangeRange->StartOffset();
     rangeEndOffset = mDocChangeRange->EndOffset();
     if (rangeStartContainer && rangeEndContainer) {
       bDamagedRange = true;
     }
   }
 
-  if (bDamagedRange && !((action == EditAction::undo) ||
-                         (action == EditAction::redo))) {
+  if (bDamagedRange && !((aAction == EditAction::undo) ||
+                         (aAction == EditAction::redo))) {
     // don't let any txns in here move the selection around behind our back.
     // Note that this won't prevent explicit selection setting from working.
     NS_ENSURE_STATE(mHTMLEditor);
     AutoTransactionsConserveSelection dontChangeMySelection(mHTMLEditor);
 
     // expand the "changed doc range" as needed
-    PromoteRange(*mDocChangeRange, action);
+    PromoteRange(*mDocChangeRange, aAction);
 
     // if we did a ranged deletion or handling backspace key, make sure we have
     // a place to put caret.
     // Note we only want to do this if the overall operation was deletion,
     // not if deletion was done along the way for EditAction::loadHTML, EditAction::insertText, etc.
     // That's why this is here rather than DidDeleteSelection().
-    if (action == EditAction::deleteSelection && mDidRangedDelete) {
+    if (aAction == EditAction::deleteSelection && mDidRangedDelete) {
       nsresult rv = InsertBRIfNeeded(selection);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // add in any needed <br>s, and remove any unneeded ones.
     AdjustSpecialBreaks();
 
     // merge any adjacent text nodes
-    if (action != EditAction::insertText &&
-        action != EditAction::insertIMEText) {
+    if (aAction != EditAction::insertText &&
+        aAction != EditAction::insertIMEText) {
       NS_ENSURE_STATE(mHTMLEditor);
       nsresult rv = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // clean up any empty nodes in the selection
     nsresult rv = RemoveEmptyNodes();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // attempt to transform any unneeded nbsp's into spaces after doing various operations
-    if (action == EditAction::insertText ||
-        action == EditAction::insertIMEText ||
-        action == EditAction::deleteSelection ||
-        action == EditAction::insertBreak ||
-        action == EditAction::htmlPaste ||
-        action == EditAction::loadHTML) {
+    if (aAction == EditAction::insertText ||
+        aAction == EditAction::insertIMEText ||
+        aAction == EditAction::deleteSelection ||
+        aAction == EditAction::insertBreak ||
+        aAction == EditAction::htmlPaste ||
+        aAction == EditAction::loadHTML) {
       rv = AdjustWhitespace(selection);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // also do this for original selection endpoints.
       NS_ENSURE_STATE(mHTMLEditor);
       NS_ENSURE_STATE(mRangeItem->mStartContainer);
       NS_ENSURE_STATE(mRangeItem->mEndContainer);
       WSRunObject(mHTMLEditor, mRangeItem->mStartContainer,
@@ -532,44 +533,44 @@ HTMLEditRules::AfterEditInner(EditAction
 
     // if we created a new block, make sure selection lands in it
     if (mNewBlock) {
       rv = PinSelectionToNewBlock(selection);
       mNewBlock = nullptr;
     }
 
     // adjust selection for insert text, html paste, and delete actions
-    if (action == EditAction::insertText ||
-        action == EditAction::insertIMEText ||
-        action == EditAction::deleteSelection ||
-        action == EditAction::insertBreak ||
-        action == EditAction::htmlPaste ||
-        action == EditAction::loadHTML) {
+    if (aAction == EditAction::insertText ||
+        aAction == EditAction::insertIMEText ||
+        aAction == EditAction::deleteSelection ||
+        aAction == EditAction::insertBreak ||
+        aAction == EditAction::htmlPaste ||
+        aAction == EditAction::loadHTML) {
       rv = AdjustSelection(selection, aDirection);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // check for any styles which were removed inappropriately
-    if (action == EditAction::insertText ||
-        action == EditAction::insertIMEText ||
-        action == EditAction::deleteSelection ||
-        IsStyleCachePreservingAction(action)) {
+    if (aAction == EditAction::insertText ||
+        aAction == EditAction::insertIMEText ||
+        aAction == EditAction::deleteSelection ||
+        IsStyleCachePreservingAction(aAction)) {
       NS_ENSURE_STATE(mHTMLEditor);
       mHTMLEditor->mTypeInState->UpdateSelState(selection);
       rv = ReapplyCachedStyles();
       NS_ENSURE_SUCCESS(rv, rv);
       ClearCachedStyles();
     }
   }
 
   NS_ENSURE_STATE(mHTMLEditor);
 
   nsresult rv =
     mHTMLEditor->HandleInlineSpellCheck(
-                   action, selection,
+                   aAction, selection,
                    mRangeItem->mStartContainer,
                    mRangeItem->mStartOffset,
                    rangeStartContainer,
                    rangeStartOffset,
                    rangeEndContainer,
                    rangeEndOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -580,35 +581,32 @@ HTMLEditRules::AfterEditInner(EditAction
   // adjust selection HINT if needed
   if (!mDidExplicitlySetInterline) {
     CheckInterlinePosition(*selection);
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 HTMLEditRules::WillDoAction(Selection* aSelection,
                             RulesInfo* aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
   MOZ_ASSERT(aInfo && aCancel && aHandled);
 
   *aCancel = false;
   *aHandled = false;
 
-  // my kingdom for dynamic cast
-  TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
-
   // Deal with actions for which we don't need to check whether the selection is
   // editable.
-  if (info->action == EditAction::outputText ||
-      info->action == EditAction::undo ||
-      info->action == EditAction::redo) {
+  if (aInfo->action == EditAction::outputText ||
+      aInfo->action == EditAction::undo ||
+      aInfo->action == EditAction::redo) {
     return TextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
   }
 
   // Nothing to do if there's no selection to act on
   if (!aSelection) {
     return NS_OK;
   }
   NS_ENSURE_TRUE(aSelection->RangeCount(), NS_OK);
@@ -633,92 +631,92 @@ HTMLEditRules::WillDoAction(Selection* a
 
     NS_ENSURE_STATE(mHTMLEditor);
     if (!mHTMLEditor->IsModifiableNode(range->GetCommonAncestor())) {
       *aCancel = true;
       return NS_OK;
     }
   }
 
-  switch (info->action) {
+  switch (aInfo->action) {
     case EditAction::insertText:
     case EditAction::insertIMEText:
       UndefineCaretBidiLevel(aSelection);
-      return WillInsertText(info->action, aSelection, aCancel, aHandled,
-                            info->inString, info->outString, info->maxLength);
+      return WillInsertText(aInfo->action, aSelection, aCancel, aHandled,
+                            aInfo->inString, aInfo->outString,
+                            aInfo->maxLength);
     case EditAction::loadHTML:
       return WillLoadHTML(aSelection, aCancel);
     case EditAction::insertBreak:
       UndefineCaretBidiLevel(aSelection);
       return WillInsertBreak(*aSelection, aCancel, aHandled);
     case EditAction::deleteSelection:
-      return WillDeleteSelection(aSelection, info->collapsedAction,
-                                 info->stripWrappers, aCancel, aHandled);
+      return WillDeleteSelection(aSelection, aInfo->collapsedAction,
+                                 aInfo->stripWrappers, aCancel, aHandled);
     case EditAction::makeList:
-      return WillMakeList(aSelection, info->blockType, info->entireList,
-                          info->bulletType, aCancel, aHandled);
+      return WillMakeList(aSelection, aInfo->blockType, aInfo->entireList,
+                          aInfo->bulletType, aCancel, aHandled);
     case EditAction::indent:
       return WillIndent(aSelection, aCancel, aHandled);
     case EditAction::outdent:
       return WillOutdent(*aSelection, aCancel, aHandled);
     case EditAction::setAbsolutePosition:
       return WillAbsolutePosition(*aSelection, aCancel, aHandled);
     case EditAction::removeAbsolutePosition:
       return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
     case EditAction::align:
-      return WillAlign(*aSelection, *info->alignType, aCancel, aHandled);
+      return WillAlign(*aSelection, *aInfo->alignType, aCancel, aHandled);
     case EditAction::makeBasicBlock:
-      return WillMakeBasicBlock(*aSelection, *info->blockType, aCancel,
+      return WillMakeBasicBlock(*aSelection, *aInfo->blockType, aCancel,
                                 aHandled);
     case EditAction::removeList:
-      return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
+      return WillRemoveList(aSelection, aInfo->bOrdered, aCancel, aHandled);
     case EditAction::makeDefListItem:
-      return WillMakeDefListItem(aSelection, info->blockType, info->entireList,
-                                 aCancel, aHandled);
+      return WillMakeDefListItem(aSelection, aInfo->blockType,
+                                 aInfo->entireList, aCancel, aHandled);
     case EditAction::insertElement:
       WillInsert(*aSelection, aCancel);
       return NS_OK;
     case EditAction::decreaseZIndex:
       return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
     case EditAction::increaseZIndex:
       return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
     default:
       return TextEditRules::WillDoAction(aSelection, aInfo,
                                          aCancel, aHandled);
   }
 }
 
-NS_IMETHODIMP
+nsresult
 HTMLEditRules::DidDoAction(Selection* aSelection,
                            RulesInfo* aInfo,
                            nsresult aResult)
 {
-  TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
-  switch (info->action) {
+  switch (aInfo->action) {
     case EditAction::insertBreak:
       return DidInsertBreak(aSelection, aResult);
     case EditAction::deleteSelection:
-      return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
+      return DidDeleteSelection(aSelection, aInfo->collapsedAction, aResult);
     case EditAction::makeBasicBlock:
     case EditAction::indent:
     case EditAction::outdent:
     case EditAction::align:
       return DidMakeBasicBlock(aSelection, aInfo, aResult);
     case EditAction::setAbsolutePosition: {
       nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
       NS_ENSURE_SUCCESS(rv, rv);
       return DidAbsolutePosition();
     }
     default:
       // pass through to TextEditRules
       return TextEditRules::DidDoAction(aSelection, aInfo, aResult);
   }
 }
 
-NS_IMETHODIMP_(bool)
+bool
 HTMLEditRules::DocumentIsEmpty()
 {
   return !!mBogusNode;
 }
 
 nsresult
 HTMLEditRules::GetListState(bool* aMixed,
                             bool* aOL,
@@ -9528,17 +9526,17 @@ HTMLEditRules::WillRelativeChangeZIndex(
   AutoSelectionRestorer selectionRestorer(aSelection, mHTMLEditor);
 
   NS_ENSURE_STATE(mHTMLEditor);
   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
   int32_t zIndex;
   return absPosHTMLEditor->RelativeChangeElementZIndex(elt, aChange, &zIndex);
 }
 
-NS_IMETHODIMP
+nsresult
 HTMLEditRules::DocumentModified()
 {
   nsContentUtils::AddScriptRunner(
     NewRunnableMethod("HTMLEditRules::DocumentModifiedWorker",
                       this,
                       &HTMLEditRules::DocumentModifiedWorker));
   return NS_OK;
 }
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -79,29 +79,32 @@ class HTMLEditRules : public TextEditRul
                     , public nsIEditActionListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditRules, TextEditRules)
 
   HTMLEditRules();
 
-  // nsIEditRules methods
-  NS_IMETHOD Init(TextEditor* aTextEditor) override;
-  NS_IMETHOD DetachEditor() override;
-  NS_IMETHOD BeforeEdit(EditAction action,
-                        nsIEditor::EDirection aDirection) override;
-  NS_IMETHOD AfterEdit(EditAction action,
-                       nsIEditor::EDirection aDirection) override;
-  NS_IMETHOD WillDoAction(Selection* aSelection, RulesInfo* aInfo,
-                          bool* aCancel, bool* aHandled) override;
-  NS_IMETHOD DidDoAction(Selection* aSelection, RulesInfo* aInfo,
-                         nsresult aResult) override;
-  NS_IMETHOD_(bool) DocumentIsEmpty() override;
-  NS_IMETHOD DocumentModified() override;
+  // TextEditRules methods
+  virtual nsresult Init(TextEditor* aTextEditor) override;
+  virtual nsresult DetachEditor() override;
+  virtual nsresult BeforeEdit(EditAction aAction,
+                              nsIEditor::EDirection aDirection) override;
+  virtual nsresult AfterEdit(EditAction aAction,
+                             nsIEditor::EDirection aDirection) override;
+  virtual nsresult WillDoAction(Selection* aSelection,
+                                RulesInfo* aInfo,
+                                bool* aCancel,
+                                bool* aHandled) override;
+  virtual nsresult DidDoAction(Selection* aSelection,
+                               RulesInfo* aInfo,
+                               nsresult aResult) override;
+  virtual bool DocumentIsEmpty() override;
+  virtual nsresult DocumentModified() override;
 
   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);
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -135,18 +135,21 @@ HTMLEditor::HTMLEditor()
   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.
-  nsCOMPtr<nsIEditActionListener> mListener = do_QueryInterface(mRules);
-  RemoveEditActionListener(mListener);
+  if (mRules) {
+    nsCOMPtr<nsIEditActionListener> listener =
+      static_cast<nsIEditActionListener*>(mRules->AsHTMLEditRules());
+    RemoveEditActionListener(listener);
+  }
 
   //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;
     listener = do_QueryInterface(mTypeInState);
@@ -1417,17 +1420,17 @@ HTMLEditor::GetBetterInsertionPointFor(n
   return afterBRNode;
 }
 
 NS_IMETHODIMP
 HTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement,
                                      bool aDeleteSelection)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   nsCOMPtr<Element> element = do_QueryInterface(aElement);
   NS_ENSURE_TRUE(element, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
 
   CommitComposition();
   AutoPlaceholderBatch beginBatching(this);
@@ -1436,17 +1439,17 @@ HTMLEditor::InsertElementAtSelection(nsI
 
   RefPtr<Selection> selection = GetSelection();
   if (!selection) {
     return NS_ERROR_FAILURE;
   }
 
   // hand off to the rules system, see if it has anything to say about this
   bool cancel, handled;
-  TextRulesInfo ruleInfo(EditAction::insertElement);
+  RulesInfo ruleInfo(EditAction::insertElement);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled) {
     if (aDeleteSelection) {
       if (!IsBlockNode(element)) {
@@ -1651,19 +1654,17 @@ HTMLEditor::SetParagraphFormat(const nsA
 NS_IMETHODIMP
 HTMLEditor::GetParagraphState(bool* aMixed,
                               nsAString& outFormat)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
-  RefPtr<HTMLEditRules> htmlRules =
-    static_cast<HTMLEditRules*>(mRules.get());
-
+  RefPtr<HTMLEditRules> htmlRules(mRules->AsHTMLEditRules());
   return htmlRules->GetParagraphState(aMixed, outFormat);
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetBackgroundColorState(bool* aMixed,
                                     nsAString& aOutColor)
 {
   if (IsCSSEnabled()) {
@@ -1829,90 +1830,83 @@ HTMLEditor::GetListState(bool* aMixed,
                          bool* aOL,
                          bool* aUL,
                          bool* aDL)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
-  RefPtr<HTMLEditRules> htmlRules =
-    static_cast<HTMLEditRules*>(mRules.get());
-
+  RefPtr<HTMLEditRules> htmlRules(mRules->AsHTMLEditRules());
   return htmlRules->GetListState(aMixed, aOL, aUL, aDL);
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetListItemState(bool* aMixed,
                              bool* aLI,
                              bool* aDT,
                              bool* aDD)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
 
-  RefPtr<HTMLEditRules> htmlRules =
-    static_cast<HTMLEditRules*>(mRules.get());
-
+  RefPtr<HTMLEditRules> htmlRules(mRules->AsHTMLEditRules());
   return htmlRules->GetListItemState(aMixed, aLI, aDT, aDD);
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetAlignment(bool* aMixed,
                          nsIHTMLEditor::EAlignment* aAlign)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ENSURE_TRUE(aMixed && aAlign, NS_ERROR_NULL_POINTER);
-  RefPtr<HTMLEditRules> htmlRules =
-    static_cast<HTMLEditRules*>(mRules.get());
-
+
+  RefPtr<HTMLEditRules> htmlRules(mRules->AsHTMLEditRules());
   return htmlRules->GetAlignment(aMixed, aAlign);
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetIndentState(bool* aCanIndent,
                            bool* aCanOutdent)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_NULL_POINTER);
 
-  RefPtr<HTMLEditRules> htmlRules =
-    static_cast<HTMLEditRules*>(mRules.get());
-
+  RefPtr<HTMLEditRules> htmlRules(mRules->AsHTMLEditRules());
   return htmlRules->GetIndentState(aCanIndent, aCanOutdent);
 }
 
 NS_IMETHODIMP
 HTMLEditor::MakeOrChangeList(const nsAString& aListType,
                              bool entireList,
                              const nsAString& aBulletType)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::makeList, nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  TextRulesInfo ruleInfo(EditAction::makeList);
+  RulesInfo ruleInfo(EditAction::makeList);
   ruleInfo.blockType = &aListType;
   ruleInfo.entireList = entireList;
   ruleInfo.bulletType = &aBulletType;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
@@ -1974,28 +1968,28 @@ HTMLEditor::MakeOrChangeList(const nsASt
 NS_IMETHODIMP
 HTMLEditor::RemoveList(const nsAString& aListType)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::removeList, nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  TextRulesInfo ruleInfo(EditAction::removeList);
+  RulesInfo ruleInfo(EditAction::removeList);
   if (aListType.LowerCaseEqualsLiteral("ol")) {
     ruleInfo.bOrdered = true;
   } else {
     ruleInfo.bOrdered = false;
   }
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
@@ -2009,28 +2003,28 @@ HTMLEditor::RemoveList(const nsAString& 
 nsresult
 HTMLEditor::MakeDefinitionItem(const nsAString& aItemType)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::makeDefListItem,
                                nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  TextRulesInfo ruleInfo(EditAction::makeDefListItem);
+  RulesInfo ruleInfo(EditAction::makeDefListItem);
   ruleInfo.blockType = &aItemType;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled) {
     // todo: no default for now.  we count on rules to handle it.
@@ -2042,28 +2036,28 @@ HTMLEditor::MakeDefinitionItem(const nsA
 nsresult
 HTMLEditor::InsertBasicBlock(const nsAString& aBlockType)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::makeBasicBlock,
                                nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  TextRulesInfo ruleInfo(EditAction::makeBasicBlock);
+  RulesInfo ruleInfo(EditAction::makeBasicBlock);
   ruleInfo.blockType = &aBlockType;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled && selection->Collapsed()) {
     nsRange* firstRange = selection->GetRangeAt(0);
@@ -2123,31 +2117,31 @@ HTMLEditor::InsertBasicBlock(const nsASt
 NS_IMETHODIMP
 HTMLEditor::Indent(const nsAString& aIndent)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
   EditAction opID = EditAction::indent;
   if (aIndent.LowerCaseEqualsLiteral("outdent")) {
     opID = EditAction::outdent;
   }
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  TextRulesInfo ruleInfo(opID);
+  RulesInfo ruleInfo(opID);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled && selection->Collapsed() && aIndent.EqualsLiteral("indent")) {
     nsRange* firstRange = selection->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
@@ -2209,27 +2203,27 @@ HTMLEditor::Indent(const nsAString& aInd
 }
 
 //TODO: IMPLEMENT ALIGNMENT!
 
 NS_IMETHODIMP
 HTMLEditor::Align(const nsAString& aAlignType)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::align, nsIEditor::eNext);
 
   bool cancel, handled;
 
   // Find out if the selection is collapsed:
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  TextRulesInfo ruleInfo(EditAction::align);
+  RulesInfo ruleInfo(EditAction::align);
   ruleInfo.alignType = &aAlignType;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
@@ -3212,17 +3206,17 @@ HTMLEditor::DoContentInserted(nsIDocumen
   }
   // We don't need to handle our own modifications
   else if (!mAction && container->IsEditable()) {
     if (IsMozEditorBogusNode(aChild)) {
       // Ignore insertion of the bogus node
       return;
     }
     // Protect the edit rules object from dying
-    nsCOMPtr<nsIEditRules> rules(mRules);
+    RefPtr<TextEditRules> rules(mRules);
     rules->DocumentModified();
 
     // Update spellcheck for only the newly-inserted node (bug 743819)
     if (mInlineSpellChecker) {
       RefPtr<nsRange> range = new nsRange(aChild);
       nsIContent* endContent = aChild;
       if (aInsertedOrAppended == eAppended) {
         // Maybe more than 1 child was appended.
@@ -3258,17 +3252,17 @@ HTMLEditor::ContentRemoved(nsIDocument* 
   }
   // We don't need to handle our own modifications
   else if (!mAction && (aContainer ? aContainer->IsEditable() : aDocument->IsEditable())) {
     if (aChild && IsMozEditorBogusNode(aChild)) {
       // Ignore removal of the bogus node
       return;
     }
     // Protect the edit rules object from dying
-    nsCOMPtr<nsIEditRules> rules(mRules);
+    RefPtr<TextEditRules> rules(mRules);
     rules->DocumentModified();
   }
 }
 
 NS_IMETHODIMP_(bool)
 HTMLEditor::IsModifiableNode(nsIDOMNode* aNode)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
@@ -3401,34 +3395,34 @@ HTMLEditor::StyleSheetLoaded(StyleSheet*
  * All editor operations which alter the doc should be prefaced
  * with a call to StartOperation, naming the action and direction.
  */
 NS_IMETHODIMP
 HTMLEditor::StartOperation(EditAction opID,
                            nsIEditor::EDirection aDirection)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   EditorBase::StartOperation(opID, aDirection);  // will set mAction, mDirection
   if (rules) {
     return rules->BeforeEdit(mAction, mDirection);
   }
   return NS_OK;
 }
 
 /**
  * All editor operations which alter the doc should be followed
  * with a call to EndOperation.
  */
 NS_IMETHODIMP
 HTMLEditor::EndOperation()
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // post processing
   nsresult rv = rules ? rules->AfterEdit(mAction, mDirection) : NS_OK;
   EditorBase::EndOperation();  // will clear mAction, mDirection
   return rv;
 }
 
 bool
@@ -3466,17 +3460,17 @@ HTMLEditor::IsContainer(nsINode* aNode)
 nsresult
 HTMLEditor::SelectEntireDocument(Selection* aSelection)
 {
   if (!aSelection || !mRules) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // is doc empty?
   if (rules->DocumentIsEmpty()) {
     // get editor root node
     Element* rootElement = GetRoot();
 
     // if its empty dont select entire doc - that would select the bogus node
     return aSelection->Collapse(rootElement, 0);
@@ -4224,31 +4218,31 @@ HTMLEditor::SetIsCSSEnabled(bool aIsCSSP
 // Set the block background color
 nsresult
 HTMLEditor::SetCSSBackgroundColor(const nsAString& aColor)
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
   CommitComposition();
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
   bool isCollapsed = selection->Collapsed();
 
   AutoPlaceholderBatch batchIt(this);
   AutoRules beginRulesSniffing(this, EditAction::insertElement,
                                nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
   AutoTransactionsConserveSelection dontChangeMySelection(this);
 
   bool cancel, handled;
-  TextRulesInfo ruleInfo(EditAction::setTextProperty);
+  RulesInfo ruleInfo(EditAction::setTextProperty);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!cancel && !handled) {
     // Loop through the ranges in the selection
     for (uint32_t i = 0; i < selection->RangeCount(); i++) {
       RefPtr<nsRange> range = selection->GetRangeAt(i);
       NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
 
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -6,30 +6,30 @@
 #ifndef mozilla_HTMLEditor_h
 #define mozilla_HTMLEditor_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/CSSEditUtils.h"
 #include "mozilla/ManualNAC.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/TextEditor.h"
+#include "mozilla/TextEditRules.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 
 #include "nsAttrName.h"
 #include "nsCOMPtr.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsIDocumentObserver.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIEditorMailSupport.h"
 #include "nsIEditorStyleSheets.h"
 #include "nsIEditorUtils.h"
-#include "nsIEditRules.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"
@@ -43,17 +43,16 @@ class nsILinkHandler;
 class nsTableWrapperFrame;
 class nsIDOMRange;
 class nsRange;
 
 namespace mozilla {
 class AutoSelectionSetterAfterTableEdit;
 class HTMLEditorEventListener;
 class HTMLEditRules;
-class TextEditRules;
 class TypeInState;
 class WSRunObject;
 struct PropItem;
 template<class T> class OwningNonNull;
 namespace dom {
 class DocumentFragment;
 } // namespace dom
 namespace widget {
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Base64.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SelectionState.h"
+#include "mozilla/TextEditRules.h"
 #include "nsAString.h"
 #include "nsCOMPtr.h"
 #include "nsCRTGlue.h" // for CRLF
 #include "nsComponentManagerUtils.h"
 #include "nsIScriptError.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsDependentSubstring.h"
@@ -37,17 +38,16 @@
 #include "nsIContent.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDocument.h"
-#include "nsIEditRules.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsIParserUtils.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsPrimitives.h"
@@ -98,20 +98,20 @@ HTMLEditor::LoadHTML(const nsAString& aI
   CommitComposition();
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::loadHTML, nsIEditor::eNext);
 
   // Get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
-  TextRulesInfo ruleInfo(EditAction::loadHTML);
+  RulesInfo ruleInfo(EditAction::loadHTML);
   bool cancel, handled;
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) {
     return NS_OK; // rules canceled the operation
   }
 
   if (!handled) {
     // Delete Selection, but only if it isn't collapsed, see bug #106269
@@ -198,17 +198,17 @@ HTMLEditor::DoInsertHTMLWithContext(cons
                                     int32_t aDestOffset,
                                     bool aDeleteSelection,
                                     bool aTrustedInput,
                                     bool aClearStyle)
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
   // Prevent the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // force IME commit; set up rules sniffing and batching
   CommitComposition();
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::htmlPaste, nsIEditor::eNext);
 
   // Get selection
   RefPtr<Selection> selection = GetSelection();
@@ -330,17 +330,17 @@ HTMLEditor::DoInsertHTMLWithContext(cons
       rv = DeleteTableCell(1);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     // collapse selection to beginning of deleted table content
     selection->CollapseToStart();
   }
 
   // give rules a chance to handle or cancel
-  TextRulesInfo ruleInfo(EditAction::insertElement);
+  RulesInfo ruleInfo(EditAction::insertElement);
   bool cancel, handled;
   rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) {
     return NS_OK; // rules canceled the operation
   }
 
   if (!handled) {
@@ -1608,20 +1608,20 @@ HTMLEditor::PasteAsCitedQuotation(const 
   AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
                                nsIEditor::eNext);
 
   // get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   // give rules a chance to handle or cancel
-  TextRulesInfo ruleInfo(EditAction::insertElement);
+  RulesInfo ruleInfo(EditAction::insertElement);
   bool cancel, handled;
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   nsCOMPtr<Element> newNode =
     DeleteSelectionAndCreateElement(*nsGkAtoms::blockquote);
@@ -1807,20 +1807,20 @@ HTMLEditor::InsertAsPlaintextQuotation(c
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
                                nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  TextRulesInfo ruleInfo(EditAction::insertElement);
+  RulesInfo ruleInfo(EditAction::insertElement);
   bool cancel, handled;
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   // Wrap the inserted quote in a <span> so we can distinguish it. If we're
   // inserting into the <body>, we use a <span> which is displayed as a block
@@ -1906,20 +1906,20 @@ HTMLEditor::InsertAsCitedQuotation(const
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
                                nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  TextRulesInfo ruleInfo(EditAction::insertElement);
+  RulesInfo ruleInfo(EditAction::insertElement);
   bool cancel, handled;
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   nsCOMPtr<Element> newNode =
     DeleteSelectionAndCreateElement(*nsGkAtoms::blockquote);
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -6,32 +6,32 @@
 #include "mozilla/HTMLEditor.h"
 
 #include "HTMLEditUtils.h"
 #include "TextEditUtils.h"
 #include "TypeInState.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/SelectionState.h"
+#include "mozilla/TextEditRules.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/mozalloc.h"
 #include "nsAString.h"
 #include "nsAttrName.h"
 #include "nsCOMPtr.h"
 #include "nsCaseTreatment.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsAtom.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMElement.h"
-#include "nsIEditRules.h"
 #include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsISupportsImpl.h"
 #include "nsLiteralString.h"
 #include "nsRange.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
 #include "nsStringFwd.h"
@@ -65,17 +65,17 @@ HTMLEditor::SetInlineProperty(const nsAS
 
 nsresult
 HTMLEditor::SetInlineProperty(nsAtom* aProperty,
                               const nsAString& aAttribute,
                               const nsAString& aValue)
 {
   NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   CommitComposition();
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   if (selection->Collapsed()) {
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
@@ -85,17 +85,17 @@ HTMLEditor::SetInlineProperty(nsAtom* aP
 
   AutoPlaceholderBatch batchIt(this);
   AutoRules beginRulesSniffing(this, EditAction::insertElement,
                                nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
   AutoTransactionsConserveSelection dontChangeMySelection(this);
 
   bool cancel, handled;
-  TextRulesInfo ruleInfo(EditAction::setTextProperty);
+  RulesInfo ruleInfo(EditAction::setTextProperty);
   // Protect the edit rules object from dying
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!cancel && !handled) {
     // Loop through the ranges in the selection
     AutoRangeArray arrayOfRanges(selection);
     for (auto& range : arrayOfRanges.mRanges) {
       // Adjust range to include any ancestors whose children are entirely
@@ -1216,19 +1216,19 @@ HTMLEditor::RemoveInlinePropertyImpl(nsA
 
   AutoPlaceholderBatch batchIt(this);
   AutoRules beginRulesSniffing(this, EditAction::removeTextProperty,
                                nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
   AutoTransactionsConserveSelection dontChangeMySelection(this);
 
   bool cancel, handled;
-  TextRulesInfo ruleInfo(EditAction::removeTextProperty);
+  RulesInfo ruleInfo(EditAction::removeTextProperty);
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!cancel && !handled) {
     // Loop through the ranges in the selection
     // Since ranges might be modified by SplitStyleAboveRange, we need hold
     // current ranges
     AutoRangeArray arrayOfRanges(selection);
     for (auto& range : arrayOfRanges.mRanges) {
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/TextEditRules.h"
 
+#include "HTMLEditRules.h"
 #include "TextEditUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEditor.h"
@@ -58,16 +59,17 @@ using namespace dom;
 TextEditRules::TextEditRules()
   : mTextEditor(nullptr)
   , mPasswordIMEIndex(0)
   , mCachedSelectionOffset(0)
   , mActionNesting(0)
   , mLockRulesSniffing(false)
   , mDidExplicitlySetInterline(false)
   , mDeleteBidiImmediately(false)
+  , mIsHTMLEditRules(false)
   , mTheAction(EditAction::none)
   , mLastStart(0)
   , mLastLength(0)
 {
   InitFields();
 }
 
 void
@@ -95,29 +97,40 @@ TextEditRules::~TextEditRules()
    // do NOT delete mTextEditor here.  We do not hold a ref count to
    // mTextEditor.  mTextEditor owns our lifespan.
 
   if (mTimer) {
     mTimer->Cancel();
   }
 }
 
+HTMLEditRules*
+TextEditRules::AsHTMLEditRules()
+{
+  return mIsHTMLEditRules ? static_cast<HTMLEditRules*>(this) : nullptr;
+}
+
+const HTMLEditRules*
+TextEditRules::AsHTMLEditRules() const
+{
+  return mIsHTMLEditRules ? static_cast<const HTMLEditRules*>(this) : nullptr;
+}
+
 NS_IMPL_CYCLE_COLLECTION(TextEditRules, mBogusNode, mCachedSelectionNode)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditRules)
-  NS_INTERFACE_MAP_ENTRY(nsIEditRules)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY(nsINamed)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
 
-NS_IMETHODIMP
+nsresult
 TextEditRules::Init(TextEditor* aTextEditor)
 {
   if (!aTextEditor) {
     return NS_ERROR_NULL_POINTER;
   }
 
   InitFields();
 
@@ -145,93 +158,93 @@ TextEditRules::Init(TextEditor* aTextEdi
   }
 
   mDeleteBidiImmediately =
     Preferences::GetBool("bidi.edit.delete_immediately", false);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 TextEditRules::SetInitialValue(const nsAString& aValue)
 {
   if (IsPasswordEditor()) {
     mPasswordText = aValue;
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 TextEditRules::DetachEditor()
 {
   if (mTimer) {
     mTimer->Cancel();
   }
   mTextEditor = nullptr;
   return NS_OK;
 }
 
-NS_IMETHODIMP
-TextEditRules::BeforeEdit(EditAction action,
+nsresult
+TextEditRules::BeforeEdit(EditAction aAction,
                           nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   AutoLockRulesSniffing lockIt(this);
   mDidExplicitlySetInterline = false;
   if (!mActionNesting) {
     // let rules remember the top level action
-    mTheAction = action;
+    mTheAction = aAction;
   }
   mActionNesting++;
 
   // get the selection and cache the position before editing
   if (NS_WARN_IF(!mTextEditor)) {
     return NS_ERROR_FAILURE;
   }
   RefPtr<TextEditor> textEditor = mTextEditor;
   RefPtr<Selection> selection = textEditor->GetSelection();
   NS_ENSURE_STATE(selection);
 
-  if (action == EditAction::setText) {
+  if (aAction == EditAction::setText) {
     // setText replaces all text, so mCachedSelectionNode might be invalid on
     // AfterEdit.
     // Since this will be used as start position of spellchecker, we should
     // use root instead.
     mCachedSelectionNode = textEditor->GetRoot();
     mCachedSelectionOffset = 0;
   } else {
     mCachedSelectionNode = selection->GetAnchorNode();
     mCachedSelectionOffset = selection->AnchorOffset();
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-TextEditRules::AfterEdit(EditAction action,
+nsresult
+TextEditRules::AfterEdit(EditAction aAction,
                          nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   AutoLockRulesSniffing lockIt(this);
 
   NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
   if (!--mActionNesting) {
     NS_ENSURE_STATE(mTextEditor);
     RefPtr<Selection> selection = mTextEditor->GetSelection();
     NS_ENSURE_STATE(selection);
 
     NS_ENSURE_STATE(mTextEditor);
     nsresult rv =
-      mTextEditor->HandleInlineSpellCheck(action, selection,
+      mTextEditor->HandleInlineSpellCheck(aAction, selection,
                                           mCachedSelectionNode,
                                           mCachedSelectionOffset,
                                           nullptr, 0, nullptr, 0);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // no longer uses mCachedSelectionNode, so release it.
     mCachedSelectionNode = nullptr;
 
@@ -250,93 +263,89 @@ TextEditRules::AfterEdit(EditAction acti
     NS_ENSURE_SUCCESS(rv, rv);
 
     // collapse the selection to the trailing BR if it's at the end of our text node
     CollapseSelectionToTrailingBRIfNeeded(selection);
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 TextEditRules::WillDoAction(Selection* aSelection,
                             RulesInfo* aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
   // null selection is legal
   MOZ_ASSERT(aInfo && aCancel && aHandled);
 
   *aCancel = false;
   *aHandled = false;
 
   // my kingdom for dynamic cast
-  TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
-
-  switch (info->action) {
+  switch (aInfo->action) {
     case EditAction::insertBreak:
       UndefineCaretBidiLevel(aSelection);
-      return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
+      return WillInsertBreak(aSelection, aCancel, aHandled, aInfo->maxLength);
     case EditAction::insertText:
     case EditAction::insertIMEText:
       UndefineCaretBidiLevel(aSelection);
-      return WillInsertText(info->action, aSelection, aCancel, aHandled,
-                            info->inString, info->outString, info->maxLength);
+      return WillInsertText(aInfo->action, aSelection, aCancel, aHandled,
+                            aInfo->inString, aInfo->outString,
+                            aInfo->maxLength);
     case EditAction::setText:
       UndefineCaretBidiLevel(aSelection);
-      return WillSetText(*aSelection, aCancel, aHandled, info->inString,
-                         info->maxLength);
+      return WillSetText(*aSelection, aCancel, aHandled, aInfo->inString,
+                         aInfo->maxLength);
     case EditAction::deleteSelection:
-      return WillDeleteSelection(aSelection, info->collapsedAction,
+      return WillDeleteSelection(aSelection, aInfo->collapsedAction,
                                  aCancel, aHandled);
     case EditAction::undo:
       return WillUndo(aSelection, aCancel, aHandled);
     case EditAction::redo:
       return WillRedo(aSelection, aCancel, aHandled);
     case EditAction::setTextProperty:
       return WillSetTextProperty(aSelection, aCancel, aHandled);
     case EditAction::removeTextProperty:
       return WillRemoveTextProperty(aSelection, aCancel, aHandled);
     case EditAction::outputText:
-      return WillOutputText(aSelection, info->outputFormat, info->outString,
-                            info->flags, aCancel, aHandled);
+      return WillOutputText(aSelection, aInfo->outputFormat, aInfo->outString,
+                            aInfo->flags, aCancel, aHandled);
     case EditAction::insertElement:
       // i had thought this would be html rules only.  but we put pre elements
       // into plaintext mail when doing quoting for reply!  doh!
       WillInsert(*aSelection, aCancel);
       return NS_OK;
     default:
       return NS_ERROR_FAILURE;
   }
 }
 
-NS_IMETHODIMP
+nsresult
 TextEditRules::DidDoAction(Selection* aSelection,
                            RulesInfo* aInfo,
                            nsresult aResult)
 {
   NS_ENSURE_STATE(mTextEditor);
   // don't let any txns in here move the selection around behind our back.
   // Note that this won't prevent explicit selection setting from working.
   AutoTransactionsConserveSelection dontChangeMySelection(mTextEditor);
 
   NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
 
-  // my kingdom for dynamic cast
-  TextRulesInfo* info = static_cast<TextRulesInfo*>(aInfo);
-
-  switch (info->action) {
+  switch (aInfo->action) {
     case EditAction::insertBreak:
       return DidInsertBreak(aSelection, aResult);
     case EditAction::insertText:
     case EditAction::insertIMEText:
       return DidInsertText(aSelection, aResult);
     case EditAction::setText:
       return DidSetText(*aSelection, aResult);
     case EditAction::deleteSelection:
-      return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
+      return DidDeleteSelection(aSelection, aInfo->collapsedAction, aResult);
     case EditAction::undo:
       return DidUndo(aSelection, aResult);
     case EditAction::redo:
       return DidRedo(aSelection, aResult);
     case EditAction::setTextProperty:
       return DidSetTextProperty(aSelection, aResult);
     case EditAction::removeTextProperty:
       return DidRemoveTextProperty(aSelection, aResult);
@@ -348,17 +357,17 @@ TextEditRules::DidDoAction(Selection* aS
   }
 }
 
 /**
  * Return false if the editor has non-empty text nodes or non-text
  * nodes.  Otherwise, i.e., there is no meaningful content,
  * return true.
  */
-NS_IMETHODIMP_(bool)
+bool
 TextEditRules::DocumentIsEmpty()
 {
   bool retVal = false;
   if (!mTextEditor || NS_FAILED(mTextEditor->DocumentIsEmpty(&retVal))) {
     retVal = true;
   }
 
   return retVal;
@@ -1672,17 +1681,17 @@ TextEditRules::CreateBRInternal(const Ed
       //     in this case?
       return nullptr;
     }
   }
 
   return brElement.forget();
 }
 
-NS_IMETHODIMP
+nsresult
 TextEditRules::DocumentModified()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 bool
 TextEditRules::IsPasswordEditor() const
 {
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -4,29 +4,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_TextEditRules_h
 #define mozilla_TextEditRules_h
 
 #include "mozilla/EditorBase.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIEditRules.h"
 #include "nsIEditor.h"
 #include "nsINamed.h"
 #include "nsISupportsImpl.h"
 #include "nsITimer.h"
 #include "nsString.h"
 #include "nscore.h"
 
 class nsIDOMNode;
 
 namespace mozilla {
 
 class AutoLockRulesSniffing;
+class HTMLEditRules;
+class RulesInfo;
 class TextEditor;
 namespace dom {
 class Selection;
 } // namespace dom
 
 /**
  * Object that encapsulates HTML text-specific editing rules.
  *
@@ -34,49 +35,51 @@ class Selection;
  * 1. All data manipulation is through the editor.
  *    Content nodes in the document tree must <B>not</B> be manipulated
  *    directly.  Content nodes in document fragments that are not part of the
  *    document itself may be manipulated at will.  Operations on document
  *    fragments must <B>not</B> go through the editor.
  * 2. Selection must not be explicitly set by the rule method.
  *    Any manipulation of Selection must be done by the editor.
  */
-class TextEditRules : public nsIEditRules
-                    , public nsITimerCallback
+class TextEditRules : public nsITimerCallback
                     , public nsINamed
 {
 public:
   typedef dom::Element Element;
   typedef dom::Selection Selection;
   typedef dom::Text Text;
   template<typename T> using OwningNonNull = OwningNonNull<T>;
 
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TextEditRules, nsIEditRules)
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TextEditRules, nsITimerCallback)
 
   TextEditRules();
 
-  // nsIEditRules methods
-  NS_IMETHOD Init(TextEditor* aTextEditor) override;
-  NS_IMETHOD SetInitialValue(const nsAString& aValue) override;
-  NS_IMETHOD DetachEditor() override;
-  NS_IMETHOD BeforeEdit(EditAction action,
-                        nsIEditor::EDirection aDirection) override;
-  NS_IMETHOD AfterEdit(EditAction action,
-                       nsIEditor::EDirection aDirection) override;
-  NS_IMETHOD WillDoAction(Selection* aSelection, RulesInfo* aInfo,
-                          bool* aCancel, bool* aHandled) override;
-  NS_IMETHOD DidDoAction(Selection* aSelection, RulesInfo* aInfo,
-                         nsresult aResult) override;
-  NS_IMETHOD_(bool) DocumentIsEmpty() override;
-  NS_IMETHOD DocumentModified() override;
+  HTMLEditRules* AsHTMLEditRules();
+  const HTMLEditRules* AsHTMLEditRules() const;
 
-  // nsINamed methods
-  NS_DECL_NSINAMED
+  virtual nsresult Init(TextEditor* aTextEditor);
+  virtual nsresult SetInitialValue(const nsAString& aValue);
+  virtual nsresult DetachEditor();
+  virtual nsresult BeforeEdit(EditAction aAction,
+                              nsIEditor::EDirection aDirection);
+  virtual nsresult AfterEdit(EditAction aAction,
+                             nsIEditor::EDirection aDirection);
+  virtual nsresult WillDoAction(Selection* aSelection,
+                                RulesInfo* aInfo,
+                                bool* aCancel,
+                                bool* aHandled);
+  virtual nsresult DidDoAction(Selection* aSelection,
+                               RulesInfo* aInfo,
+                               nsresult aResult);
+  virtual bool DocumentIsEmpty();
+  virtual nsresult DocumentModified();
 
 protected:
   virtual ~TextEditRules();
 
 public:
   void ResetIMETextPWBuf();
 
   /**
@@ -292,47 +295,54 @@ protected:
   // Cached selected offset.
   uint32_t mCachedSelectionOffset;
   uint32_t mActionNesting;
   bool mLockRulesSniffing;
   bool mDidExplicitlySetInterline;
   // In bidirectional text, delete characters not visually adjacent to the
   // caret without moving the caret first.
   bool mDeleteBidiImmediately;
+  bool mIsHTMLEditRules;
   // The top level editor action.
   EditAction mTheAction;
   nsCOMPtr<nsITimer> mTimer;
   uint32_t mLastStart;
   uint32_t mLastLength;
 
   // friends
   friend class AutoLockRulesSniffing;
 };
 
-// TODO: This class (almost struct, though) is ugly and its size isn't
-//       optimized.  Should be refined later.
-class TextRulesInfo final : public RulesInfo
+/**
+ * An object to encapsulate any additional info needed to be passed
+ * to rules system by the editor.
+ * TODO: This class (almost struct, though) is ugly and its size isn't
+ *       optimized.  Should be refined later.
+ */
+class RulesInfo final
 {
 public:
-  explicit TextRulesInfo(EditAction aAction)
-    : RulesInfo(aAction)
+  explicit RulesInfo(EditAction aAction)
+    : action(aAction)
     , inString(nullptr)
     , outString(nullptr)
     , outputFormat(nullptr)
     , maxLength(-1)
     , flags(0)
     , collapsedAction(nsIEditor::eNext)
     , stripWrappers(nsIEditor::eStrip)
     , bOrdered(false)
     , entireList(false)
     , bulletType(nullptr)
     , alignType(nullptr)
     , blockType(nullptr)
   {}
 
+  EditAction action;
+
   // EditAction::insertText / EditAction::insertIMEText
   const nsAString* inString;
   nsAString* outString;
   const nsAString* outputFormat;
   int32_t maxLength;
 
   // EditAction::outputText
   uint32_t flags;
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -34,17 +34,16 @@
 #include "nsIClipboard.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMNode.h"
 #include "nsIDocumentEncoder.h"
-#include "nsIEditRules.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISelectionController.h"
 #include "nsISupportsPrimitives.h"
 #include "nsITransferable.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsNameSpaceManager.h"
 #include "nsLiteralString.h"
@@ -633,17 +632,17 @@ TextEditor::DeleteSelection(EDirection a
 {
   MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
 
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // delete placeholder txns merge.
   AutoPlaceholderBatch batch(this, nsGkAtoms::DeleteTxnName);
   AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aAction);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
@@ -659,17 +658,17 @@ TextEditor::DeleteSelection(EDirection a
     if (mCaretStyle == 1) {
       nsresult rv = selection->CollapseToStart();
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       aAction = eNone;
     }
   }
 
-  TextRulesInfo ruleInfo(EditAction::deleteSelection);
+  RulesInfo ruleInfo(EditAction::deleteSelection);
   ruleInfo.collapsedAction = aAction;
   ruleInfo.stripWrappers = aStripWrappers;
   bool cancel, handled;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!cancel && !handled) {
     rv = DeleteSelectionImpl(aAction, aStripWrappers);
   }
@@ -683,33 +682,33 @@ TextEditor::DeleteSelection(EDirection a
 NS_IMETHODIMP
 TextEditor::InsertText(const nsAString& aStringToInsert)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   EditAction opID = EditAction::insertText;
   if (ShouldHandleIMEComposition()) {
     opID = EditAction::insertIMEText;
   }
   AutoPlaceholderBatch batch(this, nullptr);
   AutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   nsAutoString resultString;
   // XXX can we trust instring to outlive ruleInfo,
   // XXX and ruleInfo not to refer to instring in its dtor?
   //nsAutoString instring(aStringToInsert);
-  TextRulesInfo ruleInfo(opID);
+  RulesInfo ruleInfo(opID);
   ruleInfo.inString = &aStringToInsert;
   ruleInfo.outString = &resultString;
   ruleInfo.maxLength = mMaxTextLength;
 
   bool cancel, handled;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!cancel && !handled) {
@@ -725,26 +724,26 @@ TextEditor::InsertText(const nsAString& 
 NS_IMETHODIMP
 TextEditor::InsertLineBreak()
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::insertBreak, nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  TextRulesInfo ruleInfo(EditAction::insertBreak);
+  RulesInfo ruleInfo(EditAction::insertBreak);
   ruleInfo.maxLength = mMaxTextLength;
   bool cancel, handled;
   // XXX DidDoAction() won't be called when this returns error.  Perhaps,
   //     we should move the code between WillDoAction() and DidDoAction()
   //     to a new method and guarantee that DidDoAction() is always called
   //     after WillDoAction().
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -812,28 +811,28 @@ TextEditor::InsertLineBreak()
 NS_IMETHODIMP
 TextEditor::SetText(const nsAString& aString)
 {
   if (NS_WARN_IF(!mRules)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // delete placeholder txns merge.
   AutoPlaceholderBatch batch(this, nullptr);
   AutoRules beginRulesSniffing(this, EditAction::setText, nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_NULL_POINTER;
   }
-  TextRulesInfo ruleInfo(EditAction::setText);
+  RulesInfo ruleInfo(EditAction::setText);
   ruleInfo.inString = &aString;
   ruleInfo.maxLength = mMaxTextLength;
 
   bool cancel;
   bool handled;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -870,20 +869,18 @@ TextEditor::SetText(const nsAString& aSt
 nsresult
 TextEditor::BeginIMEComposition(WidgetCompositionEvent* aEvent)
 {
   NS_ENSURE_TRUE(!mComposition, NS_OK);
 
   if (IsPasswordEditor()) {
     NS_ENSURE_TRUE(mRules, NS_ERROR_NULL_POINTER);
     // Protect the edit rules object from dying
-    nsCOMPtr<nsIEditRules> rules(mRules);
-
-    TextEditRules* textEditRules = static_cast<TextEditRules*>(rules.get());
-    textEditRules->ResetIMETextPWBuf();
+    RefPtr<TextEditRules> rules(mRules);
+    rules->ResetIMETextPWBuf();
   }
 
   return EditorBase::BeginIMEComposition(aEvent);
 }
 
 nsresult
 TextEditor::UpdateIMEComposition(WidgetCompositionEvent* aCompsitionChangeEvent)
 {
@@ -954,17 +951,17 @@ TextEditor::GetInputEventTargetContent()
   return target.forget();
 }
 
 nsresult
 TextEditor::DocumentIsEmpty(bool* aIsEmpty)
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
-  if (static_cast<TextEditRules*>(mRules.get())->HasBogusNode()) {
+  if (mRules->HasBogusNode()) {
     *aIsEmpty = true;
     return NS_OK;
   }
 
   // Even if there is no bogus node, we should be detected as empty document
   // if all the children are text nodes and these have no content.
   Element* rootElement = GetRoot();
   if (!rootElement) {
@@ -1150,27 +1147,27 @@ TextEditor::SetNewlineHandling(int32_t a
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextEditor::Undo(uint32_t aCount)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   AutoUpdateViewBatch beginViewBatching(this);
 
   CommitComposition();
 
   NotifyEditorObservers(eNotifyEditorObserversOfBefore);
 
   AutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
 
-  TextRulesInfo ruleInfo(EditAction::undo);
+  RulesInfo ruleInfo(EditAction::undo);
   RefPtr<Selection> selection = GetSelection();
   bool cancel, handled;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
 
   if (!cancel && NS_SUCCEEDED(rv)) {
     rv = EditorBase::Undo(aCount);
     rv = rules->DidDoAction(selection, &ruleInfo, rv);
   }
@@ -1178,27 +1175,27 @@ TextEditor::Undo(uint32_t aCount)
   NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   return rv;
 }
 
 NS_IMETHODIMP
 TextEditor::Redo(uint32_t aCount)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   AutoUpdateViewBatch beginViewBatching(this);
 
   CommitComposition();
 
   NotifyEditorObservers(eNotifyEditorObserversOfBefore);
 
   AutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
 
-  TextRulesInfo ruleInfo(EditAction::redo);
+  RulesInfo ruleInfo(EditAction::redo);
   RefPtr<Selection> selection = GetSelection();
   bool cancel, handled;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
 
   if (!cancel && NS_SUCCEEDED(rv)) {
     rv = EditorBase::Redo(aCount);
     rv = rules->DidDoAction(selection, &ruleInfo, rv);
   }
@@ -1374,20 +1371,20 @@ TextEditor::GetAndInitDocEncoder(const n
 
 
 NS_IMETHODIMP
 TextEditor::OutputToString(const nsAString& aFormatType,
                            uint32_t aFlags,
                            nsAString& aOutputString)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   nsString resultString;
-  TextRulesInfo ruleInfo(EditAction::outputText);
+  RulesInfo ruleInfo(EditAction::outputText);
   ruleInfo.outString = &resultString;
   ruleInfo.flags = aFlags;
   // XXX Struct should store a nsAReadable*
   nsAutoString str(aFormatType);
   ruleInfo.outputFormat = &str;
   bool cancel, handled;
   nsresult rv = rules->WillDoAction(nullptr, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
@@ -1492,17 +1489,17 @@ TextEditor::PasteAsQuotation(int32_t aSe
   return rv;
 }
 
 NS_IMETHODIMP
 TextEditor::InsertAsQuotation(const nsAString& aQuotedText,
                               nsIDOMNode** aNodeInserted)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // Let the citer quote it for us:
   nsString quotedStuff;
   nsresult rv = InternetCiter::GetCiteString(aQuotedText, quotedStuff);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // It's best to put a blank line after the quoted text so that mails
   // written without thinking won't be so ugly.
@@ -1513,17 +1510,17 @@ TextEditor::InsertAsQuotation(const nsAS
   // get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   AutoPlaceholderBatch beginBatching(this);
   AutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  TextRulesInfo ruleInfo(EditAction::insertElement);
+  RulesInfo ruleInfo(EditAction::insertElement);
   bool cancel, handled;
   rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) {
     return NS_OK; // Rules canceled the operation.
   }
   if (!handled) {
     rv = InsertText(quotedStuff);
@@ -1638,50 +1635,50 @@ TextEditor::GetEmbeddedObjects(nsIArray*
  * All editor operations which alter the doc should be prefaced
  * with a call to StartOperation, naming the action and direction.
  */
 NS_IMETHODIMP
 TextEditor::StartOperation(EditAction opID,
                            nsIEditor::EDirection aDirection)
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   EditorBase::StartOperation(opID, aDirection);  // will set mAction, mDirection
   if (rules) {
     return rules->BeforeEdit(mAction, mDirection);
   }
   return NS_OK;
 }
 
 /**
  * All editor operations which alter the doc should be followed
  * with a call to EndOperation.
  */
 NS_IMETHODIMP
 TextEditor::EndOperation()
 {
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // post processing
   nsresult rv = rules ? rules->AfterEdit(mAction, mDirection) : NS_OK;
   EditorBase::EndOperation();  // will clear mAction, mDirection
   return rv;
 }
 
 nsresult
 TextEditor::SelectEntireDocument(Selection* aSelection)
 {
   if (!aSelection || !mRules) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // Protect the edit rules object from dying
-  nsCOMPtr<nsIEditRules> rules(mRules);
+  RefPtr<TextEditRules> rules(mRules);
 
   // is doc empty?
   if (rules->DocumentIsEmpty()) {
     // get root node
     nsCOMPtr<nsIDOMElement> rootElement = do_QueryInterface(GetRoot());
     NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
 
     // if it's empty don't select entire doc - that would select the bogus node
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -2,40 +2,39 @@
 /* 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 mozilla_TextEditor_h
 #define mozilla_TextEditor_h
 
 #include "mozilla/EditorBase.h"
+#include "mozilla/TextEditRules.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIEditor.h"
 #include "nsIEditorMailSupport.h"
 #include "nsIPlaintextEditor.h"
 #include "nsISupportsImpl.h"
 #include "nscore.h"
 
 class nsIContent;
 class nsIDOMDocument;
 class nsIDOMElement;
 class nsIDOMEvent;
 class nsIDOMNode;
 class nsIDocumentEncoder;
-class nsIEditRules;
 class nsIOutputStream;
 class nsISelectionController;
 class nsITransferable;
 
 namespace mozilla {
 
 class AutoEditInitRulesTrigger;
 class HTMLEditRules;
-class TextEditRules;
 namespace dom {
 class Selection;
 } // namespace dom
 
 /**
  * The text editor implementation.
  * Use to edit text document represented as a DOM tree.
  */
@@ -251,17 +250,17 @@ protected:
   bool FireClipboardEvent(EventMessage aEventMessage,
                           int32_t aSelectionType,
                           bool* aActionTaken = nullptr);
 
   bool UpdateMetaCharset(nsIDocument& aDocument,
                          const nsACString& aCharacterSet);
 
 protected:
-  nsCOMPtr<nsIEditRules> mRules;
+  RefPtr<TextEditRules> mRules;
   nsCOMPtr<nsIDocumentEncoder> mCachedDocumentEncoder;
   nsString mCachedDocumentEncoderType;
   int32_t mWrapColumn;
   int32_t mMaxTextLength;
   int32_t mInitTriggerCounter;
   int32_t mNewlineHandling;
   int32_t mCaretStyle;
 
--- a/editor/libeditor/TypeInState.cpp
+++ b/editor/libeditor/TypeInState.cpp
@@ -15,16 +15,21 @@
 #include "nsDebug.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMNode.h"
 #include "nsISupportsBase.h"
 #include "nsISupportsImpl.h"
 #include "nsReadableUtils.h"
 #include "nsStringFwd.h"
 
+// Workaround for windows headers
+#ifdef SetProp
+#undef SetProp
+#endif
+
 class nsAtom;
 class nsIDOMDocument;
 
 namespace mozilla {
 
 using namespace dom;
 
 /********************************************************************
--- a/editor/libeditor/moz.build
+++ b/editor/libeditor/moz.build
@@ -8,20 +8,16 @@ MOCHITEST_MANIFESTS += [
     'tests/browserscope/mochitest.ini',
     'tests/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
 
 BROWSER_CHROME_MANIFESTS += ['tests/browser.ini']
 
-EXPORTS += [
-    'nsIEditRules.h',
-]
-
 EXPORTS.mozilla += [
     'ChangeStyleTransaction.h',
     'CSSEditUtils.h',
     'EditorBase.h',
     'EditorController.h',
     'EditorDOMPoint.h',
     'EditorUtils.h',
     'EditTransactionBase.h',
deleted file mode 100644
--- a/editor/libeditor/nsIEditRules.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 nsIEditRules_h
-#define nsIEditRules_h
-
-#define NS_IEDITRULES_IID \
-{ 0x3836386d, 0x806a, 0x488d, \
-  { 0x8b, 0xab, 0xaf, 0x42, 0xbb, 0x4c, 0x90, 0x66 } }
-
-#include "mozilla/EditorBase.h" // for EditAction enum
-
-namespace mozilla {
-
-class TextEditor;
-namespace dom {
-class Selection;
-} // namespace dom
-
-/**
- * Base for an object to encapsulate any additional info needed to be passed
- * to rules system by the editor.
- */
-class RulesInfo
-{
-public:
-  explicit RulesInfo(EditAction aAction)
-    : action(aAction)
-  {}
-  virtual ~RulesInfo() {}
-
-  EditAction action;
-};
-
-} // namespace mozilla
-
-/**
- * Interface of editing rules.
- */
-class nsIEditRules : public nsISupports
-{
-public:
-  NS_DECLARE_STATIC_IID_ACCESSOR(NS_IEDITRULES_IID)
-
-//Interfaces for addref and release and queryinterface
-//NOTE: Use   NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
-
-  NS_IMETHOD Init(mozilla::TextEditor* aTextEditor) = 0;
-  NS_IMETHOD SetInitialValue(const nsAString& aValue) = 0;
-  NS_IMETHOD DetachEditor() = 0;
-  NS_IMETHOD BeforeEdit(EditAction action,
-                        nsIEditor::EDirection aDirection) = 0;
-  NS_IMETHOD AfterEdit(EditAction action,
-                       nsIEditor::EDirection aDirection) = 0;
-  NS_IMETHOD WillDoAction(mozilla::dom::Selection* aSelection,
-                          mozilla::RulesInfo* aInfo, bool* aCancel,
-                          bool* aHandled) = 0;
-  NS_IMETHOD DidDoAction(mozilla::dom::Selection* aSelection,
-                         mozilla::RulesInfo* aInfo, nsresult aResult) = 0;
-  NS_IMETHOD_(bool) DocumentIsEmpty() = 0;
-  NS_IMETHOD DocumentModified() = 0;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(nsIEditRules, NS_IEDITRULES_IID)
-
-#endif // #ifndef nsIEditRules_h