Bug 1460509 - part 69: Make HTMLEditRules::MakeBasicBlock() 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 19:56:41 +0900
changeset 798787 1adc8c22446a3b9bd313193f8bacc5e5681d22ae
parent 798786 ba48afee92855afc89673bdc0305af73f619cc7b
child 798788 5a402d861d0ed429a59a136231ed7d24518d79e5
push id110840
push usermasayuki@d-toybox.com
push dateWed, 23 May 2018 13:41:58 +0000
reviewersm_kato
bugs1460509
milestone62.0a1
Bug 1460509 - part 69: Make HTMLEditRules::MakeBasicBlock() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the editor r?m_kato MozReview-Commit-ID: Gfnai2runLs
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1913,17 +1913,24 @@ HTMLEditRules::WillInsertBreak(bool* aCa
     *aHandled = true;
     return NS_OK;
   }
 
   if (host == blockParent && separator != ParagraphSeparator::br) {
     // Insert a new block first
     MOZ_ASSERT(separator == ParagraphSeparator::div ||
                separator == ParagraphSeparator::p);
+    // MakeBasicBlock() creates AutoSelectionRestorer.
+    // Therefore, even if it returns NS_OK, editor might have been destroyed
+    // at restoring Selection.
     nsresult rv = MakeBasicBlock(ParagraphSeparatorElement(separator));
+    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED) ||
+        NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     // We warn on failure, but don't handle it, because it might be harmless.
     // Instead we just check that a new block was actually created.
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "HTMLEditRules::MakeBasicBlock() failed");
 
     firstRange = SelectionRef().GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
       return NS_ERROR_FAILURE;
@@ -4322,32 +4329,39 @@ HTMLEditRules::WillMakeBasicBlock(const 
   if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
 
   *aCancel = false;
   *aHandled = true;
 
+  // MakeBasicBlock() creates AutoSelectionRestorer.
+  // Therefore, even if it returns NS_OK, editor might have been destroyed
+  // at restoring Selection.
   rv = MakeBasicBlock(blockType);
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 HTMLEditRules::MakeBasicBlock(nsAtom& blockType)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsresult rv = NormalizeSelection();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
   AutoTransactionsConserveSelection dontChangeMySelection(&HTMLEditorRef());
 
   // Contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   rv = GetNodesFromSelection(EditAction::makeBasicBlock, arrayOfNodes,
                              TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -4377,88 +4391,113 @@ HTMLEditRules::MakeBasicBlock(nsAtom& bl
       // If the first editable node after selection is a br, consume it.
       // Otherwise it gets pushed into a following block after the split,
       // which is visually bad.
       nsCOMPtr<nsIContent> brContent =
         HTMLEditorRef().GetNextEditableHTMLNode(pointToInsertBlock);
       if (brContent && brContent->IsHTMLElement(nsGkAtoms::br)) {
         AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
         rv = HTMLEditorRef().DeleteNodeWithTransaction(*brContent);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       // Do the splits!
       SplitNodeResult splitNodeResult =
         HTMLEditorRef().SplitNodeDeepWithTransaction(
                           *curBlock, pointToInsertBlock,
                           SplitAtEdges::eDoNotCreateEmptyContainer);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
       EditorRawDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
       // Put a <br> element at the split point
       brContent =
         HTMLEditorRef().InsertBrElementWithTransaction(SelectionRef(),
                                                        pointToInsertBrNode);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(!brContent)) {
         return NS_ERROR_FAILURE;
       }
       // Put selection at the split point
       EditorRawDOMPoint atBrNode(brContent);
+      // Don't restore the selection
+      selectionRestorer.Abort();
       ErrorResult error;
       SelectionRef().Collapse(atBrNode, 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;
     }
 
     // We are making a block.  Consume a br, if needed.
     nsCOMPtr<nsIContent> brNode =
       HTMLEditorRef().GetNextEditableHTMLNodeInBlock(pointToInsertBlock);
     if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
       AutoEditorDOMPointChildInvalidator lockOffset(pointToInsertBlock);
       rv = HTMLEditorRef().DeleteNodeWithTransaction(*brNode);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // We don't need to act on this node any more
       arrayOfNodes.RemoveElement(brNode);
     }
     // Make sure we can put a block here.
     SplitNodeResult splitNodeResult =
       MaybeSplitAncestorsForInsertWithTransaction(blockType,
                                                   pointToInsertBlock);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
       return splitNodeResult.Rv();
     }
     RefPtr<Element> block =
       HTMLEditorRef().CreateNodeWithTransaction(blockType,
                                                 splitNodeResult.SplitPoint());
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(!block)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
     mNewBlock = block;
     // Delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
       rv = HTMLEditorRef().DeleteNodeWithTransaction(*curNode);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       arrayOfNodes.RemoveElementAt(0);
     }
+    // Don't restore the selection
+    selectionRestorer.Abort();
     // Put selection in new block
     rv = SelectionRef().Collapse(block, 0);
-    // Don't restore the selection
-    selectionRestorer.Abort();
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
   }
   // Okay, now go through all the nodes and make the right kind of blocks, or
   // whatever is approriate.  Woohoo!  Note: blockquote is handled a little
   // differently.
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -400,17 +400,31 @@ protected:
    */
   MOZ_MUST_USE nsresult
   WillRelativeChangeZIndex(int32_t aChange, bool* aCancel, bool* aHandled);
 
   nsresult WillMakeDefListItem(const nsAString* aBlockType, bool aEntireList,
                                bool* aCancel, bool* aHandled);
   nsresult WillMakeBasicBlock(const nsAString& aBlockType,
                               bool* aCancel, bool* aHandled);
-  nsresult MakeBasicBlock(nsAtom& aBlockType);
+
+  /**
+   * MakeBasicBlock() applies or clears block style around Selection.
+   * This method creates AutoSelectionRestorer.  Therefore, each caller
+   * need to check if the editor is still available even if this returns
+   * NS_OK.
+   *
+   * @param aBlockType          New block tag name.
+   *                            If nsGkAtoms::normal or nsGkAtoms::_empty,
+   *                            RemoveBlockStyle() will be called.
+   *                            If nsGkAtoms::blockquote, MakeBlockquote()
+   *                            will be called.
+   *                            Otherwise, ApplyBlockStyle() will be called.
+   */
+  MOZ_MUST_USE nsresult MakeBasicBlock(nsAtom& aBlockType);
 
   /**
    * Called after creating a basic block, indenting, outdenting or aligning
    * contents.  This method inserts moz-<br> element if start container of
    * Selection needs it.
    */
   MOZ_MUST_USE nsresult DidMakeBasicBlock();