Bug 650493 Part 1: Simplify mutation events by firing all but DOMNodeRemoved off of script runners, and DOMNodeRemoved before starting any update batches or aquiring any state. r=smaug/peterv
authorJonas Sicking <jonas@sicking.cc>
Mon, 09 May 2011 12:33:03 -0700
changeset 69458 e9ded5c5e0b7f0769245617dedb1971293ebba80
parent 69457 d2ebd2fa29a12c67d65f4c7e1cca6901647d1d83
child 69459 ad2f423c9237d34ad56be393d8c07c21b26268e2
push id76
push userbzbarsky@mozilla.com
push dateTue, 05 Jul 2011 17:00:57 +0000
treeherdermozilla-beta@d3a2732c35f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, peterv
bugs650493
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 650493 Part 1: Simplify mutation events by firing all but DOMNodeRemoved off of script runners, and DOMNodeRemoved before starting any update batches or aquiring any state. r=smaug/peterv
content/base/public/nsContentUtils.h
content/base/public/nsINode.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMAttribute.cpp
content/base/src/nsDocument.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsRange.cpp
content/base/test/test_bug548463.html
content/events/public/nsPLDOMEvent.h
content/events/src/nsPLDOMEvent.cpp
content/html/content/src/nsGenericHTMLElement.cpp
content/xul/content/src/nsXULElement.cpp
docshell/base/crashtests/crashtests.list
editor/libeditor/html/crashtests/crashtests.list
editor/libeditor/html/nsHTMLEditRules.cpp
layout/base/crashtests/crashtests.list
layout/reftests/svg/as-image/reftest.list
layout/reftests/svg/image/reftest.list
layout/reftests/svg/reftest.list
layout/reftests/svg/smil/reftest.list
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -931,16 +931,44 @@ public:
    *
    * @return true if there are mutation listeners of the specified type
    */
   static PRBool HasMutationListeners(nsINode* aNode,
                                      PRUint32 aType,
                                      nsINode* aTargetForSubtreeModified);
 
   /**
+   * Quick helper to determine whether there are any mutation listeners
+   * of a given type that apply to any content in this document. It is valid
+   * to pass null for aDocument here, in which case this function always
+   * returns PR_TRUE.
+   *
+   * @param aDocument The document to search for listeners
+   * @param aType     The type of listener (NS_EVENT_BITS_MUTATION_*)
+   *
+   * @return true if there are mutation listeners of the specified type
+   */
+  static PRBool HasMutationListeners(nsIDocument* aDocument,
+                                     PRUint32 aType);
+  /**
+   * Synchronously fire DOMNodeRemoved on aChild. Only fires the event if
+   * there really are listeners by checking using the HasMutationListeners
+   * function above. The function makes sure to hold the relevant objects alive
+   * for the duration of the event firing. However there are no guarantees
+   * that any of the objects are alive by the time the function returns.
+   * If you depend on that you need to hold references yourself.
+   *
+   * @param aChild    The node to fire DOMNodeRemoved at.
+   * @param aParent   The parent of aChild.
+   * @param aOwnerDoc The ownerDocument of aChild.
+   */
+  static void MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
+                                   nsIDocument* aOwnerDoc);
+
+  /**
    * This method creates and dispatches a trusted event.
    * Works only with events which can be created by calling
    * nsIDOMDocumentEvent::CreateEvent() with parameter "Events".
    * @param aDoc           The document which will be used to create the event.
    * @param aTarget        The target of the event, should be QIable to
    *                       nsIDOMEventTarget.
    * @param aEventName     The name of the event.
    * @param aCanBubble     Whether the event can bubble.
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -448,34 +448,17 @@ public:
   {
     return ReplaceOrInsertBefore(PR_TRUE, aNewChild, aOldChild, aReturn);
   }
   nsINode*
   AppendChild(nsINode *aNewChild, nsresult *aReturn)
   {
     return InsertBefore(aNewChild, nsnull, aReturn);
   }
-  nsresult RemoveChild(nsINode *aOldChild)
-  {
-    if (!aOldChild) {
-      return NS_ERROR_NULL_POINTER;
-    }
-
-    if (IsNodeOfType(eDATA_NODE)) {
-      return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-    }
-
-    PRInt32 index = IndexOf(aOldChild);
-    if (index == -1) {
-      // aOldChild isn't one of our children.
-      return NS_ERROR_DOM_NOT_FOUND_ERR;
-    }
-
-    return RemoveChildAt(index, PR_TRUE);
-  }
+  nsresult RemoveChild(nsINode *aOldChild);
 
   /**
    * Insert a content node at a particular index.  This method handles calling
    * BindToTree on the child appropriately.
    *
    * @param aKid the content to insert
    * @param aIndex the index it is being inserted at (the index it will have
    *        after it is inserted)
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -161,16 +161,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsIUGenCategory.h"
 #include "nsIDragService.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIOfflineCacheUpdate.h"
 #include "nsCPrefetchService.h"
 #include "nsIChromeRegistry.h"
+#include "nsEventDispatcher.h"
 #include "nsIMIMEHeaderParam.h"
 #include "nsIDOMXULCommandEvent.h"
 #include "nsIDOMDragEvent.h"
 #include "nsDOMDataTransfer.h"
 #include "nsHtml5Module.h"
 #include "nsPresContext.h"
 #include "nsLayoutStatics.h"
 #include "nsLayoutUtils.h"
@@ -3644,22 +3645,16 @@ nsContentUtils::HasMutationListeners(nsI
                                      PRUint32 aType,
                                      nsINode* aTargetForSubtreeModified)
 {
   nsIDocument* doc = aNode->GetOwnerDoc();
   if (!doc) {
     return PR_FALSE;
   }
 
-  NS_ASSERTION((aNode->IsNodeOfType(nsINode::eCONTENT) &&
-                static_cast<nsIContent*>(aNode)->
-                  IsInNativeAnonymousSubtree()) ||
-               sScriptBlockerCount == sRemovableScriptBlockerCount,
-               "Want to fire mutation events, but it's not safe");
-
   // global object will be null for documents that don't have windows.
   nsPIDOMWindow* window = doc->GetInnerWindow();
   // This relies on nsEventListenerManager::AddEventListener, which sets
   // all mutation bits when there is a listener for DOMSubtreeModified event.
   if (window && !window->HasMutationListeners(aType)) {
     return PR_FALSE;
   }
 
@@ -3709,16 +3704,60 @@ nsContentUtils::HasMutationListeners(nsI
     }
     aNode = aNode->GetNodeParent();
   }
 
   return PR_FALSE;
 }
 
 /* static */
