Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 28 May 2018 20:12:34 +0900
changeset 801971 45e919bbb946cb0ca41f9511aab9cb1361d5a289
parent 801916 3931f461c8e8668a264d52b51a4524aac39a7a16
child 801972 48faf65656467282814b015b4761f3a0ca5b720c
push id111796
push usermasayuki@d-toybox.com
push dateThu, 31 May 2018 06:40:56 +0000
reviewersm_kato
bugs1463985
milestone62.0a1
Bug 1463985 - part 1: Rename EditAction to EditSubAction and related stuff r?m_kato When we implement InputEvent.inputType, we need to set a stack class to record which edit action is currently handled. However, currently, we call smaller jobs as edit action. For example, when user types a character at selecting some characters, then, EditAction::deleteSelection is performed first, then, EditAction::insertText is performed. However, for the InputEvent.inputType, we need inserText information. So, for making new enum EditAction, we need to rename current EditAction to EditSubAction. And also this renames related stuff: EditorBase::mIsInEditAction -> EditorBase::mIsInEditSubAction EditorBase::IsInEditAction() -> EditorBase::IsInEditSubAction() EditorBase::mAction -> EditorBase::mTopLevelEditSubAction TextEditRules::mTheAction -> TextEditRules::mTopLevelEditSubAction EditorBase::StartOperation() -> EditorBase::OnStartToHandleTopLevelEditSubAction() EditorBase::EndOperation() -> EditorBase::OnEndHandlingTopLevelEditSubAction() AutoRules -> AutoTopLevelEditSubActionNotifier RulesInfo -> EditSubActionInfo MozReview-Commit-ID: cvSkPUjFm1
dom/events/IMEContentObserver.cpp
dom/html/nsTextEditorState.cpp
editor/libeditor/EditAction.h
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/EditorUtils.h
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/HTMLTableEditor.cpp
editor/libeditor/TextEditRules.cpp
editor/libeditor/TextEditRules.h
editor/libeditor/TextEditor.cpp
editor/libeditor/TextEditor.h
extensions/spellcheck/src/mozInlineSpellChecker.cpp
extensions/spellcheck/src/mozInlineSpellChecker.h
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -1638,17 +1638,17 @@ IMEContentObserver::IsSafeToNotifyIME() 
 
   // If it's in reflow, we should wait to finish the reflow.
   // FYI: This should be called again from Reflow() or ReflowInterruptible().
   if (IsReflowLocked()) {
     return false;
   }
 
   // If we're in handling an edit action, this method will be called later.
