Bug 1413181 - part 13: HTMLEditRules::MaybeSplitAncestorsForInsert() should be able to return a DOM point in text node r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Sun, 19 Nov 2017 11:05:26 +0900
changeset 701133 388355a409ffafab081de098dc3634a0d51a3d74
parent 701132 7b075bf9426bb22c1a9b23582403d3d6ef22cc3e
child 741097 0b159bee0cf392508fd11a1978f21845f2454bbc
push id90079
push usermasayuki@d-toybox.com
push dateTue, 21 Nov 2017 08:41:26 +0000
reviewersm_kato
bugs1413181
milestone59.0a1
Bug 1413181 - part 13: HTMLEditRules::MaybeSplitAncestorsForInsert() should be able to return a DOM point in text node r?m_kato HTMLEditRules::MaybeSplitAncestorsForInsert() may be called with a point in a text and it needs to return given split point as is. Additionally, the given point may be in a text node. So, it may not be represented with an nsCOMPtr<nsIContent>. Therefore, we need to add new member, EditorDOMPoint, to SplitNodeResult and when MaybeSplitAncestorsForInsert() needs to return the given point as is, it should use it. Note that if the methods which return SplitNodeResult split some nodes actually, the left node and/or the right node may be removed from the DOM tree. In this case, EditorDOMPoint cannot store such orphan node. Therefore, we cannot make mNextNode nor mPreviousNode EditorDOMPoint. MozReview-Commit-ID: LwH8RZzkrmT
editor/libeditor/EditorUtils.h
editor/libeditor/HTMLEditRules.cpp
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -170,43 +170,63 @@ public:
   }
 
   /**
    * GetRightNode() simply returns the right node which was split.
    * This won't return nullptr unless failed to split due to invalid arguments.
    */
   nsIContent* GetRightNode() const
   {
+    if (mGivenSplitPoint.IsSet()) {
+      return mGivenSplitPoint.GetChildAtOffset();
+    }
     return mPreviousNode && !mNextNode ? mPreviousNode : mNextNode;
   }
 
   /**
    * GetPreviousNode() returns previous node at the split point.
    */
-  nsIContent* GetPreviousNode() const { return mPreviousNode; }
+  nsIContent* GetPreviousNode() const
+  {
+    if (mGivenSplitPoint.IsSet()) {
+      return mGivenSplitPoint.IsEndOfContainer() ?
+               mGivenSplitPoint.GetChildAtOffset() : nullptr;
+    }
+    return mPreviousNode;
+  }
 
   /**
    * GetNextNode() returns next node at the split point.
    */
-  nsIContent* GetNextNode() const { return mNextNode; }
+  nsIContent* GetNextNode() const
+  {
+    if (mGivenSplitPoint.IsSet()) {
+      return !mGivenSplitPoint.IsEndOfContainer() ?
+                mGivenSplitPoint.GetChildAtOffset() : nullptr;
+    }
+    return mNextNode;
+  }
 
   /**
    * SplitPoint() returns the split point in the container.
    * This is useful when callers insert an element at split point with
    * EditorBase::CreateNode() or something similar methods.
    *
    * Note that the result is EditorRawDOMPoint but the nodes are grabbed
    * by this instance.  Therefore, the life time of both container node
    * and child node are guaranteed while using the result temporarily.
    */
   EditorRawDOMPoint SplitPoint() const
   {
     if (Failed()) {
       return EditorRawDOMPoint();
     }
+    if (mGivenSplitPoint.IsSet()) {
+      return mGivenSplitPoint.AsRaw();
+    }
     if (!mPreviousNode) {
       return EditorRawDOMPoint(mNextNode);
     }
     EditorRawDOMPoint point(mPreviousNode);
     DebugOnly<bool> advanced = point.AdvanceOffset();
     NS_WARNING_ASSERTION(advanced,
       "Failed to advance offset to after previous node");
     return point;
@@ -226,29 +246,53 @@ public:
     : mPreviousNode(aPreviousNodeOfSplitPoint)
     , mNextNode(aNextNodeOfSplitPoint)
     , mRv(NS_OK)
   {
     MOZ_DIAGNOSTIC_ASSERT(mPreviousNode || mNextNode);
   }
 
   /**
+   * This constructor should be used when the method didn't split any nodes
+   * but want to return given split point as right point.
+   */
+  explicit SplitNodeResult(const EditorRawDOMPoint& aGivenSplitPoint)
+    : mGivenSplitPoint(aGivenSplitPoint)
+    , mRv(NS_OK)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mGivenSplitPoint.IsSet());
+  }
+
+  /**
    * This constructor shouldn't be used by anybody except methods which
    * use this as error result when it fails.
    */
   explicit SplitNodeResult(nsresult aRv)
     : mRv(aRv)
   {
     MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv));
   }
 
 private:
+  // When methods which return this class split some nodes actually, they
+  // need to set a set of left node and right node to this class.  However,
+  // one or both of them may be moved or removed by mutation observer.
+  // In such case, we cannot represent the point with EditorDOMPoint since
+  // it requires current container node.  Therefore, we need to use
+  // nsCOMPtr<nsIContent> here instead.
   nsCOMPtr<nsIContent> mPreviousNode;
   nsCOMPtr<nsIContent> mNextNode;
 
+  // Methods which return this class may not split any nodes actually.  Then,
+  // they may want to return given split point as is since such behavior makes
+  // their callers simpler.  In this case, the point may be in a text node
+  // which cannot be represented as a node.  Therefore, we need EditorDOMPoint
+  // for representing the point.
+  EditorDOMPoint mGivenSplitPoint;
+
   nsresult mRv;
 
   SplitNodeResult() = delete;
 };
 
 /***************************************************************************
  * stack based helper class for batching a collection of transactions inside a
  * placeholder transaction.
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -7535,20 +7535,20 @@ HTMLEditRules::MaybeSplitAncestorsForIns
       // Found an ancestor node which can contain the element.
       break;
     }
   }
 
   MOZ_DIAGNOSTIC_ASSERT(pointToInsert.IsSet());
 
   // If the point itself can contain the tag, we don't need to split any
-  // ancestor nodes.
+  // ancestor nodes.  In this case, we should return the given split point
+  // as is.
   if (pointToInsert.Container() == aStartOfDeepestRightNode.Container()) {
-    return SplitNodeResult(nullptr,
-                           aStartOfDeepestRightNode.GetChildAtOffset());
+    return SplitNodeResult(aStartOfDeepestRightNode);
   }
 
   SplitNodeResult splitNodeResult =
     htmlEditor->SplitNodeDeep(*pointToInsert.GetChildAtOffset(),
                               aStartOfDeepestRightNode,
                               SplitAtEdges::eAllowToCreateEmptyContainer);
   NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
     "Failed to split the node for insert the element");