Bug 1460509 - part 17: Make HTMLEditRules::WillAbsolutePosition() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the HTML editor r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 11 May 2018 20:25:01 +0900
changeset 798735 f38656bd5025dc67547854c02553c9e5aa57432a
parent 798734 f0d8798b6529323b8a7837f135715823de4d7b52
child 798736 76dcff52103c4b865b15677c0819392a6335f2a8
push id110840
push usermasayuki@d-toybox.com
push dateWed, 23 May 2018 13:41:58 +0000
reviewersm_kato
bugs1460509
milestone62.0a1
Bug 1460509 - part 17: Make HTMLEditRules::WillAbsolutePosition() return NS_ERROR_EDITOR_DESTROYED if it causes destroying the HTML editor r?m_kato MozReview-Commit-ID: EGqb0hNpNnc
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -9964,30 +9964,54 @@ HTMLEditRules::WillAbsolutePosition(bool
     mNewBlock = focusElement;
     return NS_OK;
   }
 
   nsresult rv = NormalizeSelection();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
+
+  rv = PrepareToMakeElementAbsolutePosition(aHandled, address_of(mNewBlock));
+  // PrepareToMakeElementAbsolutePosition() may restore selection with
+  // AutoSelectionRestorer.  Therefore, the editor might have already been
+  // destroyed now.
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
+HTMLEditRules::PrepareToMakeElementAbsolutePosition(
+                 bool* aHandled,
+                 RefPtr<Element>* aTargetElement)
+{
+  MOZ_ASSERT(IsEditorDataAvailable());
+
+  MOZ_ASSERT(aHandled);
+  MOZ_ASSERT(aTargetElement);
+
   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<RefPtr<nsRange>> arrayOfRanges;
   GetPromotedRanges(arrayOfRanges, EditAction::setAbsolutePosition);
 
   // Use these ranges to contruct a list of nodes to act on.
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-  rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
-                            EditAction::setAbsolutePosition);
+  nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
+                                     EditAction::setAbsolutePosition);
   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)) {
@@ -10004,36 +10028,46 @@ HTMLEditRules::WillAbsolutePosition(bool
       MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
                                                   atStartOfSelection);
     if (NS_WARN_IF(splitNodeResult.Failed())) {
       return splitNodeResult.Rv();
     }
     RefPtr<Element> positionedDiv =
       HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
                                                 splitNodeResult.SplitPoint());
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(!positionedDiv)) {
       return NS_ERROR_FAILURE;
     }
     // Remember our new block for postprocessing
-    mNewBlock = positionedDiv;
+    *aTargetElement = positionedDiv;
     // 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);
     }
     // Put selection in new block
     *aHandled = true;
+    // Don't restore the selection
+    selectionRestorer.Abort();
     ErrorResult error;
     SelectionRef().Collapse(RawRangeBoundary(positionedDiv, 0), 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;
   }
 
   // Okay, now go through all the nodes and put them in a blockquote, or
   // whatever is appropriate.  Woohoo!
@@ -10069,34 +10103,43 @@ HTMLEditRules::WillAbsolutePosition(bool
                                                       atCurNode);
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
         if (!curPositionedDiv) {
           curPositionedDiv =
             HTMLEditorRef().CreateNodeWithTransaction(
                               *nsGkAtoms::div, splitNodeResult.SplitPoint());
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           NS_WARNING_ASSERTION(curPositionedDiv,
             "Failed to create current positioned div element");
-          mNewBlock = curPositionedDiv;
+          *aTargetElement = curPositionedDiv;
         }
         EditorRawDOMPoint atEndOfCurPositionedDiv;
         atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
         curList =
           HTMLEditorRef().CreateNodeWithTransaction(*containerName,
                                                     atEndOfCurPositionedDiv);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
         // curList is now the correct thing to put curNode in.  Remember our
         // new block for postprocessing.
       }
       // Tuck the node into the end of the active list
       rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
                                                         *curList);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       continue;
     }
 
     // Not a list item, use blockquote?  If we are inside a list item, we
     // don't want to blockquote, we want to sublist the list item.  We may
