Bug 990158 - Make inner windows use their wrapper cache. r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Sat, 09 Nov 2013 11:20:22 +0100
changeset 177572 f1e4f0f82a9a1572479ee08ed873cc61baeca0da
parent 177571 33f98fa5c415b985750ee7084808a75cca514bd7
child 177573 39b430a304cabc4b4700ea00d95ac8c859ca19aa
push id26556
push userryanvm@gmail.com
push dateTue, 08 Apr 2014 22:16:57 +0000
treeherdermozilla-central@5811efc11011 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs990158
milestone31.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 990158 - Make inner windows use their wrapper cache. r=bz.
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfo.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsIScriptGlobalObject.h
dom/base/nsWrapperCache.h
dom/bindings/BindingDeclarations.h
js/public/RootingAPI.h
js/xpconnect/src/XPCQuickStubs.h
js/xpconnect/src/xpcpublic.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -177,17 +177,16 @@ using namespace mozilla::dom;
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
 //       are defined in nsIDOMClassInfo.h.
 
 #define WINDOW_SCRIPTABLE_FLAGS                                               \
  (nsIXPCScriptable::WANT_PRECREATE |                                          \
   nsIXPCScriptable::WANT_POSTCREATE |                                         \
-  nsIXPCScriptable::WANT_FINALIZE |                                           \
   nsIXPCScriptable::WANT_ENUMERATE |                                          \
   nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE |                               \
   nsIXPCScriptable::IS_GLOBAL_OBJECT |                                        \
   nsIXPCScriptable::WANT_OUTER_OBJECT)
 
 #define ARRAY_SCRIPTABLE_FLAGS                                                \
   (DOM_DEFAULT_SCRIPTABLE_FLAGS       |                                       \
    nsIXPCScriptable::WANT_GETPROPERTY |                                       \
@@ -1850,17 +1849,17 @@ NS_IMETHODIMP
 nsWindowSH::PostCreate(nsIXPConnectWrappedNative *wrapper,
                        JSContext *cx, JSObject *obj)
 {
   JS::Rooted<JSObject*> window(cx, obj);
 
 #ifdef DEBUG
   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
 
-  NS_ASSERTION(sgo && sgo->GetGlobalJSObject() == nullptr,
+  NS_ASSERTION(sgo && sgo->GetGlobalJSObject() == obj,
                "Multiple wrappers created for global object!");
 #endif
 
   const NativeProperties* windowProperties =
     WindowBinding::sNativePropertyHooks->mNativeProperties.regular;
   const NativeProperties* eventTargetProperties =
     EventTargetBinding::sNativePropertyHooks->mNativeProperties.regular;
 
@@ -3395,36 +3394,16 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     return NS_OK;
   }
 
   return nsDOMGenericSH::NewResolve(wrapper, cx, obj, id, flags, objp,
                                     _retval);
 }
 
 NS_IMETHODIMP
-nsWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop,
-                     JSObject *obj)
-{
-  // Since this call is virtual, the exact rooting hazard static analysis is
-  // not able to determine that it happens during finalization and should be
-  // ignored. Moreover, the analysis cannot discover and validate the
-  // potential targets of the virtual call to OnFinalize below because of the
-  // indirection through nsCOMMPtr. Thus, we annotate the analysis here so
-  // that it does not report OnFinalize as GCing with |obj| on stack.
-  JS::AutoAssertNoGC nogc;
-
-  nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
-  NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
-
-  sgo->OnFinalize(obj);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
                         JSObject * obj, JSObject * *_retval)
 {
   nsGlobalWindow *origWin = nsGlobalWindow::FromWrapper(wrapper);
   nsGlobalWindow *win = origWin->GetOuterWindowInternal();
 
   if (!win) {
     // If we no longer have an outer window. No code should ever be
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -265,18 +265,16 @@ public:
   NS_IMETHOD PostCreatePrototype(JSContext * cx, JSObject * proto) MOZ_OVERRIDE;
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj) MOZ_OVERRIDE;
   NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj, bool *_retval) MOZ_OVERRIDE;
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, uint32_t flags,
                         JSObject **objp, bool *_retval) MOZ_OVERRIDE;
-  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop,
-                      JSObject *obj) MOZ_OVERRIDE;
   NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
                          JSObject * obj, JSObject * *_retval) MOZ_OVERRIDE;
 
   static bool GlobalScopePolluterNewResolve(JSContext *cx, JS::Handle<JSObject*> obj,
                                             JS::Handle<jsid> id, unsigned flags,
                                             JS::MutableHandle<JSObject*> objp);
   static bool GlobalScopePolluterGetProperty(JSContext *cx, JS::Handle<JSObject*> obj,
                                              JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1366,17 +1366,16 @@ nsGlobalWindow::ShutDown()
 
 // static
 void
 nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow)
 {
   if (aWindow->mCachedXBLPrototypeHandlers &&
       aWindow->mCachedXBLPrototypeHandlers->Count() > 0) {
     aWindow->mCachedXBLPrototypeHandlers->Clear();
-    mozilla::DropJSObjects(aWindow);
   }
 }
 
 void
 nsGlobalWindow::MaybeForgiveSpamCount()
 {
   if (IsOuterWindow() &&
       IsPopupSpamWindow())
@@ -1612,27 +1611,21 @@ nsGlobalWindow::FreeInnerObjects()
   mGamepads.Clear();
 #endif
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsISupports
 //*****************************************************************************
 
-#define OUTER_WINDOW_ONLY                                                     \
-  if (IsOuterWindow()) {
-
-#define END_OUTER_WINDOW_ONLY                                                 \
-    foundInterface = 0;                                                       \
-  } else
-
 DOMCI_DATA(Window, nsGlobalWindow)
 
 // QueryInterface implementation for nsGlobalWindow
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   // Make sure this matches the cast in nsGlobalWindow::FromWrapper()
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
 #ifdef MOZ_B2G
   NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G)
 #endif // MOZ_B2G
 #ifdef MOZ_WEBSPEECH
   NS_INTERFACE_MAP_ENTRY(nsISpeechSynthesisGetter)
