Bug 1501260 - Make HTMLEditRules::DocumentModifiedWorker() create bogus node via editor instance r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 25 Oct 2018 05:55:04 +0000
changeset 491341 f01896424c3a2fa77c20f9b9b40da89a2a09c1ba
parent 491340 a6499b00d6d6bac010206d457bb1643f7f68e1ed
child 491342 5e471c480b8a6ba4939a6c159d86a687a24bb5d8
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersm_kato
bugs1501260, 1465702
milestone65.0a1
Bug 1501260 - Make HTMLEditRules::DocumentModifiedWorker() create bogus node via editor instance r=m_kato HTMLEditRules::DocumentModifiedWorker() may be called asynchronously via AddScriptRunnder. Therefore, this may not be in the stack while editor handles an edit action. Then, HTMLEditRules cannot access edit action data which will be put on the stack after fixing bug 1465702. So, it should do it after once calling a method of editor instance (and editor instance should call back proper HTMLEditRules method). Differential Revision: https://phabricator.services.mozilla.com/D9536
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditRules.h
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
editor/libeditor/TextEditRules.cpp
editor/libeditor/TextEditRules.h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -11383,22 +11383,26 @@ HTMLEditRules::DocumentModified()
 
 void
 HTMLEditRules::DocumentModifiedWorker()
 {
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return;
   }
 