@@ -10130,66 +10173,81 @@ HTMLEditRules::WillAbsolutePosition(bool
         if (NS_WARN_IF(splitNodeResult.Failed())) {
           return splitNodeResult.Rv();
         }
         if (!curPositionedDiv) {
           EditorRawDOMPoint atListItemParent(atListItem.GetContainer());
           curPositionedDiv =
             HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
                                                       atListItemParent);
+          if (NS_WARN_IF(!CanHandleEditAction())) {
+            return NS_ERROR_EDITOR_DESTROYED;
+          }
           NS_WARNING_ASSERTION(curPositionedDiv,
             "Failed to create current positioned div element");
-          mNewBlock = curPositionedDiv;
+          *aTargetElement = curPositionedDiv;
         }
         EditorRawDOMPoint atEndOfCurPositionedDiv;
         atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
         curList =
           HTMLEditorRef().CreateNodeWithTransaction(*containerName,
                                                     atEndOfCurPositionedDiv);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
         if (NS_WARN_IF(!curList)) {
           return NS_ERROR_FAILURE;
         }
       }
       rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*listItem, *curList);
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       // Remember we indented this li
       indentedLI = listItem;
       continue;
     }
 
     // Need to make a div to put things in if we haven't already
     if (!curPositionedDiv) {
       if (curNode->IsHTMLElement(nsGkAtoms::div)) {
         curPositionedDiv = curNode->AsElement();
-        mNewBlock = curPositionedDiv;
+        *aTargetElement = curPositionedDiv;
         curList = nullptr;
         continue;
       }
       SplitNodeResult splitNodeResult =
         MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::div,
                                                     atCurNode);
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
       curPositionedDiv =
         HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div,
                                                   splitNodeResult.SplitPoint());
+      if (NS_WARN_IF(!CanHandleEditAction())) {
+        return NS_ERROR_EDITOR_DESTROYED;
+      }
       if (NS_WARN_IF(!curPositionedDiv)) {
         return NS_ERROR_FAILURE;
       }
       // Remember our new block for postprocessing
-      mNewBlock = curPositionedDiv;
+      *aTargetElement = curPositionedDiv;
       // curPositionedDiv is now the correct thing to put curNode in
     }
 
     // Tuck the node into the end of the active blockquote
     rv = HTMLEditorRef().MoveNodeToEndWithTransaction(*curNode->AsContent(),
                                                       *curPositionedDiv);
+    if (NS_WARN_IF(!CanHandleEditAction())) {
+      return NS_ERROR_EDITOR_DESTROYED;
+    }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     // Forget curList, if any
     curList = nullptr;
   }
   return NS_OK;
 }
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -253,17 +253,16 @@ protected:
                         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);
   nsresult WillAlign(const nsAString& aAlignType,
                      bool* aCancel, bool* aHandled);
-  nsresult WillAbsolutePosition(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.
    *
    * @param aCancel             Returns true if the operation is canceled.
@@ -288,16 +287,42 @@ protected:
   nsresult WillMakeDefListItem(const nsAString* aBlockType, bool aEntireList,
                                bool* aCancel, bool* aHandled);
   nsresult WillMakeBasicBlock(const nsAString& aBlockType,
                               bool* aCancel, bool* aHandled);
   nsresult MakeBasicBlock(nsAtom& aBlockType);
   nsresult DidMakeBasicBlock(RulesInfo* aInfo, nsresult aResult);
 
   /**
+   * Called before changing an element to absolute positioned.
+   * This method only prepares the operation since DidAbsolutePosition() will
+   * change it actually later.  mNewBlock is set to the target element and
+   * if necessary, some ancestor nodes of selection may be split.
+   *
+   * @param aCancel             Returns true if the operation is canceled.
+   * @param aHandled            Returns true if the edit action is handled.
+   */
+  MOZ_MUST_USE nsresult WillAbsolutePosition(bool* aCancel, bool* aHandled);
+
+  /**
+   * PrepareToMakeElementAbsolutePosition() is helper method of
+   * WillAbsolutePosition() since in some cases, needs to restore selection
+   * with AutoSelectionRestorer.  So, all callers have to check if
+   * CanHandleEditAction() still returns true after a call of this method.
+   * XXX Should be documented outline of this method.
+   *
+   * @param aHandled            Returns true if the edit action is handled.
+   * @param aTargetElement      Returns target element which should be
+   *                            changed to absolute positioned.
+   */
+  MOZ_MUST_USE nsresult
+  PrepareToMakeElementAbsolutePosition(bool* aHandled,
+                                       RefPtr<Element>* aTargetElement);
+
+  /**
    * Called if nobody handles the edit action to make an element absolute
    * positioned.
    * This method actually changes the element which is computed by
    * WillAbsolutePosition() to absolute positioned.
    * Therefore, this might cause destroying the HTML editor.
    */
   MOZ_MUST_USE nsresult DidAbsolutePosition();
 
@@ -591,17 +616,17 @@ protected:
   bool mListenerEnabled;
   bool mReturnInEmptyLIKillsList;
   bool mDidDeleteSelection;
   bool mDidRangedDelete;
   bool mRestoreContentEditableCount;
   RefPtr<nsRange> mUtilRange;
   // Need to remember an int across willJoin/didJoin...
   uint32_t mJoinOffset;
-  nsCOMPtr<Element> mNewBlock;
+  RefPtr<Element> mNewBlock;
   RefPtr<RangeItem> mRangeItem;
 
   // XXX In strict speaking, mCachedStyles isn't enough to cache inline styles
   //     because inline style can be specified with "style" attribute and/or
   //     CSS in <style> elements or CSS files.  So, we need to look for better
   //     implementation about this.
   StyleCache mCachedStyles[SIZE_STYLE_TABLE];
 };