@@ -1655,19 +1648,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
   NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance)
   NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver)
   NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window)
-  OUTER_WINDOW_ONLY
-    NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  END_OUTER_WINDOW_ONLY
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow)
 
 static PLDHashOperator
 MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
@@ -1765,16 +1755,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
   nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
@@ -1825,16 +1816,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 #ifdef DEBUG
 void
 nsGlobalWindow::RiskyUnlink()
 {
   NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
 }
@@ -1854,16 +1846,17 @@ TraceXBLHandlers(nsXBLPrototypeHandler* 
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
   if (tmp->mCachedXBLPrototypeHandlers) {
     TraceData data = { aCallbacks, aClosure };
     tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data);
   }
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 bool
 nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
 {
   if (!nsCCUncollectableMarker::sGeneration) {
     return false;
   }
@@ -1899,22 +1892,22 @@ nsresult
 nsGlobalWindow::EnsureScriptEnvironment()
 {
   nsGlobalWindow* outer = GetOuterWindowInternal();
   if (!outer) {
     NS_WARNING("No outer window available!");
     return NS_ERROR_FAILURE;
   }
 
-  if (outer->mJSObject) {
+  if (outer->GetWrapperPreserveColor()) {
     return NS_OK;
   }
 
   NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(),
-               "mJSObject is null, but we have an inner window?");
+               "No cached wrapper, but we have an inner window?");
 
   // If this window is a [i]frame, don't bother GC'ing when the frame's context
   // is destroyed since a GC will happen when the frameset or host document is
   // destroyed anyway.
   nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer);
 
   NS_ASSERTION(!outer->mContext, "Will overwrite mContext!");
 
@@ -1939,19 +1932,17 @@ JSObject *
 nsGlobalWindow::GetGlobalJSObject()
 {
   return FastGetGlobalJSObject();
 }
 
 void
 nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
 {
-  if (mJSObject) {
-    JS_CallTenuredObjectTracer(aTrc, &mJSObject, "active window global");
-  }
+  TraceWrapper(aTrc, "active window global");
 }
 
 bool
 nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
 {
   // We reuse the inner window when:
   // a. We are currently at our original document.
   // b. At least one of the following conditions are true:
@@ -2124,17 +2115,17 @@ WindowStateHolder::WindowStateHolder(nsG
   NS_PRECONDITION(aWindow, "null window");
   NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
 
   mInnerWindowHolder = aHolder;
 
   aWindow->SuspendTimeouts();
 
   // When a global goes into the bfcache, we disable script.
-  xpc::Scriptability::Get(aWindow->mJSObject).SetDocShellAllowsScript(false);
+  xpc::Scriptability::Get(aWindow->GetWrapperPreserveColor()).SetDocShellAllowsScript(false);
 }
 
 WindowStateHolder::~WindowStateHolder()
 {
   if (mInnerWindow) {
     // This window was left in the bfcache and is now going away. We need to
     // free it up.
     // Note that FreeInnerObjects may already have been called on the
@@ -2162,17 +2153,16 @@ TreatAsRemoteXUL(nsIPrincipal* aPrincipa
  * Return the native global and an nsISupports 'holder' that can be used
  * to manage the lifetime of it.
  */
 static nsresult
 CreateNativeGlobalForInner(JSContext* aCx,
                            nsGlobalWindow* aNewInner,
                            nsIURI* aURI,
                            nsIPrincipal* aPrincipal,
-                           JS::TenuredHeap<JSObject*>& aNativeGlobal,
                            nsIXPConnectJSObjectHolder** aHolder)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aNewInner);
   MOZ_ASSERT(aNewInner->IsInnerWindow());
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aHolder);
 
@@ -2190,30 +2180,25 @@ CreateNativeGlobalForInner(JSContext* aC
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
   uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
   flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
 
-  nsRefPtr<nsIXPConnectJSObjectHolder> jsholder;
   nsresult rv = xpc->InitClassesWithNewWrappedGlobal(
     aCx, ToSupports(aNewInner),
-    aPrincipal, flags, options, getter_AddRefs(jsholder));
+    aPrincipal, flags, options, aHolder);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  MOZ_ASSERT(jsholder);
-  aNativeGlobal = jsholder->GetJSObject();
-  jsholder.forget(aHolder);
-
   // Set the location information for the new global, so that tools like
   // about:memory may use that information
-  MOZ_ASSERT(aNativeGlobal.getPtr());
-  xpc::SetLocationForGlobal(aNativeGlobal, aURI);
+  MOZ_ASSERT(aNewInner->GetWrapperPreserveColor());
+  xpc::SetLocationForGlobal(aNewInner->GetWrapperPreserveColor(), aURI);
 
   return NS_OK;
 }
 
 nsresult
 nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
                                nsISupports* aState,
                                bool aForceReuseInnerWindow)
