Bug 673569 - Let each frame script have its own anonymous scope (r=smaug,Waldo,mrbkap,bsmedberg)
☠☠ backed out by 771052f15a6c ☠ ☠
authorBill McCloskey <wmccloskey@mozilla.com>
Sat, 23 Nov 2013 21:32:45 -0800
changeset 167820 4d07001e9afc078a8d6b337ac746dce99b3de2a7
parent 167819 e7aa50b699940b168766e40359b5deb8443d2e9f
child 167821 96800b422285f2b20167c56b073b07fda54ab4ba
push id4703
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 20:24:19 +0000
treeherdermozilla-aurora@20af7fbd96c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, Waldo, mrbkap, bsmedberg
bugs673569
milestone28.0a1
Bug 673569 - Let each frame script have its own anonymous scope (r=smaug,Waldo,mrbkap,bsmedberg)
browser/modules/BrowserNewTabPreloader.jsm
content/base/public/nsIMessageManager.idl
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
content/base/src/nsInProcessTabChildGlobal.cpp
content/base/src/nsInProcessTabChildGlobal.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
--- a/browser/modules/BrowserNewTabPreloader.jsm
+++ b/browser/modules/BrowserNewTabPreloader.jsm
@@ -327,17 +327,17 @@ HiddenBrowser.prototype = {
     }
 
     // Swap docShells.
     tabbrowser.swapNewTabWithBrowser(aTab, this._browser);
 
     // Load all default frame scripts attached to the target window.
     let mm = aTab.linkedBrowser.messageManager;
     let scripts = win.messageManager.getDelayedFrameScripts();
-    Array.forEach(scripts, script => mm.loadFrameScript(script, true));
+    Array.forEach(scripts, ([script, runGlobal]) => mm.loadFrameScript(script, true, runGlobal));
 
     // Remove the browser, it will be recreated by a timer.
     this._removeBrowser();
 
     // Start a timer that will kick off preloading the next newtab page.
     this._timer = createTimer(this, PRELOADER_INTERVAL_MS);
 
     // Signal that we swapped docShells.
--- a/content/base/public/nsIMessageManager.idl
+++ b/content/base/public/nsIMessageManager.idl
@@ -354,38 +354,41 @@ interface nsIContentFrameMessageManager 
 };
 
 [uuid(a2325927-9c0c-437d-9215-749c79235031)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
 };
 
-[scriptable, builtinclass, uuid(ecebfb8c-ff51-11e2-9d65-7af553959281)]
+[scriptable, builtinclass, uuid(6fb78110-45ae-11e3-8f96-0800200c9a66)]
 interface nsIFrameScriptLoader : nsISupports
 {
   /**
    * Load a script in the (remote) frame. aURL must be the absolute URL.
    * data: URLs are also supported. For example data:,dump("foo\n");
    * If aAllowDelayedLoad is true, script will be loaded when the
    * remote frame becomes available. Otherwise the script will be loaded
    * only if the frame is already available.
    */
-  void loadFrameScript(in AString aURL, in boolean aAllowDelayedLoad);
+  void loadFrameScript(in AString aURL, in boolean aAllowDelayedLoad,
+                       [optional] in boolean aRunInGlobalScope);
 
   /**
    * Removes aURL from the list of scripts which support delayed load.
    */
   void removeDelayedFrameScript(in AString aURL);
 
   /**
-   * Returns a list of all delayed scripts that will be loaded once
-   * a (remote) frame becomes available.
+   * Returns all delayed scripts that will be loaded once a (remote)
+   * frame becomes available. The return value is a list of pairs
+   * [<URL>, <WasLoadedInGlobalScope>].
    */
-  nsIDOMDOMStringList getDelayedFrameScripts();
+  [implicit_jscontext]
+  jsval getDelayedFrameScripts();
 };
 
 [scriptable, builtinclass, uuid(b37821ff-df79-44d4-821c-6d6ec4dfe1e9)]
 interface nsIProcessChecker : nsISupports
 {
 
   /**
    * Return true iff the "remote" process has |aPermission|.  This is
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -1682,17 +1682,18 @@ nsFrameLoader::MaybeCreateDocShell()
     if (os) {
       os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
                           "in-process-browser-or-app-frame-shown", nullptr);
     }
 
     if (mMessageManager) {
       mMessageManager->LoadFrameScript(
         NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
-        /* allowDelayedLoad = */ true);
+        /* allowDelayedLoad = */ true,
+        /* aRunInGlobalScope */ true);
     }
   }
 
   return NS_OK;
 }
 
 void
 nsFrameLoader::GetURL(nsString& aURI)
