Bug 1451672 - part 16: Rename EditorBase::MoveNode() to EditorBase::MoveNodeWithTransaction() and create EditorBase::MoveNodeToEndWithTransaction() r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 12 Apr 2018 23:58:52 +0900
changeset 786099 9168550d26608576eb57f0fd9dfd4628a824aa9f
parent 786098 547317b1e9a24ff718f50e03cc82c6d2695728ac
child 786100 e0373b13d6687f60a97e520538d78a1d0d5d81ca
push id107393
push usermasayuki@d-toybox.com
push dateSat, 21 Apr 2018 04:40:12 +0000
reviewersm_kato
bugs1451672
milestone61.0a1
Bug 1451672 - part 16: Rename EditorBase::MoveNode() to EditorBase::MoveNodeWithTransaction() and create EditorBase::MoveNodeToEndWithTransaction() r?m_kato This patch renames EditorBase::MoveNode() to EditorBase::MoveNodeWithTransaction() and redesign its parameters including replacing a set of container node and offset in it to EditorDOMPointBase. However, it takes magic number -1 as meaning end of the container. Therefore, this patch adds MoveNodeToEndWithTransaction() for keeping the callers simple. MozReview-Commit-ID: BeTq5c7GQNN
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLStyleEditor.cpp
editor/libeditor/SelectionState.h
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -144,16 +144,22 @@ EditorBase::SplitNodeDeepWithTransaction
               nsIContent& aMostAncestorToSplit,
               const EditorDOMPoint& aStartOfDeepestRightNode,
               SplitAtEdges aSplitAtEdges);
 template SplitNodeResult
 EditorBase::SplitNodeDeepWithTransaction(
               nsIContent& aMostAncestorToSplit,
               const EditorRawDOMPoint& aStartOfDeepestRightNode,
               SplitAtEdges aSplitAtEdges);
+template nsresult
+EditorBase::MoveNodeWithTransaction(nsIContent& aContent,
+                                    const EditorDOMPoint& aPointToInsert);
+template nsresult
+EditorBase::MoveNodeWithTransaction(nsIContent& aContent,
+                                    const EditorRawDOMPoint& aPointToInsert);
 
 EditorBase::EditorBase()
   : mPlaceholderName(nullptr)
   , mModCount(0)
   , mFlags(0)
   , mUpdateCount(0)
   , mPlaceholderBatch(0)
   , mAction(EditAction::none)
@@ -1833,65 +1839,59 @@ EditorBase::InsertContainerAbove(nsICont
   rv = InsertNodeWithTransaction(*newContainer, pointToInsertNewContainer);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
 
   return newContainer.forget();
 }
 
-/**
- * MoveNode() moves aNode to {aParent,aOffset}.
- */
+template<typename PT, typename CT>
 nsresult
