Bug 1465702 - part 7: Make protected/private methods of EditorBase/TextEditor/HTMLEditor use SelectionRefPtr() instead of GetSelection() r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 30 Oct 2018 10:04:08 +0000
changeset 502710 bda6c79cf03c921d16e4944fc200a424b4af78b0
parent 502709 258672244b3e879b464788d7e054e9f72bd67655
child 502711 1304f235bc675f518b6db59ebc47f959b6f59cce
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1465702
milestone65.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 1465702 - part 7: Make protected/private methods of EditorBase/TextEditor/HTMLEditor use SelectionRefPtr() instead of GetSelection() r=m_kato Now, any protected/private methods of editor classes can refer SelectionRefPtr() safely. So, we can cut the cost of calling GetSelection() only once per edit action handling. Differential Revision: https://phabricator.services.mozilla.com/D10011
editor/libeditor/EditorBase.cpp
editor/libeditor/HTMLAbsPositionEditor.cpp
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLStyleEditor.cpp
editor/libeditor/HTMLTableEditor.cpp
editor/libeditor/TextEditor.cpp
editor/libeditor/TextEditorDataTransfer.cpp
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -934,70 +934,65 @@ EditorBase::EndTransactionInternal()
   }
 
   EndUpdateViewBatch();
 }
 
 void
 EditorBase::BeginPlaceholderTransaction(nsAtom* aTransactionName)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!");
+
   if (!mPlaceholderBatch) {
     NotifyEditorObservers(eNotifyEditorObserversOfBefore);
     // time to turn on the batch
     BeginUpdateViewBatch();
     mPlaceholderTransaction = nullptr;
     mPlaceholderName = aTransactionName;
-    RefPtr<Selection> selection = GetSelection();
-    if (selection) {
-      mSelState.emplace();
-      mSelState->SaveSelection(selection);
-      // Composition transaction can modify multiple nodes and it merges text
-      // node for ime into single text node.
-      // So if current selection is into IME text node, it might be failed
-      // to restore selection by UndoTransaction.
-      // So we need update selection by range updater.
-      if (mPlaceholderName == nsGkAtoms::IMETxnName) {
-        mRangeUpdater.RegisterSelectionState(*mSelState);
-      }
+    mSelState.emplace();
+    mSelState->SaveSelection(SelectionRefPtr());
+    // Composition transaction can modify multiple nodes and it merges text
+    // node for ime into single text node.
+    // So if current selection is into IME text node, it might be failed
+    // to restore selection by UndoTransaction.
+    // So we need update selection by range updater.
+    if (mPlaceholderName == nsGkAtoms::IMETxnName) {
+      mRangeUpdater.RegisterSelectionState(*mSelState);
     }
   }
   mPlaceholderBatch++;
 }
 
 void
 EditorBase::EndPlaceholderTransaction()
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(mPlaceholderBatch > 0,
              "zero or negative placeholder batch count when ending batch!");
+
   if (mPlaceholderBatch == 1) {
-    RefPtr<Selection> selection = GetSelection();
-
     // By making the assumption that no reflow happens during the calls
     // to EndUpdateViewBatch and ScrollSelectionIntoView, we are able to
     // allow the selection to cache a frame offset which is used by the
     // caret drawing code. We only enable this cache here; at other times,
     // we have no way to know whether reflow invalidates it
     // See bugs 35296 and 199412.
-    if (selection) {
-      selection->SetCanCacheFrameOffset(true);
-    }
+    SelectionRefPtr()->SetCanCacheFrameOffset(true);
 
     // time to turn off the batch
     EndUpdateViewBatch();
     // make sure selection is in view
 
     // After ScrollSelectionIntoView(), the pending notifications might be
     // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
     ScrollSelectionIntoView(false);
 
     // cached for frame offset are Not available now
-    if (selection) {
-      selection->SetCanCacheFrameOffset(false);
-    }
+    SelectionRefPtr()->SetCanCacheFrameOffset(false);
 
     if (mSelState) {
       // we saved the selection state, but never got to hand it to placeholder
       // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
       if (mPlaceholderName == nsGkAtoms::IMETxnName) {
         mRangeUpdater.DropSelectionState(*mSelState);
       }
       mSelState.reset();
@@ -3251,16 +3246,18 @@ EditorBase::DoSplitNode(const EditorDOMP
   // in any case.
 }
 
 nsresult
 EditorBase::DoJoinNodes(nsINode* aNodeToKeep,
                         nsINode* aNodeToJoin,
                         nsINode* aParent)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   MOZ_ASSERT(aNodeToKeep);
   MOZ_ASSERT(aNodeToJoin);
   MOZ_ASSERT(aParent);
 
   uint32_t firstNodeLength = aNodeToJoin->Length();
 
   int32_t joinOffset;
   GetNodeLocation(aNodeToJoin, &joinOffset);
@@ -3394,21 +3391,21 @@ EditorBase::DoJoinNodes(nsINode* aNodeTo
     range.mSelection->AddRange(*newRange, err);
     if (NS_WARN_IF(err.Failed())) {
       return err.StealNSResult();
     }
   }
 
   if (allowedTransactionsToChangeSelection) {
     // Editor wants us to set selection at join point.
-    RefPtr<Selection> selection = GetSelection();
-    if (NS_WARN_IF(!selection)) {
-      return NS_ERROR_FAILURE;
-    }
-    selection->Collapse(aNodeToKeep, AssertedCast<int32_t>(firstNodeLength));
+    DebugOnly<nsresult> rv =
+      SelectionRefPtr()->Collapse(aNodeToKeep,
+                                  AssertedCast<int32_t>(firstNodeLength));
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+      "Failed to collapse Selection at end of the node");
   }
 
   return err.StealNSResult();
 }
 
 // static
 int32_t
 EditorBase::GetChildOffset(nsINode* aChild,
@@ -4158,64 +4155,56 @@ EditorBase::JoinNodesDeepWithTransaction
   }
 
   return ret;
 }
 
 void
 EditorBase::BeginUpdateViewBatch()
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(mUpdateCount >= 0, "bad state");
 
   if (!mUpdateCount) {
     // Turn off selection updates and notifications.
-    RefPtr<Selection> selection = GetSelection();
-    if (selection) {
-      selection->StartBatchChanges();
-    }
+    SelectionRefPtr()->StartBatchChanges();
   }
 
   mUpdateCount++;
 }
 
 void
 EditorBase::EndUpdateViewBatch()
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
   MOZ_ASSERT(mUpdateCount > 0, "bad state");
 
   if (mUpdateCount <= 0) {
     mUpdateCount = 0;
     return;
   }
 
   if (--mUpdateCount) {
     return;
   }
 
   // Turn selection updating and notifications back on.