@@ -2183,26 +2184,26 @@ nsFrameLoader::CreateStaticClone(nsIFram
   nsCOMPtr<nsIDocument> clonedDoc = doc->CreateStaticClone(dest->mDocShell);
   nsCOMPtr<nsIDOMDocument> clonedDOMDoc = do_QueryInterface(clonedDoc);
 
   viewer->SetDOMDocument(clonedDOMDoc);
   return NS_OK;
 }
 
 bool
-nsFrameLoader::DoLoadFrameScript(const nsAString& aURL)
+nsFrameLoader::DoLoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope)
 {
   mozilla::dom::PBrowserParent* tabParent = GetRemoteBrowser();
   if (tabParent) {
-    return tabParent->SendLoadRemoteScript(nsString(aURL));
+    return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope);
   }
   nsRefPtr<nsInProcessTabChildGlobal> tabChild =
     static_cast<nsInProcessTabChildGlobal*>(GetTabChildGlobalAsEventTarget());
   if (tabChild) {
-    tabChild->LoadFrameScript(aURL);
+    tabChild->LoadFrameScript(aURL, aRunInGlobalScope);
   }
   return true;
 }
 
 class nsAsyncMessageToChild : public nsRunnable
 {
 public:
   nsAsyncMessageToChild(JSContext* aCx,
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -180,17 +180,18 @@ public:
   void Finalize();
   nsIDocShell* GetExistingDocShell() { return mDocShell; }
   mozilla::dom::EventTarget* GetTabChildGlobalAsEventTarget();
   nsresult CreateStaticClone(nsIFrameLoader* aDest);
 
   /**
    * MessageManagerCallback methods that we override.
    */
-  virtual bool DoLoadFrameScript(const nsAString& aURL) MOZ_OVERRIDE;
+  virtual bool DoLoadFrameScript(const nsAString& aURL,
+                                 bool aRunInGlobalScope) MOZ_OVERRIDE;
   virtual bool DoSendAsyncMessage(JSContext* aCx,
                                   const nsAString& aMessage,
                                   const mozilla::dom::StructuredCloneData& aData,
                                   JS::Handle<JSObject *> aCpows,
                                   nsIPrincipal* aPrincipal) MOZ_OVERRIDE;
   virtual bool CheckPermission(const nsAString& aPermission) MOZ_OVERRIDE;
   virtual bool CheckManifestURL(const nsAString& aManifestURL) MOZ_OVERRIDE;
   virtual bool CheckAppHasPermission(const nsAString& aPermission) MOZ_OVERRIDE;
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -389,73 +389,98 @@ nsFrameMessageManager::RemoveWeakMessage
 
   return NS_OK;
 }
 
 // nsIFrameScriptLoader
 
 NS_IMETHODIMP
 nsFrameMessageManager::LoadFrameScript(const nsAString& aURL,
-                                       bool aAllowDelayedLoad)
+                                       bool aAllowDelayedLoad,
+                                       bool aRunInGlobalScope)
 {
   if (aAllowDelayedLoad) {
     if (IsGlobal() || IsWindowLevel()) {
       // Cache for future windows or frames
       mPendingScripts.AppendElement(aURL);
+      mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
     } else if (!mCallback) {
       // We're frame message manager, which isn't connected yet.
       mPendingScripts.AppendElement(aURL);
+      mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
       return NS_OK;
     }
   }
 
   if (mCallback) {
 #ifdef DEBUG_smaug
     printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
 #endif
-    NS_ENSURE_TRUE(mCallback->DoLoadFrameScript(aURL), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(mCallback->DoLoadFrameScript(aURL, aRunInGlobalScope),
+                   NS_ERROR_FAILURE);
   }
 
   for (int32_t i = 0; i < mChildManagers.Count(); ++i) {
     nsRefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(mChildManagers[i]);
     if (mm) {
       // Use false here, so that child managers don't cache the script, which
       // is already cached in the parent.
-      mm->LoadFrameScript(aURL, false);
+      mm->LoadFrameScript(aURL, false, aRunInGlobalScope);
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveDelayedFrameScript(const nsAString& aURL)
 {
-  mPendingScripts.RemoveElement(aURL);
+  for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
+    if (mPendingScripts[i] == aURL) {
+      mPendingScripts.RemoveElementAt(i);
+      mPendingScriptsGlobalStates.RemoveElementAt(i);
+      break;
+    }
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsFrameMessageManager::GetDelayedFrameScripts(nsIDOMDOMStringList** aList)
+nsFrameMessageManager::GetDelayedFrameScripts(JSContext* aCx, JS::Value* aList)
 {
   // Frame message managers may return an incomplete list because scripts
   // that were loaded after it was connected are not added to the list.
   if (!IsGlobal() && !IsWindowLevel()) {
     NS_WARNING("Cannot retrieve list of pending frame scripts for frame"
                "message managers as it may be incomplete");
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
-  nsRefPtr<nsDOMStringList> scripts = new nsDOMStringList();
+  JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, mPendingScripts.Length(), nullptr));
+  NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
+
+  JS::Rooted<JSString*> url(aCx);
+  JS::Rooted<JSObject*> pair(aCx);
+  JS::Rooted<JS::Value> pairVal(aCx);
+  for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
+    url = JS_NewUCStringCopyN(aCx, mPendingScripts[i].get(), mPendingScripts[i].Length());
+    NS_ENSURE_TRUE(url, NS_ERROR_OUT_OF_MEMORY);
 
-  for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
-    scripts->Add(mPendingScripts[i]);
+    JS::Value pairElts[] = { JS::StringValue(url),
+                             JS::BooleanValue(mPendingScriptsGlobalStates[i]) };
+
+    pair = JS_NewArrayObject(aCx, 2, pairElts);
+    NS_ENSURE_TRUE(pair, NS_ERROR_OUT_OF_MEMORY);
+
+    pairVal = JS::ObjectValue(*pair);
+    NS_ENSURE_TRUE(JS_SetElement(aCx, array, i, &pairVal),
+                   NS_ERROR_OUT_OF_MEMORY);
   }
 
-  scripts.forget(aList);
+  *aList = JS::ObjectValue(*array);
 
   return NS_OK;
 }
 
 static bool
 JSONCreator(const jschar* aBuf, uint32_t aLen, void* aData)
 {
   nsAString* result = static_cast<nsAString*>(aData);
@@ -1044,21 +1069,23 @@ nsFrameMessageManager::AddChildManager(n
     nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
     nsRefPtr<nsFrameMessageManager> kungfuDeathGrip2 = aManager;
     // We have parent manager if we're a window message manager.
     // In that case we want to load the pending scripts from global
     // message manager.
     if (mParentManager) {
       nsRefPtr<nsFrameMessageManager> globalMM = mParentManager;
       for (uint32_t i = 0; i < globalMM->mPendingScripts.Length(); ++i) {
-        aManager->LoadFrameScript(globalMM->mPendingScripts[i], false);
+        aManager->LoadFrameScript(globalMM->mPendingScripts[i], false,
+                                  globalMM->mPendingScriptsGlobalStates[i]);
       }
     }
     for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
-      aManager->LoadFrameScript(mPendingScripts[i], false);
+      aManager->LoadFrameScript(mPendingScripts[i], false,
+                                mPendingScriptsGlobalStates[i]);
     }
   }
 }
 
 void
 nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback, bool aLoadScripts)
 {
   NS_ASSERTION(!mIsBroadcaster || !mCallback,
@@ -1069,17 +1096,17 @@ nsFrameMessageManager::SetCallback(Messa
       mOwnedCallback = aCallback;
     }
     // First load global scripts by adding this to parent manager.
     if (mParentManager) {
       mParentManager->AddChildManager(this, aLoadScripts);
     }
     if (aLoadScripts) {
       for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
-        LoadFrameScript(mPendingScripts[i], false);
+        LoadFrameScript(mPendingScripts[i], false, mPendingScriptsGlobalStates[i]);
       }
     }
   }
 }
 
 void
 nsFrameMessageManager::RemoveFromParent()
 {
@@ -1299,41 +1326,46 @@ NS_NewGlobalMessageManager(nsIMessageBro
                  NS_ERROR_NOT_AVAILABLE);
   nsFrameMessageManager* mm = new nsFrameMessageManager(nullptr,
                                                         nullptr,
                                                         MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
   RegisterStrongMemoryReporter(new MessageManagerReporter());
   return CallQueryInterface(mm, aResult);
 }
 
-nsDataHashtable<nsStringHashKey, nsFrameJSScriptExecutorHolder*>*
+nsDataHashtable<nsStringHashKey, nsFrameScriptObjectExecutorHolder*>*
   nsFrameScriptExecutor::sCachedScripts = nullptr;
 nsScriptCacheCleaner* nsFrameScriptExecutor::sScriptCacheCleaner = nullptr;
 
 void
 nsFrameScriptExecutor::DidCreateGlobal()
 {
   NS_ASSERTION(mGlobal, "Should have mGlobal!");
   if (!sCachedScripts) {
     sCachedScripts =
-      new nsDataHashtable<nsStringHashKey, nsFrameJSScriptExecutorHolder*>;
+      new nsDataHashtable<nsStringHashKey, nsFrameScriptObjectExecutorHolder*>;
 
     nsRefPtr<nsScriptCacheCleaner> scriptCacheCleaner =
       new nsScriptCacheCleaner();
     scriptCacheCleaner.forget(&sScriptCacheCleaner);
   }
 }
 
 static PLDHashOperator
 CachedScriptUnrooter(const nsAString& aKey,
-                       nsFrameJSScriptExecutorHolder*& aData,
-                       void* aUserArg)
+                     nsFrameScriptObjectExecutorHolder*& aData,
+                     void* aUserArg)
 {
   JSContext* cx = static_cast<JSContext*>(aUserArg);
-  JS_RemoveScriptRoot(cx, &(aData->mScript));
+  if (aData->mScript) {
+    JS_RemoveScriptRoot(cx, &aData->mScript);
+  }
+  if (aData->mFunction) {
+    JS_RemoveObjectRoot(cx, &aData->mFunction);
+  }
   delete aData;
   return PL_DHASH_REMOVE;
 }
 
 // static
 void
 nsFrameScriptExecutor::Shutdown()
 {
@@ -1346,41 +1378,63 @@ nsFrameScriptExecutor::Shutdown()
     sCachedScripts = nullptr;
 
     nsRefPtr<nsScriptCacheCleaner> scriptCacheCleaner;
     scriptCacheCleaner.swap(sScriptCacheCleaner);
   }
 }
 
 void
-nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL)
+nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL,
+                                               bool aRunInGlobalScope)
 {
   if (!mGlobal || !sCachedScripts) {
     return;
   }
 
-  nsFrameJSScriptExecutorHolder* holder = sCachedScripts->Get(aURL);
-  if (!holder) {
-    TryCacheLoadAndCompileScript(aURL, EXECUTE_IF_CANT_CACHE);
-    holder = sCachedScripts->Get(aURL);
+  AutoSafeJSContext cx;
+  JS::Rooted<JSScript*> script(cx);
+  JS::Rooted<JSObject*> funobj(cx);
+
+  nsFrameScriptObjectExecutorHolder* holder = sCachedScripts->Get(aURL);
+  if (holder && holder->WillRunInGlobalScope() == aRunInGlobalScope) {
+    script = holder->mScript;
+    funobj = holder->mFunction;
+  } else {
+    // Don't put anything in the cache if we already have an entry
+    // with a different WillRunInGlobalScope() value.
+    bool shouldCache = !holder;
+    TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope,
+                                 shouldCache, &script, &funobj);
   }
 
-  if (holder) {
-    AutoSafeJSContext cx;
-    JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject());
-    if (global) {
-      JSAutoCompartment ac(cx, global);
-      (void) JS_ExecuteScript(cx, global, holder->mScript, nullptr);
+  JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject());
+  if (global) {
+    JSAutoCompartment ac(cx, global);
+    if (funobj) {
+      JS::Rooted<JSObject*> method(cx, JS_CloneFunctionObject(cx, funobj, global));
+      if (!method) {
+        return;
+      }
+      JS::Rooted<JS::Value> rval(cx);
+      JS::Rooted<JS::Value> methodVal(cx, JS::ObjectValue(*method));
+      (void) JS_CallFunctionValue(cx, global, methodVal,
+                                  0, nullptr, rval.address());
+    } else if (script) {
+      (void) JS_ExecuteScript(cx, global, script, nullptr);
     }
   }
 }
 
 void
 nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL,