-EditorBase::MoveNode(nsIContent* aNode,
-                     nsINode* aParent,
-                     int32_t aOffset)
-{
-  MOZ_ASSERT(aNode);
-  MOZ_ASSERT(aParent);
-  MOZ_ASSERT(aOffset == -1 ||
-             (0 <= aOffset &&
-              AssertedCast<uint32_t>(aOffset) <= aParent->Length()));
-
-  nsCOMPtr<nsINode> oldParent = aNode->GetParentNode();
-  if (NS_WARN_IF(!oldParent)) {
+EditorBase::MoveNodeWithTransaction(
+              nsIContent& aContent,
+              const EditorDOMPointBase<PT, CT>& aPointToInsert)
+{
+  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
+
+  EditorDOMPoint oldPoint(&aContent);
+  if (NS_WARN_IF(!oldPoint.IsSet())) {
     return NS_ERROR_FAILURE;
   }
-  int32_t oldOffset = oldParent->ComputeIndexOf(aNode);
-
-  if (aOffset == -1) {
-    // Magic value meaning "move to end of aParent"
-    aOffset = AssertedCast<int32_t>(aParent->Length());
-  }
-
-  // Don't do anything if it's already in right place
-  if (aParent == oldParent && aOffset == oldOffset) {
+
+  // Don't do anything if it's already in right place.
+  if (aPointToInsert == oldPoint) {
     return NS_OK;
   }
 
   // Notify our internal selection state listener
-  AutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset,
-                                  aParent, aOffset);
-
-  // Need to adjust aOffset if we're moving aNode later in its current parent
-  if (aParent == oldParent && oldOffset < aOffset) {
-    // When we delete aNode, it will make the offsets after it off by one
-    aOffset--;
-  }
+  EditorDOMPoint newPoint(aPointToInsert);
+  AutoMoveNodeSelNotify selNotify(mRangeUpdater, oldPoint, newPoint);
 
   // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
-  nsCOMPtr<nsIContent> nodeToBeMoved(aNode);
-  nsresult rv = DeleteNodeWithTransaction(*nodeToBeMoved);
+  nsresult rv = DeleteNodeWithTransaction(aContent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = InsertNodeWithTransaction(*nodeToBeMoved,
-                                 EditorRawDOMPoint(aParent, aOffset));
+  // Mutation event listener could break insertion point. Let's check it.
+  EditorRawDOMPoint pointToInsert(selNotify.ComputeInsertionPoint());
+  if (NS_WARN_IF(!pointToInsert.IsSet())) {
+    return NS_ERROR_FAILURE;
+  }
+  // If some children have removed from the container, let's append to the
+  // container.
+  // XXX Perhaps, if mutation event listener inserts or removes some children
+  //     but the child node referring with aPointToInsert is still available,
+  //     we should insert aContent before it.  However, we should keep
+  //     traditional behavior for now.
+  if (NS_WARN_IF(!pointToInsert.IsSetAndValid())) {
+    pointToInsert.SetToEndOf(pointToInsert.GetContainer());
+  }
+  rv = InsertNodeWithTransaction(aContent, pointToInsert);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 void
 EditorBase::MoveAllChildren(nsINode& aContainer,
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -465,17 +465,41 @@ public:
    * method is JoinNodesImpl().  So, see its explanation for the detail.
    *
    * @param aLeftNode   Will be removed from the DOM tree.
    * @param aRightNode  The node which will be new container of the content of
    *                    aLeftNode.
    */
   nsresult JoinNodesWithTransaction(nsINode& aLeftNode, nsINode& aRightNode);
 
-  nsresult MoveNode(nsIContent* aNode, nsINode* aParent, int32_t aOffset);
+  /**
+   * MoveNodeWithTransaction() moves aContent to aPointToInsert.
+   *
+   * @param aContent        The node to be moved.
+   */
+  template<typename PT, typename CT>
+  nsresult
+  MoveNodeWithTransaction(nsIContent& aContent,
+                          const EditorDOMPointBase<PT, CT>& aPointToInsert);
+
+  /**
+   * MoveNodeToEndWithTransaction() moves aContent to end of aNewContainer.
+   *
+   * @param aContent        The node to be moved.
+   * @param aNewContainer   The new container which will contain aContent as
+   *                        its last child.
+   */
+  nsresult
+  MoveNodeToEndWithTransaction(nsIContent& aContent,
+                               nsINode& aNewContainer)
+  {
+    EditorRawDOMPoint pointToInsert;
+    pointToInsert.SetToEndOf(&aNewContainer);
+    return MoveNodeWithTransaction(aContent, pointToInsert);
+  }
 
   /**
    * MoveAllChildren() moves all children of aContainer to before
    * aPointToInsert.GetChild().
    * See explanation of MoveChildren() for the detail of the behavior.
    *
    * @param aContainer          The container node whose all children should
    *                            be moved.
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1951,19 +1951,18 @@ HTMLEditRules::InsertBRElement(Selection
     // second break to be the first break's sibling.  This will prevent them
     // from being in different inline nodes, which would break
     // SetInterlinePosition().  It will also assure that if the user clicks
     // away and then clicks back on their new blank line, they will still get
     // the style from the line above.
     EditorDOMPoint atSecondBRElement(maybeSecondBRNode);
     if (brElement->GetNextSibling() != maybeSecondBRNode) {
       nsresult rv =
-        htmlEditor->MoveNode(maybeSecondBRNode->AsContent(),
-                             afterBRElement.GetContainer(),
-                             afterBRElement.Offset());
+        htmlEditor->MoveNodeWithTransaction(*maybeSecondBRNode->AsContent(),
+                                            afterBRElement);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
   }
 
   // SetInterlinePosition(true) means we want the caret to stick to the
   // content on the "right".  We want the caret to stick to whatever is past
@@ -3425,21 +3424,30 @@ HTMLEditRules::MoveNodeSmart(nsIContent&
   if (NS_WARN_IF(!mHTMLEditor)) {
     return EditActionIgnored(NS_ERROR_UNEXPECTED);
   }
 
   RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
 
   // Check if this node can go into the destination node
   if (htmlEditor->CanContain(aDestElement, aNode)) {
-    // If it can, move it there
-    nsresult rv =
-      htmlEditor->MoveNode(&aNode, &aDestElement, *aInOutDestOffset);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return EditActionIgnored(rv);
+    // If it can, move it there.
+    if (*aInOutDestOffset == -1) {
+      nsresult rv =
+        htmlEditor->MoveNodeToEndWithTransaction(aNode, aDestElement);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return EditActionIgnored(rv);
+      }
+    } else {
+      EditorRawDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
+      nsresult rv =
+        htmlEditor->MoveNodeWithTransaction(aNode, pointToInsert);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return EditActionIgnored(rv);
+      }
     }
     if (*aInOutDestOffset != -1) {
       (*aInOutDestOffset)++;
     }
     // XXX Should we check if the node is actually moved in this case?
     return EditActionHandled();
   }
 
@@ -3732,18 +3740,20 @@ HTMLEditRules::WillMakeList(Selection* a
 
     if (HTMLEditUtils::IsList(curNode)) {
       // do we have a curList already?
       if (curList && !EditorUtils::IsDescendantOf(*curNode, *curList)) {
         // move all of our children into curList.  cheezy way to do it: move
         // whole list and then RemoveContainerWithTransaction() on the list.
         // ConvertListType first: that routine handles converting the list
         // item types, if needed.
-        rv = htmlEditor->MoveNode(curNode, curList, -1);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode, *curList);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         newBlock = ConvertListType(curNode->AsElement(), listType, itemType);
         if (NS_WARN_IF(!newBlock)) {
           return NS_ERROR_FAILURE;
         }
         rv = htmlEditor->RemoveBlockContainerWithTransaction(*newBlock);
         NS_ENSURE_SUCCESS(rv, rv);
       } else {
         // replace list with new list type
@@ -3779,36 +3789,40 @@ HTMLEditRules::WillMakeList(Selection* a
           EditorRawDOMPoint atParentOfCurNode(atCurNode.GetContainer());
           curList = htmlEditor->CreateNodeWithTransaction(*listType,
                                                           atParentOfCurNode);
           if (NS_WARN_IF(!curList)) {
             return NS_ERROR_FAILURE;
           }
         }
         // move list item to new list
-        rv = htmlEditor->MoveNode(curNode, curList, -1);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode, *curList);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         // convert list item type if needed
         if (!curNode->IsHTMLElement(itemType)) {
           newBlock =
             htmlEditor->ReplaceContainerWithTransaction(*curNode->AsElement(),
                                                         *itemType);
           if (NS_WARN_IF(!newBlock)) {
             return NS_ERROR_FAILURE;
           }
         }
       } else {
         // item is in right type of list.  But we might still have to move it.
         // and we might need to convert list item types.
         if (!curList) {
           curList = atCurNode.GetContainerAsElement();
         } else if (atCurNode.GetContainer() != curList) {
           // move list item to new list
-          rv = htmlEditor->MoveNode(curNode, curList, -1);
-          NS_ENSURE_SUCCESS(rv, rv);
+          rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode, *curList);
+          if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+          }
         }
         if (!curNode->IsHTMLElement(itemType)) {
           newBlock =
             htmlEditor->ReplaceContainerWithTransaction(*curNode->AsElement(),
                                                         *itemType);
           if (NS_WARN_IF(!newBlock)) {
             return NS_ERROR_FAILURE;
           }
@@ -3871,17 +3885,17 @@ HTMLEditRules::WillMakeList(Selection* a
     }
 
     // if curNode isn't a list item, we must wrap it in one
     nsCOMPtr<Element> listItem;
     if (!HTMLEditUtils::IsListItem(curNode)) {
       if (IsInlineNode(curNode) && prevListItem) {
         // this is a continuation of some inline nodes that belong together in
         // the same list item.  use prevListItem
-        rv = htmlEditor->MoveNode(curNode, prevListItem, -1);
+        rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode, *prevListItem);
         NS_ENSURE_SUCCESS(rv, rv);
       } else {
         // don't wrap li around a paragraph.  instead replace paragraph with li
         if (curNode->IsHTMLElement(nsGkAtoms::p)) {
           listItem =
             htmlEditor->ReplaceContainerWithTransaction(*curNode->AsElement(),
                                                         *itemType);
           if (NS_WARN_IF(!listItem)) {
@@ -3899,18 +3913,20 @@ HTMLEditRules::WillMakeList(Selection* a
       }
     } else {
       listItem = curNode->AsElement();
     }
 
     if (listItem) {
       // if we made a new list item, deal with it: tuck the listItem into the
       // end of the active list
-      rv = htmlEditor->MoveNode(listItem, curList, -1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->MoveNodeToEndWithTransaction(*listItem, *curList);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
 
   return NS_OK;
 }
 
 
 nsresult
@@ -4300,32 +4316,36 @@ HTMLEditRules::WillCSSIndent(Selection* 
       // We do this if the next element is a list, and the list is of the
       // same type (li/ol) as curNode was a part it.
       sibling = htmlEditor->GetNextHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
             sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
             sibling->NodeInfo()->NamespaceID()) {
-        rv = htmlEditor->MoveNode(curNode->AsContent(), sibling, 0);
+        rv = htmlEditor->MoveNodeWithTransaction(*curNode->AsContent(),
+                                                 EditorRawDOMPoint(sibling, 0));
         NS_ENSURE_SUCCESS(rv, rv);
         continue;
       }
 
       // Check for whether we should join a list that preceeds curNode.
       // We do this if the previous element is a list, and the list is of
       // the same type (li/ol) as curNode was a part of.
       sibling = htmlEditor->GetPriorHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
             sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
             sibling->NodeInfo()->NamespaceID()) {
-        rv = htmlEditor->MoveNode(curNode->AsContent(), sibling, -1);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                      *sibling);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         continue;
       }
 
       // check to see if curList is still appropriate.  Which it is if
       // curNode is still right after it in the same list.
       sibling = nullptr;
       if (curList) {
         sibling = htmlEditor->GetPriorHTMLSibling(curNode);
@@ -4347,20 +4367,21 @@ HTMLEditRules::WillCSSIndent(Selection* 
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
         mNewBlock = curList;
       }
       // tuck the node into the end of the active list
-      uint32_t listLen = curList->Length();
-      rv = htmlEditor->MoveNode(curNode->AsContent(), curList, listLen);
-      NS_ENSURE_SUCCESS(rv, rv);
-
+      rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                    *curList);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       continue;
     }
 
     // Not a list item.
 
     if (IsBlockNode(*curNode)) {
       ChangeIndentation(*curNode->AsElement(), Change::plus);
       curQuote = nullptr;
@@ -4387,19 +4408,21 @@ HTMLEditRules::WillCSSIndent(Selection* 
       }
       ChangeIndentation(*curQuote, Change::plus);
       // remember our new block for postprocessing
       mNewBlock = curQuote;
       // curQuote is now the correct thing to put curNode in
     }
 
     // tuck the node into the end of the active blockquote
-    uint32_t quoteLen = curQuote->Length();
-    rv = htmlEditor->MoveNode(curNode->AsContent(), curQuote, quoteLen);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                  *curQuote);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::WillHTMLIndent(Selection* aSelection,
                               bool* aCancel,
                               bool* aHandled)
@@ -4509,32 +4532,38 @@ HTMLEditRules::WillHTMLIndent(Selection*
       // We do this if the next element is a list, and the list is of the
       // same type (li/ol) as curNode was a part it.
       sibling = htmlEditor->GetNextHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
             sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
             sibling->NodeInfo()->NamespaceID()) {
-        rv = htmlEditor->MoveNode(curNode->AsContent(), sibling, 0);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->MoveNodeWithTransaction(*curNode->AsContent(),
+                                                 EditorRawDOMPoint(sibling, 0));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         continue;
       }
 
       // Check for whether we should join a list that preceeds curNode.
       // We do this if the previous element is a list, and the list is of
       // the same type (li/ol) as curNode was a part of.
       sibling = htmlEditor->GetPriorHTMLSibling(curNode);
       if (sibling && HTMLEditUtils::IsList(sibling) &&
           atCurNode.GetContainer()->NodeInfo()->NameAtom() ==
             sibling->NodeInfo()->NameAtom() &&
           atCurNode.GetContainer()->NodeInfo()->NamespaceID() ==
             sibling->NodeInfo()->NamespaceID()) {
-        rv = htmlEditor->MoveNode(curNode->AsContent(), sibling, -1);
-        NS_ENSURE_SUCCESS(rv, rv);
+        rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                      *sibling);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
         continue;
       }
 
       // check to see if curList is still appropriate.  Which it is if
       // curNode is still right after it in the same list.
       sibling = nullptr;
       if (curList) {
         sibling = htmlEditor->GetPriorHTMLSibling(curNode);
@@ -4556,18 +4585,21 @@ HTMLEditRules::WillHTMLIndent(Selection*
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
         mNewBlock = curList;
       }
       // tuck the node into the end of the active list
-      rv = htmlEditor->MoveNode(curNode->AsContent(), curList, -1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                    *curList);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       // forget curQuote, if any
       curQuote = nullptr;
 
       continue;
     }
 
     // Not a list item, use blockquote?
 
@@ -4606,18 +4638,20 @@ HTMLEditRules::WillHTMLIndent(Selection*
         curList =
           htmlEditor->CreateNodeWithTransaction(*containerName,
                                                 splitNodeResult.SplitPoint());
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
       }
 
-      rv = htmlEditor->MoveNode(listItem, curList, -1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->MoveNodeToEndWithTransaction(*listItem, *curList);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
 
       // remember we indented this li
       indentedLI = listItem;
 
       continue;
     }
 
     // need to make a blockquote to put things in if we haven't already,
@@ -4648,18 +4682,21 @@ HTMLEditRules::WillHTMLIndent(Selection*
         return NS_ERROR_FAILURE;
       }
       // remember our new block for postprocessing
       mNewBlock = curQuote;
       // curQuote is now the correct thing to put curNode in
     }
 
     // tuck the node into the end of the active blockquote
-    rv = htmlEditor->MoveNode(curNode->AsContent(), curQuote, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                  *curQuote);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     // forget curList, if any
     curList = nullptr;
   }
   return NS_OK;
 }
 
 
 nsresult
@@ -4844,19 +4881,22 @@ HTMLEditRules::WillOutdent(Selection& aS
           while (child) {
             if (HTMLEditUtils::IsListItem(child)) {
               rv = PopListItem(*child);
               NS_ENSURE_SUCCESS(rv, rv);
             } else if (HTMLEditUtils::IsList(child)) {
               // We have an embedded list, so move it out from under the parent
               // list. Be sure to put it after the parent list because this
               // loop iterates backwards through the parent's list of children.
-
-              rv = htmlEditor->MoveNode(child, curParent, offset + 1);
-              NS_ENSURE_SUCCESS(rv, rv);
+              EditorRawDOMPoint afterCurrentList(curParent, offset + 1);
+              rv = htmlEditor->MoveNodeWithTransaction(*child,
+                                                       afterCurrentList);
+              if (NS_WARN_IF(NS_FAILED(rv))) {
+                return rv;
+              }
             } else {
               // Delete any non-list items for now
               rv = htmlEditor->DeleteNodeWithTransaction(*child);
               if (NS_WARN_IF(NS_FAILED(rv))) {
                 return rv;
               }
             }
             child = curNode->GetLastChild();
@@ -5420,18 +5460,21 @@ HTMLEditRules::WillAlign(Selection& aSel
       }
       // Remember our new block for postprocessing
       mNewBlock = curDiv;
       // Set up the alignment on the div
       rv = AlignBlock(*curDiv, aAlignType, ContentsOnly::yes);
     }
 
     // Tuck the node into the end of the active div
-    rv = htmlEditor->MoveNode(curNode->AsContent(), curDiv, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                  *curDiv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 
 /**
  * AlignInnerBlocks() aligns inside table cells or list items.
@@ -5497,17 +5540,19 @@ HTMLEditRules::AlignBlockContents(nsINod
   nsresult rv =
     htmlEditor->SetAttributeOrEquivalent(divElem, nsGkAtoms::align,
                                          aAlignType, false);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   // tuck the children into the end of the active div
   while (lastChild && (lastChild != divElem)) {
-    nsresult rv = htmlEditor->MoveNode(lastChild, divElem, 0);
+    nsresult rv =
+      htmlEditor->MoveNodeWithTransaction(*lastChild,
+                                          EditorRawDOMPoint(divElem, 0));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     lastChild = htmlEditor->GetLastEditableChild(aNode);
   }
   return NS_OK;
 }
 
@@ -6800,19 +6845,20 @@ HTMLEditRules::BustUpInlinesAtBRs(
       // beginning of inline container, in which case
       // SplitNodeDeepWithTransaction() would not actually split anything.
       aOutArrayOfNodes.AppendElement(*splitNodeResult.GetPreviousNode());
     }
 
     // Move break outside of container and also put in node list
     EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
     nsresult rv =
-      htmlEditor->MoveNode(brNode->AsContent(), atNextNode.GetContainer(),
-                           atNextNode.Offset());
-    NS_ENSURE_SUCCESS(rv, rv);
+      htmlEditor->MoveNodeWithTransaction(*brNode->AsContent(), atNextNode);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     aOutArrayOfNodes.AppendElement(*brNode);
 
     nextNode = splitNodeResult.GetNextNode();
   }
 
   // Now tack on remaining next node.
   aOutArrayOfNodes.AppendElement(*nextNode);
 
@@ -7405,22 +7451,26 @@ HTMLEditRules::ReturnInListItem(Selectio
     // Are we in a sublist?
     EditorRawDOMPoint atNextSiblingOfLeftList(leftListNode);
     DebugOnly<bool> advanced = atNextSiblingOfLeftList.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
       "Failed to advance offset after the right list node");
     if (HTMLEditUtils::IsList(atNextSiblingOfLeftList.GetContainer())) {
       // If so, move item out of this list and into the grandparent list
       nsresult rv =
-        htmlEditor->MoveNode(&aListItem,
-                             atNextSiblingOfLeftList.GetContainer(),
-                             atNextSiblingOfLeftList.Offset());
-      NS_ENSURE_SUCCESS(rv, rv);
-      rv = aSelection.Collapse(&aListItem, 0);
-      NS_ENSURE_SUCCESS(rv, rv);
+        htmlEditor->MoveNodeWithTransaction(aListItem,
+                                            atNextSiblingOfLeftList);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      ErrorResult error;
+      aSelection.Collapse(RawRangeBoundary(&aListItem, 0), error);
+      if (NS_WARN_IF(error.Failed())) {
+        return error.StealNSResult();
+      }
     } else {
       // Otherwise kill this item
       nsresult rv = htmlEditor->DeleteNodeWithTransaction(aListItem);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       // Time to insert a paragraph
@@ -7628,18 +7678,22 @@ HTMLEditRules::MakeBlockquote(nsTArray<O
       if (NS_WARN_IF(!curBlock)) {
         return NS_ERROR_FAILURE;
       }
       // remember our new block for postprocessing
       mNewBlock = curBlock;
       // note: doesn't matter if we set mNewBlock multiple times.
     }
 
-    nsresult rv = htmlEditor->MoveNode(curNode->AsContent(), curBlock, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv =
+      htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                               *curBlock);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
   return NS_OK;
 }
 
 /**
  * RemoveBlockStyle() makes the nodes have no special block type.
  */
 nsresult
@@ -7837,18 +7891,22 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<
         htmlEditor->CreateNodeWithTransaction(aBlockTag,
                                               splitNodeResult.SplitPoint());
       if (NS_WARN_IF(!curBlock)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       mNewBlock = curBlock;
       // Note: doesn't matter if we set mNewBlock multiple times.
-      nsresult rv = htmlEditor->MoveNode(curNode->AsContent(), curBlock, -1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv =
+        htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                 *curBlock);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       continue;
     }
 
     if (IsInlineNode(curNode)) {
       // If curNode is inline, pull it into curBlock.  Note: it's assumed that
       // consecutive inline nodes in aNodeArray are actually members of the
       // same block parent.  This happens to be true now as a side effect of
       // how aNodeArray is contructed, but some additional logic should be
@@ -7884,18 +7942,22 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<
         // This is possible due to mutation events, let's not assert
         return NS_ERROR_UNEXPECTED;
       }
 
       // XXX If curNode is a br, replace it with a return if going to <pre>
 
       // This is a continuation of some inline nodes that belong together in
       // the same block item.  Use curBlock.
-      nsresult rv = htmlEditor->MoveNode(curNode->AsContent(), curBlock, -1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsresult rv =
+        htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                 *curBlock);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
   return NS_OK;
 }
 
 template<typename PT, typename CT>
 SplitNodeResult
 HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction(
@@ -7972,17 +8034,20 @@ HTMLEditRules::JoinNearestEditableNodesW
   nsCOMPtr<nsINode> rightParent = aNodeRight.GetParentNode();
 
   // If they don't have the same parent, first move the right node to after the
   // left one
   if (parent != rightParent) {
     if (NS_WARN_IF(!mHTMLEditor)) {
       return EditorDOMPoint();
     }
-    nsresult rv = mHTMLEditor->MoveNode(&aNodeRight, parent, parOffset);
+    nsresult rv =
+      mHTMLEditor->MoveNodeWithTransaction(aNodeRight,
+                                           EditorRawDOMPoint(parent,
+                                                             parOffset));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return EditorDOMPoint();
     }
   }
 
   EditorDOMPoint ret(&aNodeRight, aNodeLeft.Length());
 
   // Separate join rules for differing blocks
@@ -8887,19 +8952,20 @@ HTMLEditRules::PopListItem(nsIContent& a
   // Enter twice at a list item breaks the parent list node.
   if (!bIsFirstListItem) {
     DebugOnly<bool> advanced = pointToInsertListItem.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
       "Failed to advance offset to right list node");
   }
 
   nsresult rv =
-    mHTMLEditor->MoveNode(&aListItem, pointToInsertListItem.GetContainer(),
-                          pointToInsertListItem.Offset());
-  NS_ENSURE_SUCCESS(rv, rv);
+    mHTMLEditor->MoveNodeWithTransaction(aListItem, pointToInsertListItem);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   // unwrap list item contents if they are no longer in a list
   if (!HTMLEditUtils::IsList(pointToInsertListItem.GetContainer()) &&
       HTMLEditUtils::IsListItem(&aListItem)) {
     NS_ENSURE_STATE(mHTMLEditor);
     rv = mHTMLEditor->RemoveBlockContainerWithTransaction(
                         *aListItem.AsElement());
     NS_ENSURE_SUCCESS(rv, rv);
@@ -9630,18 +9696,21 @@ HTMLEditRules::WillAbsolutePosition(Sele
                                                 atEndOfCurPositionedDiv);
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in.  Remember our
         // new block for postprocessing.
       }
       // Tuck the node into the end of the active list
-      rv = htmlEditor->MoveNode(curNode->AsContent(), curList, -1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                    *curList);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       continue;
     }
 
     // Not a list item, use blockquote?  If we are inside a list item, we
     // don't want to blockquote, we want to sublist the list item.  We may
     // have several nodes listed in the array of nodes to act on, that are in
     // the same list item.  Since we only want to indent that li once, we
     // must keep track of the most recent indented list item, and not indent
@@ -9685,18 +9754,20 @@ HTMLEditRules::WillAbsolutePosition(Sele
         atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
         curList =
           htmlEditor->CreateNodeWithTransaction(*containerName,
                                                 atEndOfCurPositionedDiv);
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
       }
-      rv = htmlEditor->MoveNode(listItem, curList, -1);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = htmlEditor->MoveNodeToEndWithTransaction(*listItem, *curList);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
       // Remember we indented this li
       indentedLI = listItem;
       continue;
     }
 
     // Need to make a div to put things in if we haven't already
     if (!curPositionedDiv) {
       if (curNode->IsHTMLElement(nsGkAtoms::div)) {
@@ -9718,17 +9789,18 @@ HTMLEditRules::WillAbsolutePosition(Sele
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       mNewBlock = curPositionedDiv;
       // curPositionedDiv is now the correct thing to put curNode in
     }
 
     // Tuck the node into the end of the active blockquote
-    rv = htmlEditor->MoveNode(curNode->AsContent(), curPositionedDiv, -1);
+    rv = htmlEditor->MoveNodeToEndWithTransaction(*curNode->AsContent(),
+                                                  *curPositionedDiv);
     NS_ENSURE_SUCCESS(rv, rv);
     // Forget curList, if any
     curList = nullptr;
   }
   return NS_OK;
 }
 
 nsresult
--- a/editor/libeditor/HTMLStyleEditor.cpp
+++ b/editor/libeditor/HTMLStyleEditor.cpp
@@ -309,22 +309,23 @@ HTMLEditor::SetInlinePropertyOnTextNode(
     Unused << newLeftNode;
   }
 
   if (aAttribute) {
     // Look for siblings that are correct type of node
     nsIContent* sibling = GetPriorHTMLSibling(textNodeForTheRange);
     if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
       // Previous sib is already right kind of inline node; slide this over
-      return MoveNode(textNodeForTheRange, sibling, -1);
+      return MoveNodeToEndWithTransaction(*textNodeForTheRange, *sibling);
     }
     sibling = GetNextHTMLSibling(textNodeForTheRange);
     if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
       // Following sib is already right kind of inline node; slide this over
-      return MoveNode(textNodeForTheRange, sibling, 0);
+      return MoveNodeWithTransaction(*textNodeForTheRange,
+                                     EditorRawDOMPoint(sibling, 0));
     }
   }
 
   // Reparent the node inside inline node with appropriate {attribute,value}
   return SetInlinePropertyOnNode(*textNodeForTheRange,
                                  aProperty, aAttribute, aValue);
 }
 