@@ -2333,66 +2318,66 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
   // Check if we're near the stack limit before we get anywhere near the
   // transplanting code.
   JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE);
 
   nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
   NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
 
+  JS::Rooted<JSObject*> newInnerGlobal(cx);
   if (reUseInnerWindow) {
     // We're reusing the current inner window.
     NS_ASSERTION(!currentInner->IsFrozen(),
                  "We should never be reusing a shared inner window");
     newInnerWindow = currentInner;
+    newInnerGlobal = currentInner->GetWrapperPreserveColor();
 
     if (aDocument != oldDoc) {
-      JS::Rooted<JSObject*> obj(cx, currentInner->mJSObject);
-      JS::ExposeObjectToActiveJS(obj);
+      JS::ExposeObjectToActiveJS(newInnerGlobal);
     }
 
     // We're reusing the inner window, but this still counts as a navigation,
     // so all expandos and such defined on the outer window should go away. Force
     // all Xray wrappers to be recomputed.
-    JS::ExposeObjectToActiveJS(mJSObject);
-    JS::Rooted<JSObject*> rootedObject(cx, mJSObject);
+    JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor());
+    JS::ExposeObjectToActiveJS(rootedObject);
     if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
       return NS_ERROR_FAILURE;
     }
 
     // Inner windows are only reused for same-origin principals, but the principals
     // don't necessarily match exactly. Update the principal on the compartment to
     // match the new document.
     // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here
     // because we haven't yet set its mDoc to aDocument.
