Bug 1413181 - part 10: Redesign EditorBase::SplitNodeDeep() r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 16 Nov 2017 12:09:57 +0900
changeset 701130 7370f20e5f8cb0070a45baebd4f78510ac361f59
parent 701129 33de454727010774d539c336154fde93b71db39a
child 701131 08afd7e2d25ad9c8374d7f0d3b701ee0e4a856df
push id90079
push usermasayuki@d-toybox.com
push dateTue, 21 Nov 2017 08:41:26 +0000
reviewersm_kato
bugs1413181
milestone59.0a1
Bug 1413181 - part 10: Redesign EditorBase::SplitNodeDeep() r?m_kato First of all, this patches fixes a bug of EditorBase::CreateNode(). It takes |EditorRawDOMPoint&| but it should be |const EditorRawDOMPoint&| for making callers can specify temporary instances. Next, this patch creates |SplitNodeResult| stack class for result of EditorBase::SplitNodeDeep(). SplitNodeDeep() needs to return previous node and next node of split point. They are called as left node and right node, but these calls are really different term usage. Therefore, this patch names: aOutLeftNode -> SplitNodeResult::GetPreviousNode() aOutRightNode -> SplitNodeResult::GetNextNode() and also declares SplitNodeResult::GetLeftNode() and SplitNodeResult::GetRightNode() which are same meaning as left/right node of other splitting methods. Additionally, of course, this patch makes SplitNodeDeep() use |const EditorRawDOMPoint&| to receive the start point of right node. MozReview-Commit-ID: FnJpeKgtzm4
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/EditorUtils.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditorDataTransfer.cpp
editor/libeditor/HTMLStyleEditor.cpp
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -1414,51 +1414,53 @@ EditorBase::SetSpellcheckUserOverride(bo
 {
   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
 
   return SyncRealTimeSpell();
 }
 
 already_AddRefed<Element>
 EditorBase::CreateNode(nsAtom* aTag,
-                       EditorRawDOMPoint& aPointToInsert)
+                       const EditorRawDOMPoint& aPointToInsert)
 {
   MOZ_ASSERT(aTag);
   MOZ_ASSERT(aPointToInsert.IsSetAndValid());
 
+  EditorRawDOMPoint pointToInsert(aPointToInsert);
+
   // XXX We need offset at new node for mRangeUpdater.  Therefore, we need
   //     to compute the offset now but this is expensive.  So, if it's possible,
   //     we need to redesign mRangeUpdater as avoiding using indices.
-  int32_t offset = static_cast<int32_t>(aPointToInsert.Offset());
+  int32_t offset = static_cast<int32_t>(pointToInsert.Offset());
 
   AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
 
   {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillCreateNode(nsDependentAtomString(aTag),
-                               GetAsDOMNode(aPointToInsert.GetChildAtOffset()));
+                               GetAsDOMNode(pointToInsert.GetChildAtOffset()));
     }
   }
 
   nsCOMPtr<Element> ret;
 
   RefPtr<CreateElementTransaction> transaction =
-    CreateTxnForCreateElement(*aTag, aPointToInsert);
+    CreateTxnForCreateElement(*aTag, pointToInsert);
   nsresult rv = DoTransaction(transaction);
   if (NS_SUCCEEDED(rv)) {
     ret = transaction->GetNewNode();
     MOZ_ASSERT(ret);
     // Now, aPointToInsert may be invalid.  I.e., ChildAtOffset() keeps
     // referring the next sibling of new node but Offset() refers the
     // new node.  Let's make refer the new node.
-    aPointToInsert.Set(ret);
-  }
-
-  mRangeUpdater.SelAdjCreateNode(aPointToInsert.Container(), offset);
+    pointToInsert.Set(ret);
+  }
+
+  mRangeUpdater.SelAdjCreateNode(pointToInsert.Container(), offset);
 
   {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->DidCreateNode(nsDependentAtomString(aTag),
                               GetAsDOMNode(ret), rv);
     }
   }
@@ -4037,134 +4039,97 @@ EditorBase::IsPreformatted(nsIDOMNode* a
   }
 
   const nsStyleText* styleText = elementStyle->StyleText();
 
   *aResult = styleText->WhiteSpaceIsSignificant();
   return NS_OK;
 }
 
-
-/**
- * This splits a node "deeply", splitting children as appropriate.  The place
- * to split is represented by a DOM point at {splitPointParent,
- * splitPointOffset}.  That DOM point must be inside aNode, which is the node
- * to split.  We return the offset in the parent of aNode where the split
- * terminates - where you would want to insert a new element, for instance, if
- * that's why you were splitting the node.
- *
- * -1 is returned on failure, in unlikely cases like the selection being
- * unavailable or cloning the node failing.  Make sure not to use the returned
- * offset for anything without checking that it's valid!  If you're not using
- * the offset, it's okay to ignore the return value.
- */
-int32_t
-EditorBase::SplitNodeDeep(nsIContent& aNode,
-                          nsIContent& aSplitPointParent,
-                          int32_t aSplitPointOffset,
-                          SplitAtEdges aSplitAtEdges,
-                          nsIContent** aOutLeftNode,
-                          nsIContent** aOutRightNode,
-                          nsCOMPtr<nsIContent>* ioChildAtSplitPointOffset)
-{
-  MOZ_ASSERT(&aSplitPointParent == &aNode ||
-             EditorUtils::IsDescendantOf(aSplitPointParent, aNode));
-
-  int32_t offset =
-    std::min(std::max(aSplitPointOffset, 0),
-             static_cast<int32_t>(aSplitPointParent.Length()));
-  EditorDOMPoint atStartOfRightNode(&aSplitPointParent, offset);
-  if (NS_WARN_IF(!atStartOfRightNode.IsSet())) {
-    return -1;
-  }
-  MOZ_ASSERT(atStartOfRightNode.IsSetAndValid());
-
-  nsCOMPtr<nsIContent> leftNode, rightNode;
+SplitNodeResult
+EditorBase::SplitNodeDeep(nsIContent& aMostAncestorToSplit,
+                          const EditorRawDOMPoint& aStartOfDeepestRightNode,
+                          SplitAtEdges aSplitAtEdges)
+{
+  MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
+  MOZ_ASSERT(aStartOfDeepestRightNode.Container() == &aMostAncestorToSplit ||
+             EditorUtils::IsDescendantOf(*aStartOfDeepestRightNode.Container(),
+                                         aMostAncestorToSplit));
+
+  if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
+    return SplitNodeResult(NS_ERROR_INVALID_ARG);
+  }
+
+  nsCOMPtr<nsIContent> newLeftNodeOfMostAncestor;
+  EditorDOMPoint atStartOfRightNode(aStartOfDeepestRightNode);
   while (true) {
-    // If we meet an orphan node before meeting aNode, we need to stop
-    // splitting.  This is a bug of the caller.
-    if (NS_WARN_IF(atStartOfRightNode.Container() != &aNode &&
+    // If we meet an orphan node before meeting aMostAncestorToSplit, we need
+    // to stop splitting.  This is a bug of the caller.
+    if (NS_WARN_IF(atStartOfRightNode.Container() != &aMostAncestorToSplit &&
                    !atStartOfRightNode.Container()->GetParent())) {
-      return -1;
+      return SplitNodeResult(NS_ERROR_FAILURE);
     }
 
     // Need to insert rules code call here to do things like not split a list
     // if you are after the last <li> or before the first, etc.  For now we
     // just have some smarts about unneccessarily splitting text nodes, which
     // should be universal enough to put straight in this EditorBase routine.
 
     if (NS_WARN_IF(!atStartOfRightNode.Container()->IsContent())) {
-      return -1;
+      return SplitNodeResult(NS_ERROR_FAILURE);
     }
     nsIContent* currentRightNode = atStartOfRightNode.Container()->AsContent();
 
     // If the split point is middle of the node or the node is not a text node
     // and we're allowed to create empty element node, split it.
     if ((aSplitAtEdges == SplitAtEdges::eAllowToCreateEmptyContainer &&
          !atStartOfRightNode.Container()->GetAsText()) ||
         (!atStartOfRightNode.IsStartOfContainer() &&
          !atStartOfRightNode.IsEndOfContainer())) {
       ErrorResult error;
-      rightNode = currentRightNode;
-      leftNode = SplitNode(atStartOfRightNode.AsRaw(), error);
+      nsCOMPtr<nsIContent> newLeftNode =
+        SplitNode(atStartOfRightNode.AsRaw(), error);
       if (NS_WARN_IF(error.Failed())) {
-        error.SuppressException();
-        return -1;
+        return SplitNodeResult(NS_ERROR_FAILURE);
+      }
+
+      if (currentRightNode == &aMostAncestorToSplit) {
+        // Actually, we split aMostAncestorToSplit.
+        return SplitNodeResult(newLeftNode, &aMostAncestorToSplit);
       }
 
       // Then, try to split its parent before current node.
       atStartOfRightNode.Set(currentRightNode);
     }
     // If the split point is end of the node and it is a text node or we're not
     // allowed to create empty container node, try to split its parent after it.
     else if (!atStartOfRightNode.IsStartOfContainer()) {
-      // XXX Making current node which wasn't split treated as new left node
-      //     here.  However, rightNode still may keep referring a descendant
-      //     of the leftNode, which was split.  This must be odd behavior for
-      //     the callers.
-      //     Perhaps, we should set rightNode to currentRightNode?
-      leftNode = currentRightNode;
+      if (currentRightNode == &aMostAncestorToSplit) {
+        return SplitNodeResult(&aMostAncestorToSplit, nullptr);
+      }
 
       // Try to split its parent after current node.
       atStartOfRightNode.Set(currentRightNode);
       DebugOnly<bool> advanced = atStartOfRightNode.AdvanceOffset();
       NS_WARNING_ASSERTION(advanced,
         "Failed to advance offset after current node");
     }
     // If the split point is start of the node and it is a text node or we're
     // not allowed to create empty container node, try to split its parent.
     else {
-      // XXX Making current node which wasn't split treated as exiting right
-      //     node here.  However, leftNode still may keep referring a
-      //     descendant of rightNode, which was created at splitting.  This
-      //     must be odd behavior for the callers.
-      //     Perhaps, we should set leftNode to nullptr?
-      rightNode = currentRightNode;
+      if (currentRightNode == &aMostAncestorToSplit) {
+        return SplitNodeResult(nullptr, &aMostAncestorToSplit);
+      }
 
       // Try to split its parent before current node.
       atStartOfRightNode.Set(currentRightNode);
     }
-
-    if (currentRightNode == &aNode) {
-      // we split all the way up to (and including) aNode; we're done
-      break;
-    }
-  }
-
-  if (aOutLeftNode) {
-    leftNode.forget(aOutLeftNode);
-  }
-  if (aOutRightNode) {
-    rightNode.forget(aOutRightNode);
-  }
-  if (ioChildAtSplitPointOffset) {
-    *ioChildAtSplitPointOffset = atStartOfRightNode.GetChildAtOffset();
-  }
-
-  return atStartOfRightNode.Offset();
+  }
+
+  return SplitNodeResult(NS_ERROR_FAILURE);
 }
 
 /**
  * This joins two like nodes "deeply", joining children as appropriate.
  * Returns the point of the join, or (nullptr, -1) in case of error.
  */
 EditorDOMPoint
 EditorBase::JoinNodeDeep(nsIContent& aLeftNode,
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -117,16 +117,17 @@ class EditAggregateTransaction;
 class EditTransactionBase;
 class ErrorResult;
 class HTMLEditor;
 class InsertNodeTransaction;
 class InsertTextTransaction;
 class JoinNodeTransaction;
 class PlaceholderTransaction;
 class RemoveStyleSheetTransaction;
+class SplitNodeResult;
 class SplitNodeTransaction;
 class TextComposition;
 class TextEditor;
 
 namespace dom {
 class DataTransfer;
 class Element;
 class EventTarget;
@@ -220,17 +221,16 @@ enum class SplitAtEdges
   // it won't be split.
   eDoNotCreateEmptyContainer,
   // EditorBase::SplitNodeDeep() always splits containers even if the split
   // point is at edge of a container.  E.g., if split point is start of an
   // inline element, empty inline element is created as a new left node.
   eAllowToCreateEmptyContainer
 };
 
-
 /**
  * Implementation of an editor object.  it will be the controller/focal point
  * for the main editor services. i.e. the GUIManager, publishing, transaction
  * manager, event interfaces. the idea for the event interfaces is to have them
  * delegate the actual commands to the editor independent of the XPFE
  * implementation.
  */
 class EditorBase : public nsIEditor
@@ -474,17 +474,17 @@ protected:
    * @param aPointToInsert  The insertion point of new element.  If this refers
    *                        end of the container or after, the transaction
    *                        will append the element to the container.
    *                        Otherwise, will insert the element before the
    *                        child node referred by this.
    * @return                The created new element node.
    */
   already_AddRefed<Element> CreateNode(nsAtom* aTag,
-                                       EditorRawDOMPoint& aPointToInsert);
+                                       const EditorRawDOMPoint& aPointToInsert);
 
   /**
    * Create a transaction for inserting aNode as a child of aParent.
    */
   already_AddRefed<InsertNodeTransaction>
     CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent,
                            int32_t aOffset);
 