@@ -358,29 +359,34 @@ HTMLEditor::SetInlinePropertyOnNodeImpl(
     }
     return NS_OK;
   }
 
   // First check if there's an adjacent sibling we can put our node into.
   nsCOMPtr<nsIContent> previousSibling = GetPriorHTMLSibling(&aNode);
   nsCOMPtr<nsIContent> nextSibling = GetNextHTMLSibling(&aNode);
   if (IsSimpleModifiableNode(previousSibling, &aProperty, aAttribute, &aValue)) {
-    nsresult rv = MoveNode(&aNode, previousSibling, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv = MoveNodeToEndWithTransaction(aNode, *previousSibling);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
       rv = JoinNodesWithTransaction(*previousSibling, *nextSibling);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
     return NS_OK;
   }
   if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
-    nsresult rv = MoveNode(&aNode, nextSibling, 0);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv =
+      MoveNodeWithTransaction(aNode, EditorRawDOMPoint(nextSibling, 0));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     return NS_OK;
   }
 
   // Don't need to do anything if property already set on node
   if (CSSEditUtils::IsCSSEditableProperty(&aNode, &aProperty, aAttribute)) {
     if (CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
           &aNode, &aProperty, aAttribute, aValue, CSSEditUtils::eComputed)) {
       return NS_OK;
@@ -647,18 +653,21 @@ HTMLEditor::ClearStyle(nsCOMPtr<nsINode>
     nsCOMPtr<nsINode> newSelParent = GetLeftmostChild(leftNode);
     if (!newSelParent) {
       newSelParent = leftNode;
     }
     // If rightNode starts with a br, suck it out of right node and into
     // leftNode.  This is so we you don't revert back to the previous style
     // if you happen to click at the end of a line.
     if (savedBR) {
-      rv = MoveNode(savedBR, newSelParent, 0);
-      NS_ENSURE_SUCCESS(rv, rv);
+      rv = MoveNodeWithTransaction(*savedBR,
+                                   EditorRawDOMPoint(newSelParent, 0));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
     // remove the style on this new hierarchy
     int32_t newSelOffset = 0;
     {
       // Track the point at the new hierarchy.  This is so we can know where
       // to put the selection after we call RemoveStyleInside().
       // RemoveStyleInside() could remove any and all of those nodes, so I
       // have to use the range tracking system to find the right spot to put
@@ -1497,25 +1506,30 @@ HTMLEditor::RelativeFontChangeOnTextNode
   }
 
   // Look for siblings that are correct type of node
   nsAtom* nodeType = aDir == FontSize::incr ? nsGkAtoms::big
                                              : nsGkAtoms::small;
   nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(textNodeForTheRange);
   if (sibling && sibling->IsHTMLElement(nodeType)) {
     // Previous sib is already right kind of inline node; slide this over
-    nsresult rv = MoveNode(textNodeForTheRange, sibling, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv = MoveNodeToEndWithTransaction(*textNodeForTheRange, *sibling);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     return NS_OK;
   }
   sibling = GetNextHTMLSibling(textNodeForTheRange);
   if (sibling && sibling->IsHTMLElement(nodeType)) {
     // Following sib is already right kind of inline node; slide this over
-    nsresult rv = MoveNode(textNodeForTheRange, sibling, 0);
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsresult rv = MoveNodeWithTransaction(*textNodeForTheRange,
+                                          EditorRawDOMPoint(sibling, 0));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
     return NS_OK;
   }
 
   // Else reparent the node inside font node with appropriate relative size
   nsCOMPtr<Element> newElement =
     InsertContainerAbove(textNodeForTheRange, nodeType);
   NS_ENSURE_STATE(newElement);
 
@@ -1608,23 +1622,23 @@ HTMLEditor::RelativeFontChangeOnNode(int
     NS_ENSURE_SUCCESS(rv, rv);
 
     // ok, chuck it in.
     // first look at siblings of aNode for matching bigs or smalls.
     // if we find one, move aNode into it.
     nsIContent* sibling = GetPriorHTMLSibling(aNode);
     if (sibling && sibling->IsHTMLElement(atom)) {
       // previous sib is already right kind of inline node; slide this over into it
-      return MoveNode(aNode, sibling, -1);
+      return MoveNodeToEndWithTransaction(*aNode, *sibling);
     }
 
     sibling = GetNextHTMLSibling(aNode);
     if (sibling && sibling->IsHTMLElement(atom)) {
       // following sib is already right kind of inline node; slide this over into it
-      return MoveNode(aNode, sibling, 0);
+      return MoveNodeWithTransaction(*aNode, EditorRawDOMPoint(sibling, 0));
     }
 
     // else insert it above aNode
     nsCOMPtr<Element> newElement = InsertContainerAbove(aNode, atom);
     NS_ENSURE_STATE(newElement);
 
     return NS_OK;
   }
--- a/editor/libeditor/SelectionState.h
+++ b/editor/libeditor/SelectionState.h
@@ -310,41 +310,48 @@ public:
 
 /**
  * Another helper class for SelectionState.  Stack based class for doing
  * Will/DidMoveNode()
  */
 
 class MOZ_STACK_CLASS AutoMoveNodeSelNotify final
 {
-private:
-  RangeUpdater& mRangeUpdater;
-  nsINode* mOldParent;
-  nsINode* mNewParent;
-  int32_t mOldOffset;
-  int32_t mNewOffset;
-
 public:
   AutoMoveNodeSelNotify(RangeUpdater& aRangeUpdater,
-                        nsINode* aOldParent,
-                        int32_t aOldOffset,
-                        nsINode* aNewParent,
-                        int32_t aNewOffset)
+                        const EditorDOMPoint& aOldPoint,
+                        const EditorDOMPoint& aNewPoint)
     : mRangeUpdater(aRangeUpdater)
-    , mOldParent(aOldParent)
-    , mNewParent(aNewParent)
-    , mOldOffset(aOldOffset)
-    , mNewOffset(aNewOffset)
+    , mOldParent(aOldPoint.GetContainer())
+    , mNewParent(aNewPoint.GetContainer())
+    , mOldOffset(aOldPoint.Offset())
+    , mNewOffset(aNewPoint.Offset())
   {
-    MOZ_ASSERT(aOldParent);
-    MOZ_ASSERT(aNewParent);
+    MOZ_ASSERT(mOldParent);
+    MOZ_ASSERT(mNewParent);
     mRangeUpdater.WillMoveNode();
   }
 
   ~AutoMoveNodeSelNotify()
   {
     mRangeUpdater.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
   }
+
+  EditorRawDOMPoint ComputeInsertionPoint() const
+  {
+    if (mOldParent == mNewParent &&
+        mOldOffset < mNewOffset) {
+      return EditorRawDOMPoint(mNewParent, mNewOffset - 1);
+    }
+    return EditorRawDOMPoint(mNewParent, mNewOffset);
+  }
+
+private:
+  RangeUpdater& mRangeUpdater;
+  nsINode* mOldParent;
+  nsINode* mNewParent;
+  uint32_t mOldOffset;
+  uint32_t mNewOffset;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef mozilla_SelectionState_h