-                                                    CacheFailedBehavior aBehavior)
+                                                    bool aRunInGlobalScope,
+                                                    bool aShouldCache,
+                                                    JS::MutableHandle<JSScript*> aScriptp,
+                                                    JS::MutableHandle<JSObject*> aFunp)
 {
   nsCString url = NS_ConvertUTF16toUTF8(aURL);
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
   if (NS_FAILED(rv)) {
     return;
   }
 
@@ -1417,42 +1471,75 @@ nsFrameScriptExecutor::TryCacheLoadAndCo
   }
 
   if (!dataString.IsEmpty()) {
     AutoSafeJSContext cx;
     JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject());
     if (global) {
       JSAutoCompartment ac(cx, global);
       JS::CompileOptions options(cx);
-      options.setNoScriptRval(true)
-             .setFileAndLine(url.get(), 1)
+      options.setFileAndLine(url.get(), 1)
              .setPrincipals(nsJSPrincipals::get(mPrincipal));
-      JS::Rooted<JSScript*> script(cx,
-        JS::Compile(cx, JS::NullPtr(), options, dataString.get(),
-                    dataString.Length()));
+      JS::Rooted<JSScript*> script(cx);
+      JS::Rooted<JSObject*> funobj(cx);
+      if (aRunInGlobalScope) {
+        options.setNoScriptRval(true);
+        script = JS::Compile(cx, JS::NullPtr(), options, dataString.get(),
+                             dataString.Length());
+      } else {
+        JS::Rooted<JSFunction *> fun(cx);
+        fun = JS::CompileFunction(cx, JS::NullPtr(), options,
+                                  nullptr, 0, nullptr, /* name, nargs, args */
+                                  dataString.get(),
+                                  dataString.Length());
+        if (!fun) {
+          return;
+        }
+        funobj = JS_GetFunctionObject(fun);
+      }
 
-      if (script) {
-        nsAutoCString scheme;
-        uri->GetScheme(scheme);
-        // We don't cache data: scripts!
-        if (!scheme.EqualsLiteral("data")) {
-          nsFrameJSScriptExecutorHolder* holder =
-            new nsFrameJSScriptExecutorHolder(script);
-          // Root the object also for caching.
-          JS_AddNamedScriptRoot(cx, &(holder->mScript),
+      if (!script && !funobj) {
+        return;
+      }
+
+      aScriptp.set(script);
+      aFunp.set(funobj);
+
+      nsAutoCString scheme;
+      uri->GetScheme(scheme);
+      // We don't cache data: scripts!
+      if (aShouldCache && !scheme.EqualsLiteral("data")) {
+        nsFrameScriptObjectExecutorHolder* holder;
+
+        // Root the object also for caching.
+        if (script) {
+          holder = new nsFrameScriptObjectExecutorHolder(script);
+          JS_AddNamedScriptRoot(cx, &holder->mScript,
                                 "Cached message manager script");
-          sCachedScripts->Put(aURL, holder);
-        } else if (aBehavior == EXECUTE_IF_CANT_CACHE) {
-          (void) JS_ExecuteScript(cx, global, script, nullptr);
+        } else {
+          holder = new nsFrameScriptObjectExecutorHolder(funobj);
+          JS_AddNamedObjectRoot(cx, &holder->mFunction,
+                                "Cached message manager function");
         }
+        sCachedScripts->Put(aURL, holder);
       }
     }
   }
 }
 
+void
+nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL,
+                                                    bool aRunInGlobalScope)
+{
+  AutoSafeJSContext cx;
+  JS::Rooted<JSScript*> script(cx);
+  JS::Rooted<JSObject*> funobj(cx);
+  TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, true, &script, &funobj);
+}
+
 bool
 nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope,
                                                   const nsACString& aID)
 {
 
   nsCOMPtr<nsIJSRuntimeService> runtimeSvc =
     do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
   NS_ENSURE_TRUE(runtimeSvc, false);
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -48,17 +48,17 @@ enum MessageManagerFlags {
   MM_OWNSCALLBACK = 16
 };
 
 class MessageManagerCallback
 {
 public:
   virtual ~MessageManagerCallback() {}
 
-  virtual bool DoLoadFrameScript(const nsAString& aURL)
+  virtual bool DoLoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope)
   {
     return true;
   }
 
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                      const nsAString& aMessage,
                                      const StructuredCloneData& aData,
                                      JS::Handle<JSObject*> aCpows,
@@ -291,16 +291,17 @@ protected:
   bool mIsBroadcaster; // true if the message manager is a broadcaster
   bool mOwnsCallback;
   bool mHandlingMessage;
   bool mDisconnected;
   mozilla::dom::ipc::MessageManagerCallback* mCallback;
   nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
   nsFrameMessageManager* mParentManager;
   nsTArray<nsString> mPendingScripts;
+  nsTArray<bool> mPendingScriptsGlobalStates;
 public:
   static nsFrameMessageManager* sParentProcessManager;
   static nsFrameMessageManager* sChildProcessManager;
   static nsFrameMessageManager* sSameProcessParentManager;
   static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages;
 private:
   enum ProcessCheckerType {
     PROCESS_CHECKER_PERMISSION,
@@ -309,49 +310,63 @@ private:
   };
   nsresult AssertProcessInternal(ProcessCheckerType aType,
                                  const nsAString& aCapability,
                                  bool* aValid);
 };
 
 class nsScriptCacheCleaner;
 