+PRBool
+nsContentUtils::HasMutationListeners(nsIDocument* aDocument,
+                                     PRUint32 aType)
+{
+  nsPIDOMWindow* window = aDocument ?
+    aDocument->GetInnerWindow() : nsnull;
+
+  // This relies on nsEventListenerManager::AddEventListener, which sets
+  // all mutation bits when there is a listener for DOMSubtreeModified event.
+  return !window || window->HasMutationListeners(aType);
+}
+
+void
+nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
+                                     nsIDocument* aOwnerDoc)
+{
+  NS_PRECONDITION(aChild, "Missing child");
+  NS_PRECONDITION(aChild->GetNodeParent() == aParent, "Wrong parent");
+  NS_PRECONDITION(aChild->GetOwnerDoc() == aOwnerDoc, "Wrong owner-doc");
+
+  NS_ASSERTION(aChild->IsNodeOfType(nsINode::eCONTENT) &&
+               static_cast<nsIContent*>(aChild)->
+                 IsInNativeAnonymousSubtree() ||
+               IsSafeToRunScript(),
+               "Want to fire DOMNodeRemoved event, but it's not safe");
+
+  // Having an explicit check here since it's an easy mistake to fall into,
+  // and there might be existing code with problems. We'd rather be safe
+  // than fire DOMNodeRemoved in all corner cases.
+  if (!IsSafeToRunScript()) {
+    return;
+  }
+
+  if (HasMutationListeners(aChild,
+        NS_EVENT_BITS_MUTATION_NODEREMOVED, aParent)) {
+    nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
+    mutation.mRelatedNode = do_QueryInterface(aParent);
+
+    mozAutoSubtreeModified subtree(aOwnerDoc, aParent);
+    nsEventDispatcher::Dispatch(aChild, nsnull, &mutation);
+  }
+}
+
+/* static */
 void
 nsContentUtils::TraverseListenerManager(nsINode *aNode,
                                         nsCycleCollectionTraversalCallback &cb)
 {
   if (!sEventListenerManagersHash.ops) {
     // We're already shut down, just return.
     return;
   }
@@ -4079,16 +4118,47 @@ nsContentUtils::CreateDocument(const nsA
 }
 
 /* static */
 nsresult
 nsContentUtils::SetNodeTextContent(nsIContent* aContent,
                                    const nsAString& aValue,
                                    PRBool aTryReuse)
 {
+  // Fire DOMNodeRemoved mutation events before we do anything else.
+  nsCOMPtr<nsIContent> owningContent;
+
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(nsnull, nsnull);
+
+  // Scope firing mutation events so that we don't carry any state that
+  // might be stale
+  {
+    // We're relying on mozAutoSubtreeModified to keep a strong reference if
+    // needed.
+    nsIDocument* doc = aContent->GetOwnerDoc();
+
+    // Optimize the common case of there being no observers
+    if (HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
+      subtree.UpdateTarget(doc, nsnull);
+      owningContent = aContent;
+      nsCOMPtr<nsINode> child;
+      bool skipFirst = aTryReuse;
+      for (child = aContent->GetFirstChild();
+           child && child->GetNodeParent() == aContent;
+           child = child->GetNextSibling()) {
+        if (skipFirst && child->IsNodeOfType(nsINode::eTEXT)) {
+          skipFirst = false;
+          continue;
+        }
+        nsContentUtils::MaybeFireNodeRemoved(child, aContent, doc);
+      }
+    }
+  }
+
   // Might as well stick a batch around this since we're performing several
   // mutations.
   mozAutoDocUpdate updateBatch(aContent->GetCurrentDoc(),
     UPDATE_CONTENT_MODEL, PR_TRUE);
 
   PRUint32 childCount = aContent->GetChildCount();
 
   if (aTryReuse && !aValue.IsEmpty()) {
@@ -4102,17 +4172,17 @@ nsContentUtils::SetNodeTextContent(nsICo
         NS_ENSURE_SUCCESS(rv, rv);
 
         removeIndex = 1;
       }
       else {
         aContent->RemoveChildAt(removeIndex, PR_TRUE);
       }
     }
-    
+
     if (removeIndex == 1) {
       return NS_OK;
     }
   }
   else {
     // i is unsigned, so i >= is always true
     for (PRUint32 i = childCount; i-- != 0; ) {
       aContent->RemoveChildAt(i, PR_TRUE);
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -54,16 +54,17 @@
 #include "nsEventDispatcher.h"
 #include "nsGkAtoms.h"
 #include "nsCOMArray.h"
 #include "nsNodeUtils.h"
 #include "nsIEventListenerManager.h"
 #include "nsTextNode.h"
 #include "mozAutoDocUpdate.h"
 #include "nsMutationEvent.h"
+#include "nsPLDOMEvent.h"
 
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 PRBool nsDOMAttribute::sInitialized;
 
 nsDOMAttribute::nsDOMAttribute(nsDOMAttributeMap *aAttrMap,
                                already_AddRefed<nsINodeInfo> aNodeInfo,
@@ -615,33 +616,16 @@ nsDOMAttribute::RemoveChildAt(PRUint32 a
   if (aIndex != 0 || !mChild) {
     return NS_OK;
   }
 
   {
     nsCOMPtr<nsIContent> child = mChild;
     nsMutationGuard::DidMutate();
     mozAutoDocUpdate updateBatch(GetOwnerDoc(), UPDATE_CONTENT_MODEL, aNotify);
-    nsMutationGuard guard;
-  
-    mozAutoSubtreeModified subtree(nsnull, nsnull);
-    if (aNotify &&
-        nsContentUtils::HasMutationListeners(mChild,
-                                             NS_EVENT_BITS_MUTATION_NODEREMOVED,
-                                             this)) {
-      mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-      nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
-      mutation.mRelatedNode =
-        do_QueryInterface(static_cast<nsIAttribute*>(this));
-      subtree.UpdateTarget(GetOwnerDoc(), this);
-      nsEventDispatcher::Dispatch(mChild, nsnull, &mutation);
-    }
-    if (guard.Mutated(0) && mChild != child) {
-      return NS_OK;
-    }
 
     doRemoveChild(aNotify);
   }
 
   nsString nullString;
   SetDOMStringToNull(nullString);
   SetValue(nullString);
   return NS_OK;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -7557,20 +7557,18 @@ nsDocument::MutationEventDispatched(nsIN
         realTargets.AppendObject(possibleTarget);
       }
     }
 
     mSubtreeModifiedTargets.Clear();
 
     PRInt32 realTargetCount = realTargets.Count();
     for (PRInt32 k = 0; k < realTargetCount; ++k) {
-      mozAutoRemovableBlockerRemover blockerRemover(this);
-
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_SUBTREEMODIFIED);
-      nsEventDispatcher::Dispatch(realTargets[k], nsnull, &mutation);
+      (new nsPLDOMEvent(realTargets[k], mutation))->RunDOMEventWhenSafe();
     }
   }
 }
 
 void
 nsDocument::AddStyleRelevantLink(Link* aLink)
 {
   NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -58,16 +58,17 @@
 #include "nsIDOMUserDataHandler.h"
 #include "nsChangeHint.h"
 #include "nsEventDispatcher.h"
 #include "nsCOMArray.h"
 #include "nsNodeUtils.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozAutoDocUpdate.h"
+#include "nsPLDOMEvent.h"
 
 #include "pldhash.h"
 #include "prprf.h"
 
 namespace css = mozilla::css;
 
 nsGenericDOMDataNode::nsGenericDOMDataNode(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsIContent(aNodeInfo)
@@ -386,29 +387,27 @@ nsGenericDOMDataNode::SetTextInternal(PR
       aOffset == textLength,
       aOffset,
       endOffset,
       aLength
     };
     nsNodeUtils::CharacterDataChanged(this, &info);
 
     if (haveMutationListeners) {
-      mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_CHARACTERDATAMODIFIED);
 
       mutation.mPrevAttrValue = oldValue;
       if (aLength > 0) {
         nsAutoString val;
         mText.AppendTo(val);
         mutation.mNewAttrValue = do_GetAtom(val);
       }
 
       mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-      nsEventDispatcher::Dispatch(this, nsnull, &mutation);
+      (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
     }
   }
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 
@@ -951,48 +950,68 @@ nsGenericTextNode::GetWholeText(nsAStrin
 }
 
 nsIContent*
 nsGenericTextNode::ReplaceWholeText(const nsAFlatString& aContent,
                                     nsresult* aResult)
 {
   *aResult = NS_OK;
 
-  // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
-  mozAutoDocUpdate updateBatch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, PR_TRUE);
-
+  // Handle parent-less nodes
   nsCOMPtr<nsIContent> parent = GetParent();
-
-  // Handle parent-less nodes
   if (!parent) {
     if (aContent.IsEmpty()) {
       return nsnull;
     }
 
-    SetText(aContent.get(), aContent.Length(), PR_TRUE);
+    SetNodeValue(aContent);
     return this;
   }
 
+  // We're relying on mozAutoSubtreeModified to keep the doc alive here.
+  nsIDocument* doc = GetOwnerDoc();
+
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(doc, nsnull);
+
   PRInt32 index = parent->IndexOf(this);
   if (index < 0) {
     NS_WARNING("Trying to use .replaceWholeText with an anonymous text node "
                "child of a binding parent?");
     *aResult = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     return nsnull;
   }
 
   // We don't support entity references or read-only nodes, so remove the
   // logically adjacent text nodes (which therefore must all be siblings of
   // this) and set this one to the provided text, if that text isn't empty.
   PRInt32 first =
     FirstLogicallyAdjacentTextNode(parent, index);
   PRInt32 last =
     LastLogicallyAdjacentTextNode(parent, index, parent->GetChildCount());
 
+  // Fire mutation events. Optimize the common case of there being no
+  // listeners
+  if (nsContentUtils::
+        HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
+    for (PRInt32 i = first; i <= last; ++i) {
+      nsCOMPtr<nsIContent> child = parent->GetChildAt((PRUint32)i);
+      if (child &&
+          (i != index || aContent.IsEmpty())) {
+        nsContentUtils::MaybeFireNodeRemoved(child, parent, doc);
+      }
+    }
+  }
+
+  // Remove the needed nodes
+  // Don't want to use 'doc' here since it might no longer be the correct
+  // document.
+  mozAutoDocUpdate updateBatch(parent->GetCurrentDoc(), UPDATE_CONTENT_MODEL,
+                               PR_TRUE);
+
   do {
     if (last == index && !aContent.IsEmpty())
       continue;
 
     parent->RemoveChildAt(last, PR_TRUE);
   } while (last-- > first);
 
   // Empty string means we removed this node too.
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -129,16 +129,17 @@
 #include "nsLayoutUtils.h"
 #include "nsIView.h"
 #include "nsIViewManager.h"
 #include "nsIScrollableFrame.h"
 #include "nsXBLInsertionPoint.h"
 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
+#include "nsPLDOMEvent.h"
 
 #ifdef MOZ_XUL
 #include "nsIXULDocument.h"
 #endif /* MOZ_XUL */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
@@ -519,16 +520,40 @@ nsINode::GetOwnerDocument(nsIDOMDocument
   *aOwnerDocument = nsnull;
 
   nsIDocument *ownerDoc = GetOwnerDocument();
 
   return ownerDoc ? CallQueryInterface(ownerDoc, aOwnerDocument) : NS_OK;
 }
 
 nsresult
+nsINode::RemoveChild(nsINode *aOldChild)
+{
+  if (!aOldChild) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  if (IsNodeOfType(eDATA_NODE)) {
+    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
+  }
+
+  if (aOldChild && aOldChild->GetNodeParent() == this) {
+    nsContentUtils::MaybeFireNodeRemoved(aOldChild, this, GetOwnerDoc());
+  }
+
+  PRInt32 index = IndexOf(aOldChild);
+  if (index == -1) {
+    // aOldChild isn't one of our children.
+    return NS_ERROR_DOM_NOT_FOUND_ERR;
+  }
+
+  return RemoveChildAt(index, PR_TRUE);
+}
+
+nsresult
 nsINode::ReplaceOrInsertBefore(PRBool aReplace, nsIDOMNode* aNewChild,
                                nsIDOMNode* aRefChild, nsIDOMNode** aReturn)
 {
   nsCOMPtr<nsINode> newChild = do_QueryInterface(aNewChild);
 
   nsresult rv;
   nsCOMPtr<nsINode> refChild;
   if (aRefChild) {
@@ -2709,18 +2734,24 @@ nsGenericElement::JoinTextNodes(nsIConte
   }
 
   return rv;
 }
 
 nsresult
 nsGenericElement::Normalize()
 {
+  // We're relying on mozAutoSubtreeModified to keep the doc alive here.
+  nsIDocument* doc = GetOwnerDoc();
+
   // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
+  mozAutoSubtreeModified subtree(doc, nsnull);
+
+  bool hasRemoveListeners = nsContentUtils::
+    HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED);
 
   nsresult result = NS_OK;
   PRUint32 index, count = GetChildCount();
 
   for (index = 0; (index < count) && (NS_OK == result); index++) {
     nsIContent *child = GetChildAt(index);
 
     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(child);
@@ -2728,39 +2759,45 @@ nsGenericElement::Normalize()
       PRUint16 nodeType;
       node->GetNodeType(&nodeType);
 
       switch (nodeType) {
         case nsIDOMNode::TEXT_NODE:
 
           // ensure that if the text node is empty, it is removed
           if (0 == child->TextLength()) {
+            if (hasRemoveListeners) {
+              nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
+            }
             result = RemoveChildAt(index, PR_TRUE);
             if (NS_FAILED(result)) {
               return result;
             }
 
             count--;
             index--;
             break;
           }
  
           if (index+1 < count) {
             // Get the sibling. If it's also a text node, then
             // remove it from the tree and join the two text
             // nodes.
-            nsIContent *sibling = GetChildAt(index + 1);
+            nsCOMPtr<nsIContent> sibling = GetChildAt(index + 1);
 
             nsCOMPtr<nsIDOMNode> siblingNode = do_QueryInterface(sibling);
 
             if (siblingNode) {
               PRUint16 siblingNodeType;
               siblingNode->GetNodeType(&siblingNodeType);
 
               if (siblingNodeType == nsIDOMNode::TEXT_NODE) {
+                if (hasRemoveListeners) {
+                  nsContentUtils::MaybeFireNodeRemoved(sibling, this, doc);
+                }
                 result = RemoveChildAt(index+1, PR_TRUE);
                 if (NS_FAILED(result)) {
                   return result;
                 }
 
                 result = JoinTextNodes(child, sibling);
                 if (NS_FAILED(result)) {
                   return result;
@@ -3512,16 +3549,19 @@ nsGenericElement::InsertChildAt(nsIConte
   NS_PRECONDITION(aKid, "null ptr");
 
   return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
 }
 
 static nsresult
 AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
 {
+  NS_ASSERTION(!aNode->GetNodeParent(),
+               "Should have removed from parent already");
+
   nsIDocument *doc = aParent->GetOwnerDoc();
 
   nsresult rv;
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -3540,16 +3580,18 @@ AdoptNodeIntoOwnerDoc(nsINode *aParent, 
 
   return NS_OK;
 }
 
 nsresult
 nsINode::doInsertChildAt(nsIContent* aKid, PRUint32 aIndex,
                          PRBool aNotify, nsAttrAndChildArray& aChildArray)
 {
+  NS_PRECONDITION(!aKid->GetNodeParent(),
+                  "Inserting node that already has parent");
   nsresult rv;
 
   if (!HasSameOwnerDoc(aKid)) {
     nsCOMPtr<nsIDOMNode> kid = do_QueryInterface(aKid, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
  
     PRUint16 nodeType = 0;
     rv = kid->GetNodeType(&nodeType);
@@ -3605,23 +3647,21 @@ nsINode::doInsertChildAt(nsIContent* aKi
     if (parent && isAppend) {
       nsNodeUtils::ContentAppended(parent, aKid, aIndex);
     } else {
       nsNodeUtils::ContentInserted(this, aKid, aIndex);
     }
 
     if (nsContentUtils::HasMutationListeners(aKid,
           NS_EVENT_BITS_MUTATION_NODEINSERTED, this)) {
-      mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-      
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED);
       mutation.mRelatedNode = do_QueryInterface(this);
 
       mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-      nsEventDispatcher::Dispatch(aKid, nsnull, &mutation);
+      (new nsPLDOMEvent(aKid, mutation))->RunDOMEventWhenSafe();
     }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsGenericElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent)
@@ -3637,51 +3677,26 @@ nsGenericElement::RemoveChildAt(PRUint32
   return NS_OK;
 }
 
 nsresult
 nsINode::doRemoveChildAt(PRUint32 aIndex, PRBool aNotify,
                          nsIContent* aKid, nsAttrAndChildArray& aChildArray,
                          PRBool aMutationEvent)
 {
-  nsIDocument* doc = GetCurrentDoc();
-
-  nsMutationGuard::DidMutate();
-
   NS_PRECONDITION(aKid && aKid->GetNodeParent() == this &&
                   aKid == GetChildAt(aIndex) &&
                   IndexOf(aKid) == (PRInt32)aIndex, "Bogus aKid");
 
+  nsMutationGuard::DidMutate();
+
+  nsIDocument* doc = GetCurrentDoc();
+
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, aNotify);
 
-  nsMutationGuard guard;
-
-  mozAutoSubtreeModified subtree(nsnull, nsnull);
-  if (aNotify &&
-      aMutationEvent &&
-      nsContentUtils::HasMutationListeners(aKid,
-        NS_EVENT_BITS_MUTATION_NODEREMOVED, this)) {
-    mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
-    nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
-    mutation.mRelatedNode = do_QueryInterface(this);
-
-    subtree.UpdateTarget(GetOwnerDoc(), this);
-    nsEventDispatcher::Dispatch(aKid, nsnull, &mutation);
-  }
-
-  // Someone may have removed the kid or any of its siblings while that event
-  // was processing.
-  if (guard.Mutated(0)) {
-    aIndex = IndexOf(aKid);
-    if (static_cast<PRInt32>(aIndex) < 0) {
-      return NS_OK;
-    }
-  }
-
   nsIContent* previousSibling = aKid->GetPreviousSibling();
 
   if (GetFirstChild() == aKid) {
     mFirstChild = aKid->GetNextSibling();
   }
 
   aChildArray.RemoveChildAt(aIndex);
 
@@ -3980,46 +3995,85 @@ nsGenericElement::FireNodeInserted(nsIDo
                                    nsCOMArray<nsIContent>& aNodes)
 {
   PRInt32 count = aNodes.Count();
   for (PRInt32 i = 0; i < count; ++i) {
     nsIContent* childContent = aNodes[i];
 
     if (nsContentUtils::HasMutationListeners(childContent,
           NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
-      mozAutoRemovableBlockerRemover blockerRemover(aDoc);
-
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED);
       mutation.mRelatedNode = do_QueryInterface(aParent);
 
       mozAutoSubtreeModified subtree(aDoc, aParent);
-      nsEventDispatcher::Dispatch(childContent, nsnull, &mutation);
+      (new nsPLDOMEvent(childContent, mutation))->RunDOMEventWhenSafe();
     }
   }
 }
 
 nsresult
 nsINode::ReplaceOrInsertBefore(PRBool aReplace, nsINode* aNewChild,
                                nsINode* aRefChild)
 {
   if (!aNewChild || (aReplace && !aRefChild)) {
     return NS_ERROR_NULL_POINTER;
   }
 
-  if (!IsNodeOfType(eDOCUMENT) &&
-      !IsNodeOfType(eDOCUMENT_FRAGMENT) &&
-      !IsElement()) {
+  if ((!IsNodeOfType(eDOCUMENT) &&
+       !IsNodeOfType(eDOCUMENT_FRAGMENT) &&
+       !IsElement()) ||
+      !aNewChild->IsNodeOfType(eCONTENT)){
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
+  PRUint16 nodeType = 0;
+  nsresult res = aNewChild->GetNodeType(&nodeType);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // Before we do anything else, fire all DOMNodeRemoved mutation events
+  // We do this up front as to avoid having to deal with script running
+  // at random places further down.
+  // Scope firing mutation events so that we don't carry any state that
+  // might be stale
+  {
+    // This check happens again further down (though then using IndexOf).
+    // We're only checking this here to avoid firing mutation events when
+    // none should be fired.
+    // It's ok that we do the check twice in the case when firing mutation
+    // events as we need to recheck after running script anyway.
+    if (aRefChild && aRefChild->GetNodeParent() != this) {
+      return NS_ERROR_DOM_NOT_FOUND_ERR;
+    }
+
+    // If we're replacing, fire for node-to-be-replaced
+    if (aReplace) {
+      nsContentUtils::MaybeFireNodeRemoved(aRefChild, this, GetOwnerDoc());
+    }
+
+    // If the new node already has a parent, fire for removing from old
+    // parent
+    nsINode* oldParent = aNewChild->GetNodeParent();
+    if (oldParent) {
+      nsContentUtils::MaybeFireNodeRemoved(aNewChild, oldParent,
+                                           aNewChild->GetOwnerDoc());
+    }
+
+    // If we're inserting a fragment, fire for all the children of the
+    // fragment
+    if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
+      static_cast<nsGenericElement*>(aNewChild)->FireNodeRemovedForChildren();
+    }
+  }
+
+  nsIDocument* doc = GetOwnerDoc();
+  nsIContent* newContent = static_cast<nsIContent*>(aNewChild);
   nsIContent* refContent;
-  nsresult res = NS_OK;
   PRInt32 insPos;
 
-  mozAutoDocConditionalContentUpdateBatch batch(GetCurrentDoc(), PR_TRUE);
+  mozAutoDocUpdate batch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, PR_TRUE);
 
   // Figure out which index to insert at
   if (aRefChild) {
     insPos = IndexOf(aRefChild);
     if (insPos < 0) {
       return NS_ERROR_DOM_NOT_FOUND_ERR;
     }
 
@@ -4032,66 +4086,71 @@ nsINode::ReplaceOrInsertBefore(PRBool aR
 
     refContent = static_cast<nsIContent*>(aRefChild);
   }
   else {
     insPos = GetChildCount();
     refContent = nsnull;
   }
 
-  if (!aNewChild->IsNodeOfType(eCONTENT)) {
-    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-  }
-
-  nsIContent* newContent = static_cast<nsIContent*>(aNewChild);
-
-  PRUint16 nodeType = 0;
-  res = aNewChild->GetNodeType(&nodeType);
-  NS_ENSURE_SUCCESS(res, res);
-
   // Make sure that the inserted node is allowed as a child of its new parent.
   if (!IsAllowedAsChild(newContent, nodeType, this, aReplace, refContent)) {
     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
   }
 
+  // If we're replacing
+  if (aReplace) {
+    refContent = GetChildAt(insPos + 1);
+
+    res = RemoveChildAt(insPos, PR_TRUE);
+    NS_ENSURE_SUCCESS(res, res);
+  }
+
+  if (newContent->IsRootOfAnonymousSubtree()) {
+    // This is anonymous content.  Don't allow its insertion
+    // anywhere, since it might have UnbindFromTree calls coming
+    // its way.
+    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
+
+  // Remove the new child from the old parent if one exists
+  nsINode* oldParent = newContent->GetNodeParent();
+  if (oldParent) {
+    PRInt32 removeIndex = oldParent->IndexOf(newContent);
+    if (removeIndex < 0) {
+      // newContent is anonymous.  We can't deal with this, so just bail
+      NS_ERROR("How come our flags didn't catch this?");
+      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    }
+    
+    NS_ASSERTION(!(oldParent == this && removeIndex == insPos),
+                 "invalid removeIndex");
+
+    res = oldParent->RemoveChildAt(removeIndex, PR_TRUE);
+    NS_ENSURE_SUCCESS(res, res);
+
+    // Adjust insert index if the node we ripped out was a sibling
+    // of the node we're inserting before
+    if (oldParent == this && removeIndex < insPos) {
+      --insPos;
+    }
+  }
+
+  // Move new child over to our document if needed. Do this after removing
+  // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved
   // DocumentType nodes are the only nodes that can have a null
   // ownerDocument according to the DOM spec, and we need to allow
   // inserting them w/o calling AdoptNode().
   if (!HasSameOwnerDoc(newContent) &&
       (nodeType != nsIDOMNode::DOCUMENT_TYPE_NODE ||
        newContent->GetOwnerDoc())) {
     res = AdoptNodeIntoOwnerDoc(this, aNewChild);
     NS_ENSURE_SUCCESS(res, res);
   }
 
-  // If we're replacing
-  if (aReplace) {
-    refContent = GetChildAt(insPos + 1);
-
-    nsMutationGuard guard;
-
-    res = RemoveChildAt(insPos, PR_TRUE);
-    NS_ENSURE_SUCCESS(res, res);
-
-    if (guard.Mutated(1)) {
-      insPos = refContent ? IndexOf(refContent) : GetChildCount();
-      if (insPos < 0) {
-        return NS_ERROR_DOM_NOT_FOUND_ERR;
-      }
-
-      // Passing PR_FALSE for aIsReplace since we now have removed the node
-      // to be replaced.
-      if (!IsAllowedAsChild(newContent, nodeType, this, PR_FALSE, refContent)) {
-        return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-      }
-    }
-  }
-
-  nsIDocument *doc = GetOwnerDoc();
-
   /*
    * Check if we're inserting a document fragment. If we are, we need
    * to remove the children of the document fragment and add them
    * individually (i.e. we don't add the actual document fragment).
    */
   if (nodeType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
     PRUint32 count = newContent->GetChildCount();
 
@@ -4184,84 +4243,28 @@ nsINode::ReplaceOrInsertBefore(PRBool aR
 
     // Notify
     if (appending) {
       nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this),
                                    firstInsertedContent, firstInsPos);
     }
 
     // Fire mutation events. Optimize for the case when there are no listeners
-    nsPIDOMWindow* window = nsnull;
-    if (doc &&
-        (((window = doc->GetInnerWindow()) &&
-          window->HasMutationListeners(NS_EVENT_BITS_MUTATION_NODEINSERTED)) ||
-         !window)) {
+    if (nsContentUtils::
+          HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
       nsGenericElement::FireNodeInserted(doc, this, fragChildren);
     }
   }
   else {
     // Not inserting a fragment but rather a single node.
 
-    if (newContent->IsRootOfAnonymousSubtree()) {
-      // This is anonymous content.  Don't allow its insertion
-      // anywhere, since it might have UnbindFromTree calls coming
-      // its way.
-      return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-    }
-
-    // Remove the element from the old parent if one exists
-    nsINode* oldParent = newContent->GetNodeParent();
-
-    if (oldParent) {
-      PRInt32 removeIndex = oldParent->IndexOf(newContent);
-
-      if (removeIndex < 0) {
-        // newContent is anonymous.  We can't deal with this, so just bail
-        NS_ERROR("How come our flags didn't catch this?");
-        return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
-      }
-      
-      NS_ASSERTION(!(oldParent == this && removeIndex == insPos),
-                   "invalid removeIndex");
-
-      nsMutationGuard guard;
-
-      res = oldParent->RemoveChildAt(removeIndex, PR_TRUE);
-      NS_ENSURE_SUCCESS(res, res);
-
-      // Adjust insert index if the node we ripped out was a sibling
-      // of the node we're inserting before
-      if (oldParent == this && removeIndex < insPos) {
-        --insPos;
-      }
-
-      if (guard.Mutated(1)) {
-        if (doc != newContent->GetOwnerDoc()) {
-          return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
-        }
-
-        insPos = refContent ? IndexOf(refContent) : GetChildCount();
-        if (insPos < 0) {
-          // Someone seriously messed up the childlist. We have no idea
-          // where to insert the new child, so just bail.
-          return NS_ERROR_DOM_NOT_FOUND_ERR;
-        }
-
-        if (newContent->GetNodeParent() ||
-            !IsAllowedAsChild(newContent, nodeType, this, PR_FALSE,
-                              refContent)) {
-          return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
-        }
-      }
-    }
-
     // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654
     //       We need to reparent here for nodes for which the parent of their
     //       wrapper is not the wrapper for their ownerDocument (XUL elements,
-    //       form controls, ...).
+    //       form controls, ...). Also applies in the fragment code above.
 
     res = InsertChildAt(newContent, insPos, PR_TRUE);
     NS_ENSURE_SUCCESS(res, res);
   }
 
   return res;
 }
 
@@ -4746,18 +4749,16 @@ nsGenericElement::SetAttrAndNotify(PRInt
     mNodeInfo->GetDocument()->AddXMLEventsContent(this);
   }
   if (aValueForAfterSetAttr) {
     rv = AfterSetAttr(aNamespaceID, aName, aValueForAfterSetAttr, aNotify);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aFireMutation) {
-    mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-    
     nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED);
 
     nsCOMPtr<nsIDOMAttr> attrNode;
     nsAutoString ns;
     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
     GetAttributeNodeNS(ns, nsDependentAtomString(aName),
                        getter_AddRefs(attrNode));
     mutation.mRelatedNode = attrNode;
@@ -4769,17 +4770,17 @@ nsGenericElement::SetAttrAndNotify(PRInt
       mutation.mNewAttrValue = do_GetAtom(newValue);
     }
     if (!aOldValue.IsEmpty()) {
       mutation.mPrevAttrValue = do_GetAtom(aOldValue);
     }
     mutation.mAttrChange = aModType;
 
     mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-    nsEventDispatcher::Dispatch(this, nsnull, &mutation);
+    (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
   }
 
   return NS_OK;
 }
 
 PRBool
 nsGenericElement::ParseAttribute(PRInt32 aNamespaceID,
                                  nsIAtom* aAttribute,
@@ -4988,33 +4989,31 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
     nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                   nsIDOMMutationEvent::REMOVAL);
   }
 
   rv = AfterSetAttr(aNameSpaceID, aName, nsnull, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (hasMutationListeners) {
-    mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
     nsCOMPtr<nsIDOMEventTarget> node =
       do_QueryInterface(static_cast<nsIContent *>(this));
     nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED);
 
     mutation.mRelatedNode = attrNode;
     mutation.mAttrName = aName;
 
     nsAutoString value;
     oldValue.ToString(value);
     if (!value.IsEmpty())
       mutation.mPrevAttrValue = do_GetAtom(value);
     mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
 
     mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-    nsEventDispatcher::Dispatch(this, nsnull, &mutation);
+    (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
   }
 
   return NS_OK;
 }
 
 const nsAttrName*
 nsGenericElement::GetAttrNameAt(PRUint32 aIndex) const
 {
@@ -5435,16 +5434,36 @@ nsGenericElement::PostHandleEventForLink
     NS_NOTREACHED("switch statements not in sync");
     return NS_ERROR_UNEXPECTED;
   }
 
   return rv;
 }
 
 void
+nsGenericElement::FireNodeRemovedForChildren()
+{
+  nsIDocument* doc = GetOwnerDoc();
+  // Optimize the common case
+  if (!nsContentUtils::
+        HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
+    return;
+  }
+
+  nsCOMPtr<nsIDocument> owningDoc = doc;
+
+  nsCOMPtr<nsINode> child;
+  for (child = GetFirstChild();
+       child && child->GetNodeParent() == this;
+       child = child->GetNextSibling()) {
+    nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
+  }
+}
+
+void
 nsGenericElement::GetLinkTarget(nsAString& aTarget)
 {
   aTarget.Truncate();
 }
 
 // NOTE: The aPresContext pointer is NOT addrefed.
 // *aSelectorList might be null even if NS_OK is returned; this
 // happens when all the selectors were pseudo-element selectors.
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -345,18 +345,16 @@ public:
     return nsContentUtils::GetContextForEventHandlers(this, aRv);
   }
   virtual void GetTextContent(nsAString &aTextContent)
   {
     nsContentUtils::GetNodeTextContent(this, PR_TRUE, aTextContent);
   }
   virtual nsresult SetTextContent(const nsAString& aTextContent)
   {
-    // Batch possible DOMSubtreeModified events.
-    mozAutoSubtreeModified subtree(GetOwnerDoc(), nsnull);
     return nsContentUtils::SetNodeTextContent(this, aTextContent, PR_FALSE);
   }
 
   // nsIContent interface methods
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
@@ -764,16 +762,21 @@ public:
   virtual nsAttrInfo GetAttrInfo(PRInt32 aNamespaceID, nsIAtom* aName) const;
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsGenericElement)
 
   virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
   {
   }
 
+  /**
+   * Fire a DOMNodeRemoved mutation event for all children of this node
+   */
+  void FireNodeRemovedForChildren();
+
 protected:
   /**
    * Set attribute and (if needed) notify documentobservers and fire off
    * mutation events.  This will send the AttributeChanged notification.
    * Callers of this method are responsible for calling AttributeWillChange,
    * since that needs to happen before the new attr value has been set, and
    * in particular before it has been parsed.
    *
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -1167,17 +1167,17 @@ CollapseRangeAfterDelete(nsIDOMRange *aR
  *
  * @param aNode The node to remove.
  */
 static nsresult
 RemoveNode(nsIDOMNode* aNode)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   nsCOMPtr<nsINode> parent = node->GetNodeParent();
-  return parent ? parent->RemoveChildAt(parent->IndexOf(node), PR_TRUE) : NS_OK;
+  return parent ? parent->RemoveChild(node) : NS_OK;
 }
 
 /**
  * Split a data node into two parts.
  *
  * @param aStartNode          The original node we are trying to split.
  * @param aStartIndex         The index at which to split.
  * @param aEndNode            The second node.
--- a/content/base/test/test_bug548463.html
+++ b/content/base/test/test_bug548463.html
@@ -43,17 +43,17 @@ function testAdoptFromDOMNodeRemoved(nod
   var thrown = false;
   try {
     document.getElementById("otherContent").appendChild(nodeToAppend);
   }
   catch (e) {
     thrown = true;
   }
 
-  ok(thrown, "adoptNode while appending should throw");
+  ok(!thrown, "adoptNode while appending should not throw");
 }
 
 var frag = document.createDocumentFragment();
 frag.appendChild(elem1);
 frag.appendChild(elem2);
 testAdoptFromDOMNodeRemoved(frag, elem1, elem2);
 
 content.appendChild(elem1);
--- a/content/events/public/nsPLDOMEvent.h
+++ b/content/events/public/nsPLDOMEvent.h
@@ -64,19 +64,21 @@ public:
       mBubbles(aBubbles),
       mDispatchChromeOnly(aDispatchChromeOnly)
   { }
 
   nsPLDOMEvent(nsINode *aEventNode, nsIDOMEvent *aEvent)
     : mEventNode(aEventNode), mEvent(aEvent), mDispatchChromeOnly(PR_FALSE)
   { }
 
+  nsPLDOMEvent(nsINode *aEventNode, nsEvent &aEvent);
+
   NS_IMETHOD Run();
   nsresult PostDOMEvent();
-  nsresult RunDOMEventWhenSafe();
+  void RunDOMEventWhenSafe();
 
   nsCOMPtr<nsINode>     mEventNode;
   nsCOMPtr<nsIDOMEvent> mEvent;
   nsString              mEventType;
   PRPackedBool          mBubbles;
   PRPackedBool          mDispatchChromeOnly;
 };
 
--- a/content/events/src/nsPLDOMEvent.cpp
+++ b/content/events/src/nsPLDOMEvent.cpp
@@ -37,16 +37,31 @@
 
 #include "nsPLDOMEvent.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsContentUtils.h"
+#include "nsEventDispatcher.h"
+#include "nsGUIEvent.h"
+
+nsPLDOMEvent::nsPLDOMEvent(nsINode *aEventNode, nsEvent &aEvent)
+  : mEventNode(aEventNode), mDispatchChromeOnly(PR_FALSE)
+{
+  PRBool trusted = NS_IS_TRUSTED_EVENT(&aEvent);
+  nsEventDispatcher::CreateEvent(nsnull, &aEvent, EmptyString(),
+                                 getter_AddRefs(mEvent));
+  NS_ASSERTION(mEvent, "Should never fail to create an event");
+  nsCOMPtr<nsIPrivateDOMEvent> priv = do_QueryInterface(mEvent);
+  NS_ASSERTION(priv, "Should also not fail to QI to nsIDOMEventPrivate");
+  priv->DuplicatePrivateData();
+  priv->SetTrusted(trusted);
+}
 
 NS_IMETHODIMP nsPLDOMEvent::Run()
 {
   if (!mEventNode) {
     return NS_OK;
   }
 
   if (mEvent) {
@@ -70,19 +85,19 @@ NS_IMETHODIMP nsPLDOMEvent::Run()
   return NS_OK;
 }
 
 nsresult nsPLDOMEvent::PostDOMEvent()
 {
   return NS_DispatchToCurrentThread(this);
 }
 
-nsresult nsPLDOMEvent::RunDOMEventWhenSafe()
+void nsPLDOMEvent::RunDOMEventWhenSafe()
 {
-  return nsContentUtils::AddScriptRunner(this) ? NS_OK : NS_ERROR_FAILURE;
+  nsContentUtils::AddScriptRunner(this);
 }
 
 nsLoadBlockingPLDOMEvent::~nsLoadBlockingPLDOMEvent()
 {
   if (mBlockedDoc) {
     mBlockedDoc->UnblockOnload(PR_TRUE);
   }
 }
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -697,25 +697,30 @@ nsGenericHTMLElement::GetInnerHTML(nsASt
 nsresult
 nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
 {
   nsIDocument* doc = GetOwnerDoc();
   NS_ENSURE_STATE(doc);
 
   nsresult rv = NS_OK;
 
+  // Batch possible DOMSubtreeModified events.
+  mozAutoSubtreeModified subtree(doc, nsnull);
+
+  FireNodeRemovedForChildren();
+
   // This BeginUpdate/EndUpdate pair is important to make us reenable the
   // scriptloader before the last EndUpdate call.
   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, PR_TRUE);
 
-  // Batch possible DOMSubtreeModified events.
-  mozAutoSubtreeModified subtree(doc, nsnull);
-
-  // Remove childnodes
-  nsContentUtils::SetNodeTextContent(this, EmptyString(), PR_FALSE);
+  // Remove childnodes.
+  // i is unsigned, so i >= is always true
+  for (PRUint32 i = GetChildCount(); i-- != 0; ) {
+    RemoveChildAt(i, PR_TRUE);
+  }
 
   nsCOMPtr<nsIDOMDocumentFragment> df;
 
   // Strong ref since appendChild can fire events
   nsRefPtr<nsScriptLoader> loader = doc->ScriptLoader();
   PRBool scripts_enabled = loader->GetEnabled();
   loader->SetEnabled(PR_FALSE);
 
@@ -737,22 +742,19 @@ nsGenericHTMLElement::SetInnerHTML(const
                                          GetNameSpaceID(),
                                          doc->GetCompatibilityMode() ==
                                              eCompatibility_NavQuirks,
                                          PR_TRUE);
     doc->SetFragmentParser(parser);
 
     // HTML5 parser has notified, but not fired mutation events.
     // Fire mutation events. Optimize for the case when there are no listeners
-    nsPIDOMWindow* window = nsnull;
     PRInt32 newChildCount = GetChildCount();
-    if (newChildCount &&
-        (((window = doc->GetInnerWindow()) &&
-          window->HasMutationListeners(NS_EVENT_BITS_MUTATION_NODEINSERTED)) ||
-         !window)) {
+    if (newChildCount && nsContentUtils::
+          HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) {
       nsCOMArray<nsIContent> childNodes;
       NS_ASSERTION(newChildCount - oldChildCount >= 0,
                    "What, some unexpected dom mutation has happened?");
       childNodes.SetCapacity(newChildCount - oldChildCount);
       for (nsINode::ChildIterator iter(this); !iter.IsDone(); iter.Next()) {
         childNodes.AppendObject(iter);
       }
       nsGenericElement::FireNodeInserted(doc, this, childNodes);
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -119,16 +119,17 @@
 #include "nsXULPopupListener.h"
 #include "nsRuleWalker.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsCSSParser.h"
 #include "nsIListBoxObject.h"
 #include "nsContentUtils.h"
 #include "nsContentList.h"
 #include "nsMutationEvent.h"
+#include "nsPLDOMEvent.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMAttributeMap.h"
 #include "nsGkAtoms.h"
 #include "nsXULContentUtils.h"
 #include "nsNodeUtils.h"
 #include "nsFrameLoader.h"
 #include "prlog.h"
@@ -1473,30 +1474,27 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpa
             MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, aNotify);
             doc->ContentStateChanged(this, stateMask);
         }
         nsNodeUtils::AttributeChanged(this, aNameSpaceID, aName,
                                       nsIDOMMutationEvent::REMOVAL);
     }
 
     if (hasMutationListeners) {
-        mozAutoRemovableBlockerRemover blockerRemover(GetOwnerDoc());
-
         nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED);
 
         mutation.mRelatedNode = attrNode;
         mutation.mAttrName = aName;
 
         if (!oldValue.IsEmpty())
           mutation.mPrevAttrValue = do_GetAtom(oldValue);
         mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
 
         mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
-        nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
-                                    nsnull, &mutation);
+        (new nsPLDOMEvent(this, mutation))->RunDOMEventWhenSafe();
     }
 
     return NS_OK;
 }
 
 void
 nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId)
 {
--- a/docshell/base/crashtests/crashtests.list
+++ b/docshell/base/crashtests/crashtests.list
@@ -1,11 +1,11 @@
 load 40929-1.html
 load 369126-1.html
 load 403574-1.xhtml
 load 430124-1.html
 load 430628-1.html
-asserts(1-4) load 432114-1.html # bug 570215
-asserts(1) load 432114-2.html # bug 570215
+load 432114-1.html
+load 432114-2.html
 load 436900-1.html
 asserts(0-2) load 436900-2.html # bug 566159
 load 500328-1.html
 load 514779-1.xhtml
--- a/editor/libeditor/html/crashtests/crashtests.list
+++ b/editor/libeditor/html/crashtests/crashtests.list
@@ -16,9 +16,8 @@ load 499844-1.html
 load 503709-1.xhtml
 load 513375-1.xhtml
 load 535632-1.xhtml
 load 574558-1.xhtml
 load 582138-1.xhtml
 load 612565-1.html
 asserts(6) load 615015-1.html # Bug 439258
 load 615450-1.html
-load 643786-1.html
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -9197,19 +9197,16 @@ nsHTMLEditRules::DocumentModified()
 
 void
 nsHTMLEditRules::DocumentModifiedWorker()
 {
   if (!mHTMLEditor) {
     return;
   }
 
-  // DeleteNode below may cause a flush, which could destroy the editor
-  nsAutoRemovableScriptBlocker scriptBlocker;
-
   nsCOMPtr<nsIHTMLEditor> kungFuDeathGrip(mHTMLEditor);
   nsCOMPtr<nsISelection> selection;
   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(res, );
 
   // Delete our bogus node, if we have one, since the document might not be
   // empty any more.
   if (mBogusNode) {
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -199,17 +199,17 @@ load 406675-1.html
 load 408292.html
 load 408299.html
 load 408450-1.xhtml
 load 409461-1.xhtml
 load 409513.html
 load 410967.html
 load 411870-1.html
 load 412651-1.html
-asserts(6) load 413587-1.svg # Bug 563481?
+load 413587-1.svg
 load 414058-1.html
 load 414175-1.xul
 load 420031-1.html
 load 420213-1.html
 load 420219-1.html
 load 420651-1.xhtml
 load 421203-1.xul
 load 423107-1.xhtml
--- a/layout/reftests/svg/as-image/reftest.list
+++ b/layout/reftests/svg/as-image/reftest.list
@@ -47,18 +47,17 @@ fails == canvas-drawImage-slice-1b.html 
 fails-if(d2d) == img-simple-3.html  img-simple-3-ref.html # bug 633072
 == img-simple-4.html  lime100x100-ref.html
 fails-if(d2d) == img-simple-5.html  img-simple-5-ref.html # bug 633072
 == img-simple-6.html  lime100x100-ref.html
 fails-if(d2d) == img-simple-7.html  img-simple-7-ref.html # bug 633072
 
 # Test with mix of <html:img> and <svg:image> referring to the same images,
 # with a variety of preserveAspectRatio values in play.
-# NOTE: The reference cases in this test triggers 72 assertions (bug 563481)
-fails-if(Android) asserts(72) == img-and-image-1.html img-and-image-1-ref.svg
+fails-if(Android) == img-and-image-1.html img-and-image-1-ref.svg
 
 # More complex <img> tests
 == img-content-outside-viewBox-1.html img-content-outside-viewBox-1-ref.html
 == img-dyn-1.html img-dyn-1-ref.html
 == img-foreignObject-1.html lime100x100-ref.html
 == img-foreignObject-embed-1.html lime100x100-ref.html
 == img-foreignObject-embed-2.html lime100x100-ref.html
 
--- a/layout/reftests/svg/image/reftest.list
+++ b/layout/reftests/svg/image/reftest.list
@@ -14,16 +14,14 @@
 == image-x-01.svg             image-x-01-ref.svg
 == image-xy-01.svg            image-xy-01-ref.svg
 == image-y-01.svg             image-y-01-ref.svg
 == image-zoom-01a.svg         image-zoom-01-ref.svg
 == image-zoom-01b.svg         image-zoom-01-ref.svg
 == image-zoom-02.svg          image-zoom-02-ref.svg
 
 # Tests for <image> with preserveAspectRatio
-# NOTE: The reference cases in the following tests trigger 20+ assertions each
-# (1 per <symbol> element), due to bug 563481.
-asserts(20) == image-preserveAspectRatio-01-raster.svg image-preserveAspectRatio-01-ref.svg
-asserts(20) == image-preserveAspectRatio-01-svg.svg    image-preserveAspectRatio-01-ref.svg
-asserts(20) == image-preserveAspectRatio-02-raster.svg image-preserveAspectRatio-02-ref.svg
-asserts(20) == image-preserveAspectRatio-02-svg.svg    image-preserveAspectRatio-02-ref.svg
-asserts(21) == image-preserveAspectRatio-03.svg        image-preserveAspectRatio-03-ref.svg
-asserts(21) == image-preserveAspectRatio-04.svg        image-preserveAspectRatio-04-ref.svg
+== image-preserveAspectRatio-01-raster.svg image-preserveAspectRatio-01-ref.svg
+== image-preserveAspectRatio-01-svg.svg    image-preserveAspectRatio-01-ref.svg
+== image-preserveAspectRatio-02-raster.svg image-preserveAspectRatio-02-ref.svg
+== image-preserveAspectRatio-02-svg.svg    image-preserveAspectRatio-02-ref.svg
+== image-preserveAspectRatio-03.svg        image-preserveAspectRatio-03-ref.svg
+== image-preserveAspectRatio-04.svg        image-preserveAspectRatio-04-ref.svg
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -178,17 +178,17 @@ random-if(gtk2Widget) == objectBoundingB
 == selector-01.svg pass.svg
 == stroke-width-percentage-01.svg pass.svg
 == style-property-not-on-script-element-01.svg pass.svg
 == style-without-type-attribute.svg pass.svg
 == svg-in-foreignObject-01.xhtml svg-in-foreignObject-01-ref.xhtml
 == svg-in-foreignObject-02.xhtml svg-in-foreignObject-01-ref.xhtml # reuse -01-ref.xhtml
 random-if(gtk2Widget) == text-font-weight-01.svg text-font-weight-01-ref.svg # bug 386713
 fails-if(Android) == switch-01.svg pass.svg # bug 652050
-asserts(3) == symbol-01.svg symbol-01-ref.svg # see bug 563481
+== symbol-01.svg symbol-01-ref.svg
 == text-gradient-01.svg text-gradient-01-ref.svg
 random-if(winWidget) == text-gradient-02.svg text-gradient-02-ref.svg # see bug 590101
 == text-gradient-03.svg pass.svg
 == text-in-link-01.svg text-in-link-01-ref.svg
 == text-in-link-02.svg text-in-link-02-ref.svg
 == text-in-link-03.svg text-in-link-03-ref.svg
 # Tests for bug 546813: sanity-check using HTML text, then test SVG behavior.
 fails-if(Android) != text-language-00.xhtml text-language-00-ref.xhtml
--- a/layout/reftests/svg/smil/reftest.list
+++ b/layout/reftests/svg/smil/reftest.list
@@ -125,17 +125,17 @@ fails == anim-fillcolor-1.svg      anim-
 == anim-feImage-preserveAspectRatio-01.svg lime.svg
 == anim-svg-preserveAspectRatio-01.svg lime.svg
 
 # animate some string attributes:
 == anim-filter-href-01.svg lime.svg
 == anim-gradient-href-01.svg lime.svg
 == anim-image-href-01.svg lime.svg
 == anim-pattern-href-01.svg lime.svg
-asserts(9) == anim-use-href-01.svg lime.svg  # the asserts here are bug 563481
+== anim-use-href-01.svg lime.svg
 
 # animate the class attribute
 == anim-class-01.svg lime.svg
 == anim-class-02.svg lime.svg
 == anim-class-03.svg lime.svg
 == anim-class-04.svg anim-class-04-ref.svg
 
 # animate with some paint server values