Bug 1414713 - EditorUtils::IsDescendantOf() should take EditorDOMPoint and EditorRawDOMPoint as out param r?m_kato, catalinb draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 06 Nov 2017 17:01:33 +0900
changeset 693925 a64b624d49c99d989713d614d418e04a0685b734
parent 693917 06be91e5fe089aacba40236419e4667c4ac69a0f
child 739200 b02af3e983ce7cc3020d03d9f109c7f9d43ea45d
push id87982
push usermasayuki@d-toybox.com
push dateTue, 07 Nov 2017 04:29:55 +0000
reviewersm_kato, catalinb
bugs1414713
milestone58.0a1
Bug 1414713 - EditorUtils::IsDescendantOf() should take EditorDOMPoint and EditorRawDOMPoint as out param r?m_kato, catalinb EditorUtils::IsDescendantOf() current takes a pointer to offset or a pointer to child content if the caller needs to know the child of the most ancestor node. However, some callers should get a child as EditorDOMPoint or EditorRawDOMPoint. Then, they can be used for some editor methods which need to take child node for performance optimization. This patch makes EditorUtils::IsDescendantOf() as only two overloads. One takes pointer to EditorRawDOMPoint as optional out argument. The other takes pointer to EditorDOMPoint as an out param. Additionally, this creates new constructor of AutoTrackDOMPoint for making it can treat EditorDOMPoint directly. MozReview-Commit-ID: IsAGTUvKI19
dom/base/RangeBoundary.h
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorUtils.cpp
editor/libeditor/EditorUtils.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditorDataTransfer.cpp
editor/libeditor/SelectionState.cpp
editor/libeditor/SelectionState.h
--- a/dom/base/RangeBoundary.h
+++ b/dom/base/RangeBoundary.h
@@ -198,23 +198,51 @@ public:
       mOffset = mozilla::Some(mParent->GetChildCount());
       return mOffset.value();
     }
     // Use nsINode::IndexOf() as the last resort due to being expensive.
     mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
     return mOffset.value();
   }
 
+  /**
+   * Set() sets a point to aOffset or aChild.
+   * If it's set with offset, mRef is invalidated.  If it's set with aChild,
+   * mOffset may be invalidated unless the offset can be computed simply.
+   */
   void
   Set(nsINode* aContainer, int32_t aOffset)
   {
     mParent = aContainer;
     mRef = nullptr;
     mOffset = mozilla::Some(aOffset);
   }
+  void
+  Set(const nsIContent* aChild)
+  {
+    MOZ_ASSERT(aChild);
+    mParent = aChild->GetParentNode();
+    mRef = aChild->GetPreviousSibling();
+    if (!mRef) {
+      mOffset = mozilla::Some(0);
+    } else {
+      mOffset.reset();
+    }
+  }
+
+  /**
+   * Clear() makes the instance not point anywhere.
+   */
+  void
+  Clear()
+  {
+    mParent = nullptr;
+    mRef = nullptr;
+    mOffset.reset();
+  }
 
   /**
    * AdvanceOffset() tries to reference next sibling of mRef if its container
    * can have children or increments offset if the container is a text node or
    * something.
    * If the container can have children and there is no next sibling, this
    * outputs warning and does nothing.  So, callers need to check if there is
    * next sibling which you need to refer.
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -3964,17 +3964,17 @@ EditorBase::SplitNodeDeep(nsIContent& aN
                           nsIContent& aSplitPointParent,
                           int32_t aSplitPointOffset,
                           EmptyContainers aEmptyContainers,
                           nsIContent** aOutLeftNode,
                           nsIContent** aOutRightNode,
                           nsCOMPtr<nsIContent>* ioChildAtSplitPointOffset)
 {
   MOZ_ASSERT(&aSplitPointParent == &aNode ||
-             EditorUtils::IsDescendantOf(&aSplitPointParent, &aNode));
+             EditorUtils::IsDescendantOf(aSplitPointParent, aNode));
   int32_t offset = aSplitPointOffset;
 
   nsCOMPtr<nsIContent> leftNode, rightNode;
   OwningNonNull<nsIContent> nodeToSplit = aSplitPointParent;
   while (true) {
     // 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
--- a/editor/libeditor/EditorUtils.cpp
+++ b/editor/libeditor/EditorUtils.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/EditorUtils.h"
 
+#include "mozilla/EditorDOMPoint.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/dom/Selection.h"
 #include "nsComponentManagerUtils.h"
 #include "nsError.h"
 #include "nsIClipboardDragDropHookList.h"
 // hooks
 #include "nsIClipboardDragDropHooks.h"
 #include "nsIContent.h"
@@ -130,70 +131,64 @@ DOMSubtreeIterator::~DOMSubtreeIterator(
 {
 }
 
 /******************************************************************************
  * some general purpose editor utils
  *****************************************************************************/
 
 bool