-struct nsFrameJSScriptExecutorHolder
+struct nsFrameScriptObjectExecutorHolder
 {
-  nsFrameJSScriptExecutorHolder(JSScript* aScript) : mScript(aScript)
-  { MOZ_COUNT_CTOR(nsFrameJSScriptExecutorHolder); }
-  ~nsFrameJSScriptExecutorHolder()
-  { MOZ_COUNT_DTOR(nsFrameJSScriptExecutorHolder); }
+  nsFrameScriptObjectExecutorHolder(JSScript* aScript) : mScript(aScript), mFunction(nullptr)
+  { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); }
+  nsFrameScriptObjectExecutorHolder(JSObject* aFunction) : mScript(nullptr), mFunction(aFunction)
+  { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); }
+  ~nsFrameScriptObjectExecutorHolder()
+  { MOZ_COUNT_DTOR(nsFrameScriptObjectExecutorHolder); }
+
+  bool WillRunInGlobalScope() { return mScript; }
+
+  // We use JS_AddNamed{Script,Object}Root to root these fields explicitly, so
+  // no need for Heap<T>.
   JSScript* mScript;
+  JSObject* mFunction;
 };
 
+class nsFrameScriptObjectExecutorStackHolder;
+
 class nsFrameScriptExecutor
 {
 public:
   static void Shutdown();
   already_AddRefed<nsIXPConnectJSObjectHolder> GetGlobal()
   {
     nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mGlobal;
     return ref.forget();
   }
 protected:
   friend class nsFrameScriptCx;
   nsFrameScriptExecutor()
   { MOZ_COUNT_CTOR(nsFrameScriptExecutor); }
   ~nsFrameScriptExecutor()
   { MOZ_COUNT_DTOR(nsFrameScriptExecutor); }
   void DidCreateGlobal();
-  void LoadFrameScriptInternal(const nsAString& aURL);
-  enum CacheFailedBehavior { EXECUTE_IF_CANT_CACHE, DONT_EXECUTE };
+  void LoadFrameScriptInternal(const nsAString& aURL, bool aRunInGlobalScope);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
-                                    CacheFailedBehavior aBehavior = DONT_EXECUTE);
+                                    bool aRunInGlobalScope,
+                                    bool aShouldCache,
+                                    JS::MutableHandle<JSScript*> aScriptp,
+                                    JS::MutableHandle<JSObject*> aFunp);
+  void TryCacheLoadAndCompileScript(const nsAString& aURL,
+                                    bool aRunInGlobalScope);
   bool InitTabChildGlobalInternal(nsISupports* aScope, const nsACString& aID);
   nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal;
   nsCOMPtr<nsIPrincipal> mPrincipal;