-  Selection* selection = mHTMLEditor->GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return;
-  }
-
-  AutoSafeEditorData setData(*this, *mHTMLEditor, *selection);
+  RefPtr<HTMLEditor> htmlEditor(mHTMLEditor);
+  htmlEditor->OnModifyDocument();
+}
+
+void
+HTMLEditRules::OnModifyDocument(Selection& aSelection)
+{
+  MOZ_ASSERT(mHTMLEditor);
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor, aSelection);
 
   // DeleteNodeWithTransaction() below may cause a flush, which could destroy
   // the editor
   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
   // Delete our bogus node, if we have one, since the document might not be
   // empty any more.
   if (mBogusNode) {
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -103,17 +103,21 @@ public:
   virtual nsresult WillDoAction(Selection* aSelection,
                                 EditSubActionInfo& aInfo,
                                 bool* aCancel,
                                 bool* aHandled) override;
   virtual nsresult DidDoAction(Selection* aSelection,
                                EditSubActionInfo& aInfo,
                                nsresult aResult) override;
   virtual bool DocumentIsEmpty() override;
-  virtual nsresult DocumentModified() override;
+
+  /**
+   * DocumentModified() is called when editor content is changed.
+   */
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult DocumentModified();
 
   nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
   nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
   nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
   nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
 
   /**
    * MakeSureElemStartsAndEndsOnCR() inserts <br> element at start (and/or end)
@@ -140,16 +144,22 @@ public:
                      const nsAString& aString);
   void DidDeleteText(Selection& aSelection,
                      nsINode& aTextNode, int32_t aOffset, int32_t aLength);
   void WillDeleteSelection(Selection& aSelection);
 
   void StartToListenToEditSubActions() { mListenerEnabled = true; }
   void EndListeningToEditSubActions() { mListenerEnabled = false; }
 
+  /**
+   * OnModifyDocument() is called when DocumentModifiedWorker() calls
+   * HTMLEditor::OnModifyDocument().
+   */
+  MOZ_CAN_RUN_SCRIPT void OnModifyDocument(Selection& aSelection);
+
 protected:
   virtual ~HTMLEditRules();
 
   HTMLEditor& HTMLEditorRef() const
   {
     MOZ_ASSERT(mData);
     return mData->HTMLEditorRef();
   }
@@ -1311,17 +1321,21 @@ protected:
    * margin-left.
    * XXX This is not aware of vertical writing-mode.
    *
    * @param aElement            The element to be indented or outdented.
    * @param aIncrease           true for indent, false for outdent.
    */
   MOZ_MUST_USE nsresult ChangeMarginStart(Element& aElement, bool aIncrease);
 
-  void DocumentModifiedWorker();
+  /**
+   * DocumentModifiedWorker() is called by DocumentModified() either
+   * synchronously or asynchronously.
+   */
+  MOZ_CAN_RUN_SCRIPT void DocumentModifiedWorker();
 
   /**
    * InitStyleCacheArray() initializes aStyleCache for usable with
    * GetInlineStyles().
    */
   void InitStyleCacheArray(StyleCache aStyleCache[SIZE_STYLE_TABLE]);
 
   /**
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3516,19 +3516,20 @@ HTMLEditor::DoContentInserted(nsIContent
                         &HTMLEditor::NotifyRootChanged));
   }
   // We don't need to handle our own modifications
   else if (!mTopLevelEditSubAction && container->IsEditable()) {
     if (IsMozEditorBogusNode(aChild)) {
       // Ignore insertion of the bogus node
       return;
     }
-    // Protect the edit rules object from dying
-    RefPtr<TextEditRules> rules(mRules);
-    rules->DocumentModified();
+    RefPtr<HTMLEditRules> htmlRules = mRules->AsHTMLEditRules();
+    if (htmlRules) {
+      htmlRules->DocumentModified();
+    }
 
     // Update spellcheck for only the newly-inserted node (bug 743819)
     if (mInlineSpellChecker) {
       RefPtr<nsRange> range = new nsRange(aChild);
       nsIContent* endContent = aChild;
       if (aInsertedOrAppended == eAppended) {
         // Maybe more than 1 child was appended.
         endContent = container->GetLastChild();
@@ -3559,19 +3560,21 @@ HTMLEditor::ContentRemoved(nsIContent* a
                         this,
                         &HTMLEditor::NotifyRootChanged));
   // We don't need to handle our own modifications
   } else if (!mTopLevelEditSubAction && aChild->GetParentNode()->IsEditable()) {
     if (aChild && IsMozEditorBogusNode(aChild)) {
       // Ignore removal of the bogus node
       return;
     }
-    // Protect the edit rules object from dying
-    RefPtr<TextEditRules> rules(mRules);
-    rules->DocumentModified();
+
+    RefPtr<HTMLEditRules> htmlRules = mRules->AsHTMLEditRules();
+    if (htmlRules) {
+      htmlRules->DocumentModified();
+    }
   }
 }
 
 NS_IMETHODIMP
 HTMLEditor::DebugUnitTests(int32_t* outNumTests,
                            int32_t* outNumTestsFailed)
 {
 #ifdef DEBUG
@@ -5217,9 +5220,23 @@ HTMLEditor::GetHTMLDocument() const
     return nullptr;
   }
   if (!doc->IsHTMLOrXHTML()) {
     return nullptr;
   }
   return doc->AsHTMLDocument();
 }
 
+void
+HTMLEditor::OnModifyDocument()
+{
+  MOZ_ASSERT(mRules);
+
+  RefPtr<Selection> selection = GetSelection();
+  if (NS_WARN_IF(!selection)) {
+    return;
+  }
+
+  RefPtr<HTMLEditRules> htmlRules = mRules->AsHTMLEditRules();
+  htmlRules->OnModifyDocument(*selection);
+}
+
 } // namespace mozilla
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -976,16 +976,23 @@ protected: // May be called by friends.
                                  nsAString* outValue);
 
   nsresult ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
                       nsAtom* aProperty, nsAtom* aAttribute);
 
   nsresult SetPositionToAbsolute(Element& aElement);
   nsresult SetPositionToStatic(Element& aElement);
 
+  /**
+   * OnModifyDocument() is called when the editor is changed.  This should
+   * be called only by HTMLEditRules::DocumentModifiedWorker() to call
+   * HTMLEditRules::OnModifyDocument().
+   */
+  MOZ_CAN_RUN_SCRIPT void OnModifyDocument();
+
 protected: // Called by helper classes.
   virtual void
   OnStartToHandleTopLevelEditSubAction(
     EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;
   virtual void OnEndHandlingTopLevelEditSubAction() override;
 
 protected: // Shouldn't be used by friend classes
   virtual ~HTMLEditor();
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -1841,22 +1841,16 @@ TextEditRules::CreateBRInternal(
     return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return CreateElementResult(NS_ERROR_FAILURE);
   }
   return CreateElementResult(brElement.forget());
 }
 
-nsresult
-TextEditRules::DocumentModified()
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 bool
 TextEditRules::IsPasswordEditor() const
 {
   return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
 }
 
 bool
 TextEditRules::IsSingleLineEditor() const
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -99,18 +99,16 @@ public:
 
   /**
    * Return false if the editor has non-empty text nodes or non-text
    * nodes.  Otherwise, i.e., there is no meaningful content,
    * return true.
    */
   virtual bool DocumentIsEmpty();
 
-  virtual nsresult DocumentModified();
-
 protected:
   virtual ~TextEditRules();
 
 public:
   void ResetIMETextPWBuf();
 
   /**
    * Handles the newline characters according to the default system prefs