@@ -1142,23 +1142,38 @@ public:
   /**
    * When you are using AppendNodeToSelectionAsRange(), call this first to
    * start a new selection.
    */
   nsresult ClearSelection();
 
   nsresult IsPreformatted(nsIDOMNode* aNode, bool* aResult);
 
-  int32_t SplitNodeDeep(nsIContent& aNode, nsIContent& aSplitPointParent,
-                        int32_t aSplitPointOffset,
-                        SplitAtEdges aSplitAtEdges,
-                        nsIContent** outLeftNode = nullptr,
-                        nsIContent** outRightNode = nullptr,
-                        nsCOMPtr<nsIContent>* ioChildAtSplitPointOffset =
-                          nullptr);
+  /**
+   * SplitNodeDeep() splits aMostAncestorToSplit deeply.
+   *
+   * @param aMostAncestorToSplit        The most ancestor node which should be
+   *                                    split.
+   * @param aStartOfDeepestRightNode    The start point of deepest right node.
+   *                                    This point must be descendant of
+   *                                    aMostAncestorToSplit.
+   * @param aSplitAtEdges               Whether the caller allows this to
+   *                                    create empty container element when
+   *                                    split point is start or end of an
+   *                                    element.
+   * @return                            SplitPoint() returns split point in
+   *                                    aMostAncestorToSplit.  The point must
+   *                                    be good to insert something if the
+   *                                    caller want to do it.
+   */
+  SplitNodeResult
+  SplitNodeDeep(nsIContent& aMostAncestorToSplit,
+                const EditorRawDOMPoint& aDeepestStartOfRightNode,
+                SplitAtEdges aSplitAtEdges);
+
   EditorDOMPoint JoinNodeDeep(nsIContent& aLeftNode,
                               nsIContent& aRightNode);
 
   nsresult GetString(const nsAString& name, nsAString& value);
 
   void BeginUpdateViewBatch();
   virtual nsresult EndUpdateViewBatch();
 
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -137,16 +137,124 @@ EditActionHandled(nsresult aRv = NS_OK)
  */
 inline EditActionResult
 EditActionCanceled(nsresult aRv = NS_OK)
 {
   return EditActionResult(aRv, true, true);
 }
 
 /***************************************************************************
+ * SplitNodeResult is a simple class for EditorBase::SplitNodeDeep().
+ * This makes the callers' code easier to read.
+ */
+class MOZ_STACK_CLASS SplitNodeResult final
+{
+public:
+  bool Succeeded() const { return NS_SUCCEEDED(mRv); }
+  bool Failed() const { return NS_FAILED(mRv); }
+  nsresult Rv() const { return mRv; }
+
+  /**
+   * DidSplit() returns true if a node was actually split.
+   */
+  bool DidSplit() const
+  {
+    return mPreviousNode && mNextNode;
+  }
+
+  /**
+   * GetLeftNode() simply returns the left node which was created at splitting.
+   * This returns nullptr if the node wasn't split.
+   */
+  nsIContent* GetLeftNode() const
+  {
+    return mPreviousNode && mNextNode ? mPreviousNode.get() : nullptr;
+  }
+
+  /**
+   * GetRightNode() simply returns the right node which was split.
+   * This won't return nullptr unless failed to split due to invalid arguments.
+   */
+  nsIContent* GetRightNode() const
+  {
+    return mPreviousNode && !mNextNode ? mPreviousNode : mNextNode;
+  }
+
+  /**
+   * GetPreviousNode() returns previous node at the split point.
+   */
+  nsIContent* GetPreviousNode() const { return mPreviousNode; }
+
+  /**
+   * GetNextNode() returns next node at the split point.
+   */
+  nsIContent* GetNextNode() const { return mNextNode; }
+
+  /**
+   * SplitPoint() returns the split point in the container.
+   * This is useful when callers insert an element at split point with
+   * EditorBase::CreateNode() or something similar methods.
+   *
+   * Note that the result is EditorRawDOMPoint but the nodes are grabbed
+   * by this instance.  Therefore, the life time of both container node
+   * and child node are guaranteed while using the result temporarily.
+   */
+  EditorRawDOMPoint SplitPoint() const
+  {
+    if (Failed()) {
+      return EditorRawDOMPoint();
+    }
+    if (!mPreviousNode) {
+      return EditorRawDOMPoint(mNextNode);
+    }
+    EditorRawDOMPoint point(mPreviousNode);
+    DebugOnly<bool> advanced = point.AdvanceOffset();
+    NS_WARNING_ASSERTION(advanced,
+      "Failed to advance offset to after previous node");
+    return point;
+  }
+
+  /**
+   * This constructor shouldn't be used by anybody except methods which
+   * use this as result when it succeeds.
+   *
+   * @param aPreviousNodeOfSplitPoint   Previous node immediately before
+   *                                    split point.
+   * @param aNextNodeOfSplitPoint       Next node immediately after split
+   *                                    point.
+   */
+  SplitNodeResult(nsIContent* aPreviousNodeOfSplitPoint,
+                  nsIContent* aNextNodeOfSplitPoint)
+    : mPreviousNode(aPreviousNodeOfSplitPoint)
+    , mNextNode(aNextNodeOfSplitPoint)
+    , mRv(NS_OK)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mPreviousNode || mNextNode);
+  }
+
+  /**
+   * This constructor shouldn't be used by anybody except methods which
+   * use this as error result when it fails.
+   */
+  explicit SplitNodeResult(nsresult aRv)
+    : mRv(aRv)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv));
+  }
+
+private:
+  nsCOMPtr<nsIContent> mPreviousNode;
+  nsCOMPtr<nsIContent> mNextNode;
+
+  nsresult mRv;
+
+  SplitNodeResult() = delete;
+};
+
+/***************************************************************************
  * stack based helper class for batching a collection of transactions inside a
  * placeholder transaction.
  */
 class MOZ_RAII AutoPlaceholderBatch final
 {
 private:
   RefPtr<EditorBase> mEditorBase;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1851,22 +1851,25 @@ HTMLEditRules::StandardBreakImpl(nsINode
     if (wsType & WSType::block) {
       bBeforeBlock = true;
     }
     nsCOMPtr<nsIDOMNode> linkDOMNode;
     if (htmlEditor->IsInLink(GetAsDOMNode(node), address_of(linkDOMNode))) {
       // Split the link
       nsCOMPtr<Element> linkNode = do_QueryInterface(linkDOMNode);
       NS_ENSURE_STATE(linkNode || !linkDOMNode);
-      nsCOMPtr<nsINode> linkParent = linkNode->GetParentNode();
-      aOffset =
-        htmlEditor->SplitNodeDeep(*linkNode, *node->AsContent(), aOffset,
+      SplitNodeResult splitLinkNodeResult =
+        htmlEditor->SplitNodeDeep(*linkNode, EditorRawDOMPoint(node, aOffset),
                                   SplitAtEdges::eDoNotCreateEmptyContainer);
-      NS_ENSURE_STATE(aOffset != -1);
-      node = linkParent;
+      if (NS_WARN_IF(splitLinkNodeResult.Failed())) {
+        return splitLinkNodeResult.Rv();
+      }
+      EditorRawDOMPoint splitPoint(splitLinkNodeResult.SplitPoint());
+      node = splitPoint.Container();
+      aOffset = splitPoint.Offset();
     }
     brNode = wsObj.InsertBreak(address_of(node), &aOffset, nsIEditor::eNone);
     NS_ENSURE_TRUE(brNode, NS_ERROR_FAILURE);
   }
   node = brNode->GetParentNode();
   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
   if (bAfterBlock && bBeforeBlock) {
     // We just placed a br between block boundaries.  This is the one case
@@ -1962,73 +1965,94 @@ HTMLEditRules::SplitMailCites(Selection*
         NS_ENSURE_STATE(mHTMLEditor);
         selNode = mHTMLEditor->GetNodeLocation(visNode, &selOffset);
         ++selOffset;
       }
     }
 
     NS_ENSURE_STATE(mHTMLEditor);
     NS_ENSURE_STATE(selNode->IsContent());
-    int32_t newOffset =
-      mHTMLEditor->SplitNodeDeep(*citeNode, *selNode->AsContent(), selOffset,
-                                 SplitAtEdges::eDoNotCreateEmptyContainer,
-                                 getter_AddRefs(leftCite),
-                                 getter_AddRefs(rightCite));
-    NS_ENSURE_STATE(newOffset != -1);
-
-    // Add an invisible <br> to the end of the left part if it was a <span> of
-    // style="display: block". This is important, since when serialising the
-    // cite to plain text, the span which caused the visual break is discarded.
-    // So the added <br> will guarantee that the serialiser will insert a
-    // break where the user saw one.
-    if (leftCite &&
-        leftCite->IsHTMLElement(nsGkAtoms::span) &&
-        leftCite->GetPrimaryFrame()->IsFrameOfType(nsIFrame::eBlockFrame)) {
-       nsCOMPtr<nsINode> lastChild = leftCite->GetLastChild();
-       if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
-         // We ignore the result here.
-         nsCOMPtr<Element> invisBR =
-           mHTMLEditor->CreateBR(leftCite, leftCite->Length());
-      }
-    }
-
-    selNode = citeNode->GetParentNode();
+    SplitNodeResult splitCiteNodeResult =
+      mHTMLEditor->SplitNodeDeep(*citeNode,
+                                 EditorRawDOMPoint(selNode, selOffset),
+                                 SplitAtEdges::eDoNotCreateEmptyContainer);
+    if (NS_WARN_IF(splitCiteNodeResult.Failed())) {
+      return splitCiteNodeResult.Rv();
+    }
+
+    // Add an invisible <br> to the end of current cite node (If new left cite
+    // has not been created, we're at the end of it.  Otherwise, we're still at
+    // the right node) if it was a <span> of style="display: block". This is
+    // important, since when serializing the cite to plain text, the span which
+    // caused the visual break is discarded.  So the added <br> will guarantee
+    // that the serializer will insert a break where the user saw one.
+    nsIContent* preveiousNodeOfSplitPoint =
+      splitCiteNodeResult.GetPreviousNode();
+    if (preveiousNodeOfSplitPoint &&
+        preveiousNodeOfSplitPoint->IsHTMLElement(nsGkAtoms::span) &&
+        preveiousNodeOfSplitPoint->GetPrimaryFrame()->
+                                     IsFrameOfType(nsIFrame::eBlockFrame)) {
+      nsCOMPtr<nsINode> lastChild =
+        preveiousNodeOfSplitPoint->GetLastChild();
+      if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
+        // We ignore the result here.
+        nsCOMPtr<Element> invisBR =
+          mHTMLEditor->CreateBR(preveiousNodeOfSplitPoint,
+                                preveiousNodeOfSplitPoint->Length());
+      }
+    }
+
+    // In most cases, <br> should be inserted after current cite.  However, if
+    // left cite hasn't been created because the split point was start of the
+    // cite node, <br> should be inserted before the current cite.
+    EditorRawDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
     NS_ENSURE_STATE(mHTMLEditor);
