Bug 1460509 - part 58: Make HTMLEditRules::WillAlign() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 16 May 2018 15:23:52 +0900
changeset 798776 f61b6a2b5cf0b76912b41c18dbad8cadb7e03893
parent 798775 4511e5964295d84fad6b73f9085132e2bf5763b6
child 798777 7b93bc7c22082f29e55b05864ee5ed6a85e8aa91
push id110840
push usermasayuki@d-toybox.com
push dateWed, 23 May 2018 13:41:58 +0000
reviewersm_kato
bugs1460509
milestone62.0a1
Bug 1460509 - part 58: Make HTMLEditRules::WillAlign() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato MozReview-Commit-ID: L6rjqZABqsI
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -5666,25 +5666,41 @@ HTMLEditRules::WillAlign(const nsAString
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
 
   rv = NormalizeSelection();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+
+  *aHandled = true;
+  rv = AlignContentsAtSelection(aAlignType);
+  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
+      NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType)
+{
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
 
   // Convert the selection ranges into "promoted" selection ranges: This
   // basically just expands the range to include the immediate block parent,
   // and then further expands to include any ancestors whose children are all
   // in the range
-  *aHandled = true;
   nsTArray<OwningNonNull<nsINode>> nodeArray;
-  rv = GetNodesFromSelection(EditAction::align, nodeArray, TouchContent::yes);
+  nsresult rv =
+    GetNodesFromSelection(EditAction::align, nodeArray, TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If we don't have any nodes, or we have only a single br, then we are
   // creating an empty alignment div.  We have to do some different things for
   // these.
   bool emptyDiv = nodeArray.IsEmpty();
@@ -5761,46 +5777,55 @@ HTMLEditRules::WillAlign(const nsAString
       nsCOMPtr<nsIContent> sibling;
       if (pointToInsertDiv.GetChild()) {
         sibling =
           HTMLEditorRef().GetNextHTMLSibling(pointToInsertDiv.GetChild());
       }
       if (sibling && !IsBlockNode(*sibling)) {
         AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertDiv);
         rv = HTMLEditorRef().DeleteNodeWithTransaction(*brContent);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
     }
     RefPtr<Element> div =
       HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
                                                 pointToInsertDiv);
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(!div)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
     mNewBlock = div;
     // Set up the alignment on the div, using HTML or CSS
     rv = AlignBlock(*div, aAlignType, ResetAlignOf::OnlyDescendants);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-    *aHandled = true;
     // Put in a moz-br so that it won't get deleted
     CreateElementResult createMozBrResult =
       CreateMozBR(EditorRawDOMPoint(div, 0));
     if (NS_WARN_IF(createMozBrResult.Failed())) {
       return createMozBrResult.Rv();
     }
     EditorRawDOMPoint atStartOfDiv(div, 0);
+    // Don't restore the selection
+    selectionRestorer.Abort();
     ErrorResult error;
     SelectionRef().Collapse(atStartOfDiv, error);
-    // Don't restore the selection
-    selectionRestorer.Abort();
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      error.SuppressException();
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
     return NS_OK;
   }
 
   // Next we detect all the transitions in the array, where a transition
   // means that adjacent nodes in the array don't have the same parent.
@@ -5863,16 +5888,19 @@ HTMLEditRules::WillAlign(const nsAString
       rv = RemoveAlignment(*curNode, aAlignType, true);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (useCSS) {
         HTMLEditorRef().mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
                                          curNode->AsElement(), nullptr,
                                          nsGkAtoms::align, &aAlignType, false);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         curDiv = nullptr;
         continue;
       }
       if (HTMLEditUtils::IsList(atCurNode.GetContainer())) {
         // If we don't use CSS, add a content to list element: they have to
         // be inside another list, i.e., >= second level of nesting.
         // XXX AlignInnerBlocks() handles list item elements and table cells.
         //     Is it intentional to change alignment of nested other type
@@ -5900,32 +5928,38 @@ HTMLEditRules::WillAlign(const nsAString
       SplitNodeResult splitNodeResult =
         MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div, atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
       curDiv =
         HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
                                                   splitNodeResult.SplitPoint());
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(!curDiv)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
       mNewBlock = curDiv;
       // Set up the alignment on the div
       rv = AlignBlock(*curDiv, aAlignType, ResetAlignOf::OnlyDescendants);
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to align the <div>");
     }
 
     // Tuck the node into the end of the active div
     rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
                                                       *curDiv);
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -326,16 +326,26 @@ protected:
                         const nsAString* aBulletType,
                         bool* aCancel, bool* aHandled,
                         const nsAString* aItemType = nullptr);
   nsresult WillRemoveList(bool aOrdered, bool* aCancel, bool* aHandled);
   nsresult WillIndent(bool* aCancel, bool* aHandled);
   nsresult WillCSSIndent(bool* aCancel, bool* aHandled);
   nsresult WillHTMLIndent(bool* aCancel, bool* aHandled);
   nsresult WillOutdent(bool* aCancel, bool* aHandled);
+
+  /**
+   * Called before aligning contents around Selection.  This method actually
+   * sets align attributes to align contents.
+   *
+   * @param aAlignType          New align attribute value where the contents
+   *                            should be aligned to.
+   * @param aCancel             Returns true if the operation is canceled.
+   * @param aHandled            Returns true if the edit action is handled.
+   */
   nsresult WillAlign(const nsAString& aAlignType,
                      bool* aCancel, bool* aHandled);
 
   /**
    * Called before changing absolute positioned element to static positioned.
    * This method actually changes the position property of nearest absolute
    * positioned element.  Therefore, this might cause destroying the HTML
    * editor.
@@ -421,16 +431,30 @@ protected:
    * @param aNode               The node whose contents should be aligned
    *                            to aAlignType.
    * @param aAlignType          New value of align attribute of <div> which
    *                            is only child of aNode.
    */
   MOZ_MUST_USE nsresult
   AlignBlockContents(nsINode& aNode, const nsAString& aAlignType);
 
+  /**
+   * AlignContentsAtSelection() aligns contents around Selection to aAlignType.
+   * This creates AutoSelectionRestorer.  Therefore, even if this returns
+   * NS_OK, CanHandleEditAction() may return false if the editor is destroyed
+   * during restoring the Selection.  So, every caller needs to check if
+   * CanHandleEditAction() returns true before modifying the DOM tree or
+   * changing Selection.
+   *
+   * @param aAlignType          New align attribute value where the contents
+   *                            should be aligned to.
+   */
+  MOZ_MUST_USE nsresult
+  AlignContentsAtSelection(const nsAString& aAlignType);
+
   nsresult AppendInnerFormatNodes(nsTArray<OwningNonNull<nsINode>>& aArray,
                                   nsINode* aNode);
   nsresult GetFormatString(nsINode* aNode, nsAString &outFormat);
   enum class Lists { no, yes };
   enum class Tables { no, yes };
   void GetInnerContent(nsINode& aNode,
                        nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
                        int32_t* aIndex, Lists aLists = Lists::yes,