-  RefPtr<Selection> selection = GetSelection();
-  if (selection) {
-    selection->EndBatchChanges();
-  }
+  SelectionRefPtr()->EndBatchChanges();
 
   HTMLEditor* htmlEditor = AsHTMLEditor();
   if (!htmlEditor) {
     return;
   }
 
   // We may need to show resizing handles or update existing ones after
   // all transactions are done. This way of doing is preferred to DOM
   // mutation events listeners because all the changes the user can apply
   // to a document may result in multiple events, some of them quite hard
   // to listen too (in particular when an ancestor of the selection is
   // changed but the selection itself is not changed).
-  if (NS_WARN_IF(!selection)) {
-    return;
-  }
-
   DebugOnly<nsresult> rv = htmlEditor->RefereshEditingUI();
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RefereshEditingUI() failed");
 }
 
 TextComposition*
 EditorBase::GetComposition() const
 {
   return mComposition;
@@ -4273,32 +4262,31 @@ EditorBase::DoAfterRedoTransaction()
 }
 
 already_AddRefed<EditAggregateTransaction>
 EditorBase::CreateTxnForDeleteSelection(EDirection aAction,
                                         nsINode** aRemovingNode,
                                         int32_t* aOffset,
                                         int32_t* aLength)
 {
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return nullptr;
-  }
+  MOZ_ASSERT(IsEditActionDataAvailable());
 
   // Check whether the selection is collapsed and we should do nothing:
-  if (NS_WARN_IF(selection->IsCollapsed() && aAction == eNone)) {
+  if (NS_WARN_IF(SelectionRefPtr()->IsCollapsed() && aAction == eNone)) {
     return nullptr;
   }
 
   // allocate the out-param transaction
   RefPtr<EditAggregateTransaction> aggregateTransaction =
     EditAggregateTransaction::Create();
 
-  for (uint32_t rangeIdx = 0; rangeIdx < selection->RangeCount(); ++rangeIdx) {
-    RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
+  for (uint32_t rangeIdx = 0;
+       rangeIdx < SelectionRefPtr()->RangeCount();
+       ++rangeIdx) {
+    RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(rangeIdx);
     if (NS_WARN_IF(!range)) {
       return nullptr;
     }
 
     // Same with range as with selection; if it is collapsed and action
     // is eNone, do nothing.
     if (!range->Collapsed()) {
       RefPtr<DeleteRangeTransaction> deleteRangeTransaction =
@@ -4525,43 +4513,54 @@ EditorBase::CreateRange(nsINode* aStartC
 {
   return nsRange::CreateRange(aStartContainer, aStartOffset,
                               aEndContainer, aEndOffset, aRange);
 }
 
 nsresult
 EditorBase::AppendNodeToSelectionAsRange(nsINode* aNode)
 {
-  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  if (NS_WARN_IF(!aNode)) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   nsCOMPtr<nsINode> parentNode = aNode->GetParentNode();
-  NS_ENSURE_TRUE(parentNode, NS_ERROR_NULL_POINTER);
+  if (NS_WARN_IF(!parentNode)) {
+    return NS_ERROR_FAILURE;
+  }
 
   int32_t offset = GetChildOffset(aNode, parentNode);
 
   RefPtr<nsRange> range;
   nsresult rv = CreateRange(parentNode, offset, parentNode, offset + 1,
                             getter_AddRefs(range));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (NS_WARN_IF(!range)) {
+    return NS_ERROR_FAILURE;
+  }
 
   ErrorResult err;
-  selection->AddRange(*range, err);
+  SelectionRefPtr()->AddRange(*range, err);
+  NS_WARNING_ASSERTION(!err.Failed(), "Failed to add range to Selection");
   return err.StealNSResult();
 }
 
 nsresult
 EditorBase::ClearSelection()
 {
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   ErrorResult rv;
-  selection->RemoveAllRanges(rv);
+  SelectionRefPtr()->RemoveAllRanges(rv);
+  NS_WARNING_ASSERTION(!rv.Failed(),
+                       "Failed to remove all ranges from Selection");
   return rv.StealNSResult();
 }
 
 already_AddRefed<Element>
 EditorBase::CreateHTMLContent(const nsAtom* aTag)
 {
   MOZ_ASSERT(aTag);
 
@@ -4703,40 +4702,45 @@ EditorBase::InitializeSelectionAncestorL
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   SelectionRefPtr()->SetAncestorLimiter(&aAncestorLimit);
 }
 
 nsresult
 EditorBase::InitializeSelection(EventTarget* aFocusEventTarget)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   nsCOMPtr<nsINode> targetNode = do_QueryInterface(aFocusEventTarget);
-  NS_ENSURE_TRUE(targetNode, NS_ERROR_INVALID_ARG);
+  if (NS_WARN_IF(!targetNode)) {
+    return NS_ERROR_INVALID_ARG;
+  }
   nsCOMPtr<nsIContent> selectionRootContent = FindSelectionRoot(targetNode);
   if (!selectionRootContent) {
     return NS_OK;
   }
 
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_STATE(selection);
-
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
-  NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
+  if (NS_WARN_IF(!presShell)) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
 
   nsCOMPtr<nsISelectionController> selectionController =
     GetSelectionController();
   if (NS_WARN_IF(!selectionController)) {
     return NS_ERROR_FAILURE;
   }
 
   // Init the caret
   RefPtr<nsCaret> caret = presShell->GetCaret();
-  NS_ENSURE_TRUE(caret, NS_ERROR_UNEXPECTED);
+  if (NS_WARN_IF(!caret)) {
+    return NS_ERROR_FAILURE;
+  }
   caret->SetIgnoreUserModify(false);
-  caret->SetSelection(selection);
+  caret->SetSelection(SelectionRefPtr());
   selectionController->SetCaretReadOnly(IsReadonly());
   selectionController->SetCaretEnabled(true);
 
   // Init selection
   selectionController->SetDisplaySelection(
                          nsISelectionController::SELECTION_ON);
   selectionController->SetSelectionFlags(
                          nsISelectionDisplay::DISPLAY_ALL);
@@ -4746,26 +4750,26 @@ EditorBase::InitializeSelection(EventTar
   // If the computed selection root isn't root content, we should set it
   // as selection ancestor limit.  However, if that is root element, it means
   // there is not limitation of the selection, then, we must set nullptr.
   // NOTE: If we set a root element to the ancestor limit, some selection
   // methods don't work fine.
   if (selectionRootContent->GetParent()) {
     InitializeSelectionAncestorLimit(*selectionRootContent);
   } else {
-    selection->SetAncestorLimiter(nullptr);
+    SelectionRefPtr()->SetAncestorLimiter(nullptr);
   }
 
   // If there is composition when this is called, we may need to restore IME
   // selection because if the editor is reframed, this already forgot IME
   // selection and the transaction.
   if (mComposition && mComposition->IsMovingToNewTextNode()) {
     // We need to look for the new text node from current selection.
     // XXX If selection is changed during reframe, this doesn't work well!
-    nsRange* firstRange = selection->GetRangeAt(0);
+    nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
     }
     EditorRawDOMPoint atStartOfFirstRange(firstRange->StartRef());
     EditorRawDOMPoint betterInsertionPoint =
       FindBetterInsertionPoint(atStartOfFirstRange);
     Text* textNode = betterInsertionPoint.GetContainerAsText();
     MOZ_ASSERT(textNode,
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -559,36 +559,34 @@ HTMLEditor::SetPositionToAbsoluteOrStati
   }
 
   return SetPositionToStatic(aElement);
 }
 
 nsresult
 HTMLEditor::SetPositionToAbsolute(Element& aElement)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   int32_t x, y;
   GetElementOrigin(aElement, x, y);
 
   mCSSEditUtils->SetCSSProperty(aElement, *nsGkAtoms::position,
                                 NS_LITERAL_STRING("absolute"));
 
   AddPositioningOffset(x, y);
   SnapToGrid(x, y);
   SetTopAndLeft(aElement, x, y);
 
   // we may need to create a br if the positioned element is alone in its
   // container
   nsINode* parentNode = aElement.GetParentNode();
   if (parentNode->GetChildCount() == 1) {
-    RefPtr<Selection> selection = GetSelection();
-    if (NS_WARN_IF(!selection)) {
-      return NS_ERROR_FAILURE;
-    }
     RefPtr<Element> newBrElement =
       InsertBrElementWithTransaction(EditorRawDOMPoint(parentNode, 0));
     if (NS_WARN_IF(!newBrElement)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -656,39 +656,31 @@ HTMLEditor::InitializeSelectionAncestorL
     MaybeCollapseSelectionAtFirstEditableNode(true);
   }
 }
 
 nsresult
 HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
               bool aIgnoreIfSelectionInEditingHost)
 {
-  // XXX Why doesn't this check if the document is alive?
-  if (!IsInitialized()) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
+  MOZ_ASSERT(IsEditActionDataAvailable());
 
   // Use editing host.  If you use root element here, selection may be
   // moved to <head> element, e.g., if there is a text node in <script>
   // element.  So, we should use active editing host.
   RefPtr<Element> editingHost = GetActiveEditingHost();
   if (NS_WARN_IF(!editingHost)) {
     return NS_OK;
   }
 
   // If selection range is already in the editing host and the range is not
   // start of the editing host, we shouldn't reset selection.  E.g., window
   // is activated when the editor had focus before inactivated.
-  if (aIgnoreIfSelectionInEditingHost && selection->RangeCount() == 1) {
-    nsRange* range = selection->GetRangeAt(0);
+  if (aIgnoreIfSelectionInEditingHost && SelectionRefPtr()->RangeCount() == 1) {
+    nsRange* range = SelectionRefPtr()->GetRangeAt(0);
     if (!range->Collapsed() ||
         range->GetStartContainer() != editingHost.get() ||
         range->StartOffset()) {
       return NS_OK;
     }
   }
 
   // Find first editable and visible node.
@@ -770,17 +762,21 @@ HTMLEditor::MaybeCollapseSelectionAtFirs
       pointToPutCaret.Set(visNode);
       DebugOnly<bool> advanced = pointToPutCaret.AdvanceOffset();
       NS_WARNING_ASSERTION(advanced,
         "Failed to advance offset from the found empty block node");
     } else {
       pointToPutCaret.Set(visNode, 0);
     }
   }
-  return selection->Collapse(pointToPutCaret);
+  nsresult rv = SelectionRefPtr()->Collapse(pointToPutCaret);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
 }
 
 nsresult
 HTMLEditor::HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent)
 {
   // NOTE: When you change this method, you should also change:
   //   * editor/libeditor/tests/test_htmleditor_keyevent_handling.html
 
@@ -821,17 +817,20 @@ HTMLEditor::HandleKeyPressEvent(WidgetKe
       }
 
       if (aKeyboardEvent->IsControl() || aKeyboardEvent->IsAlt() ||
           aKeyboardEvent->IsMeta() || aKeyboardEvent->IsOS()) {
         return NS_OK;
       }
 
       RefPtr<Selection> selection = GetSelection();
-      NS_ENSURE_TRUE(selection && selection->RangeCount(), NS_ERROR_FAILURE);
+      if (NS_WARN_IF(!selection) ||
+          NS_WARN_IF(!selection->RangeCount())) {
+        return NS_ERROR_FAILURE;
+      }
 
       nsCOMPtr<nsINode> node = selection->GetRangeAt(0)->GetStartContainer();
       MOZ_ASSERT(node);
 
       nsCOMPtr<Element> blockParent = GetBlock(*node);
 
       if (!blockParent) {
         break;
@@ -1236,35 +1235,35 @@ HTMLEditor::TabInTable(bool inIsShift,
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::InsertBrElementAtSelectionWithTransaction()
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   // calling it text insertion to trigger moz br treatment by rules
   // XXX Why do we use EditSubAction::eInsertText here?  Looks like
   //     EditSubAction::eInsertParagraphSeparator or EditSubAction::eInsertNode
   //     is better.
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eInsertText,
                                       nsIEditor::eNext);
 
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_STATE(selection);
-
-  if (!selection->IsCollapsed()) {
+  if (!SelectionRefPtr()->IsCollapsed()) {
     nsresult rv = DeleteSelectionAsSubAction(eNone, eStrip);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  EditorRawDOMPoint atStartOfSelection(EditorBase::GetStartPoint(*selection));
+  EditorRawDOMPoint atStartOfSelection(
+                      EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
   // InsertBrElementWithTransaction() will set selection after the new <br>
   // element.
   RefPtr<Element> newBrElement =
     InsertBrElementWithTransaction(atStartOfSelection, eNext);
@@ -1295,23 +1294,23 @@ HTMLEditor::CollapseSelectionToDeepestNo
 
   SelectionRefPtr()->Collapse(node, 0);
 }
 
 nsresult
 HTMLEditor::ReplaceHeadContentsWithSourceWithTransaction(
               const nsAString& aSourceToInsert)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   // don't do any post processing, rules get confused
   AutoTopLevelEditSubActionNotifier
     maybeTopLevelEditSubAction(*this,
                                EditSubAction::eReplaceHeadWithHTMLSource,
                                nsIEditor::eNone);
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   CommitComposition();
 
   // 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();
@@ -1341,17 +1340,17 @@ HTMLEditor::ReplaceHeadContentsWithSourc
 
   // Mac linebreaks: Map any remaining CR to LF:
   inputString.ReplaceSubstring(NS_LITERAL_STRING("\r"),
                                NS_LITERAL_STRING("\n"));
 
   AutoPlaceholderBatch treatAsOneTransaction(*this);
 
   // Get the first range in the selection, for context:
-  RefPtr<nsRange> range = selection->GetRangeAt(0);
+  RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(0);
   if (NS_WARN_IF(!range)) {
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult err;
   RefPtr<DocumentFragment> documentFragment =
     range->CreateContextualFragment(inputString, err);
 
@@ -2002,48 +2001,59 @@ HTMLEditor::GetHighlightColorState(bool*
   return GetCSSBackgroundColorState(aMixed, aOutColor, false);
 }
 
 nsresult
 HTMLEditor::GetCSSBackgroundColorState(bool* aMixed,
                                        nsAString& aOutColor,
                                        bool aBlockLevel)
 {
-  NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  if (NS_WARN_IF(!aMixed)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   *aMixed = false;
   // the default background color is transparent
   aOutColor.AssignLiteral("transparent");
 
-  // get selection
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_STATE(selection && selection->GetRangeAt(0));
-
-  // get selection location
-  nsCOMPtr<nsINode> parent = selection->GetRangeAt(0)->GetStartContainer();
-  NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
+  RefPtr<nsRange> firstRange = SelectionRefPtr()->GetRangeAt(0);
+  if (NS_WARN_IF(!firstRange)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsINode> parent = firstRange->GetStartContainer();
+  if (NS_WARN_IF(!parent)) {
+    return NS_ERROR_FAILURE;
+  }
 
   // is the selection collapsed?
   nsCOMPtr<nsINode> nodeToExamine;
-  if (selection->IsCollapsed() || IsTextNode(parent)) {
+  if (SelectionRefPtr()->IsCollapsed() || IsTextNode(parent)) {
     // we want to look at the parent and ancestors
     nodeToExamine = parent;
   } else {
     // otherwise we want to look at the first editable node after
     // {parent,offset} and its ancestors for divs with alignment on them
-    nodeToExamine = selection->GetRangeAt(0)->GetChildAtStartOffset();
+    nodeToExamine = firstRange->GetChildAtStartOffset();
     //GetNextNode(parent, offset, true, address_of(nodeToExamine));
   }
 
-  NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
+  if (NS_WARN_IF(!nodeToExamine)) {
+    return NS_ERROR_FAILURE;
+  }
 
   if (aBlockLevel) {
     // we are querying the block background (and not the text background), let's
     // climb to the block container
     nsCOMPtr<Element> blockParent = GetBlock(*nodeToExamine);
-    NS_ENSURE_TRUE(blockParent, NS_OK);
+    if (NS_WARN_IF(!blockParent)) {
+      return NS_OK;
+    }
 
     // Make sure to not walk off onto the Document node
     do {
       // retrieve the computed style of background-color for blockParent
       CSSEditUtils::GetComputedProperty(*blockParent,
                                         *nsGkAtoms::backgroundColor,
                                         aOutColor);
       blockParent = blockParent->GetParentElement();
@@ -2088,26 +2098,26 @@ HTMLEditor::GetCSSBackgroundColorState(b
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::GetHTMLBackgroundColorState(bool* aMixed,
                                         nsAString& aOutColor)
 {
-  //TODO: We don't handle "mixed" correctly!
-  NS_ENSURE_TRUE(aMixed, NS_ERROR_NULL_POINTER);
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  // TODO: We don't handle "mixed" correctly!
+  if (NS_WARN_IF(!aMixed)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   *aMixed = false;
   aOutColor.Truncate();
 
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_FAILURE;
-  }
-
   ErrorResult error;
   RefPtr<Element> cellOrRowOrTableElement =
     GetSelectedOrParentTableElement(error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
   for (RefPtr<Element> element = std::move(cellOrRowOrTableElement);
@@ -2127,18 +2137,20 @@ HTMLEditor::GetHTMLBackgroundColorState(
     }
 
     // No color is set, but we need to report visible color inherited
     // from nested cells/tables, so search up parent chain so that
     // let's keep checking the ancestors.
   }
 
   // If no table or cell found, get page body
-  dom::Element* bodyElement = GetRoot();
-  NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER);
+  Element* bodyElement = GetRoot();
+  if (NS_WARN_IF(!bodyElement)) {
+    return NS_ERROR_FAILURE;
+  }
 
   bodyElement->GetAttr(kNameSpaceID_None, nsGkAtoms::bgcolor, aOutColor);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetListState(bool* aMixed,
                          bool* aOL,
@@ -3121,22 +3133,17 @@ HTMLEditor::InsertLinkAroundSelection(El
     }
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::SetHTMLBackgroundColorWithTransaction(const nsAString& aColor)
 {
-  MOZ_ASSERT(IsInitialized(), "The HTMLEditor hasn't been initialized yet");
-
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(IsEditActionDataAvailable());
 
   // Find a selected or enclosing table element to set background on
   ErrorResult error;
   bool isCellSelected = false;
   RefPtr<Element> cellOrRowOrTableElement =
     GetSelectedOrParentTableElement(error, &isCellSelected);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
@@ -3476,63 +3483,72 @@ HTMLEditor::GetStyleSheetForURL(const ns
   MOZ_ASSERT(mStyleSheets[foundIndex]);
   return mStyleSheets[foundIndex];
 }
 
 nsresult
 HTMLEditor::DeleteSelectionWithTransaction(EDirection aAction,
                                            EStripWrappers aStripWrappers)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
 
   nsresult rv =
     TextEditor::DeleteSelectionWithTransaction(aAction, aStripWrappers);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // If we weren't asked to strip any wrappers, we're done.
   if (aStripWrappers == eNoStrip) {
     return NS_OK;
   }
 
-  RefPtr<Selection> selection = GetSelection();
   // Just checking that the selection itself is collapsed doesn't seem to work
   // right in the multi-range case
-  NS_ENSURE_STATE(selection);
-  NS_ENSURE_STATE(selection->GetAnchorFocusRange());
-  NS_ENSURE_STATE(selection->GetAnchorFocusRange()->Collapsed());
-
-  NS_ENSURE_STATE(selection->GetAnchorNode()->IsContent());
-  nsCOMPtr<nsIContent> content = selection->GetAnchorNode()->AsContent();
+  if (NS_WARN_IF(!SelectionRefPtr()->GetAnchorFocusRange()) ||
+      NS_WARN_IF(!SelectionRefPtr()->GetAnchorFocusRange()->Collapsed()) ||
+      NS_WARN_IF(!SelectionRefPtr()->GetAnchorNode()->IsContent())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIContent> content =
+    SelectionRefPtr()->GetAnchorNode()->AsContent();
 
   // Don't strip wrappers if this is the only wrapper in the block.  Then we'll
   // add a <br> later, so it won't be an empty wrapper in the end.
   nsCOMPtr<nsIContent> blockParent = content;
   while (blockParent && !IsBlockNode(blockParent)) {
     blockParent = blockParent->GetParent();
   }
   if (!blockParent) {
     return NS_OK;
   }
   bool emptyBlockParent;
   rv = IsEmptyNode(blockParent, &emptyBlockParent);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   if (emptyBlockParent) {
     return NS_OK;
   }
 
   if (content && !IsBlockNode(content) && !content->Length() &&
       content->IsEditable() && content != content->GetEditingHost()) {
     while (content->GetParent() && !IsBlockNode(content->GetParent()) &&
            content->GetParent()->Length() == 1 &&
            content->GetParent()->IsEditable() &&
            content->GetParent() != content->GetEditingHost()) {
       content = content->GetParent();
     }
     rv = DeleteNodeWithTransaction(*content);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::DeleteNodeWithTransaction(nsINode& aNode)
 {
@@ -3841,57 +3857,54 @@ HTMLEditor::SelectEntireDocument()
   }
 
   return EditorBase::SelectEntireDocument();
 }
 
 nsresult
 HTMLEditor::SelectAllInternal()
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   CommitComposition();
   if (NS_WARN_IF(Destroyed())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
   // XXX Perhaps, we should check whether we still have focus since composition
   //     event listener may have already moved focus to different editing
   //     host or other element.  So, perhaps, we need to retrieve anchor node
   //     before committing composition and check if selection is still in
   //     same editing host.
 
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsINode* anchorNode = selection->GetAnchorNode();
+  nsINode* anchorNode = SelectionRefPtr()->GetAnchorNode();
   if (NS_WARN_IF(!anchorNode) || NS_WARN_IF(!anchorNode->IsContent())) {
     return NS_ERROR_FAILURE;
   }
 
   nsIContent* anchorContent = anchorNode->AsContent();
   nsIContent* rootContent;
   if (anchorContent->HasIndependentSelection()) {
-    selection->SetAncestorLimiter(nullptr);
+    SelectionRefPtr()->SetAncestorLimiter(nullptr);
     rootContent = mRootElement;
   } else {
     nsCOMPtr<nsIPresShell> ps = GetPresShell();
     rootContent = anchorContent->GetSelectionRootContent(ps);
   }
 
   if (NS_WARN_IF(!rootContent)) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  Maybe<mozilla::dom::Selection::AutoUserInitiated> userSelection;
+  Maybe<Selection::AutoUserInitiated> userSelection;
   if (!rootContent->IsEditable()) {
-    userSelection.emplace(selection);
+    userSelection.emplace(SelectionRefPtr());
   }
   ErrorResult errorResult;
-  selection->SelectAllChildren(*rootContent, errorResult);
+  SelectionRefPtr()->SelectAllChildren(*rootContent, errorResult);
   NS_WARNING_ASSERTION(!errorResult.Failed(), "SelectAllChildren() failed");
   return errorResult.StealNSResult();
 }
 
 // this will NOT find aAttribute unless aAttribute has a non-null value
 // so singleton attributes like <Table border> will not be matched!
 bool
 HTMLEditor::IsTextPropertySetByContent(nsINode* aNode,
@@ -3930,32 +3943,34 @@ HTMLEditor::IsTextPropertySetByContent(n
     aNode = aNode->GetParentNode();
   }
   return false;
 }
 
 bool
 HTMLEditor::SetCaretInTableCell(Element* aElement)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   if (!aElement || !aElement->IsHTMLElement() ||
       !HTMLEditUtils::IsTableElement(aElement) ||
       !IsDescendantOfEditorRoot(aElement)) {
     return false;
   }
 
   nsIContent* node = aElement;
   while (node->HasChildren()) {
     node = node->GetFirstChild();
   }
 
   // Set selection at beginning of the found node
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, false);
-
-  return NS_SUCCEEDED(selection->Collapse(node, 0));
+  nsresult rv = SelectionRefPtr()->Collapse(node, 0);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                       "Failed to collapse Selection in aElement");
+  return NS_SUCCEEDED(rv);
 }
 
 /**
  * GetEnclosingTable() finds ancestor who is a table, if any.
  */
 Element*
 HTMLEditor::GetEnclosingTable(nsINode* aNode)
 {
@@ -4044,20 +4059,17 @@ HTMLEditor::SetSelectionAtDocumentStart(
 
 /**
  * Remove aNode, reparenting any children into the parent of aNode.  In
  * addition, insert any br's needed to preserve identity of removed block.
  */
 nsresult
 HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement)
 {
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(IsEditActionDataAvailable());
 
   // Two possibilities: the container could be empty of editable content.  If
   // that is the case, we need to compare what is before and after aNode to
   // determine if we need a br.
   //
   // Or it could be not empty, in which case we have to compare previous
   // sibling and first child to determine if we need a leading br, and compare
   // following sibling and last child to determine if we need a trailing br.
@@ -4814,16 +4826,18 @@ HTMLEditor::SetBackgroundColor(const nsA
 }
 
 nsresult
 HTMLEditor::CopyLastEditableChildStylesWithTransaction(
               Element& aPreviousBlock,
               Element& aNewBlock,
               RefPtr<Element>* aNewBrElement)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   if (NS_WARN_IF(!aNewBrElement)) {
     return NS_ERROR_INVALID_ARG;
   }
   *aNewBrElement = nullptr;
 
   RefPtr<Element> previousBlock(&aPreviousBlock);
   RefPtr<Element> newBlock(&aNewBlock);
 
@@ -4901,20 +4915,16 @@ HTMLEditor::CopyLastEditableChildStylesW
   }
 
   if (!firstClonsedElement) {
     // XXX Even if no inline elements are cloned, shouldn't we create new
     //     <br> element for aNewBlock?
     return NS_OK;
   }
 
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_FAILURE;
-  }
   RefPtr<Element> brElement =
     InsertBrElementWithTransaction(EditorRawDOMPoint(firstClonsedElement, 0));
   if (NS_WARN_IF(!brElement)) {
     return NS_ERROR_FAILURE;
   }
   *aNewBrElement = brElement.forget();
   return NS_OK;
 }
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -1034,35 +1034,36 @@ nsresult
 HTMLEditor::GetInlinePropertyBase(nsAtom& aProperty,
                                   nsAtom* aAttribute,
                                   const nsAString* aValue,
                                   bool* aFirst,
                                   bool* aAny,
                                   bool* aAll,
                                   nsAString* outValue)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   *aAny = false;
   *aAll = true;
   *aFirst = false;
   bool first = true;
 
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-
-  bool isCollapsed = selection->IsCollapsed();
-  RefPtr<nsRange> range = selection->GetRangeAt(0);
+  bool isCollapsed = SelectionRefPtr()->IsCollapsed();
+  RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(0);
   // XXX: Should be a while loop, to get each separate range
   // XXX: ERROR_HANDLING can currentItem be null?
   if (range) {
     // For each range, set a flag
     bool firstNodeInRange = true;
 
     if (isCollapsed) {
       nsCOMPtr<nsINode> collapsedNode = range->GetStartContainer();
-      NS_ENSURE_TRUE(collapsedNode, NS_ERROR_FAILURE);
+      if (NS_WARN_IF(!collapsedNode)) {
+        return NS_ERROR_FAILURE;
+      }
       bool isSet, theSetting;
       nsString tOutString;
       if (aAttribute) {
         mTypeInState->GetTypingState(isSet, theSetting, &aProperty, aAttribute,
                                      &tOutString);
         if (outValue) {
           outValue->Assign(tOutString);
         }
@@ -1501,33 +1502,39 @@ HTMLEditor::DecreaseFontSize()
   }
 
   return RelativeFontChange(FontSize::decr);
 }
 
 nsresult
 HTMLEditor::RelativeFontChange(FontSize aDir)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   CommitComposition();
 
-  // Get the selection
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
   // If selection is collapsed, set typing state
-  if (selection->IsCollapsed()) {
+  if (SelectionRefPtr()->IsCollapsed()) {
     nsAtom& atom = aDir == FontSize::incr ? *nsGkAtoms::big :
-                                             *nsGkAtoms::small;
+                                            *nsGkAtoms::small;
 
     // Let's see in what kind of element the selection is
-    NS_ENSURE_TRUE(selection->RangeCount() &&
-                   selection->GetRangeAt(0)->GetStartContainer(), NS_OK);
-    OwningNonNull<nsINode> selectedNode =
-      *selection->GetRangeAt(0)->GetStartContainer();
+    if (NS_WARN_IF(!SelectionRefPtr()->RangeCount())) {
+      return NS_OK;
+    }
+    RefPtr<nsRange> firstRange = SelectionRefPtr()->GetRangeAt(0);
+    if (NS_WARN_IF(!firstRange) ||
+        NS_WARN_IF(!firstRange->GetStartContainer())) {
+      return NS_OK;
+    }
+    OwningNonNull<nsINode> selectedNode = *firstRange->GetStartContainer();
     if (IsTextNode(selectedNode)) {
-      NS_ENSURE_TRUE(selectedNode->GetParentNode(), NS_OK);
+      if (NS_WARN_IF(!selectedNode->GetParentNode())) {
+        return NS_OK;
+      }
       selectedNode = *selectedNode->GetParentNode();
     }
     if (!CanContainTag(selectedNode, atom)) {
       return NS_OK;
     }
 
     // Manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
@@ -1539,30 +1546,34 @@ HTMLEditor::RelativeFontChange(FontSize 
   AutoPlaceholderBatch treatAsOneTransaction(*this);
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
                                       *this, EditSubAction::eSetTextProperty,
                                       nsIEditor::eNext);
   AutoSelectionRestorer restoreSelectionLater(*this);
   AutoTransactionsConserveSelection dontChangeMySelection(*this);
 
   // Loop through the ranges in the selection
-  AutoRangeArray arrayOfRanges(selection);
+  AutoRangeArray arrayOfRanges(SelectionRefPtr());
   for (auto& range : arrayOfRanges.mRanges) {
     // Adjust range to include any ancestors with entirely selected children
     nsresult rv = PromoteInlineRange(*range);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
     // Check for easy case: both range endpoints in same text node
     nsCOMPtr<nsINode> startNode = range->GetStartContainer();
     nsCOMPtr<nsINode> endNode = range->GetEndContainer();
     if (startNode == endNode && IsTextNode(startNode)) {
       rv = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
                                         range->StartOffset(),
                                         range->EndOffset());
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     } else {
       // Not the easy case.  Range not contained in single text node.  There
       // are up to three phases here.  There are all the nodes reported by the
       // subtree iterator to be processed.  And there are potentially a
       // starting textnode and an ending textnode which are only partially
       // contained by the range.
 
       // Let's handle the nodes reported by the iterator.  These nodes are
@@ -1572,43 +1583,51 @@ HTMLEditor::RelativeFontChange(FontSize 
 
       OwningNonNull<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
 
       // Iterate range and build up array
       rv = iter->Init(range);
       if (NS_SUCCEEDED(rv)) {
         nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
         for (; !iter->IsDone(); iter->Next()) {
-          NS_ENSURE_TRUE(iter->GetCurrentNode()->IsContent(), NS_ERROR_FAILURE);
+          if (NS_WARN_IF(!iter->GetCurrentNode()->IsContent())) {
+            return NS_ERROR_FAILURE;
+          }
           OwningNonNull<nsIContent> node = *iter->GetCurrentNode()->AsContent();
 
           if (IsEditable(node)) {
             arrayOfNodes.AppendElement(node);
           }
         }
 
         // Now that we have the list, do the font size change on each node
         for (auto& node : arrayOfNodes) {
           rv = RelativeFontChangeOnNode(aDir == FontSize::incr ? +1 : -1, node);
-          NS_ENSURE_SUCCESS(rv, rv);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+          }
         }
       }
       // Now check the start and end parents of the range to see if they need
       // to be separately handled (they do if they are text nodes, due to how
       // the subtree iterator works - it will not have reported them).
       if (IsTextNode(startNode) && IsEditable(startNode)) {
         rv = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
                                           range->StartOffset(),
                                           startNode->Length());
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
       if (IsTextNode(endNode) && IsEditable(endNode)) {
         rv = RelativeFontChangeOnTextNode(aDir, *endNode->GetAsText(), 0,
                                           range->EndOffset());
-        NS_ENSURE_SUCCESS(rv, rv);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -1396,16 +1396,18 @@ HTMLEditor::DeleteSelectedTableColumnsWi
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditor::DeleteTableColumnWithTransaction(Element& aTableElement,
                                              int32_t aColumnIndex)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   // XXX Why don't this method remove proper <col> (and <colgroup>)?
   ErrorResult error;
   IgnoredErrorResult ignoredError;
   for (int32_t rowIndex = 0;; rowIndex++) {
     CellData cellData(*this, aTableElement, rowIndex, aColumnIndex,
                       ignoredError);
     // Failure means that there is no more row in the table.  In this case,
     // we shouldn't return error since we just reach the end of the table.
@@ -1468,20 +1470,16 @@ HTMLEditor::DeleteTableColumnWithTransac
     // the table instead.
     TableSize tableSize(*this, aTableElement, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
 
     if (tableSize.mRowCount == 1) {
       // We're deleting the last row.  So, let's remove the <table> now.
-      RefPtr<Selection> selection = GetSelection();
-      if (NS_WARN_IF(!selection)) {
-        return NS_ERROR_FAILURE;
-      }
       nsresult rv = DeleteTableElementAndChildrenWithTransaction(aTableElement);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       return NS_OK;
     }
 
     // Delete the row by placing caret in cell we were to delete.  We need
@@ -3953,25 +3951,22 @@ HTMLEditor::CellAndIndexes::Update(HTMLE
 
 void
 HTMLEditor::SetSelectionAfterTableEdit(Element* aTable,
                                        int32_t aRow,
                                        int32_t aCol,
                                        int32_t aDirection,
                                        bool aSelected)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   if (NS_WARN_IF(!aTable) || Destroyed()) {
     return;
   }
 
-  RefPtr<Selection> selection = GetSelection();
-  if (!selection) {
-    return;
-  }
-
   RefPtr<Element> cell;
   bool done = false;
   do {
     cell = GetTableCellElementAt(*aTable, aRow, aCol);
     if (cell) {
       if (aSelected) {
         // Reselect the cell
         DebugOnly<nsresult> rv = SelectContentInternal(*cell);
@@ -4021,17 +4016,19 @@ HTMLEditor::SetSelectionAfterTableEdit(E
 
   // We didn't find a cell
   // Set selection to just before the table
   if (aTable->GetParentNode()) {
     EditorRawDOMPoint atTable(aTable);
     if (NS_WARN_IF(!atTable.IsSetAndValid())) {
       return;
     }
-    selection->Collapse(atTable);
+    DebugOnly<nsresult> rv = SelectionRefPtr()->Collapse(atTable);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                         "Failed to collapse Selection at the table");
     return;
   }
   // Last resort: Set selection to start of doc
   // (it's very bad to not have a valid selection!)
   SetSelectionAtDocumentStart();
 }
 
 NS_IMETHODIMP
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -876,103 +876,97 @@ TextEditor::DeleteSelectionWithTransacti
   }
 
   return rv;
 }
 
 already_AddRefed<Element>
 TextEditor::DeleteSelectionAndCreateElement(nsAtom& aTag)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   nsresult rv = DeleteSelectionAndPrepareToCreateNode();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return nullptr;
-  }
-
-  EditorRawDOMPoint pointToInsert(selection->AnchorRef());
+  EditorRawDOMPoint pointToInsert(SelectionRefPtr()->AnchorRef());
   if (!pointToInsert.IsSet()) {
     return nullptr;
   }
   RefPtr<Element> newElement = CreateNodeWithTransaction(aTag, pointToInsert);
 
   // We want the selection to be just after the new node
   EditorRawDOMPoint afterNewElement(newElement);
   MOZ_ASSERT(afterNewElement.IsSetAndValid());
   DebugOnly<bool> advanced = afterNewElement.AdvanceOffset();
   NS_WARNING_ASSERTION(advanced,
                        "Failed to move offset next to the new element");
   ErrorResult error;
-  selection->Collapse(afterNewElement, error);
+  SelectionRefPtr()->Collapse(afterNewElement, error);
   if (NS_WARN_IF(error.Failed())) {
     // XXX Even if it succeeded to create new element, this returns error
     //     when Selection.Collapse() fails something.  This could occur with
     //     mutation observer or mutation event listener.
     error.SuppressException();
     return nullptr;
   }
   return newElement.forget();
 }
 
 nsresult
 TextEditor::DeleteSelectionAndPrepareToCreateNode()
 {
-  RefPtr<Selection> selection = GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
+  MOZ_ASSERT(IsEditActionDataAvailable());
 
-  if (NS_WARN_IF(!selection->GetAnchorFocusRange())) {
+  if (NS_WARN_IF(!SelectionRefPtr()->GetAnchorFocusRange())) {
     return NS_OK;
   }
 
-  if (!selection->GetAnchorFocusRange()->Collapsed()) {
+  if (!SelectionRefPtr()->GetAnchorFocusRange()->Collapsed()) {
     nsresult rv = DeleteSelectionAsSubAction(eNone, eStrip);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-    MOZ_ASSERT(selection->GetAnchorFocusRange() &&
-               selection->GetAnchorFocusRange()->Collapsed(),
+    MOZ_ASSERT(SelectionRefPtr()->GetAnchorFocusRange() &&
+               SelectionRefPtr()->GetAnchorFocusRange()->Collapsed(),
                "Selection not collapsed after delete");
   }
 
   // If the selection is a chardata node, split it if necessary and compute
   // where to put the new node
-  EditorDOMPoint atAnchor(selection->AnchorRef());
+  EditorDOMPoint atAnchor(SelectionRefPtr()->AnchorRef());
   if (NS_WARN_IF(!atAnchor.IsSet()) || !atAnchor.IsInDataNode()) {
     return NS_OK;
   }
 
   if (NS_WARN_IF(!atAnchor.GetContainer()->GetParentNode())) {
     return NS_ERROR_FAILURE;
   }
 
   if (atAnchor.IsStartOfContainer()) {
     EditorRawDOMPoint atAnchorContainer(atAnchor.GetContainer());
     if (NS_WARN_IF(!atAnchorContainer.IsSetAndValid())) {
       return NS_ERROR_FAILURE;
     }
     ErrorResult error;
-    selection->Collapse(atAnchorContainer, error);
+    SelectionRefPtr()->Collapse(atAnchorContainer, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
     return NS_OK;
   }
 
   if (atAnchor.IsEndOfContainer()) {
     EditorRawDOMPoint afterAnchorContainer(atAnchor.GetContainer());
     if (NS_WARN_IF(!afterAnchorContainer.AdvanceOffset())) {
       return NS_ERROR_FAILURE;
     }
     ErrorResult error;
-    selection->Collapse(afterAnchorContainer, error);
+    SelectionRefPtr()->Collapse(afterAnchorContainer, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
     return NS_OK;
   }
 
   ErrorResult error;
   nsCOMPtr<nsIContent> newLeftNode = SplitNodeWithTransaction(atAnchor, error);
@@ -980,17 +974,17 @@ TextEditor::DeleteSelectionAndPrepareToC
     return error.StealNSResult();
   }
 
   EditorRawDOMPoint atRightNode(atAnchor.GetContainer());
   if (NS_WARN_IF(!atRightNode.IsSet())) {
     return NS_ERROR_FAILURE;
   }
   MOZ_ASSERT(atRightNode.IsSetAndValid());
-  selection->Collapse(atRightNode, error);
+  SelectionRefPtr()->Collapse(atRightNode, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TextEditor::InsertText(const nsAString& aStringToInsert)
@@ -1798,48 +1792,45 @@ TextEditor::Redo(uint32_t aCount)
 
   NotifyEditorObservers(eNotifyEditorObserversOfEnd);
   return rv;
 }
 
 bool
 TextEditor::CanCutOrCopy(PasswordFieldAllowed aPasswordFieldAllowed)
 {
-  RefPtr<Selection> selection = GetSelection();
-  if (!selection) {
-    return false;
-  }
+  MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (aPasswordFieldAllowed == ePasswordFieldNotAllowed &&
       IsPasswordEditor()) {
     return false;
   }
 
-  return !selection->IsCollapsed();
+  return !SelectionRefPtr()->IsCollapsed();
 }
 
 bool
 TextEditor::FireClipboardEvent(EventMessage aEventMessage,
                                int32_t aSelectionType,
                                bool* aActionTaken)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   if (aEventMessage == ePaste) {
     CommitComposition();
   }
 
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
-  NS_ENSURE_TRUE(presShell, false);
-
-  RefPtr<Selection> selection = GetSelection();
-  if (!selection) {
+  if (NS_WARN_IF(!presShell)) {
     return false;
   }
 
   if (!nsCopySupport::FireClipboardEvent(aEventMessage, aSelectionType,
-                                         presShell, selection, aActionTaken)) {
+                                         presShell, SelectionRefPtr(),
+                                         aActionTaken)) {
     return false;
   }
 
   // If the event handler caused the editor to be destroyed, return false.
   // Otherwise return true to indicate that the event was not cancelled.
   return !mDidPreDestroy;
 }
 
@@ -1930,16 +1921,18 @@ TextEditor::CanDelete(bool* aCanDelete)
   return NS_OK;
 }
 
 already_AddRefed<nsIDocumentEncoder>
 TextEditor::GetAndInitDocEncoder(const nsAString& aFormatType,
                                  uint32_t aDocumentEncoderFlags,
                                  const nsACString& aCharset) const
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   nsCOMPtr<nsIDocumentEncoder> docEncoder;
   if (!mCachedDocumentEncoder ||
       !mCachedDocumentEncoderType.Equals(aFormatType)) {
     nsAutoCString formatType(NS_DOC_ENCODER_CONTRACTID_BASE);
     LossyAppendUTF16toASCII(aFormatType, formatType);
     docEncoder = do_CreateInstance(formatType.get());
     if (NS_WARN_IF(!docEncoder)) {
       return nullptr;
@@ -1970,21 +1963,17 @@ TextEditor::GetAndInitDocEncoder(const n
   if (wrapWidth >= 0) {
     Unused << docEncoder->SetWrapColumn(wrapWidth);
   }
 
   // Set the selection, if appropriate.
   // We do this either if the OutputSelectionOnly flag is set,
   // in which case we use our existing selection ...
   if (aDocumentEncoderFlags & nsIDocumentEncoder::OutputSelectionOnly) {
-    RefPtr<Selection> selection = GetSelection();
-    if (NS_WARN_IF(!selection)) {
-      return nullptr;
-    }
-    rv = docEncoder->SetSelection(selection);
+    rv = docEncoder->SetSelection(SelectionRefPtr());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
   }
   // ... or if the root element is not a body,
   // in which case we set the selection to encompass the root.
   else {
     dom::Element* rootElement = GetRoot();
@@ -2174,20 +2163,19 @@ TextEditor::InsertWithQuotationsAsSubAct
   return NS_OK;
 }
 
 nsresult
 TextEditor::SharedOutputString(uint32_t aFlags,
                                bool* aIsCollapsed,
                                nsAString& aResult)
 {
-  RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
+  MOZ_ASSERT(IsEditActionDataAvailable());
 
-  *aIsCollapsed = selection->IsCollapsed();
+  *aIsCollapsed = SelectionRefPtr()->IsCollapsed();
 
   if (!*aIsCollapsed) {
     aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
   }
   // If the selection isn't collapsed, we'll use the whole document.
   return ComputeValueInternal(NS_LITERAL_STRING("text/plain"), aFlags, aResult);
 }
 
--- a/editor/libeditor/TextEditorDataTransfer.cpp
+++ b/editor/libeditor/TextEditorDataTransfer.cpp
@@ -65,35 +65,35 @@ TextEditor::PrepareTransferable(nsITrans
 }
 
 nsresult
 TextEditor::InsertTextAt(const nsAString& aStringToInsert,
                          nsINode* aDestinationNode,
                          int32_t aDestOffset,
                          bool aDoDeleteSelection)
 {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   if (aDestinationNode) {
-    RefPtr<Selection> selection = GetSelection();
-    NS_ENSURE_STATE(selection);
-
     nsCOMPtr<nsINode> targetNode = aDestinationNode;
     int32_t targetOffset = aDestOffset;
 
     if (aDoDeleteSelection) {
       // Use an auto tracker so that our drop point is correctly
       // positioned after the delete.
       AutoTrackDOMPoint tracker(mRangeUpdater, &targetNode, &targetOffset);
       nsresult rv = DeleteSelectionAsSubAction(eNone, eStrip);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     ErrorResult error;
-    selection->Collapse(RawRangeBoundary(targetNode, targetOffset), error);
+    SelectionRefPtr()->Collapse(RawRangeBoundary(targetNode, targetOffset),
+                                error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
   }
 
   nsresult rv = InsertTextAsSubAction(aStringToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;