-  static nsDataHashtable<nsStringHashKey, nsFrameJSScriptExecutorHolder*>* sCachedScripts;
+  static nsDataHashtable<nsStringHashKey, nsFrameScriptObjectExecutorHolder*>* sCachedScripts;
   static nsScriptCacheCleaner* sScriptCacheCleaner;
 };
 
 class nsScriptCacheCleaner MOZ_FINAL : public nsIObserver
 {
   NS_DECL_ISUPPORTS
 
   nsScriptCacheCleaner()
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */
 /* vim: set sw=4 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsInProcessTabChildGlobal.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
@@ -330,40 +330,42 @@ nsInProcessTabChildGlobal::InitTabChildG
   nsISupports* scopeSupports = NS_ISUPPORTS_CAST(EventTarget*, this);
   NS_ENSURE_STATE(InitTabChildGlobalInternal(scopeSupports, id));
   return NS_OK;
 }
 
 class nsAsyncScriptLoad : public nsRunnable
 {
 public:
-  nsAsyncScriptLoad(nsInProcessTabChildGlobal* aTabChild, const nsAString& aURL)
-  : mTabChild(aTabChild), mURL(aURL) {}
+    nsAsyncScriptLoad(nsInProcessTabChildGlobal* aTabChild, const nsAString& aURL,
+                      bool aRunInGlobalScope)
+      : mTabChild(aTabChild), mURL(aURL), mRunInGlobalScope(aRunInGlobalScope) {}
 
   NS_IMETHOD Run()
   {
-    mTabChild->LoadFrameScript(mURL);
+    mTabChild->LoadFrameScript(mURL, mRunInGlobalScope);
     return NS_OK;
   }
   nsRefPtr<nsInProcessTabChildGlobal> mTabChild;
   nsString mURL;
+  bool mRunInGlobalScope;
 };
 
 void
-nsInProcessTabChildGlobal::LoadFrameScript(const nsAString& aURL)
+nsInProcessTabChildGlobal::LoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope)
 {
   if (!nsContentUtils::IsSafeToRunScript()) {
-    nsContentUtils::AddScriptRunner(new nsAsyncScriptLoad(this, aURL));
+    nsContentUtils::AddScriptRunner(new nsAsyncScriptLoad(this, aURL, aRunInGlobalScope));
     return;
   }
   if (!mInitialized) {
     mInitialized = true;
     Init();
   }
   bool tmp = mLoadingScript;
   mLoadingScript = true;
-  LoadFrameScriptInternal(aURL);
+  LoadFrameScriptInternal(aURL, aRunInGlobalScope);
   mLoadingScript = tmp;
   if (!mLoadingScript && mDelayedDisconnect) {
     mDelayedDisconnect = false;
     Disconnect();
   }
 }
--- a/content/base/src/nsInProcessTabChildGlobal.h
+++ b/content/base/src/nsInProcessTabChildGlobal.h
@@ -114,17 +114,17 @@ public:
                                                     aUseCapture,
                                                     aWantsUntrusted,
                                                     optional_argc);
   }
   using nsDOMEventTargetHelper::AddEventListener;
 
   virtual JSContext* GetJSContextForEventHandlers() MOZ_OVERRIDE { return nsContentUtils::GetSafeJSContext(); }
   virtual nsIPrincipal* GetPrincipal() MOZ_OVERRIDE { return mPrincipal; }
-  void LoadFrameScript(const nsAString& aURL);
+  void LoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope);
   void Disconnect();
   void SendMessageToParent(const nsString& aMessage, bool aSync,
                            const nsString& aJSON,
                            nsTArray<nsString>* aJSONRetVal);
   nsFrameMessageManager* GetInnerManager()
   {
     return static_cast<nsFrameMessageManager*>(mMessageManager.get());
   }
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -387,17 +387,17 @@ child:
 
     SelectionEvent(WidgetSelectionEvent event);
 
     /**
      * Activate event forwarding from client to parent.
      */
     ActivateFrameEvent(nsString aType, bool capture);
 
-    LoadRemoteScript(nsString aURL);
+    LoadRemoteScript(nsString aURL, bool aRunInGlobalScope);
 
     /**
      * Create a asynchronous request to render whatever document is
      * loaded in the child when this message arrives.  When the
      * request finishes, PDocumentRenderer:__delete__ is sent back to
      * this side to notify completion.
      *
      * |documentRect| is the area of the remote document to draw,
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -217,20 +217,21 @@ TabChild::PreloadSlowThings()
 
     nsRefPtr<TabChild> tab(new TabChild(ContentChild::GetSingleton(),
                                         TabContext(), /* chromeFlags */ 0));
     if (!NS_SUCCEEDED(tab->Init()) ||
         !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) {
         return;
     }
     // Just load and compile these scripts, but don't run them.