-  if (mEditorBase && mEditorBase->IsInEditAction()) {
+  if (mEditorBase && mEditorBase->IsInEditSubAction()) {
     return false;
   }
 
   return true;
 }
 
 void
 IMEContentObserver::FlushMergeableNotifications()
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -2010,17 +2010,17 @@ nsTextEditorState::UnbindFromFrame(nsTex
   // If it was, however, it should be unbounded from the same frame.
   MOZ_ASSERT(aFrame == mBoundFrame, "Unbinding from the wrong frame");
   NS_ENSURE_TRUE_VOID(!aFrame || aFrame == mBoundFrame);
 
   // If the editor is modified but nsIEditorObserver::EditAction() hasn't been
   // called yet, we need to notify it here because editor may be destroyed
   // before EditAction() is called if selection listener causes flushing layout.
   if (mTextListener && mTextEditor && mEditorInitialized &&
-      mTextEditor->IsInEditAction()) {
+      mTextEditor->IsInEditSubAction()) {
     mTextListener->OnEditActionHandled();
   }
 
   // We need to start storing the value outside of the editor if we're not
   // going to use it anymore, so retrieve it for now.
   nsAutoString value;
   GetValue(value, true);
 
--- a/editor/libeditor/EditAction.h
+++ b/editor/libeditor/EditAction.h
@@ -7,17 +7,17 @@
 #define mozilla_EditAction_h
 
 namespace mozilla {
 
 // This is int32_t instead of int16_t because nsIInlineSpellChecker.idl's
 // spellCheckAfterEditorChange is defined to take it as a long.
 // TODO: Make each name eFoo and investigate whether the numeric values
 //       still have some meaning.
-enum class EditAction : int32_t
+enum class EditSubAction : int32_t
 {
   ignore = -1,
 
   none = 0,
   undo,
   redo,
   insertNode,
   createNode,
@@ -54,14 +54,14 @@ enum class EditAction : int32_t
   setAbsolutePosition,
   removeAbsolutePosition,
   decreaseZIndex,
   increaseZIndex,
 };
 
 } // namespace mozilla
 
-inline bool operator!(const mozilla::EditAction& aOp)
+inline bool operator!(const mozilla::EditSubAction& aEditSubAction)
 {
-  return aOp == mozilla::EditAction::none;
+  return aEditSubAction == mozilla::EditSubAction::none;
 }
 
 #endif // #ifdef mozilla_EditAction_h
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -24,20 +24,20 @@
 #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 "mozilla/CheckedInt.h"         // for CheckedInt
 #include "mozilla/ComputedStyle.h"      // for ComputedStyle
-#include "mozilla/EditAction.h"         // for EditAction
+#include "mozilla/EditAction.h"         // for EditSubAction
 #include "mozilla/EditorDOMPoint.h"     // for EditorDOMPoint
 #include "mozilla/EditorSpellCheck.h"   // for EditorSpellCheck
-#include "mozilla/EditorUtils.h"        // for AutoRules, etc.
+#include "mozilla/EditorUtils.h"        // for various helper classes.
 #include "mozilla/EditTransactionBase.h" // for EditTransactionBase
 #include "mozilla/FlushType.h"          // for FlushType::Frames
 #include "mozilla/IMEContentObserver.h" // for IMEContentObserver
 #include "mozilla/IMEStateManager.h"    // for IMEStateManager
 #include "mozilla/mozalloc.h"           // for operator new, etc.
 #include "mozilla/mozInlineSpellChecker.h" // for mozInlineSpellChecker
 #include "mozilla/mozSpellChecker.h"    // for mozSpellChecker
 #include "mozilla/Preferences.h"        // for Preferences
@@ -155,25 +155,25 @@ EditorBase::MoveNodeWithTransaction(nsIC
                                     const EditorRawDOMPoint& aPointToInsert);
 
 EditorBase::EditorBase()
   : mPlaceholderName(nullptr)
   , mModCount(0)
   , mFlags(0)
   , mUpdateCount(0)
   , mPlaceholderBatch(0)
-  , mAction(EditAction::none)
+  , mTopLevelEditSubAction(EditSubAction::none)
   , mDirection(eNone)
   , mDocDirtyState(-1)
   , mSpellcheckCheckboxState(eTriUnset)
   , mShouldTxnSetSelection(true)
   , mDidPreDestroy(false)
   , mDidPostCreate(false)
   , mDispatchInputEvent(true)
-  , mIsInEditAction(false)
+  , mIsInEditSubAction(false)
   , mHidingCaret(false)
   , mSpellCheckerDictionaryUpdated(true)
   , mIsHTMLEditorClass(false)
 {
 }
 
 EditorBase::~EditorBase()
 {
@@ -253,17 +253,17 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorB
 
 nsresult
 EditorBase::Init(nsIDocument& aDocument,
                  Element* aRoot,
                  nsISelectionController* aSelectionController,
                  uint32_t aFlags,
                  const nsAString& aValue)
 {
-  MOZ_ASSERT(mAction == EditAction::none,
+  MOZ_ASSERT(mTopLevelEditSubAction == EditSubAction::none,
              "Initializing during an edit action is an error");
 
   // First only set flags, but other stuff shouldn't be initialized now.
   // Don't move this call after initializing mDocument.
   // SetFlags() can check whether it's called during initialization or not by
   // them.  Note that SetFlags() will be called by PostCreate().
 #ifdef DEBUG
   nsresult rv =
@@ -1351,17 +1351,19 @@ EditorBase::CreateNodeWithTransaction(
 {
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
   // 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.
   Unused << aPointToInsert.Offset();
 
-  AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::createNode,
+                                      nsIEditor::eNext);
 
   RefPtr<Element> newElement;
 
   RefPtr<CreateElementTransaction> transaction =
     CreateElementTransaction::Create(*this, aTagName, aPointToInsert);
   nsresult rv = DoTransaction(transaction);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     // XXX Why do we do this even when DoTransaction() returned error?
@@ -1427,17 +1429,19 @@ EditorBase::InsertNodeWithTransaction(
               nsIContent& aContentToInsert,
               const EditorDOMPointBase<PT, CT>& aPointToInsert)
 {
   if (NS_WARN_IF(!aPointToInsert.IsSet())) {
     return NS_ERROR_INVALID_ARG;
   }
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
-  AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertNode,
+                                      nsIEditor::eNext);
 
   RefPtr<InsertNodeTransaction> transaction =
     InsertNodeTransaction::Create(*this, aContentToInsert, aPointToInsert);
   nsresult rv = DoTransaction(transaction);
 
   mRangeUpdater.SelAdjInsertNode(aPointToInsert);
 
   if (mRules && mRules->AsHTMLEditRules()) {
@@ -1489,17 +1493,19 @@ EditorBase::SplitNodeWithTransaction(
 {
   if (NS_WARN_IF(!aStartOfRightNode.IsSet()) ||
       NS_WARN_IF(!aStartOfRightNode.GetContainerAsContent())) {
     aError.Throw(NS_ERROR_INVALID_ARG);
     return nullptr;
   }
   MOZ_ASSERT(aStartOfRightNode.IsSetAndValid());
 
-  AutoRules beginRulesSniffing(this, EditAction::splitNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::splitNode,
+                                      nsIEditor::eNext);
 
   // 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);
@@ -1554,18 +1560,19 @@ EditorBase::JoinNodes(nsINode* aLeftNode
 
 nsresult
 EditorBase::JoinNodesWithTransaction(nsINode& aLeftNode,
                                      nsINode& aRightNode)
 {
   nsCOMPtr<nsINode> parent = aLeftNode.GetParentNode();
   MOZ_ASSERT(parent);
 
-  AutoRules beginRulesSniffing(this, EditAction::joinNode,
-                               nsIEditor::ePrevious);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::joinNode,
+                                      nsIEditor::ePrevious);
 
   // Remember some values; later used for saved selection updating.
   // Find the offset between the nodes to be joined.
   int32_t offset = parent->ComputeIndexOf(&aRightNode);
   // Find the number of children of the lefthand node
   uint32_t oldLeftNodeLen = aLeftNode.Length();
 
   if (mRules && mRules->AsHTMLEditRules()) {
@@ -1622,18 +1629,19 @@ EditorBase::DeleteNode(nsINode* aNode)
     return NS_ERROR_INVALID_ARG;
   }
   return DeleteNodeWithTransaction(*aNode);
 }
 
 nsresult
 EditorBase::DeleteNodeWithTransaction(nsINode& aNode)
 {
-  AutoRules beginRulesSniffing(this, EditAction::createNode,
-                               nsIEditor::ePrevious);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::createNode,
+                                      nsIEditor::ePrevious);
 
   if (mRules && mRules->AsHTMLEditRules()) {
     Selection* selection = GetSelection();
     if (selection) {
       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
       htmlEditRules->WillDeleteNode(*selection, aNode);
     } else {
       NS_WARNING("Selection has gone");
@@ -2110,17 +2118,17 @@ private:
   bool mIsComposing;
 };
 
 void
 EditorBase::NotifyEditorObservers(NotificationForEditorObservers aNotification)
 {
   switch (aNotification) {
     case eNotifyEditorObserversOfEnd:
-      mIsInEditAction = false;
+      mIsInEditSubAction = false;
 
       if (mTextInputListener) {
         RefPtr<TextInputListener> listener = mTextInputListener;
         listener->OnEditActionHandled();
       }
 
       if (mIMEContentObserver) {
         RefPtr<IMEContentObserver> observer = mIMEContentObserver;
@@ -2137,29 +2145,29 @@ EditorBase::NotifyEditorObservers(Notifi
 
       if (!mDispatchInputEvent) {
         return;
       }
 
       FireInputEvent();
       break;
     case eNotifyEditorObserversOfBefore:
-      if (NS_WARN_IF(mIsInEditAction)) {
+      if (NS_WARN_IF(mIsInEditSubAction)) {
         break;
       }
 
-      mIsInEditAction = true;
+      mIsInEditSubAction = true;
 
       if (mIMEContentObserver) {
         RefPtr<IMEContentObserver> observer = mIMEContentObserver;
         observer->BeforeEditAction();
       }
       break;
     case eNotifyEditorObserversOfCancel:
-      mIsInEditAction = false;
+      mIsInEditSubAction = false;
 
       if (mIMEContentObserver) {
         RefPtr<IMEContentObserver> observer = mIMEContentObserver;
         observer->CancelEditAction();
       }
       break;
     default:
       MOZ_CRASH("Handle all notifications here");
@@ -2405,39 +2413,30 @@ EditorBase::GetRootElement(Element** aRo
 {
   NS_ENSURE_ARG_POINTER(aRootElement);
   NS_ENSURE_TRUE(mRootElement, NS_ERROR_NOT_AVAILABLE);
   RefPtr<Element> rootElement = mRootElement;
   rootElement.forget(aRootElement);
   return NS_OK;
 }
 
-/**
- * All editor operations which alter the doc should be prefaced
- * with a call to StartOperation, naming the action and direction.
- */
-nsresult
-EditorBase::StartOperation(EditAction opID,
-                           nsIEditor::EDirection aDirection)
+void
+EditorBase::OnStartToHandleTopLevelEditSubAction(
+              EditSubAction aEditSubAction,
+              nsIEditor::EDirection aDirection)
 {
-  mAction = opID;
+  mTopLevelEditSubAction = aEditSubAction;
   mDirection = aDirection;
-  return NS_OK;
 }
 
-/**
- * All editor operations which alter the doc should be followed
- * with a call to EndOperation.
- */
-nsresult
-EditorBase::EndOperation()
+void
+EditorBase::OnEndHandlingTopLevelEditSubAction()
 {
-  mAction = EditAction::none;
+  mTopLevelEditSubAction = EditSubAction::none;
   mDirection = eNone;
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 EditorBase::CloneAttribute(const nsAString& aAttribute,
                            Element* aDestElement,
                            Element* aSourceElement)
 {
   NS_ENSURE_TRUE(aDestElement && aSourceElement, NS_ERROR_NULL_POINTER);
@@ -2909,18 +2908,19 @@ EditorBase::NotifyDocumentListeners(
 }
 
 nsresult
 EditorBase::SetTextImpl(Selection& aSelection, const nsAString& aString,
                         Text& aCharData)
 {
   const uint32_t length = aCharData.Length();
 
-  AutoRules beginRulesSniffing(this, EditAction::setText,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::setText,
+                                      nsIEditor::eNext);
 
   // Let listeners know what's up
   if (!mActionListeners.IsEmpty() && length) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillDeleteText(&aCharData, 0, length);
     }
   }
@@ -2982,18 +2982,19 @@ EditorBase::DeleteTextWithTransaction(Ch
                                       uint32_t aLength)
 {
   RefPtr<DeleteTextTransaction> transaction =
     DeleteTextTransaction::MaybeCreate(*this, aCharData, aOffset, aLength);
   if (NS_WARN_IF(!transaction)) {
     return NS_ERROR_FAILURE;
   }
 
-  AutoRules beginRulesSniffing(this, EditAction::deleteText,
-                               nsIEditor::ePrevious);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteText,
+                                      nsIEditor::ePrevious);
 
   // Let listeners know what's up
   if (!mActionListeners.IsEmpty()) {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillDeleteText(&aCharData, aOffset, aLength);
     }
   }
@@ -4574,30 +4575,30 @@ EditorBase::HandleKeyPressEvent(WidgetKe
     case NS_VK_ALT:
       aKeyboardEvent->PreventDefault(); // consumed
       return NS_OK;
   }
   return NS_OK;
 }
 
 nsresult
-EditorBase::HandleInlineSpellCheck(EditAction action,
+EditorBase::HandleInlineSpellCheck(EditSubAction aEditSubAction,
                                    Selection& aSelection,
                                    nsINode* previousSelectedNode,
                                    uint32_t previousSelectedOffset,
                                    nsINode* aStartContainer,
                                    uint32_t aStartOffset,
                                    nsINode* aEndContainer,
                                    uint32_t aEndOffset)
 {
   if (!mInlineSpellChecker) {
     return NS_OK;
   }
   return mInlineSpellChecker->SpellCheckAfterEditorChange(
-                                action, aSelection,
+                                aEditSubAction, aSelection,
                                 previousSelectedNode, previousSelectedOffset,
                                 aStartContainer, aStartOffset, aEndContainer,
                                 aEndOffset);
 }
 
 already_AddRefed<nsIContent>
 EditorBase::FindSelectionRoot(nsINode* aNode)
 {
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -46,18 +46,18 @@ class nsIPresShell;
 class nsISupports;
 class nsITransaction;
 class nsITransactionListener;
 class nsIWidget;
 class nsRange;
 
 namespace mozilla {
 class AddStyleSheetTransaction;
-class AutoRules;
 class AutoSelectionRestorer;
+class AutoTopLevelEditSubActionNotifier;
 class AutoTransactionsConserveSelection;
 class AutoUpdateViewBatch;
 class ChangeAttributeTransaction;
 class CompositionTransaction;
 class CreateElementTransaction;
 class CSSEditUtils;
 class DeleteNodeTransaction;
 class DeleteTextTransaction;
@@ -77,17 +77,17 @@ class SplitNodeResult;
 class SplitNodeTransaction;
 class TextComposition;
 class TextEditor;
 class TextEditRules;
 class TextInputListener;
 class TextServicesDocument;
 class TypeInState;
 class WSRunObject;
-enum class EditAction : int32_t;
+enum class EditSubAction : int32_t;
 
 namespace dom {
 class DataTransfer;
 class DragEvent;
 class Element;
 class EventTarget;
 class Text;
 } // namespace dom
@@ -581,20 +581,20 @@ public:
   }
 
   bool IsModifiable() const
   {
     return !IsReadonly();
   }
 
   /**
-   * IsInEditAction() return true while the instance is handling an edit action.
-   * Otherwise, false.
+   * IsInEditSubAction() return true while the instance is handling an edit
+   * sub-action.  Otherwise, false.
    */
-  bool IsInEditAction() const { return mIsInEditAction; }
+  bool IsInEditSubAction() const { return mIsInEditSubAction; }
 
   /**
    * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching
    * "input" event.
    */
   void SuppressDispatchingInputEvent(bool aSuppress)
   {
     mDispatchInputEvent = !aSuppress;
@@ -1528,17 +1528,17 @@ protected: // May be called by friends.
   nsresult CreateRange(nsINode* aStartContainer, int32_t aStartOffset,
                        nsINode* aEndContainer, int32_t aEndOffset,
                        nsRange** aRange);
 
   static bool IsPreformatted(nsINode* aNode);
 
   bool GetShouldTxnSetSelection();
 
-  nsresult HandleInlineSpellCheck(EditAction action,
+  nsresult HandleInlineSpellCheck(EditSubAction aEditSubAction,
                                   Selection& aSelection,
                                   nsINode* previousSelectedNode,
                                   uint32_t previousSelectedOffset,
                                   nsINode* aStartContainer,
                                   uint32_t aStartOffset,
                                   nsINode* aEndContainer,
                                   uint32_t aEndOffset);
 
@@ -1580,28 +1580,35 @@ protected: // May be called by friends.
   /**
    * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
    * with nsCaret::RemoveForceHide().  This does NOT set visibility of
    * nsCaret.  Therefore, this is stateless.
    */
   void HideCaret(bool aHide);
 
 protected: // Called by helper classes.
-  /**
-   * All editor operations which alter the doc should be prefaced
-   * with a call to StartOperation, naming the action and direction.
-   */
-  virtual nsresult StartOperation(EditAction opID,
-                                  nsIEditor::EDirection aDirection);
 
   /**
-   * All editor operations which alter the doc should be followed
-   * with a call to EndOperation.
+   * OnStartToHandleTopLevelEditSubAction() is called when
+   * mTopLevelEditSubAction is EditSubAction::none and somebody starts to
+   * handle aEditSubAction.
+   *
+   * @param aEditSubAction      Top level edit sub action which will be
+   *                            handled soon.
+   * @param aDirection          Direction of aEditSubAction.
    */
-  virtual nsresult EndOperation();
+  virtual void
+  OnStartToHandleTopLevelEditSubAction(EditSubAction aEditSubAction,
+                                       nsIEditor::EDirection aDirection);
+
+  /**
+   * OnEndHandlingTopLevelEditSubAction() is called after
+   * mTopLevelEditSubAction is handled.
+   */
+  virtual void OnEndHandlingTopLevelEditSubAction();
 
   /**
    * Routines for managing the preservation of selection across
    * various editor actions.
    */
   bool ArePreservingSelection();
   void PreserveSelectionAcrossActions(Selection* aSel);
   nsresult RestorePreservedSelection(Selection* aSel);
@@ -1878,45 +1885,45 @@ protected:
   uint32_t mModCount;
   // Behavior flags. See nsIPlaintextEditor.idl for the flags we use.
   uint32_t mFlags;
 
   int32_t mUpdateCount;
 
   // Nesting count for batching.
   int32_t mPlaceholderBatch;
-  // The current editor action.
-  EditAction mAction;
+  // The top level edit sub-action.
+  EditSubAction mTopLevelEditSubAction;
 
-  // The current direction of editor action.
+  // The top level edit sub-action's direction.
   EDirection mDirection;
   // -1 = not initialized
   int8_t mDocDirtyState;
   // A Tristate value.
   uint8_t mSpellcheckCheckboxState;
 
   // Turn off for conservative selection adjustment by transactions.
   bool mShouldTxnSetSelection;
   // Whether PreDestroy has been called.
   bool mDidPreDestroy;
   // Whether PostCreate has been called.
   bool mDidPostCreate;
   bool mDispatchInputEvent;
-  // True while the instance is handling an edit action.
-  bool mIsInEditAction;
+  // True while the instance is handling an edit sub-action.
+  bool mIsInEditSubAction;
   // Whether caret is hidden forcibly.
   bool mHidingCaret;
   // Whether spellchecker dictionary is initialized after focused.
   bool mSpellCheckerDictionaryUpdated;
   // Whether we are an HTML editor class.
   bool mIsHTMLEditorClass;
 
   friend class AutoPlaceholderBatch;
-  friend class AutoRules;
   friend class AutoSelectionRestorer;
+  friend class AutoTopLevelEditSubActionNotifier;
   friend class AutoTransactionsConserveSelection;
   friend class AutoUpdateViewBatch;
   friend class CompositionTransaction;
   friend class CreateElementTransaction;
   friend class CSSEditUtils;
   friend class DeleteTextTransaction;
   friend class HTMLEditRules;
   friend class HTMLEditUtils;
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -524,45 +524,51 @@ public:
 
   /**
    * Abort() cancels to restore the selection.
    */
   void Abort();
 };
 
 /***************************************************************************
- * stack based helper class for StartOperation()/EndOperation() sandwich
+ * AutoTopLevelEditSubActionNotifier notifies editor of start to handle
+ * top level edit sub-action and end handling top level edit sub-action.
  */
-class MOZ_RAII AutoRules final
+class MOZ_RAII AutoTopLevelEditSubActionNotifier final
 {
 public:
-  AutoRules(EditorBase* aEditorBase, EditAction aAction,
-            nsIEditor::EDirection aDirection
-            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  AutoTopLevelEditSubActionNotifier(EditorBase& aEditorBase,
+                                    EditSubAction aEditSubAction,
+                                    nsIEditor::EDirection aDirection
+                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mEditorBase(aEditorBase)
     , mDoNothing(false)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    // mAction will already be set if this is nested call
-    if (mEditorBase && !mEditorBase->mAction) {
-      mEditorBase->StartOperation(aAction, aDirection);
+    // mTopLevelEditSubAction will already be set if this is nested call
+    // XXX Looks like that this is not aware of unexpected nested edit action
+    //     handling via selectionchange event listener or mutation event
+    //     listener.
+    if (!mEditorBase.mTopLevelEditSubAction) {
+      mEditorBase.OnStartToHandleTopLevelEditSubAction(aEditSubAction,
+                                                       aDirection);
     } else {
       mDoNothing = true; // nested calls will end up here
     }
   }
 
-  ~AutoRules()
+  ~AutoTopLevelEditSubActionNotifier()
   {
-    if (mEditorBase && !mDoNothing) {
-      mEditorBase->EndOperation();
+    if (!mDoNothing) {
+      mEditorBase.OnEndHandlingTopLevelEditSubAction();
     }
   }
 
 protected:
-  EditorBase* mEditorBase;
+  EditorBase& mEditorBase;
   bool mDoNothing;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /***************************************************************************
  * stack based helper class for turning off active selection adjustment
  * by low level transactions
  */
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -45,37 +45,41 @@
 namespace mozilla {
 
 using namespace dom;
 
 nsresult
 HTMLEditor::SetSelectionToAbsoluteOrStatic(bool aEnabled)
 {
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this,
-                               aEnabled ? EditAction::setAbsolutePosition :
-                                          EditAction::removeAbsolutePosition,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this,
+                                      aEnabled ?
+                                        EditSubAction::setAbsolutePosition :
+                                        EditSubAction::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);
 
-  RulesInfo ruleInfo(aEnabled ? EditAction::setAbsolutePosition :
-                                EditAction::removeAbsolutePosition);
+  EditSubActionInfo subActionInfo(
+                      aEnabled ? EditSubAction::setAbsolutePosition :
+                                 EditSubAction::removeAbsolutePosition);
   bool cancel, handled;
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (NS_FAILED(rv) || cancel) {
     return rv;
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 already_AddRefed<Element>
 HTMLEditor::GetAbsolutelyPositionedSelectionContainer()
 {
   nsAutoString positionStr;
   RefPtr<Element> element = GetSelectionContainer();
 
@@ -134,36 +138,39 @@ HTMLEditor::SetZIndex(Element& aElement,
 
   mCSSEditUtils->SetCSSProperty(aElement, *nsGkAtoms::z_index, zIndexStr);
 }
 
 nsresult
 HTMLEditor::AddZIndex(int32_t aChange)
 {
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this,
-                               (aChange < 0) ? EditAction::decreaseZIndex :
-                                               EditAction::increaseZIndex,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this,
+                                      aChange < 0 ?
+                                        EditSubAction::decreaseZIndex :
+                                        EditSubAction::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);
-  RulesInfo ruleInfo(aChange < 0 ? EditAction::decreaseZIndex :
-                                   EditAction::increaseZIndex);
+  EditSubActionInfo subActionInfo(aChange < 0 ? EditSubAction::decreaseZIndex :
+                                                EditSubAction::increaseZIndex);
   bool cancel, handled;
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 int32_t
 HTMLEditor::GetZIndex(Element& aElement)
 {
   nsAutoString zIndexStr;
 
   nsresult rv =
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -59,18 +59,16 @@
 #ifdef SetProp
 #undef SetProp
 #endif
 
 class nsISupports;
 
 namespace mozilla {
 
-class RulesInfo;
-
 using namespace dom;
 
 //const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
 //const static char* kMOZEditorBogusNodeValue="TRUE";
 
 enum
 {
   kLonely = 0,
@@ -79,29 +77,29 @@ enum
   kBothSibs = 3
 };
 
 /********************************************************
  *  first some helpful functors we will use
  ********************************************************/
 
 static bool
-IsStyleCachePreservingAction(EditAction action)
-{
-  return action == EditAction::deleteSelection ||
-         action == EditAction::insertBreak ||
-         action == EditAction::makeList ||
-         action == EditAction::indent ||
-         action == EditAction::outdent ||
-         action == EditAction::align ||
-         action == EditAction::makeBasicBlock ||
-         action == EditAction::removeList ||
-         action == EditAction::makeDefListItem ||
-         action == EditAction::insertElement ||
-         action == EditAction::insertQuotation;
+IsStyleCachePreservingSubAction(EditSubAction aEditSubAction)
+{
+  return aEditSubAction == EditSubAction::deleteSelection ||
+         aEditSubAction == EditSubAction::insertBreak ||
+         aEditSubAction == EditSubAction::makeList ||
+         aEditSubAction == EditSubAction::indent ||
+         aEditSubAction == EditSubAction::outdent ||
+         aEditSubAction == EditSubAction::align ||
+         aEditSubAction == EditSubAction::makeBasicBlock ||
+         aEditSubAction == EditSubAction::removeList ||
+         aEditSubAction == EditSubAction::makeDefListItem ||
+         aEditSubAction == EditSubAction::insertElement ||
+         aEditSubAction == EditSubAction::insertQuotation;
 }
 
 static nsAtom&
 ParagraphSeparatorElement(ParagraphSeparator separator)
 {
   switch (separator) {
     default:
       MOZ_FALLTHROUGH_ASSERT("Unexpected paragraph separator!");
@@ -299,31 +297,31 @@ HTMLEditRules::Init(TextEditor* aTextEdi
     nsresult rv = InsertBRElementToEmptyListItemsAndTableCellsInChangedRange();
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to insert <br> elements to empty list items and table cells");
   }
 
-  StartToListenToEditActions();
+  StartToListenToEditSubActions();
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::DetachEditor()
 {
-  EndListeningToEditActions();
+  EndListeningToEditSubActions();
   mHTMLEditor = nullptr;
   return TextEditRules::DetachEditor();
 }
 
 nsresult
-HTMLEditRules::BeforeEdit(EditAction aAction,
+HTMLEditRules::BeforeEdit(EditSubAction aEditSubAction,
                           nsIEditor::EDirection aDirection)
 {
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   if (mLockRulesSniffing) {
     return NS_OK;
@@ -367,20 +365,20 @@ HTMLEditRules::BeforeEdit(EditAction aAc
       mDocChangeRange->Reset();
     }
     if (mUtilRange) {
       // Ditto for mUtilRange.
       mUtilRange->Reset();
     }
 
     // Remember current inline styles for deletion and normal insertion ops
-    if (aAction == EditAction::insertText ||
-        aAction == EditAction::insertIMEText ||
-        aAction == EditAction::deleteSelection ||
-        IsStyleCachePreservingAction(aAction)) {
+    if (aEditSubAction == EditSubAction::insertText ||
+        aEditSubAction == EditSubAction::insertIMEText ||
+        aEditSubAction == EditSubAction::deleteSelection ||
+        IsStyleCachePreservingSubAction(aEditSubAction)) {
       nsCOMPtr<nsINode> selNode =
         aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
       nsresult rv = CacheInlineStyles(selNode);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
@@ -395,24 +393,24 @@ HTMLEditRules::BeforeEdit(EditAction aAc
     }
 
     // Check that selection is in subtree defined by body node
     nsresult rv = ConfirmSelectionInBody();
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     // Let rules remember the top level action
-    mTheAction = aAction;
+    mTopLevelEditSubAction = aEditSubAction;
   }
   return NS_OK;
 }
 
 
 nsresult
-HTMLEditRules::AfterEdit(EditAction aAction,
+HTMLEditRules::AfterEdit(EditSubAction aEditSubAction,
                          nsIEditor::EDirection aDirection)
 {
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   if (mLockRulesSniffing) {
     return NS_OK;
@@ -427,17 +425,17 @@ HTMLEditRules::AfterEdit(EditAction aAct
     Selection* selection = mHTMLEditor->GetSelection();
     if (NS_WARN_IF(!selection)) {
       return NS_ERROR_FAILURE;
     }
 
     AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
     // Do all the tricky stuff
-    rv = AfterEditInner(aAction, aDirection);
+    rv = AfterEditInner(aEditSubAction, aDirection);
     // Perhaps, we need to do the following jobs even if the editor has been
     // destroyed since they adjust some states of HTML document but don't
     // modify the DOM tree nor Selection.
 
     // Free up selectionState range item
     HTMLEditorRef().mRangeUpdater.DropRangeItem(mRangeItem);
 
     // Reset the contenteditable count to its previous value
@@ -455,27 +453,27 @@ HTMLEditRules::AfterEdit(EditAction aAct
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
-HTMLEditRules::AfterEditInner(EditAction aAction,
+HTMLEditRules::AfterEditInner(EditSubAction aEditSubAction,
                               nsIEditor::EDirection aDirection)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsresult rv = ConfirmSelectionInBody();
   if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to normalize Selection");
-  if (aAction == EditAction::ignore) {
+  if (aEditSubAction == EditSubAction::ignore) {
     return NS_OK;
   }
 
   nsCOMPtr<nsINode> rangeStartContainer, rangeEndContainer;
   uint32_t rangeStartOffset = 0, rangeEndOffset = 0;
   // do we have a real range to act on?
   bool bDamagedRange = false;
   if (mDocChangeRange) {
@@ -483,70 +481,71 @@ HTMLEditRules::AfterEditInner(EditAction
     rangeEndContainer = mDocChangeRange->GetEndContainer();
     rangeStartOffset = mDocChangeRange->StartOffset();
     rangeEndOffset = mDocChangeRange->EndOffset();
     if (rangeStartContainer && rangeEndContainer) {
       bDamagedRange = true;
     }
   }
 
-  if (bDamagedRange && !((aAction == EditAction::undo) ||
-                         (aAction == EditAction::redo))) {
+  if (bDamagedRange && !((aEditSubAction == EditSubAction::undo) ||
+                         (aEditSubAction == EditSubAction::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.
     AutoTransactionsConserveSelection dontChangeMySelection(&HTMLEditorRef());
 
     // expand the "changed doc range" as needed
-    PromoteRange(*mDocChangeRange, aAction);
+    PromoteRange(*mDocChangeRange, aEditSubAction);
 
     // 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 (aAction == EditAction::deleteSelection && mDidRangedDelete) {
+    // not if deletion was done along the way for EditSubAction::loadHTML,
+    // EditSubAction::insertText, etc.  That's why this is here rather than
+    // DidDeleteSelection().
+    if (aEditSubAction == EditSubAction::deleteSelection && mDidRangedDelete) {
       nsresult rv = InsertBRIfNeeded();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     // add in any needed <br>s, and remove any unneeded ones.
     nsresult rv = InsertBRElementToEmptyListItemsAndTableCellsInChangedRange();
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to insert <br> elements to empty list items and table cells");
 
     // merge any adjacent text nodes
-    if (aAction != EditAction::insertText &&
-        aAction != EditAction::insertIMEText) {
+    if (aEditSubAction != EditSubAction::insertText &&
+        aEditSubAction != EditSubAction::insertIMEText) {
       nsresult rv = HTMLEditorRef().CollapseAdjacentTextNodes(mDocChangeRange);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     // clean up any empty nodes in the selection
     rv = RemoveEmptyNodesInChangedRange();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // attempt to transform any unneeded nbsp's into spaces after doing various operations
-    if (aAction == EditAction::insertText ||
-        aAction == EditAction::insertIMEText ||
-        aAction == EditAction::deleteSelection ||
-        aAction == EditAction::insertBreak ||
-        aAction == EditAction::htmlPaste ||
-        aAction == EditAction::loadHTML) {
+    if (aEditSubAction == EditSubAction::insertText ||
+        aEditSubAction == EditSubAction::insertIMEText ||
+        aEditSubAction == EditSubAction::deleteSelection ||
+        aEditSubAction == EditSubAction::insertBreak ||
+        aEditSubAction == EditSubAction::htmlPaste ||
+        aEditSubAction == EditSubAction::loadHTML) {
       rv = AdjustWhitespace();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // also do this for original selection endpoints.
       NS_ENSURE_STATE(mRangeItem->mStartContainer);
       NS_ENSURE_STATE(mRangeItem->mEndContainer);
@@ -567,43 +566,43 @@ HTMLEditRules::AfterEditInner(EditAction
         return NS_ERROR_EDITOR_DESTROYED;
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
         "Failed to pin selection to the new block");
       mNewBlock = nullptr;
     }
 
     // adjust selection for insert text, html paste, and delete actions
-    if (aAction == EditAction::insertText ||
-        aAction == EditAction::insertIMEText ||
-        aAction == EditAction::deleteSelection ||
-        aAction == EditAction::insertBreak ||
-        aAction == EditAction::htmlPaste ||
-        aAction == EditAction::loadHTML) {
+    if (aEditSubAction == EditSubAction::insertText ||
+        aEditSubAction == EditSubAction::insertIMEText ||
+        aEditSubAction == EditSubAction::deleteSelection ||
+        aEditSubAction == EditSubAction::insertBreak ||
+        aEditSubAction == EditSubAction::htmlPaste ||
+        aEditSubAction == EditSubAction::loadHTML) {
       rv = AdjustSelection(aDirection);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     // check for any styles which were removed inappropriately
-    if (aAction == EditAction::insertText ||
-        aAction == EditAction::insertIMEText ||
-        aAction == EditAction::deleteSelection ||
-        IsStyleCachePreservingAction(aAction)) {
+    if (aEditSubAction == EditSubAction::insertText ||
+        aEditSubAction == EditSubAction::insertIMEText ||
+        aEditSubAction == EditSubAction::deleteSelection ||
+        IsStyleCachePreservingSubAction(aEditSubAction)) {
       HTMLEditorRef().mTypeInState->UpdateSelState(&SelectionRef());
       rv = ReapplyCachedStyles();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       ClearCachedStyles();
     }
   }
 
-  rv = HTMLEditorRef().HandleInlineSpellCheck(aAction, SelectionRef(),
+  rv = HTMLEditorRef().HandleInlineSpellCheck(aEditSubAction, SelectionRef(),
                                               mRangeItem->mStartContainer,
                                               mRangeItem->mStartOffset,
                                               rangeStartContainer,
                                               rangeStartOffset,
                                               rangeEndContainer,
                                               rangeEndOffset);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -620,38 +619,38 @@ HTMLEditRules::AfterEditInner(EditAction
     CheckInterlinePosition();
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::WillDoAction(Selection* aSelection,
-                            RulesInfo* aInfo,
+                            EditSubActionInfo& aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
-  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+  if (NS_WARN_IF(!aSelection)) {
     return NS_ERROR_INVALID_ARG;
   }
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   MOZ_ASSERT(aCancel);
   MOZ_ASSERT(aHandled);
 
   *aCancel = false;
   *aHandled = false;
 
   // Deal with actions for which we don't need to check whether the selection is
   // editable.
-  if (aInfo->action == EditAction::outputText ||
-      aInfo->action == EditAction::undo ||
-      aInfo->action == EditAction::redo) {
+  if (aInfo.mEditSubAction == EditSubAction::outputText ||
+      aInfo.mEditSubAction == EditSubAction::undo ||
+      aInfo.mEditSubAction == EditSubAction::redo) {
     return TextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
 
   // Nothing to do if there's no selection to act on
   if (NS_WARN_IF(!SelectionRef().RangeCount())) {
     return NS_OK;
@@ -675,105 +674,105 @@ HTMLEditRules::WillDoAction(Selection* a
 
     NS_ENSURE_STATE(mHTMLEditor);
     if (!HTMLEditorRef().IsModifiableNode(range->GetCommonAncestor())) {
       *aCancel = true;
       return NS_OK;
     }
   }
 
-  switch (aInfo->action) {
-    case EditAction::insertText:
-    case EditAction::insertIMEText:
+  switch (aInfo.mEditSubAction) {
+    case EditSubAction::insertText:
+    case EditSubAction::insertIMEText:
       UndefineCaretBidiLevel();
-      return WillInsertText(aInfo->action, aCancel, aHandled,
-                            aInfo->inString, aInfo->outString,
-                            aInfo->maxLength);
-    case EditAction::loadHTML:
+      return WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
+                            aInfo.inString, aInfo.outString,
+                            aInfo.maxLength);
+    case EditSubAction::loadHTML:
       return WillLoadHTML();
-    case EditAction::insertBreak:
+    case EditSubAction::insertBreak:
       UndefineCaretBidiLevel();
       return WillInsertBreak(aCancel, aHandled);
-    case EditAction::deleteSelection:
-      return WillDeleteSelection(aInfo->collapsedAction, aInfo->stripWrappers,
+    case EditSubAction::deleteSelection:
+      return WillDeleteSelection(aInfo.collapsedAction, aInfo.stripWrappers,
                                  aCancel, aHandled);
-    case EditAction::makeList:
-      return WillMakeList(aInfo->blockType, aInfo->entireList,
-                          aInfo->bulletType, aCancel, aHandled);
-    case EditAction::indent:
+    case EditSubAction::makeList:
+      return WillMakeList(aInfo.blockType, aInfo.entireList,
+                          aInfo.bulletType, aCancel, aHandled);
+    case EditSubAction::indent:
       return WillIndent(aCancel, aHandled);
-    case EditAction::outdent:
+    case EditSubAction::outdent:
       return WillOutdent(aCancel, aHandled);
-    case EditAction::setAbsolutePosition:
+    case EditSubAction::setAbsolutePosition:
       return WillAbsolutePosition(aCancel, aHandled);
-    case EditAction::removeAbsolutePosition:
+    case EditSubAction::removeAbsolutePosition:
       return WillRemoveAbsolutePosition(aCancel, aHandled);
-    case EditAction::align:
-      return WillAlign(*aInfo->alignType, aCancel, aHandled);
-    case EditAction::makeBasicBlock:
-      return WillMakeBasicBlock(*aInfo->blockType, aCancel, aHandled);
-    case EditAction::removeList: {
+    case EditSubAction::align:
+      return WillAlign(*aInfo.alignType, aCancel, aHandled);
+    case EditSubAction::makeBasicBlock:
+      return WillMakeBasicBlock(*aInfo.blockType, aCancel, aHandled);
+    case EditSubAction::removeList: {
       nsresult rv = WillRemoveList(aCancel, aHandled);
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
           NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       return NS_OK;
     }
-    case EditAction::makeDefListItem:
-      return WillMakeDefListItem(aInfo->blockType,
-                                 aInfo->entireList, aCancel, aHandled);
-    case EditAction::insertElement: {
+    case EditSubAction::makeDefListItem:
+      return WillMakeDefListItem(aInfo.blockType,
+                                 aInfo.entireList, aCancel, aHandled);
+    case EditSubAction::insertElement: {
       nsresult rv = WillInsert(aCancel);
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
       return NS_OK;
     }
-    case EditAction::decreaseZIndex:
+    case EditSubAction::decreaseZIndex:
       return WillRelativeChangeZIndex(-1, aCancel, aHandled);
-    case EditAction::increaseZIndex:
+    case EditSubAction::increaseZIndex:
       return WillRelativeChangeZIndex(1, aCancel, aHandled);
     default:
       return TextEditRules::WillDoAction(&SelectionRef(), aInfo,
                                          aCancel, aHandled);
   }
 }
 
 nsresult
 HTMLEditRules::DidDoAction(Selection* aSelection,
-                           RulesInfo* aInfo,
+                           EditSubActionInfo& aInfo,
                            nsresult aResult)
 {
-  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+  if (NS_WARN_IF(!aSelection)) {
     return NS_ERROR_INVALID_ARG;
   }
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
 
-  switch (aInfo->action) {
-    case EditAction::insertText:
-    case EditAction::insertBreak:
-    case EditAction::insertIMEText:
+  switch (aInfo.mEditSubAction) {
+    case EditSubAction::insertText:
+    case EditSubAction::insertBreak:
+    case EditSubAction::insertIMEText:
       return NS_OK;
-    case EditAction::deleteSelection:
+    case EditSubAction::deleteSelection:
       return DidDeleteSelection();
-    case EditAction::makeBasicBlock:
-    case EditAction::indent:
-    case EditAction::outdent:
-    case EditAction::align:
+    case EditSubAction::makeBasicBlock:
+    case EditSubAction::indent:
+    case EditSubAction::outdent:
+    case EditSubAction::align:
       return DidMakeBasicBlock();
-    case EditAction::setAbsolutePosition: {
+    case EditSubAction::setAbsolutePosition: {
       nsresult rv = DidMakeBasicBlock();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       return DidAbsolutePosition();
     }
     default:
       return TextEditRules::DidDoAction(aSelection, aInfo, aResult);
@@ -974,22 +973,22 @@ HTMLEditRules::GetAlignment(bool* aMixed
              atStartOfSelection.Offset() == static_cast<uint32_t>(rootOffset)) {
     // If we have selected the body, let's look at the first editable node
     nodeToExamine = HTMLEditorRef().GetNextEditableNode(atStartOfSelection);
     if (NS_WARN_IF(!nodeToExamine)) {
       return NS_ERROR_FAILURE;
     }
   } else {
     nsTArray<RefPtr<nsRange>> arrayOfRanges;
-    GetPromotedRanges(arrayOfRanges, EditAction::align);
+    GetPromotedRanges(arrayOfRanges, EditSubAction::align);
 
     // Use these ranges to construct a list of nodes to act on.
     nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
     nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
-                                       EditAction::align, TouchContent::no);
+                                       EditSubAction::align, TouchContent::no);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     nodeToExamine = arrayOfNodes.SafeElementAt(0);
     if (NS_WARN_IF(!nodeToExamine)) {
       return NS_ERROR_FAILURE;
     }
   }
@@ -1122,17 +1121,18 @@ HTMLEditRules::GetIndentState(bool* aCan
     return NS_ERROR_FAILURE;
   }
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   // contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv =
-    GetNodesFromSelection(EditAction::indent, arrayOfNodes, TouchContent::no);
+    GetNodesFromSelection(EditSubAction::indent, arrayOfNodes,
+                          TouchContent::no);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // examine nodes in selection for blockquotes or list elements;
   // these we can outdent.  Note that we return true for canOutdent
   // if *any* of the selection is outdentable, rather than all of it.
   bool useCSS = HTMLEditorRef().IsCSSEnabled();
@@ -1409,34 +1409,34 @@ HTMLEditRules::WillInsert(bool* aCancel)
       }
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
       }
     }
   }
 
   if (mDidDeleteSelection &&
-      (mTheAction == EditAction::insertText ||
-       mTheAction == EditAction::insertIMEText ||
-       mTheAction == EditAction::deleteSelection)) {
+      (mTopLevelEditSubAction == EditSubAction::insertText ||
+       mTopLevelEditSubAction == EditSubAction::insertIMEText ||
+       mTopLevelEditSubAction == EditSubAction::deleteSelection)) {
     nsresult rv = ReapplyCachedStyles();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   // For most actions we want to clear the cached styles, but there are
   // exceptions
-  if (!IsStyleCachePreservingAction(mTheAction)) {
+  if (!IsStyleCachePreservingSubAction(mTopLevelEditSubAction)) {
     ClearCachedStyles();
   }
   return NS_OK;
 }
 
 nsresult
-HTMLEditRules::WillInsertText(EditAction aAction,
+HTMLEditRules::WillInsertText(EditSubAction aEditSubAction,
                               bool* aCancel,
                               bool* aHandled,
                               const nsAString* inString,
                               nsAString* outString,
                               int32_t aMaxLength)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
@@ -1494,17 +1494,17 @@ HTMLEditRules::WillInsertText(EditAction
 
   // dont put text in places that can't have it
   if (!EditorBase::IsTextNode(pointToInsert.GetContainer()) &&
       !HTMLEditorRef().CanContainTag(*pointToInsert.GetContainer(),
                                      *nsGkAtoms::textTagName)) {
     return NS_ERROR_FAILURE;
   }
 
-  if (aAction == EditAction::insertIMEText) {
+  if (aEditSubAction == EditSubAction::insertIMEText) {
     // Right now the WSRunObject code bails on empty strings, but IME needs
     // the InsertTextWithTransaction() call to still happen since empty strings
     // are meaningful there.
     // If there is one or more IME selections, its minimum offset should be
     // the insertion point.
     int32_t IMESelectionOffset =
       HTMLEditorRef().GetIMESelectionStartOffsetIn(
                         pointToInsert.GetContainer());
@@ -1530,17 +1530,17 @@ HTMLEditRules::WillInsertText(EditAction
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  // aAction == kInsertText
+  // aEditSubAction == kInsertText
 
   // find where we are
   EditorDOMPoint currentPoint(pointToInsert);
 
   // is our text going to be PREformatted?
   // We remember this so that we know how to handle tabs.
   bool isPRE = EditorBase::IsPreformatted(pointToInsert.GetContainer());
 
@@ -3796,17 +3796,17 @@ HTMLEditRules::MoveBlock(Element& aLeftB
                          int32_t aLeftOffset,
                          int32_t aRightOffset)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   // GetNodesFromPoint is the workhorse that figures out what we wnat to move.
   nsresult rv = GetNodesFromPoint(EditorDOMPoint(&aRightBlock, aRightOffset),
-                                  EditAction::makeList, arrayOfNodes,
+                                  EditSubAction::makeList, arrayOfNodes,
                                   TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditActionIgnored(rv);
   }
 
   EditActionResult ret(NS_OK);
   for (uint32_t i = 0; i < arrayOfNodes.Length(); i++) {
     // get the node to act on
@@ -4500,17 +4500,17 @@ HTMLEditRules::WillRemoveList(bool* aCan
   nsresult rv = NormalizeSelection();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
-  GetPromotedRanges(arrayOfRanges, EditAction::makeList);
+  GetPromotedRanges(arrayOfRanges, EditSubAction::makeList);
 
   // use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   rv = GetListActionNodes(arrayOfNodes, EntireList::no, TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -4606,17 +4606,17 @@ HTMLEditRules::MakeBasicBlock(nsAtom& bl
     return rv;
   }
 
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
   AutoTransactionsConserveSelection dontChangeMySelection(&HTMLEditorRef());
 
   // Contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-  rv = GetNodesFromSelection(EditAction::makeBasicBlock, arrayOfNodes,
+  rv = GetNodesFromSelection(EditSubAction::makeBasicBlock, arrayOfNodes,
                              TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
     nsRange* firstRange = SelectionRef().GetRangeAt(0);
@@ -4882,17 +4882,17 @@ HTMLEditRules::IndentAroundSelectionWith
   if (liNode) {
     arrayOfNodes.AppendElement(*liNode);
   } else {
     // convert the selection ranges into "promoted" selection ranges:
     // this basically just expands the range to include the immediate
     // block parent, and then further expands to include any ancestors
     // whose children are all in the range
     nsresult rv =
-      GetNodesFromSelection(EditAction::indent, arrayOfNodes,
+      GetNodesFromSelection(EditSubAction::indent, arrayOfNodes,
                             TouchContent::yes);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // if nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
@@ -5165,22 +5165,22 @@ HTMLEditRules::IndentAroundSelectionWith
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
 
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
-  GetPromotedRanges(arrayOfRanges, EditAction::indent);
+  GetPromotedRanges(arrayOfRanges, EditSubAction::indent);
 
   // use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv =
-    GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditAction::indent,
+    GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditSubAction::indent,
                          TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // if nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
     nsRange* firstRange = SelectionRef().GetRangeAt(0);
@@ -5547,17 +5547,18 @@ HTMLEditRules::OutdentAroundSelection()
   bool useCSS = HTMLEditorRef().IsCSSEnabled();
 
   // Convert the selection ranges into "promoted" selection ranges: this
   // basically just expands the range to include the immediate block parent,
   // and then further expands to include any ancestors whose children are all
   // in the range
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv =
-    GetNodesFromSelection(EditAction::outdent, arrayOfNodes, TouchContent::yes);
+    GetNodesFromSelection(EditSubAction::outdent, arrayOfNodes,
+                          TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return SplitRangeOffFromNodeResult(rv);
   }
 
   // Okay, now go through all the nodes and remove a level of blockquoting,
   // or whatever is appropriate.  Wohoo!
 
   nsCOMPtr<nsIContent> leftContentOfLastOutdented;
@@ -6185,17 +6186,17 @@ HTMLEditRules::AlignContentsAtSelection(
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
 
   // Convert the selection ranges into "promoted" selection ranges: This
   // basically just expands the range to include the immediate block parent,
   // and then further expands to include any ancestors whose children are all
   // in the range
   nsTArray<OwningNonNull<nsINode>> nodeArray;
   nsresult rv =
-    GetNodesFromSelection(EditAction::align, nodeArray, TouchContent::yes);
+    GetNodesFromSelection(EditSubAction::align, nodeArray, TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If we don't have any nodes, or we have only a single br, then we are
   // creating an empty alignment div.  We have to do some different things for
   // these.
   bool emptyDiv = nodeArray.IsEmpty();
@@ -7118,26 +7119,26 @@ HTMLEditRules::NormalizeSelection()
     "Failed to extend selection");
   return NS_OK;
 }
 
 EditorDOMPoint
 HTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere,
                                 nsINode& aNode,
                                 int32_t aOffset,
-                                EditAction actionID)
+                                EditSubAction aEditSubAction)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // we do one thing for text actions, something else entirely for other
   // actions
-  if (actionID == EditAction::insertText ||
-      actionID == EditAction::insertIMEText ||
-      actionID == EditAction::insertBreak ||
-      actionID == EditAction::deleteText) {
+  if (aEditSubAction == EditSubAction::insertText ||
+      aEditSubAction == EditSubAction::insertIMEText ||
+      aEditSubAction == EditSubAction::insertBreak ||
+      aEditSubAction == EditSubAction::deleteText) {
     bool isSpace, isNBSP;
     nsCOMPtr<nsIContent> content =
       aNode.IsContent() ? aNode.AsContent() : nullptr;
     nsCOMPtr<nsIContent> temp;
     int32_t newOffset = aOffset;
     // for text actions, we want to look backwards (or forwards, as
     // appropriate) for additional whitespace or nbsp's.  We may have to act on
     // these later even though they are outside of the initial selection.  Even
@@ -7199,29 +7200,29 @@ HTMLEditRules::GetPromotedPoint(RulesEnd
       HTMLEditorRef().GetPreviousEditableHTMLNodeInBlock(point);
     while (!nearNode &&
            !point.IsContainerHTMLElement(nsGkAtoms::body) &&
            point.GetContainer()->GetParentNode()) {
       // some cutoffs are here: we don't need to also include them in the
       // aWhere == kEnd case.  as long as they are in one or the other it will
       // work.  special case for outdent: don't keep looking up if we have
       // found a blockquote element to act on
-      if (actionID == EditAction::outdent &&
+      if (aEditSubAction == EditSubAction::outdent &&
           point.IsContainerHTMLElement(nsGkAtoms::blockquote)) {
         break;
       }
 
       // Don't walk past the editable section. Note that we need to check
       // before walking up to a parent because we need to return the parent
       // object, so the parent itself might not be in the editable area, but
       // it's OK if we're not performing a block-level action.
-      bool blockLevelAction = actionID == EditAction::indent ||
-                              actionID == EditAction::outdent ||
-                              actionID == EditAction::align ||
-                              actionID == EditAction::makeBasicBlock;
+      bool blockLevelAction = aEditSubAction == EditSubAction::indent ||
+                              aEditSubAction == EditSubAction::outdent ||
+                              aEditSubAction == EditSubAction::align ||
+                              aEditSubAction == EditSubAction::makeBasicBlock;
       if (!HTMLEditorRef().IsDescendantOfEditorRoot(
                              point.GetContainer()->GetParentNode()) &&
           (blockLevelAction ||
            !HTMLEditorRef().IsDescendantOfEditorRoot(point.GetContainer()))) {
         break;
       }
 
       point.Set(point.GetContainer());
@@ -7245,19 +7246,19 @@ HTMLEditRules::GetPromotedPoint(RulesEnd
   }
 
   // look ahead through any further inline nodes that aren't across a <br> from
   // us, and that are enclosed in the same block.
   // XXX Currently, we stop block-extending when finding visible <br> element.
   //     This might be different from "block-extend" of execCommand spec.
   //     However, the spec is really unclear.
   // XXX Probably, scanning only editable nodes is wrong for
-  //     EditAction::makeBasicBlock because it might be better to wrap existing
-  //     inline elements even if it's non-editable.  For example, following
-  //     examples with insertParagraph causes different result:
+  //     EditSubAction::makeBasicBlock because it might be better to wrap
+  //     existing inline elements even if it's non-editable.  For example,
+  //     following examples with insertParagraph causes different result:
   //     * <div contenteditable>foo[]<b contenteditable="false">bar</b></div>
   //     * <div contenteditable>foo[]<b>bar</b></div>
   //     * <div contenteditable>foo[]<b contenteditable="false">bar</b>baz</div>
   //     Only in the first case, after the caret position isn't wrapped with
   //     new <div> element.
   nsCOMPtr<nsIContent> nextNode =
     HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
 
@@ -7310,41 +7311,41 @@ HTMLEditRules::GetPromotedPoint(RulesEnd
     }
     nearNode = HTMLEditorRef().GetNextEditableHTMLNodeInBlock(point);
   }
   return point;
 }
 
 void
 HTMLEditRules::GetPromotedRanges(nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
-                                 EditAction inOperationType)
+                                 EditSubAction aEditSubAction)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   uint32_t rangeCount = SelectionRef().RangeCount();
   for (uint32_t i = 0; i < rangeCount; i++) {
     RefPtr<nsRange> selectionRange = SelectionRef().GetRangeAt(i);
     MOZ_ASSERT(selectionRange);
 
     // Clone range so we don't muck with actual selection ranges
     RefPtr<nsRange> opRange = selectionRange->CloneRange();
 
     // Make a new adjusted range to represent the appropriate block content.
     // The basic idea is to push out the range endpoints to truly enclose the
     // blocks that we will affect.  This call alters opRange.
-    PromoteRange(*opRange, inOperationType);
+    PromoteRange(*opRange, aEditSubAction);
 
     // Stuff new opRange into array
     outArrayOfRanges.AppendElement(opRange);
   }
 }
 
 void
 HTMLEditRules::PromoteRange(nsRange& aRange,
-                            EditAction aOperationType)
+                            EditSubAction aEditSubAction)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
   MOZ_ASSERT(!aRange.IsInSelection());
 
   if (!aRange.IsPositioned()) {
     return;
   }
 
@@ -7373,20 +7374,20 @@ HTMLEditRules::PromoteRange(nsRange& aRa
         startNode = block;
         endNode = block;
         startOffset = 0;
         endOffset = block->Length();
       }
     }
   }
 
-  if (aOperationType == EditAction::insertText ||
-      aOperationType == EditAction::insertIMEText ||
-      aOperationType == EditAction::insertBreak ||
-      aOperationType == EditAction::deleteText) {
+  if (aEditSubAction == EditSubAction::insertText ||
+      aEditSubAction == EditSubAction::insertIMEText ||
+      aEditSubAction == EditSubAction::insertBreak ||
+      aEditSubAction == EditSubAction::deleteText) {
      if (!startNode->IsContent() ||
          !endNode->IsContent()) {
        // GetPromotedPoint cannot promote node when action type is text
        // operation and selected node isn't content node.
        return;
      }
   }
 
@@ -7394,23 +7395,23 @@ HTMLEditRules::PromoteRange(nsRange& aRa
   // This is tricky.  The basic idea is to push out the range endpoints to
   // truly enclose the blocks that we will affect.
 
   // Make sure that the new range ends up to be in the editable section.
   // XXX Looks like that this check wastes the time.  Perhaps, we should
   //     implement a method which checks both two DOM points in the editor
   //     root.
   EditorDOMPoint startPoint =
-    GetPromotedPoint(kStart, *startNode, startOffset, aOperationType);
+    GetPromotedPoint(kStart, *startNode, startOffset, aEditSubAction);
   if (!HTMLEditorRef().IsDescendantOfEditorRoot(
                          EditorBase::GetNodeAtRangeOffsetPoint(startPoint))) {
     return;
   }
   EditorDOMPoint endPoint =
-    GetPromotedPoint(kEnd, *endNode, endOffset, aOperationType);
+    GetPromotedPoint(kEnd, *endNode, endOffset, aEditSubAction);
   EditorRawDOMPoint lastRawPoint(endPoint);
   lastRawPoint.RewindOffset();
   if (!HTMLEditorRef().IsDescendantOfEditorRoot(
                          EditorBase::GetNodeAtRangeOffsetPoint(lastRawPoint))) {
     return;
   }
 
   DebugOnly<nsresult> rv = aRange.SetStartAndEnd(startPoint, endPoint);
@@ -7434,17 +7435,17 @@ public:
 private:
   nsTArray<OwningNonNull<nsINode>>& mArray;
 };
 
 nsresult
 HTMLEditRules::GetNodesForOperation(
                  nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
                  nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
-                 EditAction aOperationType,
+                 EditSubAction aEditSubAction,
                  TouchContent aTouchContent)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (aTouchContent == TouchContent::yes) {
     // Split text nodes. This is necessary, since GetPromotedPoint() may return a
     // range ending in a text node in case where part of a pre-formatted
     // elements needs to be moved.
@@ -7525,17 +7526,17 @@ HTMLEditRules::GetNodesForOperation(
       nsTArray<OwningNonNull<nsINode>> nodes;
       iter.AppendList(UniqueFunctor(aOutArrayOfNodes), nodes);
       aOutArrayOfNodes.AppendElements(nodes);
     }
   }
 
   // Certain operations should not act on li's and td's, but rather inside
   // them.  Alter the list as needed.
-  if (aOperationType == EditAction::makeBasicBlock) {
+  if (aEditSubAction == EditSubAction::makeBasicBlock) {
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
       if (HTMLEditUtils::IsListItem(node)) {
         int32_t j = i;
         aOutArrayOfNodes.RemoveElementAt(i);
         GetInnerContent(*node, aOutArrayOfNodes, &j);
       }
     }
@@ -7546,50 +7547,50 @@ HTMLEditRules::GetNodesForOperation(
         if (!HTMLEditorRef().IsVisibleTextNode(*text)) {
           aOutArrayOfNodes.RemoveElementAt(i);
         }
       }
     }
   }
   // Indent/outdent already do something special for list items, but we still
   // need to make sure we don't act on table elements
-  else if (aOperationType == EditAction::outdent ||
-           aOperationType == EditAction::indent ||
-           aOperationType == EditAction::setAbsolutePosition) {
+  else if (aEditSubAction == EditSubAction::outdent ||
+           aEditSubAction == EditSubAction::indent ||
+           aEditSubAction == EditSubAction::setAbsolutePosition) {
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
       if (HTMLEditUtils::IsTableElementButNotTable(node)) {
         int32_t j = i;
         aOutArrayOfNodes.RemoveElementAt(i);
         GetInnerContent(*node, aOutArrayOfNodes, &j);
       }
     }
   }
   // Outdent should look inside of divs.
-  if (aOperationType == EditAction::outdent &&
+  if (aEditSubAction == EditSubAction::outdent &&
       !HTMLEditorRef().IsCSSEnabled()) {
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
       if (node->IsHTMLElement(nsGkAtoms::div)) {
         int32_t j = i;
         aOutArrayOfNodes.RemoveElementAt(i);
         GetInnerContent(*node, aOutArrayOfNodes, &j, Lists::no, Tables::no);
       }
     }
   }
 
 
   // Post-process the list to break up inline containers that contain br's, but
   // only for operations that might care, like making lists or paragraphs
-  if (aOperationType == EditAction::makeBasicBlock ||
-      aOperationType == EditAction::makeList ||
-      aOperationType == EditAction::align ||
-      aOperationType == EditAction::setAbsolutePosition ||
-      aOperationType == EditAction::indent ||
-      aOperationType == EditAction::outdent) {
+  if (aEditSubAction == EditSubAction::makeBasicBlock ||
+      aEditSubAction == EditSubAction::makeList ||
+      aEditSubAction == EditSubAction::align ||
+      aEditSubAction == EditSubAction::setAbsolutePosition ||
+      aEditSubAction == EditSubAction::indent ||
+      aEditSubAction == EditSubAction::outdent) {
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
       // XXX Why do we run this loop even when aTouchContent is "no"?
       if (aTouchContent == TouchContent::yes && IsInlineNode(node) &&
           HTMLEditorRef().IsContainer(node) && !EditorBase::IsTextNode(node)) {
         nsTArray<OwningNonNull<nsINode>> arrayOfInlines;
         nsresult rv = BustUpInlinesAtBRs(*node->AsContent(), arrayOfInlines);
         if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -7645,17 +7646,17 @@ HTMLEditRules::GetListActionNodes(
     }
   }
 
   {
     // We don't like other people messing with our selection!
     AutoTransactionsConserveSelection dontChangeMySelection(&HTMLEditorRef());
 
     // contruct a list of nodes to act on.
-    nsresult rv = GetNodesFromSelection(EditAction::makeList,
+    nsresult rv = GetNodesFromSelection(EditSubAction::makeList,
                                         aOutArrayOfNodes, aTouchContent);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // Pre-process our list of nodes
   for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
@@ -7756,17 +7757,17 @@ HTMLEditRules::GetDefinitionListItemType
 nsresult
 HTMLEditRules::GetParagraphFormatNodes(
                  nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // Contruct a list of nodes to act on.
   nsresult rv =
-   GetNodesFromSelection(EditAction::makeBasicBlock,
+   GetNodesFromSelection(EditSubAction::makeBasicBlock,
                          outArrayOfNodes, TouchContent::no);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Pre-process our list of nodes
   for (int32_t i = outArrayOfNodes.Length() - 1; i >= 0; i--) {
     OwningNonNull<nsINode> testNode = outArrayOfNodes[i];
@@ -7950,65 +7951,65 @@ HTMLEditRules::GetHighestInlineParent(ns
     content = parent;
   }
   return content;
 }
 
 nsresult
 HTMLEditRules::GetNodesFromPoint(
                  const EditorDOMPoint& aPoint,
-                 EditAction aOperation,
+                 EditSubAction aEditSubAction,
                  nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
                  TouchContent aTouchContent)
 {
   if (NS_WARN_IF(!aPoint.IsSet())) {
     return NS_ERROR_INVALID_ARG;
   }
   RefPtr<nsRange> range = new nsRange(aPoint.GetContainer());
   IgnoredErrorResult ignoredError;
   range->SetStart(aPoint, ignoredError);
   // error will assert on failure, because we are not cleaning it up,
   // but we're asserting in that case anyway.
   MOZ_ASSERT(!ignoredError.Failed());
 
   // Expand the range to include adjacent inlines
-  PromoteRange(*range, aOperation);
+  PromoteRange(*range, aEditSubAction);
 
   // Make array of ranges
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
 
   // Stuff new opRange into array
   arrayOfRanges.AppendElement(range);
 
   // Use these ranges to contruct a list of nodes to act on
   nsresult rv =
-    GetNodesForOperation(arrayOfRanges, outArrayOfNodes, aOperation,
+    GetNodesForOperation(arrayOfRanges, outArrayOfNodes, aEditSubAction,
                          aTouchContent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::GetNodesFromSelection(
-                 EditAction aOperation,
+                 EditSubAction aEditSubAction,
                  nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
                  TouchContent aTouchContent)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // Promote selection ranges
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
-  GetPromotedRanges(arrayOfRanges, aOperation);
+  GetPromotedRanges(arrayOfRanges, aEditSubAction);
 
   // Use these ranges to contruct a list of nodes to act on.
   nsresult rv = GetNodesForOperation(arrayOfRanges, outArrayOfNodes,
-                                     aOperation, aTouchContent);
+                                     aEditSubAction, aTouchContent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
@@ -9459,17 +9460,17 @@ HTMLEditRules::ReapplyCachedStyles()
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       // This style has disappeared through deletion.  Let's add the styles to
       // mTypeInState when same style isn't applied to the node already.
-      if ((!bAny || IsStyleCachePreservingAction(mTheAction)) &&
+      if ((!bAny || IsStyleCachePreservingSubAction(mTopLevelEditSubAction)) &&
            (!styleAtInsertionPoint[i].mPresent ||
             styleAtInsertionPoint[i].value != mCachedStyles[i].value)) {
         HTMLEditorRef().mTypeInState->SetProp(mCachedStyles[i].tag,
                                               mCachedStyles[i].attr,
                                               mCachedStyles[i].value);
       }
     }
   }
@@ -11061,22 +11062,22 @@ HTMLEditRules::PrepareToMakeElementAbsol
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
 
   // Convert the selection ranges into "promoted" selection ranges: this
   // basically just expands the range to include the immediate block parent,
   // and then further expands to include any ancestors whose children are all
   // in the range.
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
-  GetPromotedRanges(arrayOfRanges, EditAction::setAbsolutePosition);
+  GetPromotedRanges(arrayOfRanges, EditSubAction::setAbsolutePosition);
 
   // Use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
-                                     EditAction::setAbsolutePosition,
+                                     EditSubAction::setAbsolutePosition,
                                      TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
     nsRange* firstRange = SelectionRef().GetRangeAt(0);
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -21,20 +21,19 @@ class nsAtom;
 class nsIEditor;
 class nsINode;
 class nsRange;
 
 namespace mozilla {
 
 class EditActionResult;
 class HTMLEditor;
-class RulesInfo;
 class SplitNodeResult;
 class TextEditor;
-enum class EditAction : int32_t;
+enum class EditSubAction : int32_t;
 
 namespace dom {
 class Element;
 class Selection;
 } // namespace dom
 
 struct StyleCache final : public PropItem
 {
@@ -92,26 +91,26 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditRules, TextEditRules)
 
   HTMLEditRules();
 
   // TextEditRules methods
   virtual nsresult Init(TextEditor* aTextEditor) override;
   virtual nsresult DetachEditor() override;
-  virtual nsresult BeforeEdit(EditAction aAction,
+  virtual nsresult BeforeEdit(EditSubAction aEditSubAction,
                               nsIEditor::EDirection aDirection) override;
-  virtual nsresult AfterEdit(EditAction aAction,
+  virtual nsresult AfterEdit(EditSubAction aEditSubAction,
                              nsIEditor::EDirection aDirection) override;
   virtual nsresult WillDoAction(Selection* aSelection,
-                                RulesInfo* aInfo,
+                                EditSubActionInfo& aInfo,
                                 bool* aCancel,
                                 bool* aHandled) override;
   virtual nsresult DidDoAction(Selection* aSelection,
-                               RulesInfo* aInfo,
+                               EditSubActionInfo& 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);
@@ -139,18 +138,18 @@ public:
                     nsINode& aLeftNode, nsINode& aRightNode);
   void DidInsertText(Selection& aSelection,
                      nsINode& aTextNode, int32_t aOffset,
                      const nsAString& aString);
   void DidDeleteText(Selection& aSelection,
                      nsINode& aTextNode, int32_t aOffset, int32_t aLength);
   void WillDeleteSelection(Selection& aSelection);
 
-  void StartToListenToEditActions() { mListenerEnabled = true; }
-  void EndListeningToEditActions() { mListenerEnabled = false; }
+  void StartToListenToEditSubActions() { mListenerEnabled = true; }
+  void EndListeningToEditSubActions() { mListenerEnabled = false; }
 
 protected:
   virtual ~HTMLEditRules();
 
   HTMLEditor& HTMLEditorRef() const
   {
     MOZ_ASSERT(mData);
     return mData->HTMLEditorRef();
@@ -183,27 +182,27 @@ protected:
    */
   MOZ_MUST_USE nsresult WillInsert(bool* aCancel = nullptr);
 
   /**
    * Called before inserting text.
    * This method may actually inserts text into the editor.  Therefore, this
    * might cause destroying the editor.
    *
-   * @param aAction             Must be EditAction::insertIMEText or
-   *                            EditAction::insertText.
+   * @param aEditSubAction      Must be EditSubAction::insertIMEText or
+   *                            EditSubAction::insertText.
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    * @param inString            String to be inserted.
    * @param outString           String actually inserted.
    * @param aMaxLength          The maximum string length which the editor
    *                            allows to set.
    */
   MOZ_MUST_USE nsresult
-  WillInsertText(EditAction aAction, bool* aCancel, bool* aHandled,
+  WillInsertText(EditSubAction aEditSubAction, bool* aCancel, bool* aHandled,
                  const nsAString* inString, nsAString* outString,
                  int32_t aMaxLength);
 
   /**
    * WillLoadHTML() is called before loading enter document from source.
    * This removes bogus node if there is.
    */
   MOZ_MUST_USE nsresult WillLoadHTML();
@@ -700,17 +699,18 @@ protected:
   MOZ_MUST_USE nsresult
   ReturnInListItem(Element& aListItem, nsINode& aNode, int32_t aOffset);
 
   /**
    * Called after handling edit action.  This may adjust Selection, remove
    * unnecessary empty nodes, create <br> elements if needed, etc.
    */
   MOZ_MUST_USE nsresult
-  AfterEditInner(EditAction action, nsIEditor::EDirection aDirection);
+  AfterEditInner(EditSubAction aEditSubAction,
+                 nsIEditor::EDirection aDirection);
 
   /**
    * IndentAroundSelectionWithCSS() indents around Selection with CSS.
    * This method creates AutoSelectionRestorer.  Therefore, each caller
    * need to check if the editor is still available even if this returns
    * NS_OK.
    */
   MOZ_MUST_USE nsresult IndentAroundSelectionWithCSS();
@@ -897,64 +897,66 @@ protected:
    * where the other methods easier to handle edit action.
    */
   MOZ_MUST_USE nsresult NormalizeSelection();
 
   /**
    * GetPromotedPoint() figures out where a start or end point for a block
    * operation really is.
    */
-  EditorDOMPoint GetPromotedPoint(RulesEndpoint aWhere, nsINode& aNode,
-                                  int32_t aOffset, EditAction actionID);
+  EditorDOMPoint
+  GetPromotedPoint(RulesEndpoint aWhere, nsINode& aNode, int32_t aOffset,
+                   EditSubAction aEditSubAction);
 
   /**
    * GetPromotedRanges() runs all the selection range endpoint through
    * GetPromotedPoint().
    */
   void GetPromotedRanges(nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
-                         EditAction inOperationType);
+                         EditSubAction aEditSubAction);
 
   /**
    * PromoteRange() expands a range to include any parents for which all
    * editable children are already in range.
    */
-  void PromoteRange(nsRange& aRange, EditAction inOperationType);
+  void PromoteRange(nsRange& aRange, EditSubAction aEditSubAction);
 
   /**
    * GetNodesForOperation() runs through the ranges in the array and construct a
    * new array of nodes to be acted on.
    *
    * XXX This name stats with "Get" but actually this modifies the DOM tree with
    *     transaction.  We should rename this to making clearer what this does.
    */
   enum class TouchContent { no, yes };
   MOZ_MUST_USE nsresult
   GetNodesForOperation(nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
                        nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
-                       EditAction aOperationType, TouchContent aTouchContent);
+                       EditSubAction aEditSubAction,
+                       TouchContent aTouchContent);
 
   void GetChildNodesForOperation(
          nsINode& aNode,
          nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
 
   /**
    * GetNodesFromPoint() constructs a list of nodes from a point that will be
    * operated on.
    */
   MOZ_MUST_USE nsresult
-  GetNodesFromPoint(const EditorDOMPoint& aPoint, EditAction aOperation,
+  GetNodesFromPoint(const EditorDOMPoint& aPoint, EditSubAction aEditSubAction,
                     nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
                     TouchContent aTouchContent);
 
   /**
    * GetNodesFromSelection() constructs a list of nodes from the selection that
    * will be operated on.
    */
   MOZ_MUST_USE nsresult
-  GetNodesFromSelection(EditAction aOperation,
+  GetNodesFromSelection(EditSubAction aEditSubAction,
                         nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
                         TouchContent aTouchContent);
 
   enum class EntireList { no, yes };
   MOZ_MUST_USE nsresult
   GetListActionNodes(nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
                      EntireList aEntireList, TouchContent aTouchContent);
   void GetDefinitionListItemTypes(Element* aElement, bool* aDT, bool* aDD);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -151,17 +151,17 @@ HTMLEditor::HTMLEditor()
       ? ParagraphSeparator::div : ParagraphSeparator::br)
 {
   mIsHTMLEditorClass = true;
 }
 
 HTMLEditor::~HTMLEditor()
 {
   if (mRules && mRules->AsHTMLEditRules()) {
-    mRules->AsHTMLEditRules()->EndListeningToEditActions();
+    mRules->AsHTMLEditRules()->EndListeningToEditSubActions();
   }
 
   mTypeInState = nullptr;
 
   if (mLinkHandler && IsInitialized()) {
     nsCOMPtr<nsIPresShell> ps = GetPresShell();
 
     if (ps && ps->GetPresContext()) {
@@ -1149,17 +1149,19 @@ HTMLEditor::TabInTable(bool inIsShift,
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::InsertBrElementAtSelectionWithTransaction()
 {
   // calling it text insertion to trigger moz br treatment by rules
-  AutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertText,
+                                      nsIEditor::eNext);
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
   if (!selection->IsCollapsed()) {
     nsresult rv = DeleteSelectionAsAction(eNone, eStrip);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -1216,24 +1218,28 @@ HTMLEditor::CollapseSelectionToDeepestNo
  * This is mostly like InsertHTMLWithCharsetAndContext, but we can't use that
  * because it is selection-based and the rules code won't let us edit under the
  * <head> node
  */
 NS_IMETHODIMP
 HTMLEditor::ReplaceHeadContentsWithHTML(const nsAString& aSourceToInsert)
 {
   // don't do any post processing, rules get confused
-  AutoRules beginRulesSniffing(this, EditAction::ignore, nsIEditor::eNone);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::ignore,
+                                      nsIEditor::eNone);
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   CommitComposition();
 
-  // Do not use AutoRules -- rules code won't let us insert in <head>.  Use
-  // the head node as a parent and delete/insert directly.
+  // Do not use AutoTopLevelEditSubActionNotifier -- rules code won't let us
+  // insert in <head>.  Use the head node as a parent and delete/insert
+  // directly.
+  // XXX We're using AutoTopLevelEditSubActionNotifier above...
   nsCOMPtr<nsIDocument> document = GetDocument();
   if (NS_WARN_IF(!document)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   RefPtr<nsContentList> nodeList =
     document->GetElementsByTagName(NS_LITERAL_STRING("head"));
   NS_ENSURE_TRUE(nodeList, NS_ERROR_NULL_POINTER);
@@ -1549,28 +1555,30 @@ HTMLEditor::InsertElementAtSelection(Ele
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
 
   CommitComposition();
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertElement,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertElement,
+                                      nsIEditor::eNext);
 
   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;
-  RulesInfo ruleInfo(EditAction::insertElement);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  EditSubActionInfo subActionInfo(EditSubAction::insertElement);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled) {
     if (aDeleteSelection) {
       if (!IsBlockNode(aElement)) {
         // E.g., inserting an image.  In this case we don't need to delete any
@@ -1632,17 +1640,17 @@ HTMLEditor::InsertElementAtSelection(Ele
         RefPtr<Element> newBrElement =
           InsertBrElementWithTransaction(*selection, insertedPoint, ePrevious);
         if (NS_WARN_IF(!newBrElement)) {
           return NS_ERROR_FAILURE;
         }
       }
     }
   }
-  rv = rules->DidDoAction(selection, &ruleInfo, rv);
+  rv = rules->DidDoAction(selection, subActionInfo, rv);
   return rv;
 }
 
 template<typename PT, typename CT>
 EditorDOMPoint
 HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
               nsIContent& aNode,
               const EditorDOMPointBase<PT, CT>& aPointToInsert,
@@ -2005,27 +2013,30 @@ HTMLEditor::MakeOrChangeList(const nsASt
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::makeList, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::makeList,
+                                      nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  RulesInfo ruleInfo(EditAction::makeList);
-  ruleInfo.blockType = &aListType;
-  ruleInfo.entireList = entireList;
-  ruleInfo.bulletType = &aBulletType;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  EditSubActionInfo subActionInfo(EditSubAction::makeList);
+  subActionInfo.blockType = &aListType;
+  subActionInfo.entireList = entireList;
+  subActionInfo.bulletType = &aBulletType;
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled && selection->IsCollapsed()) {
     nsRange* firstRange = selection->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
@@ -2081,52 +2092,55 @@ HTMLEditor::MakeOrChangeList(const nsASt
     }
     ErrorResult error;
     selection->Collapse(RawRangeBoundary(newItem, 0), error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 NS_IMETHODIMP
 HTMLEditor::RemoveList(const nsAString& aListType)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::removeList, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::removeList,
+                                      nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  RulesInfo ruleInfo(EditAction::removeList);
+  EditSubActionInfo subActionInfo(EditSubAction::removeList);
   if (aListType.LowerCaseEqualsLiteral("ol")) {
-    ruleInfo.bOrdered = true;
+    subActionInfo.bOrdered = true;
   } else {
-    ruleInfo.bOrdered = false;
-  }
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+    subActionInfo.bOrdered = false;
+  }
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   // no default behavior for this yet.  what would it mean?
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 nsresult
 HTMLEditor::MakeDefinitionListItemWithTransaction(nsAtom& aTagName)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
@@ -2135,37 +2149,39 @@ HTMLEditor::MakeDefinitionListItemWithTr
              &aTagName == nsGkAtoms::dd);
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::makeDefListItem,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::makeDefListItem,
+                                      nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
   nsDependentAtomString tagName(&aTagName);
-  RulesInfo ruleInfo(EditAction::makeDefListItem);
-  ruleInfo.blockType = &tagName;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  EditSubActionInfo subActionInfo(EditSubAction::makeDefListItem);
+  subActionInfo.blockType = &tagName;
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled) {
     // todo: no default for now.  we count on rules to handle it.
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 nsresult
 HTMLEditor::InsertBasicBlockWithTransaction(nsAtom& aTagName)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
@@ -2174,26 +2190,28 @@ HTMLEditor::InsertBasicBlockWithTransact
              &aTagName != nsGkAtoms::dt);
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::makeBasicBlock,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::makeBasicBlock,
+                                      nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   nsDependentAtomString tagName(&aTagName);
-  RulesInfo ruleInfo(EditAction::makeBasicBlock);
-  ruleInfo.blockType = &tagName;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  EditSubActionInfo subActionInfo(EditSubAction::makeBasicBlock);
+  subActionInfo.blockType = &tagName;
+  nsresult rv =
+   rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled && selection->IsCollapsed()) {
     nsRange* firstRange = selection->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
@@ -2244,43 +2262,45 @@ HTMLEditor::InsertBasicBlockWithTransact
     // reposition selection to inside the block
     ErrorResult error;
     selection->Collapse(RawRangeBoundary(newBlock, 0), error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 NS_IMETHODIMP
 HTMLEditor::Indent(const nsAString& aIndent)
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   bool cancel, handled;
-  EditAction opID = EditAction::indent;
+  EditSubAction editSubAction = EditSubAction::indent;
   if (aIndent.LowerCaseEqualsLiteral("outdent")) {
-    opID = EditAction::outdent;
+    editSubAction = EditSubAction::outdent;
   }
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, editSubAction, nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  RulesInfo ruleInfo(opID);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  EditSubActionInfo subActionInfo(editSubAction);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
   if (!handled && selection->IsCollapsed() && aIndent.EqualsLiteral("indent")) {
     nsRange* firstRange = selection->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
@@ -2345,43 +2365,46 @@ HTMLEditor::Indent(const nsAString& aInd
       return NS_ERROR_FAILURE;
     }
     selection->Collapse(RawRangeBoundary(firstRange->GetStartContainer(), 0),
                         error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 //TODO: IMPLEMENT ALIGNMENT!
 
 NS_IMETHODIMP
 HTMLEditor::Align(const nsAString& aAlignType)
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::align, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::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);
-  RulesInfo ruleInfo(EditAction::align);
-  ruleInfo.alignType = &aAlignType;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  EditSubActionInfo subActionInfo(EditSubAction::align);
+  subActionInfo.alignType = &aAlignType;
+  nsresult rv =
+   rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 already_AddRefed<Element>
 HTMLEditor::GetElementOrParentByTagName(const nsAString& aTagName,
                                         nsINode* aNode)
 {
   MOZ_ASSERT(!aTagName.IsEmpty());
 
@@ -3356,17 +3379,17 @@ HTMLEditor::DoContentInserted(nsIContent
   if (ShouldReplaceRootElement()) {
     UpdateRootElement();
     nsContentUtils::AddScriptRunner(
       NewRunnableMethod("HTMLEditor::NotifyRootChanged",
                         this,
                         &HTMLEditor::NotifyRootChanged));
   }
   // We don't need to handle our own modifications
-  else if (!mAction && container->IsEditable()) {
+  else if (!mTopLevelEditSubAction && container->IsEditable()) {
     if (IsMozEditorBogusNode(aChild)) {
       // Ignore insertion of the bogus node
       return;
     }
     // Protect the edit rules object from dying
     RefPtr<TextEditRules> rules(mRules);
     rules->DocumentModified();
 
@@ -3399,17 +3422,17 @@ HTMLEditor::ContentRemoved(nsIContent* a
 
   if (SameCOMIdentity(aChild, mRootElement)) {
     mRootElement = nullptr;
     nsContentUtils::AddScriptRunner(
       NewRunnableMethod("HTMLEditor::NotifyRootChanged",
                         this,
                         &HTMLEditor::NotifyRootChanged));
   // We don't need to handle our own modifications
-  } else if (!mAction && aChild->GetParentNode()->IsEditable()) {
+  } else if (!mTopLevelEditSubAction && aChild->GetParentNode()->IsEditable()) {
     if (aChild && IsMozEditorBogusNode(aChild)) {
       // Ignore removal of the bogus node
       return;
     }
     // Protect the edit rules object from dying
     RefPtr<TextEditRules> rules(mRules);
     rules->DocumentModified();
   }
@@ -3465,48 +3488,51 @@ HTMLEditor::StyleSheetLoaded(StyleSheet*
       // Also save in our arrays of urls and sheets
       AddNewStyleSheetToList(mLastStyleSheetURL, aSheet);
     }
   }
 
   return NS_OK;
 }
 
-/**
- * All editor operations which alter the doc should be prefaced
- * with a call to StartOperation, naming the action and direction.
- */
-nsresult
-HTMLEditor::StartOperation(EditAction opID,
-                           nsIEditor::EDirection aDirection)
+void
+HTMLEditor::OnStartToHandleTopLevelEditSubAction(
+              EditSubAction aEditSubAction,
+              nsIEditor::EDirection aDirection)
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
-  EditorBase::StartOperation(opID, aDirection);  // will set mAction, mDirection
-  if (rules) {
-    return rules->BeforeEdit(mAction, mDirection);
-  }
-  return NS_OK;
+  EditorBase::OnStartToHandleTopLevelEditSubAction(aEditSubAction, aDirection);
+  if (!rules) {
+    return;
+  }
+
+  MOZ_ASSERT(mTopLevelEditSubAction == aEditSubAction);
+  MOZ_ASSERT(mDirection == aDirection);
+  DebugOnly<nsresult> rv =
+    rules->BeforeEdit(mTopLevelEditSubAction, mDirection);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+    "HTMLEditRules::BeforeEdit() failed to handle something");
 }
 
-/**
- * All editor operations which alter the doc should be followed
- * with a call to EndOperation.
- */
-nsresult
-HTMLEditor::EndOperation()
+void
+HTMLEditor::OnEndHandlingTopLevelEditSubAction()
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // post processing
-  nsresult rv = rules ? rules->AfterEdit(mAction, mDirection) : NS_OK;
-  EditorBase::EndOperation();  // will clear mAction, mDirection
-  return rv;
+  DebugOnly<nsresult> rv =
+    rules ? rules->AfterEdit(mTopLevelEditSubAction, mDirection) : NS_OK;
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+    "HTMLEditRules::AfterEdit() failed to handle something");
+  EditorBase::OnEndHandlingTopLevelEditSubAction();
+  MOZ_ASSERT(!mTopLevelEditSubAction);
+  MOZ_ASSERT(mDirection == eNone);
 }
 
 bool
 HTMLEditor::TagCanContainTag(nsAtom& aParentTag,
                              nsAtom& aChildTag) const
 {
   int32_t childTagEnum;
   // XXX Should this handle #cdata-section too?
@@ -4338,25 +4364,29 @@ HTMLEditor::SetCSSBackgroundColorWithTra
   RefPtr<TextEditRules> rules(mRules);
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
   bool isCollapsed = selection->IsCollapsed();
 
   AutoPlaceholderBatch batchIt(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertElement,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertElement,
+                                      nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
   AutoTransactionsConserveSelection dontChangeMySelection(this);
 
   bool cancel, handled;
-  RulesInfo ruleInfo(EditAction::setTextProperty);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  EditSubActionInfo subActionInfo(EditSubAction::setTextProperty);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return 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);
 
       nsCOMPtr<Element> cachedBlockParent;
 
@@ -4467,17 +4497,17 @@ HTMLEditor::SetCSSBackgroundColorWithTra
                                                        false);
           }
         }
       }
     }
   }
   if (!cancel) {
     // Post-process
-    rv = rules->DidDoAction(selection, &ruleInfo, rv);
+    rv = rules->DidDoAction(selection, subActionInfo, rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::SetBackgroundColor(const nsAString& aColor)
 {
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -40,17 +40,17 @@ class nsIClipboard;
 class nsILinkHandler;
 class nsTableWrapperFrame;
 class nsRange;
 
 namespace mozilla {
 class AutoSelectionSetterAfterTableEdit;
 class EmptyEditableFunctor;
 class ResizerSelectionListener;
-enum class EditAction : int32_t;
+enum class EditSubAction : int32_t;
 struct PropItem;
 template<class T> class OwningNonNull;
 namespace dom {
 class DocumentFragment;
 class Event;
 class MouseEvent;
 } // namespace dom
 namespace widget {
@@ -731,28 +731,20 @@ protected: // May be called by friends.
 
   nsresult ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
                       nsAtom* aProperty, nsAtom* aAttribute);
 
   nsresult SetPositionToAbsolute(Element& aElement);
   nsresult SetPositionToStatic(Element& aElement);
 
 protected: // Called by helper classes.
-  /**
-   * All editor operations which alter the doc should be prefaced
-   * with a call to StartOperation, naming the action and direction.
-   */
-  virtual nsresult StartOperation(EditAction opID,
-                                  nsIEditor::EDirection aDirection) override;
-
-  /**
-   * All editor operations which alter the doc should be followed
-   * with a call to EndOperation.
-   */
-  virtual nsresult EndOperation() override;
+  virtual void
+  OnStartToHandleTopLevelEditSubAction(
+    EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;
+  virtual void OnEndHandlingTopLevelEditSubAction() override;
 
   virtual nsresult EndUpdateViewBatch() override;
 
 protected: // Shouldn't be used by friend classes
   virtual ~HTMLEditor();
 
   /**
    * InsertNodeIntoProperAncestorWithTransaction() attempts to insert aNode
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -87,28 +87,33 @@ static nsresult FindTargetNode(nsINode* 
 nsresult
 HTMLEditor::LoadHTML(const nsAString& aInputString)
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
   // force IME commit; set up rules sniffing and batching
   CommitComposition();
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::loadHTML, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::loadHTML,
+                                      nsIEditor::eNext);
 
   // Get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
-  RulesInfo ruleInfo(EditAction::loadHTML);
+  EditSubActionInfo subActionInfo(EditSubAction::loadHTML);
   bool cancel, handled;
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   if (cancel) {
     return NS_OK; // rules canceled the operation
   }
 
   if (!handled) {
     // Delete Selection, but only if it isn't collapsed, see bug #106269
     if (!selection->IsCollapsed()) {
       rv = DeleteSelectionAsAction(eNone, eStrip);
@@ -153,17 +158,17 @@ HTMLEditor::LoadHTML(const nsAString& aI
       if (NS_WARN_IF(!pointToInsert.Offset())) {
         // Append the remaining children to the container if offset is
         // overflown.
         pointToInsert.SetToEndOf(pointToInsert.GetContainer());
       }
     }
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 NS_IMETHODIMP
 HTMLEditor::InsertHTML(const nsAString& aInString)
 {
   const nsString& empty = EmptyString();
 
   return DoInsertHTMLWithContext(aInString, empty, empty, empty,
@@ -202,17 +207,19 @@ HTMLEditor::DoInsertHTMLWithContext(cons
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
   // Prevent the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // force IME commit; set up rules sniffing and batching
   CommitComposition();
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::htmlPaste, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::htmlPaste,
+                                      nsIEditor::eNext);
 
   // Get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
   // create a dom document fragment that represents the structure to paste
   nsCOMPtr<nsINode> fragmentAsNode, streamStartParent, streamEndParent;
   int32_t streamStartOffset = 0, streamEndOffset = 0;
@@ -328,19 +335,19 @@ HTMLEditor::DoInsertHTMLWithContext(cons
       rv = DeleteTableCell(1);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     // collapse selection to beginning of deleted table content
     selection->CollapseToStart(IgnoreErrors());
   }
 
   // give rules a chance to handle or cancel
-  RulesInfo ruleInfo(EditAction::insertElement);
+  EditSubActionInfo subActionInfo(EditSubAction::insertElement);
   bool cancel, handled;
-  rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  rv = rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) {
     return NS_OK; // rules canceled the operation
   }
 
   if (!handled) {
     // Adjust position based on the first node we are going to insert.
     // FYI: WillDoAction() above might have changed the selection.
@@ -684,17 +691,17 @@ HTMLEditor::DoInsertHTMLWithContext(cons
           if (afterLeftLink.AdvanceOffset()) {
             selection->Collapse(afterLeftLink);
           }
         }
       }
     }
   }
 
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 bool
 HTMLEditor::IsInLink(nsINode* aNode,
                      nsCOMPtr<nsINode>* outLink)
 {
   NS_ENSURE_TRUE(aNode, false);
   if (outLink) {
@@ -1577,46 +1584,51 @@ HTMLEditor::PasteAsQuotation(int32_t aSe
   return PasteAsCitedQuotation(citation, aSelectionType);
 }
 
 NS_IMETHODIMP
 HTMLEditor::PasteAsCitedQuotation(const nsAString& aCitation,
                                   int32_t aSelectionType)
 {
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::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
-  RulesInfo ruleInfo(EditAction::insertElement);
+  EditSubActionInfo subActionInfo(EditSubAction::insertElement);
   bool cancel, handled;
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   nsCOMPtr<Element> newNode =
     DeleteSelectionAndCreateElement(*nsGkAtoms::blockquote);
   NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
 
   // Try to set type=cite.  Ignore it if this fails.
   newNode->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
                    NS_LITERAL_STRING("cite"), true);
 
   // Set the selection to the underneath the node we just inserted:
   rv = selection->Collapse(newNode, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // XXX Why don't we call HTMLEditRules::DidDoAction() after Paste()?
   return Paste(aSelectionType);
 }
 
 /**
  * Paste a plaintext quotation.
  */
 nsresult
 HTMLEditor::PasteAsPlaintextQuotation(int32_t aSelectionType)
@@ -1782,26 +1794,30 @@ HTMLEditor::InsertAsPlaintextQuotation(c
                                        bool aAddCites,
                                        nsINode** aNodeInserted)
 {
   // get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertQuotation,
+                                      nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  RulesInfo ruleInfo(EditAction::insertElement);
+  EditSubActionInfo subActionInfo(EditSubAction::insertElement);
   bool cancel, handled;
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return 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
   // and sized to the screen using 98 viewport width units.
   // We could use 100vw, but 98vw avoids a horizontal scroll bar where possible.
@@ -1851,16 +1867,18 @@ HTMLEditor::InsertAsPlaintextQuotation(c
 
   // Set the selection to just after the inserted node:
   if (NS_SUCCEEDED(rv) && newNode) {
     EditorRawDOMPoint afterNewNode(newNode);
     if (afterNewNode.AdvanceOffset()) {
       selection->Collapse(afterNewNode);
     }
   }
+
+  // XXX Why don't we call HTMLEditRules::DidDoAction() here?
   return rv;
 }
 
 NS_IMETHODIMP
 HTMLEditor::StripCites()
 {
   return TextEditor::StripCites();
 }
@@ -1883,26 +1901,30 @@ HTMLEditor::InsertAsCitedQuotation(const
     return InsertAsPlaintextQuotation(aQuotedText, true, aNodeInserted);
   }
 
   // get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertQuotation,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertQuotation,
+                                      nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  RulesInfo ruleInfo(EditAction::insertElement);
+  EditSubActionInfo subActionInfo(EditSubAction::insertElement);
   bool cancel, handled;
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   nsCOMPtr<Element> newNode =
     DeleteSelectionAndCreateElement(*nsGkAtoms::blockquote);
   NS_ENSURE_TRUE(newNode, NS_ERROR_NULL_POINTER);
 
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -80,26 +80,30 @@ HTMLEditor::SetInlineProperty(nsAtom* aP
   if (selection->IsCollapsed()) {
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
     mTypeInState->SetProp(aProperty, aAttribute, aValue);
     return NS_OK;
   }
 
   AutoPlaceholderBatch batchIt(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertElement,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertElement,
+                                      nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
   AutoTransactionsConserveSelection dontChangeMySelection(this);
 
   bool cancel, handled;
-  RulesInfo ruleInfo(EditAction::setTextProperty);
+  EditSubActionInfo subActionInfo(EditSubAction::setTextProperty);
   // Protect the edit rules object from dying
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return 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
       // selected
       rv = PromoteInlineRange(*range);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -170,17 +174,17 @@ HTMLEditor::SetInlineProperty(nsAtom* aP
                                           range->EndOffset(), *aProperty,
                                           aAttribute, aValue);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
   if (!cancel) {
     // Post-process
-    return rules->DidDoAction(selection, &ruleInfo, rv);
+    return rules->DidDoAction(selection, subActionInfo, rv);
   }
   return NS_OK;
 }
 
 // Helper function for SetInlinePropertyOn*: is aNode a simple old <b>, <font>,
 // <span style="">, etc. that we can reuse instead of creating a new one?
 bool
 HTMLEditor::IsSimpleModifiableNode(nsIContent* aContent,
@@ -1184,18 +1188,19 @@ HTMLEditor::GetInlinePropertyWithAttrVal
     val = &aValue;
   return GetInlinePropertyBase(*aProperty, aAttribute, val, aFirst, aAny, aAll, &outValue);
 }
 
 NS_IMETHODIMP
 HTMLEditor::RemoveAllInlineProperties()
 {
   AutoPlaceholderBatch batchIt(this);
-  AutoRules beginRulesSniffing(this, EditAction::resetTextProperties,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::resetTextProperties,
+                                      nsIEditor::eNext);
 
   nsresult rv = RemoveInlineProperty(nullptr, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::RemoveInlineProperty(const nsAString& aProperty,
@@ -1229,27 +1234,31 @@ HTMLEditor::RemoveInlineProperty(nsAtom*
       mTypeInState->ClearProp(aProperty, aAttribute);
     } else {
       mTypeInState->ClearAllProps();
     }
     return NS_OK;
   }
 
   AutoPlaceholderBatch batchIt(this);
-  AutoRules beginRulesSniffing(this, EditAction::removeTextProperty,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::removeTextProperty,
+                                      nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
   AutoTransactionsConserveSelection dontChangeMySelection(this);
 
   bool cancel, handled;
-  RulesInfo ruleInfo(EditAction::removeTextProperty);
+  EditSubActionInfo subActionInfo(EditSubAction::removeTextProperty);
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return 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) {
       if (aProperty == nsGkAtoms::name) {
         // Promote range if it starts or end in a named anchor and we want to
@@ -1334,17 +1343,17 @@ HTMLEditor::RemoveInlineProperty(nsAtom*
             SetInlinePropertyOnNode(node, *aProperty, aAttribute, value);
           }
         }
       }
     }
   }
   if (!cancel) {
     // Post-process
-    rv = rules->DidDoAction(selection, &ruleInfo, rv);
+    rv = rules->DidDoAction(selection, subActionInfo, rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::IncreaseFontSize()
 {
@@ -1386,18 +1395,19 @@ HTMLEditor::RelativeFontChange(FontSize 
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
     mTypeInState->SetProp(&atom, nullptr, EmptyString());
     return NS_OK;
   }
 
   // Wrap with txn batching, rules sniffing, and selection preservation code
   AutoPlaceholderBatch batchIt(this);
-  AutoRules beginRulesSniffing(this, EditAction::setTextProperty,
-                               nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::setTextProperty,
+                                      nsIEditor::eNext);
   AutoSelectionRestorer selectionRestorer(selection, this);
   AutoTransactionsConserveSelection dontChangeMySelection(this);
 
   // Loop through the ranges in the selection
   AutoRangeArray arrayOfRanges(selection);
   for (auto& range : arrayOfRanges.mRanges) {
     // Adjust range to include any ancestors with entirely selected children
     nsresult rv = PromoteInlineRange(*range);
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -370,17 +370,19 @@ HTMLEditor::InsertTableColumn(int32_t aN
                      &curStartRowIndex, &curStartColIndex,
                      &rowSpan, &colSpan,
                      &actualRowSpan, &actualColSpan, &isSelected);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertNode,
+                                      nsIEditor::eNext);
 
   // Use column after current cell if requested
   if (aAfter) {
     startColIndex += actualColSpan;
     //Detect when user is adding after a COLSPAN=0 case
     // Assume they want to stop the "0" behavior and
     // really add a new column. Thus we set the
     // colspan to its true value
@@ -503,17 +505,19 @@ HTMLEditor::InsertTableRow(int32_t aNumb
   NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
 
   int32_t rowCount, colCount;
   rv = GetTableSize(table, &rowCount, &colCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertNode,
+                                      nsIEditor::eNext);
 
   if (aAfter) {
     // Use row after current cell
     startRowIndex += actualRowSpan;
 
     //Detect when user is adding after a ROWSPAN=0 case
     // Assume they want to stop the "0" behavior and
     // really add a new row. Thus we set the
@@ -714,17 +718,19 @@ HTMLEditor::DeleteTableCell(int32_t aNum
                                &startRowIndex, &startColIndex);
 
   NS_ENSURE_SUCCESS(rv, rv);
   // Don't fail if we didn't find a table or cell
   NS_ENSURE_TRUE(table && cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent rules testing until we're done
-  AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteNode,
+                                      nsIEditor::eNext);
 
   RefPtr<Element> firstCell;
   rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (firstCell && selection->RangeCount() > 1) {
     // When > 1 selected cell,
     //  ignore aNumber and use selected cells
@@ -898,17 +904,19 @@ HTMLEditor::DeleteTableCellContents()
                                &startRowIndex, &startColIndex);
   NS_ENSURE_SUCCESS(rv, rv);
   // Don't fail if no cell found
   NS_ENSURE_TRUE(cell, NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND);
 
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent rules testing until we're done
-  AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteNode,
+                                      nsIEditor::eNext);
   //Don't let Rules System change the selection
   AutoTransactionsConserveSelection dontChangeSelection(this);
 
 
   RefPtr<Element> firstCell;
   rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -935,17 +943,19 @@ HTMLEditor::DeleteTableCellContents()
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::DeleteCellContents(Element* aCell)
 {
   // Prevent rules testing until we're done
-  AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteNode,
+                                      nsIEditor::eNext);
 
   while (nsCOMPtr<nsINode> child = aCell->GetLastChild()) {
     nsresult rv = DeleteNodeWithTransaction(*child);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   return NS_OK;
@@ -975,17 +985,19 @@ HTMLEditor::DeleteTableColumn(int32_t aN
     return DeleteTable2(table, selection);
   }
 
   // Check for counts too high
   aNumber = std::min(aNumber,(colCount-startColIndex));
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent rules testing until we're done
-  AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteNode,
+                                      nsIEditor::eNext);
 
   // Test if deletion is controlled by selected cells
   RefPtr<Element> firstCell;
   rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t rangeCount = selection->RangeCount();
 
@@ -1138,17 +1150,19 @@ HTMLEditor::DeleteTableRow(int32_t aNumb
 
   // Shortcut the case of deleting all rows in table
   if (!startRowIndex && aNumber >= rowCount) {
     return DeleteTable2(table, selection);
   }
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent rules testing until we're done
-  AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteNode,
+                                      nsIEditor::eNext);
 
   RefPtr<Element> firstCell;
   rv = GetFirstSelectedCell(nullptr, getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t rangeCount = selection->RangeCount();
 
   if (firstCell && rangeCount > 1) {
@@ -1217,17 +1231,19 @@ HTMLEditor::DeleteRow(Element* aTable,
 
   RefPtr<Element> cell;
   RefPtr<Element> cellInDeleteRow;
   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
   bool    isSelected;
   int32_t colIndex = 0;
 
   // Prevent rules testing until we're done
-  AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(*
+                                      this, EditSubAction::deleteNode,
+                                      nsIEditor::eNext);
 
   // The list of cells we will change rowspan in
   //  and the new rowspan values for each
   nsTArray<RefPtr<Element> > spanCellList;
   nsTArray<int32_t> newSpanList;
 
   int32_t rowCount, colCount;
   nsresult rv = GetTableSize(aTable, &rowCount, &colCount);
@@ -1657,17 +1673,19 @@ HTMLEditor::SplitTableCell()
 
   // Must have some span to split
   if (actualRowSpan <= 1 && actualColSpan <= 1) {
     return NS_OK;
   }
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertNode,
+                                      nsIEditor::eNext);
 
   // We reset selection
   AutoSelectionSetterAfterTableEdit setCaret(*this, table, startRowIndex,
                                              startColIndex, ePreviousColumn,
                                              false);
   //...so suppress Rules System selection munging
   AutoTransactionsConserveSelection dontChangeSelection(this);
 
@@ -1890,17 +1908,19 @@ HTMLEditor::SwitchTableCellHeaderType(El
 {
   if (NS_WARN_IF(!aSourceCell)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell created by
   // ReplaceContainerAndCloneAttributesWithTransaction().
-  AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertNode,
+                                      nsIEditor::eNext);
 
   // Save current selection to restore when done.
   // This is needed so ReplaceContainerAndCloneAttributesWithTransaction()
   // can monitor selection when replacing nodes.
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
@@ -2131,18 +2151,19 @@ HTMLEditor::JoinTableCells(bool aMergeNo
             NS_ENSURE_SUCCESS(rv, rv);
           }
         }
       }
     }
 
     // All cell contents are merged. Delete the empty cells we accumulated
     // Prevent rules testing until we're done
-    AutoRules beginRulesSniffing(this, EditAction::deleteNode,
-                                 nsIEditor::eNext);
+    AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                        *this, EditSubAction::deleteNode,
+                                        nsIEditor::eNext);
 
     for (uint32_t i = 0, n = deleteList.Length(); i < n; i++) {
       RefPtr<Element> nodeToBeRemoved = deleteList[i];
       if (nodeToBeRemoved) {
         rv = DeleteNodeWithTransaction(*nodeToBeRemoved);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
@@ -2246,17 +2267,19 @@ HTMLEditor::JoinTableCells(bool aMergeNo
 nsresult
 HTMLEditor::MergeCells(RefPtr<Element> aTargetCell,
                        RefPtr<Element> aCellToMerge,
                        bool aDeleteCellToMerge)
 {
   NS_ENSURE_TRUE(aTargetCell && aCellToMerge, NS_ERROR_NULL_POINTER);
 
   // Prevent rules testing until we're done
-  AutoRules beginRulesSniffing(this, EditAction::deleteNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteNode,
+                                      nsIEditor::eNext);
 
   // Don't need to merge if cell is empty
   if (!IsEmptyCell(aCellToMerge)) {
     // Get index of last child in target cell
     // If we fail or don't have children,
     //  we insert at index 0
     int32_t insertIndex = 0;
 
@@ -2455,17 +2478,19 @@ HTMLEditor::NormalizeTable(Element* aTab
   nsresult rv = GetTableSize(table, &rowCount, &colCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Save current selection
   AutoSelectionRestorer selectionRestorer(selection, this);
 
   AutoPlaceholderBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertNode,
+                                      nsIEditor::eNext);
 
   RefPtr<Element> cell;
   int32_t startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
   bool    isSelected;
 
   // Scan all cells in each row to detect bad rowspan values
   for (rowIndex = 0; rowIndex < rowCount; rowIndex++) {
     rv = FixBadRowSpan(table, rowIndex, rowCount);
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -67,17 +67,17 @@ TextEditRules::TextEditRules()
   , mData(nullptr)
   , mPasswordIMEIndex(0)
   , mCachedSelectionOffset(0)
   , mActionNesting(0)
   , mLockRulesSniffing(false)
   , mDidExplicitlySetInterline(false)
   , mDeleteBidiImmediately(false)
   , mIsHTMLEditRules(false)
-  , mTheAction(EditAction::none)
+  , mTopLevelEditSubAction(EditSubAction::none)
   , mLastStart(0)
   , mLastLength(0)
 {
   InitFields();
 }
 
 void
 TextEditRules::InitFields()
@@ -88,17 +88,17 @@ TextEditRules::InitFields()
   mPasswordIMEIndex = 0;
   mBogusNode = nullptr;
   mCachedSelectionNode = nullptr;
   mCachedSelectionOffset = 0;
   mActionNesting = 0;
   mLockRulesSniffing = false;
   mDidExplicitlySetInterline = false;
   mDeleteBidiImmediately = false;
-  mTheAction = EditAction::none;
+  mTopLevelEditSubAction = EditSubAction::none;
   mTimer = nullptr;
   mLastStart = 0;
   mLastLength = 0;
 }
 
 TextEditRules::~TextEditRules()
 {
    // do NOT delete mTextEditor here.  We do not hold a ref count to
@@ -196,36 +196,36 @@ TextEditRules::DetachEditor()
   if (mTimer) {
     mTimer->Cancel();
   }
   mTextEditor = nullptr;
   return NS_OK;
 }
 
 nsresult
-TextEditRules::BeforeEdit(EditAction aAction,
+TextEditRules::BeforeEdit(EditSubAction aEditSubAction,
                           nsIEditor::EDirection aDirection)
 {
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   AutoLockRulesSniffing lockIt(this);
   mDidExplicitlySetInterline = false;
   if (!mActionNesting) {
     // let rules remember the top level action
-    mTheAction = aAction;
+    mTopLevelEditSubAction = aEditSubAction;
   }
   mActionNesting++;
 
-  if (aAction == EditAction::setText) {
+  if (aEditSubAction == EditSubAction::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 = mTextEditor->GetRoot();
     mCachedSelectionOffset = 0;
   } else {
     Selection* selection = mTextEditor->GetSelection();
@@ -235,17 +235,17 @@ TextEditRules::BeforeEdit(EditAction aAc
     mCachedSelectionNode = selection->GetAnchorNode();
     mCachedSelectionOffset = selection->AnchorOffset();
   }
 
   return NS_OK;
 }
 
 nsresult
-TextEditRules::AfterEdit(EditAction aAction,
+TextEditRules::AfterEdit(EditSubAction aEditSubAction,
                          nsIEditor::EDirection aDirection)
 {
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   if (mLockRulesSniffing) {
     return NS_OK;
@@ -258,17 +258,17 @@ TextEditRules::AfterEdit(EditAction aAct
     Selection* selection = mTextEditor->GetSelection();
     if (NS_WARN_IF(!selection)) {
       return NS_ERROR_FAILURE;
     }
 
     AutoSafeEditorData setData(*this, *mTextEditor, *selection);
 
     nsresult rv =
-      TextEditorRef().HandleInlineSpellCheck(aAction, *selection,
+      TextEditorRef().HandleInlineSpellCheck(aEditSubAction, *selection,
                                              mCachedSelectionNode,
                                              mCachedSelectionOffset,
                                              nullptr, 0, nullptr, 0);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // no longer uses mCachedSelectionNode, so release it.
@@ -301,96 +301,96 @@ TextEditRules::AfterEdit(EditAction aAct
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to selection to after the text node in TextEditor");
   }
   return NS_OK;
 }
 
 nsresult
 TextEditRules::WillDoAction(Selection* aSelection,
-                            RulesInfo* aInfo,
+                            EditSubActionInfo& aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
-  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+  if (NS_WARN_IF(!aSelection)) {
     return NS_ERROR_INVALID_ARG;
   }
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   MOZ_ASSERT(aCancel);
   MOZ_ASSERT(aHandled);
 
   *aCancel = false;
   *aHandled = false;
 
   AutoSafeEditorData setData(*this, *mTextEditor, *aSelection);
 
   // my kingdom for dynamic cast
-  switch (aInfo->action) {
-    case EditAction::insertBreak:
+  switch (aInfo.mEditSubAction) {
+    case EditSubAction::insertBreak:
       UndefineCaretBidiLevel();
-      return WillInsertBreak(aCancel, aHandled, aInfo->maxLength);
-    case EditAction::insertText:
-    case EditAction::insertIMEText:
+      return WillInsertBreak(aCancel, aHandled, aInfo.maxLength);
+    case EditSubAction::insertText:
+    case EditSubAction::insertIMEText:
       UndefineCaretBidiLevel();
-      return WillInsertText(aInfo->action, aCancel, aHandled,
-                            aInfo->inString, aInfo->outString,
-                            aInfo->maxLength);
-    case EditAction::setText:
+      return WillInsertText(aInfo.mEditSubAction, aCancel, aHandled,
+                            aInfo.inString, aInfo.outString,
+                            aInfo.maxLength);
+    case EditSubAction::setText:
       UndefineCaretBidiLevel();
-      return WillSetText(aCancel, aHandled, aInfo->inString,
-                         aInfo->maxLength);
-    case EditAction::deleteSelection:
-      return WillDeleteSelection(aInfo->collapsedAction, aCancel, aHandled);
-    case EditAction::undo:
+      return WillSetText(aCancel, aHandled, aInfo.inString,
+                         aInfo.maxLength);
+    case EditSubAction::deleteSelection:
+      return WillDeleteSelection(aInfo.collapsedAction, aCancel, aHandled);
+    case EditSubAction::undo:
       return WillUndo(aCancel, aHandled);
-    case EditAction::redo:
+    case EditSubAction::redo:
       return WillRedo(aCancel, aHandled);
-    case EditAction::setTextProperty:
+    case EditSubAction::setTextProperty:
       return WillSetTextProperty(aCancel, aHandled);
-    case EditAction::removeTextProperty:
+    case EditSubAction::removeTextProperty:
       return WillRemoveTextProperty(aCancel, aHandled);
-    case EditAction::outputText:
-      return WillOutputText(aInfo->outputFormat, aInfo->outString, aInfo->flags,
+    case EditSubAction::outputText:
+      return WillOutputText(aInfo.outputFormat, aInfo.outString, aInfo.flags,
                             aCancel, aHandled);
-    case EditAction::insertElement:
+    case EditSubAction::insertElement:
       // i had thought this would be html rules only.  but we put pre elements
       // into plaintext mail when doing quoting for reply!  doh!
       return WillInsert(aCancel);
     default:
       return NS_ERROR_FAILURE;
   }
 }
 
 nsresult
 TextEditRules::DidDoAction(Selection* aSelection,
-                           RulesInfo* aInfo,
+                           EditSubActionInfo& aInfo,
                            nsresult aResult)
 {
-  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+  if (NS_WARN_IF(!aSelection)) {
     return NS_ERROR_INVALID_ARG;
   }
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   AutoSafeEditorData setData(*this, *mTextEditor, *aSelection);
 
   // 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(&TextEditorRef());
 
-  switch (aInfo->action) {
-    case EditAction::deleteSelection:
+  switch (aInfo.mEditSubAction) {
+    case EditSubAction::deleteSelection:
       return DidDeleteSelection();
-    case EditAction::undo:
+    case EditSubAction::undo:
       return DidUndo(aResult);
-    case EditAction::redo:
+    case EditSubAction::redo:
       return DidRedo(aResult);
     default:
       // Don't fail on transactions we don't handle here!
       return NS_OK;
   }
 }
 
 bool
@@ -663,30 +663,30 @@ TextEditRules::HandleNewLines(nsString& 
     case nsIPlaintextEditor::eNewlinesPasteIntact:
       // even if we're pasting newlines, don't paste leading/trailing ones
       aString.Trim(CRLF, true, true);
       break;
   }
 }
 
 nsresult
-TextEditRules::WillInsertText(EditAction aAction,
+TextEditRules::WillInsertText(EditSubAction aEditSubAction,
                               bool* aCancel,
                               bool* aHandled,
                               const nsAString* inString,
                               nsAString* outString,
                               int32_t aMaxLength)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  if (inString->IsEmpty() && aAction != EditAction::insertIMEText) {
+  if (inString->IsEmpty() && aEditSubAction != EditSubAction::insertIMEText) {
     // HACK: this is a fix for bug 19395
     // I can't outlaw all empty insertions
     // because IME transaction depend on them
     // There is more work to do to make the
     // world safe for IME.
     *aCancel = true;
     *aHandled = false;
     return NS_OK;
@@ -702,17 +702,17 @@ TextEditRules::WillInsertText(EditAction
   nsresult rv =
     TruncateInsertionIfNeeded(inString, outString, aMaxLength, &truncated);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   // If we're exceeding the maxlength when composing IME, we need to clean up
   // the composing text, so we shouldn't return early.
   if (truncated && outString->IsEmpty() &&
-      aAction != EditAction::insertIMEText) {
+      aEditSubAction != EditSubAction::insertIMEText) {
     *aCancel = true;
     return NS_OK;
   }
 
   uint32_t start = 0;
   uint32_t end = 0;
 
   // handle password field docs
@@ -738,17 +738,17 @@ TextEditRules::WillInsertText(EditAction
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // handle password field data
   // this has the side effect of changing all the characters in aOutString
   // to the replacement character
   if (IsPasswordEditor() &&
-      aAction == EditAction::insertIMEText) {
+      aEditSubAction == EditSubAction::insertIMEText) {
     RemoveIMETextFromPWBuf(start, outString);
   }
 
   // People have lots of different ideas about what text fields
   // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
   // The six possible options are:
   // 0. paste newlines intact
   // 1. paste up to the first newline (default)
@@ -805,17 +805,17 @@ TextEditRules::WillInsertText(EditAction
   }
 
   // we need to get the doc
   nsCOMPtr<nsIDocument> doc = TextEditorRef().GetDocument();
   if (NS_WARN_IF(!doc)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  if (aAction == EditAction::insertIMEText) {
+  if (aEditSubAction == EditSubAction::insertIMEText) {
     // Find better insertion point to insert text.
     EditorRawDOMPoint betterInsertionPoint =
       TextEditorRef().FindBetterInsertionPoint(atStartOfSelection);
     // If there is one or more IME selections, its minimum offset should be
     // the insertion point.
     int32_t IMESelectionOffset =
       TextEditorRef().GetIMESelectionStartOffsetIn(
                         betterInsertionPoint.GetContainer());
@@ -827,17 +827,17 @@ TextEditRules::WillInsertText(EditAction
                                                    betterInsertionPoint);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else {
-    // aAction == EditAction::insertText
+    // aEditSubAction == EditSubAction::insertText
 
     // don't change my selection in subtransactions
     AutoTransactionsConserveSelection dontChangeMySelection(&TextEditorRef());
 
     EditorRawDOMPoint pointAfterStringInserted;
     rv = TextEditorRef().InsertTextWithTransaction(*doc, *outString,
                                                    atStartOfSelection,
                                                    &pointAfterStringInserted);
@@ -1494,18 +1494,19 @@ TextEditRules::CreateBogusNodeIfNeeded()
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (mBogusNode) {
     // Let's not create more than one, ok?
     return NS_OK;
   }
 
   // tell rules system to not do any post-processing
-  AutoRules beginRulesSniffing(&TextEditorRef(), EditAction::ignore,
-                               nsIEditor::eNone);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      TextEditorRef(), EditSubAction::ignore,
+                                      nsIEditor::eNone);
 
   RefPtr<Element> rootElement = TextEditorRef().GetRoot();
   if (!rootElement) {
     // We don't even have a body yet, don't insert any bogus nodes at
     // this point.
     return NS_OK;
   }
 
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -18,19 +18,19 @@
 #include "nsISupportsImpl.h"
 #include "nsITimer.h"
 #include "nsString.h"
 #include "nscore.h"
 
 namespace mozilla {
 
 class AutoLockRulesSniffing;
+class EditSubActionInfo;
 class HTMLEditor;
 class HTMLEditRules;
-class RulesInfo;
 namespace dom {
 class Selection;
 } // namespace dom
 
 /**
  * Object that encapsulates HTML text-specific editing rules.
  *
  * To be a good citizen, edit rules must live by these restrictions:
@@ -71,26 +71,26 @@ public:
   TextEditRules();
 
   HTMLEditRules* AsHTMLEditRules();
   const HTMLEditRules* AsHTMLEditRules() const;
 
   virtual nsresult Init(TextEditor* aTextEditor);
   virtual nsresult SetInitialValue(const nsAString& aValue);
   virtual nsresult DetachEditor();
-  virtual nsresult BeforeEdit(EditAction aAction,
+  virtual nsresult BeforeEdit(EditSubAction aEditSubAction,
                               nsIEditor::EDirection aDirection);
-  virtual nsresult AfterEdit(EditAction aAction,
+  virtual nsresult AfterEdit(EditSubAction aEditSubAction,
                              nsIEditor::EDirection aDirection);
   virtual nsresult WillDoAction(Selection* aSelection,
-                                RulesInfo* aInfo,
+                                EditSubActionInfo& aInfo,
                                 bool* aCancel,
                                 bool* aHandled);
   virtual nsresult DidDoAction(Selection* aSelection,
-                               RulesInfo* aInfo,
+                               EditSubActionInfo& aInfo,
                                nsresult aResult);
 
   /**
    * Return false if the editor has non-empty text nodes or non-text
    * nodes.  Otherwise, i.e., there is no meaningful content,
    * return true.
    */
   virtual bool DocumentIsEmpty();
@@ -150,27 +150,27 @@ protected:
 
   // TextEditRules implementation methods
 
   /**
    * Called before inserting text.
    * This method may actually inserts text into the editor.  Therefore, this
    * might cause destroying the editor.
    *
-   * @param aAction             Must be EditAction::insertIMEText or
-   *                            EditAction::insertText.
+   * @param aEditSubAction      Must be EditSubAction::insertIMEText or
+   *                            EditSubAction::insertText.
    * @param aCancel             Returns true if the operation is canceled.
    * @param aHandled            Returns true if the edit action is handled.
    * @param inString            String to be inserted.
    * @param outString           String actually inserted.
    * @param aMaxLength          The maximum string length which the editor
    *                            allows to set.
    */
   MOZ_MUST_USE nsresult
-  WillInsertText(EditAction aAction, bool* aCancel, bool* aHandled,
+  WillInsertText(EditSubAction aEditSubAction, bool* aCancel, bool* aHandled,
                  const nsAString* inString, nsAString* outString,
                  int32_t aMaxLength);
 
   /**
    * Called before inserting a line break into the editor.
    * This method removes selected text if selection isn't collapsed.
    * Therefore, this might cause destroying the editor.
    *
@@ -494,81 +494,81 @@ protected:
   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;
+  EditSubAction mTopLevelEditSubAction;
   nsCOMPtr<nsITimer> mTimer;
   uint32_t mLastStart;
   uint32_t mLastLength;
 
   // friends
   friend class AutoLockRulesSniffing;
 };
 
 /**
  * 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
+class MOZ_STACK_CLASS EditSubActionInfo final
 {
 public:
-  explicit RulesInfo(EditAction aAction)
-    : action(aAction)
+  explicit EditSubActionInfo(EditSubAction aEditSubAction)
+    : mEditSubAction(aEditSubAction)
     , 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;
+  EditSubAction mEditSubAction;
 
-  // EditAction::insertText / EditAction::insertIMEText
+  // EditSubAction::insertText / EditSubAction::insertIMEText
   const nsAString* inString;
   nsAString* outString;
   const nsAString* outputFormat;
   int32_t maxLength;
 
-  // EditAction::outputText
+  // EditSubAction::outputText
   uint32_t flags;
 
-  // EditAction::deleteSelection
+  // EditSubAction::deleteSelection
   nsIEditor::EDirection collapsedAction;
   nsIEditor::EStripWrappers stripWrappers;
 
-  // EditAction::removeList
+  // EditSubAction::removeList
   bool bOrdered;
 
-  // EditAction::makeList
+  // EditSubAction::makeList
   bool entireList;
   const nsAString* bulletType;
 
-  // EditAction::align
+  // EditSubAction::align
   const nsAString* alignType;
 
-  // EditAction::makeBasicBlock
+  // EditSubAction::makeBasicBlock
   const nsAString* blockType;
 };
 
 /**
- * Stack based helper class for StartOperation()/EndOperation() sandwich.
+ * Stack based helper class for managing TextEditRules::mLockRluesSniffing.
  * This class sets a bool letting us know to ignore any rules sniffing
  * that tries to occur reentrantly.
  */
 class MOZ_STACK_CLASS AutoLockRulesSniffing final
 {
 public:
   explicit AutoLockRulesSniffing(TextEditRules* aRules)
     : mRules(aRules)
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -8,17 +8,17 @@
 #include "EditAggregateTransaction.h"
 #include "HTMLEditRules.h"
 #include "InternetCiter.h"
 #include "TextEditUtils.h"
 #include "gfxFontUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorDOMPoint.h"
-#include "mozilla/EditorUtils.h" // AutoPlaceholderBatch, AutoRules
+#include "mozilla/EditorUtils.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEditRules.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TextServicesDocument.h"
@@ -663,17 +663,19 @@ TextEditor::DeleteSelectionAsAction(EDir
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // delete placeholder txns merge.
   AutoPlaceholderBatch batch(this, nsGkAtoms::DeleteTxnName);
-  AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aDirection);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteSelection,
+                                      aDirection);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   // If there is an existing selection when an extended delete is requested,
   //  platforms that use "caret-style" caret positioning collapse the
   //  selection to the  start and then create a new selection.
@@ -696,28 +698,31 @@ TextEditor::DeleteSelectionAsAction(EDir
         }
         break;
       }
       default:
         break;
     }
   }
 
-  RulesInfo ruleInfo(EditAction::deleteSelection);
-  ruleInfo.collapsedAction = aDirection;
-  ruleInfo.stripWrappers = aStripWrappers;
+  EditSubActionInfo subActionInfo(EditSubAction::deleteSelection);
+  subActionInfo.collapsedAction = aDirection;
+  subActionInfo.stripWrappers = aStripWrappers;
   bool cancel, handled;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   if (!cancel && !handled) {
     rv = DeleteSelectionWithTransaction(aDirection, aStripWrappers);
   }
   if (!cancel) {
     // post-process
-    rv = rules->DidDoAction(selection, &ruleInfo, rv);
+    rv = rules->DidDoAction(selection, subActionInfo, rv);
   }
   return rv;
 }
 
 nsresult
 TextEditor::DeleteSelectionWithTransaction(EDirection aDirection,
                                            EStripWrappers aStripWrappers)
 {
@@ -739,17 +744,19 @@ TextEditor::DeleteSelectionWithTransacti
                                   &deleteCharLength);
     if (NS_WARN_IF(!deleteSelectionTransaction)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   RefPtr<CharacterData> deleteCharData =
     CharacterData::FromNodeOrNull(deleteNode);
-  AutoRules beginRulesSniffing(this, EditAction::deleteSelection, aDirection);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::deleteSelection,
+                                      aDirection);
 
   if (mRules && mRules->AsHTMLEditRules()) {
     if (!deleteNode) {
       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
       htmlEditRules->WillDeleteSelection(*selection);
     } else if (!deleteCharData) {
       RefPtr<HTMLEditRules> htmlEditRules = mRules->AsHTMLEditRules();
       htmlEditRules->WillDeleteNode(*selection, *deleteNode);
@@ -936,51 +943,53 @@ TextEditor::InsertTextAsAction(const nsA
 {
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
-  EditAction opID = EditAction::insertText;
+  EditSubAction editSubAction = EditSubAction::insertText;
   if (ShouldHandleIMEComposition()) {
-    opID = EditAction::insertIMEText;
+    editSubAction = EditSubAction::insertIMEText;
   }
 
   AutoPlaceholderBatch batch(this, nullptr);
-  AutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, editSubAction, nsIEditor::eNext);
 
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoString resultString;
-  // XXX can we trust instring to outlive ruleInfo,
-  // XXX and ruleInfo not to refer to instring in its dtor?
+  // XXX can we trust instring to outlive subActionInfo,
+  // XXX and subActionInfo not to refer to instring in its dtor?
   //nsAutoString instring(aStringToInsert);
-  RulesInfo ruleInfo(opID);
-  ruleInfo.inString = &aStringToInsert;
-  ruleInfo.outString = &resultString;
-  ruleInfo.maxLength = mMaxTextLength;
+  EditSubActionInfo subActionInfo(editSubAction);
+  subActionInfo.inString = &aStringToInsert;
+  subActionInfo.outString = &resultString;
+  subActionInfo.maxLength = mMaxTextLength;
 
   bool cancel, handled;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (!cancel && !handled) {
     // we rely on rules code for now - no default implementation
   }
   if (cancel) {
     return NS_OK;
   }
   // post-process
-  return rules->DidDoAction(selection, &ruleInfo, NS_OK);
+  return rules->DidDoAction(selection, subActionInfo, NS_OK);
 }
 
 NS_IMETHODIMP
 TextEditor::InsertLineBreak()
 {
   return InsertParagraphSeparatorAsAction();
 }
 
@@ -990,27 +999,29 @@ TextEditor::InsertParagraphSeparatorAsAc
   if (!mRules) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertBreak, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertBreak,
+                                      nsIEditor::eNext);
 
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
 
-  RulesInfo ruleInfo(EditAction::insertBreak);
-  ruleInfo.maxLength = mMaxTextLength;
+  EditSubActionInfo subActionInfo(EditSubAction::insertBreak);
+  subActionInfo.maxLength = mMaxTextLength;
   bool cancel, handled;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  nsresult rv = rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     // XXX DidDoAction() won't be called when WillDoAction() 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().
     return rv;
   }
 
@@ -1067,47 +1078,50 @@ TextEditor::InsertParagraphSeparatorAsAc
           selection->SetInterlinePosition(true, IgnoreErrors());
         }
       }
     }
   }
 
   if (!cancel) {
     // post-process, always called if WillInsertBreak didn't return cancel==true
-    rv = rules->DidDoAction(selection, &ruleInfo, rv);
+    rv = rules->DidDoAction(selection, subActionInfo, rv);
   }
   return rv;
 }
 
 nsresult
 TextEditor::SetText(const nsAString& aString)
 {
   if (NS_WARN_IF(!mRules)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // delete placeholder txns merge.
   AutoPlaceholderBatch batch(this, nullptr);
-  AutoRules beginRulesSniffing(this, EditAction::setText, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::setText,
+                                      nsIEditor::eNext);
 
   // pre-process
   RefPtr<Selection> selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_NULL_POINTER;
   }
-  RulesInfo ruleInfo(EditAction::setText);
-  ruleInfo.inString = &aString;
-  ruleInfo.maxLength = mMaxTextLength;
+  EditSubActionInfo subActionInfo(EditSubAction::setText);
+  subActionInfo.inString = &aString;
+  subActionInfo.maxLength = mMaxTextLength;
 
   bool cancel;
   bool handled;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (cancel) {
     return NS_OK;
   }
   if (!handled) {
     // We want to select trailing BR node to remove all nodes to replace all,
@@ -1129,17 +1143,17 @@ TextEditor::SetText(const nsAString& aSt
         NS_WARNING_ASSERTION(NS_FAILED(rv), "Failed to remove all text");
       } else {
         rv = InsertTextAsAction(aString);
         NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert the new text");
       }
     }
   }
   // post-process
-  return rules->DidDoAction(selection, &ruleInfo, rv);
+  return rules->DidDoAction(selection, subActionInfo, rv);
 }
 
 bool
 TextEditor::EnsureComposition(WidgetCompositionEvent& aCompositionEvent)
 {
   if (mComposition) {
     return true;
   }
@@ -1210,17 +1224,17 @@ TextEditor::OnCompositionChange(WidgetCo
     compositionChangeEventHandlingMarker(mComposition, &aCompsitionChangeEvent);
 
   RefPtr<nsCaret> caretP = presShell->GetCaret();
 
   nsresult rv;
   {
     AutoPlaceholderBatch batch(this, nsGkAtoms::IMETxnName);
 
-    MOZ_ASSERT(mIsInEditAction,
+    MOZ_ASSERT(mIsInEditSubAction,
       "AutoPlaceholderBatch should've notified the observes of before-edit");
     rv = InsertTextAsAction(aCompsitionChangeEvent.mData);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to insert new composition string");
 
     if (caretP) {
       caretP->SetSelection(selection);
     }
@@ -1481,32 +1495,34 @@ TextEditor::Undo(uint32_t aCount)
 
   NotifyEditorObservers(eNotifyEditorObserversOfBefore);
   if (NS_WARN_IF(!CanUndo()) || NS_WARN_IF(Destroyed())) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv;
   {
-    AutoRules beginRulesSniffing(this, EditAction::undo, nsIEditor::eNone);
+    AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                        *this, EditSubAction::undo,
+                                        nsIEditor::eNone);
 
-    RulesInfo ruleInfo(EditAction::undo);
+    EditSubActionInfo subActionInfo(EditSubAction::undo);
     RefPtr<Selection> selection = GetSelection();
     bool cancel, handled;
-    rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+    rv = rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
     if (!cancel && NS_SUCCEEDED(rv)) {
       RefPtr<TransactionManager> transactionManager(mTransactionManager);
       for (uint32_t i = 0; i < aCount; ++i) {
         rv = transactionManager->Undo();
         if (NS_WARN_IF(NS_FAILED(rv))) {
           break;
         }
         DoAfterUndoTransaction();
       }
-      rv = rules->DidDoAction(selection, &ruleInfo, rv);
+      rv = rules->DidDoAction(selection, subActionInfo, rv);
     }
   }
 
   NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   return rv;
 }
 
 NS_IMETHODIMP
@@ -1534,32 +1550,34 @@ TextEditor::Redo(uint32_t aCount)
 
   NotifyEditorObservers(eNotifyEditorObserversOfBefore);
   if (NS_WARN_IF(!CanRedo()) || NS_WARN_IF(Destroyed())) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv;
   {
-    AutoRules beginRulesSniffing(this, EditAction::redo, nsIEditor::eNone);
+    AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                        *this, EditSubAction::redo,
+                                        nsIEditor::eNone);
 
-    RulesInfo ruleInfo(EditAction::redo);
+    EditSubActionInfo subActionInfo(EditSubAction::redo);
     RefPtr<Selection> selection = GetSelection();
     bool cancel, handled;
-    rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+    rv = rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
     if (!cancel && NS_SUCCEEDED(rv)) {
       RefPtr<TransactionManager> transactionManager(mTransactionManager);
       for (uint32_t i = 0; i < aCount; ++i) {
         nsresult rv = transactionManager->Redo();
         if (NS_WARN_IF(NS_FAILED(rv))) {
           break;
         }
         DoAfterRedoTransaction();
       }
-      rv = rules->DidDoAction(selection, &ruleInfo, rv);
+      rv = rules->DidDoAction(selection, subActionInfo, rv);
     }
   }
 
   NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   return rv;
 }
 
 bool
@@ -1731,26 +1749,27 @@ TextEditor::GetAndInitDocEncoder(const n
 NS_IMETHODIMP
 TextEditor::OutputToString(const nsAString& aFormatType,
                            uint32_t aFlags,
                            nsAString& aOutputString)
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
-  RulesInfo ruleInfo(EditAction::outputText);
-  ruleInfo.outString = &aOutputString;
-  ruleInfo.flags = aFlags;
-  ruleInfo.outputFormat = &aFormatType;
+  EditSubActionInfo subActionInfo(EditSubAction::outputText);
+  subActionInfo.outString = &aOutputString;
+  subActionInfo.flags = aFlags;
+  subActionInfo.outputFormat = &aFormatType;
   Selection* selection = GetSelection();
   if (NS_WARN_IF(!selection)) {
     return NS_ERROR_FAILURE;
   }
   bool cancel, handled;
-  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
+  nsresult rv =
+    rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
   if (handled) {
     // This case will get triggered by password fields or single text node only.
     return rv;
   }
 
@@ -1761,16 +1780,17 @@ TextEditor::OutputToString(const nsAStri
   }
 
   nsCOMPtr<nsIDocumentEncoder> encoder =
     GetAndInitDocEncoder(aFormatType, aFlags, charsetStr);
   if (NS_WARN_IF(!encoder)) {
     return NS_ERROR_FAILURE;
   }
 
+  // XXX Why don't we call TextEditRules::DidDoAction() here?
   return encoder->EncodeToString(aOutputString);
 }
 
 NS_IMETHODIMP
 TextEditor::InsertTextWithQuotations(const nsAString& aStringToInsert)
 {
   nsresult rv = InsertTextAsAction(aStringToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1839,35 +1859,40 @@ TextEditor::InsertAsQuotation(const nsAS
     quotedStuff.Append(char16_t('\n'));
   }
 
   // get selection
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   AutoPlaceholderBatch beginBatching(this);
-  AutoRules beginRulesSniffing(this, EditAction::insertText, nsIEditor::eNext);
+  AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
+                                      *this, EditSubAction::insertText,
+                                      nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  RulesInfo ruleInfo(EditAction::insertElement);
+  EditSubActionInfo subActionInfo(EditSubAction::insertElement);
   bool cancel, handled;
-  rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
-  NS_ENSURE_SUCCESS(rv, rv);
+  rv = rules->WillDoAction(selection, subActionInfo, &cancel, &handled);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   if (cancel) {
     return NS_OK; // Rules canceled the operation.
   }
   if (!handled) {
     rv = InsertTextAsAction(quotedStuff);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to insert quoted text");
 
     // XXX Should set *aNodeInserted to the first node inserted
     if (aNodeInserted && NS_SUCCEEDED(rv)) {
       *aNodeInserted = nullptr;
     }
   }
+  // XXX Why don't we call TextEditRules::DidDoAction()?
   return rv;
 }
 
 NS_IMETHODIMP
 TextEditor::PasteAsCitedQuotation(const nsAString& aCitation,
                                   int32_t aSelectionType)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
@@ -1963,48 +1988,51 @@ TextEditor::GetEmbeddedObjects(nsIArray*
   if (NS_WARN_IF(!aNodeList)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aNodeList = nullptr;
   return NS_OK;
 }
 
-/**
- * All editor operations which alter the doc should be prefaced
- * with a call to StartOperation, naming the action and direction.
- */
-nsresult
-TextEditor::StartOperation(EditAction opID,
-                           nsIEditor::EDirection aDirection)
+void
+TextEditor::OnStartToHandleTopLevelEditSubAction(
+              EditSubAction aEditSubAction,
+              nsIEditor::EDirection aDirection)
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
-  EditorBase::StartOperation(opID, aDirection);  // will set mAction, mDirection
-  if (rules) {
-    return rules->BeforeEdit(mAction, mDirection);
+  EditorBase::OnStartToHandleTopLevelEditSubAction(aEditSubAction, aDirection);
+  if (!rules) {
+    return;
   }
-  return NS_OK;
+
+  MOZ_ASSERT(mTopLevelEditSubAction == aEditSubAction);
+  MOZ_ASSERT(mDirection == aDirection);
+  DebugOnly<nsresult> rv =
+    rules->BeforeEdit(mTopLevelEditSubAction, mDirection);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+    "TextEditRules::BeforeEdit() failed to handle something");
 }
 
-/**
- * All editor operations which alter the doc should be followed
- * with a call to EndOperation.
- */
-nsresult
-TextEditor::EndOperation()
+void
+TextEditor::OnEndHandlingTopLevelEditSubAction()
 {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // post processing
-  nsresult rv = rules ? rules->AfterEdit(mAction, mDirection) : NS_OK;
-  EditorBase::EndOperation();  // will clear mAction, mDirection
-  return rv;
+  DebugOnly<nsresult> rv =
+    rules ? rules->AfterEdit(mTopLevelEditSubAction, mDirection) : NS_OK;
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+    "TextEditRules::AfterEdit() failed to handle something");
+  EditorBase::OnEndHandlingTopLevelEditSubAction();
+  MOZ_ASSERT(!mTopLevelEditSubAction);
+  MOZ_ASSERT(mDirection == eNone);
 }
 
 nsresult
 TextEditor::SelectEntireDocument(Selection* aSelection)
 {
   if (!aSelection || !mRules) {
     return NS_ERROR_NULL_POINTER;
   }
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -18,17 +18,17 @@
 class nsIContent;
 class nsIDocumentEncoder;
 class nsIOutputStream;
 class nsISelectionController;
 class nsITransferable;
 
 namespace mozilla {
 class AutoEditInitRulesTrigger;
-enum class EditAction : int32_t;
+enum class EditSubAction : int32_t;
 
 namespace dom {
 class DragEvent;
 class Selection;
 } // namespace dom
 
 /**
  * The text editor implementation.
@@ -238,28 +238,21 @@ protected: // May be called by friends.
    */
   nsresult ExtendSelectionForDelete(Selection* aSelection,
                                     nsIEditor::EDirection* aAction);
 
   static void GetDefaultEditorPrefs(int32_t& aNewLineHandling,
                                     int32_t& aCaretStyle);
 
 protected: // Called by helper classes.
-  /**
-   * All editor operations which alter the doc should be prefaced
-   * with a call to StartOperation, naming the action and direction.
-   */
-  virtual nsresult StartOperation(EditAction opID,
-                                  nsIEditor::EDirection aDirection) override;
 
-  /**
-   * All editor operations which alter the doc should be followed
-   * with a call to EndOperation.
-   */
-  virtual nsresult EndOperation() override;
+  virtual void
+  OnStartToHandleTopLevelEditSubAction(
+    EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;
+  virtual void OnEndHandlingTopLevelEditSubAction() override;
 
   void BeginEditorInit();
   nsresult EndEditorInit();
 
 protected: // Shouldn't be used by friend classes
   virtual ~TextEditor();
 
   /**
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -106,34 +106,34 @@ mozInlineSpellStatus::mozInlineSpellStat
 //
 //    This is the most complicated case. For changes, we need to compute the
 //    range of stuff that changed based on the old and new caret positions,
 //    as well as use a range possibly provided by the editor (start and end,
 //    which are usually nullptr) to get a range with the union of these.
 
 nsresult
 mozInlineSpellStatus::InitForEditorChange(
-    EditAction aAction,
+    EditSubAction aEditSubAction,
     nsINode* aAnchorNode, uint32_t aAnchorOffset,
     nsINode* aPreviousNode, uint32_t aPreviousOffset,
     nsINode* aStartNode, uint32_t aStartOffset,
     nsINode* aEndNode, uint32_t aEndOffset)
 {
   if (NS_WARN_IF(!aAnchorNode) || NS_WARN_IF(!aPreviousNode)) {
     return NS_ERROR_FAILURE;
   }
 
   // save the anchor point as a range so we can find the current word later
   mAnchorRange = PositionToCollapsedRange(aAnchorNode, aAnchorOffset);
   if (NS_WARN_IF(!mAnchorRange)) {
     return NS_ERROR_FAILURE;
   }
 
-  bool deleted = aAction == EditAction::deleteSelection;
-  if (aAction == EditAction::insertIMEText) {
+  bool deleted = aEditSubAction == EditSubAction::deleteSelection;
+  if (aEditSubAction == EditSubAction::insertIMEText) {
     // IME may remove the previous node if it cancels composition when
     // there is no text around the composition.
     deleted = !aPreviousNode->IsInComposedDoc();
   }
 
   if (deleted) {
     // Deletes are easy, the range is just the current anchor. We set the range
     // to check to be empty, FinishInitOnEvent will fill in the range to be
@@ -169,17 +169,17 @@ mozInlineSpellStatus::InitForEditorChang
                                 aPreviousNode, aPreviousOffset);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // On insert save this range: DoSpellCheck optimizes things in this range.
   // Otherwise, just leave this nullptr.
-  if (aAction == EditAction::insertText)
+  if (aEditSubAction == EditSubAction::insertText)
     mCreatedRange = mRange;
 
   // if we were given a range, we need to expand our range to encompass it
   if (aStartNode && aEndNode) {
     cmpResult = mRange->ComparePoint(*aStartNode, aStartOffset, errorResult);
     if (NS_WARN_IF(errorResult.Failed())) {
       return errorResult.StealNSResult();
     }
@@ -549,17 +549,17 @@ mozInlineSpellChecker::SpellCheckingStat
 mozInlineSpellChecker::mozInlineSpellChecker() :
     mNumWordsInSpellSelection(0),
     mMaxNumWordsInSpellSelection(250),
     mNumPendingSpellChecks(0),
     mNumPendingUpdateCurrentDictionary(0),
     mDisabledAsyncToken(0),
     mNeedsCheckAfterNavigation(false),
     mFullSpellCheckScheduled(false),
-    mIsListeningToEditActions(false)
+    mIsListeningToEditSubActions(false)
 {
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs)
     prefs->GetIntPref(kMaxSpellCheckSelectionSize, &mMaxNumWordsInSpellSelection);
   mMaxMisspellingsPerCheck = mMaxNumWordsInSpellSelection * 3 / 4;
 }
 
 mozInlineSpellChecker::~mozInlineSpellChecker()
@@ -700,17 +700,17 @@ mozInlineSpellChecker::UpdateCanEnableIn
 
 nsresult
 mozInlineSpellChecker::RegisterEventListeners()
 {
   if (NS_WARN_IF(!mTextEditor)) {
     return NS_ERROR_FAILURE;
   }
 
-  StartToListenToEditActions();
+  StartToListenToEditSubActions();
 
   nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument();
   if (NS_WARN_IF(!doc)) {
     return NS_ERROR_FAILURE;
   }
   doc->AddEventListener(NS_LITERAL_STRING("blur"), this, true, false);
   doc->AddEventListener(NS_LITERAL_STRING("click"), this, false, false);
   doc->AddEventListener(NS_LITERAL_STRING("keypress"), this, false, false);
@@ -721,17 +721,17 @@ mozInlineSpellChecker::RegisterEventList
 
 nsresult
 mozInlineSpellChecker::UnregisterEventListeners()
 {
   if (NS_WARN_IF(!mTextEditor)) {
     return NS_ERROR_FAILURE;
   }
 
-  EndListeningToEditActions();
+  EndListeningToEditSubActions();
 
   nsCOMPtr<nsIDocument> doc = mTextEditor->GetDocument();
   if (NS_WARN_IF(!doc)) {
     return NS_ERROR_FAILURE;
   }
   doc->RemoveEventListener(NS_LITERAL_STRING("blur"), this, true);
   doc->RemoveEventListener(NS_LITERAL_STRING("click"), this, false);
   doc->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, false);
@@ -856,32 +856,32 @@ mozInlineSpellChecker::NotifyObservers(c
 //
 //    The start and end positions specify a range for the thing that happened,
 //    but these are usually nullptr, even when you'd think they would be useful
 //    because you want the range (for example, pasting). We ignore them in
 //    this case.
 
 nsresult
 mozInlineSpellChecker::SpellCheckAfterEditorChange(
-    EditAction aAction, Selection& aSelection,
+    EditSubAction aEditSubAction, Selection& aSelection,
     nsINode *aPreviousSelectedNode, uint32_t aPreviousSelectedOffset,
     nsINode *aStartNode, uint32_t aStartOffset,
     nsINode *aEndNode, uint32_t aEndOffset)
 {
   nsresult rv;
   if (!mSpellCheck)
     return NS_OK; // disabling spell checking is not an error
 
   // this means something has changed, and we never check the current word,
   // therefore, we should spellcheck for subsequent caret navigations
   mNeedsCheckAfterNavigation = true;
 
   // the anchor node is the position of the caret
   auto status = MakeUnique<mozInlineSpellStatus>(this);
-  rv = status->InitForEditorChange(aAction,
+  rv = status->InitForEditorChange(aEditSubAction,
                                    aSelection.GetAnchorNode(),
                                    aSelection.AnchorOffset(),
                                    aPreviousSelectedNode,
                                    aPreviousSelectedOffset,
                                    aStartNode, aStartOffset,
                                    aEndNode, aEndOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = ScheduleSpellCheck(Move(status));
@@ -1031,27 +1031,27 @@ mozInlineSpellChecker::IgnoreWords(const
   NS_ENSURE_SUCCESS(rv, rv);
   return ScheduleSpellCheck(Move(status));
 }
 
 void
 mozInlineSpellChecker::DidSplitNode(nsINode* aExistingRightNode,
                                     nsINode* aNewLeftNode)
 {
-  if (!mIsListeningToEditActions) {
+  if (!mIsListeningToEditSubActions) {
     return;
   }
   SpellCheckBetweenNodes(aNewLeftNode, 0, aNewLeftNode, 0);
 }
 
 void
 mozInlineSpellChecker::DidJoinNodes(nsINode& aLeftNode,
                                     nsINode& aRightNode)
 {
-  if (!mIsListeningToEditActions) {
+  if (!mIsListeningToEditSubActions) {
     return;
   }
   SpellCheckBetweenNodes(&aRightNode, 0, &aRightNode, 0);
 }
 
 // mozInlineSpellChecker::MakeSpellCheckRange
 //
 //    Given begin and end positions, this function constructs a range as
--- a/extensions/spellcheck/src/mozInlineSpellChecker.h
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.h
@@ -18,29 +18,29 @@ class mozInlineSpellWordUtil;
 class mozInlineSpellChecker;
 class mozISpellI18NUtil;
 class mozInlineSpellResume;
 class UpdateCurrentDictionaryCallback;
 
 namespace mozilla {
 class EditorSpellCheck;
 class TextEditor;
-enum class EditAction : int32_t;
+enum class EditSubAction : int32_t;
 
 namespace dom {
 class Event;
 } // namespace dom
 } // namespace mozilla
 
 class mozInlineSpellStatus
 {
 public:
   explicit mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker);
 
-  nsresult InitForEditorChange(mozilla::EditAction aAction,
+  nsresult InitForEditorChange(mozilla::EditSubAction aEditSubAction,
                                nsINode* aAnchorNode, uint32_t aAnchorOffset,
                                nsINode* aPreviousNode, uint32_t aPreviousOffset,
                                nsINode* aStartNode, uint32_t aStartOffset,
                                nsINode* aEndNode, uint32_t aEndOffset);
   nsresult InitForNavigation(bool aForceCheck, int32_t aNewPositionOffset,
                              nsINode* aOldAnchorNode, uint32_t aOldAnchorOffset,
                              nsINode* aNewAnchorNode, uint32_t aNewAnchorOffset,
                              bool* aContinue);
@@ -175,17 +175,17 @@ private:
   bool mNeedsCheckAfterNavigation;
 
   // Set when we have a pending mozInlineSpellResume which will check
   // the whole document.
   bool mFullSpellCheckScheduled;
 
   // Set to true when this instance needs to listen to edit actions of
   // the editor.
-  bool mIsListeningToEditActions;
+  bool mIsListeningToEditSubActions;
 
 public:
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIINLINESPELLCHECKER
   NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker, nsIDOMEventListener)
 
@@ -252,17 +252,17 @@ public:
 
   nsresult ResumeCheck(mozilla::UniquePtr<mozInlineSpellStatus>&& aStatus);
 
   // Those methods are called when mTextEditor splits a node or joins the
   // given nodes.
   void DidSplitNode(nsINode* aExistingRightNode, nsINode* aNewLeftNode);
   void DidJoinNodes(nsINode& aRightNode, nsINode& aLeftNode);
 
-  nsresult SpellCheckAfterEditorChange(mozilla::EditAction aAction,
+  nsresult SpellCheckAfterEditorChange(mozilla::EditSubAction aEditSubAction,
                                        mozilla::dom::Selection& aSelection,
                                        nsINode* aPreviousSelectedNode,
                                        uint32_t aPreviousSelectedOffset,
                                        nsINode* aStartNode,
                                        uint32_t aStartOffset,
                                        nsINode* aEndNode,
                                        uint32_t aEndOffset);
 
@@ -274,13 +274,13 @@ protected:
   nsresult CurrentDictionaryUpdated();
 
   // track the number of pending spell checks and async operations that may lead
   // to spell checks, notifying observers accordingly
   void ChangeNumPendingSpellChecks(int32_t aDelta,
                                    mozilla::TextEditor* aTextEditor = nullptr);
   void NotifyObservers(const char* aTopic, mozilla::TextEditor* aTextEditor);
 
-  void StartToListenToEditActions() { mIsListeningToEditActions = true; }
-  void EndListeningToEditActions() { mIsListeningToEditActions = false; }
+  void StartToListenToEditSubActions() { mIsListeningToEditSubActions = true; }
+  void EndListeningToEditSubActions() { mIsListeningToEditSubActions = false; }
 };
 
 #endif // #ifndef mozilla_mozInlineSpellChecker_h