Bug 1457083 - part 1: Make public methods of TextEditUtils and HTMLEditUtils guarantee that the editor instance and selection instance won't be destroyed while it handles any edit actions r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 26 Apr 2018 22:41:34 +0900
changeset 473362 d7e797f898b05a4edc47b7369dad793162cb91e6
parent 473361 f5cbef593cc1a17680e3ac94b7fa9b23a9ee41c0
child 473363 fcacbe739f6779e1bdb51773c1480c9d3271c6ee
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1457083
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1457083 - part 1: Make public methods of TextEditUtils and HTMLEditUtils guarantee that the editor instance and selection instance won't be destroyed while it handles any edit actions r=m_kato This patch creates a stack class in TextEditRules, its name is AutoSafeEditorData and make all public methods (except editor observer methods) which may change DOM tree, fire some DOM events or calling some XPCOM listeners create its instance in the stack. Then, it grabs associated editor instance and its selection instance. Next patch will make remaining public methods create AutoSafeEditorData. MozReview-Commit-ID: 8oshdhL3ONQ
editor/libeditor/EditorBase.h
editor/libeditor/HTMLAbsPositionEditor.cpp
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
editor/libeditor/HTMLEditor.h
editor/libeditor/TextEditRules.cpp
editor/libeditor/TextEditRules.h
editor/libeditor/TextEditor.cpp
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -9,17 +9,16 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
 #include "mozilla/EditorDOMPoint.h"     // for EditorDOMPoint
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/OwningNonNull.h"      // for OwningNonNull
 #include "mozilla/PresShell.h"          // for PresShell
 #include "mozilla/RangeBoundary.h"      // for RawRangeBoundary, RangeBoundary
 #include "mozilla/SelectionState.h"     // for RangeUpdater, etc.
 #include "mozilla/StyleSheet.h"         // for StyleSheet
-#include "mozilla/TextEditRules.h"      // for TextEditRules
 #include "mozilla/TransactionManager.h" // for TransactionManager
 #include "mozilla/WeakPtr.h"            // for WeakPtr
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Text.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed, nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"                // for nsIDocument
@@ -71,16 +70,17 @@ class InsertNodeTransaction;
 class InsertTextTransaction;
 class JoinNodeTransaction;
 class PlaceholderTransaction;
 class RemoveStyleSheetTransaction;
 class SplitNodeResult;
 class SplitNodeTransaction;
 class TextComposition;
 class TextEditor;
+class TextEditRules;
 class TextInputListener;
 class TextServicesDocument;
 enum class EditAction : int32_t;
 
 namespace dom {
 class DataTransfer;
 class DragEvent;
 class Element;
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -522,17 +522,17 @@ HTMLEditor::SetPositionToStatic(Element&
                                      EmptyString());
   }
 
   if (aElement.IsHTMLElement(nsGkAtoms::div) &&
       !HasStyleOrIdOrClass(&aElement)) {
     RefPtr<HTMLEditRules> htmlRules =
       static_cast<HTMLEditRules*>(mRules.get());
     NS_ENSURE_TRUE(htmlRules, NS_ERROR_FAILURE);
-    nsresult rv = htmlRules->MakeSureElemStartsOrEndsOnCR(aElement);
+    nsresult rv = htmlRules->MakeSureElemStartsAndEndsOnCR(aElement);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = RemoveContainerWithTransaction(aElement);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -240,62 +240,77 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHER
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLEditRules, TextEditRules,
                                    mDocChangeRange, mUtilRange, mNewBlock,
                                    mRangeItem)
 
 nsresult
 HTMLEditRules::Init(TextEditor* aTextEditor)
 {
-  if (NS_WARN_IF(!aTextEditor)) {
+  if (NS_WARN_IF(!aTextEditor) ||
+      NS_WARN_IF(!aTextEditor->AsHTMLEditor())) {
     return NS_ERROR_INVALID_ARG;
   }
 
   InitFields();
 
   mHTMLEditor = aTextEditor->AsHTMLEditor();
   if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  // call through to base class Init
+    return NS_ERROR_FAILURE;
+  }
+  Selection* selection = aTextEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
   nsresult rv = TextEditRules::Init(aTextEditor);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_FAILURE;
+  }
 
   // cache any prefs we care about
   static const char kPrefName[] =
     "editor.html.typing.returnInEmptyListItemClosesList";
   nsAutoCString returnInEmptyLIKillsList;
   Preferences::GetCString(kPrefName, returnInEmptyLIKillsList);
 
   // only when "false", becomes FALSE.  Otherwise (including empty), TRUE.
   // XXX Why was this pref designed as a string and not bool?
   mReturnInEmptyLIKillsList = !returnInEmptyLIKillsList.EqualsLiteral("false");
 
   // make a utility range for use by the listenter
-  nsCOMPtr<nsINode> node = mHTMLEditor->GetRoot();
+  nsCOMPtr<nsINode> node = HTMLEditorRef().GetRoot();
   if (!node) {
-    node = mHTMLEditor->GetDocument();
-  }
-
-  NS_ENSURE_STATE(node);
+    node = HTMLEditorRef().GetDocument();
+    if (NS_WARN_IF(!node)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
 
   mUtilRange = new nsRange(node);
 
   // set up mDocChangeRange to be whole doc
   // temporarily turn off rules sniffing
   AutoLockRulesSniffing lockIt(this);
   if (!mDocChangeRange) {
     mDocChangeRange = new nsRange(node);
   }
 
   if (node->IsElement()) {
     ErrorResult rv;
     mDocChangeRange->SelectNode(*node, rv);
-    NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
     AdjustSpecialBreaks();
   }
 
   StartToListenToEditActions();
 
   return NS_OK;
 }
 
