Bug 673569 - Let each frame script have its own scope (r=smaug)
authorTom Schuster <evilpies@gmail.com>
Fri, 13 Jun 2014 19:56:38 +0200
changeset 215352 6cc6ba5dccac10bcbc686b6116aa3c64d01177b4
parent 215351 666a2522440ade8fe0e74a45f0cc650b5680bb1a
child 215353 37184d8682dbf89d1a4b15e2f64aa5748847a6d7
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs673569
milestone33.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 scope (r=smaug)
accessible/jsat/EventManager.jsm
accessible/jsat/content-script.js
browser/base/content/test/social/social_crash_content_helper.js
browser/components/sessionstore/test/browser_privatetabs.js
browser/components/sessionstore/test/content-forms.js
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsFrameMessageManager.h
content/base/src/nsInProcessTabChildGlobal.cpp
content/base/src/nsInProcessTabChildGlobal.h
content/base/test/chrome/cpows_child.js
dom/browser-element/mochitest/browserElement_Alert.js
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
testing/mochitest/b2g_start_script.js
testing/specialpowers/content/MozillaLogger.js
testing/specialpowers/content/specialpowersAPI.js
toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -24,18 +24,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Events',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'States',
   'resource://gre/modules/accessibility/Constants.jsm');
 
 this.EXPORTED_SYMBOLS = ['EventManager'];
 