-    nsCOMPtr<Element> brNode = mHTMLEditor->CreateBR(selNode, newOffset);
+    nsCOMPtr<Element> brNode =
+      mHTMLEditor->CreateBR(pointToInsertBrNode.Container(),
+                            pointToInsertBrNode.Offset());
     NS_ENSURE_STATE(brNode);
-
-    // want selection before the break, and on same line
+    // Now, offset of pointToInsertBrNode is invalid.  Let's clear it.
+    pointToInsertBrNode.Clear();
+
+    // Want selection before the break, and on same line.
+    EditorRawDOMPoint atBrNode(brNode);
     aSelection->SetInterlinePosition(true);
-    rv = aSelection->Collapse(selNode, newOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    ErrorResult error;
+    aSelection->Collapse(atBrNode, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
+    }
+
+    selNode = atBrNode.Container();
+    selOffset = atBrNode.Offset();
 
     // if citeNode wasn't a block, we might also want another break before it.
     // We need to examine the content both before the br we just added and also
     // just after it.  If we don't have another br or block boundary adjacent,
     // then we will need a 2nd br added to achieve blank line that user expects.
     if (IsInlineNode(*citeNode)) {
       NS_ENSURE_STATE(mHTMLEditor);
-      WSRunObject wsObj(mHTMLEditor, selNode, newOffset);
+      WSRunObject wsObj(mHTMLEditor, selNode, selOffset);
       nsCOMPtr<nsINode> visNode;
       int32_t visOffset=0;
       WSType wsType;
-      wsObj.PriorVisibleNode(selNode, newOffset, address_of(visNode),
+      wsObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
                              &visOffset, &wsType);
       if (wsType == WSType::normalWS || wsType == WSType::text ||
           wsType == WSType::special) {
         NS_ENSURE_STATE(mHTMLEditor);
-        WSRunObject wsObjAfterBR(mHTMLEditor, selNode, newOffset+1);
-        wsObjAfterBR.NextVisibleNode(selNode, newOffset + 1,
+        WSRunObject wsObjAfterBR(mHTMLEditor, selNode, selOffset + 1);
+        wsObjAfterBR.NextVisibleNode(selNode, selOffset + 1,
                                      address_of(visNode), &visOffset, &wsType);
         if (wsType == WSType::normalWS || wsType == WSType::text ||
             wsType == WSType::special ||
             // In case we're at the very end.
             wsType == WSType::thisBlock) {
           NS_ENSURE_STATE(mHTMLEditor);
-          brNode = mHTMLEditor->CreateBR(selNode, newOffset);
+          brNode = mHTMLEditor->CreateBR(selNode, selOffset);
           NS_ENSURE_STATE(brNode);
         }
       }
     }
 
     // delete any empty cites
     bool bEmptyCite = false;
     if (leftCite) {
@@ -2041,25 +2065,25 @@ HTMLEditRules::SplitMailCites(Selection*
         NS_ENSURE_STATE(mHTMLEditor);
         rv = mHTMLEditor->DeleteNode(leftCite);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
 
-    if (rightCite) {
+    if (citeNode) {
       NS_ENSURE_STATE(mHTMLEditor);
-      rv = mHTMLEditor->IsEmptyNode(rightCite, &bEmptyCite, true, false);
+      rv = mHTMLEditor->IsEmptyNode(citeNode, &bEmptyCite, true, false);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (bEmptyCite) {
         NS_ENSURE_STATE(mHTMLEditor);
-        rv = mHTMLEditor->DeleteNode(rightCite);
+        rv = mHTMLEditor->DeleteNode(citeNode);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
     *aHandled = true;
   }
   return NS_OK;
@@ -3825,77 +3849,88 @@ HTMLEditRules::MakeBasicBlock(Selection&
       aSelection.GetRangeAt(0)->GetChildAtStartOffset();
     int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
 
     if (&blockType == nsGkAtoms::normal ||
         &blockType == nsGkAtoms::_empty) {
       // We are removing blocks (going to "body text")
       NS_ENSURE_TRUE(htmlEditor->GetBlock(container), NS_ERROR_NULL_POINTER);
       OwningNonNull<Element> curBlock = *htmlEditor->GetBlock(container);
-      if (HTMLEditUtils::IsFormatNode(curBlock)) {
-        // If the first editable node after selection is a br, consume it.
-        // Otherwise it gets pushed into a following block after the split,
-        // which is visually bad.
-        nsCOMPtr<nsIContent> brNode =
-          htmlEditor->GetNextEditableHTMLNode(
-                        EditorRawDOMPoint(container, child, offset));
-        if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
-          rv = htmlEditor->DeleteNode(brNode);
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
-        // Do the splits!
-        offset =
-          htmlEditor->SplitNodeDeep(curBlock, *container->AsContent(), offset,
-                                    SplitAtEdges::eDoNotCreateEmptyContainer);
-        NS_ENSURE_STATE(offset != -1);
-        // Put a br at the split point
-        brNode = htmlEditor->CreateBR(curBlock->GetParentNode(), offset);
-        NS_ENSURE_STATE(brNode);
-        // Put selection at the split point
-        rv = aSelection.Collapse(curBlock->GetParentNode(), offset);
-        // Don't restore the selection
-        selectionRestorer.Abort();
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-      // Else nothing to do!
-    } else {
-      // We are making a block.  Consume a br, if needed.
+      if (!HTMLEditUtils::IsFormatNode(curBlock)) {
+        return NS_OK;
+      }
+
+      // If the first editable node after selection is a br, consume it.
+      // Otherwise it gets pushed into a following block after the split,
+      // which is visually bad.
       nsCOMPtr<nsIContent> brNode =
-        htmlEditor->GetNextEditableHTMLNodeInBlock(
+        htmlEditor->GetNextEditableHTMLNode(
                       EditorRawDOMPoint(container, child, offset));
       if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
         rv = htmlEditor->DeleteNode(brNode);
         NS_ENSURE_SUCCESS(rv, rv);
-        // We don't need to act on this node any more
-        arrayOfNodes.RemoveElement(brNode);
-        // XXX We need to recompute child here because SplitAsNeeded() and
-        //     EditorBase::SplitNodeDeep() don't compute child in some cases.
-        child = container->GetChildAt(offset);
-      }
-      // Make sure we can put a block here
-      rv = SplitAsNeeded(blockType, container, offset, address_of(child));
-      NS_ENSURE_SUCCESS(rv, rv);
-      EditorRawDOMPoint atChild(container, child, offset);
-      RefPtr<Element> block = htmlEditor->CreateNode(&blockType, atChild);
-      NS_ENSURE_STATE(block);
-      // Remember our new block for postprocessing
-      mNewBlock = block;
-      // Delete anything that was in the list of nodes
-      while (!arrayOfNodes.IsEmpty()) {
-        OwningNonNull<nsINode> curNode = arrayOfNodes[0];
-        rv = htmlEditor->DeleteNode(curNode);
-        NS_ENSURE_SUCCESS(rv, rv);
-        arrayOfNodes.RemoveElementAt(0);
-      }
-      // Put selection in new block
-      rv = aSelection.Collapse(block, 0);
+      }
+      // Do the splits!
+      SplitNodeResult splitNodeResult =
+        htmlEditor->SplitNodeDeep(curBlock,
+                                  EditorRawDOMPoint(container, offset),
+                                  SplitAtEdges::eDoNotCreateEmptyContainer);
+      if (NS_WARN_IF(splitNodeResult.Failed())) {
+        return splitNodeResult.Rv();
+      }
+      EditorRawDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
+      // Put a br at the split point
+      brNode = htmlEditor->CreateBR(pointToInsertBrNode.Container(),
+                                    pointToInsertBrNode.Offset());
+      NS_ENSURE_STATE(brNode);
+      // Put selection at the split point
+      EditorRawDOMPoint atBrNode(brNode);
+      ErrorResult error;
+      aSelection.Collapse(atBrNode, error);
       // Don't restore the selection
       selectionRestorer.Abort();
+      if (NS_WARN_IF(error.Failed())) {
+        return error.StealNSResult();
+      }
+      return NS_OK;
+    }
+
+    // We are making a block.  Consume a br, if needed.
+    nsCOMPtr<nsIContent> brNode =
+      htmlEditor->GetNextEditableHTMLNodeInBlock(
+                    EditorRawDOMPoint(container, child, offset));
+    if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
+      rv = htmlEditor->DeleteNode(brNode);
       NS_ENSURE_SUCCESS(rv, rv);
-    }
+      // We don't need to act on this node any more
+      arrayOfNodes.RemoveElement(brNode);
+      // XXX We need to recompute child here because SplitAsNeeded() don't
+      //     compute child in some cases.
+      child = container->GetChildAt(offset);
+    }
+    // Make sure we can put a block here
+    rv = SplitAsNeeded(blockType, container, offset, address_of(child));
+    NS_ENSURE_SUCCESS(rv, rv);
+    EditorRawDOMPoint atChild(container, child, offset);
+    RefPtr<Element> block = htmlEditor->CreateNode(&blockType, atChild);
+    NS_ENSURE_STATE(block);
+    // Remember our new block for postprocessing
+    mNewBlock = block;
+    // Delete anything that was in the list of nodes
+    while (!arrayOfNodes.IsEmpty()) {
+      OwningNonNull<nsINode> curNode = arrayOfNodes[0];
+      rv = htmlEditor->DeleteNode(curNode);
+      NS_ENSURE_SUCCESS(rv, rv);
+      arrayOfNodes.RemoveElementAt(0);
+    }
+    // Put selection in new block
+    rv = aSelection.Collapse(block, 0);
+    // Don't restore the selection
+    selectionRestorer.Abort();
+    NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
   // Okay, now go through all the nodes and make the right kind of blocks, or
   // whatever is approriate.  Woohoo!  Note: blockquote is handled a little
   // differently.
   if (&blockType == nsGkAtoms::blockquote) {
     rv = MakeBlockquote(arrayOfNodes);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -4706,42 +4741,47 @@ HTMLEditRules::SplitBlock(Element& aBloc
                           nsIContent** aOutMiddleNode)
 {
   // aStartChild and aEndChild must be exclusive descendants of aBlock
   MOZ_ASSERT(EditorUtils::IsDescendantOf(aStartChild, aBlock) &&
              EditorUtils::IsDescendantOf(aEndChild, aBlock));
   NS_ENSURE_TRUE_VOID(mHTMLEditor);
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
-  // Get split point location
-  OwningNonNull<nsIContent> startParent = *aStartChild.GetParent();
-  int32_t startOffset = startParent->IndexOf(&aStartChild);
-
-  // Do the splits!
-  nsCOMPtr<nsIContent> newMiddleNode1;
-  htmlEditor->SplitNodeDeep(aBlock, startParent, startOffset,
-                            SplitAtEdges::eDoNotCreateEmptyContainer,
-                            aOutLeftNode, getter_AddRefs(newMiddleNode1));
-
-  // Get split point location
-  OwningNonNull<nsIContent> endParent = *aEndChild.GetParent();
-  // +1 because we want to be after the child
-  int32_t endOffset = 1 + endParent->IndexOf(&aEndChild);
-
-  // Do the splits!
-  nsCOMPtr<nsIContent> newMiddleNode2;
-  htmlEditor->SplitNodeDeep(aBlock, endParent, endOffset,
-                            SplitAtEdges::eDoNotCreateEmptyContainer,
-                            getter_AddRefs(newMiddleNode2), aOutRightNode);
+  // Split at the start.
+  SplitNodeResult splitAtStartResult =
+    htmlEditor->SplitNodeDeep(aBlock, EditorRawDOMPoint(&aStartChild),
+                              SplitAtEdges::eDoNotCreateEmptyContainer);
+  NS_WARNING_ASSERTION(splitAtStartResult.Succeeded(),
+    "Failed to split aBlock at start");
+
+  // Split at after the end
+  EditorRawDOMPoint atAfterEnd(&aEndChild);
+  DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset();
+  NS_WARNING_ASSERTION(advanced,
+    "Failed to advance offset after the end node");
+  SplitNodeResult splitAtEndResult =
+    htmlEditor->SplitNodeDeep(aBlock, atAfterEnd,
+                              SplitAtEdges::eDoNotCreateEmptyContainer);
+  NS_WARNING_ASSERTION(splitAtEndResult.Succeeded(),
+    "Failed to split aBlock at after end");
+
+  if (aOutLeftNode) {
+    NS_IF_ADDREF(*aOutLeftNode = splitAtStartResult.GetPreviousNode());
+  }
+
+  if (aOutRightNode) {
+    NS_IF_ADDREF(*aOutRightNode = splitAtEndResult.GetNextNode());
+  }
 
   if (aOutMiddleNode) {
-    if (newMiddleNode2) {
-      newMiddleNode2.forget(aOutMiddleNode);
+    if (splitAtEndResult.GetPreviousNode()) {
+      NS_IF_ADDREF(*aOutMiddleNode = splitAtEndResult.GetPreviousNode());
     } else {
-      newMiddleNode1.forget(aOutMiddleNode);
+      NS_IF_ADDREF(*aOutMiddleNode = splitAtStartResult.GetNextNode());
     }
   }
 }
 
 nsresult
 HTMLEditRules::OutdentPartOfBlock(Element& aBlock,
                                   nsIContent& aStartChild,
                                   nsIContent& aEndChild,
@@ -4846,23 +4886,29 @@ HTMLEditRules::CreateStyleForInsertText(
   // then process setting any styles
   int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
   item = Move(mHTMLEditor->mTypeInState->TakeSetProperty());
 
   if (item || relFontSize) {
     // we have at least one style to add; make a new text node to insert style
     // nodes above.
     if (RefPtr<Text> text = node->GetAsText()) {
+      if (NS_WARN_IF(!mHTMLEditor)) {
+        return NS_ERROR_FAILURE;
+      }
       // if we are in a text node, split it
-      NS_ENSURE_STATE(mHTMLEditor);
-      offset =
-        mHTMLEditor->SplitNodeDeep(*text, *text, offset,
+      SplitNodeResult splitTextNodeResult =
+        mHTMLEditor->SplitNodeDeep(*text, EditorRawDOMPoint(text, offset),
                                    SplitAtEdges::eAllowToCreateEmptyContainer);
-      NS_ENSURE_STATE(offset != -1);
-      node = node->GetParentNode();
+      if (NS_WARN_IF(splitTextNodeResult.Failed())) {
+        return splitTextNodeResult.Rv();
+      }
+      EditorRawDOMPoint splitPoint(splitTextNodeResult.SplitPoint());
+      node = splitPoint.Container();
+      offset = splitPoint.Offset();
     }
     if (!mHTMLEditor->IsContainer(node)) {
       return NS_OK;
     }
     OwningNonNull<Text> newNode =
       EditorBase::CreateTextNode(aDoc, EmptyString());
     NS_ENSURE_STATE(mHTMLEditor);
     nsresult rv = mHTMLEditor->InsertNode(*newNode, *node, offset);
@@ -6392,51 +6438,70 @@ HTMLEditRules::GetParagraphFormatNodes(
     }
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& item)
 {
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   bool isCollapsed = item.mStartContainer == item.mEndContainer &&
                      item.mStartOffset == item.mEndOffset;
 
   nsCOMPtr<nsIContent> endInline = GetHighestInlineParent(*item.mEndContainer);
-
-  // if we have inline parents above range endpoints, split them
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // XXX Oh, then, if the range is collapsed, we don't need to call
+  //     GetHighestInlineParent(), isn't it?
   if (endInline && !isCollapsed) {
-    nsCOMPtr<nsINode> resultEndNode = endInline->GetParentNode();
-    NS_ENSURE_STATE(mHTMLEditor);
-    // item.mEndContainer must be content if endInline isn't null
-    int32_t resultEndOffset =
-      mHTMLEditor->SplitNodeDeep(*endInline, *item.mEndContainer->AsContent(),
-                                 item.mEndOffset,
-                                 SplitAtEdges::eDoNotCreateEmptyContainer);
-    NS_ENSURE_TRUE(resultEndOffset != -1, NS_ERROR_FAILURE);
-    // reset range
-    item.mEndContainer = resultEndNode;
-    item.mEndOffset = resultEndOffset;
+    RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+    SplitNodeResult splitEndInlineResult =
+      htmlEditor->SplitNodeDeep(*endInline,
+                                EditorRawDOMPoint(item.mEndContainer,
+                                                  item.mEndOffset),
+                                SplitAtEdges::eDoNotCreateEmptyContainer);
+    if (NS_WARN_IF(splitEndInlineResult.Failed())) {
+      return splitEndInlineResult.Rv();
+    }
+    if (NS_WARN_IF(!mHTMLEditor)) {
+      return NS_ERROR_FAILURE;
+    }
+    EditorRawDOMPoint splitPointAtEnd(splitEndInlineResult.SplitPoint());
+    item.mEndContainer = splitPointAtEnd.Container();
+    item.mEndOffset = splitPointAtEnd.Offset();
   }
 
   nsCOMPtr<nsIContent> startInline =
     GetHighestInlineParent(*item.mStartContainer);
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_FAILURE;
+  }
 
   if (startInline) {
-    nsCOMPtr<nsINode> resultStartNode = startInline->GetParentNode();
-    NS_ENSURE_STATE(mHTMLEditor);
-    int32_t resultStartOffset =
-      mHTMLEditor->SplitNodeDeep(*startInline,
-                                 *item.mStartContainer->AsContent(),
-                                 item.mStartOffset,
-                                 SplitAtEdges::eDoNotCreateEmptyContainer);
-    NS_ENSURE_TRUE(resultStartOffset != -1, NS_ERROR_FAILURE);
-    // reset range
-    item.mStartContainer = resultStartNode;
-    item.mStartOffset = resultStartOffset;
+    RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+    SplitNodeResult splitStartInlineResult =
+      htmlEditor->SplitNodeDeep(*startInline,
+                                EditorRawDOMPoint(item.mStartContainer,
+                                                  item.mStartOffset),
+                                SplitAtEdges::eDoNotCreateEmptyContainer);
+    if (NS_WARN_IF(splitStartInlineResult.Failed())) {
+      return splitStartInlineResult.Rv();
+    }
+    if (NS_WARN_IF(!mHTMLEditor)) {
+      return NS_ERROR_FAILURE;
+    }
+    EditorRawDOMPoint splitPointAtStart(splitStartInlineResult.SplitPoint());
+    item.mStartContainer = splitPointAtStart.Container();
+    item.mStartOffset = splitPointAtStart.Offset();
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::BustUpInlinesAtBRs(
                  nsIContent& aNode,
@@ -6452,55 +6517,52 @@ HTMLEditRules::BustUpInlinesAtBRs(
   iter.AppendList(functor, arrayOfBreaks);
 
   // If there aren't any breaks, just put inNode itself in the array
   if (arrayOfBreaks.IsEmpty()) {
     aOutArrayOfNodes.AppendElement(aNode);
     return NS_OK;
   }
 
-  // Else we need to bust up inNode along all the breaks
-  nsCOMPtr<nsINode> inlineParentNode = aNode.GetParentNode();
-  nsCOMPtr<nsIContent> splitDeepNode = &aNode;
-  nsCOMPtr<nsIContent> leftNode, rightNode;
-
-  for (uint32_t i = 0; i < arrayOfBreaks.Length(); i++) {
-    OwningNonNull<Element> breakNode = *arrayOfBreaks[i]->AsElement();
-    NS_ENSURE_TRUE(splitDeepNode, NS_ERROR_NULL_POINTER);
-    NS_ENSURE_TRUE(breakNode->GetParent(), NS_ERROR_NULL_POINTER);
-    OwningNonNull<nsIContent> splitParentNode = *breakNode->GetParent();
-    int32_t splitOffset = splitParentNode->IndexOf(breakNode);
-
-    int32_t resultOffset =
-      htmlEditor->SplitNodeDeep(*splitDeepNode, splitParentNode, splitOffset,
-                                SplitAtEdges::eAllowToCreateEmptyContainer,
-                                getter_AddRefs(leftNode),
-                                getter_AddRefs(rightNode));
-    NS_ENSURE_STATE(resultOffset != -1);
-
-    // Put left node in node list
-    if (leftNode) {
+  // Else we need to bust up aNode along all the breaks
+  nsCOMPtr<nsIContent> nextNode = &aNode;
+  for (OwningNonNull<nsINode>& brNode : arrayOfBreaks) {
+    EditorRawDOMPoint atBrNode(brNode);
+    if (NS_WARN_IF(!atBrNode.IsSet())) {
+      return NS_ERROR_FAILURE;
+    }
+    SplitNodeResult splitNodeResult =
+      htmlEditor->SplitNodeDeep(*nextNode, atBrNode,
+                                SplitAtEdges::eAllowToCreateEmptyContainer);
+    if (NS_WARN_IF(splitNodeResult.Failed())) {
+      return splitNodeResult.Rv();
+    }
+
+    // Put previous node at the split point.
+    if (splitNodeResult.GetPreviousNode()) {
       // Might not be a left node.  A break might have been at the very
       // beginning of inline container, in which case SplitNodeDeep would not
       // actually split anything
-      aOutArrayOfNodes.AppendElement(*leftNode);
-    }
+      aOutArrayOfNodes.AppendElement(*splitNodeResult.GetPreviousNode());
+    }
+
     // Move break outside of container and also put in node list
+    EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
     nsresult rv =
-      htmlEditor->MoveNode(breakNode, inlineParentNode, resultOffset);
+      htmlEditor->MoveNode(brNode->AsContent(), atNextNode.Container(),
+                           atNextNode.Offset());
     NS_ENSURE_SUCCESS(rv, rv);
-    aOutArrayOfNodes.AppendElement(*breakNode);
-
-    // Now rightNode becomes the new node to split
-    splitDeepNode = rightNode;
-  }
-  // Now tack on remaining rightNode, if any, to the list
-  if (rightNode) {
-    aOutArrayOfNodes.AppendElement(*rightNode);
-  }
+    aOutArrayOfNodes.AppendElement(*brNode);
+
+    nextNode = splitNodeResult.GetNextNode();
+  }
+
+  // Now tack on remaining next node.
+  aOutArrayOfNodes.AppendElement(*nextNode);
+
   return NS_OK;
 }
 
 nsIContent*
 HTMLEditRules::GetHighestInlineParent(nsINode& aNode)
 {
   if (!aNode.IsContent() || IsBlockNode(aNode)) {
     return nullptr;
@@ -6674,25 +6736,33 @@ HTMLEditRules::ReturnInHeader(Selection&
   int32_t offset = headerParent ? headerParent->IndexOf(&aHeader) : -1;
 
   // Get ws code to adjust any ws
   nsCOMPtr<nsINode> node = &aNode;
   nsresult rv = WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
                                                         address_of(node),
                                                         &aOffset);
   NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(!node->IsContent())) {
+    return NS_ERROR_FAILURE;
+  }
 
   // Split the header
-  NS_ENSURE_STATE(node->IsContent());
-  htmlEditor->SplitNodeDeep(aHeader, *node->AsContent(), aOffset,
-                            SplitAtEdges::eAllowToCreateEmptyContainer);
-
-  // If the left-hand heading is empty, put a mozbr in it
+  ErrorResult error;
+  SplitNodeResult splitHeaderResult =
+    htmlEditor->SplitNodeDeep(aHeader, EditorRawDOMPoint(node, aOffset),
+                              SplitAtEdges::eAllowToCreateEmptyContainer);
+  NS_WARNING_ASSERTION(splitHeaderResult.Succeeded(),
+    "Failed to split aHeader");
+
+  // If the previous heading of split point is empty, put a mozbr into it.
   nsCOMPtr<nsIContent> prevItem = htmlEditor->GetPriorHTMLSibling(&aHeader);
-  if (prevItem && HTMLEditUtils::IsHeader(*prevItem)) {
+  if (prevItem) {
+    MOZ_DIAGNOSTIC_ASSERT(
+      HTMLEditUtils::IsHeader(*prevItem));
     bool isEmptyNode;
     rv = htmlEditor->IsEmptyNode(prevItem, &isEmptyNode);
     NS_ENSURE_SUCCESS(rv, rv);
     if (isEmptyNode) {
       rv = CreateMozBR(prevItem->AsDOMNode(), 0);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
@@ -6884,65 +6954,77 @@ HTMLEditRules::SplitParagraph(Selection&
   if (NS_WARN_IF(!mHTMLEditor)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<HTMLEditor> htmlEditor = mHTMLEditor;
 
   // split para
   // get ws code to adjust any ws
-  nsCOMPtr<nsIContent> leftPara, rightPara;
   nsCOMPtr<nsINode> selNode = aStartOfRightNode.Container();
   int32_t selOffset = aStartOfRightNode.Offset();
   nsresult rv =
     WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
                                             address_of(selNode), &selOffset);
-  // XXX When it fails, why do we need to return selection node?  (Why can the
-  //     caller trust the result even when it returns error?)
   NS_ENSURE_SUCCESS(rv, rv);
-  // split the paragraph
-  NS_ENSURE_STATE(selNode->IsContent());
-  int32_t offset =
-    htmlEditor->SplitNodeDeep(aParentDivOrP, *selNode->AsContent(), selOffset,
-                              SplitAtEdges::eAllowToCreateEmptyContainer,
-                              getter_AddRefs(leftPara),
-                              getter_AddRefs(rightPara));
-  if (NS_WARN_IF(offset == -1)) {
+  if (NS_WARN_IF(!selNode->IsContent())) {
     return NS_ERROR_FAILURE;
   }
+
+  // Split the paragraph.
+  SplitNodeResult splitDivOrPResult =
+    htmlEditor->SplitNodeDeep(aParentDivOrP,
+                              EditorRawDOMPoint(selNode, selOffset),
+                              SplitAtEdges::eAllowToCreateEmptyContainer);
+  if (NS_WARN_IF(splitDivOrPResult.Failed())) {
+    return splitDivOrPResult.Rv();
+  }
+  if (NS_WARN_IF(!splitDivOrPResult.DidSplit())) {
+    return NS_ERROR_FAILURE;
+  }
+
   // Get rid of the break, if it is visible (otherwise it may be needed to
   // prevent an empty p).
   if (aNextBRNode && htmlEditor->IsVisibleBRElement(aNextBRNode)) {
     rv = htmlEditor->DeleteNode(aNextBRNode);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  // remove ID attribute on the paragraph we just created
-  rv = htmlEditor->RemoveAttribute(rightPara->AsElement(), nsGkAtoms::id);
+  // Remove ID attribute on the paragraph from the existing right node.
+  rv = htmlEditor->RemoveAttribute(aParentDivOrP.AsElement(), nsGkAtoms::id);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We need to ensure to both paragraphs visible even if they are empty.
   // However, moz-<br> element isn't useful in this case because moz-<br>
   // elements will be ignored by PlaintextSerializer.  Additionally,
   // moz-<br> will be exposed as <br> with Element.innerHTML.  Therefore,
   // we can use normal <br> elements for placeholder in this case.
   // Note that Chromium also behaves so.
-  rv = InsertBRIfNeeded(*leftPara);
+  rv = InsertBRIfNeeded(*splitDivOrPResult.GetPreviousNode());
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = InsertBRIfNeeded(*rightPara);
+  rv = InsertBRIfNeeded(*splitDivOrPResult.GetNextNode());
   NS_ENSURE_SUCCESS(rv, rv);
 
   // selection to beginning of right hand para;
   // look inside any containers that are up front.
-  nsIContent* child = htmlEditor->GetLeftmostChild(rightPara, true);
+  nsIContent* child = htmlEditor->GetLeftmostChild(&aParentDivOrP, true);
   if (EditorBase::IsTextNode(child) || htmlEditor->IsContainer(child)) {
-    aSelection.Collapse(child, 0);
+    EditorRawDOMPoint atStartOfChild(child, 0);
+    ErrorResult error;
+    aSelection.Collapse(atStartOfChild, error);
+    if (NS_WARN_IF(error.Failed())) {
+      error.SuppressException();
+    }
   } else {
     EditorRawDOMPoint atChild(child);
-    aSelection.Collapse(atChild);
+    ErrorResult error;
+    aSelection.Collapse(atChild, error);
+    if (NS_WARN_IF(error.Failed())) {
+      error.SuppressException();
+    }
   }
   return NS_OK;
 }
 
 /**
  * ReturnInListItem: do the right thing for returns pressed in list items
  */
 nsresult
@@ -7018,20 +7100,26 @@ HTMLEditRules::ReturnInListItem(Selectio
   }
 
   // Else we want a new list item at the same list level.  Get ws code to
   // adjust any ws.
   nsCOMPtr<nsINode> selNode = &aNode;
   rv = WSRunObject::PrepareToSplitAcrossBlocks(htmlEditor,
                                                address_of(selNode), &aOffset);
   NS_ENSURE_SUCCESS(rv, rv);
-  // Now split list item
-  NS_ENSURE_STATE(selNode->IsContent());
-  htmlEditor->SplitNodeDeep(aListItem, *selNode->AsContent(), aOffset,
-                            SplitAtEdges::eAllowToCreateEmptyContainer);
+  if (NS_WARN_IF(!selNode->IsContent())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Now split the list item.
+  SplitNodeResult splitListItemResult =
+    htmlEditor->SplitNodeDeep(aListItem, EditorRawDOMPoint(selNode, aOffset),
+                              SplitAtEdges::eAllowToCreateEmptyContainer);
+  NS_WARNING_ASSERTION(splitListItemResult.Succeeded(),
+    "Failed to split the list item");
 
   // Hack: until I can change the damaged doc range code back to being
   // extra-inclusive, I have to manually detect certain list items that may be
   // left empty.
   nsCOMPtr<nsIContent> prevItem = htmlEditor->GetPriorHTMLSibling(&aListItem);
   if (prevItem && HTMLEditUtils::IsListItem(prevItem)) {
     bool isEmptyNode;
     rv = htmlEditor->IsEmptyNode(prevItem, &isEmptyNode);
@@ -7069,46 +7157,56 @@ HTMLEditRules::ReturnInListItem(Selectio
                                                      &aListItem,
                                                      getter_AddRefs(brNode));
         NS_ENSURE_SUCCESS(rv, rv);
         if (brNode) {
           EditorRawDOMPoint atBrNode(brNode);
           if (NS_WARN_IF(!atBrNode.IsSetAndValid())) {
             return NS_ERROR_FAILURE;
           }
-          rv = aSelection.Collapse(atBrNode);
-          NS_ENSURE_SUCCESS(rv, rv);
+          ErrorResult error;
+          aSelection.Collapse(atBrNode, error);
+          if (NS_WARN_IF(error.Failed())) {
+            return error.StealNSResult();
+          }
           return NS_OK;
         }
       } else {
         WSRunObject wsObj(htmlEditor, &aListItem, 0);
         nsCOMPtr<nsINode> visNode;
         int32_t visOffset = 0;
         WSType wsType;
         wsObj.NextVisibleNode(&aListItem, 0, address_of(visNode),
                               &visOffset, &wsType);
         if (wsType == WSType::special || wsType == WSType::br ||
             visNode->IsHTMLElement(nsGkAtoms::hr)) {
           EditorRawDOMPoint atVisNode(visNode);
           if (NS_WARN_IF(!atVisNode.IsSetAndValid())) {
             return NS_ERROR_FAILURE;
           }
-          rv = aSelection.Collapse(atVisNode);
-          NS_ENSURE_SUCCESS(rv, rv);
-          return NS_OK;
-        } else {
-          rv = aSelection.Collapse(visNode, visOffset);
-          NS_ENSURE_SUCCESS(rv, rv);
+          ErrorResult error;
+          aSelection.Collapse(atVisNode, error);
+          if (NS_WARN_IF(error.Failed())) {
+            return error.StealNSResult();
+          }
           return NS_OK;
         }
-      }
-    }
-  }
-  rv = aSelection.Collapse(&aListItem, 0);
-  NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = aSelection.Collapse(visNode, visOffset);
+        NS_ENSURE_SUCCESS(rv, rv);
+        return NS_OK;
+      }
+    }
+  }
+
+  ErrorResult error;
+  aSelection.Collapse(EditorRawDOMPoint(&aListItem, 0), error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
   return NS_OK;
 }
 
 /**
  * MakeBlockquote() puts the list of nodes into one or more blockquotes.
  */
 nsresult
 HTMLEditRules::MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
@@ -7419,58 +7517,74 @@ HTMLEditRules::SplitAsNeeded(nsAtom& aTa
 }
 
 nsresult
 HTMLEditRules::SplitAsNeeded(nsAtom& aTag,
                              nsCOMPtr<nsINode>& inOutParent,
                              int32_t& inOutOffset,
                              nsCOMPtr<nsIContent>* inOutChildAtOffset)
 {
-  NS_ENSURE_TRUE(inOutParent, NS_ERROR_NULL_POINTER);
+  if (NS_WARN_IF(!inOutParent)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (NS_WARN_IF(!mHTMLEditor)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
   // Check that we have a place that can legally contain the tag
   nsCOMPtr<nsINode> tagParent, splitNode;
   for (nsCOMPtr<nsINode> parent = inOutParent; parent;
        parent = parent->GetParentNode()) {
     // Sniffing up the parent tree until we find a legal place for the block
 
     // Don't leave the active editing host
     NS_ENSURE_STATE(mHTMLEditor);
-    if (!mHTMLEditor->IsDescendantOfEditorRoot(parent)) {
+    if (!htmlEditor->IsDescendantOfEditorRoot(parent)) {
       // XXX Why do we need to check mHTMLEditor again here?
       NS_ENSURE_STATE(mHTMLEditor);
-      if (parent != mHTMLEditor->GetActiveEditingHost()) {
+      if (parent != htmlEditor->GetActiveEditingHost()) {
         return NS_ERROR_FAILURE;
       }
     }
 
-    NS_ENSURE_STATE(mHTMLEditor);
-    if (mHTMLEditor->CanContainTag(*parent, aTag)) {
+    if (htmlEditor->CanContainTag(*parent, aTag)) {
       // Success
       tagParent = parent;
       break;
     }
 
     splitNode = parent;
   }
+
   if (!tagParent) {
     // Could not find a place to build tag!
     return NS_ERROR_FAILURE;
   }
-  if (splitNode && splitNode->IsContent() && inOutParent->IsContent()) {
-    // We found a place for block, but above inOutParent. We need to split.
-    NS_ENSURE_STATE(mHTMLEditor);
-    int32_t offset =
-      mHTMLEditor->SplitNodeDeep(*splitNode->AsContent(),
-                                 *inOutParent->AsContent(), inOutOffset,
-                                 SplitAtEdges::eAllowToCreateEmptyContainer,
-                                 nullptr, nullptr, inOutChildAtOffset);
-    NS_ENSURE_STATE(offset != -1);
-    inOutParent = tagParent;
-    inOutOffset = offset;
+
+  if (!splitNode || !splitNode->IsContent() || !inOutParent->IsContent()) {
+    return NS_OK;
+  }
+
+  // We found a place for block, but above inOutParent. We need to split.
+  NS_ENSURE_STATE(mHTMLEditor);
+  SplitNodeResult splitNodeResult =
+    mHTMLEditor->SplitNodeDeep(*splitNode->AsContent(),
+                               EditorRawDOMPoint(inOutParent, inOutOffset),
+                               SplitAtEdges::eAllowToCreateEmptyContainer);
+  if (NS_WARN_IF(splitNodeResult.Failed())) {
+    return splitNodeResult.Rv();
+  }
+
+  EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
+  inOutParent = splitPoint.Container();
+  inOutOffset = splitPoint.Offset();
+  if (inOutChildAtOffset) {
+    *inOutChildAtOffset = splitPoint.GetChildAtOffset();
   }
   return NS_OK;
 }
 
 /**
  * JoinNodesSmart: Join two nodes, doing whatever makes sense for their
  * children (which often means joining them, too).  aNodeLeft & aNodeRight must
  * be same type of node.
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1614,29 +1614,28 @@ HTMLEditor::InsertNodeAtPoint(nsIDOMNode
       // where we were originally asked.
       parent = topChild = origParent;
       break;
     }
     topChild = parent;
     parent = parent->GetParent();
   }
   if (parent != topChild) {
-    nsCOMPtr<nsIContent> child;
-    if (ioChildAtOffset) {
-      child = do_QueryInterface(*ioChildAtOffset);
+    // We need to split some levels above the original selection parent.
+    SplitNodeResult splitNodeResult =
+      SplitNodeDeep(*topChild, EditorRawDOMPoint(origParent, *ioOffset),
+                    aSplitAtEdges);
+    if (NS_WARN_IF(splitNodeResult.Failed())) {
+      return splitNodeResult.Rv();
     }
-    // we need to split some levels above the original selection parent
-    int32_t offset = SplitNodeDeep(*topChild, *origParent, *ioOffset,
-                                   aSplitAtEdges,
-                                   nullptr, nullptr, address_of(child));
-    NS_ENSURE_STATE(offset != -1);
-    *ioParent = GetAsDOMNode(parent);
-    *ioOffset = offset;
+    EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
+    *ioParent = GetAsDOMNode(splitPoint.Container());
+    *ioOffset = splitPoint.Offset();
     if (ioChildAtOffset) {
-      *ioChildAtOffset = GetAsDOMNode(child);
+      *ioChildAtOffset = GetAsDOMNode(splitPoint.GetChildAtOffset());
     }
   }
   // Now we can insert the new node
   nsresult rv = InsertNode(*node, *parent, *ioOffset);
   if (isDocumentFragment) {
     *ioChildAtOffset = do_QueryInterface(parent->GetChildAt(*ioOffset));
   }
   return rv;
@@ -1970,62 +1969,66 @@ HTMLEditor::MakeOrChangeList(const nsASt
   ruleInfo.blockType = &aListType;
   ruleInfo.entireList = entireList;
   ruleInfo.bulletType = &aBulletType;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
-  if (!handled) {
-    // Find out if the selection is collapsed:
-    bool isCollapsed = selection->Collapsed();
-
-    NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
-                   selection->GetRangeAt(0)->GetStartContainer() &&
-                   selection->GetRangeAt(0)->GetStartContainer()->IsContent(),
-                   NS_ERROR_FAILURE);
-    OwningNonNull<nsIContent> node =
-      *selection->GetRangeAt(0)->GetStartContainer()->AsContent();
-    int32_t offset = selection->GetRangeAt(0)->StartOffset();
-    nsCOMPtr<nsIContent> child =
-      selection->GetRangeAt(0)->GetChildAtStartOffset();
-
-    if (isCollapsed) {
-      // have to find a place to put the list
-      nsCOMPtr<nsIContent> parent = node;
-      nsCOMPtr<nsIContent> topChild = node;
-
-      RefPtr<nsAtom> listAtom = NS_Atomize(aListType);
-      while (!CanContainTag(*parent, *listAtom)) {
-        topChild = parent;
-        parent = parent->GetParent();
+  if (!handled && selection->Collapsed()) {
+    nsRange* firstRange = selection->GetRangeAt(0);
+    if (NS_WARN_IF(!firstRange)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+    if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
+        NS_WARN_IF(!atStartOfSelection.Container()->IsContent())) {
+      return NS_ERROR_FAILURE;
+    }
+
+    // Have to find a place to put the list.
+    EditorDOMPoint pointToInsertList(atStartOfSelection);
+
+    RefPtr<nsAtom> listAtom = NS_Atomize(aListType);
+    while (!CanContainTag(*pointToInsertList.Container(), *listAtom)) {
+      pointToInsertList.Set(pointToInsertList.Container());
+      if (NS_WARN_IF(!pointToInsertList.IsSet()) ||
+          NS_WARN_IF(!pointToInsertList.Container()->IsContent())) {
+        return NS_ERROR_FAILURE;
       }
-
-      if (parent != node) {
-        // we need to split up to the child of parent
-        offset = SplitNodeDeep(*topChild, *node, offset,
-                               SplitAtEdges::eAllowToCreateEmptyContainer,
-                               nullptr, nullptr,
-                               address_of(child));
-        NS_ENSURE_STATE(offset != -1);
+    }
+
+    if (pointToInsertList.Container() != atStartOfSelection.Container()) {
+      // We need to split up to the child of parent.
+      SplitNodeResult splitNodeResult =
+        SplitNodeDeep(*pointToInsertList.GetChildAtOffset(),
+                      atStartOfSelection,
+                      SplitAtEdges::eAllowToCreateEmptyContainer);
+      if (NS_WARN_IF(splitNodeResult.Failed())) {
+        return splitNodeResult.Rv();
       }
-
-      // make a list
-      MOZ_DIAGNOSTIC_ASSERT(child);
-      EditorRawDOMPoint atChild(parent, child, offset);
-      RefPtr<Element> newList = CreateNode(listAtom, atChild);
-      NS_ENSURE_STATE(newList);
-      // make a list item
-      EditorRawDOMPoint atStartOfNewList(newList, 0);
-      RefPtr<Element> newItem = CreateNode(nsGkAtoms::li, atStartOfNewList);
-      NS_ENSURE_STATE(newItem);
-      rv = selection->Collapse(newItem, 0);
-      NS_ENSURE_SUCCESS(rv, rv);
+      pointToInsertList = splitNodeResult.SplitPoint();
+      if (NS_WARN_IF(!pointToInsertList.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
     }
+
+    // Create a list and insert it before the right node if we split some
+    // parents of start of selection above, or just start of selection
+    // otherwise.
+    RefPtr<Element> newList = CreateNode(listAtom, pointToInsertList.AsRaw());
+    NS_ENSURE_STATE(newList);
+    // make a list item
+    EditorRawDOMPoint atStartOfNewList(newList, 0);
+    RefPtr<Element> newItem = CreateNode(nsGkAtoms::li, atStartOfNewList);
+    NS_ENSURE_STATE(newItem);
+    rv = selection->Collapse(newItem, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
 
 NS_IMETHODIMP
 HTMLEditor::RemoveList(const nsAString& aListType)
 {
@@ -2115,61 +2118,65 @@ HTMLEditor::InsertBasicBlock(const nsASt
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   TextRulesInfo ruleInfo(EditAction::makeBasicBlock);
   ruleInfo.blockType = &aBlockType;
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
-  if (!handled) {
-    // Find out if the selection is collapsed:
-    bool isCollapsed = selection->Collapsed();
-
-    NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
-                   selection->GetRangeAt(0)->GetStartContainer() &&
-                   selection->GetRangeAt(0)->GetStartContainer()->IsContent(),
-                   NS_ERROR_FAILURE);
-    OwningNonNull<nsIContent> node =
-      *selection->GetRangeAt(0)->GetStartContainer()->AsContent();
-    int32_t offset = selection->GetRangeAt(0)->StartOffset();
-    nsCOMPtr<nsIContent> child =
-      selection->GetRangeAt(0)->GetChildAtStartOffset();
-
-    if (isCollapsed) {
-      // have to find a place to put the block
-      nsCOMPtr<nsIContent> parent = node;
-      nsCOMPtr<nsIContent> topChild = node;
-
-      RefPtr<nsAtom> blockAtom = NS_Atomize(aBlockType);
-      while (!CanContainTag(*parent, *blockAtom)) {
-        NS_ENSURE_TRUE(parent->GetParent(), NS_ERROR_FAILURE);
-        topChild = parent;
-        parent = parent->GetParent();
+  if (!handled && selection->Collapsed()) {
+    nsRange* firstRange = selection->GetRangeAt(0);
+    if (NS_WARN_IF(!firstRange)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+    if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
+        NS_WARN_IF(!atStartOfSelection.Container()->IsContent())) {
+      return NS_ERROR_FAILURE;
+    }
+
+    // Have to find a place to put the block.
+    EditorDOMPoint pointToInsertBlock(atStartOfSelection);
+
+    RefPtr<nsAtom> blockAtom = NS_Atomize(aBlockType);
+    while (!CanContainTag(*pointToInsertBlock.Container(), *blockAtom)) {
+      pointToInsertBlock.Set(pointToInsertBlock.Container());
+      if (NS_WARN_IF(!pointToInsertBlock.IsSet()) ||
+          NS_WARN_IF(!pointToInsertBlock.Container()->IsContent())) {
+        return NS_ERROR_FAILURE;
       }
-
-      if (parent != node) {
-        // we need to split up to the child of parent
-        offset = SplitNodeDeep(*topChild, *node, offset,
-                               SplitAtEdges::eAllowToCreateEmptyContainer,
-                               nullptr, nullptr,
-                               address_of(child));
-        NS_ENSURE_STATE(offset != -1);
+    }
+
+    if (pointToInsertBlock.Container() != atStartOfSelection.Container()) {
+      // We need to split up to the child of the point to insert a block.
+      SplitNodeResult splitBlockResult =
+        SplitNodeDeep(*pointToInsertBlock.GetChildAtOffset(),
+                      atStartOfSelection,
+                      SplitAtEdges::eAllowToCreateEmptyContainer);
+      if (NS_WARN_IF(splitBlockResult.Failed())) {
+        return splitBlockResult.Rv();
       }
-
-      // make a block
-      MOZ_DIAGNOSTIC_ASSERT(child);
-      EditorRawDOMPoint atChild(parent, child, offset);
-      RefPtr<Element> newBlock = CreateNode(blockAtom, atChild);
-      NS_ENSURE_STATE(newBlock);
-
-      // reposition selection to inside the block
-      rv = selection->Collapse(newBlock, 0);
-      NS_ENSURE_SUCCESS(rv, rv);
+      pointToInsertBlock = splitBlockResult.SplitPoint();
+      if (NS_WARN_IF(!pointToInsertBlock.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
     }
+
+    // Create a block and insert it before the right node if we split some
+    // parents of start of selection above, or just start of selection
+    // otherwise.
+    RefPtr<Element> newBlock =
+      CreateNode(blockAtom, pointToInsertBlock.AsRaw());
+    NS_ENSURE_STATE(newBlock);
+
+    // reposition selection to inside the block
+    rv = selection->Collapse(newBlock, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
 
 NS_IMETHODIMP
 HTMLEditor::Indent(const nsAString& aIndent)
 {
@@ -2193,67 +2200,72 @@ HTMLEditor::Indent(const nsAString& aInd
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   TextRulesInfo ruleInfo(opID);
   nsresult rv = rules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) {
     return rv;
   }
 
-  if (!handled) {
-    // Do default - insert a blockquote node if selection collapsed
-    bool isCollapsed = selection->Collapsed();
-
-    NS_ENSURE_TRUE(selection->GetRangeAt(0) &&
-                   selection->GetRangeAt(0)->GetStartContainer() &&
-                   selection->GetRangeAt(0)->GetStartContainer()->IsContent(),
-                   NS_ERROR_FAILURE);
-    OwningNonNull<nsIContent> node =
-      *selection->GetRangeAt(0)->GetStartContainer()->AsContent();
-    int32_t offset = selection->GetRangeAt(0)->StartOffset();
-    nsCOMPtr<nsIContent> child =
-      selection->GetRangeAt(0)->GetChildAtStartOffset();
-
-    if (aIndent.EqualsLiteral("indent")) {
-      if (isCollapsed) {
-        // have to find a place to put the blockquote
-        nsCOMPtr<nsIContent> parent = node;
-        nsCOMPtr<nsIContent> topChild = node;
-        while (!CanContainTag(*parent, *nsGkAtoms::blockquote)) {
-          NS_ENSURE_TRUE(parent->GetParent(), NS_ERROR_FAILURE);
-          topChild = parent;
-          parent = parent->GetParent();
-        }
-
-        if (parent != node) {
-          // we need to split up to the child of parent
-          offset = SplitNodeDeep(*topChild, *node, offset,
-                                 SplitAtEdges::eAllowToCreateEmptyContainer,
-                                 nullptr, nullptr,
-                                 address_of(child));
-          NS_ENSURE_STATE(offset != -1);
-        }
-
-        // make a blockquote
-        MOZ_DIAGNOSTIC_ASSERT(child);
-        EditorRawDOMPoint atChild(parent, child, offset);
-        RefPtr<Element> newBQ = CreateNode(nsGkAtoms::blockquote, atChild);
-        NS_ENSURE_STATE(newBQ);
-        // put a space in it so layout will draw the list item
-        rv = selection->Collapse(newBQ, 0);
-        NS_ENSURE_SUCCESS(rv, rv);
-        rv = InsertText(NS_LITERAL_STRING(" "));
-        NS_ENSURE_SUCCESS(rv, rv);
-        // reposition selection to before the space character
-        NS_ENSURE_STATE(selection->GetRangeAt(0));
-        rv = selection->Collapse(selection->GetRangeAt(0)->GetStartContainer(),
-                                 0);
-        NS_ENSURE_SUCCESS(rv, rv);
+  if (!handled && selection->Collapsed() && aIndent.EqualsLiteral("indent")) {
+    nsRange* firstRange = selection->GetRangeAt(0);
+    if (NS_WARN_IF(!firstRange)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
+    if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
+        NS_WARN_IF(!atStartOfSelection.Container()->IsContent())) {
+      return NS_ERROR_FAILURE;
+    }
+
+    // Have to find a place to put the blockquote.
+    EditorDOMPoint pointToInsertBlockquote(atStartOfSelection);
+
+    while (!CanContainTag(*pointToInsertBlockquote.Container(),
+                          *nsGkAtoms::blockquote)) {
+      pointToInsertBlockquote.Set(pointToInsertBlockquote.Container());
+      if (NS_WARN_IF(!pointToInsertBlockquote.IsSet()) ||
+          NS_WARN_IF(!pointToInsertBlockquote.Container()->IsContent())) {
+        return NS_ERROR_FAILURE;
       }
     }
+
+    if (pointToInsertBlockquote.Container() !=
+          atStartOfSelection.Container()) {
+      // We need to split up to the child of parent.
+      SplitNodeResult splitBlockquoteResult =
+        SplitNodeDeep(*pointToInsertBlockquote.GetChildAtOffset(),
+                      atStartOfSelection,
+                      SplitAtEdges::eAllowToCreateEmptyContainer);
+      if (NS_WARN_IF(splitBlockquoteResult.Failed())) {
+        return splitBlockquoteResult.Rv();
+      }
+      pointToInsertBlockquote = splitBlockquoteResult.SplitPoint();
+      if (NS_WARN_IF(!pointToInsertBlockquote.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
+    }
+
+    // Create a list and insert it before the right node if we split some
+    // parents of start of selection above, or just start of selection
+    // otherwise.
+    RefPtr<Element> newBQ =
+      CreateNode(nsGkAtoms::blockquote, pointToInsertBlockquote.AsRaw());
+    NS_ENSURE_STATE(newBQ);
+    // put a space in it so layout will draw the list item
+    rv = selection->Collapse(newBQ, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = InsertText(NS_LITERAL_STRING(" "));
+    NS_ENSURE_SUCCESS(rv, rv);
+    // reposition selection to before the space character
+    NS_ENSURE_STATE(selection->GetRangeAt(0));
+    rv = selection->Collapse(selection->GetRangeAt(0)->GetStartContainer(),
+                             0);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
 
 //TODO: IMPLEMENT ALIGNMENT!
 
 NS_IMETHODIMP
 HTMLEditor::Align(const nsAString& aAlignType)
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -267,20 +267,16 @@ HTMLEditor::DoInsertHTMLWithContext(cons
     // to delete everything.
     if (aDeleteSelection) {
       return DeleteSelection(eNone, eStrip);
     }
     return NS_OK;
   }
 
   // Are there any table elements in the list?
-  // node and offset for insertion
-  nsCOMPtr<nsIDOMNode> parentNode;
-  int32_t offsetOfNewNode;
-
   // check for table cell selection mode
   bool cellSelectionMode = false;
   nsCOMPtr<nsIDOMElement> cell;
   rv = GetFirstSelectedCell(nullptr, getter_AddRefs(cell));
   if (NS_SUCCEEDED(rv) && cell) {
     cellSelectionMode = true;
   }
 
@@ -327,16 +323,18 @@ HTMLEditor::DoInsertHTMLWithContext(cons
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) {
     return NS_OK; // rules canceled the operation
   }
 
   if (!handled) {
     // The rules code (WillDoAction above) might have changed the selection.
     // refresh our memory...
+    nsCOMPtr<nsIDOMNode> parentNode;
+    int32_t offsetOfNewNode;
     rv = GetStartNodeAndOffset(selection, getter_AddRefs(parentNode), &offsetOfNewNode);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_TRUE(parentNode, NS_ERROR_FAILURE);
 
     // Adjust position based on the first node we are going to insert.
     NormalizeEOLInsertPosition(nodeList[0], address_of(parentNode),
                                &offsetOfNewNode);
 
@@ -352,25 +350,32 @@ HTMLEditor::DoInsertHTMLWithContext(cons
     }
 
     // Remember if we are in a link.
     bool bStartedInLink = IsInLink(parentNode);
 
     // Are we in a text node? If so, split it.
     if (IsTextNode(parentNode)) {
       nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parentNode);
-      NS_ENSURE_STATE(parentContent || !parentNode);
-      offsetOfNewNode =
-        SplitNodeDeep(*parentContent, *parentContent, offsetOfNewNode,
+      EditorRawDOMPoint pointToSplit(parentContent, offsetOfNewNode);
+      if (NS_WARN_IF(!pointToSplit.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
+      SplitNodeResult splitNodeResult =
+        SplitNodeDeep(*parentContent, pointToSplit,
                       SplitAtEdges::eAllowToCreateEmptyContainer);
-      NS_ENSURE_STATE(offsetOfNewNode != -1);
-      nsCOMPtr<nsIDOMNode> temp;
-      rv = parentNode->GetParentNode(getter_AddRefs(temp));
-      NS_ENSURE_SUCCESS(rv, rv);
-      parentNode = temp;
+      if (NS_WARN_IF(splitNodeResult.Failed())) {
+        return splitNodeResult.Rv();
+      }
+      EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
+      if (NS_WARN_IF(!splitPoint.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
+      parentNode = do_QueryInterface(splitPoint.Container());
+      offsetOfNewNode = splitPoint.Offset();
     }
 
     // build up list of parents of first node in list that are either
     // lists or tables.  First examine front of paste node list.
     nsTArray<OwningNonNull<Element>> startListAndTableArray;
     GetListAndTableParents(StartOrEnd::start, nodeList,
                            startListAndTableArray);
 
@@ -644,22 +649,23 @@ HTMLEditor::DoInsertHTMLWithContext(cons
         // so, if we just pasted a link, I split it.  Why do that instead of just
         // nudging selection point beyond it?  Because it might have ended in a BR
         // that is not visible.  If so, the code above just placed selection
         // inside that.  So I split it instead.
         nsCOMPtr<nsIContent> linkContent = do_QueryInterface(link);
         NS_ENSURE_STATE(linkContent || !link);
         nsCOMPtr<nsIContent> selContent = do_QueryInterface(selNode);
         NS_ENSURE_STATE(selContent || !selNode);
-        nsCOMPtr<nsIContent> leftLink;
-        SplitNodeDeep(*linkContent, *selContent, selOffset,
-                      SplitAtEdges::eDoNotCreateEmptyContainer,
-                      getter_AddRefs(leftLink));
-        if (leftLink) {
-          EditorRawDOMPoint afterLeftLink(leftLink);
+        SplitNodeResult splitLinkResult =
+          SplitNodeDeep(*linkContent, EditorRawDOMPoint(selContent, selOffset),
+                        SplitAtEdges::eDoNotCreateEmptyContainer);
+        NS_WARNING_ASSERTION(splitLinkResult.Succeeded(),
+          "Failed to split the link");
+        if (splitLinkResult.GetPreviousNode()) {
+          EditorRawDOMPoint afterLeftLink(splitLinkResult.GetPreviousNode());
           if (afterLeftLink.AdvanceOffset()) {
             selection->Collapse(afterLeftLink);
           }
         }
       }
     }
   }
 
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -520,16 +520,23 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPt
                                  nsAtom* aProperty,
                                  const nsAString* aAttribute,
                                  nsIContent** aOutLeftNode,
                                  nsIContent** aOutRightNode)
 {
   NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE((*aNode)->IsContent(), NS_OK);
 
+  if (aOutLeftNode) {
+    *aOutLeftNode = nullptr;
+  }
+  if (aOutRightNode) {
+    *aOutRightNode = nullptr;
+  }
+
   // Split any matching style nodes above the node/offset
   OwningNonNull<nsIContent> node = *(*aNode)->AsContent();
 
   bool useCSS = IsCSSEnabled();
 
   bool isSet;
   while (!IsBlockNode(node) && node->GetParent() &&
          IsEditable(node->GetParent())) {
@@ -548,23 +555,31 @@ HTMLEditor::SplitStyleAbovePoint(nsCOMPt
         (aProperty && node->IsHTMLElement(aProperty)) ||
         // node is href - test if really <a href=...
         (aProperty == nsGkAtoms::href && HTMLEditUtils::IsLink(node)) ||
         // or node is any prop, and we asked to split them all
         (!aProperty && NodeIsProperty(node)) ||
         // or the style is specified in the style attribute
         isSet) {
       // Found a style node we need to split
-      int32_t offset = SplitNodeDeep(*node, *(*aNode)->AsContent(), *aOffset,
-                                     SplitAtEdges::eAllowToCreateEmptyContainer,
-                                     aOutLeftNode, aOutRightNode);
-      NS_ENSURE_TRUE(offset != -1, NS_ERROR_FAILURE);
-      // reset startNode/startOffset
-      *aNode = node->GetParent();
-      *aOffset = offset;
+      SplitNodeResult splitNodeResult =
+        SplitNodeDeep(*node, EditorRawDOMPoint(*aNode, *aOffset),
+                      SplitAtEdges::eAllowToCreateEmptyContainer);
+      NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
+        "Failed to split the node");
+
+      EditorRawDOMPoint atRightNode(splitNodeResult.SplitPoint());
+      *aNode = atRightNode.Container();
+      *aOffset = atRightNode.Offset();
+      if (aOutLeftNode) {
+        NS_IF_ADDREF(*aOutLeftNode = splitNodeResult.GetPreviousNode());
+      }
+      if (aOutRightNode) {
+        NS_IF_ADDREF(*aOutRightNode = splitNodeResult.GetNextNode());
+      }
     }
     node = node->GetParent();
   }
 
   return NS_OK;
 }
 
 nsresult