@@ -310,46 +325,48 @@ HTMLEditRules::DetachEditor()
 nsresult
 HTMLEditRules::BeforeEdit(EditAction aAction,
                           nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
-  NS_ENSURE_STATE(mHTMLEditor);
-  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
 
   AutoLockRulesSniffing lockIt(this);
   mDidExplicitlySetInterline = false;
 
   if (!mActionNesting) {
     mActionNesting++;
 
     // Clear our flag about if just deleted a range
     mDidRangedDelete = false;
 
+    Selection* selection = mHTMLEditor->GetSelection();
+    if (NS_WARN_IF(!selection)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
     // Remember where our selection was before edit action took place:
 
-    // Get selection
-    RefPtr<Selection> selection = htmlEditor->GetSelection();
-
     // Get the selection location
-    if (NS_WARN_IF(!selection) || !selection->RangeCount()) {
+    if (!SelectionRef().RangeCount()) {
       return NS_ERROR_UNEXPECTED;
     }
-    mRangeItem->mStartContainer = selection->GetRangeAt(0)->GetStartContainer();
-    mRangeItem->mStartOffset = selection->GetRangeAt(0)->StartOffset();
-    mRangeItem->mEndContainer = selection->GetRangeAt(0)->GetEndContainer();
-    mRangeItem->mEndOffset = selection->GetRangeAt(0)->EndOffset();
+    mRangeItem->StoreRange(SelectionRef().GetRangeAt(0));
     nsCOMPtr<nsINode> selStartNode = mRangeItem->mStartContainer;
     nsCOMPtr<nsINode> selEndNode = mRangeItem->mEndContainer;
 
     // Register with range updater to track this as we perturb the doc
-    htmlEditor->mRangeUpdater.RegisterRangeItem(mRangeItem);
+    HTMLEditorRef().mRangeUpdater.RegisterRangeItem(mRangeItem);
 
     // Clear deletion state bool
     mDidDeleteSelection = false;
 
     // Clear out mDocChangeRange and mUtilRange
     if (mDocChangeRange) {
       // Clear out our accounting of what changed
       mDocChangeRange->Reset();
@@ -362,21 +379,23 @@ HTMLEditRules::BeforeEdit(EditAction aAc
     // Remember current inline styles for deletion and normal insertion ops
     if (aAction == EditAction::insertText ||
         aAction == EditAction::insertIMEText ||
         aAction == EditAction::deleteSelection ||
         IsStyleCachePreservingAction(aAction)) {
       nsCOMPtr<nsINode> selNode =
         aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
       nsresult rv = CacheInlineStyles(selNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
 
     // Stabilize the document against contenteditable count changes
-    nsHTMLDocument* htmlDoc = htmlEditor->GetHTMLDocument();
+    nsHTMLDocument* htmlDoc = HTMLEditorRef().GetHTMLDocument();
     if (NS_WARN_IF(!htmlDoc)) {
       return NS_ERROR_FAILURE;
     }
     if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
       htmlDoc->ChangeContentEditableCount(nullptr, +1);
       mRestoreContentEditableCount = true;
     }
 
@@ -392,46 +411,54 @@ HTMLEditRules::BeforeEdit(EditAction aAc
 nsresult
 HTMLEditRules::AfterEdit(EditAction aAction,
                          nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
-  NS_ENSURE_STATE(mHTMLEditor);
-  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
-
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
   AutoLockRulesSniffing lockIt(this);
 
   MOZ_ASSERT(mActionNesting > 0);
   nsresult rv = NS_OK;
   mActionNesting--;
   if (!mActionNesting) {
+    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);
 
     // Free up selectionState range item
-    htmlEditor->mRangeUpdater.DropRangeItem(mRangeItem);
+    HTMLEditorRef().mRangeUpdater.DropRangeItem(mRangeItem);
 
     // Reset the contenteditable count to its previous value
     if (mRestoreContentEditableCount) {
-      nsHTMLDocument* htmlDoc = htmlEditor->GetHTMLDocument();
+      nsHTMLDocument* htmlDoc = HTMLEditorRef().GetHTMLDocument();
       if (NS_WARN_IF(!htmlDoc)) {
         return NS_ERROR_FAILURE;
       }
       if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
         htmlDoc->ChangeContentEditableCount(nullptr, -1);
       }
       mRestoreContentEditableCount = false;
     }
   }
 
-  NS_ENSURE_SUCCESS(rv, rv);
-
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::AfterEditInner(EditAction aAction,
                               nsIEditor::EDirection aDirection)
 {
   ConfirmSelectionInBody();
@@ -573,131 +600,150 @@ HTMLEditRules::AfterEditInner(EditAction
 }
 
 nsresult
 HTMLEditRules::WillDoAction(Selection* aSelection,
                             RulesInfo* aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
-  MOZ_ASSERT(aInfo && aCancel && aHandled);
+  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  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) {
     return TextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
   }
 
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
+
   // Nothing to do if there's no selection to act on
-  if (!aSelection) {
+  if (NS_WARN_IF(!SelectionRef().RangeCount())) {
     return NS_OK;
   }
-  NS_ENSURE_TRUE(aSelection->RangeCount(), NS_OK);
 
   RefPtr<nsRange> range = aSelection->GetRangeAt(0);
   nsCOMPtr<nsINode> selStartNode = range->GetStartContainer();
 
-  NS_ENSURE_STATE(mHTMLEditor);
-  if (!mHTMLEditor->IsModifiableNode(selStartNode)) {
+  if (!HTMLEditorRef().IsModifiableNode(selStartNode)) {
     *aCancel = true;
     return NS_OK;
   }
 
   nsCOMPtr<nsINode> selEndNode = range->GetEndContainer();
 
   if (selStartNode != selEndNode) {
-    NS_ENSURE_STATE(mHTMLEditor);
-    if (!mHTMLEditor->IsModifiableNode(selEndNode)) {
+    if (!HTMLEditorRef().IsModifiableNode(selEndNode)) {
       *aCancel = true;
       return NS_OK;
     }
 
     NS_ENSURE_STATE(mHTMLEditor);
-    if (!mHTMLEditor->IsModifiableNode(range->GetCommonAncestor())) {
+    if (!HTMLEditorRef().IsModifiableNode(range->GetCommonAncestor())) {
       *aCancel = true;
       return NS_OK;
     }
   }
 
   switch (aInfo->action) {
     case EditAction::insertText:
     case EditAction::insertIMEText:
-      UndefineCaretBidiLevel(aSelection);
-      return WillInsertText(aInfo->action, aSelection, aCancel, aHandled,
+      UndefineCaretBidiLevel(&SelectionRef());
+      return WillInsertText(aInfo->action, &SelectionRef(), aCancel, aHandled,
                             aInfo->inString, aInfo->outString,
                             aInfo->maxLength);
     case EditAction::loadHTML:
-      return WillLoadHTML(aSelection, aCancel);
+      return WillLoadHTML(&SelectionRef(), aCancel);
     case EditAction::insertBreak:
-      UndefineCaretBidiLevel(aSelection);
-      return WillInsertBreak(*aSelection, aCancel, aHandled);
+      UndefineCaretBidiLevel(&SelectionRef());
+      return WillInsertBreak(SelectionRef(), aCancel, aHandled);
     case EditAction::deleteSelection:
-      return WillDeleteSelection(aSelection, aInfo->collapsedAction,
+      return WillDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
                                  aInfo->stripWrappers, aCancel, aHandled);
     case EditAction::makeList:
-      return WillMakeList(aSelection, aInfo->blockType, aInfo->entireList,
+      return WillMakeList(&SelectionRef(), aInfo->blockType, aInfo->entireList,
                           aInfo->bulletType, aCancel, aHandled);
     case EditAction::indent:
-      return WillIndent(aSelection, aCancel, aHandled);
+      return WillIndent(&SelectionRef(), aCancel, aHandled);
     case EditAction::outdent:
-      return WillOutdent(*aSelection, aCancel, aHandled);
+      return WillOutdent(SelectionRef(), aCancel, aHandled);
     case EditAction::setAbsolutePosition:
-      return WillAbsolutePosition(*aSelection, aCancel, aHandled);
+      return WillAbsolutePosition(SelectionRef(), aCancel, aHandled);
     case EditAction::removeAbsolutePosition:
-      return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
+      return WillRemoveAbsolutePosition(&SelectionRef(), aCancel, aHandled);
     case EditAction::align:
-      return WillAlign(*aSelection, *aInfo->alignType, aCancel, aHandled);
+      return WillAlign(SelectionRef(), *aInfo->alignType, aCancel, aHandled);
     case EditAction::makeBasicBlock:
-      return WillMakeBasicBlock(*aSelection, *aInfo->blockType, aCancel,
+      return WillMakeBasicBlock(SelectionRef(), *aInfo->blockType, aCancel,
                                 aHandled);
     case EditAction::removeList:
-      return WillRemoveList(aSelection, aInfo->bOrdered, aCancel, aHandled);
+      return WillRemoveList(&SelectionRef(), aInfo->bOrdered,
+                            aCancel, aHandled);
     case EditAction::makeDefListItem:
-      return WillMakeDefListItem(aSelection, aInfo->blockType,
+      return WillMakeDefListItem(&SelectionRef(), aInfo->blockType,
                                  aInfo->entireList, aCancel, aHandled);
     case EditAction::insertElement:
-      WillInsert(*aSelection, aCancel);
+      WillInsert(SelectionRef(), aCancel);
       return NS_OK;
     case EditAction::decreaseZIndex:
-      return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
+      return WillRelativeChangeZIndex(&SelectionRef(), -1, aCancel, aHandled);
     case EditAction::increaseZIndex:
-      return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
+      return WillRelativeChangeZIndex(&SelectionRef(), 1, aCancel, aHandled);
     default:
-      return TextEditRules::WillDoAction(aSelection, aInfo,
+      return TextEditRules::WillDoAction(&SelectionRef(), aInfo,
                                          aCancel, aHandled);
   }
 }
 
 nsresult
 HTMLEditRules::DidDoAction(Selection* aSelection,
                            RulesInfo* aInfo,
                            nsresult aResult)
 {
+  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *aSelection);
+
   switch (aInfo->action) {
     case EditAction::insertBreak:
-      return DidInsertBreak(aSelection, aResult);
+      return DidInsertBreak(&SelectionRef(), aResult);
     case EditAction::deleteSelection:
-      return DidDeleteSelection(aSelection, aInfo->collapsedAction, aResult);
+      return DidDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
+                                aResult);
     case EditAction::makeBasicBlock:
     case EditAction::indent:
     case EditAction::outdent:
     case EditAction::align:
-      return DidMakeBasicBlock(aSelection, aInfo, aResult);
+      return DidMakeBasicBlock(&SelectionRef(), aInfo, aResult);
     case EditAction::setAbsolutePosition: {
-      nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv = DidMakeBasicBlock(&SelectionRef(), aInfo, aResult);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       return DidAbsolutePosition();
     }
     default:
-      // pass through to TextEditRules
       return TextEditRules::DidDoAction(aSelection, aInfo, aResult);
   }
 }
 
 bool
 HTMLEditRules::DocumentIsEmpty()
 {
   return !!mBogusNode;
@@ -711,20 +757,32 @@ HTMLEditRules::GetListState(bool* aMixed
 {
   NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
   *aMixed = false;
   *aOL = false;
   *aUL = false;
   *aDL = false;
   bool bNonList = false;
 
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  Selection* selection = mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
                                    TouchContent::no);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // Examine list type for nodes in selection.
   for (const auto& curNode : arrayOfNodes) {
     if (!curNode->IsElement()) {
       bNonList = true;
     } else if (curNode->IsHTMLElement(nsGkAtoms::ul)) {
       *aUL = true;
     } else if (curNode->IsHTMLElement(nsGkAtoms::ol)) {
@@ -762,20 +820,32 @@ HTMLEditRules::GetListItemState(bool* aM
 {
   NS_ENSURE_TRUE(aMixed && aLI && aDT && aDD, NS_ERROR_NULL_POINTER);
   *aMixed = false;
   *aLI = false;
   *aDT = false;
   *aDD = false;
   bool bNonList = false;
 
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  Selection* selection = mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv = GetListActionNodes(arrayOfNodes, EntireList::no,
                                    TouchContent::no);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // examine list type for nodes in selection
   for (const auto& node : arrayOfNodes) {
     if (!node->IsElement()) {
       bNonList = true;
     } else if (node->IsAnyOfHTMLElements(nsGkAtoms::ul,
                                          nsGkAtoms::ol,
                                          nsGkAtoms::li)) {
@@ -804,82 +874,97 @@ HTMLEditRules::GetListItemState(bool* aM
 }
 
 nsresult
 HTMLEditRules::GetAlignment(bool* aMixed,
                             nsIHTMLEditor::EAlignment* aAlign)
 {
   MOZ_ASSERT(aMixed && aAlign);
 
-  NS_ENSURE_STATE(mHTMLEditor);
-  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  Selection* selection = mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   // For now, just return first alignment.  We'll lie about if it's mixed.
   // This is for efficiency given that our current ui doesn't care if it's
   // mixed.
   // cmanske: NOT TRUE! We would like to pay attention to mixed state in Format
   // | Align submenu!
 
   // This routine assumes that alignment is done ONLY via divs
 
   // Default alignment is left
   *aMixed = false;
   *aAlign = nsIHTMLEditor::eLeft;
 
-  // Get selection
-  NS_ENSURE_STATE(htmlEditor->GetSelection());
-  OwningNonNull<Selection> selection = *htmlEditor->GetSelection();
-
   // Get selection location
-  NS_ENSURE_TRUE(htmlEditor->GetRoot(), NS_ERROR_FAILURE);
-  OwningNonNull<Element> root = *htmlEditor->GetRoot();
+  if (NS_WARN_IF(!HTMLEditorRef().GetRoot())) {
+    return NS_ERROR_FAILURE;
+  }
+  OwningNonNull<Element> root = *HTMLEditorRef().GetRoot();
 
   int32_t rootOffset = root->GetParentNode() ?
                        root->GetParentNode()->ComputeIndexOf(root) : -1;
 
-  nsRange* firstRange = selection->GetRangeAt(0);
+  nsRange* firstRange = SelectionRef().GetRangeAt(0);
   if (NS_WARN_IF(!firstRange)) {
     return NS_ERROR_FAILURE;
   }
   EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
   MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
 
   // Is the selection collapsed?
   nsCOMPtr<nsINode> nodeToExamine;
   if (selection->IsCollapsed() || atStartOfSelection.GetContainerAsText()) {
     // If selection is collapsed, we want to look at the container of selection
     // start and its ancestors for divs with alignment on them.  If we are in a
     // text node, then that is the node of interest.
     nodeToExamine = atStartOfSelection.GetContainer();
+    if (NS_WARN_IF(!nodeToExamine)) {
+      return NS_ERROR_FAILURE;
+    }
   } else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
              atStartOfSelection.Offset() == static_cast<uint32_t>(rootOffset)) {
     // If we have selected the body, let's look at the first editable node
-    nodeToExamine = htmlEditor->GetNextEditableNode(atStartOfSelection);
+    nodeToExamine = HTMLEditorRef().GetNextEditableNode(atStartOfSelection);
+    if (NS_WARN_IF(!nodeToExamine)) {
+      return NS_ERROR_FAILURE;
+    }
   } else {
     nsTArray<RefPtr<nsRange>> arrayOfRanges;
-    GetPromotedRanges(selection, arrayOfRanges, EditAction::align);
+    GetPromotedRanges(SelectionRef(), arrayOfRanges, EditAction::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);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     nodeToExamine = arrayOfNodes.SafeElementAt(0);
-  }
-
-  NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
-
-  nsCOMPtr<Element> blockParent = htmlEditor->GetBlock(*nodeToExamine);
-
-  NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
-
-  if (htmlEditor->IsCSSEnabled() &&
+    if (NS_WARN_IF(!nodeToExamine)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  RefPtr<Element> blockParent = HTMLEditorRef().GetBlock(*nodeToExamine);
+  if (NS_WARN_IF(!blockParent)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (HTMLEditorRef().IsCSSEnabled() &&
       CSSEditUtils::IsCSSEditableProperty(blockParent, nullptr,
                                           nsGkAtoms::align)) {
     // We are in CSS mode and we know how to align this element with CSS
     nsAutoString value;
     // Let's get the value(s) of text-align or margin-left/margin-right
     CSSEditUtils::GetCSSEquivalentToHTMLInlineStyleSet(
       blockParent, nullptr, nsGkAtoms::align, value, CSSEditUtils::eComputed);
     if (value.EqualsLiteral("center") ||
@@ -973,38 +1058,48 @@ MarginPropertyAtomForIndent(nsINode& aNo
   return direction.EqualsLiteral("rtl") ?
     *nsGkAtoms::marginRight : *nsGkAtoms::marginLeft;
 }
 
 nsresult
 HTMLEditRules::GetIndentState(bool* aCanIndent,
                               bool* aCanOutdent)
 {
+  if (NS_WARN_IF(!aCanIndent) || NS_WARN_IF(!aCanOutdent)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   // XXX Looks like that this is implementation of
   //     nsIHTMLEditor::getIndentState() however nobody calls this method
   //     even with the interface method.
-  NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_FAILURE);
   *aCanIndent = true;
   *aCanOutdent = false;
 
-  // get selection
-  NS_ENSURE_STATE(mHTMLEditor && mHTMLEditor->GetSelection());
-  OwningNonNull<Selection> selection = *mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  Selection* selection = mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   // contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-  nsresult rv = GetNodesFromSelection(*selection, EditAction::indent,
+  nsresult rv = GetNodesFromSelection(SelectionRef(), EditAction::indent,
                                       arrayOfNodes, TouchContent::no);
-  NS_ENSURE_SUCCESS(rv, rv);
+  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.
-  NS_ENSURE_STATE(mHTMLEditor);
-  bool useCSS = mHTMLEditor->IsCSSEnabled();
+  bool useCSS = HTMLEditorRef().IsCSSEnabled();
   for (auto& curNode : Reversed(arrayOfNodes)) {
     if (HTMLEditUtils::IsNodeThatCanOutdent(curNode)) {
       *aCanOutdent = true;
       break;
     } else if (useCSS) {
       // we are in CSS mode, indentation is done using the margin-left (or margin-right) property
       nsAtom& marginProperty = MarginPropertyAtomForIndent(curNode);
       nsAutoString value;
@@ -1025,41 +1120,38 @@ HTMLEditRules::GetIndentState(bool* aCan
   if (*aCanOutdent) {
     return NS_OK;
   }
 
   // if we haven't found something to outdent yet, also check the parents
   // of selection endpoints.  We might have a blockquote or list item
   // in the parent hierarchy.
 
-  if (NS_WARN_IF(!mHTMLEditor)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  Element* rootElement = mHTMLEditor->GetRoot();
+  Element* rootElement = HTMLEditorRef().GetRoot();
   if (NS_WARN_IF(!rootElement)) {
     return NS_ERROR_FAILURE;
   }
 
   // Test selection start container hierarchy.
-  EditorRawDOMPoint selectionStartPoint(EditorBase::GetStartPoint(selection));
+  EditorRawDOMPoint selectionStartPoint(
+                      EditorBase::GetStartPoint(&SelectionRef()));
   if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
     return NS_ERROR_FAILURE;
   }
   for (nsINode* node = selectionStartPoint.GetContainer();
        node && node != rootElement;
        node = node->GetParentNode()) {
     if (HTMLEditUtils::IsNodeThatCanOutdent(node)) {
       *aCanOutdent = true;
       return NS_OK;
     }
   }
 
   // Test selection end container hierarchy.
-  EditorRawDOMPoint selectionEndPoint(EditorBase::GetEndPoint(selection));
+  EditorRawDOMPoint selectionEndPoint(EditorBase::GetEndPoint(&SelectionRef()));
   if (NS_WARN_IF(!selectionEndPoint.IsSet())) {
     return NS_ERROR_FAILURE;
   }
   for (nsINode* node = selectionEndPoint.GetContainer();
        node && node != rootElement;
        node = node->GetParentNode()) {
     if (HTMLEditUtils::IsNodeThatCanOutdent(node)) {
       *aCanOutdent = true;
@@ -1069,82 +1161,94 @@ HTMLEditRules::GetIndentState(bool* aCan
   return NS_OK;
 }
 
 
 nsresult
 HTMLEditRules::GetParagraphState(bool* aMixed,
                                  nsAString& outFormat)
 {
+  if (NS_WARN_IF(!aMixed)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   // This routine is *heavily* tied to our ui choices in the paragraph
   // style popup.  I can't see a way around that.
-  NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
   *aMixed = true;
   outFormat.Truncate(0);
 
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  Selection* selection = mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
   bool bMixed = false;
   // using "x" as an uninitialized value, since "" is meaningful
   nsAutoString formatStr(NS_LITERAL_STRING("x"));
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv = GetParagraphFormatNodes(arrayOfNodes, TouchContent::no);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // post process list.  We need to replace any block nodes that are not format
   // nodes with their content.  This is so we only have to look "up" the hierarchy
   // to find format nodes, instead of both up and down.
   for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
     auto& curNode = arrayOfNodes[i];
     nsAutoString format;
     // if it is a known format node we have it easy
     if (IsBlockNode(curNode) && !HTMLEditUtils::IsFormatNode(curNode)) {
       // arrayOfNodes.RemoveObject(curNode);
       rv = AppendInnerFormatNodes(arrayOfNodes, curNode);
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
 
   // we might have an empty node list.  if so, find selection parent
   // and put that on the list
   if (arrayOfNodes.IsEmpty()) {
-    if (NS_WARN_IF(!mHTMLEditor)) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-    Selection* selection = mHTMLEditor->GetSelection();
-    if (NS_WARN_IF(!selection)) {
-      return NS_ERROR_FAILURE;
-    }
-    EditorRawDOMPoint selectionStartPoint(EditorBase::GetStartPoint(selection));
+    EditorRawDOMPoint selectionStartPoint(
+                        EditorBase::GetStartPoint(&SelectionRef()));
     if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
       return NS_ERROR_FAILURE;
     }
     arrayOfNodes.AppendElement(*selectionStartPoint.GetContainer());
   }
 
   // remember root node
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<Element> rootElem = mHTMLEditor->GetRoot();
-  NS_ENSURE_TRUE(rootElem, NS_ERROR_NULL_POINTER);
+  Element* rootElement = HTMLEditorRef().GetRoot();
+  if (NS_WARN_IF(!rootElement)) {
+    return NS_ERROR_FAILURE;
+  }
 
   // loop through the nodes in selection and examine their paragraph format
   for (auto& curNode : Reversed(arrayOfNodes)) {
     nsAutoString format;
     // if it is a known format node we have it easy
     if (HTMLEditUtils::IsFormatNode(curNode)) {
       GetFormatString(curNode, format);
     } else if (IsBlockNode(curNode)) {
       // this is a div or some other non-format block.
       // we should ignore it.  Its children were appended to this list
       // by AppendInnerFormatNodes() call above.  We will get needed
       // info when we examine them instead.
       continue;
     } else {
       nsINode* node = curNode->GetParentNode();
       while (node) {
-        if (node == rootElem) {
+        if (node == rootElement) {
           format.Truncate(0);
           break;
         } else if (HTMLEditUtils::IsFormatNode(node)) {
           GetFormatString(node, format);
           break;
         }
         // else keep looking up
         node = node->GetParentNode();
@@ -9391,17 +9495,17 @@ HTMLEditRules::RemoveAlignment(nsINode& 
     if (child->IsHTMLElement(nsGkAtoms::center)) {
       // the current node is a CENTER element
       // first remove children's alignment
       nsresult rv = RemoveAlignment(*child, aAlignType, true);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // we may have to insert BRs in first and last position of element's children
       // if the nodes before/after are not blocks and not BRs
-      rv = MakeSureElemStartsOrEndsOnCR(*child);
+      rv = MakeSureElemStartsAndEndsOnCR(*child);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // now remove the CENTER container
       NS_ENSURE_STATE(mHTMLEditor);
       rv = mHTMLEditor->RemoveContainerWithTransaction(*child->AsElement());
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (IsBlockNode(*child) || child->IsHTMLElement(nsGkAtoms::hr)) {
       // the current node is a block element
@@ -9497,21 +9601,37 @@ HTMLEditRules::MakeSureElemStartsOrEndsO
     if (NS_WARN_IF(!brElement)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
 nsresult
-HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode)
-{
+HTMLEditRules::MakeSureElemStartsAndEndsOnCR(nsINode& aNode)
+{
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  Selection* selection = mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
   nsresult rv = MakeSureElemStartsOrEndsOnCR(aNode, false);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return MakeSureElemStartsOrEndsOnCR(aNode, true);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  rv = MakeSureElemStartsOrEndsOnCR(aNode, true);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 nsresult
 HTMLEditRules::AlignBlock(Element& aElement,
                           const nsAString& aAlignType,
                           ContentsOnly aContentsOnly)
 {
   if (!IsBlockNode(aElement) && !aElement.IsHTMLElement(nsGkAtoms::hr)) {
@@ -9943,32 +10063,34 @@ HTMLEditRules::DocumentModified()
 
 void
 HTMLEditRules::DocumentModifiedWorker()
 {
   if (!mHTMLEditor) {
     return;
   }
 
+  Selection* selection = mHTMLEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return;
+  }
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+
   // DeleteNodeWithTransaction() below may cause a flush, which could destroy
   // the editor
   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
-  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
-  RefPtr<Selection> selection = htmlEditor->GetSelection();
-  if (!selection) {
-    return;
-  }
-
   // Delete our bogus node, if we have one, since the document might not be
   // empty any more.
   if (mBogusNode) {
-    DebugOnly<nsresult> rv = htmlEditor->DeleteNodeWithTransaction(*mBogusNode);
+    DebugOnly<nsresult> rv =
+      HTMLEditorRef().DeleteNodeWithTransaction(*mBogusNode);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
       "Failed to remove the bogus node");
     mBogusNode = nullptr;
   }
 
   // Try to recreate the bogus node if needed.
-  CreateBogusNodeIfNeeded(selection);
+  CreateBogusNodeIfNeeded(&SelectionRef());
 }
 
 } // namespace mozilla
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -97,38 +97,42 @@ public:
   virtual bool DocumentIsEmpty() override;
   virtual nsresult DocumentModified() override;
 
   nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
   nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
   nsresult GetIndentState(bool* aCanIndent, bool* aCanOutdent);
   nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
   nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
-  nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode);
+  nsresult MakeSureElemStartsAndEndsOnCR(nsINode& aNode);
 
   void DidCreateNode(Element* aNewElement);
   void DidInsertNode(nsIContent& aNode);
   void WillDeleteNode(nsINode* aChild);
   void DidSplitNode(nsINode* aExistingRightNode,
                     nsINode* aNewLeftNode);
   void WillJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
   void DidJoinNodes(nsINode& aLeftNode, nsINode& aRightNode);
   void DidInsertText(nsINode* aTextNode, int32_t aOffset,
                      const nsAString& aString);
   void DidDeleteText(nsINode* aTextNode, int32_t aOffset, int32_t aLength);
   void WillDeleteSelection(Selection* aSelection);
 
-  void DeleteNodeIfCollapsedText(nsINode& aNode);
-
   void StartToListenToEditActions() { mListenerEnabled = true; }
   void EndListeningToEditActions() { mListenerEnabled = false; }
 
 protected:
   virtual ~HTMLEditRules();
 
+  HTMLEditor& HTMLEditorRef() const
+  {
+    MOZ_ASSERT(mData);
+    return mData->HTMLEditorRef();
+  }
+
   enum RulesEndpoint
   {
     kStart,
     kEnd
   };
 
   void InitFields();
 
@@ -139,16 +143,18 @@ protected:
                           bool* aHandled,
                           const nsAString* inString,
                           nsAString* outString,
                           int32_t aMaxLength);
   nsresult WillLoadHTML(Selection* aSelection, bool* aCancel);
   nsresult WillInsertBreak(Selection& aSelection, bool* aCancel,
                            bool* aHandled);
 
+  void DeleteNodeIfCollapsedText(nsINode& aNode);
+
   /**
    * InsertBRElement() inserts a <br> element into aInsertToBreak.
    *
    * @param aSelection          The selection.
    * @param aInsertToBreak      The point where new <br> element will be
    *                            inserted before.
    */
   nsresult InsertBRElement(Selection& aSelection,
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -7,17 +7,16 @@
 #define mozilla_HTMLEditor_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ComposerCommandsUpdater.h"
 #include "mozilla/CSSEditUtils.h"
 #include "mozilla/ManualNAC.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/TextEditor.h"
-#include "mozilla/TextEditRules.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 
 #include "nsAttrName.h"
 #include "nsCOMPtr.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsIDocumentObserver.h"
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -52,16 +52,17 @@ using namespace dom;
   };
 
 /********************************************************
  * mozilla::TextEditRules
  ********************************************************/
 
 TextEditRules::TextEditRules()
   : mTextEditor(nullptr)
+  , mData(nullptr)
   , mPasswordIMEIndex(0)
   , mCachedSelectionOffset(0)
   , mActionNesting(0)
   , mLockRulesSniffing(false)
   , mDidExplicitlySetInterline(false)
   , mDeleteBidiImmediately(false)
   , mIsHTMLEditRules(false)
   , mTheAction(EditAction::none)
@@ -122,45 +123,56 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
 
 nsresult
 TextEditRules::Init(TextEditor* aTextEditor)
 {
-  if (!aTextEditor) {
-    return NS_ERROR_NULL_POINTER;
+  if (NS_WARN_IF(!aTextEditor)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  Selection* selection = aTextEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
   }
 
   InitFields();
 
   // We hold a non-refcounted reference back to our editor.
   mTextEditor = aTextEditor;
-  RefPtr<Selection> selection = mTextEditor->GetSelection();
-  NS_WARNING_ASSERTION(selection, "editor cannot get selection");
+  AutoSafeEditorData setData(*this, *mTextEditor, *selection);
 
-  // Put in a magic br if needed. This method handles null selection,
+  // Put in a magic <br> if needed. This method handles null selection,
   // which should never happen anyway
-  nsresult rv = CreateBogusNodeIfNeeded(selection);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = CreateBogusNodeIfNeeded(&SelectionRef());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // If the selection hasn't been set up yet, set it up collapsed to the end of
   // our editable content.
-  if (!selection->RangeCount()) {
-    rv = mTextEditor->CollapseSelectionToEnd(selection);
-    NS_ENSURE_SUCCESS(rv, rv);
+  if (!SelectionRef().RangeCount()) {
+    rv = TextEditorRef().CollapseSelectionToEnd(&SelectionRef());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   if (IsPlaintextEditor()) {
     // ensure trailing br node
     rv = CreateTrailingBRIfNeeded();
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
+  // XXX We should use AddBoolVarCache and use "current" value at initializing.
   mDeleteBidiImmediately =
     Preferences::GetBool("bidi.edit.delete_immediately", false);
 
   return NS_OK;
 }
 
 nsresult
 TextEditRules::SetInitialValue(const nsAString& aValue)
@@ -194,30 +206,31 @@ TextEditRules::BeforeEdit(EditAction aAc
   if (!mActionNesting) {
     // let rules remember the top level action
     mTheAction = aAction;
   }
   mActionNesting++;
 
   // get the selection and cache the position before editing
   if (NS_WARN_IF(!mTextEditor)) {
-    return NS_ERROR_FAILURE;
+    return NS_ERROR_NOT_AVAILABLE;
   }
-  RefPtr<TextEditor> textEditor = mTextEditor;
-  RefPtr<Selection> selection = textEditor->GetSelection();
-  NS_ENSURE_STATE(selection);
 
   if (aAction == EditAction::setText) {
     // setText replaces all text, so mCachedSelectionNode might be invalid on
     // AfterEdit.
     // Since this will be used as start position of spellchecker, we should
     // use root instead.
-    mCachedSelectionNode = textEditor->GetRoot();
+    mCachedSelectionNode = mTextEditor->GetRoot();
     mCachedSelectionOffset = 0;
   } else {
+    Selection* selection = mTextEditor->GetSelection();
+    if (NS_WARN_IF(!selection)) {
+      return NS_ERROR_FAILURE;
+    }
     mCachedSelectionNode = selection->GetAnchorNode();
     mCachedSelectionOffset = selection->AnchorOffset();
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -227,134 +240,162 @@ TextEditRules::AfterEdit(EditAction aAct
   if (mLockRulesSniffing) {
     return NS_OK;
   }
 
   AutoLockRulesSniffing lockIt(this);
 
   MOZ_ASSERT(mActionNesting>0, "bad action nesting!");
   if (!--mActionNesting) {
-    NS_ENSURE_STATE(mTextEditor);
-    RefPtr<Selection> selection = mTextEditor->GetSelection();
-    NS_ENSURE_STATE(selection);
+    if (NS_WARN_IF(!mTextEditor)) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+    Selection* selection = mTextEditor->GetSelection();
+    if (NS_WARN_IF(!selection)) {
+      return NS_ERROR_FAILURE;
+    }
 
-    NS_ENSURE_STATE(mTextEditor);
+    AutoSafeEditorData setData(*this, *mTextEditor, *selection);
+
     nsresult rv =
-      mTextEditor->HandleInlineSpellCheck(aAction, *selection,
-                                          mCachedSelectionNode,
-                                          mCachedSelectionOffset,
-                                          nullptr, 0, nullptr, 0);
-    NS_ENSURE_SUCCESS(rv, rv);
+      TextEditorRef().HandleInlineSpellCheck(aAction, *selection,
+                                             mCachedSelectionNode,
+                                             mCachedSelectionOffset,
+                                             nullptr, 0, nullptr, 0);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
     // no longer uses mCachedSelectionNode, so release it.
     mCachedSelectionNode = nullptr;
 
     // if only trailing <br> remaining remove it
     rv = RemoveRedundantTrailingBR();
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // detect empty doc
-    rv = CreateBogusNodeIfNeeded(selection);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = CreateBogusNodeIfNeeded(&SelectionRef());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
     // ensure trailing br node
     rv = CreateTrailingBRIfNeeded();
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
     // collapse the selection to the trailing BR if it's at the end of our text node
-    CollapseSelectionToTrailingBRIfNeeded(selection);
+    CollapseSelectionToTrailingBRIfNeeded(&SelectionRef());
   }
   return NS_OK;
 }
 
 nsresult
 TextEditRules::WillDoAction(Selection* aSelection,
                             RulesInfo* aInfo,
                             bool* aCancel,
                             bool* aHandled)
 {
-  // null selection is legal
-  MOZ_ASSERT(aInfo && aCancel && aHandled);
+  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  if (NS_WARN_IF(!mTextEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  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:
-      UndefineCaretBidiLevel(aSelection);
-      return WillInsertBreak(aSelection, aCancel, aHandled, aInfo->maxLength);
+      UndefineCaretBidiLevel(&SelectionRef());
+      return WillInsertBreak(&SelectionRef(), aCancel, aHandled,
+                             aInfo->maxLength);
     case EditAction::insertText:
     case EditAction::insertIMEText:
-      UndefineCaretBidiLevel(aSelection);
-      return WillInsertText(aInfo->action, aSelection, aCancel, aHandled,
+      UndefineCaretBidiLevel(&SelectionRef());
+      return WillInsertText(aInfo->action, &SelectionRef(), aCancel, aHandled,
                             aInfo->inString, aInfo->outString,
                             aInfo->maxLength);
     case EditAction::setText:
-      UndefineCaretBidiLevel(aSelection);
-      return WillSetText(*aSelection, aCancel, aHandled, aInfo->inString,
+      UndefineCaretBidiLevel(&SelectionRef());
+      return WillSetText(SelectionRef(), aCancel, aHandled, aInfo->inString,
                          aInfo->maxLength);
     case EditAction::deleteSelection:
-      return WillDeleteSelection(aSelection, aInfo->collapsedAction,
+      return WillDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
                                  aCancel, aHandled);
     case EditAction::undo:
-      return WillUndo(aSelection, aCancel, aHandled);
+      return WillUndo(&SelectionRef(), aCancel, aHandled);
     case EditAction::redo:
-      return WillRedo(aSelection, aCancel, aHandled);
+      return WillRedo(&SelectionRef(), aCancel, aHandled);
     case EditAction::setTextProperty:
-      return WillSetTextProperty(aSelection, aCancel, aHandled);
+      return WillSetTextProperty(&SelectionRef(), aCancel, aHandled);
     case EditAction::removeTextProperty:
-      return WillRemoveTextProperty(aSelection, aCancel, aHandled);
+      return WillRemoveTextProperty(&SelectionRef(), aCancel, aHandled);
     case EditAction::outputText:
-      return WillOutputText(aSelection, aInfo->outputFormat, aInfo->outString,
-                            aInfo->flags, aCancel, aHandled);
+      return WillOutputText(&SelectionRef(), aInfo->outputFormat,
+                            aInfo->outString, aInfo->flags, aCancel, aHandled);
     case EditAction::insertElement:
       // i had thought this would be html rules only.  but we put pre elements
       // into plaintext mail when doing quoting for reply!  doh!
-      WillInsert(*aSelection, aCancel);
+      WillInsert(SelectionRef(), aCancel);
       return NS_OK;
     default:
       return NS_ERROR_FAILURE;
   }
 }
 
 nsresult
 TextEditRules::DidDoAction(Selection* aSelection,
                            RulesInfo* aInfo,
                            nsresult aResult)
 {
-  NS_ENSURE_STATE(mTextEditor);
+  if (NS_WARN_IF(!aSelection) || NS_WARN_IF(!aInfo)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  if (NS_WARN_IF(!mTextEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  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(mTextEditor);
-
-  NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
+  AutoTransactionsConserveSelection dontChangeMySelection(&TextEditorRef());
 
   switch (aInfo->action) {
     case EditAction::insertBreak:
-      return DidInsertBreak(aSelection, aResult);
+      return DidInsertBreak(&SelectionRef(), aResult);
     case EditAction::insertText:
     case EditAction::insertIMEText:
-      return DidInsertText(aSelection, aResult);
+      return DidInsertText(&SelectionRef(), aResult);
     case EditAction::setText:
-      return DidSetText(*aSelection, aResult);
+      return DidSetText(SelectionRef(), aResult);
     case EditAction::deleteSelection:
-      return DidDeleteSelection(aSelection, aInfo->collapsedAction, aResult);
+      return DidDeleteSelection(&SelectionRef(), aInfo->collapsedAction,
+                                aResult);
     case EditAction::undo:
-      return DidUndo(aSelection, aResult);
+      return DidUndo(&SelectionRef(), aResult);
     case EditAction::redo:
-      return DidRedo(aSelection, aResult);
+      return DidRedo(&SelectionRef(), aResult);
     case EditAction::setTextProperty:
-      return DidSetTextProperty(aSelection, aResult);
+      return DidSetTextProperty(&SelectionRef(), aResult);
     case EditAction::removeTextProperty:
-      return DidRemoveTextProperty(aSelection, aResult);
+      return DidRemoveTextProperty(&SelectionRef(), aResult);
     case EditAction::outputText:
-      return DidOutputText(aSelection, aResult);
+      return DidOutputText(&SelectionRef(), aResult);
     default:
       // Don't fail on transactions we don't handle here!
       return NS_OK;
   }
 }
 
 /**
  * Return false if the editor has non-empty text nodes or non-text
@@ -1597,16 +1638,27 @@ TextEditRules::RemoveIMETextFromPWBuf(ui
   mPasswordIMEText.Assign(*aIMEString);
 }
 
 NS_IMETHODIMP
 TextEditRules::Notify(nsITimer* aTimer)
 {
   MOZ_ASSERT(mTimer);
 
+  if (NS_WARN_IF(!mTextEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  Selection* selection = mTextEditor->GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AutoSafeEditorData setData(*this, *mTextEditor, *selection);
+
   // Check whether our text editor's password flag was changed before this
   // "hide password character" timer actually fires.
   nsresult rv = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
   ASSERT_PASSWORD_LENGTHS_EQUAL();
   mLastLength = 0;
   return rv;
 }
 
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -3,31 +3,33 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_TextEditRules_h
 #define mozilla_TextEditRules_h
 
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorDOMPoint.h"
+#include "mozilla/HTMLEditor.h" // for nsIEditor::AsHTMLEditor()
+#include "mozilla/TextEditor.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIEditor.h"
 #include "nsINamed.h"
 #include "nsISupportsImpl.h"
 #include "nsITimer.h"
 #include "nsString.h"
 #include "nscore.h"
 
 namespace mozilla {
 
 class AutoLockRulesSniffing;
+class HTMLEditor;
 class HTMLEditRules;
 class RulesInfo;
-class TextEditor;
 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:
@@ -276,20 +278,86 @@ protected:
   bool IsSingleLineEditor() const;
   bool IsPlaintextEditor() const;
   bool IsReadonly() const;
   bool IsDisabled() const;
   bool IsMailEditor() const;
   bool DontEchoPassword() const;
 
 private:
-  // Note that we do not refcount the editor.
-  TextEditor* mTextEditor;
+  TextEditor* MOZ_NON_OWNING_REF mTextEditor;
 
 protected:
+  /**
+   * AutoSafeEditorData grabs editor instance and related instances during
+   * handling an edit action.  When this is created, its pointer is set to
+   * the mSafeData, and this guarantees the lifetime of grabbing objects
+   * until it's destroyed.
+   */
+  class MOZ_STACK_CLASS AutoSafeEditorData
+  {
+  public:
+    AutoSafeEditorData(TextEditRules& aTextEditRules,
+                       TextEditor& aTextEditor,
+                       Selection& aSelection)
+      : mTextEditRules(aTextEditRules)
+      , mHTMLEditor(nullptr)
+    {
+      // mTextEditRules may have AutoSafeEditorData instance since in some
+      // cases. E.g., while public methods of *EditRules are called, it
+      // calls attaching editor's method, then, editor will call public
+      // methods of *EditRules again.
+      if (mTextEditRules.mData) {
+        return;
+      }
+      mTextEditor = &aTextEditor;
+      mHTMLEditor = aTextEditor.AsHTMLEditor();
+      mSelection = &aSelection;
+      mTextEditRules.mData = this;
+    }
+
+    ~AutoSafeEditorData()
+    {
+      if (mTextEditRules.mData != this) {
+        return;
+      }
+      mTextEditRules.mData = nullptr;
+    }
+
+    TextEditor& TextEditorRef() const { return *mTextEditor; }
+    HTMLEditor& HTMLEditorRef() const
+    {
+      MOZ_ASSERT(mHTMLEditor);
+      return *mHTMLEditor;
+    }
+    Selection& SelectionRef() const { return *mSelection; }
+
+  private:
+    // This class should be created by public methods TextEditRules and
+    // HTMLEditRules and in the stack.  So, the lifetime of this should
+    // be guaranteed the callers of the public methods.
+    TextEditRules& MOZ_NON_OWNING_REF mTextEditRules;
+    RefPtr<TextEditor> mTextEditor;
+    // Shortcut for HTMLEditorRef().  So, not necessary to use RefPtr.
+    HTMLEditor* MOZ_NON_OWNING_REF mHTMLEditor;
+    RefPtr<Selection> mSelection;
+  };
+  AutoSafeEditorData* mData;
+
+  TextEditor& TextEditorRef() const
+  {
+    MOZ_ASSERT(mData);
+    return mData->TextEditorRef();
+  }
+  Selection& SelectionRef() const
+  {
+    MOZ_ASSERT(mData);
+    return mData->SelectionRef();
+  }
+
   // A buffer we use to store the real value of password editors.
   nsString mPasswordText;
   // A buffer we use to track the IME composition string.
   nsString mPasswordIMEText;
   uint32_t mPasswordIMEIndex;
   // Magic node acts as placeholder in empty doc.
   nsCOMPtr<nsIContent> mBogusNode;
   // Cached selected node.
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1738,18 +1738,22 @@ TextEditor::OutputToString(const nsAStri
 
   nsString resultString;
   RulesInfo ruleInfo(EditAction::outputText);
   ruleInfo.outString = &resultString;
   ruleInfo.flags = aFlags;
   // XXX Struct should store a nsAReadable*
   nsAutoString str(aFormatType);
   ruleInfo.outputFormat = &str;
+  Selection* selection = GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
   bool cancel, handled;
-  nsresult rv = rules->WillDoAction(nullptr, &ruleInfo, &cancel, &handled);
+  nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
   if (handled) {
     // This case will get triggered by password fields.
     aOutputString.Assign(*(ruleInfo.outString));
     return rv;
   }