-this.EventManager = function EventManager(aContentScope) {
+this.EventManager = function EventManager(aContentScope, aContentControl) {
   this.contentScope = aContentScope;
+  this.contentControl = aContentControl;
   this.addEventListener = this.contentScope.addEventListener.bind(
     this.contentScope);
   this.removeEventListener = this.contentScope.removeEventListener.bind(
     this.contentScope);
   this.sendMsgFunc = this.contentScope.sendAsyncMessage.bind(
     this.contentScope);
   this.webProgress = this.contentScope.docShell.
     QueryInterface(Ci.nsIInterfaceRequestor).
@@ -94,17 +95,17 @@ this.EventManager.prototype = {
     });
 
     try {
       switch (aEvent.type) {
       case 'wheel':
       {
         let attempts = 0;
         let delta = aEvent.deltaX || aEvent.deltaY;
-        this.contentScope.contentControl.autoMove(
+        this.contentControl.autoMove(
          null,
          { moveMethod: delta > 0 ? 'moveNext' : 'movePrevious',
            onScreenOnly: true, noOpIfOnScreen: true, delay: 500 });
         break;
       }
       case 'scroll':
       case 'resize':
       {
@@ -178,17 +179,17 @@ this.EventManager.prototype = {
             Presentation.
               actionInvoked(aEvent.accessible,
                             event.isEnabled ? 'select' : 'unselect'));
         }
         break;
       }
       case Events.SCROLLING_START:
       {
-        this.contentScope.contentControl.autoMove(aEvent.accessible);
+        this.contentControl.autoMove(aEvent.accessible);
         break;
       }
       case Events.TEXT_CARET_MOVED:
       {
         let acc = aEvent.accessible;
         let characterCount = acc.
           QueryInterface(Ci.nsIAccessibleText).characterCount;
         let caretOffset = aEvent.
@@ -249,17 +250,17 @@ this.EventManager.prototype = {
             break;
           }
           this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
         } else {
           let vc = Utils.getVirtualCursor(this.contentScope.content.document);
           if (vc.position &&
             (Utils.getState(vc.position).contains(States.DEFUNCT) ||
               Utils.isInSubtree(vc.position, aEvent.accessible))) {
-            this.contentScope.contentControl.autoMove(
+            this.contentControl.autoMove(
               evt.targetPrevSibling || evt.targetParent,
               { moveToFocused: true, delay: 500 });
           }
         }
         break;
       }
       case Events.TEXT_INSERTED:
       case Events.TEXT_REMOVED:
@@ -274,29 +275,29 @@ this.EventManager.prototype = {
         break;
       }
       case Events.FOCUS:
       {
         // Put vc where the focus is at
         let acc = aEvent.accessible;
         let doc = aEvent.accessibleDocument;
         if (acc.role != Roles.DOCUMENT && doc.role != Roles.CHROME_WINDOW) {
-         this.contentScope.contentControl.autoMove(acc);
+         this.contentControl.autoMove(acc);
        }
        break;
       }
       case Events.DOCUMENT_LOAD_COMPLETE:
       {
-        this.contentScope.contentControl.autoMove(
+        this.contentControl.autoMove(
           aEvent.accessible, { delay: 500 });
         break;
       }
       case Events.VALUE_CHANGE:
       {
-        let position = this.contentScope.contentControl.vc.position;
+        let position = this.contentControl.vc.position;
         let target = aEvent.accessible;
         if (position === target ||
             Utils.getEmbeddedControl(position) === target) {
           this.present(Presentation.valueChanged(target));
         }
       }
     }
   },
--- a/accessible/jsat/content-script.js
+++ b/accessible/jsat/content-script.js
@@ -135,26 +135,26 @@ addMessageListener(
     Logger.debug('AccessFu:Start');
     if (m.json.buildApp)
       Utils.MozBuildApp = m.json.buildApp;
 
     addMessageListener('AccessFu:ContextMenu', activateContextMenu);
     addMessageListener('AccessFu:Scroll', scroll);
     addMessageListener('AccessFu:AdjustRange', adjustRange);
 
-    if (!eventManager) {
-      eventManager = new EventManager(this);
-    }
-    eventManager.start();
-
     if (!contentControl) {
       contentControl = new ContentControl(this);
     }
     contentControl.start();
 
+    if (!eventManager) {
+      eventManager = new EventManager(this, contentControl);
+    }
+    eventManager.start();
+
     sendAsyncMessage('AccessFu:ContentStarted');
   });
 
 addMessageListener(
   'AccessFu:Stop',
   function(m) {
     Logger.debug('AccessFu:Stop');
 
--- a/browser/base/content/test/social/social_crash_content_helper.js
+++ b/browser/base/content/test/social/social_crash_content_helper.js
@@ -8,17 +8,17 @@ let Cu = Components.utils;
 Cu.import("resource://gre/modules/ctypes.jsm");
 let crash = function() { // this will crash when called.
   let zero = new ctypes.intptr_t(8);
   let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
   badptr.contents
 };
 
 
-TestHelper = {
+let TestHelper = {
   init: function() {
     addMessageListener("social-test:crash", this);
   },
 
   receiveMessage: function(msg) {
     switch (msg.name) {
       case "social-test:crash":
         privateNoteIntentionalCrash();
--- a/browser/components/sessionstore/test/browser_privatetabs.js
+++ b/browser/components/sessionstore/test/browser_privatetabs.js
@@ -60,17 +60,17 @@ add_task(function() {
     if (tab2) {
       gBrowser.removeTab(tab2);
     }
   }
 });
 
 add_task(function () {
   const FRAME_SCRIPT = "data:," +
-    "docShell.QueryInterface%28Ci.nsILoadContext%29.usePrivateBrowsing%3Dtrue";
+    "docShell.QueryInterface%28Components.interfaces.nsILoadContext%29.usePrivateBrowsing%3Dtrue";
 
   // Clear the list of closed windows.
   while (ss.getClosedWindowCount()) {
     ss.forgetClosedWindow(0);
   }
 
   // Create a new window to attach our frame script to.
   let win = yield promiseNewWindowLoaded();
--- a/browser/components/sessionstore/test/content-forms.js
+++ b/browser/components/sessionstore/test/content-forms.js
@@ -1,14 +1,16 @@
 /* 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/. */
 
 "use strict";
 
+let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
 /**
  * This frame script is only loaded for sessionstore mochitests. It contains
  * a bunch of utility functions used to test form data collection and
  * restoration in remote browsers.
  */
 
 function queryElement(data) {
   let frame = content;
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -9,16 +9,17 @@
 
 #include "AppProcessChecker.h"
 #include "ContentChild.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsError.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
+#include "jsfriendapi.h"
 #include "nsJSUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsFrameLoader.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
@@ -424,19 +425,16 @@ nsFrameMessageManager::RemoveWeakMessage
 
 // nsIFrameScriptLoader
 
 NS_IMETHODIMP
 nsFrameMessageManager::LoadFrameScript(const nsAString& aURL,
                                        bool aAllowDelayedLoad,
                                        bool aRunInGlobalScope)
 {
-  // FIXME: Bug 673569 is currently disabled.
-  aRunInGlobalScope = true;
-
   if (aAllowDelayedLoad) {
     if (IsGlobal() || IsBroadcaster()) {
       // 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);
@@ -1430,59 +1428,56 @@ nsFrameScriptExecutor::LoadFrameScriptIn
                                                bool aRunInGlobalScope)
 {
   if (!mGlobal || !sCachedScripts) {
     return;
   }
 
   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);
+                                 shouldCache, &script);
   }
 
   JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject());
   if (global) {
     JSAutoCompartment ac(cx, global);
     bool ok = true;
-    if (funobj) {
-      JS::Rooted<JSObject*> method(cx, JS_CloneFunctionObject(cx, funobj, global));
-      if (!method) {
-        return;
+    if (script) {
+      if (aRunInGlobalScope) {
+        ok = JS::CloneAndExecuteScript(cx, global, script);
+      } else {
+        JS::Rooted<JSObject*> scope(cx);
+        ok = js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope);
+        if (ok){
+          // Force the scope to stay alive.
+          mAnonymousGlobalScopes.AppendElement(scope);
+        }
       }
-      JS::Rooted<JS::Value> rval(cx);
-      JS::Rooted<JS::Value> methodVal(cx, JS::ObjectValue(*method));
-      ok = JS_CallFunctionValue(cx, global, methodVal,
-                                JS::HandleValueArray::empty(), &rval);
-    } else if (script) {
-      ok = JS::CloneAndExecuteScript(cx, global, script);
     }
 
     if (!ok) {
       nsJSUtils::ReportPendingException(cx);
     }
   }
 }
 
 void
 nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL,
                                                     bool aRunInGlobalScope,
                                                     bool aShouldCache,
-                                                    JS::MutableHandle<JSScript*> aScriptp,
-                                                    JS::MutableHandle<JSObject*> aFunp)
+                                                    JS::MutableHandle<JSScript*> aScriptp)
 {
   nsCString url = NS_ConvertUTF16toUTF8(aURL);
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
   if (NS_FAILED(rv)) {
     return;
   }
 
@@ -1526,67 +1521,56 @@ nsFrameScriptExecutor::TryCacheLoadAndCo
 
   if (dataStringBuf && dataStringLength > 0) {
     AutoSafeJSContext cx;
     JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject());
     if (global) {
       JSAutoCompartment ac(cx, global);
       JS::CompileOptions options(cx);
       options.setFileAndLine(url.get(), 1);
+      options.setNoScriptRval(true);
       JS::Rooted<JSScript*> script(cx);
-      JS::Rooted<JSObject*> funobj(cx);
+
       if (aRunInGlobalScope) {
-        options.setNoScriptRval(true);
         if (!JS::Compile(cx, JS::NullPtr(), options, srcBuf, &script)) {
           return;
         }
       } else {
-        JS::Rooted<JSFunction *> fun(cx);
-        if (!JS::CompileFunction(cx, JS::NullPtr(), options,
-                                 nullptr, 0, nullptr, /* name, nargs, args */
-                                 srcBuf, &fun))
-        {
+        // We can't clone compile-and-go scripts.
+        options.setCompileAndGo(false);
+        if (!JS::Compile(cx, JS::NullPtr(), options, srcBuf, &script)) {
           return;
         }
-        funobj = JS_GetFunctionObject(fun);
-      }
-
-      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(cx, script);
-        } else {
-          holder = new nsFrameScriptObjectExecutorHolder(cx, funobj);
+          holder = new nsFrameScriptObjectExecutorHolder(cx, script, aRunInGlobalScope);
         }
         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);
+  TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, true, &script);
 }
 
 bool
 nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope,
                                                   const nsACString& aID)
 {
 
   nsCOMPtr<nsIJSRuntimeService> runtimeSvc =
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -355,62 +355,58 @@ private:
   JS::PersistentRooted<JSObject*> mCpows;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 class nsScriptCacheCleaner;
 
 struct nsFrameScriptObjectExecutorHolder
 {
-  nsFrameScriptObjectExecutorHolder(JSContext* aCx, JSScript* aScript)
-   : mScript(aCx, aScript), mFunction(aCx, nullptr)
-  { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); }
-
-  nsFrameScriptObjectExecutorHolder(JSContext* aCx, JSObject* aFunction)
-   : mScript(aCx, nullptr), mFunction(aCx, aFunction)
+  nsFrameScriptObjectExecutorHolder(JSContext* aCx, JSScript* aScript, bool aRunInGlobalScope)
+   : mScript(aCx, aScript), mRunInGlobalScope(aRunInGlobalScope)
   { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); }
 
   ~nsFrameScriptObjectExecutorHolder()
   { MOZ_COUNT_DTOR(nsFrameScriptObjectExecutorHolder); }
 
-  bool WillRunInGlobalScope() { return mScript; }
+  bool WillRunInGlobalScope() { return mRunInGlobalScope; }
 
   JS::PersistentRooted<JSScript*> mScript;
-  JS::PersistentRooted<JSObject*> mFunction;
+  bool mRunInGlobalScope;
 };
 
 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); }
+  nsFrameScriptExecutor() { MOZ_COUNT_CTOR(nsFrameScriptExecutor); }
+  ~nsFrameScriptExecutor() { MOZ_COUNT_DTOR(nsFrameScriptExecutor); }
+
   void DidCreateGlobal();
   void LoadFrameScriptInternal(const nsAString& aURL, bool aRunInGlobalScope);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
                                     bool aRunInGlobalScope,
                                     bool aShouldCache,
-                                    JS::MutableHandle<JSScript*> aScriptp,
-                                    JS::MutableHandle<JSObject*> aFunp);
+                                    JS::MutableHandle<JSScript*> aScriptp);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
                                     bool aRunInGlobalScope);
   bool InitTabChildGlobalInternal(nsISupports* aScope, const nsACString& aID);
   nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal;
   nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsAutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes;
