Bug 1460509 - part 61: Make HTMLEditRules::SplitBlock() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 17 May 2018 16:28:52 +0900
changeset 798779 628d6f26bd9f2054da351592bf1dcf1e0d69e17e
parent 798778 df2b6285101f7ad3bb5c17055c0e41891d5187fa
child 798780 2e022b1173c768f4d7c1378793e8cbdb72f63f17
push id110840
push usermasayuki@d-toybox.com
push dateWed, 23 May 2018 13:41:58 +0000
reviewersm_kato
bugs1460509
milestone62.0a1
Bug 1460509 - part 61: Make HTMLEditRules::SplitBlock() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato And this patch renames it to SplitRangeOffFromBlock() for making the name explain what it does since "split" is used as splitting to two nodes widely in editor. MozReview-Commit-ID: GrRu5sI4yrP
editor/libeditor/EditorUtils.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
--- a/editor/libeditor/EditorUtils.h
+++ b/editor/libeditor/EditorUtils.h
@@ -354,16 +354,112 @@ private:
   EditorDOMPoint mGivenSplitPoint;
 
   nsresult mRv;
 
   SplitNodeResult() = delete;
 };
 
 /***************************************************************************
+ * SplitRangeOffFromNodeResult class is a simple class for methods which split a
+ * node at 2 points for making part of the node split off from the node.
+ */
+class MOZ_STACK_CLASS SplitRangeOffFromNodeResult final
+{
+public:
+  bool Succeeded() const { return NS_SUCCEEDED(mRv); }
+  bool Failed() const { return NS_FAILED(mRv); }
+  nsresult Rv() const { return mRv; }
+
+  /**
+   * GetLeftContent() returns new created node before the part of quarried out.
+   * This may return nullptr if the method didn't split at start edge of
+   * the node.
+   */
+  nsIContent* GetLeftContent() const { return mLeftContent; }
+  dom::Element* GetLeftContentAsElement() const
+  {
+    return mLeftContent && mLeftContent->IsElement() ?
+             mLeftContent->AsElement() : nullptr;
+  }
+
+  /**
+   * GetMiddleContent() returns new created node between left node and right
+   * node.  I.e., this is quarried out from the node.  This may return nullptr
+   * if the method unwrapped the middle node.
+   */
+  nsIContent* GetMiddleContent() const { return mMiddleContent; }
+  dom::Element* GetMiddleContentAsElement() const
+  {
+    return mMiddleContent && mMiddleContent->IsElement() ?
+             mMiddleContent->AsElement() : nullptr;
+  }
+
+  /**
+   * GetRightContent() returns the right node after the part of quarried out.
+   * This may return nullptr it the method didn't split at end edge of the
+   * node.
+   */
+  nsIContent* GetRightContent() const { return mRightContent; }
+  dom::Element* GetRightContentAsElement() const
+  {
+    return mRightContent && mRightContent->IsElement() ?
+             mRightContent->AsElement() : nullptr;
+  }
+
+  SplitRangeOffFromNodeResult(nsIContent* aLeftContent, nsIContent* aMiddleContent,
+                   nsIContent* aRightContent)
+    : mLeftContent(aLeftContent)
+    , mMiddleContent(aMiddleContent)
+    , mRightContent(aRightContent)
+    , mRv(NS_OK)
+  {
+  }
+
+  SplitRangeOffFromNodeResult(SplitNodeResult& aSplitResultAtLeftOfMiddleNode,
+                         SplitNodeResult& aSplitResultAtRightOfMiddleNode)
+    : mRv(NS_OK)
+  {
+    if (aSplitResultAtLeftOfMiddleNode.Succeeded()) {
+      mLeftContent = aSplitResultAtLeftOfMiddleNode.GetPreviousNode();
+    }
+    if (aSplitResultAtRightOfMiddleNode.Succeeded()) {
+      mRightContent = aSplitResultAtRightOfMiddleNode.GetNextNode();
+      mMiddleContent = aSplitResultAtRightOfMiddleNode.GetPreviousNode();
+    }
+    if (!mMiddleContent && aSplitResultAtLeftOfMiddleNode.Succeeded()) {
+      mMiddleContent = aSplitResultAtLeftOfMiddleNode.GetNextNode();
+    }
+  }
+
+  explicit SplitRangeOffFromNodeResult(nsresult aRv)
+    : mRv(aRv)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mRv));
+  }
+
+  SplitRangeOffFromNodeResult(
+    const SplitRangeOffFromNodeResult& aOther) = delete;
+  SplitRangeOffFromNodeResult&
+  operator=(const SplitRangeOffFromNodeResult& aOther) = delete;
+  SplitRangeOffFromNodeResult(SplitRangeOffFromNodeResult&& aOther) = default;
+  SplitRangeOffFromNodeResult&
+  operator=(SplitRangeOffFromNodeResult&& aOther) = default;
+
+private:
+  nsCOMPtr<nsIContent> mLeftContent;
+  nsCOMPtr<nsIContent> mMiddleContent;
+  nsCOMPtr<nsIContent> mRightContent;
+
+  nsresult mRv;
+
+  SplitRangeOffFromNodeResult() = delete;
+};
+
+/***************************************************************************
  * stack based helper class for batching a collection of transactions inside a
  * placeholder transaction.
  */
 class MOZ_RAII AutoPlaceholderBatch final
 {
 private:
   RefPtr<EditorBase> mEditorBase;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -5371,108 +5371,113 @@ HTMLEditRules::WillOutdent(bool* aCancel
  */
 nsresult
 HTMLEditRules::RemovePartOfBlock(Element& aBlock,
                                  nsIContent& aStartChild,
                                  nsIContent& aEndChild)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  SplitBlock(aBlock, aStartChild, aEndChild);
+  SplitRangeOffFromNodeResult splitResult =
+    SplitRangeOffFromBlock(aBlock, aStartChild, aEndChild);
+  if (NS_WARN_IF(splitResult.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+  NS_WARNING_ASSERTION(splitResult.Succeeded(),
+    "Failed to split the range off from the block element");
   // Get rid of part of blockquote we are outdenting
   nsresult rv = HTMLEditorRef().RemoveBlockContainerWithTransaction(aBlock);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-void
-HTMLEditRules::SplitBlock(Element& aBlock,
-                          nsIContent& aStartChild,
-                          nsIContent& aEndChild,
-                          nsIContent** aOutLeftNode,
-                          nsIContent** aOutRightNode,
-                          nsIContent** aOutMiddleNode)
-{
-  MOZ_ASSERT(IsEditorDataAvailable());
-
-  // aStartChild and aEndChild must be exclusive descendants of aBlock
-  MOZ_ASSERT(EditorUtils::IsDescendantOf(aStartChild, aBlock) &&
-             EditorUtils::IsDescendantOf(aEndChild, aBlock));
+SplitRangeOffFromNodeResult
+HTMLEditRules::SplitRangeOffFromBlock(Element& aBlockElement,
+                                      nsIContent& aStartOfMiddleElement,
+                                      nsIContent& aEndOfMiddleElement)
+{
+  MOZ_ASSERT(IsEditorDataAvailable());
+
+  // aStartOfMiddleElement and aEndOfMiddleElement must be exclusive
+  // descendants of aBlockElement.
+  MOZ_ASSERT(EditorUtils::IsDescendantOf(aStartOfMiddleElement, aBlockElement));
+  MOZ_ASSERT(EditorUtils::IsDescendantOf(aEndOfMiddleElement, aBlockElement));
 
   // Split at the start.
   SplitNodeResult splitAtStartResult =
     HTMLEditorRef().SplitNodeDeepWithTransaction(
-                      aBlock, EditorRawDOMPoint(&aStartChild),
+                      aBlockElement, EditorRawDOMPoint(&aStartOfMiddleElement),
                       SplitAtEdges::eDoNotCreateEmptyContainer);
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
+  }
   NS_WARNING_ASSERTION(splitAtStartResult.Succeeded(),
-    "Failed to split aBlock at start");
+    "Failed to split aBlockElement at start");
 
   // Split at after the end
-  EditorRawDOMPoint atAfterEnd(&aEndChild);
+  EditorRawDOMPoint atAfterEnd(&aEndOfMiddleElement);
   DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset();
   NS_WARNING_ASSERTION(advanced,
     "Failed to advance offset after the end node");
   SplitNodeResult splitAtEndResult =
     HTMLEditorRef().SplitNodeDeepWithTransaction(
-                      aBlock, atAfterEnd,
+                      aBlockElement, atAfterEnd,
                       SplitAtEdges::eDoNotCreateEmptyContainer);
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
+  }
   NS_WARNING_ASSERTION(splitAtEndResult.Succeeded(),
-    "Failed to split aBlock at after end");
-
-  if (aOutLeftNode) {
-    NS_IF_ADDREF(*aOutLeftNode = splitAtStartResult.GetPreviousNode());
-  }
-
-  if (aOutRightNode) {
-    NS_IF_ADDREF(*aOutRightNode = splitAtEndResult.GetNextNode());
-  }
-
-  if (aOutMiddleNode) {
-    if (splitAtEndResult.GetPreviousNode()) {
-      NS_IF_ADDREF(*aOutMiddleNode = splitAtEndResult.GetPreviousNode());
-    } else {
-      NS_IF_ADDREF(*aOutMiddleNode = splitAtStartResult.GetNextNode());
-    }
-  }
+    "Failed to split aBlockElement at after end");
+
+  return SplitRangeOffFromNodeResult(splitAtStartResult, splitAtEndResult);
 }
 
 nsresult
 HTMLEditRules::OutdentPartOfBlock(Element& aBlock,
                                   nsIContent& aStartChild,
                                   nsIContent& aEndChild,
                                   bool aIsBlockIndentedWithCSS,
                                   nsIContent** aOutLeftNode,
                                   nsIContent** aOutRightNode)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
   MOZ_ASSERT(aOutLeftNode && aOutRightNode);
 
-  nsCOMPtr<nsIContent> middleNode;
-  SplitBlock(aBlock, aStartChild, aEndChild, aOutLeftNode, aOutRightNode,
-             getter_AddRefs(middleNode));
-
-  if (NS_WARN_IF(!middleNode) || NS_WARN_IF(!middleNode->IsElement())) {
+  SplitRangeOffFromNodeResult splitResult =
+    SplitRangeOffFromBlock(aBlock, aStartChild, aEndChild);
+  if (NS_WARN_IF(splitResult.Rv() == NS_ERROR_EDITOR_DESTROYED)) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
+  if (aOutLeftNode) {
+    NS_IF_ADDREF(*aOutLeftNode = splitResult.GetLeftContent());
+  }
+  if (aOutRightNode) {
+    NS_IF_ADDREF(*aOutRightNode = splitResult.GetRightContent());
+  }
+
+  if (NS_WARN_IF(!splitResult.GetMiddleContentAsElement())) {
     return NS_ERROR_FAILURE;
   }
 
   if (!aIsBlockIndentedWithCSS) {
     nsresult rv =
       HTMLEditorRef().RemoveBlockContainerWithTransaction(
-                        *middleNode->AsElement());
+                        *splitResult.GetMiddleContentAsElement());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
-  if (middleNode->IsElement()) {
-    // We do nothing if middleNode isn't an element
-    nsresult rv = DecreaseMarginToOutdent(*middleNode->AsElement());
+  if (splitResult.GetMiddleContentAsElement()) {
+    nsresult rv =
+      DecreaseMarginToOutdent(*splitResult.GetMiddleContentAsElement());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
 
   return NS_OK;
 }
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -526,22 +526,31 @@ protected:
    * Called after handling edit action.  This may adjust Selection, remove
    * unnecessary empty nodes, create <br> elements if needed, etc.
    */
   MOZ_MUST_USE nsresult
   AfterEditInner(EditAction action, nsIEditor::EDirection aDirection);
 
   nsresult RemovePartOfBlock(Element& aBlock, nsIContent& aStartChild,
                              nsIContent& aEndChild);
-  void SplitBlock(Element& aBlock,
-                  nsIContent& aStartChild,
-                  nsIContent& aEndChild,
-                  nsIContent** aOutLeftNode = nullptr,
-                  nsIContent** aOutRightNode = nullptr,
-                  nsIContent** aOutMiddleNode = nullptr);
+
+  /**
+   * SplitRangeOffFromBlock() splits aBlock at two points, before aStartChild
+   * and after aEndChild.  If they are very start or very end of aBlcok, this
+   * won't create empty block.
+   *
+   * @param aBlockElement           A block element which will be split.
+   * @param aStartOfMiddleElement   Start node of middle block element.
+   * @param aEndOfMiddleElement     End node of middle block element.
+   */
+  MOZ_MUST_USE SplitRangeOffFromNodeResult
+  SplitRangeOffFromBlock(Element& aBlockElement,
+                         nsIContent& aStartOfMiddleElement,
+                         nsIContent& aEndOfMiddleElement);
+
   nsresult OutdentPartOfBlock(Element& aBlock,
                               nsIContent& aStartChild,
                               nsIContent& aEndChild,
                               bool aIsBlockIndentedWithCSS,
                               nsIContent** aOutLeftNode,
                               nsIContent** aOutRightNode);
 
   /**