Bug 401155: Make it safe to create a wrapper for an object at any time by making sure wrapping doesn't write script. Also set up a service that tries to keep track of when it's safe to execute script. r=bz sr=jst
authorjonas@sicking.cc
Fri, 14 Mar 2008 16:08:57 -0700
changeset 13098 a054532372902bafaf15f94820d0863095afdb60
parent 13097 56f5f3c0f7e2251761b865031b39bf920c4aa544
child 13099 1a0a0970be1452a82f4abf4d227d9572ece05be6
push idunknown
push userunknown
push dateunknown
reviewersbz, jst
bugs401155
milestone1.9b5pre
Bug 401155: Make it safe to create a wrapper for an object at any time by making sure wrapping doesn't write script. Also set up a service that tries to keep track of when it's safe to execute script. r=bz sr=jst
content/base/public/nsContentUtils.h
content/base/public/nsIDocument.h
content/base/public/nsIObjectLoadingContent.idl
content/base/src/nsContentUtils.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsObjectLoadingContent.cpp
content/base/src/nsObjectLoadingContent.h
content/base/src/nsScriptLoader.cpp
content/base/src/nsScriptLoader.h
content/xbl/src/nsBindingManager.cpp
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLBinding.h
content/xbl/src/nsXBLService.cpp
content/xul/content/src/nsXULElement.cpp
dom/src/base/nsDOMClassInfo.cpp
dom/src/base/nsDOMClassInfo.h
layout/base/nsDocumentViewer.cpp
layout/base/nsPresShell.cpp
layout/generic/Makefile.in
layout/generic/nsObjectFrame.cpp
view/src/Makefile.in
view/src/nsViewManager.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -85,16 +85,17 @@ class nsIConsoleService;
 class nsIStringBundleService;
 class nsIStringBundle;
 class nsIContentPolicy;
 class nsILineBreaker;
 class nsIWordBreaker;
 class nsIJSRuntimeService;
 class nsIEventListenerManager;
 class nsIScriptContext;
+class nsIRunnable;
 template<class E> class nsCOMArray;
 class nsIPref;
 class nsVoidArray;
 struct JSRuntime;
 class nsICaseConversion;
 class nsIWidget;
 class nsPIDOMWindow;
 #ifdef MOZ_XTF
@@ -1172,16 +1173,55 @@ public:
    */
   static void GetOfflineAppManifest(nsIDOMWindow *aWindow, nsIURI **aURI);
 
   /**
    * Check whether an application should be allowed to use offline APIs.
    */
   static PRBool OfflineAppAllowed(nsIURI *aURI);
 
+  /**
+   * Increases the count of blockers preventing scripts from running.
+   * NOTE: You might want to use nsAutoScriptBlocker rather than calling
+   * this directly
+   */
+  static void AddScriptBlocker();
+
+  /**
+   * Decreases the count of blockers preventing scripts from running.
+   * NOTE: You might want to use nsAutoScriptBlocker rather than calling
+   * this directly
+   *
+   * WARNING! Calling this function could synchronously execute scripts.
+   */
+  static void RemoveScriptBlocker();
+
+  /**
+   * Add a runnable that is to be executed as soon as it's safe to execute
+   * scripts.
+   * NOTE: If it's currently safe to execute scripts, aRunnable will be run
+   *       synchronously before the function returns.
+   *
+   * @param aRunnable  The nsIRunnable to run as soon as it's safe to execute
+   *                   scripts. Passing null is allowed and results in nothing
+   *                   happening. It is also allowed to pass an object that
+   *                   has not yet been AddRefed.
+   */
+  static PRBool AddScriptRunner(nsIRunnable* aRunnable);
+
+  /**
+   * Returns true if it's safe to execute content script and false otherwise.
+   *
+   * The only known case where this lies is mutation events. They run, and can
+   * run anything else, when this function returns false, but this is ok.
+   */
+  static PRBool IsSafeToRunScript() {
+    return sScriptBlockerCount == 0;
+  }
+
 private:
 
   static PRBool InitializeEventTable();
 
   static nsresult doReparentContentWrapper(nsIContent *aChild,
                                            JSContext *cx,
                                            JSObject *aOldGlobal,
                                            JSObject *aNewGlobal,
@@ -1244,16 +1284,19 @@ private:
   static PRInt32 sScriptRootCount[NS_STID_ARRAY_UBOUND];
   static PRUint32 sJSGCThingRootCount;
 
 #ifdef IBMBIDI
   static nsIBidiKeyboard* sBidiKeyboard;
 #endif
 
   static PRBool sInitialized;
+  static PRUint32 sScriptBlockerCount;
+  static nsCOMArray<nsIRunnable>* sBlockedScriptRunners;
+  static PRUint32 sRunnersCountAtFirstBlocker;
 };
 
 #define NS_HOLD_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz),        \
                                 &NS_CYCLE_COLLECTION_NAME(clazz))
 
 #define NS_DROP_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::DropJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz))
@@ -1262,16 +1305,17 @@ private:
 class nsCxPusher
 {
 public:
   nsCxPusher();
   ~nsCxPusher(); // Calls Pop();
 
   // Returns PR_FALSE if something erroneous happened.
   PRBool Push(nsISupports *aCurrentTarget);
+  PRBool Push(JSContext *cx);
   void Pop();
 
 private:
   nsCOMPtr<nsIJSContextStack> mStack;
   nsCOMPtr<nsIScriptContext> mScx;
   PRBool mScriptIsRunning;
 };
 
@@ -1312,16 +1356,26 @@ private:
 
   static nsIJSRuntimeService* sJSRuntimeService;
   static JSRuntime* sJSScriptRuntime;
 
   void* mPtr;
   nsresult mResult;
 };
 
+class nsAutoScriptBlocker {
+public:
+  nsAutoScriptBlocker() {
+    nsContentUtils::AddScriptBlocker();
+  }
+  ~nsAutoScriptBlocker() {
+    nsContentUtils::RemoveScriptBlocker();
+  }
+};
+
 #define NS_AUTO_GCROOT_PASTE2(tok,line) tok##line
 #define NS_AUTO_GCROOT_PASTE(tok,line) \
   NS_AUTO_GCROOT_PASTE2(tok,line)
 #define NS_AUTO_GCROOT(ptr, result) \ \
   nsAutoGCRoot NS_AUTO_GCROOT_PASTE(_autoGCRoot_, __LINE__) \
   (ptr, result)
 
 #define NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(_class)                      \
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -591,16 +591,18 @@ public:
   virtual PRBool RemoveObserver(nsIDocumentObserver* aObserver) = 0;
 
   // Observation hooks used to propagate notifications to document observers.
   // BeginUpdate must be called before any batch of modifications of the
   // content model or of style data, EndUpdate must be called afterward.
   // To make this easy and painless, use the mozAutoDocUpdate helper class.
   virtual void BeginUpdate(nsUpdateType aUpdateType) = 0;
   virtual void EndUpdate(nsUpdateType aUpdateType) = 0;
+  virtual PRUint32 GetUpdateNestingLevel() = 0;
+  virtual PRBool AllUpdatesAreContent() = 0;
   virtual void BeginLoad() = 0;
   virtual void EndLoad() = 0;
   // notify that one or two content nodes changed state
   // either may be nsnull, but not both
   virtual void ContentStatesChanged(nsIContent* aContent1,
                                     nsIContent* aContent2,
                                     PRInt32 aStateMask) = 0;
 
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -38,17 +38,17 @@
 #include "nsISupports.idl"
 
 interface nsIObjectFrame;
 interface nsIPluginInstance;
 
 /**
  * This interface represents a content node that loads objects.
  */
