Bug 673569 - Let each frame script have its own anonymous scope (r=smaug,Waldo,mrbkap,bsmedberg)
authorBill McCloskey <wmccloskey@mozilla.com>
Sat, 23 Nov 2013 21:32:45 -0800
changeset 160932 e42976d8d6568997631d35536d995141172a2c1c
parent 160931 ff4cb698555c74559d87be7f5e01071aaf5423a1
child 160933 6de31ee204e61409fb9459d563e81c2b425b188e
push id25859
push userkwierso@gmail.com
push dateWed, 18 Dec 2013 05:00:23 +0000
treeherderautoland@862cb6a1cc88 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, Waldo, mrbkap, bsmedberg
bugs673569
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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
layout/tools/reftest/reftest.js
--- 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(ad57800b-ff21-4e2f-91d3-e68615ae8afe)]
 interface nsIProcessChecker : nsISupports
 {
 
   /**
    * Return true if 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()
 {
@@ -1297,41 +1324,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()
 {
@@ -1344,41 +1376,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;
   }
 
@@ -1415,42 +1469,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
@@ -396,17 +396,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
@@ -218,20 +218,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);
@@ -2061,24 +2062,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)
@@ -2198,17 +2199,17 @@ TabChild::InitTabChildGlobal(FrameScript
     chromeHandler->AddEventListener(NS_LITERAL_STRING("DOMMetaAdded"), 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
@@ -242,17 +242,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,
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -1868,17 +1868,17 @@ function RegisterMessageListenersAndLoad
         "reftest:ExpectProcessCrash",
         function (m) { RecvExpectProcessCrash(); }
     );
     gBrowserMessageManager.addMessageListener(
         "reftest:EnableAsyncScroll",
         function (m) { SetAsyncScroll(true); }
     );
 
-    gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true);
+    gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true, true);
 }
 
 function SetAsyncScroll(enabled)
 {
     gBrowser.QueryInterface(CI.nsIFrameLoaderOwner).frameLoader.renderMode =
         enabled ? CI.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL :
                   CI.nsIFrameLoader.RENDER_MODE_DEFAULT;
 }