Bug 1408125 - part 4: Redesign HTMLEditor::InsertNodeAtPoint() with EditorRawDOMPoint r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 28 Nov 2017 22:28:07 +0900
changeset 710427 cd51d1fe8ac3b118e8c38b415ae94de8374d2c26
parent 710426 83fa9fd4c7b083941e6d01bbdc894556c5356563
child 710428 68f119a84599b19d560132a0ad2de0b4a43aac54
push id92823
push usermasayuki@d-toybox.com
push dateSun, 10 Dec 2017 02:40:55 +0000
reviewersm_kato
bugs1408125
milestone59.0a1
Bug 1408125 - part 4: Redesign HTMLEditor::InsertNodeAtPoint() with EditorRawDOMPoint r?m_kato HTMLEditor::InsertNodeAtPoint() should take |const EditorRawDOMPoint&| as an argument which specifies point to insert. Additionally, it should take |EditorDOMPoint*| to return the next point of actual insertion point. Additionally, this patch renames it to InsertNodeAtProperAncestor() for explaining what it will do. MozReview-Commit-ID: HYUzSlyPxAd
editor/libeditor/EditorDOMPoint.h
editor/libeditor/HTMLEditUtils.cpp
editor/libeditor/HTMLEditUtils.h
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/HTMLEditorDataTransfer.cpp
editor/libeditor/TextEditUtils.cpp
editor/libeditor/TextEditUtils.h
--- a/editor/libeditor/EditorDOMPoint.h
+++ b/editor/libeditor/EditorDOMPoint.h
@@ -114,16 +114,26 @@ public:
   {
     mIsChildInitialized = aPointedNode && mChild;
     NS_WARNING_ASSERTION(IsSet(),
       "The child is nullptr or doesn't have its parent");
     NS_WARNING_ASSERTION(mChild && mChild->GetParentNode() == mParent,
       "Initializing RangeBoundary with invalid value");
   }
 