-    tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT);
+    tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
     // Load, compile, and run these scripts.
     tab->RecvLoadRemoteScript(
-        NS_LITERAL_STRING("chrome://global/content/preload.js"));
+        NS_LITERAL_STRING("chrome://global/content/preload.js"),
+        true);
 
     nsCOMPtr<nsIDocShell> docShell = do_GetInterface(tab->mWebNav);
     if (nsIPresShell* presShell = docShell->GetPresShell()) {
         // Initialize and do an initial reflow of the about:blank
         // PresShell to let it preload some things for us.
         presShell->Initialize(0, 0);
         nsIDocument* doc = presShell->GetDocument();
         doc->FlushPendingNotifications(Flush_Layout);
@@ -2053,24 +2054,24 @@ bool
 TabChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor)
 {
   OfflineCacheUpdateChild* offlineCacheUpdate = static_cast<OfflineCacheUpdateChild*>(actor);
   delete offlineCacheUpdate;
   return true;
 }
 
 bool
-TabChild::RecvLoadRemoteScript(const nsString& aURL)
+TabChild::RecvLoadRemoteScript(const nsString& aURL, const bool& aRunInGlobalScope)
 {
   if (!mGlobal && !InitTabChildGlobal())
     // This can happen if we're half-destroyed.  It's not a fatal
     // error.
     return true;
 
-  LoadFrameScriptInternal(aURL);
+  LoadFrameScriptInternal(aURL, aRunInGlobalScope);
   return true;
 }
 
 bool
 TabChild::RecvAsyncMessage(const nsString& aMessage,
                            const ClonedMessageData& aData,
                            const InfallibleTArray<CpowEntry>& aCpows,
                            const IPC::Principal& aPrincipal)