-    JSCompartment *compartment = js::GetObjectCompartment(currentInner->mJSObject);
+    JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal);
 #ifdef DEBUG
     bool sameOrigin = false;
     nsIPrincipal *existing =
       nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
     aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
     MOZ_ASSERT(sameOrigin);
 #endif
     JS_SetCompartmentPrincipals(compartment,
                                 nsJSPrincipals::get(aDocument->NodePrincipal()));
   } else {
     if (aState) {
       newInnerWindow = wsh->GetInnerWindow();
+      newInnerGlobal = newInnerWindow->GetWrapperPreserveColor();
       mInnerWindowHolder = wsh->GetInnerWindowHolder();
-
-      NS_ASSERTION(newInnerWindow, "Got a state without inner window");
-    } else if (thisChrome) {
-      newInnerWindow = new nsGlobalChromeWindow(this);
-    } else if (mIsModalContentWindow) {
-      newInnerWindow = new nsGlobalModalWindow(this);
     } else {
-      newInnerWindow = new nsGlobalWindow(this);
-    }
-
-    if (!aState) {
+      if (thisChrome) {
+        newInnerWindow = new nsGlobalChromeWindow(this);
+      } else if (mIsModalContentWindow) {
+        newInnerWindow = new nsGlobalModalWindow(this);
+      } else {
+        newInnerWindow = new nsGlobalWindow(this);
+      }
+
       // Freeze the outer window and null out the inner window so
       // that initializing classes on the new inner doesn't end up
       // reaching into the old inner window for classes etc.
       //
       // [This happens with Object.prototype when XPConnect creates
       // a temporary global while initializing classes; the reason
       // being that xpconnect creates the temp global w/o a parent
       // and proto, which makes the JS engine look up classes in
@@ -2402,29 +2387,30 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
       Freeze();
       mCreatingInnerWindow = true;
       // Every script context we are initialized with must create a
       // new global.
       rv = CreateNativeGlobalForInner(cx, newInnerWindow,
                                       aDocument->GetDocumentURI(),
                                       aDocument->NodePrincipal(),
-                                      newInnerWindow->mJSObject,
                                       getter_AddRefs(mInnerWindowHolder));
-      NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerWindow->mJSObject && mInnerWindowHolder,
-                   "Failed to get script global and holder");
+      newInnerGlobal = mInnerWindowHolder->GetJSObject();
+      NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal &&
+                   newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
+                   "Failed to get script global");
 
       mCreatingInnerWindow = false;
       createdInnerWindow = true;
       Thaw();
 
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    if (currentInner && currentInner->mJSObject) {
+    if (currentInner && currentInner->GetWrapperPreserveColor()) {
       if (oldDoc == aDocument) {
         // Move the navigator from the old inner window to the new one since
         // this is a document.write. This is safe from a same-origin point of
         // view because document.write can only be used by the same origin.
         newInnerWindow->mNavigator = currentInner->mNavigator;
         currentInner->mNavigator = nullptr;
         if (newInnerWindow->mNavigator) {
           newInnerWindow->mNavigator->SetWindow(newInnerWindow);
@@ -2446,122 +2432,114 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       // held in the bfcache.
       if (!currentInner->IsFrozen()) {
         currentInner->FreeInnerObjects();
       }
     }
 
     mInnerWindow = newInnerWindow;
 
-    if (!mJSObject) {
-      JS::Rooted<JSObject*> global(cx, newInnerWindow->FastGetGlobalJSObject());
-      JS::Rooted<JSObject*> outer(cx, NewOuterWindowProxy(cx, global, thisChrome));
+    if (!GetWrapperPreserveColor()) {
+      JS::Rooted<JSObject*> outer(cx,
+        NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
       NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
 
       js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
+      js::SetProxyExtra(outer, 1, JS::ObjectValue(*newInnerGlobal));
 
       // Inform the nsJSContext, which is the canonical holder of the outer.
       mContext->SetWindowProxy(outer);
       mContext->DidInitializeContext();
 
-      mJSObject = mContext->GetWindowProxy();
-      SetWrapper(mJSObject);
+      SetWrapper(mContext->GetWindowProxy());
     } else {
-      JS::ExposeObjectToActiveJS(newInnerWindow->mJSObject);
-      JS::Rooted<JSObject*> global(cx, newInnerWindow->mJSObject);
+      JS::ExposeObjectToActiveJS(newInnerGlobal);
       JS::Rooted<JSObject*> outerObject(cx,
-        NewOuterWindowProxy(cx, global, thisChrome));
+        NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
       if (!outerObject) {
         NS_ERROR("out of memory");
         return NS_ERROR_FAILURE;
       }
 
-      js::SetProxyExtra(mJSObject, 0, js::PrivateValue(nullptr));
-
-      JS::Rooted<JSObject*> obj(cx, mJSObject);
+      JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
+
+      js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
+
       outerObject = xpc::TransplantObject(cx, obj, outerObject);
       if (!outerObject) {
         NS_ERROR("unable to transplant wrappers, probably OOM");
         return NS_ERROR_FAILURE;
       }
 
       js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
 
-      mJSObject = outerObject;
-      SetWrapper(mJSObject);
+      SetWrapper(outerObject);
 
       {
-        JSAutoCompartment ac(cx, mJSObject);
-
-        JS::Rooted<JSObject*> obj(cx, mJSObject);
-        JS::Rooted<JSObject*> newParent(cx, newInnerWindow->mJSObject);
-        JS_SetParent(cx, obj, newParent);
+        JSAutoCompartment ac(cx, outerObject);
+
+        JS_SetParent(cx, outerObject, newInnerGlobal);
 
         // Inform the nsJSContext, which is the canonical holder of the outer.
-        mContext->SetWindowProxy(obj);
+        mContext->SetWindowProxy(outerObject);
 
         NS_ASSERTION(!JS_IsExceptionPending(cx),
                      "We might overwrite a pending exception!");
-        XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
+        XPCWrappedNativeScope* scope = xpc::GetObjectScope(outerObject);
         if (scope->mWaiverWrapperMap) {
-          scope->mWaiverWrapperMap->Reparent(cx, newParent);
+          scope->mWaiverWrapperMap->Reparent(cx, newInnerGlobal);
         }
       }
     }
 
     // Enter the new global's compartment.
-    JSAutoCompartment ac(cx, mJSObject);
+    JSAutoCompartment ac(cx, GetWrapperPreserveColor());
 
     // Set scriptability based on the state of the docshell.
     bool allow = GetDocShell()->GetCanExecuteScripts();
-    xpc::Scriptability::Get(mJSObject).SetDocShellAllowsScript(allow);
+    xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);
 
     // If we created a new inner window above, we need to do the last little bit
     // of initialization now that the dust has settled.
     if (createdInnerWindow) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
-      nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerWindow->mJSObject,
+      nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerGlobal,
                                                     getter_AddRefs(wrapper));
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
       rv = wrapper->FinishInitForWrappedGlobal();
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (!aState) {
-      if (!JS_DefineProperty(cx, newInnerWindow->mJSObject, "window",
-                             OBJECT_TO_JSVAL(mJSObject),
+      if (!JS_DefineProperty(cx, newInnerGlobal, "window",
+                             OBJECT_TO_JSVAL(GetWrapperPreserveColor()),
                              JS_PropertyStub, JS_StrictPropertyStub,
                              JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) {
         NS_ERROR("can't create the 'window' property");
         return NS_ERROR_FAILURE;
       }
     }
   }
 
-  JSAutoCompartment ac(cx, mJSObject);
+  JSAutoCompartment ac(cx, GetWrapperPreserveColor());
 
   if (!aState && !reUseInnerWindow) {
     // Loading a new page and creating a new inner window, *not*
     // restoring from session history.
 
     // Now that both the the inner and outer windows are initialized
     // let the script context do its magic to hook them together.
+    MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
 #ifdef DEBUG
-    JS::Rooted<JSObject*> newInnerJSObject(cx,
-        newInnerWindow->FastGetGlobalJSObject());
-#endif
-
-    MOZ_ASSERT(mContext->GetWindowProxy() == mJSObject);
-#ifdef DEBUG
-    JS::Rooted<JSObject*> rootedJSObject(cx, mJSObject);
+    JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
     JS::Rooted<JSObject*> proto1(cx), proto2(cx);
     JS_GetPrototype(cx, rootedJSObject, &proto1);
-    JS_GetPrototype(cx, newInnerJSObject, &proto2);
+    JS_GetPrototype(cx, newInnerGlobal, &proto2);
     NS_ASSERTION(proto1 == proto2,
                  "outer and inner globals should have the same prototype");
 #endif
 
     nsCOMPtr<Element> frame = GetFrameElementInternal();
     if (frame) {
       nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow();
       if (parentWindow && parentWindow->TimeoutSuspendCount()) {
@@ -2580,34 +2558,34 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       if (newInnerWindow->mDoc != aDocument) {
         newInnerWindow->mDoc = aDocument;
 
         // We're reusing the inner window for a new document. In this
         // case we don't clear the inner window's scope, but we must
         // make sure the cached document property gets updated.
 
         // XXXmarkh - tell other languages about this?
-        JS::Rooted<JSObject*> obj(cx, currentInner->mJSObject);
+        JS::Rooted<JSObject*> obj(cx, currentInner->GetWrapperPreserveColor());
         ::JS_DeleteProperty(cx, obj, "document");
       }
     } else {
       newInnerWindow->InnerSetNewDocument(aDocument);
 
       // Initialize DOM classes etc on the inner window.
-      JS::Rooted<JSObject*> obj(cx, newInnerWindow->mJSObject);
+      JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
       rv = mContext->InitClasses(obj);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // If the document comes from a JAR, check if the channel was determined
     // to be unsafe. If so, permanently disable script on the compartment by
     // calling Block() and throwing away the key.
     nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
     if (jarChannel && jarChannel->GetIsUnsafe()) {
-      xpc::Scriptability::Get(newInnerWindow->mJSObject).Block();
+      xpc::Scriptability::Get(newInnerGlobal).Block();
     }
 
     if (mArguments) {
       newInnerWindow->DefineArgumentsProperty(mArguments);
       mArguments = nullptr;
     }
 
     // Give the new inner window our chrome event handler (since it
@@ -2616,17 +2594,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   }
 
   mContext->GC(JS::gcreason::SET_NEW_DOCUMENT);
   mContext->DidInitializeContext();
 
   // We wait to fire the debugger hook until the window is all set up and hooked
   // up with the outer. See bug 969156.
   if (createdInnerWindow) {
-    JS::Rooted<JSObject*> global(cx, newInnerWindow->mJSObject);
+    JS::Rooted<JSObject*> global(cx, newInnerWindow->GetWrapper());
     JS_FireOnNewGlobalObject(cx, global);
   }
 
   if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
     // We should probably notify. However if this is the, arguably bad,
     // situation when we're creating a temporary non-chrome-about-blank
     // document in a chrome docshell, don't notify just yet. Instead wait
     // until we have a real chrome doc.
@@ -2785,18 +2763,17 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
 void
 nsGlobalWindow::DetachFromDocShell()
 {
   NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!");
 
   // DetachFromDocShell means the window is being torn down. Drop our
   // reference to the script context, allowing it to be deleted
   // later. Meanwhile, keep our weak reference to the script object
-  // (mJSObject) so that it can be retrieved later (until it is
-  // finalized by the JS GC).
+  // so that it can be retrieved later (until it is finalized by the JS GC).
 
   NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!");
 
   // Call FreeInnerObjects on all inner windows, not just the current
   // one, since some could be held by WindowStateHolder objects that
   // are GC-owned.
   for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
        inner != this;
@@ -3210,29 +3187,21 @@ nsGlobalWindow::DispatchDOMEvent(WidgetE
                                  nsEventStatus* aEventStatus)
 {
   return EventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMWindow*>(this),
                                            aEvent, aDOMEvent, aPresContext,
                                            aEventStatus);
 }
 
 void
-nsGlobalWindow::OnFinalize(JSObject* aObject)
-{
-  if (aObject == mJSObject) {
-    mJSObject = nullptr;
-  }
-}
-
-void
 nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
 {
   MOZ_ASSERT(IsOuterWindow());
-  if (aObject == mJSObject) {
-    mJSObject.setToCrashOnTouch();
+  if (aObject == GetWrapperPreserveColor()) {
+    PoisonWrapper();
   }
 }
 
 nsresult
 nsGlobalWindow::SetArguments(nsIArray *aArguments)
 {
   MOZ_ASSERT(IsOuterWindow());
   nsresult rv;
@@ -3270,17 +3239,17 @@ nsresult
 nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments)
 {
   MOZ_ASSERT(!mIsModalContentWindow); // Handled separately.
   nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
   NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
   AutoPushJSContext cx(ctx->GetNativeContext());
   NS_ENSURE_TRUE(cx, NS_ERROR_NOT_INITIALIZED);
 
-  JS::Rooted<JSObject*> obj(cx, mJSObject);
+  JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
   return GetContextInternal()->SetProperty(obj, "arguments", aArguments);
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptObjectPrincipal
 //*****************************************************************************
 
 nsIPrincipal*
@@ -4410,18 +4379,18 @@ nsGlobalWindow::SetOpener(nsIDOMWindow* 
     // expandos on Xrays as needed.
 
     JS::Rooted<JSObject*> otherObj(cx, glob->GetGlobalJSObject());
     if (!otherObj) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
-    JS::Rooted<JSObject*> thisObj(cx, mJSObject);
-    if (!mJSObject) {
+    JS::Rooted<JSObject*> thisObj(cx, GetWrapperPreserveColor());
+    if (!thisObj) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
     if (!JS_WrapObject(cx, &otherObj) ||
         !JS_WrapObject(cx, &thisObj) ||
         !JS_DefineProperty(cx, thisObj, "opener", JS::ObjectValue(*otherObj),
                            JS_PropertyStub, JS_StrictPropertyStub,
@@ -5095,18 +5064,18 @@ nsGlobalWindow::RequestAnimationFrame(co
 {
   FORWARD_TO_INNER_OR_THROW(RequestAnimationFrame, (aCallback, aError), aError,
                             0);
 
   if (!mDoc) {
     return 0;
   }
 
-  if (mJSObject) {
-    js::NotifyAnimationActivity(mJSObject);
+  if (GetWrapperPreserveColor()) {
+    js::NotifyAnimationActivity(GetWrapperPreserveColor());
   }
 
   int32_t handle;
   aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
   return handle;
 }
 
 NS_IMETHODIMP
@@ -5657,17 +5626,17 @@ nsGlobalWindow::DispatchResizeEvent(cons
   ErrorResult res;
   nsRefPtr<Event> domEvent =
     mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res);
   if (res.Failed()) {
     return false;
   }
 
   AutoSafeJSContext cx;
-  JSAutoCompartment ac(cx, mJSObject);
+  JSAutoCompartment ac(cx, GetWrapperPreserveColor());
   DOMWindowResizeEventDetail detail;
   detail.mWidth = aSize.width;
   detail.mHeight = aSize.height;
   JS::Rooted<JS::Value> detailValue(cx);
   if (!detail.ToObject(cx, &detailValue)) {
     return false;
   }
 
@@ -5694,17 +5663,17 @@ nsGlobalWindow::DispatchResizeEvent(cons
   return defaultActionEnabled;
 }
 
 void
 nsGlobalWindow::RefreshCompartmentPrincipal()
 {
   FORWARD_TO_INNER(RefreshCompartmentPrincipal, (), /* void */ );
 
-  JS_SetCompartmentPrincipals(js::GetObjectCompartment(mJSObject),
+  JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()),
                               nsJSPrincipals::get(mDoc->NodePrincipal()));
 }
 
 static already_AddRefed<nsIDocShellTreeItem>
 GetCallerDocShellTreeItem()
 {
   JSContext *cx = nsContentUtils::GetCurrentJSContext();
   nsCOMPtr<nsIDocShellTreeItem> callerItem;
@@ -8652,20 +8621,17 @@ nsGlobalWindow::GetCachedXBLPrototypeHan
 }
 
 void
 nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
                                          JS::Handle<JSObject*> aHandler)
 {
   if (!mCachedXBLPrototypeHandlers) {
     mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>();
-  }
-
-  if (!mCachedXBLPrototypeHandlers->Count()) {
-    mozilla::HoldJSObjects(this);
+    PreserveWrapper(ToSupports(this));
   }
 
   mCachedXBLPrototypeHandlers->Put(aKey, aHandler);
 }
 
 /**
  * GetScriptableFrameElement is called when script reads
  * nsIGlobalWindow::frameElement.
@@ -12537,17 +12503,17 @@ nsGlobalWindow::EnsureSizeUpToDate()
   }
 }
 
 already_AddRefed<nsISupports>
 nsGlobalWindow::SaveWindowState()
 {
   NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state");
 
-  if (!mContext || !mJSObject) {
+  if (!mContext || !GetWrapperPreserveColor()) {
     // The window may be getting torn down; don't bother saving state.
     return nullptr;
   }
 
   nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
   NS_ASSERTION(inner, "No inner window to save");
 
   // Don't do anything else to this inner window! After this point, all
@@ -12567,17 +12533,17 @@ nsGlobalWindow::SaveWindowState()
   return state.forget();
 }
 
 nsresult
 nsGlobalWindow::RestoreWindowState(nsISupports *aState)
 {
   NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window");
 
-  if (!mContext || !mJSObject) {
+  if (!mContext || !GetWrapperPreserveColor()) {
     // The window may be getting torn down; don't bother restoring state.
     return NS_OK;
   }
 
   nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
   NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
 
 #ifdef DEBUG_PAGE_CACHE
@@ -13547,34 +13513,34 @@ nsGlobalWindow::GetConsole(JSContext* aC
                            JS::MutableHandle<JS::Value> aConsole)
 {
   ErrorResult rv;
   nsRefPtr<Console> console = GetConsole(rv);
   if (rv.Failed()) {
     return rv.ErrorCode();
   }
 
-  JS::Rooted<JSObject*> thisObj(aCx, mJSObject);
-  if (!mJSObject) {
+  JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
+  if (!thisObj) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JS_WrapObject(aCx, &thisObj) ||
       !WrapNewBindingObject(aCx, thisObj, console, aConsole)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue)
 {
-  JS::Rooted<JSObject*> thisObj(aCx, mJSObject);
-  if (!mJSObject) {
+  JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
+  if (!thisObj) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JS_WrapObject(aCx, &thisObj) ||
       !JS_DefineProperty(aCx, thisObj, "console", aValue,
                          JS_PropertyStub, JS_StrictPropertyStub,
                          JSPROP_ENUMERATE)) {
     return NS_ERROR_FAILURE;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -353,26 +353,26 @@ public:
   }
 
   // nsIGlobalJSObjectHolder
   virtual JSObject *GetGlobalJSObject();
 
   // nsIScriptGlobalObject
   JSObject *FastGetGlobalJSObject() const
   {
-    return mJSObject;
+    return GetWrapperPreserveColor();
   }
+
   void TraceGlobalJSObject(JSTracer* aTrc);
 
   virtual nsresult EnsureScriptEnvironment();
 
   virtual nsIScriptContext *GetScriptContext();
 
   void PoisonOuterWindowProxy(JSObject *aObject);
-  virtual void OnFinalize(JSObject* aObject);
 
   virtual bool IsBlackForCC(bool aTracingNeeded = true);
 
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal();
 
   // nsIDOMWindow
   NS_DECL_NSIDOMWINDOW
@@ -1501,19 +1501,16 @@ protected:
   uint32_t                      mTimeoutPublicIdCounter;
   uint32_t                      mTimeoutFiringDepth;
   nsRefPtr<nsLocation>          mLocation;
   nsRefPtr<nsHistory>           mHistory;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
-  // The JS global object.  Global objects are always allocated tenured.
-  JS::TenuredHeap<JSObject*> mJSObject;
-
   typedef nsCOMArray<nsIDOMStorageEvent> nsDOMStorageEventArray;
   nsDOMStorageEventArray mPendingStorageEvents;
 
   uint32_t mTimeoutsSuspendDepth;
 
   // the method that was used to focus mFocusedNode
   uint32_t mFocusMethod;
 
--- a/dom/base/nsIScriptGlobalObject.h
+++ b/dom/base/nsIScriptGlobalObject.h
@@ -28,18 +28,18 @@ struct ErrorEventInit;
 // aStatus will be filled in with the status.
 bool
 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
                      const mozilla::dom::ErrorEventInit &aErrorEvent,
                      nsEventStatus *aStatus);
 
 
 #define NS_ISCRIPTGLOBALOBJECT_IID \
-{ 0x6995e1ff, 0x9fc5, 0x44a7, \
- { 0xbd, 0x7c, 0xe7, 0xcd, 0x44, 0x47, 0x22, 0x87 } }
+{ 0x876f83bd, 0x6314, 0x460a, \
+  { 0xa0, 0x45, 0x1c, 0x8f, 0x46, 0x2f, 0xb8, 0xe1 } }
 
 /**
  * The global object which keeps a script context for each supported script
  * language. This often used to store per-window global state.
  * This is a heavyweight interface implemented only by DOM globals, and
  * it might go away some time in the future.
  */
 
@@ -63,25 +63,16 @@ public:
    */
   virtual nsIScriptContext *GetScriptContext() = 0;
 
   nsIScriptContext* GetContext() {
     return GetScriptContext();
   }
 
   /**
-   * Called when the global script for a language is finalized, typically as
-   * part of its GC process.  By the time this call is made, the
-   * nsIScriptContext for the language has probably already been removed.
-   * After this call, the passed object is dead - which should generally be the
-   * same object the global is using for a global for that language.
-   */
-  virtual void OnFinalize(JSObject* aObject) = 0;
-
-  /**
    * Handle a script error.  Generally called by a script context.
    */
   virtual nsresult HandleScriptError(
                      const mozilla::dom::ErrorEventInit &aErrorEventInit,
                      nsEventStatus *aEventStatus) {
     NS_ENSURE_STATE(NS_HandleScriptError(this, aErrorEventInit, aEventStatus));
     return NS_OK;
   }
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -6,18 +6,18 @@
 #ifndef nsWrapperCache_h___
 #define nsWrapperCache_h___
 
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Assertions.h"
 #include "js/Id.h"          // must come before js/RootingAPI.h
 #include "js/Value.h"       // must come before js/RootingAPI.h
 #include "js/RootingAPI.h"
+#include "js/Tracer.h"
 
-struct JSTracer;
 class XPCWrappedNativeScope;
 
 #define NS_WRAPPERCACHE_IID \
 { 0x6f3179a1, 0x36f7, 0x4a5c, \
   { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } }
 
 /**
  * Class to store the wrapper for an object. This can only be used with objects
@@ -217,16 +217,31 @@ public:
 #ifdef DEBUG
     // Make sure the cycle collector will be able to traverse to the wrapper.
     CheckCCWrapperTraversal(aScriptObjectHolder, aTracer);
 #endif
   }
 
   void ReleaseWrapper(void* aScriptObjectHolder);
 
+protected:
+  void TraceWrapper(JSTracer* aTrc, const char* name)
+  {
+    if (mWrapper) {
+      JS_CallHeapObjectTracer(aTrc, &mWrapper, name);
+    }
+  }
+
+  void PoisonWrapper()
+  {
+    if (mWrapper) {
+      mWrapper.setToCrashOnTouch();
+    }
+  }
+
 private:
   JSObject *GetWrapperJSObject() const
   {
     return mWrapper;
   }
 
   void SetWrapperJSObject(JSObject* aWrapper)
   {
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -20,20 +20,16 @@
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h" // for nsRefPtr member variables
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/OwningNonNull.h"
 
 class nsWrapperCache;
 
-// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't
-// try to use it without fixing that first.
-class nsGlobalWindow;
-
 namespace mozilla {
 namespace dom {
 
 // Struct that serves as a base class for all dictionaries.  Particularly useful
 // so we can use IsBaseOf to detect dictionary template arguments.
 struct DictionaryBase
 {
 protected:
@@ -438,22 +434,16 @@ public:
 
 inline nsWrapperCache*
 GetWrapperCache(nsWrapperCache* cache)
 {
   return cache;
 }
 
 inline nsWrapperCache*
-GetWrapperCache(nsGlobalWindow*)
-{
-  return nullptr;
-}
-
-inline nsWrapperCache*
 GetWrapperCache(void* p)
 {
   return nullptr;
 }
 
 // Helper template for smart pointers to resolve ambiguity between
 // GetWrappeCache(void*) and GetWrapperCache(const ParentObject&).
 template <template <typename> class SmartPtr, typename T>
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -240,16 +240,28 @@ class Heap : public js::HeapBase<T>
         } else if (js::GCMethods<T>::needsPostBarrier(ptr)) {
             relocate();  /* Called before overwriting ptr. */
             ptr = newPtr;
         } else {
             ptr = newPtr;
         }
     }
 
+    /*
+     * Set the pointer to a value which will cause a crash if it is
+     * dereferenced.
+     */
+    void setToCrashOnTouch() {
+        ptr = reinterpret_cast<T>(crashOnTouchPointer);
+    }
+
+    bool isSetToCrashOnTouch() {
+        return ptr == crashOnTouchPointer;
+    }
+
   private:
     void init(T newPtr) {
         MOZ_ASSERT(!js::GCMethods<T>::poisoned(newPtr));
         ptr = newPtr;
         if (js::GCMethods<T>::needsPostBarrier(ptr))
             post();
     }
 
@@ -261,16 +273,20 @@ class Heap : public js::HeapBase<T>
     }
 
     void relocate() {
 #ifdef JSGC_GENERATIONAL
         js::GCMethods<T>::relocate(&ptr);
 #endif
     }
 
+    enum {
+        crashOnTouchPointer = 1
+    };
+
     T ptr;
 };
 
 #ifdef JS_DEBUG
 /*
  * For generational GC, assert that an object is in the tenured generation as
  * opposed to being in the nursery.
  */
@@ -358,33 +374,20 @@ class TenuredHeap : public js::HeapBase<
         return *this;
     }
 
     TenuredHeap<T> &operator=(const TenuredHeap<T>& other) {
         bits = other.bits;
         return *this;
     }
 
-    /*
-     * Set the pointer to a value which will cause a crash if it is
-     * dereferenced.
-     */
-    void setToCrashOnTouch() {
-        bits = (bits & flagsMask) | crashOnTouchPointer;
-    }
-
-    bool isSetToCrashOnTouch() {
-        return (bits & ~flagsMask) == crashOnTouchPointer;
-    }
-
   private:
     enum {
         maskBits = 3,
         flagsMask = (1 << maskBits) - 1,
-        crashOnTouchPointer = 1 << maskBits
     };
 
     uintptr_t bits;
 };
 
 /*
  * Reference to a T that has been rooted elsewhere. This is most useful
  * as a parameter type, which guarantees that the T lvalue is properly
--- a/js/xpconnect/src/XPCQuickStubs.h
+++ b/js/xpconnect/src/XPCQuickStubs.h
@@ -501,22 +501,16 @@ castNativeArgFromWrapper(JSContext *cx,
 }
 
 inline nsWrapperCache*
 xpc_qsGetWrapperCache(nsWrapperCache *cache)
 {
     return cache;
 }
 
-// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't
-// try to use it without fixing that first.
-class nsGlobalWindow;
-inline nsWrapperCache*
-xpc_qsGetWrapperCache(nsGlobalWindow *not_allowed);
-
 inline nsWrapperCache*
 xpc_qsGetWrapperCache(void *p)
 {
     return nullptr;
 }
 
 /** Convert an XPCOM pointer to jsval. Return true on success.
  * aIdentity is a performance optimization. Set it to true,
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -19,16 +19,17 @@
 #include "nsWrapperCache.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "mozilla/dom/JSSlots.h"
 #include "nsMathUtils.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/BindingDeclarations.h"
 
+class nsGlobalWindow;
 class nsIPrincipal;
 class nsScriptNameSpaceManager;
 class nsIGlobalObject;
 class nsIMemoryReporterCallback;
 
 #ifndef BAD_TLS_INDEX
 #define BAD_TLS_INDEX ((uint32_t) -1)
 #endif
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -43,16 +43,17 @@ namespace test {
 class AFakePCObserver;
 #endif
 }
 
 #ifdef USE_FAKE_MEDIA_STREAMS
 class Fake_DOMMediaStream;
 #endif
 
+class nsGlobalWindow;
 class nsIDOMMediaStream;
 class nsDOMDataChannel;
 
 namespace mozilla {
 class DataChannel;
 class DtlsIdentity;
 class NrIceCtx;
 class NrIceMediaStream;