-EditorUtils::IsDescendantOf(nsINode* aNode,
-                            nsINode* aParent,
-                            int32_t* aOffset)
+EditorUtils::IsDescendantOf(const nsINode& aNode,
+                            const nsINode& aParent,
+                            EditorRawDOMPoint* aOutPoint /* = nullptr */)
 {
-  MOZ_ASSERT(aNode && aParent);
-  if (aNode == aParent) {
+  if (aOutPoint) {
+    aOutPoint->Clear();
+  }
+
+  if (&aNode == &aParent) {
     return false;
   }
 
-  for (nsCOMPtr<nsINode> node = aNode; node; node = node->GetParentNode()) {
-    if (node->GetParentNode() == aParent) {
-      if (aOffset) {
-        *aOffset = aParent->IndexOf(node);
+  for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
+    if (node->GetParentNode() == &aParent) {
+      if (aOutPoint) {
+        MOZ_ASSERT(node->IsContent());
+        aOutPoint->Set(node->AsContent());
       }
       return true;
     }
   }
 
   return false;
 }
 
 bool
-EditorUtils::IsDescendantOf(nsINode* aNode,
-                            nsINode* aParent,
-                            nsIContent** aChild)
+EditorUtils::IsDescendantOf(const nsINode& aNode,
+                            const nsINode& aParent,
+                            EditorDOMPoint* aOutPoint)
 {
-  MOZ_ASSERT(aNode && aParent && aChild);
-  *aChild = nullptr;
-  if (aNode == aParent) {
+  MOZ_ASSERT(aOutPoint);
+  aOutPoint->Clear();
+  if (&aNode == &aParent) {
     return false;
   }
 
-  for (nsCOMPtr<nsINode> node = aNode; node; node = node->GetParentNode()) {
-    if (node->GetParentNode() == aParent) {
-      *aChild = node->AsContent();
+  for (const nsINode* node = &aNode; node; node = node->GetParentNode()) {
+    if (node->GetParentNode() == &aParent) {
+      MOZ_ASSERT(node->IsContent());
+      aOutPoint->Set(node->AsContent());
       return true;
     }
   }
 
   return false;
 }
 
 bool
-EditorUtils::IsDescendantOf(nsIDOMNode* aNode,
-                            nsIDOMNode* aParent,
-                            int32_t* aOffset)
-{
-  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
-  nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
-  NS_ENSURE_TRUE(node && parent, false);
-  return IsDescendantOf(node, parent, aOffset);
-}
-
-bool
 EditorUtils::IsLeafNode(nsIDOMNode* aNode)
 {
   bool hasChildren = false;
   if (aNode)
     aNode->HasChildNodes(&hasChildren);
   return !hasChildren;
 }
 
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifndef mozilla_EditorUtils_h
 #define mozilla_EditorUtils_h
 
 #include "mozilla/dom/Selection.h"
 #include "mozilla/EditorBase.h"
+#include "mozilla/EditorDOMPoint.h"
 #include "mozilla/GuardObjects.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsIDOMNode.h"
 #include "nsIEditor.h"
 #include "nscore.h"
 
 class nsAtom;
@@ -373,23 +374,29 @@ public:
   {
     return true;
   }
 };
 
 class EditorUtils final
 {
 public:
-  // Note that aChild isn't a normal XPCOM outparam and won't get AddRef'ed.
-  static bool IsDescendantOf(nsINode* aNode, nsINode* aParent,
-                             nsIContent** aChild);
-  static bool IsDescendantOf(nsINode* aNode, nsINode* aParent,
-                             int32_t* aOffset = nullptr);
-  static bool IsDescendantOf(nsIDOMNode* aNode, nsIDOMNode* aParent,
-                             int32_t* aOffset = nullptr);
+  /**
+   * IsDescendantOf() checks if aNode is a child or a descendant of aParent.
+   * aOutPoint is set to the child of aParent.
+   *
+   * @return            true if aNode is a child or a descendant of aParent.
+   */
+  static bool IsDescendantOf(const nsINode& aNode,
+                             const nsINode& aParent,
+                             EditorRawDOMPoint* aOutPoint = nullptr);
+  static bool IsDescendantOf(const nsINode& aNode,
+                             const nsINode& aParent,
+                             EditorDOMPoint* aOutPoint);
+
   static bool IsLeafNode(nsIDOMNode* aNode);
 };
 
 class EditorHookUtils final
 {
 public:
   static bool DoInsertionHook(nsIDOMDocument* aDoc, nsIDOMEvent* aEvent,
                               nsITransferable* aTrans);
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -2853,221 +2853,240 @@ HTMLEditRules::TryToJoinBlocks(nsIConten
       rightBlock->GetParentNode() == leftBlock) {
     return EditActionHandled();
   }
 
   // Special rule here: if we are trying to join list items, and they are in
   // different lists, join the lists instead.
   bool mergeLists = false;
   nsAtom* existingList = nsGkAtoms::_empty;
-  nsIContent* childInBlock = nullptr;
+  EditorDOMPoint childInBlock;
   nsCOMPtr<Element> leftList, rightList;
   if (HTMLEditUtils::IsListItem(leftBlock) &&
       HTMLEditUtils::IsListItem(rightBlock)) {
     leftList = leftBlock->GetParentElement();
     rightList = rightBlock->GetParentElement();
     if (leftList && rightList && leftList != rightList &&
-        !EditorUtils::IsDescendantOf(leftList, rightBlock, &childInBlock) &&
-        !EditorUtils::IsDescendantOf(rightList, leftBlock, &childInBlock)) {
+        !EditorUtils::IsDescendantOf(*leftList, *rightBlock, &childInBlock) &&
+        !EditorUtils::IsDescendantOf(*rightList, *leftBlock, &childInBlock)) {
       // There are some special complications if the lists are descendants of
       // the other lists' items.  Note that it is okay for them to be
       // descendants of the other lists themselves, which is the usual case for
       // sublists in our implementation.
+      MOZ_DIAGNOSTIC_ASSERT(!childInBlock.IsSet());
       leftBlock = leftList;
       rightBlock = rightList;
       mergeLists = true;
       existingList = leftList->NodeInfo()->NameAtom();
     }
   }
 
   AutoTransactionsConserveSelection dontChangeMySelection(htmlEditor);
 
-  int32_t rightOffset = 0;
-  int32_t leftOffset = -1;
-
   // offset below is where you find yourself in rightBlock when you traverse
   // upwards from leftBlock
-  if (EditorUtils::IsDescendantOf(leftBlock, rightBlock, &rightOffset)) {
+  EditorDOMPoint rightBlockChild;
+  if (EditorUtils::IsDescendantOf(*leftBlock, *rightBlock, &rightBlockChild)) {
     // Tricky case.  Left block is inside right block.  Do ws adjustment.  This
     // just destroys non-visible ws at boundaries we will be joining.
-    rightOffset++;
+    rightBlockChild.AdvanceOffset();
     nsresult rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
                                                   WSRunObject::kBlockEnd,
                                                   leftBlock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return EditActionIgnored(rv);
     }
 
     {
       // We can't just track rightBlock because it's an Element.
-      nsCOMPtr<nsINode> trackingRightBlock(rightBlock);
-      AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater,
-                                address_of(trackingRightBlock), &rightOffset);
+      AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater, &rightBlockChild);
       rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
                                            WSRunObject::kAfterBlock,
-                                           rightBlock, rightOffset);
+                                           rightBlock,
+                                           rightBlockChild.Offset());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return EditActionIgnored(rv);
       }
 
-      if (trackingRightBlock->IsElement()) {
-        rightBlock = trackingRightBlock->AsElement();
+      // XXX AutoTrackDOMPoint instance, tracker, hasn't been destroyed here.
+      //     Do we really need to do update rightBlock here??
+      MOZ_ASSERT(rightBlock == rightBlockChild.Container());
+      if (rightBlockChild.Container()->IsElement()) {
+        rightBlock = rightBlockChild.Container()->AsElement();
       } else {
-        if (NS_WARN_IF(!trackingRightBlock->GetParentElement())) {
+        if (NS_WARN_IF(!rightBlockChild.Container()->GetParentElement())) {
           return EditActionIgnored(NS_ERROR_UNEXPECTED);
         }
-        rightBlock = trackingRightBlock->GetParentElement();
+        rightBlock = rightBlockChild.Container()->GetParentElement();
       }
     }
     // Do br adjustment.
     nsCOMPtr<Element> brNode =
       CheckForInvisibleBR(*leftBlock, BRLocation::blockEnd);
     EditActionResult ret(NS_OK);
     if (mergeLists) {
       // The idea here is to take all children in rightList that are past
       // offset, and pull them into leftlist.
-      for (nsCOMPtr<nsIContent> child = childInBlock;
-           child; child = rightList->GetChildAt(rightOffset)) {
+      // XXX Looks like that when mergeLists is true, childInBlock has never
+      //     been set.  So, this block must be dead code.
+      MOZ_DIAGNOSTIC_ASSERT(childInBlock.IsSet());
+      uint32_t offset = rightBlockChild.Offset();
+      for (nsCOMPtr<nsIContent> child = childInBlock.GetChildAtOffset();
+           child; child = rightList->GetChildAt(offset)) {
         rv = htmlEditor->MoveNode(child, leftList, -1);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return EditActionIgnored(rv);
         }
       }
       // XXX Should this set to true only when above for loop moves the node?
       ret.MarkAsHandled();
+      // childInBlock and rightBlockChild were moved to leftList.  So, they
+      // are now invalid.
+      rightBlockChild.Clear();
+      childInBlock.Clear();
     } else {
       // XXX Why do we ignore the result of MoveBlock()?
       EditActionResult retMoveBlock =
-        MoveBlock(*leftBlock, *rightBlock, leftOffset, rightOffset);
+        MoveBlock(*leftBlock, *rightBlock,
+                  -1, rightBlockChild.Offset());
       if (retMoveBlock.Handled()) {
         ret.MarkAsHandled();
       }
+      // Now, all children of rightBlock were moved to leftBlock.  So,
+      // rightBlockChild is now invalid.
+      rightBlockChild.Clear();
     }
     if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
       ret.MarkAsHandled();
     }
     return ret;
   }
 
+  MOZ_DIAGNOSTIC_ASSERT(!rightBlockChild.IsSet());
+
   // Offset below is where you find yourself in leftBlock when you traverse
   // upwards from rightBlock
-  if (EditorUtils::IsDescendantOf(rightBlock, leftBlock, &leftOffset)) {
+  EditorDOMPoint leftBlockChild;
+  if (EditorUtils::IsDescendantOf(*rightBlock, *leftBlock, &leftBlockChild)) {
     // Tricky case.  Right block is inside left block.  Do ws adjustment.  This
     // just destroys non-visible ws at boundaries we will be joining.
     nsresult rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
                                                   WSRunObject::kBlockStart,
                                                   rightBlock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return EditActionIgnored(rv);
     }
 
     {
       // We can't just track leftBlock because it's an Element, so track
       // something else.
-      nsCOMPtr<nsINode> trackingLeftBlock(leftBlock);
-      AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater,
-                                address_of(trackingLeftBlock), &leftOffset);
+      AutoTrackDOMPoint tracker(htmlEditor->mRangeUpdater, &leftBlockChild);
       rv = WSRunObject::ScrubBlockBoundary(htmlEditor,
                                            WSRunObject::kBeforeBlock,
-                                           leftBlock, leftOffset);
+                                           leftBlock, leftBlockChild.Offset());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return EditActionIgnored(rv);
       }
-
-      if (trackingLeftBlock->IsElement()) {
-        leftBlock = trackingLeftBlock->AsElement();
+      // XXX AutoTrackDOMPoint instance, tracker, hasn't been destroyed here.
+      //     Do we really need to do update rightBlock here??
+      MOZ_DIAGNOSTIC_ASSERT(leftBlock == leftBlockChild.Container());
+      if (leftBlockChild.Container()->IsElement()) {
+        leftBlock = leftBlockChild.Container()->AsElement();
       } else {
-        if (NS_WARN_IF(!trackingLeftBlock->GetParentElement())) {
+        if (NS_WARN_IF(!leftBlockChild.Container()->GetParentElement())) {
           return EditActionIgnored(NS_ERROR_UNEXPECTED);
         }
-        leftBlock = trackingLeftBlock->GetParentElement();
+        leftBlock = leftBlockChild.Container()->GetParentElement();
       }
     }
     // Do br adjustment.
     nsCOMPtr<Element> brNode =
-      CheckForInvisibleBR(*leftBlock, BRLocation::beforeBlock, leftOffset);
+      CheckForInvisibleBR(*leftBlock, BRLocation::beforeBlock,
+                          leftBlockChild.Offset());
     EditActionResult ret(NS_OK);
     if (mergeLists) {
       // XXX Why do we ignore the result of MoveContents()?
+      int32_t offset = leftBlockChild.Offset();
       EditActionResult retMoveContents =
-        MoveContents(*rightList, *leftList, &leftOffset);
+        MoveContents(*rightList, *leftList, &offset);
       if (retMoveContents.Handled()) {
         ret.MarkAsHandled();
       }
+      // leftBlockChild was moved to rightList.  So, it's invalid now.
+      leftBlockChild.Clear();
     } else {
       // Left block is a parent of right block, and the parent of the previous
       // visible content.  Right block is a child and contains the contents we
       // want to move.
 
-      int32_t previousContentOffset;
-      nsCOMPtr<nsINode> previousContentParent;
-
+      EditorDOMPoint previousContent;
       if (&aLeftNode == leftBlock) {
         // We are working with valid HTML, aLeftNode is a block node, and is
         // therefore allowed to contain rightBlock.  This is the simple case,
         // we will simply move the content in rightBlock out of its block.
-        previousContentParent = leftBlock;
-        previousContentOffset = leftOffset;
+        previousContent = leftBlockChild;
       } else {
         // We try to work as well as possible with HTML that's already invalid.
         // Although "right block" is a block, and a block must not be contained
         // in inline elements, reality is that broken documents do exist.  The
         // DIRECT parent of "left NODE" might be an inline element.  Previous
         // versions of this code skipped inline parents until the first block
         // parent was found (and used "left block" as the destination).
         // However, in some situations this strategy moves the content to an
         // unexpected position.  (see bug 200416) The new idea is to make the
         // moving content a sibling, next to the previous visible content.
-
-        previousContentParent = aLeftNode.GetParentNode();
-        previousContentOffset = previousContentParent ?
-          previousContentParent->IndexOf(&aLeftNode) : -1;
+        previousContent.Set(&aLeftNode);
 
         // We want to move our content just after the previous visible node.
-        previousContentOffset++;
+        previousContent.AdvanceOffset();
       }
 
       // Because we don't want the moving content to receive the style of the
       // previous content, we split the previous content's style.
 
       nsCOMPtr<Element> editorRoot = htmlEditor->GetEditorRoot();
       if (!editorRoot || &aLeftNode != editorRoot) {
         nsCOMPtr<nsIContent> splittedPreviousContent;
+        nsCOMPtr<nsINode> previousContentParent = previousContent.Container();
+        int32_t previousContentOffset = previousContent.Offset();
         rv = htmlEditor->SplitStyleAbovePoint(
                            address_of(previousContentParent),
                            &previousContentOffset,
                            nullptr, nullptr, nullptr,
                            getter_AddRefs(splittedPreviousContent));
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return EditActionIgnored(rv);
         }
 
         if (splittedPreviousContent) {
-          previousContentParent = splittedPreviousContent->GetParentNode();
-          previousContentOffset = previousContentParent ?
-            previousContentParent->IndexOf(splittedPreviousContent) : -1;
+          previousContent.Set(splittedPreviousContent);
+        } else {
+          previousContent.Set(previousContentParent, previousContentOffset);
         }
       }
 
-      if (NS_WARN_IF(!previousContentParent)) {
+      if (NS_WARN_IF(!previousContent.IsSet())) {
         return EditActionIgnored(NS_ERROR_NULL_POINTER);
       }
 
-      ret |= MoveBlock(*previousContentParent->AsElement(), *rightBlock,
-                       previousContentOffset, rightOffset);
+      ret |= MoveBlock(*previousContent.Container()->AsElement(), *rightBlock,
+                       previousContent.Offset(), 0);
       if (NS_WARN_IF(ret.Failed())) {
         return ret;
       }
     }
     if (brNode && NS_SUCCEEDED(htmlEditor->DeleteNode(brNode))) {
       ret.MarkAsHandled();
     }
     return ret;
   }
 
+  MOZ_DIAGNOSTIC_ASSERT(!rightBlockChild.IsSet());
+  MOZ_DIAGNOSTIC_ASSERT(!leftBlockChild.IsSet());
+
   // Normal case.  Blocks are siblings, or at least close enough.  An example
   // of the latter is <p>paragraph</p><ul><li>one<li>two<li>three</ul>.  The
   // first li and the p are not true siblings, but we still want to join them
   // if you backspace from li into p.
 
   // Adjust whitespace at block boundaries
   nsresult rv =
     WSRunObject::PrepareToJoinBlocks(htmlEditor, leftBlock, rightBlock);
@@ -3084,17 +3103,17 @@ HTMLEditRules::TryToJoinBlocks(nsIConten
     EditorDOMPoint pt = JoinNodesSmart(*leftBlock, *rightBlock);
     if (pt.IsSet() && mergeLists) {
       RefPtr<Element> newBlock =
         ConvertListType(rightBlock, existingList, nsGkAtoms::li);
     }
     ret.MarkAsHandled();
   } else {
     // Nodes are dissimilar types.
-    ret |= MoveBlock(*leftBlock, *rightBlock, leftOffset, rightOffset);
+    ret |= MoveBlock(*leftBlock, *rightBlock, -1, 0);
     if (NS_WARN_IF(ret.Failed())) {
       return ret;
     }
   }
   if (brNode) {
     rv = htmlEditor->DeleteNode(brNode);
     // XXX In other top level if blocks, the result of DeleteNode()
     //     is ignored.  Why does only this result is respected?
@@ -3433,17 +3452,17 @@ HTMLEditRules::WillMakeList(Selection* a
       if (TextEditUtils::IsBreak(curNode)) {
         prevListItem = nullptr;
       }
       continue;
     }
 
     if (HTMLEditUtils::IsList(curNode)) {
       // do we have a curList already?
-      if (curList && !EditorUtils::IsDescendantOf(curNode, curList)) {
+      if (curList && !EditorUtils::IsDescendantOf(*curNode, *curList)) {
         // move all of our children into curList.  cheezy way to do it: move
         // whole list and then RemoveContainer() on the list.  ConvertListType
         // first: that routine handles converting the list item types, if
         // needed
         NS_ENSURE_STATE(mHTMLEditor);
         rv = mHTMLEditor->MoveNode(curNode, curList, -1);
         NS_ENSURE_SUCCESS(rv, rv);
         newBlock = ConvertListType(curNode->AsElement(), listType, itemType);
@@ -3464,17 +3483,17 @@ HTMLEditRules::WillMakeList(Selection* a
       continue;
     }
 
     if (HTMLEditUtils::IsListItem(curNode)) {
       NS_ENSURE_STATE(mHTMLEditor);
       if (!curParent->IsHTMLElement(listType)) {
         // list item is in wrong type of list. if we don't have a curList,
         // split the old list and make a new list of correct type.
-        if (!curList || EditorUtils::IsDescendantOf(curNode, curList)) {
+        if (!curList || EditorUtils::IsDescendantOf(*curNode, *curList)) {
           NS_ENSURE_STATE(mHTMLEditor);
           NS_ENSURE_STATE(curParent->IsContent());
           ErrorResult rv;
           nsCOMPtr<nsIContent> splitNode =
             mHTMLEditor->SplitNode(*curParent->AsContent(), offset, rv);
           NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
           newBlock = splitNode ? splitNode->AsElement() : nullptr;
           int32_t offset;
@@ -4400,17 +4419,17 @@ HTMLEditRules::WillOutdent(Selection& aS
         }
         rv = PopListItem(*curNode->AsContent());
         NS_ENSURE_SUCCESS(rv, rv);
         continue;
       }
       // Do we have a blockquote that we are already committed to removing?
       if (curBlockQuote) {
         // If so, is this node a descendant?
-        if (EditorUtils::IsDescendantOf(curNode, curBlockQuote)) {
+        if (EditorUtils::IsDescendantOf(*curNode, *curBlockQuote)) {
           lastBQChild = curNode;
           // Then we don't need to do anything different for this node
           continue;
         }
         // Otherwise, we have progressed beyond end of curBlockQuote, so
         // let's handle it now.  We need to remove the portion of
         // curBlockQuote that contains [firstBQChild - lastBQChild].
         rv = OutdentPartOfBlock(*curBlockQuote, *firstBQChild, *lastBQChild,
@@ -4527,28 +4546,28 @@ HTMLEditRules::WillOutdent(Selection& aS
     if (aSelection.Collapsed()) {
       // Push selection past end of rememberedLeftBQ
       NS_ENSURE_TRUE(aSelection.GetRangeAt(0), NS_OK);
       nsCOMPtr<nsINode> startNode =
         aSelection.GetRangeAt(0)->GetStartContainer();
       int32_t startOffset = aSelection.GetRangeAt(0)->StartOffset();
       if (rememberedLeftBQ &&
           (startNode == rememberedLeftBQ ||
-           EditorUtils::IsDescendantOf(startNode, rememberedLeftBQ))) {
+           EditorUtils::IsDescendantOf(*startNode, *rememberedLeftBQ))) {
         // Selection is inside rememberedLeftBQ - push it past it.
         startNode = rememberedLeftBQ->GetParentNode();
         startOffset = startNode ? 1 + startNode->IndexOf(rememberedLeftBQ) : 0;
         aSelection.Collapse(startNode, startOffset);
       }
       // And pull selection before beginning of rememberedRightBQ
       startNode = aSelection.GetRangeAt(0)->GetStartContainer();
       startOffset = aSelection.GetRangeAt(0)->StartOffset();
       if (rememberedRightBQ &&
           (startNode == rememberedRightBQ ||
-           EditorUtils::IsDescendantOf(startNode, rememberedRightBQ))) {
+           EditorUtils::IsDescendantOf(*startNode, *rememberedRightBQ))) {
         // Selection is inside rememberedRightBQ - push it before it.
         startNode = rememberedRightBQ->GetParentNode();
         startOffset = startNode ? startNode->IndexOf(rememberedRightBQ) : -1;
         aSelection.Collapse(startNode, startOffset);
       }
     }
     return NS_OK;
   }
@@ -4579,18 +4598,18 @@ void
 HTMLEditRules::SplitBlock(Element& aBlock,
                           nsIContent& aStartChild,
                           nsIContent& aEndChild,
                           nsIContent** aOutLeftNode,
                           nsIContent** aOutRightNode,
                           nsIContent** aOutMiddleNode)
 {
   // aStartChild and aEndChild must be exclusive descendants of aBlock
-  MOZ_ASSERT(EditorUtils::IsDescendantOf(&aStartChild, &aBlock) &&
-             EditorUtils::IsDescendantOf(&aEndChild, &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!
@@ -6391,17 +6410,17 @@ HTMLEditRules::GetHighestInlineParent(ns
   if (&aNode == host) {
     return nullptr;
   }
 
   // If aNode is outside of the <body> element, we don't support to edit
   // such elements for now.
   // XXX This should be MOZ_ASSERT after fixing bug 1413131 for avoiding
   //     calling this expensive method.
-  if (NS_WARN_IF(!EditorUtils::IsDescendantOf(&aNode, host))) {
+  if (NS_WARN_IF(!EditorUtils::IsDescendantOf(aNode, *host))) {
     return nullptr;
   }
 
   // Looks for the highest inline parent in the editing host.
   nsIContent* content = aNode.AsContent();
   for (nsIContent* parent = content->GetParent();
        parent && parent != host && IsInlineNode(*parent);
        parent = parent->GetParent()) {
@@ -7083,17 +7102,17 @@ HTMLEditRules::RemoveBlockStyle(nsTArray
       // Recursion time
       nsTArray<OwningNonNull<nsINode>> childArray;
       GetChildNodesForOperation(*curNode, childArray);
       nsresult rv = RemoveBlockStyle(childArray);
       NS_ENSURE_SUCCESS(rv, rv);
     } else if (IsInlineNode(curNode)) {
       if (curBlock) {
         // If so, is this node a descendant?
-        if (EditorUtils::IsDescendantOf(curNode, curBlock)) {
+        if (EditorUtils::IsDescendantOf(*curNode, *curBlock)) {
           // Then we don't need to do anything different for this node
           lastNode = curNode->AsContent();
           continue;
         }
         // Otherwise, we have progressed beyond end of curBlock, so let's
         // handle it now.  We need to remove the portion of curBlock that
         // contains [firstNode - lastNode].
         nsresult rv = RemovePartOfBlock(*curBlock, *firstNode, *lastNode);
@@ -8155,31 +8174,31 @@ HTMLEditRules::SelectionEndpointInNode(n
   for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
     RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
     nsINode* startContainer = range->GetStartContainer();
     if (startContainer) {
       if (aNode == startContainer) {
         *aResult = true;
         return NS_OK;
       }
-      if (EditorUtils::IsDescendantOf(startContainer, aNode)) {
+      if (EditorUtils::IsDescendantOf(*startContainer, *aNode)) {
         *aResult = true;
         return NS_OK;
       }
     }
     nsINode* endContainer = range->GetEndContainer();
     if (startContainer == endContainer) {
       continue;
     }
     if (endContainer) {
       if (aNode == endContainer) {
         *aResult = true;
         return NS_OK;
       }
-      if (EditorUtils::IsDescendantOf(endContainer, aNode)) {
+      if (EditorUtils::IsDescendantOf(*endContainer, *aNode)) {
         *aResult = true;
         return NS_OK;
       }
     }
   }
   return NS_OK;
 }
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -753,17 +753,17 @@ HTMLEditor::IsBlockNode(nsINode* aNode)
  */
 Element*
 HTMLEditor::GetBlockNodeParent(nsINode* aNode,
                                nsINode* aAncestorLimiter)
 {
   MOZ_ASSERT(aNode);
   MOZ_ASSERT(!aAncestorLimiter ||
              aNode == aAncestorLimiter ||
-             EditorUtils::IsDescendantOf(aNode, aAncestorLimiter),
+             EditorUtils::IsDescendantOf(*aNode, *aAncestorLimiter),
              "aNode isn't in aAncestorLimiter");
 
   // The caller has already reached the limiter.
   if (aNode == aAncestorLimiter) {
     return nullptr;
   }
 
   nsCOMPtr<nsINode> p = aNode->GetParentNode();
@@ -786,17 +786,17 @@ HTMLEditor::GetBlockNodeParent(nsINode* 
  * Returns the node if it's a block, otherwise GetBlockNodeParent
  */
 Element*
 HTMLEditor::GetBlock(nsINode& aNode,
                      nsINode* aAncestorLimiter)
 {
   MOZ_ASSERT(!aAncestorLimiter ||
              &aNode == aAncestorLimiter ||
-             EditorUtils::IsDescendantOf(&aNode, aAncestorLimiter),
+             EditorUtils::IsDescendantOf(aNode, *aAncestorLimiter),
              "aNode isn't in aAncestorLimiter");
 
   if (NodeIsBlockStatic(&aNode)) {
     return aNode.AsElement();
   }
   return GetBlockNodeParent(&aNode);
 }
 
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -437,17 +437,21 @@ HTMLEditor::DoInsertHTMLWithContext(cons
 
       NS_ENSURE_TRUE(curNode, NS_ERROR_FAILURE);
       NS_ENSURE_TRUE(curNode != fragmentAsNode, NS_ERROR_FAILURE);
       NS_ENSURE_TRUE(!TextEditUtils::IsBody(curNode), 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.
-        if (EditorUtils::IsDescendantOf(curNode, insertedContextParent)) {
+        nsCOMPtr<nsINode> insertedContextParentNode =
+          do_QueryInterface(insertedContextParent);
+        if (NS_WARN_IF(!insertedContextParentNode) ||
+            EditorUtils::IsDescendantOf(*nodeList[j],
+                                        *insertedContextParentNode)) {
           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.
       if (HTMLEditUtils::IsTableRow(curNode) &&
@@ -2375,17 +2379,17 @@ HTMLEditor::ReplaceOrphanedStructure(
   // insert them twice.
   uint32_t removedCount = 0;
   uint32_t originalLength = aNodeArray.Length();
   for (uint32_t i = 0; i < originalLength; i++) {
     uint32_t idx = aStartOrEnd == StartOrEnd::start ?
       (i - removedCount) : (originalLength - i - 1);
     OwningNonNull<nsINode> endpoint = aNodeArray[idx];
     if (endpoint == replaceNode ||
-        EditorUtils::IsDescendantOf(endpoint, replaceNode)) {
+        EditorUtils::IsDescendantOf(*endpoint, *replaceNode)) {
       aNodeArray.RemoveElementAt(idx);
       removedCount++;
     }
   }
 
   // Now replace the removed nodes with the structural parent
   if (aStartOrEnd == StartOrEnd::end) {
     aNodeArray.AppendElement(*replaceNode);
--- a/editor/libeditor/SelectionState.cpp
+++ b/editor/libeditor/SelectionState.cpp
@@ -282,25 +282,25 @@ RangeUpdater::SelAdjDeleteNode(nsINode* 
     }
     if (item->mEndContainer == aNode) {
       item->mEndContainer = parent;
       item->mEndOffset = offset;
     }
 
     // check for range endpoints that are in descendants of aNode
     nsCOMPtr<nsINode> oldStart;
-    if (EditorUtils::IsDescendantOf(item->mStartContainer, aNode)) {
+    if (EditorUtils::IsDescendantOf(*item->mStartContainer, *aNode)) {
       oldStart = item->mStartContainer;  // save for efficiency hack below.
       item->mStartContainer = parent;
       item->mStartOffset = offset;
     }
 
     // avoid having to call IsDescendantOf() for common case of range startnode == range endnode.
     if (item->mEndContainer == oldStart ||
-        EditorUtils::IsDescendantOf(item->mEndContainer, aNode)) {
+        EditorUtils::IsDescendantOf(*item->mEndContainer, *aNode)) {
       item->mEndContainer = parent;
       item->mEndOffset = offset;
     }
   }
 }
 
 nsresult
 RangeUpdater::SelAdjSplitNode(nsIContent& aOldRightNode,
--- a/editor/libeditor/SelectionState.h
+++ b/editor/libeditor/SelectionState.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_SelectionState_h
 #define mozilla_SelectionState_h
 
+#include "mozilla/EditorDOMPoint.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMNode.h"
 #include "nsINode.h"
 #include "nsTArray.h"
 #include "nscore.h"
 
 class nsCycleCollectionTraversalCallback;
 class nsIDOMCharacterData;
@@ -175,52 +176,75 @@ ImplCycleCollectionUnlink(RangeUpdater& 
 class MOZ_STACK_CLASS AutoTrackDOMPoint final
 {
 private:
   RangeUpdater& mRangeUpdater;
   // Allow tracking either nsIDOMNode or nsINode until nsIDOMNode is gone
   nsCOMPtr<nsINode>* mNode;
   nsCOMPtr<nsIDOMNode>* mDOMNode;
   int32_t* mOffset;
+  EditorDOMPoint* mPoint;
   RefPtr<RangeItem> mRangeItem;
 
 public:
   AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
                     nsCOMPtr<nsINode>* aNode, int32_t* aOffset)
     : mRangeUpdater(aRangeUpdater)
     , mNode(aNode)
     , mDOMNode(nullptr)
     , mOffset(aOffset)
+    , mPoint(nullptr)
   {
     mRangeItem = new RangeItem();
     mRangeItem->mStartContainer = *mNode;
     mRangeItem->mEndContainer = *mNode;
     mRangeItem->mStartOffset = *mOffset;
     mRangeItem->mEndOffset = *mOffset;
     mRangeUpdater.RegisterRangeItem(mRangeItem);
   }
 
   AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
                     nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset)
     : mRangeUpdater(aRangeUpdater)
     , mNode(nullptr)
     , mDOMNode(aNode)
     , mOffset(aOffset)
+    , mPoint(nullptr)
   {
     mRangeItem = new RangeItem();
     mRangeItem->mStartContainer = do_QueryInterface(*mDOMNode);
     mRangeItem->mEndContainer = do_QueryInterface(*mDOMNode);
     mRangeItem->mStartOffset = *mOffset;
     mRangeItem->mEndOffset = *mOffset;
     mRangeUpdater.RegisterRangeItem(mRangeItem);
   }
 
+  AutoTrackDOMPoint(RangeUpdater& aRangeUpdater,
+                    EditorDOMPoint* aPoint)
+    : mRangeUpdater(aRangeUpdater)
+    , mNode(nullptr)
+    , mDOMNode(nullptr)
+    , mOffset(nullptr)
+    , mPoint(aPoint)
+  {
+    mRangeItem = new RangeItem();
+    mRangeItem->mStartContainer = mPoint->Container();
+    mRangeItem->mEndContainer = mPoint->Container();
+    mRangeItem->mStartOffset = mPoint->Offset();
+    mRangeItem->mEndOffset = mPoint->Offset();
+    mRangeUpdater.RegisterRangeItem(mRangeItem);
+  }
+
   ~AutoTrackDOMPoint()
   {
     mRangeUpdater.DropRangeItem(mRangeItem);
+    if (mPoint) {
+      mPoint->Set(mRangeItem->mStartContainer, mRangeItem->mStartOffset);
+      return;
+    }
     if (mNode) {
       *mNode = mRangeItem->mStartContainer;
     } else {
       *mDOMNode = GetAsDOMNode(mRangeItem->mStartContainer);
     }
     *mOffset = mRangeItem->mStartOffset;
   }
 };