Bug 1460509 - part 44: Make HTMLEditRules::GetNodesForOperation() and related methods return NS_ERROR_EDITOR_DESTROYED if they cause destroying the editor r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 15 May 2018 18:07:34 +0900
changeset 798762 137b2f47174b3c469933f7bef433bad57fe73c60
parent 798761 29a58365db705ddab2c83c30c9b14b6e771f2377
child 798763 721d4dddaa2a5a4af8898e08777f75b8b4dc2e44
push id110840
push usermasayuki@d-toybox.com
push dateWed, 23 May 2018 13:41:58 +0000
reviewersm_kato
bugs1460509
milestone62.0a1
Bug 1460509 - part 44: Make HTMLEditRules::GetNodesForOperation() and related methods return NS_ERROR_EDITOR_DESTROYED if they cause destroying the editor r?m_kato Despite of the name of GetNodesForOperation() and related methods, it changes the DOM tree if their aTouchContent is TouchContent::yes (by default). Therefore, they should return NS_ERROR_EDITOR_DESTROYED if they cause destroying the editor and all callers should check the result. Therefore, this patch mark them as MOZ_MUST_USE. Additionally, because of importance of aTouchContent, this patch makes the arguments not optional. This change must make other developers being careful to use them. (Although TouchContent does not feel dangerous. We should rename it to make its risk clearer.) On the other hand, this patch removes aTouchContent from GetParagraphFormatNodes() since it's always called with TouchContent::no. Therefore, this method is always safe. Although I tried to document those methods, but I have not understood them completely yet. Perhaps, we should redesign them in another bug both to learn them and making them faster and simpler. MozReview-Commit-ID: 4vknJGUdwEe
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -1217,17 +1217,17 @@ HTMLEditRules::GetParagraphState(bool* a
 
   AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
 
   bool bMixed = false;
   // using "x" as an uninitialized value, since "" is meaningful
   nsAutoString formatStr(NS_LITERAL_STRING("x"));
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-  nsresult rv = GetParagraphFormatNodes(arrayOfNodes, TouchContent::no);
+  nsresult rv = GetParagraphFormatNodes(arrayOfNodes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // post process list.  We need to replace any block nodes that are not format
   // nodes with their content.  This is so we only have to look "up" the hierarchy
   // to find format nodes, instead of both up and down.
   for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
@@ -3797,17 +3797,18 @@ HTMLEditRules::WillMakeList(const nsAStr
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   rv = GetListActionNodes(arrayOfNodes,
-                          aEntireList ? EntireList::yes : EntireList::no);
+                          aEntireList ? EntireList::yes : EntireList::no,
+                          TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // check if all our nodes are <br>s, or empty inlines
   bool bOnlyBreaks = true;
   for (auto& curNode : arrayOfNodes) {
     // if curNode is not a Break or empty inline, we're done
@@ -4147,17 +4148,17 @@ HTMLEditRules::WillRemoveList(bool aOrde
 
   AutoSelectionRestorer selectionRestorer(&SelectionRef(), &HTMLEditorRef());
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
   GetPromotedRanges(arrayOfRanges, EditAction::makeList);
 
   // use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-  rv = GetListActionNodes(arrayOfNodes, EntireList::no);
+  rv = GetListActionNodes(arrayOfNodes, EntireList::no, TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Remove all non-editable nodes.  Leave them be.
   for (int32_t i = arrayOfNodes.Length() - 1; i >= 0; i--) {
     OwningNonNull<nsINode> testNode = arrayOfNodes[i];
     if (!HTMLEditorRef().IsEditable(testNode)) {
@@ -4237,17 +4238,18 @@ HTMLEditRules::MakeBasicBlock(nsAtom& bl
   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);
+  rv = GetNodesFromSelection(EditAction::makeBasicBlock, arrayOfNodes,
+                             TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
     nsRange* firstRange = SelectionRef().GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
@@ -4465,17 +4467,18 @@ HTMLEditRules::WillCSSIndent(bool* aCanc
 
   if (liNode) {
     arrayOfNodes.AppendElement(*liNode);
   } else {
     // 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
-    rv = GetNodesFromSelection(EditAction::indent, arrayOfNodes);
+    rv = GetNodesFromSelection(EditAction::indent, arrayOfNodes,
+                               TouchContent::yes);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   // if nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
     // get selection location
@@ -4699,17 +4702,18 @@ HTMLEditRules::WillHTMLIndent(bool* aCan
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
   GetPromotedRanges(arrayOfRanges, EditAction::indent);
 
   // use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-  rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditAction::indent);
+  rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes, EditAction::indent,
+                            TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // if nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
     nsRange* firstRange = SelectionRef().GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
@@ -4969,17 +4973,18 @@ HTMLEditRules::WillOutdent(bool* aCancel
   {
     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
     nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-    rv = GetNodesFromSelection(EditAction::outdent, arrayOfNodes);
+    rv = GetNodesFromSelection(EditAction::outdent, arrayOfNodes,
+                               TouchContent::yes);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Okay, now go through all the nodes and remove a level of blockquoting,
     // or whatever is appropriate.  Wohoo!
 
     nsCOMPtr<Element> curBlockQuote;
@@ -5562,17 +5567,17 @@ HTMLEditRules::WillAlign(const nsAString
   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);
+  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();
@@ -6774,35 +6779,39 @@ HTMLEditRules::GetNodesForOperation(
         continue;
       }
 
       if (!atEnd.IsStartOfContainer() && !atEnd.IsEndOfContainer()) {
         // Split the text node.
         ErrorResult error;
         nsCOMPtr<nsIContent> newLeftNode =
           HTMLEditorRef().SplitNodeWithTransaction(atEnd, error);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          error.SuppressException();
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(error.Failed())) {
           return error.StealNSResult();
         }
 
         // Correct the range.
         // The new end parent becomes the parent node of the text.
-        // XXX We want nsRange::SetEnd(const RawRangeBoundary&)
         EditorRawDOMPoint atContainerOfSplitNode(atEnd.GetContainer());
+        MOZ_ASSERT(!range->IsInSelection());
         range->SetEnd(atContainerOfSplitNode, error);
         if (NS_WARN_IF(error.Failed())) {
           error.SuppressException();
         }
       }
     }
   }
 
   // Bust up any inlines that cross our range endpoints, but only if we are
   // allowed to touch content.
-
+  // XXX Why don't we merge this block with the previous block?
   if (aTouchContent == TouchContent::yes) {
     nsTArray<OwningNonNull<RangeItem>> rangeItemArray;
     rangeItemArray.AppendElements(aArrayOfRanges.Length());
 
     // First register ranges for special editor gravity
     for (auto& rangeItem : rangeItemArray) {
       rangeItem = new RangeItem();
       rangeItem->StoreRange(aArrayOfRanges[0]);
@@ -6898,16 +6907,17 @@ HTMLEditRules::GetNodesForOperation(
   if (aOperationType == EditAction::makeBasicBlock ||
       aOperationType == EditAction::makeList ||
       aOperationType == EditAction::align ||
       aOperationType == EditAction::setAbsolutePosition ||
       aOperationType == EditAction::indent ||
       aOperationType == EditAction::outdent) {
     for (int32_t i = aOutArrayOfNodes.Length() - 1; i >= 0; i--) {
       OwningNonNull<nsINode> node = aOutArrayOfNodes[i];
+      // XXX Why do we run this loop even when aTouchContent is "no"?
       if (aTouchContent == TouchContent::yes && IsInlineNode(node) &&
           HTMLEditorRef().IsContainer(node) && !EditorBase::IsTextNode(node)) {
         nsTArray<OwningNonNull<nsINode>> arrayOfInlines;
         nsresult rv = BustUpInlinesAtBRs(*node->AsContent(), arrayOfInlines);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
@@ -7065,25 +7075,24 @@ HTMLEditRules::GetDefinitionListItemType
     } else if (child->IsHTMLElement(nsGkAtoms::dd)) {
       *aDD = true;
     }
   }
 }
 
 nsresult
 HTMLEditRules::GetParagraphFormatNodes(
-                 nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
-                 TouchContent aTouchContent)
+                 nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes)
 {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // Contruct a list of nodes to act on.
   nsresult rv =
    GetNodesFromSelection(EditAction::makeBasicBlock,
-                         outArrayOfNodes, aTouchContent);
+                         outArrayOfNodes, TouchContent::no);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Pre-process our list of nodes
   for (int32_t i = outArrayOfNodes.Length() - 1; i >= 0; i--) {
     OwningNonNull<nsINode> testNode = outArrayOfNodes[i];
 
@@ -7294,21 +7303,21 @@ HTMLEditRules::GetNodesFromPoint(
 
   // Make array of ranges
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
 
   // Stuff new opRange into array
   arrayOfRanges.AppendElement(range);
 
   // Use these ranges to contruct a list of nodes to act on
-  nsresult rv2 =
+  nsresult rv =
     GetNodesForOperation(arrayOfRanges, outArrayOfNodes, aOperation,
                          aTouchContent);
-  if (NS_WARN_IF(NS_FAILED(rv2))) {
-    return rv2;
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   return NS_OK;
 }
 
 /**
  * GetNodesFromSelection() constructs a list of nodes from the selection that
  * will be operated on.
@@ -10387,17 +10396,18 @@ HTMLEditRules::PrepareToMakeElementAbsol
   // in the range.
 
   nsTArray<RefPtr<nsRange>> arrayOfRanges;
   GetPromotedRanges(arrayOfRanges, EditAction::setAbsolutePosition);
 
   // Use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
-                                     EditAction::setAbsolutePosition);
+                                     EditAction::setAbsolutePosition,
+                                     TouchContent::yes);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes)) {
     nsRange* firstRange = SelectionRef().GetRangeAt(0);
     if (NS_WARN_IF(!firstRange)) {
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -472,41 +472,38 @@ protected:
   nsresult ExpandSelectionForDeletion();
   nsresult NormalizeSelection();
   EditorDOMPoint GetPromotedPoint(RulesEndpoint aWhere, nsINode& aNode,
                                   int32_t aOffset, EditAction actionID);
   void GetPromotedRanges(nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
                          EditAction inOperationType);
   void PromoteRange(nsRange& aRange, EditAction inOperationType);
   enum class TouchContent { no, yes };
-  nsresult GetNodesForOperation(
-             nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
-             nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
-             EditAction aOperationType,
-             TouchContent aTouchContent = TouchContent::yes);
+  MOZ_MUST_USE nsresult
+  GetNodesForOperation(nsTArray<RefPtr<nsRange>>& aArrayOfRanges,
+                       nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
+                       EditAction aOperationType, TouchContent aTouchContent);
   void GetChildNodesForOperation(
          nsINode& aNode,
          nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
-  nsresult GetNodesFromPoint(const EditorDOMPoint& aPoint,
-                             EditAction aOperation,
-                             nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
-                             TouchContent aTouchContent);
-  nsresult GetNodesFromSelection(
-             EditAction aOperation,
-             nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
-             TouchContent aTouchContent = TouchContent::yes);
+  MOZ_MUST_USE nsresult
+  GetNodesFromPoint(const EditorDOMPoint& aPoint, EditAction aOperation,
+                    nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
+                    TouchContent aTouchContent);
+  MOZ_MUST_USE nsresult
+  GetNodesFromSelection(EditAction aOperation,
+                        nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
+                        TouchContent aTouchContent);
   enum class EntireList { no, yes };
-  nsresult GetListActionNodes(
-             nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
-             EntireList aEntireList,
-             TouchContent aTouchContent = TouchContent::yes);
+  MOZ_MUST_USE nsresult
+  GetListActionNodes(nsTArray<OwningNonNull<nsINode>>& aOutArrayOfNodes,
+                     EntireList aEntireList, TouchContent aTouchContent);
   void GetDefinitionListItemTypes(Element* aElement, bool* aDT, bool* aDD);
-  nsresult GetParagraphFormatNodes(
-             nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes,
-             TouchContent aTouchContent = TouchContent::yes);
+  nsresult
+  GetParagraphFormatNodes(nsTArray<OwningNonNull<nsINode>>& outArrayOfNodes);
   void LookInsideDivBQandList(nsTArray<OwningNonNull<nsINode>>& aNodeArray);
 
   /**
    * BustUpInlinesAtRangeEndpoints() splits nodes at both start and end of
    * aRangeItem.  If this splits at every point, this modifies aRangeItem
    * to point each split point (typically, right node).  Note that this splits
    * nodes only in highest inline element at every point.
    *