+  explicit EditorDOMPointBase(nsIDOMNode* aDOMPointedNode)
+    : mIsChildInitialized(false)
+  {
+    nsCOMPtr<nsIContent> child = do_QueryInterface(aDOMPointedNode);
+    if (NS_WARN_IF(!child)) {
+      return;
+    }
+    this->Set(child);
+  }
+
   EditorDOMPointBase(nsINode* aContainer,
                      nsIContent* aPointedNode,
                      int32_t aOffset)
     : mParent(aContainer)
     , mChild(aPointedNode)
     , mOffset(mozilla::Some(aOffset))
     , mIsChildInitialized(true)
   {
--- a/editor/libeditor/HTMLEditUtils.cpp
+++ b/editor/libeditor/HTMLEditUtils.cpp
@@ -219,16 +219,22 @@ HTMLEditUtils::IsTable(nsINode* aNode)
  * IsTableRow() returns true if aNode is an html tr.
  */
 bool
 HTMLEditUtils::IsTableRow(nsIDOMNode* aNode)
 {
   return EditorBase::NodeIsType(aNode, nsGkAtoms::tr);
 }
 
+bool
+HTMLEditUtils::IsTableRow(nsINode* aNode)
+{
+  return aNode && aNode->IsHTMLElement(nsGkAtoms::tr);
+}
+
 /**
  * IsTableCell() returns true if aNode is an html td or th.
  */
 bool
 HTMLEditUtils::IsTableCell(nsIDOMNode* aNode)
 {
   MOZ_ASSERT(aNode);
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
@@ -304,16 +310,22 @@ HTMLEditUtils::IsBlockquote(nsIDOMNode* 
  * IsPre() returns true if aNode is an html pre node.
  */
 bool
 HTMLEditUtils::IsPre(nsIDOMNode* aNode)
 {
   return EditorBase::NodeIsType(aNode, nsGkAtoms::pre);
 }
 
+bool
+HTMLEditUtils::IsPre(nsINode* aNode)
+{
+  return aNode && aNode->IsHTMLElement(nsGkAtoms::pre);
+}
+
 /**
  * IsImage() returns true if aNode is an html image node.
  */
 bool
 HTMLEditUtils::IsImage(nsINode* aNode)
 {
   return aNode && aNode->IsHTMLElement(nsGkAtoms::img);
 }
--- a/editor/libeditor/HTMLEditUtils.h
+++ b/editor/libeditor/HTMLEditUtils.h
@@ -24,29 +24,31 @@ public:
   static bool IsHeader(nsINode& aNode);
   static bool IsHeader(nsIDOMNode* aNode);
   static bool IsParagraph(nsIDOMNode* aNode);
   static bool IsListItem(nsINode* aNode);
   static bool IsListItem(nsIDOMNode* aNode);
   static bool IsTable(nsIDOMNode* aNode);
   static bool IsTable(nsINode* aNode);
   static bool IsTableRow(nsIDOMNode* aNode);
+  static bool IsTableRow(nsINode* aNode);
   static bool IsTableElement(nsINode* aNode);
   static bool IsTableElement(nsIDOMNode* aNode);
   static bool IsTableElementButNotTable(nsINode* aNode);
   static bool IsTableElementButNotTable(nsIDOMNode* aNode);
   static bool IsTableCell(nsINode* node);
   static bool IsTableCell(nsIDOMNode* aNode);
   static bool IsTableCellOrCaption(nsINode& aNode);
   static bool IsList(nsINode* aNode);
   static bool IsList(nsIDOMNode* aNode);
   static bool IsOrderedList(nsIDOMNode* aNode);
   static bool IsUnorderedList(nsIDOMNode* aNode);
   static bool IsBlockquote(nsIDOMNode* aNode);
   static bool IsPre(nsIDOMNode* aNode);
+  static bool IsPre(nsINode* aNode);
   static bool IsAnchor(nsIDOMNode* aNode);
   static bool IsImage(nsINode* aNode);
   static bool IsImage(nsIDOMNode* aNode);
   static bool IsLink(nsIDOMNode* aNode);
   static bool IsLink(nsINode* aNode);
   static bool IsNamedAnchor(nsINode* aNode);
   static bool IsNamedAnchor(nsIDOMNode* aNode);
   static bool IsDiv(nsIDOMNode* aNode);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1494,137 +1494,114 @@ HTMLEditor::InsertElementAtSelection(nsI
     if (parentSelectedNode) {
       int32_t offsetForInsert = selection->AnchorOffset();
       // Adjust position based on the node we are going to insert.
       nsCOMPtr<nsIDOMNode> parentSelectedDOMNode =
         GetAsDOMNode(parentSelectedNode);
       NormalizeEOLInsertPosition(element, address_of(parentSelectedDOMNode),
                                  &offsetForInsert);
 
-      rv = InsertNodeAtPoint(node, address_of(parentSelectedDOMNode),
-                             &offsetForInsert,
-                             SplitAtEdges::eAllowToCreateEmptyContainer);
-      NS_ENSURE_SUCCESS(rv, rv);
+      EditorDOMPoint insertedPoint =
+        InsertNodeIntoProperAncestor(
+          *element, EditorRawDOMPoint(parentSelectedDOMNode, offsetForInsert),
+          SplitAtEdges::eAllowToCreateEmptyContainer);
+      if (NS_WARN_IF(!insertedPoint.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
       // Set caret after element, but check for special case
       //  of inserting table-related elements: set in first cell instead
       if (!SetCaretInTableCell(aElement)) {
         rv = SetCaretAfterElement(aElement);
         NS_ENSURE_SUCCESS(rv, rv);
       }
       // check for inserting a whole table at the end of a block. If so insert
       // a br after it.
       if (HTMLEditUtils::IsTable(node) &&
           IsLastEditableChild(element)) {
+        DebugOnly<bool> advanced = insertedPoint.AdvanceOffset();
+        NS_WARNING_ASSERTION(advanced,
+          "Failed to advance offset from inserted point");
         // Collapse selection to the new <br> element node after creating it.
         RefPtr<Element> newBRElement =
-          CreateBRImpl(*selection,
-                       EditorRawDOMPoint(parentSelectedDOMNode,
-                                         offsetForInsert + 1),
-                       ePrevious);
+          CreateBRImpl(*selection, insertedPoint.AsRaw(), ePrevious);
         if (NS_WARN_IF(!newBRElement)) {
           return NS_ERROR_FAILURE;
         }
       }
     }
   }
   rv = rules->DidDoAction(selection, &ruleInfo, rv);
   return rv;
 }
 
-
-/**
- * InsertNodeAtPoint() attempts to insert aNode into the document, at a point
- * specified by {*ioParent,*ioOffset}.  Checks with strict dtd to see if
- * containment is allowed.  If not allowed, will attempt to find a parent in
- * the parent hierarchy of *ioParent that will accept aNode as a child.  If
- * such a parent is found, will split the document tree from
- * {*ioParent,*ioOffset} up to parent, and then insert aNode.
- * ioParent & ioOffset are then adjusted to point to the actual location that
- * aNode was inserted at.  aNoEmptyNodes specifies if the splitting process
- * is allowed to reslt in empty nodes.  ioChildAtOffset, if provided, is the
- * child node at offset if ioParent is non-null, and the function will update
- * *ioChildAtOffset upon returning.
- *
- * @param aNode             Node to insert.
- * @param ioParent          Insertion parent.
- * @param ioOffset          Insertion offset.
- * @param aSplitAtEdges     Splitting can result in empty nodes?
- * @param ioChildAtOffset   Child node at insertion offset (optional).
- */
-nsresult
-HTMLEditor::InsertNodeAtPoint(nsIDOMNode* aNode,
-                              nsCOMPtr<nsIDOMNode>* ioParent,
-                              int32_t* ioOffset,
-                              SplitAtEdges aSplitAtEdges,
-                              nsCOMPtr<nsIDOMNode>* ioChildAtOffset)
+EditorDOMPoint
+HTMLEditor::InsertNodeIntoProperAncestor(
+              nsIContent& aNode,
+              const EditorRawDOMPoint& aPointToInsert,
+              SplitAtEdges aSplitAtEdges)
 {
-  nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
-  NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE(ioParent, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE(*ioParent, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE(ioOffset, NS_ERROR_NULL_POINTER);
-  bool isDocumentFragment = false;
-  if (ioChildAtOffset) {
-    *ioChildAtOffset = aNode;
-    uint16_t nodeType = 0;
-    if (NS_SUCCEEDED(aNode->GetNodeType(&nodeType)) &&
-        nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
-      // For document fragments, we can't return aNode itself in
-      // *ioChildAtOffset, so we have to find out the inserted child after
-      // the insertion is successfully finished.
-      isDocumentFragment = true;
+  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
+    return EditorDOMPoint();
+  }
+  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
+
+  // Search up the parent chain to find a suitable container.
+  EditorDOMPoint pointToInsert(aPointToInsert);
+  MOZ_ASSERT(pointToInsert.IsSet());
+  while (!CanContain(*pointToInsert.Container(), aNode)) {
+    // If the current parent is a root (body or table element)
+    // then go no further - we can't insert.
+    if (pointToInsert.Container()->IsHTMLElement(nsGkAtoms::body) ||
+        HTMLEditUtils::IsTableElement(pointToInsert.Container())) {
+      return EditorDOMPoint();
     }
-  }
-
-  nsCOMPtr<nsIContent> parent = do_QueryInterface(*ioParent);
-  NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
-  nsCOMPtr<nsIContent> topChild = parent;
-  nsCOMPtr<nsIContent> origParent = parent;
-
-  // Search up the parent chain to find a suitable container
-  while (!CanContain(*parent, *node)) {
-    // If the current parent is a root (body or table element)
-    // then go no further - we can't insert
-    if (parent->IsHTMLElement(nsGkAtoms::body) ||
-        HTMLEditUtils::IsTableElement(parent)) {
-      return NS_ERROR_FAILURE;
+
+    // Get the next point.
+    pointToInsert.Set(pointToInsert.Container());
+    if (NS_WARN_IF(!pointToInsert.IsSet())) {
+      return EditorDOMPoint();
     }
-    // Get the next parent
-    NS_ENSURE_TRUE(parent->GetParentNode(), NS_ERROR_FAILURE);
-    if (!IsEditable(parent->GetParentNode())) {
+
+    if (!IsEditable(pointToInsert.Container())) {
       // There's no suitable place to put the node in this editing host.  Maybe
       // someone is trying to put block content in a span.  So just put it
       // where we were originally asked.
-      parent = topChild = origParent;
+      pointToInsert = aPointToInsert;
       break;
     }
-    topChild = parent;
-    parent = parent->GetParent();
-  }
-  if (parent != topChild) {
+  }
+
+  if (pointToInsert != aPointToInsert) {
     // We need to split some levels above the original selection parent.
+    MOZ_ASSERT(pointToInsert.GetChildAtOffset());
     SplitNodeResult splitNodeResult =
-      SplitNodeDeep(*topChild, EditorRawDOMPoint(origParent, *ioOffset),
-                    aSplitAtEdges);
+      SplitNodeDeep(*pointToInsert.GetChildAtOffset(),
+                    aPointToInsert, aSplitAtEdges);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
-      return splitNodeResult.Rv();
+      return EditorDOMPoint();
     }
-    EditorRawDOMPoint splitPoint(splitNodeResult.SplitPoint());
-    *ioParent = GetAsDOMNode(splitPoint.Container());
-    *ioOffset = splitPoint.Offset();
-    if (ioChildAtOffset) {
-      *ioChildAtOffset = GetAsDOMNode(splitPoint.GetChildAtOffset());
+    pointToInsert = splitNodeResult.SplitPoint();
+    MOZ_ASSERT(pointToInsert.IsSet());
+  }
+
+  {
+    // After inserting a node, pointToInsert will refer next sibling of
+    // the new node but keep referring the new node's offset.
+    // This method's result should be the point at insertion, it's useful
+    // even if the new node is moved by mutation observer immediately.
+    // So, let's lock only the offset and child node should be recomputed
+    // when it's necessary.
+    AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
+    // Now we can insert the new node.
+    nsresult rv = InsertNode(aNode, pointToInsert.AsRaw());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return EditorDOMPoint();
     }
   }
-  // Now we can insert the new node
-  nsresult rv = InsertNode(*node, EditorRawDOMPoint(parent, *ioOffset));
-  if (isDocumentFragment) {
-    *ioChildAtOffset = do_QueryInterface(parent->GetChildAt(*ioOffset));
-  }
-  return rv;
+  return pointToInsert;
 }
 
 NS_IMETHODIMP
 HTMLEditor::SelectElement(nsIDOMElement* aElement)
 {
   nsCOMPtr<Element> element = do_QueryInterface(aElement);
   NS_ENSURE_STATE(element || !aElement);
 
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -333,21 +333,37 @@ public:
 
   // nsICSSLoaderObserver
   NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet,
                               bool aWasAlternate, nsresult aStatus) override;
 
   // Utility Routines, not part of public API
   NS_IMETHOD TypedText(const nsAString& aString,
                        ETypingAction aAction) override;
-  nsresult InsertNodeAtPoint(nsIDOMNode* aNode,
-                             nsCOMPtr<nsIDOMNode>* ioParent,
-                             int32_t* ioOffset,
-                             SplitAtEdges aSplitAtEdges,
-                             nsCOMPtr<nsIDOMNode>* ioChildAtOffset = nullptr);
+
+  /**
+   * InsertNodeIntoProperAncestor() attempts to insert aNode into the document,
+   * at aPointToInsert.  Checks with strict dtd to see if containment is
+   * allowed.  If not allowed, will attempt to find a parent in the parent
+   * hierarchy of aPointToInsert.Container() that will accept aNode as a child.
+   * If such a parent is found, will split the document tree from aPointToInsert
+   * up to parent, and then insert aNode. aPointToInsert is then adjusted to
+   * point to the actual location that aNode was inserted at.  aSplitAtEdges
+   * specifies if the splitting process is allowed to result in empty nodes.
+   *
+   * @param aNode             Node to insert.
+   * @param aPointToInsert    Insertion point.
+   * @param aSplitAtEdges     Splitting can result in empty nodes?
+   * @return                  Returns inserted point if succeeded.
+   *                          Otherwise, the result is not set.
+   */
+  EditorDOMPoint
+  InsertNodeIntoProperAncestor(nsIContent& aNode,
+                               const EditorRawDOMPoint& aPointToInsert,
+                               SplitAtEdges aSplitAtEdges);
 
   /**
    * Use this to assure that selection is set after attribute nodes when
    * trying to collapse selection at begining of a block node
    * e.g., when setting at beginning of a table cell
    * This will stop at a table, however, since we don't want to
    * "drill down" into nested tables.
    * @param aSelection      Optional. If null, we get current selection.
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -425,260 +425,270 @@ HTMLEditor::DoInsertHTMLWithContext(cons
 
     // don't orphan partial list or table structure
     if (highWaterMark >= 0) {
       ReplaceOrphanedStructure(StartOrEnd::end, nodeList,
                                endListAndTableArray, highWaterMark);
     }
 
     // Loop over the node list and paste the nodes:
-    nsCOMPtr<nsIDOMNode> parentBlock, lastInsertNode, insertedContextParent;
+    nsCOMPtr<nsIDOMNode> parentBlock;
     nsCOMPtr<nsINode> parentNodeNode = do_QueryInterface(parentNode);
     NS_ENSURE_STATE(parentNodeNode || !parentNode);
     if (IsBlockNode(parentNodeNode)) {
       parentBlock = parentNode;
     } else if (parentNodeNode) {
       parentBlock = GetAsDOMNode(GetBlockNodeParent(parentNodeNode));
     }
 
-    int32_t listCount = nodeList.Length();
-    for (int32_t j = 0; j < listCount; j++) {
-      bool bDidInsert = false;
-      nsCOMPtr<nsIDOMNode> curNode = nodeList[j]->AsDOMNode();
-
-      NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
-      NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
-      NS_ENSURE_TRUE(!TextEditUtils::IsBody(curNode), NS_ERROR_FAILURE);
+    nsCOMPtr<nsIContent> lastInsertNode;
+    nsCOMPtr<nsINode> insertedContextParent;
+    EditorDOMPoint pointToInsert(parentNodeNode, offsetOfNewNode);
+    for (OwningNonNull<nsINode>& curNode : nodeList) {
+      if (NS_WARN_IF(curNode == fragmentAsNodeNode) ||
+          NS_WARN_IF(TextEditUtils::IsBody(curNode))) {
+        return NS_ERROR_FAILURE;
+      }
 
       if (insertedContextParent) {
-        // if we had to insert something higher up in the paste hierarchy, we want to
-        // skip any further paste nodes that descend from that.  Else we will paste twice.
-        nsCOMPtr<nsINode> insertedContextParentNode =
-          do_QueryInterface(insertedContextParent);
-        if (NS_WARN_IF(!insertedContextParentNode) ||
-            EditorUtils::IsDescendantOf(*nodeList[j],
-                                        *insertedContextParentNode)) {
+        // If we had to insert something higher up in the paste hierarchy,
+        // we want to skip any further paste nodes that descend from that.
+        // Else we will paste twice.
+        if (EditorUtils::IsDescendantOf(*curNode,
+                                        *insertedContextParent)) {
           continue;
         }
       }
 
       // give the user a hand on table element insertion.  if they have
       // a table or table row on the clipboard, and are trying to insert
       // into a table or table row, insert the appropriate children instead.
+      bool bDidInsert = false;
       if (HTMLEditUtils::IsTableRow(curNode) &&
-          HTMLEditUtils::IsTableRow(parentNode) &&
+          HTMLEditUtils::IsTableRow(pointToInsert.Container()) &&
           (HTMLEditUtils::IsTable(curNode) ||
-           HTMLEditUtils::IsTable(parentNode))) {
-        nsCOMPtr<nsIDOMNode> child;
-        curNode->GetFirstChild(getter_AddRefs(child));
-        while (child) {
-          rv = InsertNodeAtPoint(child, address_of(parentNode),
-                                 &offsetOfNewNode,
-                                 SplitAtEdges::eDoNotCreateEmptyContainer);
-          if (NS_FAILED(rv)) {
+           HTMLEditUtils::IsTable(pointToInsert.Container()))) {
+        for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
+             firstChild;
+             firstChild = curNode->GetFirstChild()) {
+          EditorDOMPoint insertedPoint =
+            InsertNodeIntoProperAncestor(
+              *firstChild, pointToInsert.AsRaw(),
+              SplitAtEdges::eDoNotCreateEmptyContainer);
+          if (NS_WARN_IF(!insertedPoint.IsSet())) {
             break;
           }
-
           bDidInsert = true;
-          lastInsertNode = child;
-          offsetOfNewNode++;
-
-          curNode->GetFirstChild(getter_AddRefs(child));
+          lastInsertNode = firstChild;
+          pointToInsert = insertedPoint;
+          DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+          NS_WARNING_ASSERTION(advanced,
+            "Failed to advance offset from inserted point");
         }
       }
       // give the user a hand on list insertion.  if they have
       // a list on the clipboard, and are trying to insert
       // into a list or list item, insert the appropriate children instead,
       // ie, merge the lists instead of pasting in a sublist.
       else if (HTMLEditUtils::IsList(curNode) &&
-               (HTMLEditUtils::IsList(parentNode) ||
-                HTMLEditUtils::IsListItem(parentNode))) {
-        nsCOMPtr<nsIDOMNode> child, tmp;
-        curNode->GetFirstChild(getter_AddRefs(child));
-        while (child) {
-          if (HTMLEditUtils::IsListItem(child) ||
-              HTMLEditUtils::IsList(child)) {
+               (HTMLEditUtils::IsList(pointToInsert.Container()) ||
+                HTMLEditUtils::IsListItem(pointToInsert.Container()))) {
+        for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
+             firstChild;
+             firstChild = curNode->GetFirstChild()) {
+          if (HTMLEditUtils::IsListItem(firstChild) ||
+              HTMLEditUtils::IsList(firstChild)) {
             // Check if we are pasting into empty list item. If so
             // delete it and paste into parent list instead.
-            if (HTMLEditUtils::IsListItem(parentNode)) {
+            if (HTMLEditUtils::IsListItem(pointToInsert.Container())) {
               bool isEmpty;
-              rv = IsEmptyNode(parentNode, &isEmpty, true);
+              rv = IsEmptyNode(pointToInsert.Container(), &isEmpty, true);
               if (NS_SUCCEEDED(rv) && isEmpty) {
-                int32_t newOffset;
-                nsCOMPtr<nsIDOMNode> listNode = GetNodeLocation(parentNode, &newOffset);
-                if (listNode) {
-                  DeleteNode(parentNode);
-                  parentNode = listNode;
-                  offsetOfNewNode = newOffset;
+                if (NS_WARN_IF(!pointToInsert.Container()->GetParentNode())) {
+                  // Is it an orphan node?
+                } else {
+                  DeleteNode(pointToInsert.Container());
+                  pointToInsert.Set(pointToInsert.Container());
                 }
               }
             }
-            rv = InsertNodeAtPoint(child, address_of(parentNode),
-                                   &offsetOfNewNode,
-                                   SplitAtEdges::eDoNotCreateEmptyContainer);
-            if (NS_FAILED(rv)) {
+            EditorDOMPoint insertedPoint =
+              InsertNodeIntoProperAncestor(
+                *firstChild, pointToInsert.AsRaw(),
+                SplitAtEdges::eDoNotCreateEmptyContainer);
+            if (NS_WARN_IF(!insertedPoint.IsSet())) {
               break;
             }
 
             bDidInsert = true;
-            lastInsertNode = child;
-            offsetOfNewNode++;
+            lastInsertNode = firstChild;
+            pointToInsert = insertedPoint;
+            DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+            NS_WARNING_ASSERTION(advanced,
+              "Failed to advance offset from inserted point");
           } else {
-            curNode->RemoveChild(child, getter_AddRefs(tmp));
+            AutoEditorDOMPointChildInvalidator lockOffset(pointToInsert);
+            ErrorResult error;
+            curNode->RemoveChild(*firstChild, error);
+            if (NS_WARN_IF(error.Failed())) {
+              error.SuppressException();
+            }
           }
-          curNode->GetFirstChild(getter_AddRefs(child));
         }
       } else if (parentBlock && HTMLEditUtils::IsPre(parentBlock) &&
                  HTMLEditUtils::IsPre(curNode)) {
         // Check for pre's going into pre's.
-        nsCOMPtr<nsIDOMNode> child;
-        curNode->GetFirstChild(getter_AddRefs(child));
-        while (child) {
-          rv = InsertNodeAtPoint(child, address_of(parentNode),
-                                 &offsetOfNewNode,
-                                 SplitAtEdges::eDoNotCreateEmptyContainer);
-          if (NS_FAILED(rv)) {
+        for (nsCOMPtr<nsIContent> firstChild = curNode->GetFirstChild();
+             firstChild;
+             firstChild = curNode->GetFirstChild()) {
+          EditorDOMPoint insertedPoint =
+            InsertNodeIntoProperAncestor(
+              *firstChild, pointToInsert.AsRaw(),
+              SplitAtEdges::eDoNotCreateEmptyContainer);
+          if (NS_WARN_IF(!insertedPoint.IsSet())) {
             break;
           }
 
           bDidInsert = true;
-          lastInsertNode = child;
-          offsetOfNewNode++;
-
-          curNode->GetFirstChild(getter_AddRefs(child));
+          lastInsertNode = firstChild;
+          pointToInsert = insertedPoint;
+          DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+          NS_WARNING_ASSERTION(advanced,
+            "Failed to advance offset from inserted point");
         }
       }
 
       if (!bDidInsert || NS_FAILED(rv)) {
-        // try to insert
-        rv = InsertNodeAtPoint(curNode, address_of(parentNode),
-                               &offsetOfNewNode,
-                               SplitAtEdges::eDoNotCreateEmptyContainer);
-        if (NS_SUCCEEDED(rv)) {
-          bDidInsert = true;
-          lastInsertNode = curNode;
+        // Try to insert.
+        EditorDOMPoint insertedPoint =
+          InsertNodeIntoProperAncestor(
+            *curNode->AsContent(), pointToInsert.AsRaw(),
+            SplitAtEdges::eDoNotCreateEmptyContainer);
+        if (insertedPoint.IsSet()) {
+          lastInsertNode = curNode->AsContent();
+          pointToInsert = insertedPoint;
         }
 
         // Assume failure means no legal parent in the document hierarchy,
         // try again with the parent of curNode in the paste hierarchy.
-        nsCOMPtr<nsIDOMNode> parent;
-        while (NS_FAILED(rv) && curNode) {
-          curNode->GetParentNode(getter_AddRefs(parent));
-          if (parent && !TextEditUtils::IsBody(parent)) {
-            rv = InsertNodeAtPoint(parent, address_of(parentNode),
-                                   &offsetOfNewNode,
-                                   SplitAtEdges::eDoNotCreateEmptyContainer,
-                                   address_of(lastInsertNode));
-            if (NS_SUCCEEDED(rv)) {
-              bDidInsert = true;
-              insertedContextParent = parent;
-#ifdef DEBUG
-              nsCOMPtr<nsINode> node = do_QueryInterface(parentNode);
-              MOZ_ASSERT(lastInsertNode == GetAsDOMNode(node->GetChildAt(offsetOfNewNode)));
-#endif
-            }
+        for (nsCOMPtr<nsIContent> content =
+               curNode->IsContent() ? curNode->AsContent() : nullptr;
+             content && !insertedPoint.IsSet();
+             content = content->GetParent()) {
+          if (NS_WARN_IF(!content->GetParent()) ||
+              NS_WARN_IF(TextEditUtils::IsBody(content->GetParent()))) {
+            continue;
           }
-          curNode = parent;
+          nsCOMPtr<nsINode> oldParent = content->GetParentNode();
+          insertedPoint =
+            InsertNodeIntoProperAncestor(
+              *content->GetParent(), pointToInsert.AsRaw(),
+              SplitAtEdges::eDoNotCreateEmptyContainer);
+          if (insertedPoint.IsSet()) {
+            insertedContextParent = oldParent;
+            pointToInsert = insertedPoint;
+          }
         }
       }
       if (lastInsertNode) {
-        parentNode = GetNodeLocation(lastInsertNode, &offsetOfNewNode);
-        offsetOfNewNode++;
+        pointToInsert.Set(lastInsertNode);
+        DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+        NS_WARNING_ASSERTION(advanced,
+          "Failed to advance offset from inserted point");
       }
     }
 
     // Now collapse the selection to the end of what we just inserted:
     if (lastInsertNode) {
       // set selection to the end of what we just pasted.
-      nsCOMPtr<nsIDOMNode> selNode, tmp, highTable;
+      nsCOMPtr<nsINode> selNode;
       int32_t selOffset;
 
       // but don't cross tables
       if (!HTMLEditUtils::IsTable(lastInsertNode)) {
-        nsCOMPtr<nsINode> lastInsertNode_ = do_QueryInterface(lastInsertNode);
-        NS_ENSURE_STATE(lastInsertNode_ || !lastInsertNode);
-        selNode = GetAsDOMNode(GetLastEditableLeaf(*lastInsertNode_));
-        tmp = selNode;
-        while (tmp && tmp != lastInsertNode) {
-          if (HTMLEditUtils::IsTable(tmp)) {
-            highTable = tmp;
+        selNode = GetLastEditableLeaf(*lastInsertNode);
+        nsINode* highTable = nullptr;
+        for (nsINode* parent = selNode;
+             parent && parent != lastInsertNode;
+             parent = parent->GetParentNode()) {
+          if (HTMLEditUtils::IsTable(parent)) {
+            highTable = parent;
           }
-          nsCOMPtr<nsIDOMNode> parent = tmp;
-          tmp->GetParentNode(getter_AddRefs(parent));
-          tmp = parent;
         }
         if (highTable) {
           selNode = highTable;
         }
       }
       if (!selNode) {
         selNode = lastInsertNode;
       }
-      if (IsTextNode(selNode) ||
+      if (EditorBase::IsTextNode(selNode) ||
           (IsContainer(selNode) && !HTMLEditUtils::IsTable(selNode))) {
-        rv = GetLengthOfDOMNode(selNode, (uint32_t&)selOffset);
-        NS_ENSURE_SUCCESS(rv, rv);
+        selOffset = selNode->Length();
       } else {
         // We need to find a container for selection.  Look up.
-        tmp = selNode;
-        selNode = GetNodeLocation(tmp, &selOffset);
-        // selNode might be null in case a mutation listener removed
+        EditorRawDOMPoint pointAtContainer(selNode);
+        if (NS_WARN_IF(!pointAtContainer.IsSet())) {
+          return NS_ERROR_FAILURE;
+        }
+        // The container might be null in case a mutation listener removed
         // the stuff we just inserted from the DOM.
-        NS_ENSURE_STATE(selNode);
-        ++selOffset;  // want to be *after* last leaf node in paste
+        selNode = pointAtContainer.Container();
+        // Want to be *after* last leaf node in paste.
+        selOffset = pointAtContainer.Offset() + 1;
       }
 
       // make sure we don't end up with selection collapsed after an invisible break node
       WSRunObject wsRunObj(this, selNode, selOffset);
       nsCOMPtr<nsINode> visNode;
       int32_t outVisOffset=0;
       WSType visType;
-      nsCOMPtr<nsINode> selNode_(do_QueryInterface(selNode));
-      wsRunObj.PriorVisibleNode(selNode_, selOffset, address_of(visNode),
+      wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
                                 &outVisOffset, &visType);
       if (visType == WSType::br) {
         // we are after a break.  Is it visible?  Despite the name,
         // PriorVisibleNode does not make that determination for breaks.
         // It also may not return the break in visNode.  We have to pull it
         // out of the WSRunObject's state.
         if (!IsVisibleBRElement(wsRunObj.mStartReasonNode)) {
           // don't leave selection past an invisible break;
           // reset {selNode,selOffset} to point before break
-          selNode = GetNodeLocation(GetAsDOMNode(wsRunObj.mStartReasonNode), &selOffset);
+          EditorRawDOMPoint atStartReasonNode(wsRunObj.mStartReasonNode);
+          selNode = atStartReasonNode.Container();
+          selOffset = atStartReasonNode.Offset();
           // we want to be inside any inline style prior to break
           WSRunObject wsRunObj(this, selNode, selOffset);
-          selNode_ = do_QueryInterface(selNode);
-          wsRunObj.PriorVisibleNode(selNode_, selOffset, address_of(visNode),
+          wsRunObj.PriorVisibleNode(selNode, selOffset, address_of(visNode),
                                     &outVisOffset, &visType);
           if (visType == WSType::text || visType == WSType::normalWS) {
-            selNode = GetAsDOMNode(visNode);
+            selNode = visNode;
             selOffset = outVisOffset;  // PriorVisibleNode already set offset to _after_ the text or ws
           } else if (visType == WSType::special) {
             // prior visible thing is an image or some other non-text thingy.
             // We want to be right after it.
-            selNode = GetNodeLocation(GetAsDOMNode(wsRunObj.mStartReasonNode), &selOffset);
-            ++selOffset;
+            atStartReasonNode.Set(wsRunObj.mStartReasonNode);
+            selNode = atStartReasonNode.Container();
+            selOffset = atStartReasonNode.Offset() + 1;
           }
         }
       }
       selection->Collapse(selNode, selOffset);
 
       // if we just pasted a link, discontinue link style
       nsCOMPtr<nsIDOMNode> link;
-      if (!bStartedInLink && IsInLink(selNode, address_of(link))) {
+      if (!bStartedInLink &&
+          IsInLink(GetAsDOMNode(selNode), address_of(link))) {
         // 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);
         SplitNodeResult splitLinkResult =
-          SplitNodeDeep(*linkContent, EditorRawDOMPoint(selContent, selOffset),
+          SplitNodeDeep(*linkContent, EditorRawDOMPoint(selNode, 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/TextEditUtils.cpp
+++ b/editor/libeditor/TextEditUtils.cpp
@@ -30,16 +30,23 @@ namespace mozilla {
  * IsBody() returns true if aNode is an html body node.
  */
 bool
 TextEditUtils::IsBody(nsIDOMNode* aNode)
 {
   return EditorBase::NodeIsType(aNode, nsGkAtoms::body);
 }
 
+bool
+TextEditUtils::IsBody(nsINode* aNode)
+{
+  MOZ_ASSERT(aNode);
+  return aNode->IsHTMLElement(nsGkAtoms::body);
+}
+
 /**
  * IsBreak() returns true if aNode is an html break node.
  */
 bool
 TextEditUtils::IsBreak(nsIDOMNode* aNode)
 {
   return EditorBase::NodeIsType(aNode, nsGkAtoms::br);
 }
--- a/editor/libeditor/TextEditUtils.h
+++ b/editor/libeditor/TextEditUtils.h
@@ -15,16 +15,17 @@ namespace mozilla {
 
 class TextEditor;
 
 class TextEditUtils final
 {
 public:
   // from TextEditRules:
   static bool IsBody(nsIDOMNode* aNode);
+  static bool IsBody(nsINode* aNode);
   static bool IsBreak(nsIDOMNode* aNode);
   static bool IsBreak(nsINode* aNode);
   static bool IsMozBR(nsIDOMNode* aNode);
   static bool IsMozBR(nsINode* aNode);
   static bool HasMozAttr(nsIDOMNode* aNode);
 };
 
 /***************************************************************************