Bug 1283497, unbind editor created native anonymous content properly, r=wchen
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 14 Jul 2016 13:41:14 +0300
changeset 330103 b6ab1c8708cf7ef3f6384ee584645029722deece
parent 330102 feb851eb3003b58f5210692376ea570e6b8d0654
child 330104 5789d5804cae470433d96cd92f9f411af2400202
push id9858
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 14:37:10 +0000
treeherdermozilla-aurora@203106ef6cb6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswchen
bugs1283497
milestone50.0a1
Bug 1283497, unbind editor created native anonymous content properly, r=wchen
editor/libeditor/HTMLAnonymousNodeEditor.cpp
--- a/editor/libeditor/HTMLAnonymousNodeEditor.cpp
+++ b/editor/libeditor/HTMLAnonymousNodeEditor.cpp
@@ -24,17 +24,17 @@
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsIDocumentObserver.h"
 #include "nsIHTMLAbsPosEditor.h"
 #include "nsIHTMLInlineTableEditor.h"
 #include "nsIHTMLObjectResizer.h"
-#include "nsIMutationObserver.h"
+#include "nsStubMutationObserver.h"
 #include "nsINode.h"
 #include "nsIPresShell.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsUtils.h"
 #include "nsLiteralString.h"
 #include "nsPresContext.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
@@ -87,46 +87,77 @@ static int32_t GetCSSFloatValue(nsIDOMCS
         f = 5;
       break;
     }
   }
 
   return (int32_t) f;
 }
 
-class ElementDeletionObserver final : public nsIMutationObserver
+class ElementDeletionObserver final : public nsStubMutationObserver
 {
 public:
-  ElementDeletionObserver(nsINode* aNativeAnonNode, nsINode* aObservedNode)
+  ElementDeletionObserver(nsIContent* aNativeAnonNode,
+                          nsIContent* aObservedNode)
     : mNativeAnonNode(aNativeAnonNode)
     , mObservedNode(aObservedNode)
   {}
 
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIMUTATIONOBSERVER
+  NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
+  NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
 
 protected:
   ~ElementDeletionObserver() {}
-  nsINode* mNativeAnonNode;
-  nsINode* mObservedNode;
+  nsIContent* mNativeAnonNode;
+  nsIContent* mObservedNode;
 };
 
 NS_IMPL_ISUPPORTS(ElementDeletionObserver, nsIMutationObserver)
-NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(ElementDeletionObserver)
+
+void
+ElementDeletionObserver::ParentChainChanged(nsIContent* aContent)
+{
+  // If the native anonymous content has been unbound already in
+  // DeleteRefToAnonymousNode, mNativeAnonNode's parentNode is null.
+  if (aContent == mObservedNode && mNativeAnonNode &&
+      mNativeAnonNode->GetParentNode() == aContent) {
+    // If the observed node has been moved to another document, there isn't much
+    // we can do easily. But at least be safe and unbind the native anonymous
+    // content and stop observing changes.
+    if (mNativeAnonNode->OwnerDoc() != mObservedNode->OwnerDoc()) {
+      mObservedNode->RemoveMutationObserver(this);
+      mObservedNode = nullptr;
+      mNativeAnonNode->RemoveMutationObserver(this);
+      mNativeAnonNode->UnbindFromTree();
+      mNativeAnonNode = nullptr;
+      NS_RELEASE_THIS();
+      return;
+    }
+
+    // We're staying in the same document, just rebind the native anonymous
+    // node so that the subtree root points to the right object etc.
+    mNativeAnonNode->UnbindFromTree();
+    mNativeAnonNode->BindToTree(mObservedNode->GetUncomposedDoc(), mObservedNode,
+                                mObservedNode, true);
+  }
+}
 
 void
 ElementDeletionObserver::NodeWillBeDestroyed(const nsINode* aNode)
 {
   NS_ASSERTION(aNode == mNativeAnonNode || aNode == mObservedNode,
                "Wrong aNode!");
   if (aNode == mNativeAnonNode) {
     mObservedNode->RemoveMutationObserver(this);
+    mObservedNode = nullptr;
   } else {
     mNativeAnonNode->RemoveMutationObserver(this);
-    static_cast<nsIContent*>(mNativeAnonNode)->UnbindFromTree();
+    mNativeAnonNode->UnbindFromTree();
+    mNativeAnonNode = nullptr;
   }
 
   NS_RELEASE_THIS();
 }
 
 // Returns in *aReturn an anonymous nsDOMElement of type aTag,
 // child of aParentNode. If aIsCreatedHidden is true, the class
 // "hidden" is added to the created element. If aAnonClass is not
@@ -237,30 +268,30 @@ HTMLEditor::DeleteRefToAnonymousNode(nsI
 
   if (aElement) {
     nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
     if (content) {
       nsAutoScriptBlocker scriptBlocker;
       // Need to check whether aShell has been destroyed (but not yet deleted).
       // In that case presContext->GetPresShell() returns nullptr.
       // See bug 338129.
-      if (aShell && aShell->GetPresContext() &&
+      if (content->IsInComposedDoc() && aShell && aShell->GetPresContext() &&
           aShell->GetPresContext()->GetPresShell() == aShell) {
         nsCOMPtr<nsIDocumentObserver> docObserver = do_QueryInterface(aShell);
         if (docObserver) {
           // Call BeginUpdate() so that the nsCSSFrameConstructor/PresShell
           // knows we're messing with the frame tree.
           nsCOMPtr<nsIDocument> document = GetDocument();
           if (document)
             docObserver->BeginUpdate(document, UPDATE_CONTENT_MODEL);
 
           // XXX This is wrong (bug 439258).  Once it's fixed, the NS_WARNING
           // in RestyleManager::RestyleForRemove should be changed back
           // to an assertion.
-          docObserver->ContentRemoved(content->GetUncomposedDoc(),
+          docObserver->ContentRemoved(content->GetComposedDoc(),
                                       aParentContent, content, -1,
                                       content->GetPreviousSibling());
           if (document)
             docObserver->EndUpdate(document, UPDATE_CONTENT_MODEL);
         }
       }
       content->UnbindFromTree();
     }