@@ -2191,17 +2192,17 @@ TabChild::InitTabChildGlobal(FrameScript
     chromeHandler->AddEventListener(NS_LITERAL_STRING("scroll"), this, false);
   }
 
   if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) {
     mTriedBrowserInit = true;
     // Initialize the child side of the browser element machinery,
     // if appropriate.
     if (IsBrowserOrApp()) {
-      RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT);
+      RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
     }
   }
 
   return true;
 }
 
 bool
 TabChild::InitRenderingState()
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -237,17 +237,17 @@ public:
                               const int32_t&  aKeyCode,
                               const int32_t&  aCharCode,
                               const int32_t&  aModifiers,
                               const bool&     aPreventDefault);
     virtual bool RecvCompositionEvent(const mozilla::WidgetCompositionEvent& event);
     virtual bool RecvTextEvent(const mozilla::WidgetTextEvent& event);
     virtual bool RecvSelectionEvent(const mozilla::WidgetSelectionEvent& event);
     virtual bool RecvActivateFrameEvent(const nsString& aType, const bool& capture);
-    virtual bool RecvLoadRemoteScript(const nsString& aURL);
+    virtual bool RecvLoadRemoteScript(const nsString& aURL, const bool& aRunInGlobalScope);
     virtual bool RecvAsyncMessage(const nsString& aMessage,
                                   const ClonedMessageData& aData,
                                   const InfallibleTArray<CpowEntry>& aCpows,
                                   const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
 
     virtual PDocumentRendererChild*
     AllocPDocumentRendererChild(const nsRect& documentRect, const gfxMatrix& transform,
                                 const nsString& bgcolor,