+
   static nsDataHashtable<nsStringHashKey, nsFrameScriptObjectExecutorHolder*>* sCachedScripts;
   static nsScriptCacheCleaner* sScriptCacheCleaner;
 };
 
 class nsScriptCacheCleaner MOZ_FINAL : public nsIObserver
 {
   ~nsScriptCacheCleaner() {}
 
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -98,30 +98,33 @@ nsInProcessTabChildGlobal::DoSendAsyncMe
 }
 
 nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
                                                      nsIContent* aOwner,
                                                      nsFrameMessageManager* aChrome)
 : mDocShell(aShell), mInitialized(false), mLoadingScript(false),
   mOwner(aOwner), mChromeMessageManager(aChrome)
 {
+  mozilla::HoldJSObjects(this);
 
   // If owner corresponds to an <iframe mozbrowser> or <iframe mozapp>, we'll
   // have to tweak our PreHandleEvent implementation.
   nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwner);
   if (browserFrame) {
     mIsBrowserOrAppFrame = browserFrame->GetReallyIsBrowserOrApp();
   }
   else {
     mIsBrowserOrAppFrame = false;
   }
 }
 
 nsInProcessTabChildGlobal::~nsInProcessTabChildGlobal()
 {
+  mAnonymousGlobalScopes.Clear();
+  mozilla::DropJSObjects(this);
 }
 
 /* [notxpcom] boolean markForCC (); */
 // This method isn't automatically forwarded safely because it's notxpcom, so
 // the IDL binding doesn't know what value to return.
 NS_IMETHODIMP_(bool)
 nsInProcessTabChildGlobal::MarkForCC()
 {
@@ -138,20 +141,38 @@ nsInProcessTabChildGlobal::Init()
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                    "Couldn't initialize nsInProcessTabChildGlobal");
   mMessageManager = new nsFrameMessageManager(this,
                                               nullptr,
                                               dom::ipc::MM_CHILD);
   return NS_OK;
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(nsInProcessTabChildGlobal,
-                                   DOMEventTargetHelper,
-                                   mMessageManager,
-                                   mGlobal)
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsInProcessTabChildGlobal)
+
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
+                                                  DOMEventTargetHelper)
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
+                                               DOMEventTargetHelper)
+  for (uint32_t i = 0; i < tmp->mAnonymousGlobalScopes.Length(); ++i) {
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAnonymousGlobalScopes[i])
+  }
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsInProcessTabChildGlobal,
+                                                DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousGlobalScopes)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsInProcessTabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIInProcessContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
--- a/content/base/src/nsInProcessTabChildGlobal.h
+++ b/content/base/src/nsInProcessTabChildGlobal.h
@@ -34,18 +34,19 @@ class nsInProcessTabChildGlobal : public
                                   public nsIScriptObjectPrincipal,
                                   public nsSupportsWeakReference,
                                   public mozilla::dom::ipc::MessageManagerCallback
 {
 public:
   nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
                             nsFrameMessageManager* aChrome);
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsInProcessTabChildGlobal,
-                                           mozilla::DOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsInProcessTabChildGlobal,
+                                                         mozilla::DOMEventTargetHelper)
+
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
   NS_IMETHOD SendSyncMessage(const nsAString& aMessageName,
                              JS::Handle<JS::Value> aObject,
                              JS::Handle<JS::Value> aRemote,
                              nsIPrincipal* aPrincipal,
                              JSContext* aCx,
                              uint8_t aArgc,
--- a/content/base/test/chrome/cpows_child.js
+++ b/content/base/test/chrome/cpows_child.js
@@ -120,16 +120,18 @@ function async_test()
 {
   dump('beginning cpow async test\n');
   async_obj = make_object();
   sendAsyncMessage("cpows:async",
     make_json(),
     async_obj);
 }
 
+var rpc_obj;
+
 function rpc_test()
 {
   dump('beginning cpow rpc test\n');
   rpc_obj = make_object();
   rpc_obj.data.reenter = function  () {
     sendRpcMessage("cpows:reenter", { }, { data: { valid: true } });
     return "ok";
   }
--- a/dom/browser-element/mochitest/browserElement_Alert.js
+++ b/dom/browser-element/mochitest/browserElement_Alert.js
@@ -42,19 +42,19 @@ function runTest() {
 }
 
 function test1() {
   iframe.addEventListener('mozbrowsershowmodalprompt', test2);
 
   // Do window.alert within the iframe, then modify the global |testState|
   // after the alert.
   var script = 'data:,\
-    testState = 0; \
+    this.testState = 0; \
     content.alert("Hello, world!"); \
-    testState = 1; \
+    this.testState = 1; \
   ';
 
   mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
 
   // Triggers a mozbrowsershowmodalprompt event, which sends us down to test2.
 }
 
 // test2 is a mozbrowsershowmodalprompt listener.
@@ -66,39 +66,39 @@ function test2(e) {
 
   SimpleTest.executeSoon(function() { test2a(e); });
 }
 
 function test2a(e) {
   // The iframe should be blocked on the alert call at the moment, so testState
   // should still be 0.
   var script = 'data:,\
-    if (testState === 0) { \
+    if (this.testState === 0) { \
       sendAsyncMessage("test-success", "1: Correct testState"); \
     } \
     else { \
-      sendAsyncMessage("test-fail", "1: Wrong testState: " + testState); \
+      sendAsyncMessage("test-fail", "1: Wrong testState: " + this.testState); \
     }';
 
   mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
   numPendingChildTests++;
 
   waitForPendingTests(function() { test3(e); });
 }
 
 function test3(e) {
   // Now unblock the iframe and check that the script completed.
   e.detail.unblock();
 
   var script2 = 'data:,\
-    if (testState === 1) { \
+    if (this.testState === 1) { \
       sendAsyncMessage("test-success", "2: Correct testState"); \
     } \
     else { \
-      sendAsyncMessage("test-try-again", "2: Wrong testState (for now): " + testState); \
+      sendAsyncMessage("test-try-again", "2: Wrong testState (for now): " + this.testState); \
     }';
 
   // Urgh.  e.unblock() didn't necessarily unblock us immediately, so we have
   // to spin and wait.
   function onTryAgain() {
     SimpleTest.executeSoon(function() {
       //dump('onTryAgain\n');
       mm.loadFrameScript(script2, /* allowDelayedLoad = */ false);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -113,25 +113,51 @@ static bool sActiveDurationMsSet = false
 typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
 static TabChildMap* sTabChildren;
 
 TabChildBase::TabChildBase()
   : mContentDocumentIsDisplayed(false)
   , mTabChildGlobal(nullptr)
   , mInnerSize(0, 0)
 {
+  mozilla::HoldJSObjects(this);
 }
 
+TabChildBase::~TabChildBase()
+{
+  mAnonymousGlobalScopes.Clear();
+  mozilla::DropJSObjects(this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildBase)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TabChildBase)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChildGlobal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousGlobalScopes)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TabChildBase)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChildGlobal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TabChildBase)
+  for (uint32_t i = 0; i < tmp->mAnonymousGlobalScopes.Length(); ++i) {
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAnonymousGlobalScopes[i])
+  }
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION(TabChildBase, mTabChildGlobal, mGlobal)
 
 void
 TabChildBase::InitializeRootMetrics()
 {
   // Calculate a really simple resolution that we probably won't
   // be keeping, as well as putting the scroll offset back to
   // the top-left of the page.
   mLastRootMetrics.mViewport = CSSRect(CSSPoint(), kDefaultViewportSize);
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -160,18 +160,19 @@ protected:
 // It make sense to place in this class all helper functions, and functionality which could be shared between
 // Cross-process/Cross-thread implmentations.
 class TabChildBase : public nsISupports,
                      public nsFrameScriptExecutor,
                      public ipc::MessageManagerCallback
 {
 public:
     TabChildBase();
+
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_CLASS(TabChildBase)
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TabChildBase)
 
     virtual nsIWebNavigation* WebNavigation() = 0;
     virtual nsIWidget* WebWidget() = 0;
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
     bool IsAsyncPanZoomEnabled();
     // Recalculates the display state, including the CSS
     // viewport. This should be called whenever we believe the
     // viewport data on a document may have changed. If it didn't
@@ -183,17 +184,17 @@ public:
                                          const bool& aIsRoot,
                                          const mozilla::layers::ZoomConstraints& aConstraints) = 0;
 
     nsEventStatus DispatchSynthesizedMouseEvent(uint32_t aMsg, uint64_t aTime,
                                                 const LayoutDevicePoint& aRefPoint,
                                                 nsIWidget* aWidget);
 
 protected:
-    ~TabChildBase() {}
+    virtual ~TabChildBase();
     CSSSize GetPageSize(nsCOMPtr<nsIDocument> aDocument, const CSSSize& aViewport);
 
     // Get the DOMWindowUtils for the top-level window in this tab.
     already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils();
     // Get the Document for the top-level window in this tab.
     already_AddRefed<nsIDocument> GetDocument();
 
     // Wrapper for nsIDOMWindowUtils.setCSSViewport(). This updates some state
--- a/testing/mochitest/b2g_start_script.js
+++ b/testing/mochitest/b2g_start_script.js
@@ -54,17 +54,17 @@ function openWindow(aEvent) {
   popupIframe.addEventListener('mozbrowseropenwindow', openWindow);
 
   popupIframe.addEventListener('mozbrowserloadstart', function(e) {
     popupIframe.focus();
     let mm = popupIframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
     mm.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
     mm.loadFrameScript(CHILD_SCRIPT_API, true);
     mm.loadFrameScript(CHILD_SCRIPT, true);
-    mm.loadFrameScript('data:,attachSpecialPowersToWindow%28content%29%3B', true);
+    mm.loadFrameScript('data:,attachSpecialPowersToWindow(content);', true);
   });
 
   container.parentNode.appendChild(popupIframe);
 }
 container.addEventListener('mozbrowseropenwindow', openWindow);
 
 if (outOfProcess) {
   let specialpowers = {};
@@ -82,18 +82,26 @@ if (outOfProcess) {
   mm.addMessageListener("SpecialPowers.Focus", specialPowersObserver);
   mm.addMessageListener("SPPermissionManager", specialPowersObserver);
   mm.addMessageListener("SPLoadChromeScript", specialPowersObserver);
   mm.addMessageListener("SPChromeScriptMessage", specialPowersObserver);
 
   mm.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
   mm.loadFrameScript(CHILD_SCRIPT_API, true);
   mm.loadFrameScript(CHILD_SCRIPT, true);
+
   //Workaround for bug 848411, once that bug is fixed, the following line can be removed
-  mm.loadFrameScript('data:,addEventListener%28%22DOMWindowCreated%22%2C%20function%28e%29%20%7B%0A%20%20removeEventListener%28%22DOMWindowCreated%22%2C%20arguments.callee%2C%20false%29%3B%0A%20%20var%20window%20%3D%20e.target.defaultView%3B%0A%20%20window.wrappedJSObject.SpecialPowers.addPermission%28%22allowXULXBL%22%2C%20true%2C%20window.document%29%3B%0A%7D%0A%29%3B', true);
+  function contentScript() {
+    addEventListener("DOMWindowCreated", function listener(e) {
+      removeEventListener("DOMWindowCreated", listener, false);
+      var window = e.target.defaultView;
+      window.wrappedJSObject.SpecialPowers.addPermission("allowXULXBL", true, window.document);
+    });
+  }
+  mm.loadFrameScript("data:,(" + encodeURI(contentScript.toSource()) + ")();", true);
 
   specialPowersObserver._isFrameScriptLoaded = true;
 }
 
 
 if (onDevice) {
   var cpuLock = Cc["@mozilla.org/power/powermanagerservice;1"]
                       .getService(Ci.nsIPowerManagerService)
--- a/testing/specialpowers/content/MozillaLogger.js
+++ b/testing/specialpowers/content/MozillaLogger.js
@@ -1,12 +1,14 @@
 /**
  * MozillaLogger, a base class logger that just logs to stdout.
  */
 
+"use strict";
+
 function MozillaLogger(aPath) {
 }
 
 MozillaLogger.prototype = {
 
   init : function(path) {},
   
   getLogCallback : function() {
@@ -82,17 +84,17 @@ MozillaFileLogger.prototype = {
     var PR_CREATE_FILE  = 0x08;
     var PR_APPEND       = 0x10;
     this._file = Components.classes["@mozilla.org/file/local;1"].
                             createInstance(Components.interfaces.nsILocalFile);
     this._file.initWithPath(path);
     this._foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
                                      createInstance(Components.interfaces.nsIFileOutputStream);
     this._foStream.init(this._file, PR_WRITE_ONLY | PR_CREATE_FILE | PR_APPEND,
-                                     0664, 0);
+                                     436 /* 0664 */, 0);
   },
 
   getLogCallback : function() {
     return function (msg) {
       var data = msg.num + " " + msg.level + " " + msg.info.join(' ') + "\n";
       if (MozillaFileLogger._foStream)
         this._foStream.write(data, data.length);
 
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -1475,17 +1475,17 @@ SpecialPowersAPI.prototype = {
       removeSystemEventListener(target, type, listener, useCapture);
   },
 
   getDOMRequestService: function() {
     var serv = Services.DOMRequest;
     var res = { __exposedProps__: {} };
     var props = ["createRequest", "createCursor", "fireError", "fireSuccess",
                  "fireDone", "fireDetailedError"];
-    for (i in props) {
+    for (var i in props) {
       let prop = props[i];
       res[prop] = function() { return serv[prop].apply(serv, arguments) };
       res.__exposedProps__[prop] = "r";
     }
     return res;
   },
 
   setLogFile: function(path) {
--- a/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
+++ b/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js
@@ -8,17 +8,17 @@ let Cu = Components.utils;
 Cu.import("resource://gre/modules/ctypes.jsm");
 let crash = function() { // this will crash when called.
   let zero = new ctypes.intptr_t(8);
   let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
   badptr.contents
 };
 
 
-TestHelper = {
+let TestHelper = {
   init: function() {
     addMessageListener("thumbnails-test:crash", this);
   },
 
   receiveMessage: function(msg) {
     switch (msg.name) {
       case "thumbnails-test:crash":
         privateNoteIntentionalCrash();