-[scriptable, uuid(42f9358e-300f-4a44-afd7-7830b750e955)]
+[scriptable, uuid(90ab443e-3e99-405e-88c9-9c42adaa3217)]
 interface nsIObjectLoadingContent : nsISupports
 {
   const unsigned long TYPE_LOADING  = 0;
   const unsigned long TYPE_IMAGE    = 1;
   const unsigned long TYPE_PLUGIN   = 2;
   const unsigned long TYPE_DOCUMENT = 3;
   const unsigned long TYPE_NULL     = 4;
 
@@ -66,16 +66,23 @@ interface nsIObjectLoadingContent : nsIS
 
   /**
    * Gets the content type that corresponds to the give MIME type.  See the
    * constants above for the list of possible values.  If nothing else fits,
    * TYPE_NULL will be returned.
    */
   unsigned long getContentTypeForMIMEType(in AUTF8String aMimeType);
 
+  /**
+   * Returns the plugin instance if it has already been instantiated. This
+   * will never instantiate the plugin and so is safe to call even when
+   * content script must not execute.
+   */
+  [noscript] readonly attribute nsIPluginInstance pluginInstance;
+
 
   /**
    * Makes sure that a frame for this object exists, and that the plugin is
    * instantiated. This method does nothing if the type is not #TYPE_PLUGIN.
    * There is no guarantee that there will be a frame after this method is
    * called; for example, the node may have a display:none style. If plugin
    * instantiation is possible, it will be done synchronously by this method,
    * and the plugin instance will be returned. A success return value does not
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -144,16 +144,18 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsCompressedCharMap.h"
 #include "nsINativeKeyBindings.h"
 #include "nsIDOMNSUIEvent.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsXULPopupManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
+#include "nsIRunnable.h"
+#include "nsDOMJSUtils.h"
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #endif
 #include "nsCycleCollectionParticipant.h"
 
 // for ReportToConsole
 #include "nsIStringBundle.h"
@@ -190,16 +192,19 @@ nsIWordBreaker *nsContentUtils::sWordBre
 nsICaseConversion *nsContentUtils::sCaseConv;
 nsVoidArray *nsContentUtils::sPtrsToPtrsToRelease;
 nsIScriptRuntime *nsContentUtils::sScriptRuntimes[NS_STID_ARRAY_UBOUND];
 PRInt32 nsContentUtils::sScriptRootCount[NS_STID_ARRAY_UBOUND];
 PRUint32 nsContentUtils::sJSGCThingRootCount;
 #ifdef IBMBIDI
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull;
 #endif
+PRUint32 nsContentUtils::sScriptBlockerCount = 0;
+nsCOMArray<nsIRunnable>* nsContentUtils::sBlockedScriptRunners = nsnull;
+PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
 
 nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService;
 JSRuntime *nsAutoGCRoot::sJSScriptRuntime;
 
 PRBool nsContentUtils::sInitialized = PR_FALSE;
 
 static PLDHashTable sEventListenerManagersHash;
 
@@ -318,16 +323,19 @@ nsContentUtils::Init()
     if (!PL_DHashTableInit(&sEventListenerManagersHash, &hash_table_ops,
                            nsnull, sizeof(EventListenerManagerMapEntry), 16)) {
       sEventListenerManagersHash.ops = nsnull;
 
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
+  sBlockedScriptRunners = new nsCOMArray<nsIRunnable>;
+  NS_ENSURE_TRUE(sBlockedScriptRunners, NS_ERROR_OUT_OF_MEMORY);
+
   sInitialized = PR_TRUE;
 
   return NS_OK;
 }
 
 PRBool
 nsContentUtils::InitializeEventTable() {
   NS_ASSERTION(!sEventTable, "EventTable already initialized!");
@@ -816,16 +824,22 @@ nsContentUtils::Shutdown()
     // wrapper table.
 
     if (sEventListenerManagersHash.entryCount == 0) {
       PL_DHashTableFinish(&sEventListenerManagersHash);
       sEventListenerManagersHash.ops = nsnull;
     }
   }
 
+  NS_ASSERTION(!sBlockedScriptRunners ||
+               sBlockedScriptRunners->Count() == 0,
+               "How'd this happen?");
+  delete sBlockedScriptRunners;
+  sBlockedScriptRunners = nsnull;
+
   nsAutoGCRoot::Shutdown();
 }
 
 // static
 PRBool
 nsContentUtils::IsCallerTrustedForCapability(const char* aCapability)
 {
   // The secman really should handle UniversalXPConnect case, since that
@@ -2583,47 +2597,64 @@ nsCxPusher::Push(nsISupports *aCurrentTa
       NS_ENSURE_TRUE(sgo || !hasHadScriptObject, PR_FALSE);
     }
   } else {
     sgo = do_QueryInterface(aCurrentTarget);
   }
 
   JSContext *cx = nsnull;
 
+  nsCOMPtr<nsIScriptContext> scx;
+
   if (sgo) {
-    mScx = sgo->GetContext();
-
-    if (mScx) {
-      cx = (JSContext *)mScx->GetNativeContext();
+    scx = sgo->GetContext();
+
+    if (scx) {
+      cx = (JSContext *)scx->GetNativeContext();
     }
     // Bad, no JSContext from script global object!
     NS_ENSURE_TRUE(cx, PR_FALSE);
   }
 
+  // If there's no native context in the script context it must be
+  // in the process or being torn down. We don't want to notify the
+  // script context about scripts having been evaluated in such a
+  // case, calling with a null cx is fine in that case.
+  return Push(cx);
+}
+
+PRBool
+nsCxPusher::Push(JSContext *cx)
+{
+  if (mScx) {
+    NS_ERROR("Whaaa! No double pushing with nsCxPusher::Push()!");
+
+    return PR_FALSE;
+  }
+
   if (cx) {
+    mScx = GetScriptContextFromJSContext(cx);
+    if (!mScx) {
+      // Should probably return PR_FALSE. See bug 416916.
+      return PR_TRUE;
+    }
+
     if (!mStack) {
       mStack = do_GetService(kJSStackContractID);
     }
 
     if (mStack) {
       if (IsContextOnStack(mStack, cx)) {
         // If the context is on the stack, that means that a script
         // is running at the moment in the context.
         mScriptIsRunning = PR_TRUE;
       }
 
       mStack->Push(cx);
     }
-  } else {
-    // If there's no native context in the script context it must be
-    // in the process or being torn down. We don't want to notify the
-    // script context about scripts having been evaluated in such a
-    // case, so null out mScx.
-
-    mScx = nsnull;
   }
   return PR_TRUE;
 }
 
 void
 nsCxPusher::Pop()
 {
   if (!mScx || !mStack) {
@@ -3957,16 +3988,75 @@ nsContentUtils::DOMEventToNativeKeyEvent
 
   aNativeEvent->nativeEvent = GetNativeEvent(aDOMEvent);
 
   return PR_TRUE;
 }
 
 /* static */
 void
+nsContentUtils::AddScriptBlocker()
+{
+  if (!sScriptBlockerCount) {
+    NS_ASSERTION(sRunnersCountAtFirstBlocker == 0,
+                 "Should not already have a count");
+    sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Count();
+  }
+  ++sScriptBlockerCount;
+}
+
+/* static */
+void
+nsContentUtils::RemoveScriptBlocker()
+{
+  --sScriptBlockerCount;
+  if (sScriptBlockerCount) {
+    return;
+  }
+
+  PRUint32 firstBlocker = sRunnersCountAtFirstBlocker;
+  PRUint32 lastBlocker = sBlockedScriptRunners->Count();
+  sRunnersCountAtFirstBlocker = 0;
+  NS_ASSERTION(firstBlocker <= lastBlocker,
+               "bad sRunnersCountAtFirstBlocker");
+
+  while (firstBlocker < lastBlocker) {
+    nsCOMPtr<nsIRunnable> runnable = (*sBlockedScriptRunners)[firstBlocker];
+    sBlockedScriptRunners->RemoveObjectAt(firstBlocker);
+    --lastBlocker;
+
+    runnable->Run();
+    NS_ASSERTION(lastBlocker == sBlockedScriptRunners->Count() &&
+                 sRunnersCountAtFirstBlocker == 0,
+                 "Bad count");
+    NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
+  }
+}
+
+
+/* static */
+PRBool
+nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
+{
+  if (!aRunnable) {
+    return PR_FALSE;
+  }
+
+  if (sScriptBlockerCount) {
+    return sBlockedScriptRunners->AppendObject(aRunnable);
+  }
+  
+  nsCOMPtr<nsIRunnable> run = aRunnable;
+  run->Run();
+
+  return PR_TRUE;
+}
+
+/* static */
+void
 nsContentUtils::HidePopupsInDocument(nsIDocument* aDocument)
 {
   NS_PRECONDITION(aDocument, "Null document");
 
 #ifdef MOZ_XUL
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm) {
     nsCOMPtr<nsISupports> container = aDocument->GetContainer();
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -2696,50 +2696,64 @@ nsDocument::RemoveObserver(nsIDocumentOb
 void
 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
 {
   if (mUpdateNestLevel == 0) {
     mBindingManager->BeginOutermostUpdate();
   }
   
   ++mUpdateNestLevel;
-  if (mScriptLoader) {
-    mScriptLoader->AddExecuteBlocker();
+  if (aUpdateType == UPDATE_CONTENT_MODEL) {
+    ++mContentUpdateNestLevel;
   }
   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
+
+  nsContentUtils::AddScriptBlocker();
 }
 
 void
 nsDocument::EndUpdate(nsUpdateType aUpdateType)
 {
+  nsContentUtils::RemoveScriptBlocker();
   NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
 
+  if (aUpdateType == UPDATE_CONTENT_MODEL) {
+    --mContentUpdateNestLevel;
+  }
   --mUpdateNestLevel;
   if (mUpdateNestLevel == 0) {
     // This set of updates may have created XBL bindings.  Let the
     // binding manager know we're done.
     mBindingManager->EndOutermostUpdate();
   }
 
-  if (mScriptLoader) {
-    mScriptLoader->RemoveExecuteBlocker();
-  }
-
   if (mUpdateNestLevel == 0) {
     PRUint32 length = mFinalizableFrameLoaders.Length();
     if (length > 0) {
       nsTArray<nsRefPtr<nsFrameLoader> > loaders;
       mFinalizableFrameLoaders.SwapElements(loaders);
       for (PRInt32 i = 0; i < length; ++i) {
         loaders[i]->Finalize();
       }
     }
   }
 }
 
+PRUint32
+nsDocument::GetUpdateNestingLevel()
+{
+  return mUpdateNestLevel;
+}
+
+PRBool
+nsDocument::AllUpdatesAreContent()
+{
+  return mContentUpdateNestLevel == mUpdateNestLevel;
+}
+
 void
 nsDocument::BeginLoad()
 {
   // Block onload here to prevent having to deal with blocking and
   // unblocking it while we know the document is loading.
   BlockOnload();
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
@@ -5811,16 +5825,18 @@ nsDocument::MutationEventDispatched(nsIN
         realTargets.AppendObject(possibleTarget);
       }
     }
 
     mSubtreeModifiedTargets.Clear();
 
     PRInt32 realTargetCount = realTargets.Count();
     for (PRInt32 k = 0; k < realTargetCount; ++k) {
+      mozAutoDocUpdateContentUnnest updateUnnest(this);
+
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_SUBTREEMODIFIED);
       nsEventDispatcher::Dispatch(realTargets[k], nsnull, &mutation);
     }
   }
 }
 
 static PRUint32 GetURIHash(nsIURI* aURI)
 {
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -469,16 +469,18 @@ public:
    * return false if the observer cannot be found.
    */
   virtual PRBool RemoveObserver(nsIDocumentObserver* aObserver);
 
   // Observation hooks used to propagate notifications to document
   // observers.
   virtual void BeginUpdate(nsUpdateType aUpdateType);
   virtual void EndUpdate(nsUpdateType aUpdateType);
+  virtual PRUint32 GetUpdateNestingLevel();
+  virtual PRBool AllUpdatesAreContent();
   virtual void BeginLoad();
   virtual void EndLoad();
   virtual void ContentStatesChanged(nsIContent* aContent1,
                                     nsIContent* aContent2,
                                     PRInt32 aStateMask);
 
   virtual void AttributeWillChange(nsIContent* aChild,
                                    PRInt32 aNameSpaceID,
@@ -794,16 +796,18 @@ protected:
   nsRefPtr<nsXMLEventsManager> mXMLEventsManager;
 
   nsCOMPtr<nsIScriptEventManager> mScriptEventManager;
 
   nsString mBaseTarget;
 
   // Our update nesting level
   PRUint32 mUpdateNestLevel;
+  // Our UPDATE_CONTENT_MODEL update nesting level
+  PRUint32 mContentUpdateNestLevel;
 
 private:
   friend class nsUnblockOnloadEvent;
 
   void PostUnblockOnloadEvent();
   void DoUnblockOnload();
 
   /**
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -487,16 +487,18 @@ nsGenericDOMDataNode::SetTextInternal(PR
 
       mutation.mPrevAttrValue = oldValue;
       if (aLength > 0) {
         nsAutoString val;
         mText.AppendTo(val);
         mutation.mNewAttrValue = do_GetAtom(val);
       }
 
+      mozAutoDocUpdateContentUnnest updateUnnest(document);
+
       mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
       nsEventDispatcher::Dispatch(this, nsnull, &mutation);
     }
   }
 
   return NS_OK;
 }
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -2753,16 +2753,19 @@ nsGenericElement::doInsertChildAt(nsICon
     } else {
       nsNodeUtils::ContentInserted(container, aKid, aIndex);
     }
 
     if (nsContentUtils::HasMutationListeners(aKid,
           NS_EVENT_BITS_MUTATION_NODEINSERTED, container)) {
       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEINSERTED);
       mutation.mRelatedNode = do_QueryInterface(container);
+
+      mozAutoDocUpdateContentUnnest updateUnnest(aDocument);
+
       mozAutoSubtreeModified subtree(container->GetOwnerDoc(), container);
       nsEventDispatcher::Dispatch(aKid, nsnull, &mutation);
     }
   }
 
   return NS_OK;
 }
 
@@ -2820,16 +2823,19 @@ nsGenericElement::doRemoveChildAt(PRUint
   nsMutationGuard guard;
 
   mozAutoSubtreeModified subtree(nsnull, nsnull);
   if (aNotify &&
       nsContentUtils::HasMutationListeners(aKid,
         NS_EVENT_BITS_MUTATION_NODEREMOVED, container)) {
     nsMutationEvent mutation(PR_TRUE, NS_MUTATION_NODEREMOVED);
     mutation.mRelatedNode = do_QueryInterface(container);
+
+    mozAutoDocUpdateContentUnnest updateUnnest(aDocument);
+
     subtree.UpdateTarget(container->GetOwnerDoc(), container);
     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 = container->IndexOf(aKid);
@@ -3787,16 +3793,19 @@ nsGenericElement::SetAttrAndNotify(PRInt
     GetAttr(aNamespaceID, aName, newValue);
     if (!newValue.IsEmpty()) {
       mutation.mNewAttrValue = do_GetAtom(newValue);
     }
     if (!aOldValue.IsEmpty()) {
       mutation.mPrevAttrValue = do_GetAtom(aOldValue);
     }
     mutation.mAttrChange = modType;
+
+    mozAutoDocUpdateContentUnnest updateUnnest(document);
+
     mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
     nsEventDispatcher::Dispatch(this, nsnull, &mutation);
   }
 
   if (aNamespaceID == kNameSpaceID_XMLEvents && 
       aName == nsGkAtoms::event && mNodeInfo->GetDocument()) {
     mNodeInfo->GetDocument()->AddXMLEventsContent(this);
   }
@@ -4034,16 +4043,18 @@ nsGenericElement::UnsetAttr(PRInt32 aNam
     mutation.mAttrName = aName;
 
     nsAutoString value;
     oldValue.ToString(value);
     if (!value.IsEmpty())
       mutation.mPrevAttrValue = do_GetAtom(value);
     mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
 
+    mozAutoDocUpdateContentUnnest updateUnnest(document);
+
     mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
     nsEventDispatcher::Dispatch(this, nsnull, &mutation);
   }
 
   return AfterSetAttr(aNameSpaceID, aName, nsnull, aNotify);
 }
 
 const nsAttrName*
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -58,16 +58,17 @@
 #include "nsGenericDOMNodeList.h"
 #include "nsContentUtils.h"
 #include "nsNodeUtils.h"
 #include "nsAttrAndChildArray.h"
 #include "mozFlushType.h"
 #include "nsDOMAttributeMap.h"
 #include "nsIWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsIDocument.h"
 
 class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIFrame;
 class nsIDOMNamedNodeMap;
 class nsDOMCSSDeclaration;
 class nsIDOMCSSStyleDeclaration;
 class nsIURI;
@@ -1061,9 +1062,38 @@ public:
   nsNSElementTearoff(nsGenericElement *aContent) : mContent(aContent)
   {
   }
   
 private:
   nsRefPtr<nsGenericElement> mContent;
 };
 
+class mozAutoDocUpdateContentUnnest
+{
+public:
+  mozAutoDocUpdateContentUnnest(nsIDocument* aDocument)
+  {
+    if (aDocument) {
+      NS_ASSERTION(aDocument->AllUpdatesAreContent(),
+                   "There are non-content updates in progress");
+      mNestingLevel = aDocument->GetUpdateNestingLevel();
+      for (PRUint32 i = 0; i < mNestingLevel; ++i) {
+        nsContentUtils::RemoveScriptBlocker();
+      }
+    }
+    else {
+      mNestingLevel = 0;
+    }
+  }
+
+  ~mozAutoDocUpdateContentUnnest()
+  {
+    for (PRUint32 i = 0; i < mNestingLevel; ++i) {
+      nsContentUtils::AddScriptBlocker();
+    }
+  }
+
+private:
+  PRUint32 mNestingLevel;
+};
+
 #endif /* nsGenericElement_h___ */
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -122,20 +122,21 @@ NS_IMETHODIMP
 nsAsyncInstantiateEvent::Run()
 {
   // Check if we've been "revoked"
   if (mContent->mPendingInstantiateEvent != this)
     return NS_OK;
   mContent->mPendingInstantiateEvent = nsnull;
 
   // Make sure that we still have the right frame (NOTE: we don't need to check
-  // the type here - GetFrame() only returns object frames, and that means we're
-  // a plugin)
+  // the type here - GetExistingFrame() only returns object frames, and that
+  // means we're a plugin)
   // Also make sure that we still refer to the same data.
-  nsIObjectFrame* frame = mContent->GetFrame(PR_FALSE);
+  nsIObjectFrame* frame = mContent->
+    GetExistingFrame(nsObjectLoadingContent::eFlushContent);
   if (frame == mFrame &&
       mContent->mURI == mURI &&
       mContent->mContentType.Equals(mContentType)) {
     if (LOG_ENABLED()) {
       nsCAutoString spec;
       if (mURI) {
         mURI->GetSpec(spec);
       }
@@ -514,17 +515,17 @@ nsObjectLoadingContent::OnStartRequest(n
     case eType_Plugin:
       mInstantiating = PR_TRUE;
       if (mType != newType) {
         // This can go away once plugin loading moves to content (bug 90268)
         mType = newType;
         notifier.Notify();
       }
       nsIObjectFrame* frame;
-      frame = GetFrame(PR_TRUE);
+      frame = GetExistingFrame(eFlushLayout);
       if (!frame) {
         // Do nothing in this case: This is probably due to a display:none
         // frame. If we ever get a frame, HasNewFrame will do the right thing.
         // Abort the load though, we have no use for the data.
         mInstantiating = PR_FALSE;
         return NS_BINDING_ABORTED;
       }
 
@@ -580,17 +581,17 @@ nsObjectLoadingContent::OnStartRequest(n
       if (mContentType.EqualsLiteral("application/x-director")) {
         LOG(("OBJLC [%p]: (ignoring)\n", this));
         rv = NS_OK; // otherwise, the AutoFallback will make us fall back
         return NS_BINDING_ABORTED;
       }
 #endif
       Fallback(PR_FALSE);
     } else if (mType == eType_Plugin) {
-      nsIObjectFrame* frame = GetFrame(PR_FALSE);
+      nsIObjectFrame* frame = GetExistingFrame(eFlushContent);
       if (frame) {
         // We have to notify the wrapper here instead of right after
         // Instantiate because the plugin only gets instantiated by
         // OnStartRequest, not by Instantiate.
         frame->TryNotifyContentObjectWrapper();
       }
     }
     return rv;
@@ -669,17 +670,17 @@ nsObjectLoadingContent::EnsureInstantiat
   // Must set our out parameter to null as we have various early returns with
   // an NS_OK result.
   *aInstance = nsnull;
 
   if (mType != eType_Plugin) {
     return NS_OK;
   }
 
-  nsIObjectFrame* frame = GetFrame(PR_FALSE);
+  nsIObjectFrame* frame = GetExistingFrame(eFlushContent);
   if (frame) {
     // If we have a frame, we may have pending instantiate events; revoke
     // them.
     if (mPendingInstantiateEvent) {
       LOG(("OBJLC [%p]: Revoking pending instantiate event\n", this));
       mPendingInstantiateEvent = nsnull;
     }
   } else {
@@ -706,17 +707,17 @@ nsObjectLoadingContent::EnsureInstantiat
     nsPresShellIterator iter(doc);
     nsCOMPtr<nsIPresShell> shell;
     while ((shell = iter.GetNextShell())) {
       shell->RecreateFramesFor(thisContent);
     }
 
     mInstantiating = PR_FALSE;
 
-    frame = GetFrame(PR_FALSE);
+    frame = GetExistingFrame(eFlushContent);
     if (!frame) {
       return NS_OK;
     }
   }
 
   nsIFrame *nsiframe;
   CallQueryInterface(frame, &nsiframe);
   nsWeakFrame weakFrame(nsiframe);
@@ -770,16 +771,29 @@ nsObjectLoadingContent::HasNewFrame(nsIO
       // when the event runs.
       mPendingInstantiateEvent = event;
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsObjectLoadingContent::GetPluginInstance(nsIPluginInstance** aInstance)
+{
+  *aInstance = nsnull;
+
+  nsIObjectFrame* objFrame = GetExistingFrame(eDontFlush);
+  if (!objFrame) {
+    return NS_OK;
+  }
+
+  return objFrame->GetPluginInstance(*aInstance);
+}
+
+NS_IMETHODIMP
 nsObjectLoadingContent::GetContentTypeForMIMEType(const nsACString& aMIMEType,
                                                   PRUint32* aType)
 {
   *aType = GetTypeOfContent(PromiseFlatCString(aMIMEType));
   return NS_OK;
 }
 
 // nsIInterfaceRequestor
@@ -1542,23 +1556,22 @@ nsObjectLoadingContent::GetObjectBaseURI
                                               thisContent->GetOwnerDoc(),
                                               baseURI);
   } else {
     baseURI.swap(*aURI);
   }
 }
 
 nsIObjectFrame*
-nsObjectLoadingContent::GetFrame(PRBool aFlushLayout)
+nsObjectLoadingContent::GetExistingFrame(FlushType aFlushType)
 {
   nsCOMPtr<nsIContent> thisContent = 
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
 
-  PRBool flushed = PR_FALSE;
   nsIFrame* frame;
   do {
     nsIDocument* doc = thisContent->GetCurrentDoc();
     if (!doc) {
       return nsnull; // No current doc -> no frame
     }
 
     nsIPresShell* shell = doc->GetPrimaryShell();
@@ -1566,27 +1579,27 @@ nsObjectLoadingContent::GetFrame(PRBool 
       return nsnull; // No presentation -> no frame
     }
 
     frame = shell->GetPrimaryFrameFor(thisContent);
     if (!frame) {
       return nsnull;
     }
 
-    if (flushed) {
+    if (aFlushType == eDontFlush) {
       break;
     }
     
     // OK, let's flush out and try again.  Note that we want to reget
     // the document, etc, since flushing might run script.
     mozFlushType flushType =
-      aFlushLayout ? Flush_Layout : Flush_ContentAndNotify;
+      aFlushType == eFlushLayout ? Flush_Layout : Flush_ContentAndNotify;
     doc->FlushPendingNotifications(flushType);
 
-    flushed = PR_TRUE;
+    aFlushType = eDontFlush;
   } while (1);
 
   nsIObjectFrame* objFrame;
   CallQueryInterface(frame, &objFrame);
   return objFrame;
 }
 
 void
@@ -1605,17 +1618,17 @@ nsObjectLoadingContent::HandleBeingBlock
     }
   }
 }
 
 nsresult
 nsObjectLoadingContent::TryInstantiate(const nsACString& aMIMEType,
                                        nsIURI* aURI)
 {
-  nsIObjectFrame* frame = GetFrame(PR_FALSE);
+  nsIObjectFrame* frame = GetExistingFrame(eFlushContent);
   if (!frame) {
     LOG(("OBJLC [%p]: No frame yet\n", this));
     return NS_OK; // Not a failure to have no frame
   }
   nsIFrame* iframe;
   CallQueryInterface(frame, &iframe);
   if (iframe->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     LOG(("OBJLC [%p]: Frame hasn't been reflown yet\n", this));
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -266,24 +266,37 @@ class nsObjectLoadingContent : public ns
 
     /**
      * Gets the base URI to be used for this object. This differs from
      * nsIContent::GetBaseURI in that it takes codebase attributes into
      * account.
      */
     void GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI);
 
+
     /**
      * Gets the frame that's associated with this content node in
-     * presentation 0.  If aFlushLayout is true, this function will
-     * flush layout before trying to get the frame.  This is needed
-     * in some cases by plug-ins to ensure that NPP_SetWindow() gets
-     * called (from nsObjectFrame::DidReflow).
+     * presentation 0. Always returns null if the node doesn't currently
+     * have a frame.
+     *
+     * @param aFlush When eFlushContent will flush content notifications
+     *               before returning a non-null value.
+     *               When eFlushLayout will flush layout and content
+     *               notifications before returning a non-null value.
+     *               When eDontFlush will never flush.
+     *         
+     *   eFlushLayout is needed in some cases by plug-ins to ensure
+     *   that NPP_SetWindow() gets called (from nsObjectFrame::DidReflow).
      */
-    nsIObjectFrame* GetFrame(PRBool aFlushLayout);
+    enum FlushType {
+      eFlushContent,
+      eFlushLayout,
+      eDontFlush
+    };
+    nsIObjectFrame* GetExistingFrame(FlushType aFlushType);
 
     /**
      * Handle being blocked by a content policy.  aStatus is the nsresult
      * return value of the Should* call, while aRetval is what it returned in
      * its out parameter.
      */
     void HandleBeingBlockedByContentPolicy(nsresult aStatus,
                                            PRInt16 aRetval);
@@ -403,13 +416,13 @@ class nsObjectLoadingContent : public ns
      */
     PRBool                      mInstantiating : 1;
     // Blocking status from content policy
     PRBool                      mUserDisabled  : 1;
     PRBool                      mSuppressed    : 1;
     // Whether we fell back because of an unsupported type
     PRBool                      mTypeUnsupported:1;
 
-    friend struct nsAsyncInstantiateEvent;
+    friend class nsAsyncInstantiateEvent;
 };
 
 
 #endif
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -154,18 +154,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLo
 
 //////////////////////////////////////////////////////////////
 //
 //////////////////////////////////////////////////////////////
 
 nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
   : mDocument(aDocument),
     mBlockerCount(0),
-    mEnabled(PR_TRUE),
-    mHadPendingScripts(PR_FALSE)
+    mEnabled(PR_TRUE)
 {
 }
 
 nsScriptLoader::~nsScriptLoader()
 {
   mObservers.Clear();
 
   for (PRInt32 i = 0; i < mPendingRequests.Count(); i++) {
@@ -486,33 +485,42 @@ nsScriptLoader::ProcessScriptElement(nsI
     request->mLoading = PR_FALSE;
     request->mIsInline = PR_TRUE;
     request->mURI = mDocument->GetDocumentURI();
 
     request->mLineNo = aElement->GetScriptLineNumber();
 
     // If we've got existing pending requests, add ourselves
     // to this list.
-    if (mPendingRequests.Count() == 0 && ReadyToExecuteScripts()) {
+    if (mPendingRequests.Count() == 0 && ReadyToExecuteScripts() &&
+        nsContentUtils::IsSafeToRunScript()) {
       return ProcessRequest(request);
     }
   }
 
   // Add the request to our pending requests list
   NS_ENSURE_TRUE(mPendingRequests.AppendObject(request),
                  NS_ERROR_OUT_OF_MEMORY);
 
+  // If there weren't any pending requests before, and this one is
+  // ready to execute, do that as soon as it's safe.
+  if (mPendingRequests.Count() == 1 && !request->mLoading &&
+      ReadyToExecuteScripts()) {
+    nsContentUtils::AddScriptRunner(new nsRunnableMethod<nsScriptLoader>(this,
+      &nsScriptLoader::ProcessPendingRequests));
+  }
+
   // Added as pending request, now we can send blocking back
   return NS_ERROR_HTMLPARSER_BLOCK;
 }
 
 nsresult
 nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
 {
-  NS_ASSERTION(ReadyToExecuteScripts(),
+  NS_ASSERTION(ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript(),
                "Caller forgot to check ReadyToExecuteScripts()");
 
   NS_ENSURE_ARG(aRequest);
   nsAFlatString* script;
   nsAutoString textData;
 
   // If there's no script text, we try to get it from the element
   if (aRequest->mIsInline) {
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -142,41 +142,25 @@ public:
       ProcessPendingRequestsAsync();
     }
     mEnabled = aEnabled;
   }
 
   /**
    * Add/remove blocker. Blockers will stop scripts from executing, but not
    * from loading.
-   * NOTE! Calling RemoveExecuteBlocker could potentially execute pending
-   * scripts synchronously. In other words, it should not be done at 'unsafe'
-   * times
    */
   void AddExecuteBlocker()
   {
-    if (!mBlockerCount++) {
-      mHadPendingScripts = mPendingRequests.Count() != 0;
-    }
+    ++mBlockerCount;
   }
   void RemoveExecuteBlocker()
   {
     if (!--mBlockerCount) {
-      // If there were pending scripts then the newly added scripts will
-      // execute once whatever event triggers the pending scripts fires.
-      // However, due to synchronous loads and pushed event queues it's
-      // possible that the requests that were there have already been processed
-      // if so we need to process any new requests asynchronously.
-      // Ideally that should be fixed such that it can't happen.
-      if (mHadPendingScripts) {
-        ProcessPendingRequestsAsync();
-      }
-      else {
-        ProcessPendingRequests();
-      }
+      ProcessPendingRequestsAsync();
     }
   }
 
   /**
    * Convert the given buffer to a UTF-16 string.
    * @param aChannel     Channel corresponding to the data. May be null.
    * @param aData        The data to convert
    * @param aLength      Length of the data
@@ -241,12 +225,11 @@ protected:
   nsIDocument* mDocument;                   // [WEAK]
   nsCOMArray<nsIScriptLoaderObserver> mObservers;
   nsCOMArray<nsScriptLoadRequest> mPendingRequests;
   nsCOMPtr<nsIScriptElement> mCurrentScript;
   // XXXbz do we want to cycle-collect these or something?  Not sure.
   nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders;
   PRUint32 mBlockerCount;
   PRPackedBool mEnabled;
-  PRPackedBool mHadPendingScripts;
 };
 
 #endif //__nsScriptLoader_h__
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -967,38 +967,23 @@ void
 nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize)
 {
   if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
     return;
 
   mProcessingAttachedStack = PR_TRUE;
 
   PRUint32 currentIndex = aSkipSize;
+  // Excute constructors. Do this from high index to low
   while (mAttachedStack.Length() > aSkipSize) {
-    // First install all implementations. Do this from low index to high
-    // since that way we'll automatically get any new bindings added in the
-    // process.
-    for (; currentIndex < mAttachedStack.Length(); ++currentIndex) {
-      nsRefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(currentIndex);
-      if (binding) {
-        nsresult rv = binding->EnsureScriptAPI();
-        if (NS_FAILED(rv)) {
-          mAttachedStack[currentIndex] = nsnull;
-        }
-      }
-    }
-
-    // Then excute constructors. Do this from high index to low
-    while (currentIndex > aSkipSize && currentIndex == mAttachedStack.Length()) {
-      --currentIndex;
-      nsRefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(currentIndex);
-      mAttachedStack.RemoveElementAt(currentIndex);
-      if (binding) {
-        binding->ExecuteAttachedHandler();
-      }
+    PRUint32 lastItem = mAttachedStack.Length() - 1;
+    nsRefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem);
+    mAttachedStack.RemoveElementAt(lastItem);
+    if (binding) {
+      binding->ExecuteAttachedHandler();
     }
   }
 
   // If NodeWillBeDestroyed has run we don't want to clobber
   // mProcessingAttachedStack set there.
   if (mDocument) {
     mProcessingAttachedStack = PR_FALSE;
   }
@@ -1532,26 +1517,16 @@ nsBindingManager::BeginOutermostUpdate()
 
 void
 nsBindingManager::EndOutermostUpdate()
 {
   if (!mProcessingAttachedStack) {
     ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
     mAttachedStackSizeOnOutermost = 0;
   }
-  else {
-    PRUint32 i = mAttachedStackSizeOnOutermost;
-    for (; i < mAttachedStack.Length(); ++i) {
-      nsRefPtr<nsXBLBinding> binding = mAttachedStack[i];
-      nsresult rv = binding->EnsureScriptAPI();
-      if (NS_FAILED(rv)) {
-        mAttachedStack[i] = nsnull;
-      }
-    }
-  }
 }
 
 void
 nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
                                        nsIContent* aChild,
                                        PRUint32 aIndexInContainer,
                                        PRBool aAppend)
 {
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -267,18 +267,17 @@ nsXBLJSClass::Destroy()
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Constructors/Destructors
 nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
   : mPrototypeBinding(aBinding),
     mInsertionPointTable(nsnull),
     mIsStyleBinding(PR_TRUE),
-    mMarkedForDeath(PR_FALSE),
-    mInstalledAPI(PR_FALSE)
+    mMarkedForDeath(PR_FALSE)
 {
   NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
   // Grab a ref to the document info so the prototype binding won't die
   NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
 }
 
 
 nsXBLBinding::~nsXBLBinding(void)
@@ -781,32 +780,16 @@ nsXBLBinding::GenerateAnonymousContent()
     }
 
     // Conserve space by wiping the attributes off the clone.
     if (mContent)
       mContent->UnsetAttr(namespaceID, name, PR_FALSE);
   }
 }
 
-nsresult
-nsXBLBinding::EnsureScriptAPI()
-{
-  if (mInstalledAPI) {
-    return NS_OK;
-  }
-  
-  // Set mInstalledAPI right away since we'll recurse into here from
-  // nsElementSH::PostCreate when InstallImplementation is called.
-  mInstalledAPI = PR_TRUE;
-
-  InstallEventHandlers();
-
-  return InstallImplementation();
-}
-
 void
 nsXBLBinding::InstallEventHandlers()
 {
   // Don't install handlers if scripts aren't allowed.
   if (AllowScripts()) {
     // Fetch the handlers prototypes for this binding.
     nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
 
--- a/content/xbl/src/nsXBLBinding.h
+++ b/content/xbl/src/nsXBLBinding.h
@@ -115,17 +115,18 @@ public:
 
   PRBool HasStyleSheets() const;
   PRBool InheritsStyle() const;
   PRBool ImplementsInterface(REFNSIID aIID) const;
   PRBool ShouldBuildChildFrames() const;
 
   void GenerateAnonymousContent();
   void InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement);
-  nsresult EnsureScriptAPI();
+  void InstallEventHandlers();
+  nsresult InstallImplementation();
 
   void ExecuteAttachedHandler();
   void ExecuteDetachedHandler();
   void UnhookEventHandlers();
 
   nsIAtom* GetBaseTag(PRInt32* aNameSpaceID);
   nsXBLBinding* RootBinding();
   nsXBLBinding* GetFirstStyleBinding();
@@ -162,30 +163,24 @@ public:
 
   PRBool AllowScripts();  // XXX make const
 
   void RemoveInsertionParent(nsIContent* aParent);
   PRBool HasInsertionParent(nsIContent* aParent);
 
 // MEMBER VARIABLES
 protected:
-  // These two functions recursively install the event handlers
-  // and implementation on this binding and its base class bindings.
-  // External callers should call EnsureScriptAPI instead.
-  void InstallEventHandlers();
-  nsresult InstallImplementation();
 
   nsAutoRefCnt mRefCnt;
   nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
   nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
   nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
   
   nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
   
   // A hash from nsIContent* -> (a sorted array of nsXBLInsertionPoint)
   nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>* mInsertionPointTable;
 
   PRPackedBool mIsStyleBinding;
   PRPackedBool mMarkedForDeath;
-  PRPackedBool mInstalledAPI;
 };
 
 #endif // nsXBLBinding_h_
--- a/content/xbl/src/nsXBLService.cpp
+++ b/content/xbl/src/nsXBLService.cpp
@@ -558,16 +558,23 @@ nsXBLService::LoadBindings(nsIContent* a
   }
 
   // Set the binding's bound element.
   newBinding->SetBoundElement(aContent);
 
   // Tell the binding to build the anonymous content.
   newBinding->GenerateAnonymousContent();
 
+  // Tell the binding to install event handlers
+  newBinding->InstallEventHandlers();
+
+  // Set up our properties
+  rv = newBinding->InstallImplementation();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Figure out if we have any scoped sheets.  If so, we do a second resolve.
   *aResolveStyle = newBinding->HasStyleSheets();
   
   newBinding.swap(*aBinding);
 
   return NS_OK; 
 }
 
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1388,16 +1388,18 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpa
 
         mutation.mRelatedNode = attrNode;
         mutation.mAttrName = aName;
 
         if (!oldValue.IsEmpty())
           mutation.mPrevAttrValue = do_GetAtom(oldValue);
         mutation.mAttrChange = nsIDOMMutationEvent::REMOVAL;
 
+        mozAutoDocUpdateContentUnnest updateUnnest(doc);
+
         mozAutoSubtreeModified subtree(GetOwnerDoc(), this);
         nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
                                     nsnull, &mutation);
     }
 
     return NS_OK;
 }
 
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -54,16 +54,18 @@
 #include "nsIScriptError.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "xptcall.h"
 #include "prprf.h"
 #include "nsTArray.h"
 #include "nsCSSValue.h"
+#include "nsIRunnable.h"
+#include "nsThreadUtils.h"
 
 // JavaScript includes
 #include "jsapi.h"
 #include "jsprvtd.h"    // we are using private JS typedefs...
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsnum.h"
 #include "jsscope.h"
@@ -7134,30 +7136,27 @@ nsElementSH::PostCreate(nsIXPConnectWrap
     // return here.
 
     return NS_OK;
   }
 
   // We must ensure that the XBL Binding is installed before we hand
   // back this object.
 
-  nsRefPtr<nsXBLBinding> binding;
   if (content->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
-      (binding = doc->BindingManager()->GetBinding(content))) {
-    // There's already a binding for this element, make sure that
-    // the script API has been installed.
-    // Note that this could end up recusing into code that calls
-    // WrapNative. So don't do anything important beyond this point
-    // as that will not be done to the wrapper returned from that
-    // WrapNative call.
-    // In theory we could also call ExecuteAttachedHandler here if
-    // we also removed the binding from the PAQ queue, but that seems
-    // like a scary change that would mosly just add more inconsistencies.
-
-    return binding->EnsureScriptAPI();
+      doc->BindingManager()->GetBinding(content)) {
+    // There's already a binding for this element so nothing left to
+    // be done here.
+
+    // In theory we could call ExecuteAttachedHandler here when it's safe to
+    // run script if we also removed the binding from the PAQ queue, but that
+    // seems like a scary change that would mosly just add more
+    // inconsistencies.
+
+    return NS_OK;
   }
 
   // See if we have a frame.
   nsIPresShell *shell = doc->GetPrimaryShell();
 
   if (!shell) {
     return NS_OK;
   }
@@ -7171,16 +7170,17 @@ nsElementSH::PostCreate(nsIXPConnectWrap
   }
 
   // Get the computed -moz-binding directly from the style context
   nsPresContext *pctx = shell->GetPresContext();
   NS_ENSURE_TRUE(pctx, NS_ERROR_UNEXPECTED);
 
   // Make sure the style context goes away _before_ we execute the binding
   // constructor, since the constructor can destroy the relevant presshell.
+  nsRefPtr<nsXBLBinding> binding;
   {
     // Scope for the nsRefPtr
     nsRefPtr<nsStyleContext> sc = pctx->StyleSet()->ResolveStyleFor(content,
                                                                     nsnull);
     NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE);
 
     nsCSSValue::URL *bindingURL = sc->GetStyleDisplay()->mBinding;
     if (!bindingURL) {
@@ -7195,27 +7195,23 @@ nsElementSH::PostCreate(nsIXPConnectWrap
     NS_ENSURE_TRUE(xblService, NS_ERROR_NOT_AVAILABLE);
 
     xblService->LoadBindings(content, bindingURL->mURI,
                              bindingURL->mOriginPrincipal, PR_FALSE,
                              getter_AddRefs(binding), &dummy);
   }
   
   if (binding) {
-
-#ifdef DEBUG
-    PRBool safeToRunScript = PR_FALSE;
-    pctx->PresShell()->IsSafeToFlush(safeToRunScript);
-    NS_ASSERTION(safeToRunScript, "Wrapping when it's not safe to flush");
-#endif
-
-    rv = binding->EnsureScriptAPI();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    binding->ExecuteAttachedHandler();
+    if (nsContentUtils::IsSafeToRunScript()) {
+      binding->ExecuteAttachedHandler();
+    }
+    else {
+      nsContentUtils::AddScriptRunner(new nsRunnableMethod<nsXBLBinding>(
+        binding, &nsXBLBinding::ExecuteAttachedHandler));
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsElementSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj, PRBool *_retval)
@@ -8796,27 +8792,34 @@ nsHTMLSelectElementSH::SetProperty(nsIXP
 // This resolve hook makes embed.nsIFoo work as if
 // QueryInterface(Components.interfaces.nsIFoo) was called on the
 // plugin instance, the result of calling QI, assuming it's
 // successful, will be defined on the embed element as a nsIFoo
 // property.
 
 // static
 nsresult
-nsHTMLPluginObjElementSH::GetPluginInstance(nsIXPConnectWrappedNative *wrapper,
-                                            nsIPluginInstance **_result)
+nsHTMLPluginObjElementSH::GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *wrapper,
+                                                  nsIPluginInstance **_result)
 {
   *_result = nsnull;
 
   nsCOMPtr<nsIContent> content(do_QueryWrappedNative(wrapper));
   NS_ENSURE_TRUE(content, NS_ERROR_UNEXPECTED);
 
-  // Make sure that there is a plugin
   nsCOMPtr<nsIObjectLoadingContent> objlc(do_QueryInterface(content));
   NS_ASSERTION(objlc, "Object nodes must implement nsIObjectLoadingContent");
+
+  // If it's not safe to run script we'll only return the instance if it
+  // exists.
+  if (!nsContentUtils::IsSafeToRunScript()) {
+    return objlc->GetPluginInstance(_result);
+  }
+
+  // Make sure that there is a plugin
   return objlc->EnsureInstantiation(_result);
 }
 
 // Check if proto is already in obj's prototype chain.
 
 static PRBool
 IsObjInProtoChain(JSContext *cx, JSObject *obj, JSObject *proto)
 {
@@ -8832,32 +8835,61 @@ IsObjInProtoChain(JSContext *cx, JSObjec
     }
 
     o = p;
   }
 
   return PR_FALSE;
 }
 
-
-// Note that not only XPConnect calls this PostCreate() method when
-// it creates wrappers, nsObjectFrame also calls this method when a
-// plugin is loaded if the embed/object element is already wrapped to
-// get the scriptable plugin inserted into the embed/object's proto
-// chain.
-
-NS_IMETHODIMP
-nsHTMLPluginObjElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper,
-                                     JSContext *cx, JSObject *obj)
-{
-  nsresult rv = nsElementSH::PostCreate(wrapper, cx, obj);
-  NS_ENSURE_SUCCESS(rv, rv);
+class nsPluginProtoChainInstallRunner : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  nsPluginProtoChainInstallRunner(nsIXPConnectWrappedNative* wrapper,
+                                  nsIScriptContext* scriptContext)
+    : mWrapper(wrapper),
+      mContext(scriptContext)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    JSObject* obj = nsnull;
+    mWrapper->GetJSObject(&obj);
+    NS_ASSERTION(obj, "Should never be null");
+    nsHTMLPluginObjElementSH::SetupProtoChain(
+      mWrapper, (JSContext*)mContext->GetNativeContext(), obj);
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIXPConnectWrappedNative> mWrapper;
+  nsCOMPtr<nsIScriptContext> mContext;
+};
+
+NS_IMPL_ISUPPORTS1(nsPluginProtoChainInstallRunner, nsIRunnable)
+
+// static
+nsresult
+nsHTMLPluginObjElementSH::SetupProtoChain(nsIXPConnectWrappedNative *wrapper,
+                                          JSContext *cx,
+                                          JSObject *obj)
+{
+  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
+               "Shouldn't have gotten in here");
+
+  nsCxPusher cxPusher;
+  if (!cxPusher.Push(cx)) {
+    return NS_OK;
+  }
 
   nsCOMPtr<nsIPluginInstance> pi;
-  rv = GetPluginInstance(wrapper, getter_AddRefs(pi));
+  nsresult rv = GetPluginInstanceIfSafe(wrapper, getter_AddRefs(pi));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!pi) {
     // No plugin around for this object.
 
     return NS_OK;
   }
 
@@ -8871,17 +8903,17 @@ nsHTMLPluginObjElementSH::PostCreate(nsI
     // Didn't get a plugin instance JSObject, nothing we can do then.
 
     return NS_OK;
   }
 
   if (IsObjInProtoChain(cx, obj, pi_obj)) {
     // We must have re-entered ::PostCreate() from nsObjectFrame()
     // (through the EnsureInstantiation() call in
-    // GetPluginInstance()), this means that we've already done what
+    // GetPluginInstanceIfSafe()), this means that we've already done what
     // we're about to do in this function so we can just return here.
 
     return NS_OK;
   }
 
 
   // If we got an xpconnect-wrapped plugin object, set obj's
   // prototype's prototype to the scriptable plugin.
@@ -8961,16 +8993,36 @@ nsHTMLPluginObjElementSH::PostCreate(nsI
   //   |          by LiveConnect
   //   |
   //   |__ xpc wrapped native embed node
   //
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsHTMLPluginObjElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper,
+                                     JSContext *cx, JSObject *obj)
+{
+  nsresult rv = nsElementSH::PostCreate(wrapper, cx, obj);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (nsContentUtils::IsSafeToRunScript()) {
+    return SetupProtoChain(wrapper, cx, obj);
+  }
+
+  nsCOMPtr<nsIScriptContext> scriptContext =
+    GetScriptContextFromJSContext(cx);
+  NS_ENSURE_TRUE(scriptContext, NS_ERROR_UNEXPECTED);
+
+  nsContentUtils::AddScriptRunner(
+      new nsPluginProtoChainInstallRunner(wrapper, scriptContext));
+
+  return NS_OK;
+}
 
 NS_IMETHODIMP
 nsHTMLPluginObjElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
                                       JSContext *cx, JSObject *obj, jsval id,
                                       jsval *vp, PRBool *_retval)
 {
   JSAutoRequest ar(cx);
 
@@ -9062,17 +9114,17 @@ nsHTMLPluginObjElementSH::SetProperty(ns
 }
 
 NS_IMETHODIMP
 nsHTMLPluginObjElementSH::Call(nsIXPConnectWrappedNative *wrapper,
                                JSContext *cx, JSObject *obj, PRUint32 argc,
                                jsval *argv, jsval *vp, PRBool *_retval)
 {
   nsCOMPtr<nsIPluginInstance> pi;
-  nsresult rv = GetPluginInstance(wrapper, getter_AddRefs(pi));
+  nsresult rv = GetPluginInstanceIfSafe(wrapper, getter_AddRefs(pi));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!pi) {
     // No plugin around for this object.
 
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -9279,17 +9331,17 @@ nsHTMLPluginObjElementSH::NewResolve(nsI
   }
 
   // This code resolves embed.nsIFoo to the nsIFoo wrapper of the
   // plugin/applet instance. We only want to do that for plugin
   // instances that are not scriptable using NPRuntime or are Java
   // plugin instances.
 
   nsCOMPtr<nsIPluginInstance> pi;
-  nsresult rv = GetPluginInstance(wrapper, getter_AddRefs(pi));
+  nsresult rv = GetPluginInstanceIfSafe(wrapper, getter_AddRefs(pi));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPluginInstanceInternal> plugin_internal =
     do_QueryInterface(pi);
 
 #ifdef OJI
   nsCOMPtr<nsIJVMPluginInstance> java_plugin_instance =
     do_QueryInterface(pi);
--- a/dom/src/base/nsDOMClassInfo.h
+++ b/dom/src/base/nsDOMClassInfo.h
@@ -972,18 +972,18 @@ protected:
     : nsHTMLElementSH(aData)
   {
   }
 
   virtual ~nsHTMLPluginObjElementSH()
   {
   }
 
-  nsresult GetPluginInstance(nsIXPConnectWrappedNative *aWrapper,
-                             nsIPluginInstance **aResult);
+  static nsresult GetPluginInstanceIfSafe(nsIXPConnectWrappedNative *aWrapper,
+                                          nsIPluginInstance **aResult);
 
   static nsresult GetPluginJSObject(JSContext *cx, JSObject *obj,
                                     nsIPluginInstance *plugin_inst,
                                     JSObject **plugin_obj,
                                     JSObject **plugin_proto);
 
   static nsresult GetJavaPluginJSObject(JSContext *cx, JSObject *obj,
                                         nsIPluginInstance *plugin_inst,
@@ -999,16 +999,20 @@ public:
   NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
   NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
   NS_IMETHOD Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                   JSObject *obj, PRUint32 argc, jsval *argv, jsval *vp,
                   PRBool *_retval);
 
+
+  static nsresult SetupProtoChain(nsIXPConnectWrappedNative *wrapper,
+                                  JSContext *cx, JSObject *obj);
+
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsHTMLPluginObjElementSH(aData);
   }
 };
 
 
 // HTMLOptionsCollection helper
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1043,16 +1043,18 @@ DocumentViewerImpl::PermitUnload(PRBool 
   nsPIDOMWindow *window = mDocument->GetWindow();
 
   if (!window) {
     // This is odd, but not fatal
     NS_WARNING("window not set for document!");
     return NS_OK;
   }
 
+  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
+
   // Now, fire an BeforeUnload event to the document and see if it's ok
   // to unload...
   nsEventStatus status = nsEventStatus_eIgnore;
   nsBeforePageUnloadEvent event(PR_TRUE, NS_BEFORE_PAGE_UNLOAD);
   event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
   // XXX Dispatching to |window|, but using |document| as the target.
   event.target = mDocument;
   nsresult rv = NS_OK;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -755,16 +755,17 @@ FrameArena::FreeFrame(size_t aSize, void
 struct nsCallbackEventRequest
 {
   nsIReflowCallback* callback;
   nsCallbackEventRequest* next;
 };
 
 // ----------------------------------------------------------------------------
 class nsPresShellEventCB;
+class nsAutoCauseReflowNotifier;
 
 class PresShell : public nsIPresShell, public nsIViewObserver,
                   public nsStubDocumentObserver,
                   public nsISelectionController, public nsIObserver,
                   public nsSupportsWeakReference
 {
 public:
   PresShell();
@@ -1004,18 +1005,24 @@ public:
 protected:
   virtual ~PresShell();
 
   void HandlePostedReflowCallbacks();
   void CancelPostedReflowCallbacks();
 
   void UnsuppressAndInvalidate();
 
-  void     WillCauseReflow() { ++mChangeNestCount; }
+
+  void WillCauseReflow() {
+    nsContentUtils::AddScriptBlocker();
+    ++mChangeNestCount;
+  }
   nsresult DidCauseReflow();
+  friend class nsAutoCauseReflowNotifier;
+
   void     WillDoReflow();
   void     DidDoReflow();
   nsresult ProcessReflowCommands(PRBool aInterruptible);
   void     ClearReflowEventStatus();
   void     PostReflowEvent();
   
   void DoReflow(nsIFrame* aFrame);
 #ifdef DEBUG
@@ -1204,16 +1211,39 @@ private:
   nsCOMPtr<nsITimer> mResizeEventTimer;
 
   typedef void (*nsPluginEnumCallback)(PresShell*, nsIContent*);
   void EnumeratePlugins(nsIDOMDocument *aDocument,
                         const nsString &aPluginTag,
                         nsPluginEnumCallback aCallback);
 };
 
+class nsAutoCauseReflowNotifier
+{
+public:
+  nsAutoCauseReflowNotifier(PresShell* aShell)
+    : mShell(aShell)
+  {
+    mShell->WillCauseReflow();
+  }
+  ~nsAutoCauseReflowNotifier()
+  {
+    // This check should not be needed. Currently the only place that seem
+    // to need it is the code that deals with bug 337586.
+    if (!mShell->mHaveShutDown) {
+      mShell->DidCauseReflow();
+    }
+    else {
+      nsContentUtils::RemoveScriptBlocker();
+    }
+  }
+
+  PresShell* mShell;
+};
+
 class nsPresShellEventCB : public nsDispatchingCallback
 {
 public:
   nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
 
   virtual void HandleEvent(nsEventChainPostVisitor& aVisitor)
   {
     if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) {
@@ -2378,41 +2408,45 @@ PresShell::InitialReflow(nscoord aWidth,
   nsIFrame* rootFrame = FrameManager()->GetRootFrame();
   
   if (root) {
     MOZ_TIMER_DEBUGLOG(("Reset and start: Frame Creation: PresShell::InitialReflow(), this=%p\n",
                         (void*)this));
     MOZ_TIMER_RESET(mFrameCreationWatch);
     MOZ_TIMER_START(mFrameCreationWatch);
 
-    WillCauseReflow();
-    mFrameConstructor->BeginUpdate();
-
-    if (!rootFrame) {
-      // Have style sheet processor construct a frame for the
-      // precursors to the root content object's frame
-      mFrameConstructor->ConstructRootFrame(root, &rootFrame);
-      FrameManager()->SetRootFrame(rootFrame);
-    }
-
-    // Have the style sheet processor construct frame for the root
-    // content object down
-    mFrameConstructor->ContentInserted(nsnull, root, 0, nsnull);
-    VERIFY_STYLE_TREE;
-    MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::InitialReflow(), this=%p\n",
-                        (void*)this));
-    MOZ_TIMER_STOP(mFrameCreationWatch);
-
-    // Something in mFrameConstructor->ContentInserted may have caused
-    // Destroy() to get called, bug 337586.
+    {
+      nsAutoCauseReflowNotifier reflowNotifier(this);
+      mFrameConstructor->BeginUpdate();
+
+      if (!rootFrame) {
+        // Have style sheet processor construct a frame for the
+        // precursors to the root content object's frame
+        mFrameConstructor->ConstructRootFrame(root, &rootFrame);
+        FrameManager()->SetRootFrame(rootFrame);
+      }
+
+      // Have the style sheet processor construct frame for the root
+      // content object down
+      mFrameConstructor->ContentInserted(nsnull, root, 0, nsnull);
+      VERIFY_STYLE_TREE;
+      MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::InitialReflow(), this=%p\n",
+                          (void*)this));
+      MOZ_TIMER_STOP(mFrameCreationWatch);
+
+      // Something in mFrameConstructor->ContentInserted may have caused
+      // Destroy() to get called, bug 337586.
+      NS_ENSURE_STATE(!mHaveShutDown);
+
+      mFrameConstructor->EndUpdate();
+    }
+
+    // DidCauseReflow may have killed us too
     NS_ENSURE_STATE(!mHaveShutDown);
 
-    mFrameConstructor->EndUpdate();
-    DidCauseReflow();
-
     // Run the XBL binding constructors for any new frames we've constructed
     mDocument->BindingManager()->ProcessAttachedQueue();
 
     // Constructors may have killed us too
     NS_ENSURE_STATE(!mHaveShutDown);
 
     // Now flush out pending restyles before we actually reflow, in
     // case XBL constructors changed styles somewhere.
@@ -2520,30 +2554,29 @@ PresShell::ResizeReflow(nscoord aWidth, 
 
     // Make sure style is up to date
     mFrameConstructor->ProcessPendingRestyles();
 
     if (!mIsDestroying) {
       // XXX Do a full invalidate at the beginning so that invalidates along
       // the way don't have region accumulation issues?
 
-      WillCauseReflow();
-      WillDoReflow();
-
       {
+        nsAutoCauseReflowNotifier crNotifier(this);
+        WillDoReflow();
+
         // Kick off a top-down reflow
         AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
         mIsReflowing = PR_TRUE;
 
         mDirtyRoots.RemoveElement(rootFrame);
         DoReflow(rootFrame);
         mIsReflowing = PR_FALSE;
       }
 
-      DidCauseReflow();
       DidDoReflow();
     }
 
     batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
   }
 
   if (aHeight == NS_UNCONSTRAINEDSIZE) {
     mPresContext->SetVisibleArea(
@@ -3081,16 +3114,17 @@ PresShell::RestoreRootScrollPosition()
 {
   // Restore frame state for the root scroll frame
   nsCOMPtr<nsILayoutHistoryState> historyState =
     mDocument->GetLayoutHistoryState();
   // Make sure we don't reenter reflow via the sync paint that happens while
   // we're scrolling to our restored position.  Entering reflow for the
   // scrollable frame will cause it to reenter ScrollToRestoredPosition(), and
   // it'll get all confused.
+  nsAutoScriptBlocker scriptBlocker;
   ++mChangeNestCount;
 
   if (historyState) {
     nsIFrame* scrollFrame = GetRootScrollFrame();
     if (scrollFrame) {
       nsIScrollableFrame* scrollableFrame;
       CallQueryInterface(scrollFrame, &scrollableFrame);
       if (scrollableFrame) {
@@ -3371,16 +3405,18 @@ PresShell::RecreateFramesFor(nsIContent*
 
   NS_ASSERTION(mViewManager, "Should have view manager");
   nsIViewManager::UpdateViewBatch batch(mViewManager);
 
   // Have to make sure that the content notifications are flushed before we
   // start messing with the frame model; otherwise we can get content doubling.
   mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
 
+  nsAutoScriptBlocker scriptBlocker;
+
   nsStyleChangeList changeList;
   changeList.AppendChange(nsnull, aContent, nsChangeHint_ReconstructFrame);
 
   // Mark ourselves as not safe to flush while we're doing frame construction.
   ++mChangeNestCount;
   nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList);
   --mChangeNestCount;
   
@@ -4462,32 +4498,40 @@ PresShell::HandlePostedReflowCallbacks()
 
    if (shouldFlush)
      FlushPendingNotifications(Flush_Layout);
 }
 
 NS_IMETHODIMP 
 PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
 {
-  aIsSafeToFlush = PR_TRUE;
-
-  if (mIsReflowing || mChangeNestCount) {
-    // Not safe if we are reflowing or in the middle of frame construction
-    aIsSafeToFlush = PR_FALSE;
-  } else {
+  // XXX technically we don't need to check anything but
+  // nsContentUtils::IsSafeToRunScript here since that should be false
+  // if any of the other flags are set.
+  
+  // Not safe if we are reflowing or in the middle of frame construction
+  aIsSafeToFlush = nsContentUtils::IsSafeToRunScript() &&
+                   !mIsReflowing &&
+                   !mChangeNestCount;
+
+  if (aIsSafeToFlush) {
     // Not safe if we are painting
     nsIViewManager* viewManager = GetViewManager();
     if (viewManager) {
       PRBool isPainting = PR_FALSE;
       viewManager->IsPainting(isPainting);
       if (isPainting) {
         aIsSafeToFlush = PR_FALSE;
       }
     }
   }
+
+  NS_ASSERTION(aIsSafeToFlush == nsContentUtils::IsSafeToRunScript(),
+               "Someone forgot to block scripts");
+
   return NS_OK;
 }
 
 
 NS_IMETHODIMP 
 PresShell::FlushPendingNotifications(mozFlushType aType)
 {
   return DoFlushPendingNotifications(aType, PR_FALSE);
@@ -4581,17 +4625,18 @@ PresShell::IsReflowLocked(PRBool* aIsRef
 void
 PresShell::CharacterDataChanged(nsIDocument *aDocument,
                                 nsIContent*  aContent,
                                 CharacterDataChangeInfo* aInfo)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
 
-  WillCauseReflow();
+  nsAutoCauseReflowNotifier crNotifier(this);
+
   if (mCaret) {
     // Invalidate the caret's current location before we call into the frame
     // constructor. It is important to do this now, and not wait until the
     // resulting reflow, because this call causes continuation frames of the
     // text frame the caret is in to forget what part of the content they
     // refer to, making it hard for them to return the correct continuation
     // frame to the caret.
     mCaret->InvalidateOutsideCaret();
@@ -4610,33 +4655,31 @@ PresShell::CharacterDataChanged(nsIDocum
           aContent)
       mFrameConstructor->RestyleForAppend(container, index);
     else
       mFrameConstructor->RestyleForInsertOrChange(container, aContent);
   }
 
   mFrameConstructor->CharacterDataChanged(aContent, aInfo->mAppend);
   VERIFY_STYLE_TREE;
-  DidCauseReflow();
 }
 
 void
 PresShell::ContentStatesChanged(nsIDocument* aDocument,
                                 nsIContent* aContent1,
                                 nsIContent* aContent2,
                                 PRInt32 aStateMask)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStatesChanged");
   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
 
   if (mDidInitialReflow) {
-    WillCauseReflow();
+    nsAutoCauseReflowNotifier crNotifier(this);
     mFrameConstructor->ContentStatesChanged(aContent1, aContent2, aStateMask);
     VERIFY_STYLE_TREE;
-    DidCauseReflow();
   }
 }
 
 
 void
 PresShell::AttributeChanged(nsIDocument* aDocument,
                             nsIContent*  aContent,
                             PRInt32      aNameSpaceID,
@@ -4646,79 +4689,76 @@ PresShell::AttributeChanged(nsIDocument*
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
 
   // XXXwaterson it might be more elegant to wait until after the
   // initial reflow to begin observing the document. That would
   // squelch any other inappropriate notifications as well.
   if (mDidInitialReflow) {
-    WillCauseReflow();
+    nsAutoCauseReflowNotifier crNotifier(this);
     mFrameConstructor->AttributeChanged(aContent, aNameSpaceID,
                                         aAttribute, aModType, aStateMask);
     VERIFY_STYLE_TREE;
-    DidCauseReflow();
   }
 }
 
 void
 PresShell::ContentAppended(nsIDocument *aDocument,
                            nsIContent* aContainer,
                            PRInt32     aNewIndexInContainer)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
   NS_PRECONDITION(aContainer, "must have container");
   
   if (!mDidInitialReflow) {
     return;
   }
   
-  WillCauseReflow();
+  nsAutoCauseReflowNotifier crNotifier(this);
   MOZ_TIMER_DEBUGLOG(("Start: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
   MOZ_TIMER_START(mFrameCreationWatch);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
   mFrameConstructor->RestyleForAppend(aContainer, aNewIndexInContainer);
 
   mFrameConstructor->ContentAppended(aContainer, aNewIndexInContainer);
   VERIFY_STYLE_TREE;
 
   MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
   MOZ_TIMER_STOP(mFrameCreationWatch);
-  DidCauseReflow();
 }
 
 void
 PresShell::ContentInserted(nsIDocument* aDocument,
                            nsIContent*  aContainer,
                            nsIContent*  aChild,
                            PRInt32      aIndexInContainer)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
 
   if (!mDidInitialReflow) {
     return;
   }
   
-  WillCauseReflow();
+  nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
   if (aContainer)
     mFrameConstructor->RestyleForInsertOrChange(aContainer, aChild);
 
   mFrameConstructor->ContentInserted(aContainer, aChild,
                                      aIndexInContainer, nsnull);
   VERIFY_STYLE_TREE;
-  DidCauseReflow();
 }
 
 void
 PresShell::ContentRemoved(nsIDocument *aDocument,
                           nsIContent* aContainer,
                           nsIContent* aChild,
                           PRInt32     aIndexInContainer)
 {
@@ -4729,41 +4769,37 @@ PresShell::ContentRemoved(nsIDocument *a
   if (mCaret) {
     mCaret->InvalidateOutsideCaret();
   }
 
   // Notify the ESM that the content has been removed, so that
   // it can clean up any state related to the content.
   mPresContext->EventStateManager()->ContentRemoved(aChild);
 
-  WillCauseReflow();
+  nsAutoCauseReflowNotifier crNotifier(this);
 
   // Call this here so it only happens for real content mutations and
   // not cases when the frame constructor calls its own methods to force
   // frame reconstruction.
   if (aContainer)
     mFrameConstructor->RestyleForRemove(aContainer, aChild, aIndexInContainer);
 
   PRBool didReconstruct;
   mFrameConstructor->ContentRemoved(aContainer, aChild,
                                     aIndexInContainer, &didReconstruct);
 
   VERIFY_STYLE_TREE;
-  DidCauseReflow();
 }
 
 nsresult
 PresShell::ReconstructFrames(void)
 {
-  nsresult rv = NS_OK;
-          
-  WillCauseReflow();
-  rv = mFrameConstructor->ReconstructDocElementHierarchy();
+  nsAutoCauseReflowNotifier crNotifier(this);
+  nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
   VERIFY_STYLE_TREE;
-  DidCauseReflow();
 
   return rv;
 }
 
 void
 nsIPresShell::ReconstructStyleDataInternal()
 {
   mStylesHaveChanged = PR_FALSE;
@@ -5522,17 +5558,21 @@ nsresult PresShell::RetargetEventToParen
 
 NS_IMETHODIMP
 PresShell::HandleEvent(nsIView         *aView,
                        nsGUIEvent*     aEvent,
                        nsEventStatus*  aEventStatus)
 {
   NS_ASSERTION(aView, "null view");
 
-  if (mIsDestroying || mIsReflowing || mChangeNestCount) {
+  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
+               "How did we get here if it's not safe to run scripts?");
+
+  if (mIsDestroying || mIsReflowing || mChangeNestCount ||
+      !nsContentUtils::IsSafeToRunScript()) {
     return NS_OK;
   }
 
 #ifdef ACCESSIBILITY
   if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT) {
     return HandleEventInternal(aEvent, aView, aEventStatus);
   }
 #endif
@@ -6164,16 +6204,18 @@ PresShell::DidCauseReflow()
     // our reflow.  Make sure these get processed at some point.
 
     // XXXbz why is this really needed?  ProcessReflowCommands handles posting
     // reflow events if there are reflow roots remaining, and FrameNeedsReflow
     // posts events as needed as well.  I think we should remove this.
     PostReflowEvent();
   }
 
+  nsContentUtils::RemoveScriptBlocker();
+
   return NS_OK;
 }
 
 void
 PresShell::WillDoReflow()
 {
   // We just reflowed, tell the caret that its frame might have moved.
   // XXXbz that comment makes no sense
@@ -6341,16 +6383,17 @@ PresShell::ProcessReflowCommands(PRBool 
 
     // If reflow is interruptible, then make a note of our deadline.
     const PRIntervalTime deadline = aInterruptible
         ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
         : (PRIntervalTime)0;
 
     // Scope for the reflow entry point
     {
+      nsAutoScriptBlocker scriptBlocker;
       AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
       mIsReflowing = PR_TRUE;
 
       do {
         // Send an incremental reflow notification to the target frame.
         PRInt32 idx = mDirtyRoots.Count() - 1;
         nsIFrame *target = static_cast<nsIFrame*>(mDirtyRoots[idx]);
         mDirtyRoots.RemoveElementAt(idx);
@@ -6370,17 +6413,20 @@ PresShell::ProcessReflowCommands(PRBool 
                (!aInterruptible || PR_IntervalNow() < deadline));
 
       // XXXwaterson for interruptible reflow, examine the tree and
       // re-enqueue any unflowed reflow targets.
 
       mIsReflowing = PR_FALSE;
     }
 
-    DidDoReflow();
+    // Exiting the scriptblocker might have killed us
+    if (!mIsDestroying) {
+      DidDoReflow();
+    }
 
     // DidDoReflow might have killed us
     if (!mIsDestroying) {
 #ifdef DEBUG
       if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
         printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
                (void*)this);
       }
@@ -6507,19 +6553,22 @@ PresShell::Observe(nsISupports* aSubject
 
       // Because "chrome:" URL equality is messy, reframe image box
       // frames (hack!).
       nsStyleChangeList changeList;
       WalkFramesThroughPlaceholders(mPresContext, rootFrame,
                                     ReframeImageBoxes, &changeList);
       // Mark ourselves as not safe to flush while we're doing frame
       // construction.
-      ++mChangeNestCount;
-      mFrameConstructor->ProcessRestyledFrames(changeList);
-      --mChangeNestCount;
+      {
+        nsAutoScriptBlocker scriptBlocker;
+        ++mChangeNestCount;
+        mFrameConstructor->ProcessRestyledFrames(changeList);
+        --mChangeNestCount;
+      }
 
       batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
 #ifdef ACCESSIBILITY
       InvalidateAccessibleSubtree(nsnull);
 #endif
     }
     return NS_OK;
   }
--- a/layout/generic/Makefile.in
+++ b/layout/generic/Makefile.in
@@ -182,16 +182,17 @@ LOCAL_INCLUDES += \
 		-I$(srcdir) \
 		-I$(srcdir)/../base \
 		-I$(srcdir)/../forms \
 		-I$(srcdir)/../tables \
 		-I$(srcdir)/../xul/base/src \
 		-I$(srcdir)/../../content/xul/content/src \
 		-I$(srcdir)/../../content/base/src \
 		-I$(srcdir)/../../content/html/content/src \
+		-I$(srcdir)/../../dom/src/base \
 		$(MOZ_CAIRO_CFLAGS) \
 		$(NULL)
 
 ifdef MOZ_ENABLE_GTK2
 CXXFLAGS += $(MOZ_GTK2_CFLAGS)
 endif
 
 libs::
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -103,16 +103,17 @@
 #include "nsTransform2D.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "nsAttrName.h"
 #include "nsDataHashtable.h"
+#include "nsDOMClassInfo.h"
 
 // headers for plugin scriptability
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 #include "nsIClassInfo.h"
 
@@ -1851,47 +1852,27 @@ nsObjectFrame::NotifyContentObjectWrappe
 
   nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
   nsContentUtils::XPConnect()->
     GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), mContent,
                                    NS_GET_IID(nsISupports),
                                    getter_AddRefs(wrapper));
 
   if (!wrapper) {
-    // Nothing to do here if there's no wrapper for mContent
-    return;
-  }
-
-  nsCOMPtr<nsIClassInfo> ci(do_QueryInterface(mContent));
-  if (!ci)
-    return;
-
-  nsCOMPtr<nsISupports> s;
-  ci->GetHelperForLanguage(nsIProgrammingLanguage::JAVASCRIPT,
-                           getter_AddRefs(s));
-
-  nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(s));
-
-  if (!helper) {
-    // There's nothing we can do if there's no helper
+    // Nothing to do here if there's no wrapper for mContent. The proto
+    // chain will be fixed appropriately when the wrapper is created.
     return;
   }
 
   JSObject *obj = nsnull;
   nsresult rv = wrapper->GetJSObject(&obj);
   if (NS_FAILED(rv))
     return;
 
-  nsCxPusher cxPusher;
-  if (cxPusher.Push(mContent)) {
-    // Abuse the scriptable helper to trigger prototype setup for the
-    // wrapper for mContent so that this plugin becomes part of the DOM
-    // object.
-    helper->PostCreate(wrapper, cx, obj);
-  }
+  nsHTMLPluginObjElementSH::SetupProtoChain(wrapper, cx, obj);
 }
 
 // static
 nsIObjectFrame *
 nsObjectFrame::GetNextObjectFrame(nsPresContext* aPresContext, nsIFrame* aRoot)
 {
   nsIFrame* child = aRoot->GetFirstChild(nsnull);
 
--- a/view/src/Makefile.in
+++ b/view/src/Makefile.in
@@ -52,16 +52,19 @@ LIBXUL_LIBRARY	= 1
 REQUIRES	= xpcom \
 		  string \
 		  gfx \
 		  widget \
 		  dom \
 		  pref \
 		  thebes \
 		  cairo \
+		  content \
+		  js \
+		  layout \
 		  $(NULL)
 
 EXTRA_DSO_LIBS = gkgfx
 
 CPPSRCS		= \
 		nsView.cpp \
 		nsScrollPortView.cpp \
 		nsViewManager.cpp \
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -56,16 +56,17 @@
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsRegion.h"
 #include "nsInt64.h"
 #include "nsScrollPortView.h"
 #include "nsHashtable.h"
 #include "nsCOMArray.h"
 #include "nsThreadUtils.h"
+#include "nsContentUtils.h"
 
 #include "gfxContext.h"
 
 static NS_DEFINE_IID(kBlenderCID, NS_BLENDER_CID);
 static NS_DEFINE_IID(kRegionCID, NS_REGION_CID);
 static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
 
 /**
@@ -447,57 +448,61 @@ void nsViewManager::Refresh(nsView *aVie
   MOZ_TIMER_START(mWatch);
 #endif
 
   NS_ASSERTION(!IsPainting(), "recursive painting not permitted");
   if (IsPainting()) {
     RootViewManager()->mRecursiveRefreshPending = PR_TRUE;
     return;
   }  
-  SetPainting(PR_TRUE);
 
-  nsCOMPtr<nsIRenderingContext> localcx;
-  NS_ASSERTION(aView->GetWidget(),
-               "Must have a widget to calculate coordinates correctly");
-  if (nsnull == aContext)
-    {
-      localcx = CreateRenderingContext(*aView);
+  {
+    nsAutoScriptBlocker scriptBlocker;
+    SetPainting(PR_TRUE);
 
-      //couldn't get rendering context. this is ok at init time atleast
-      if (nsnull == localcx) {
-        SetPainting(PR_FALSE);
-        return;
+    nsCOMPtr<nsIRenderingContext> localcx;
+    NS_ASSERTION(aView->GetWidget(),
+                 "Must have a widget to calculate coordinates correctly");
+    if (nsnull == aContext)
+      {
+        localcx = CreateRenderingContext(*aView);
+
+        //couldn't get rendering context. this is ok at init time atleast
+        if (nsnull == localcx) {
+          SetPainting(PR_FALSE);
+          return;
+        }
+      } else {
+        // plain assignment grabs another reference.
+        localcx = aContext;
       }
-    } else {
-      // plain assignment grabs another reference.
-      localcx = aContext;
-    }
-
-  PRInt32 p2a = mContext->AppUnitsPerDevPixel();
 
-  nsRefPtr<gfxContext> ctx = localcx->ThebesContext();
+    PRInt32 p2a = mContext->AppUnitsPerDevPixel();
 
-  ctx->Save();
+    nsRefPtr<gfxContext> ctx = localcx->ThebesContext();
 
-  nsPoint vtowoffset = aView->ViewToWidgetOffset();
-  ctx->Translate(gfxPoint(gfxFloat(vtowoffset.x) / p2a,
-                          gfxFloat(vtowoffset.y) / p2a));
+    ctx->Save();
+
+    nsPoint vtowoffset = aView->ViewToWidgetOffset();
+    ctx->Translate(gfxPoint(gfxFloat(vtowoffset.x) / p2a,
+                            gfxFloat(vtowoffset.y) / p2a));
 
-  ctx->Translate(gfxPoint(-gfxFloat(viewRect.x) / p2a,
-                          -gfxFloat(viewRect.y) / p2a));
+    ctx->Translate(gfxPoint(-gfxFloat(viewRect.x) / p2a,
+                            -gfxFloat(viewRect.y) / p2a));
+
+    nsRegion opaqueRegion;
+    AddCoveringWidgetsToOpaqueRegion(opaqueRegion, mContext, aView);
+    damageRegion.Sub(damageRegion, opaqueRegion);
 
-  nsRegion opaqueRegion;
-  AddCoveringWidgetsToOpaqueRegion(opaqueRegion, mContext, aView);
-  damageRegion.Sub(damageRegion, opaqueRegion);
+    RenderViews(aView, *localcx, damageRegion);
 
-  RenderViews(aView, *localcx, damageRegion);
+    ctx->Restore();
 
-  ctx->Restore();
-
-  SetPainting(PR_FALSE);
+    SetPainting(PR_FALSE);
+  }
 
   if (RootViewManager()->mRecursiveRefreshPending) {
     // Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE
     // we'll reenter this code from the UpdateAllViews call.
     RootViewManager()->mRecursiveRefreshPending = PR_FALSE;
     UpdateAllViews(aUpdateFlags);
   }