diff --git a/caps/src/nsSecurityManagerFactory.cpp b/caps/src/nsSecurityManagerFactory.cpp index 071bef3ffa1b..e13971c82c67 100644 --- a/caps/src/nsSecurityManagerFactory.cpp +++ b/caps/src/nsSecurityManagerFactory.cpp @@ -66,7 +66,7 @@ NS_IMETHODIMP nsSecurityNameSet::InitializeNameSet(nsIScriptContext* aScriptContext) { AutoJSContext cx; - JS::Rooted global(cx, aScriptContext->GetWindowProxy()); + JS::Rooted global(cx, aScriptContext->GetNativeGlobal()); JSAutoCompartment ac(cx, global); /* diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index 87d505487e18..cbc4bf9e0244 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -908,7 +908,7 @@ nsEventListenerManager::CompileEventHandlerInternal(nsListenerStruct *aListenerS aListenerStruct->mTypeAtom, &argCount, &argNames); - JSAutoCompartment ac(cx, context->GetWindowProxy()); + JSAutoCompartment ac(cx, context->GetNativeGlobal()); JS::CompileOptions options(cx); options.setFileAndLine(url.get(), lineNo) .setVersion(SCRIPTVERSION_DEFAULT); diff --git a/dom/base/nsDOMJSUtils.h b/dom/base/nsDOMJSUtils.h index bac8aff65983..1d0a62207442 100644 --- a/dom/base/nsDOMJSUtils.h +++ b/dom/base/nsDOMJSUtils.h @@ -28,8 +28,6 @@ GetScriptContextFromJSContext(JSContext *cx) return scx; } -JSObject* GetDefaultScopeFromJSContext(JSContext *cx); - // A factory function for turning a JS::Value argv into an nsIArray // but also supports an effecient way of extracting the original argv. // Bug 312003 describes why this must be "void *", but argv will be cast to diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index fc586bc0b85a..9f6b5c70962f 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2048,9 +2048,8 @@ nsGlobalWindow::SetOuterObject(JSContext* aCx, JS::Handle aOuterObjec { JSAutoCompartment ac(aCx, aOuterObject); - // Inform the nsJSContext, which is the canonical holder of the outer. - MOZ_ASSERT(IsOuterWindow()); - mContext->SetWindowProxy(aOuterObject); + // Indicate the default compartment object associated with this cx. + js::SetDefaultObjectForContext(aCx, aOuterObject); // Set up the prototype for the outer object. JS::Rooted inner(aCx, JS_GetParent(aOuterObject)); @@ -2362,7 +2361,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, CreateOuterObject(newInnerWindow); mContext->DidInitializeContext(); - mJSObject = mContext->GetWindowProxy(); + mJSObject = mContext->GetNativeGlobal(); SetWrapper(mJSObject); } else { JS::Rooted global(cx, @@ -2446,7 +2445,11 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, newInnerWindow->FastGetGlobalJSObject()); #endif - MOZ_ASSERT(mContext->GetWindowProxy() == mJSObject); + // Now that we're connecting the outer global to the inner one, + // we must have transplanted it. The JS engine tries to maintain + // the global object's compartment as its default compartment, + // so update that now since it might have changed. + js::SetDefaultObjectForContext(cx, mJSObject); #ifdef DEBUG JS::Rooted rootedJSObject(cx, mJSObject); JS::Rooted proto1(cx), proto2(cx); diff --git a/dom/base/nsIScriptContext.h b/dom/base/nsIScriptContext.h index 11f7e73aa6da..5c72e4781526 100644 --- a/dom/base/nsIScriptContext.h +++ b/dom/base/nsIScriptContext.h @@ -27,8 +27,8 @@ class nsIDOMWindow; class nsIURI; #define NS_ISCRIPTCONTEXT_IID \ -{ 0xf3859ce7, 0x7551, 0x4760, \ - { 0x84, 0x29, 0x64, 0x4f, 0x26, 0x1e, 0xdb, 0x91 } } +{ 0x1d931a17, 0x453a, 0x47fb, \ + { 0x94, 0x66, 0x2d, 0x3e, 0xd1, 0xef, 0x7a, 0xc5 } } /* This MUST match JSVERSION_DEFAULT. This version stuff if we don't know what language we have is a little silly... */ @@ -103,6 +103,12 @@ public: **/ virtual JSContext* GetNativeContext() = 0; + /** + * Return the native global object for this context. + * + **/ + virtual JSObject* GetNativeGlobal() = 0; + /** * Initialize the context generally. Does not create a global object. **/ @@ -159,12 +165,6 @@ public: * Tell the context we're done reinitializing it. */ virtual void DidInitializeContext() = 0; - - /** - * Access the Window Proxy. The setter should only be called by nsGlobalWindow. - */ - virtual void SetWindowProxy(JS::Handle aWindowProxy) = 0; - virtual JSObject* GetWindowProxy() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptContext, NS_ISCRIPTCONTEXT_IID) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 37b32a1e138c..3ad6a2d67def 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -80,9 +80,7 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/Attributes.h" #include "mozilla/dom/CanvasRenderingContext2DBinding.h" -#include "mozilla/CycleCollectedJSRuntime.h" -#include "nsCycleCollectionNoteRootCallback.h" #include "GeckoProfiler.h" using namespace mozilla; @@ -824,8 +822,7 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) nsJSContext::nsJSContext(bool aGCOnDestruction, nsIScriptGlobalObject* aGlobalObject) - : mWindowProxy(nullptr) - , mGCOnDestruction(aGCOnDestruction) + : mGCOnDestruction(aGCOnDestruction) , mGlobalObjectRef(aGlobalObject) { EnsureStatics(); @@ -839,8 +836,7 @@ nsJSContext::nsJSContext(bool aGCOnDestruction, ++sContextCount; - mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS | - JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT; + mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS; mContext = ::JS_NewContext(sRuntime, gStackSize); if (mContext) { @@ -859,7 +855,6 @@ nsJSContext::nsJSContext(bool aGCOnDestruction, mIsInitialized = false; mScriptsEnabled = true; mProcessingScriptTag = false; - NS_HOLD_JS_OBJECTS(this, nsJSContext); } nsJSContext::~nsJSContext() @@ -910,14 +905,12 @@ nsJSContext::DestroyJSContext() JS_DestroyContextNoGC(mContext); mContext = nullptr; - NS_DROP_JS_OBJECTS(this, nsJSContext); } // QueryInterface implementation for nsJSContext NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSContext) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSContext) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowProxy) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext) @@ -925,14 +918,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSContext) "Trying to unlink a context with outstanding requests."); tmp->mIsInitialized = false; tmp->mGCOnDestruction = false; - tmp->mWindowProxy = nullptr; + if (tmp->mContext) { + JSAutoRequest ar(tmp->mContext); + js::SetDefaultObjectForContext(tmp->mContext, nullptr); + } tmp->DestroyJSContext(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobalObjectRef) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSContext) NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSContext, tmp->GetCCRefcnt()) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobalObjectRef) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContext"); + nsContentUtils::XPConnect()->NoteJSContext(tmp->mContext, cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSContext) @@ -1085,7 +1082,7 @@ nsIScriptGlobalObject * nsJSContext::GetGlobalObject() { AutoJSContext cx; - JS::Rooted global(mContext, GetWindowProxy()); + JS::Rooted global(mContext, GetNativeGlobal()); if (!global) { return nullptr; } @@ -1134,10 +1131,16 @@ nsJSContext::GetGlobalObject() return sgo; } +JSObject* +nsJSContext::GetNativeGlobal() +{ + return js::DefaultObjectForContextOrNull(mContext); +} + JSContext* nsJSContext::GetNativeContext() { - return mContext; + return xpc_UnmarkGrayContext(mContext); } nsresult @@ -1177,7 +1180,7 @@ nsJSContext::SetProperty(JS::Handle aTarget, const char* aPropName, n Maybe tempStorage; - JS::Rooted global(mContext, GetWindowProxy()); + JS::Rooted global(mContext, GetNativeGlobal()); nsresult rv = ConvertSupportsTojsvals(aArgs, global, &argc, &argv, tempStorage); NS_ENSURE_SUCCESS(rv, rv); @@ -1473,7 +1476,7 @@ nsJSContext::AddSupportsPrimitiveTojsvals(nsISupports *aArg, JS::Value *aArgv) AutoFree iidGuard(iid); // Free iid upon destruction. nsCOMPtr wrapper; - JS::Rooted global(cx, GetWindowProxy()); + JS::Rooted global(cx, xpc_UnmarkGrayObject(GetNativeGlobal())); JS::Rooted v(cx); nsresult rv = nsContentUtils::WrapNative(cx, global, data, iid, v.address(), @@ -2575,18 +2578,6 @@ nsJSContext::ReportPendingException() } } -void -nsJSContext::SetWindowProxy(JS::Handle aWindowProxy) -{ - mWindowProxy = aWindowProxy; -} - -JSObject* -nsJSContext::GetWindowProxy() -{ - return xpc_UnmarkGrayObject(mWindowProxy); -} - void nsJSContext::LikelyShortLivingObjectCreated() { diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index bf0c936b8ecf..c9803e7f4da5 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -19,7 +19,6 @@ class nsICycleCollectorListener; class nsIXPConnectJSObjectHolder; class nsRootedJSValueArray; class nsScriptNameSpaceManager; -class nsCycleCollectionNoteRootCallback; namespace mozilla { template class Maybe; @@ -54,6 +53,7 @@ public: inline nsIScriptGlobalObject *GetGlobalObjectRef() { return mGlobalObjectRef; } virtual JSContext* GetNativeContext() MOZ_OVERRIDE; + virtual JSObject* GetNativeGlobal() MOZ_OVERRIDE; virtual nsresult InitContext() MOZ_OVERRIDE; virtual bool IsContextInitialized() MOZ_OVERRIDE; @@ -70,9 +70,6 @@ public: virtual void WillInitializeContext() MOZ_OVERRIDE; virtual void DidInitializeContext() MOZ_OVERRIDE; - virtual void SetWindowProxy(JS::Handle aWindowProxy) MOZ_OVERRIDE; - virtual JSObject* GetWindowProxy() MOZ_OVERRIDE; - static void LoadStart(); static void LoadEnd(); @@ -128,7 +125,7 @@ public: { // Verify that we have a global so that this // does always return a null when GetGlobalObject() is null. - JSObject* global = GetWindowProxy(); + JSObject* global = GetNativeGlobal(); return global ? mGlobalObjectRef.get() : nullptr; } protected: @@ -153,14 +150,12 @@ protected: // function will set aside the frame chain on mContext before // reporting. void ReportPendingException(); - private: void DestroyJSContext(); nsrefcnt GetCCRefcnt(); JSContext *mContext; - JS::Heap mWindowProxy; bool mIsInitialized; bool mScriptsEnabled; diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index 4797645ba2a5..51fa66f2a5a5 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -14,7 +14,6 @@ #include "nsJSUtils.h" #include "jsapi.h" #include "jsdbgapi.h" -#include "jsfriendapi.h" #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsIXPConnect.h" @@ -141,11 +140,7 @@ nsJSUtils::ReportPendingException(JSContext *aContext) if (JS_IsExceptionPending(aContext)) { bool saved = JS_SaveFrameChain(aContext); { - nsIScriptContext* scx = GetScriptContextFromJSContext(aContext); - JS::Rooted scope(aContext); - scope = scx ? scx->GetWindowProxy() - : js::DefaultObjectForContextOrNull(aContext); - JSAutoCompartment ac(aContext, scope); + JSAutoCompartment ac(aContext, js::DefaultObjectForContextOrNull(aContext)); JS_ReportPendingException(aContext); } if (saved) { @@ -292,19 +287,3 @@ nsJSUtils::EvaluateString(JSContext* aCx, return NS_ERROR_OUT_OF_MEMORY; return rv; } - -// -// nsDOMJSUtils.h -// - -JSObject* GetDefaultScopeFromJSContext(JSContext *cx) -{ - // DOM JSContexts don't store their default compartment object on - // the cx, so in those cases we need to fetch it via the scx - // instead. - nsIScriptContext *scx = GetScriptContextFromJSContext(cx); - if (scx) { - return scx->GetWindowProxy(); - } - return js::DefaultObjectForContextOrNull(cx); -} diff --git a/dom/bluetooth/BluetoothManager.cpp b/dom/bluetooth/BluetoothManager.cpp index 1fcce356898c..43118c15fa39 100644 --- a/dom/bluetooth/BluetoothManager.cpp +++ b/dom/bluetooth/BluetoothManager.cpp @@ -68,7 +68,7 @@ public: AutoPushJSContext cx(sc->GetNativeContext()); - JS::Rooted global(cx, sc->GetWindowProxy()); + JS::Rooted global(cx, sc->GetNativeGlobal()); rv = nsContentUtils::WrapNative(cx, global, adapter, aValue); if (NS_FAILED(rv)) { NS_WARNING("Cannot create native object!"); diff --git a/dom/file/ArchiveRequest.cpp b/dom/file/ArchiveRequest.cpp index e66fa3e24208..5d2230bc76c9 100644 --- a/dom/file/ArchiveRequest.cpp +++ b/dom/file/ArchiveRequest.cpp @@ -135,7 +135,7 @@ ArchiveRequest::ReaderReady(nsTArray >& aFileList, AutoPushJSContext cx(sc->GetNativeContext()); NS_ASSERTION(cx, "Failed to get a context!"); - JS::Rooted global(cx, sc->GetWindowProxy()); + JS::Rooted global(cx, sc->GetNativeGlobal()); NS_ASSERTION(global, "Failed to get global object!"); JSAutoCompartment ac(cx, global); diff --git a/dom/file/FileRequest.cpp b/dom/file/FileRequest.cpp index 7de459f2bfcc..0893466bb9b6 100644 --- a/dom/file/FileRequest.cpp +++ b/dom/file/FileRequest.cpp @@ -79,7 +79,7 @@ FileRequest::NotifyHelperCompleted(FileHelper* aFileHelper) JS::Rooted result(cx); - JS::Rooted global(cx, sc->GetWindowProxy()); + JS::Rooted global(cx, sc->GetNativeGlobal()); NS_ASSERTION(global, "Failed to get global object!"); JSAutoCompartment ac(cx, global); diff --git a/dom/mobilemessage/src/MobileMessageCallback.cpp b/dom/mobilemessage/src/MobileMessageCallback.cpp index 05951a234223..f81dd5432843 100644 --- a/dom/mobilemessage/src/MobileMessageCallback.cpp +++ b/dom/mobilemessage/src/MobileMessageCallback.cpp @@ -56,7 +56,7 @@ MobileMessageCallback::NotifySuccess(nsISupports *aMessage) AutoPushJSContext cx(scriptContext->GetNativeContext()); NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); - JS::Rooted global(cx, scriptContext->GetWindowProxy()); + JS::Rooted global(cx, scriptContext->GetNativeGlobal()); NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); JSAutoCompartment ac(cx, global); diff --git a/dom/mobilemessage/src/MobileMessageCursorCallback.cpp b/dom/mobilemessage/src/MobileMessageCursorCallback.cpp index c22030e4f947..77af8722e44d 100644 --- a/dom/mobilemessage/src/MobileMessageCursorCallback.cpp +++ b/dom/mobilemessage/src/MobileMessageCursorCallback.cpp @@ -67,7 +67,7 @@ MobileMessageCursorCallback::NotifyCursorResult(nsISupports* aResult) AutoPushJSContext cx(scriptContext->GetNativeContext()); NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); - JS::Rooted global(cx, scriptContext->GetWindowProxy()); + JS::Rooted global(cx, scriptContext->GetNativeGlobal()); NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); JSAutoCompartment ac(cx, global); diff --git a/dom/mobilemessage/src/MobileMessageManager.cpp b/dom/mobilemessage/src/MobileMessageManager.cpp index 76e896442194..57724428403f 100644 --- a/dom/mobilemessage/src/MobileMessageManager.cpp +++ b/dom/mobilemessage/src/MobileMessageManager.cpp @@ -155,7 +155,7 @@ MobileMessageManager::Send(const JS::Value& aNumber_, const nsAString& aMessage, return NS_ERROR_INVALID_ARG; } - JS::Rooted global(cx, sc->GetWindowProxy()); + JS::Rooted global(cx, sc->GetNativeGlobal()); NS_ASSERTION(global, "Failed to get global object!"); JSAutoCompartment ac(cx, global); diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 918dab6b6cc2..c607d78721dc 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -831,7 +831,8 @@ public: // call to JS_SetGCParameter inside CreateJSContextForWorker. WorkerJSRuntime(WorkerPrivate* aWorkerPrivate) : CycleCollectedJSRuntime(WORKER_DEFAULT_RUNTIME_HEAPSIZE, - JS_NO_HELPER_THREADS), + JS_NO_HELPER_THREADS, + false), mWorkerPrivate(aWorkerPrivate) { // We need to ensure that a JSContext outlives the cycle collector, and diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 9a23d160c25f..b5ca78df239d 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -30,7 +30,6 @@ /* XXX DOM dependency */ #include "nsIScriptContext.h" -#include "nsDOMJSUtils.h" #include "SandboxPrivate.h" #include "nsJSPrincipals.h" #include "nsContentUtils.h" @@ -1697,7 +1696,7 @@ NS_IMETHODIMP jsdContext::GetGlobalObject (jsdIValue **_rval) { ASSERT_VALID_EPHEMERAL; - JSObject *glob = GetDefaultScopeFromJSContext(mJSCx); + JSObject *glob = js::DefaultObjectForContextOrNull(mJSCx); JSDValue *jsdv = JSD_NewValue (mJSDCx, OBJECT_TO_JSVAL(glob)); if (!jsdv) return NS_ERROR_FAILURE; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index dc4680d12508..73dfb8cd2014 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2399,15 +2399,6 @@ JS_IsGCMarkingTracer(JSTracer *trc) return IS_GC_MARKING_TRACER(trc); } -#ifdef DEBUG -extern JS_PUBLIC_API(bool) -JS_IsMarkingGray(JSTracer *trc) -{ - JS_ASSERT(JS_IsGCMarkingTracer(trc)); - return trc->callback == GCMarker::GrayCallback; -} -#endif - JS_PUBLIC_API(void) JS_GC(JSRuntime *rt) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index ae99cdb9b594..87a5ffa6d493 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1641,18 +1641,16 @@ JS_StringToVersion(const char *string); /* JS_BIT(10) is currently unused. */ -#define JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT JS_BIT(11) /* This JSContext does not use a - default compartment object. Such - an object will not be set implicitly, - and attempts to get or set it will - assert. */ +/* JS_BIT(11) is currently unused. */ #define JSOPTION_NO_SCRIPT_RVAL JS_BIT(12) /* A promise to the compiler that a null rval out-param will be passed to each call to JS_ExecuteScript. */ - -/* JS_BIT(13) is currently unused. */ +#define JSOPTION_UNROOTED_GLOBAL JS_BIT(13) /* The GC will not root the + contexts' default compartment + object, leaving that up to the + embedding. */ #define JSOPTION_BASELINE JS_BIT(14) /* Baseline compiler. */ @@ -2327,12 +2325,6 @@ JS_SetFinalizeCallback(JSRuntime *rt, JSFinalizeCallback cb); extern JS_PUBLIC_API(bool) JS_IsGCMarkingTracer(JSTracer *trc); -/* For assertions only. */ -#ifdef DEBUG -extern JS_PUBLIC_API(bool) -JS_IsMarkingGray(JSTracer *trc); -#endif - /* * JS_IsAboutToBeFinalized checks if the given object is going to be finalized * at the end of the current GC. When called outside of the context of a GC, diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 0bf6bef441da..8be7fbe8c35c 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1279,7 +1279,7 @@ JSContext::mark(JSTracer *trc) /* Stack frames and slots are traced by StackSpace::mark. */ /* Mark other roots-by-definition in the JSContext. */ - if (defaultCompartmentObject_) + if (defaultCompartmentObject_ && !hasOption(JSOPTION_UNROOTED_GLOBAL)) MarkObjectRoot(trc, &defaultCompartmentObject_, "default compartment object"); if (isExceptionPending()) MarkValueRoot(trc, &exception, "exception"); diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 9e08fd1942ef..4b2c8227780c 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -444,10 +444,7 @@ struct JSContext : public js::ExclusiveContext, public: inline void setDefaultCompartmentObject(JSObject *obj); inline void setDefaultCompartmentObjectIfUnset(JSObject *obj); - JSObject *maybeDefaultCompartmentObject() const { - JS_ASSERT(!hasOption(JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT)); - return defaultCompartmentObject_; - } + JSObject *maybeDefaultCompartmentObject() const { return defaultCompartmentObject_; } /* Wrap cx->exception for the current compartment. */ void wrapPendingException(); diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 654b9ff13f2f..d933d404e581 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -429,18 +429,14 @@ JSContext::setPendingException(js::Value v) { inline void JSContext::setDefaultCompartmentObject(JSObject *obj) { - JS_ASSERT(!hasOption(JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT)); defaultCompartmentObject_ = obj; } inline void JSContext::setDefaultCompartmentObjectIfUnset(JSObject *obj) { - if (!hasOption(JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT) && - !defaultCompartmentObject_) - { + if (!defaultCompartmentObject_) setDefaultCompartmentObject(obj); - } } inline void diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index f880af0350ca..ef25ca501246 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -769,6 +769,12 @@ js::ContextHasOutstandingRequests(const JSContext *cx) } #endif +JS_FRIEND_API(bool) +js::HasUnrootedGlobal(const JSContext *cx) +{ + return cx->hasOption(JSOPTION_UNROOTED_GLOBAL); +} + JS_FRIEND_API(void) js::SetActivityCallback(JSRuntime *rt, ActivityCallback cb, void *arg) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 09f3f8ccc146..ff3d33b36322 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -674,6 +674,9 @@ JS_FRIEND_API(bool) ContextHasOutstandingRequests(const JSContext *cx); #endif +JS_FRIEND_API(bool) +HasUnrootedGlobal(const JSContext *cx); + typedef void (* ActivityCallback)(void *arg, bool active); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 2a6471fb1cda..a8590b2c9ad6 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -682,17 +682,12 @@ bool JSRuntime::initSelfHosting(JSContext *cx) { JS_ASSERT(!selfHostingGlobal_); - - bool receivesDefaultObject = !cx->hasOption(JSOPTION_NO_DEFAULT_COMPARTMENT_OBJECT); - RootedObject savedGlobal(cx, receivesDefaultObject - ? js::DefaultObjectForContextOrNull(cx) - : NULL); + RootedObject savedGlobal(cx, js::DefaultObjectForContextOrNull(cx)); if (!(selfHostingGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class, NULL, JS::DontFireOnNewGlobalHook))) return false; JSAutoCompartment ac(cx, selfHostingGlobal_); - if (receivesDefaultObject) - js::SetDefaultObjectForContext(cx, selfHostingGlobal_); + js::SetDefaultObjectForContext(cx, selfHostingGlobal_); Rooted shg(cx, &selfHostingGlobal_->as()); /* * During initialization of standard classes for the self-hosting global, @@ -761,8 +756,7 @@ JSRuntime::initSelfHosting(JSContext *cx) ok = Evaluate(cx, shg, options, src, srcLen, &rv); } JS_SetErrorReporter(cx, oldReporter); - if (receivesDefaultObject) - js::SetDefaultObjectForContext(cx, savedGlobal); + js::SetDefaultObjectForContext(cx, savedGlobal); return ok; } diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index 53bc5fc83aea..aef0342600ac 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -47,6 +47,8 @@ class nsWrapperCache; native JSEqualityOp(JSEqualityOp); [ptr] native JSScriptPtr(JSScript); [ptr] native voidPtrPtr(void*); +[ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer); +[ref] native nsCCTraversalCallbackRef(nsCycleCollectionTraversalCallback); [ptr] native nsAXPCNativeCallContextPtr(nsAXPCNativeCallContext); [ptr] native nsWrapperCachePtr(nsWrapperCache); [ref] native JSCompartmentOptions(JS::CompartmentOptions); @@ -63,6 +65,11 @@ interface nsIInterfaceInfo; interface nsIXPCSecurityManager; interface nsIPrincipal; +%{C++ +class nsCycleCollectionTraversalCallback; +class nsScriptObjectTracer; +%} + /***************************************************************************/ [uuid(909e8641-7c54-4dff-9b94-ba631f057b33)] interface nsIXPConnectJSObjectHolder : nsISupports @@ -291,7 +298,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } } %} -[uuid(c4d0187c-6a78-4bdf-9cd9-d218644b715a)] +[uuid(0ebc00f0-f3ad-11e2-b778-0800200c9a66)] interface nsIXPConnect : nsISupports { %{ C++ @@ -559,6 +566,14 @@ interface nsIXPConnect : nsISupports in JSObjectPtr sandbox, in boolean returnStringOnly); + /** + * Note aJSContext as a child to the cycle collector. + * @param aJSContext The JSContext to note. + * @param aCb The cycle collection traversal callback. + */ + [noscript,notxpcom] void noteJSContext(in JSContextPtr aJSContext, + in nsCCTraversalCallbackRef aCb); + /** * Whether or not XPConnect should report all JS exceptions when returning * from JS into C++. False by default, although any value set in the diff --git a/js/xpconnect/src/XPCJSContextStack.cpp b/js/xpconnect/src/XPCJSContextStack.cpp index 798938b1709f..f5b987d6b2af 100644 --- a/js/xpconnect/src/XPCJSContextStack.cpp +++ b/js/xpconnect/src/XPCJSContextStack.cpp @@ -70,14 +70,10 @@ XPCJSContextStack::Push(JSContext *cx) // compartment that's same-origin with the current one, we can skip it. nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager(); if ((e.cx == cx) && ssm) { - // DOM JSContexts don't store their default compartment object on - // the cx, so in those cases we need to fetch it via the scx - // instead. - RootedObject defaultScope(cx, GetDefaultScopeFromJSContext(cx)); - + RootedObject defaultGlobal(cx, js::DefaultObjectForContextOrNull(cx)); nsIPrincipal *currentPrincipal = GetCompartmentPrincipal(js::GetContextCompartment(cx)); - nsIPrincipal *defaultPrincipal = GetObjectPrincipal(defaultScope); + nsIPrincipal *defaultPrincipal = GetObjectPrincipal(defaultGlobal); bool equal = false; currentPrincipal->Equals(defaultPrincipal, &equal); if (equal) { diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 1976ad1465b1..732dd4c96645 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2860,7 +2860,7 @@ SourceHook(JSContext *cx, JS::Handle script, jschar **src, } XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) - : CycleCollectedJSRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS), + : CycleCollectedJSRuntime(32L * 1024L * 1024L, JS_USE_HELPER_THREADS, true), mJSContextStack(new XPCJSContextStack()), mCallContext(nullptr), mAutoRoots(nullptr), diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index f82b1455a5e5..0c563f4da6fc 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -1152,7 +1152,7 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, return retval; XPCContext *xpcc = ccx.GetXPCContext(); - JSContext *cx = ccx.GetJSContext(); + JSContext *cx = xpc_UnmarkGrayContext(ccx.GetJSContext()); if (!cx || !xpcc || !IsReflectable(methodIndex)) return NS_ERROR_FAILURE; diff --git a/js/xpconnect/src/nsCxPusher.cpp b/js/xpconnect/src/nsCxPusher.cpp index c762318dd434..260fe671b538 100644 --- a/js/xpconnect/src/nsCxPusher.cpp +++ b/js/xpconnect/src/nsCxPusher.cpp @@ -124,14 +124,14 @@ AutoCxPusher::AutoCxPusher(JSContext* cx, bool allowNull) // Enter a request and a compartment for the duration that the cx is on the // stack if non-null. + // + // NB: We call UnmarkGrayContext so that this can obsolete the need for the + // old XPCAutoRequest as well. if (cx) { mAutoRequest.construct(cx); - - // DOM JSContexts don't store their default compartment object on the cx. - JSObject *compartmentObject = mScx ? mScx->GetWindowProxy() - : js::DefaultObjectForContextOrNull(cx); - if (compartmentObject) - mAutoCompartment.construct(cx, compartmentObject); + if (js::DefaultObjectForContextOrNull(cx)) + mAutoCompartment.construct(cx, js::DefaultObjectForContextOrNull(cx)); + xpc_UnmarkGrayContext(cx); } } diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index ae28382264fc..48519bc3f85b 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -342,6 +342,14 @@ xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS) } } +NS_IMETHODIMP_(void) +nsXPConnect::NoteJSContext(JSContext *aJSContext, + nsCycleCollectionTraversalCallback &aCb) +{ + aCb.NoteNativeChild(aJSContext, mozilla::CycleCollectedJSRuntime::JSContextParticipant()); +} + + /***************************************************************************/ /***************************************************************************/ // nsIXPConnect interface methods... @@ -1315,7 +1323,8 @@ xpc_ActivateDebugMode() JSContext* nsXPConnect::GetCurrentJSContext() { - return GetRuntime()->GetJSContextStack()->Peek(); + JSContext *cx = GetRuntime()->GetJSContextStack()->Peek(); + return xpc_UnmarkGrayContext(cx); } /* virtual */ diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index ee311de7fd3a..249c1a920c45 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -162,6 +162,21 @@ xpc_UnmarkGrayScript(JSScript *script) return script; } +inline JSContext * +xpc_UnmarkGrayContext(JSContext *cx) +{ + if (cx) { + JSObject *global = js::DefaultObjectForContextOrNull(cx); + xpc_UnmarkGrayObject(global); + if (global && JS_IsInRequest(JS_GetRuntime(cx))) { + JSObject *scope = JS::CurrentGlobalOrNull(cx); + if (scope != global) + xpc_UnmarkGrayObject(scope); + } + } + return cx; +} + // If aVariant is an XPCVariant, this marks the object to be in aGeneration. // This also unmarks the gray JSObject. extern void diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index dcd73be366d3..1b0587687ca4 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -278,6 +278,47 @@ private: bool mAnyMarked; }; +class JSContextParticipant : public nsCycleCollectionParticipant +{ +public: + NS_IMETHOD Root(void *n) + { + return NS_OK; + } + NS_IMETHOD Unlink(void *n) + { + return NS_OK; + } + NS_IMETHOD Unroot(void *n) + { + return NS_OK; + } + NS_IMETHOD_(void) DeleteCycleCollectable(void *n) + { + } + NS_IMETHOD Traverse(void *n, nsCycleCollectionTraversalCallback &cb) + { + JSContext *cx = static_cast(n); + + // JSContexts do not have an internal refcount and always have a single + // owner (e.g., nsJSContext). Thus, the default refcount is 1. However, + // in the (abnormal) case of synchronous cycle-collection, the context + // may be actively executing code in which case we want to treat it as + // rooted by adding an extra refcount. + unsigned refCount = js::ContextHasOutstandingRequests(cx) ? 2 : 1; + + cb.DescribeRefCountedNode(refCount, "JSContext"); + if (JSObject *global = js::DefaultObjectForContextOrNull(cx)) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]"); + cb.NoteJSChild(global); + } + + return NS_OK; + } +}; + +static JSContextParticipant JSContext_cycleCollectorGlobal; + struct Closure { bool cycleCollectionEnabled; @@ -434,10 +475,12 @@ NoteJSChildGrayWrapperShim(void* aData, void* aThing) static const JSZoneParticipant sJSZoneCycleCollectorGlobal; CycleCollectedJSRuntime::CycleCollectedJSRuntime(uint32_t aMaxbytes, - JSUseHelperThreads aUseHelperThreads) + JSUseHelperThreads aUseHelperThreads, + bool aExpectUnrootedGlobals) : mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal), mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal), - mJSRuntime(nullptr) + mJSRuntime(nullptr), + mExpectUnrootedGlobals(aExpectUnrootedGlobals) #ifdef DEBUG , mObjectToUnlink(nullptr) #endif @@ -497,6 +540,23 @@ CycleCollectedJSRuntime::UnmarkSkippableJSHolders() mJSHolders.Enumerate(UnmarkJSHolder, nullptr); } +void +CycleCollectedJSRuntime::MaybeTraceGlobals(JSTracer* aTracer) const +{ + JSContext* iter = nullptr; + while (JSContext* acx = JS_ContextIterator(Runtime(), &iter)) { + MOZ_ASSERT(js::HasUnrootedGlobal(acx) == mExpectUnrootedGlobals); + if (!js::HasUnrootedGlobal(acx)) { + continue; + } + + if (JSObject* global = js::DefaultObjectForContextOrNull(acx)) { + JS::AssertGCThingMustBeTenured(global); + JS_CallObjectTracer(aTracer, &global, "Global Object"); + } + } +} + void CycleCollectedJSRuntime::DescribeGCThing(bool aIsMarked, void* aThing, JSGCTraceKind aTraceKind, @@ -674,9 +734,32 @@ CycleCollectedJSRuntime::TraverseObjectShim(void* aData, void* aThing) JSTRACE_OBJECT, closure->cb); } +// For all JS objects that are held by native objects but aren't held +// through rooting or locking, we need to add all the native objects that +// hold them so that the JS objects are colored correctly in the cycle +// collector. This includes JSContexts that don't have outstanding requests, +// because their global object wasn't marked by the JS GC. All other JS +// roots were marked by the JS GC and will be colored correctly in the cycle +// collector. +void +CycleCollectedJSRuntime::MaybeTraverseGlobals(nsCycleCollectionNoteRootCallback& aCb) const +{ + JSContext *iter = nullptr, *acx; + while ((acx = JS_ContextIterator(Runtime(), &iter))) { + // Add the context to the CC graph only if traversing it would + // end up doing something. + JSObject* global = js::DefaultObjectForContextOrNull(acx); + if (global && xpc_IsGrayGCThing(global)) { + aCb.NoteNativeRoot(acx, JSContextParticipant()); + } + } +} + void CycleCollectedJSRuntime::TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb) { + MaybeTraverseGlobals(aCb); + // NB: This is here just to preserve the existing XPConnect order. I doubt it // would hurt to do this after the JS holders. TraverseAdditionalNativeRoots(aCb); @@ -756,6 +839,8 @@ TraceJSHolder(void* aHolder, nsScriptObjectTracer*& aTracer, void* aArg) void CycleCollectedJSRuntime::TraceNativeGrayRoots(JSTracer* aTracer) { + MaybeTraceGlobals(aTracer); + // NB: This is here just to preserve the existing XPConnect order. I doubt it // would hurt to do this after the JS holders. TraceAdditionalNativeGrayRoots(aTracer); @@ -831,6 +916,13 @@ CycleCollectedJSRuntime::AssertNoObjectsToTrace(void* aPossibleJSHolder) } #endif +// static +nsCycleCollectionParticipant* +CycleCollectedJSRuntime::JSContextParticipant() +{ + return &JSContext_cycleCollectorGlobal; +} + nsCycleCollectionParticipant* CycleCollectedJSRuntime::GCThingParticipant() { @@ -880,18 +972,19 @@ CycleCollectedJSRuntime::UsefulToMergeZones() const JSContext* cx; JSAutoRequest ar(nsContentUtils::GetSafeJSContext()); while ((cx = JS_ContextIterator(mJSRuntime, &iter))) { - // Skip anything without an nsIScriptContext. + // Skip anything without an nsIScriptContext, as well as any scx whose + // NativeGlobal() is not an outer window (this happens with XUL Prototype + // compilation scopes, for example, which we're not interested in). nsIScriptContext* scx = GetScriptContextFromJSContext(cx); - JS::RootedObject obj(cx, scx ? scx->GetWindowProxy() : nullptr); - if (!obj) { + JS::RootedObject global(cx, scx ? scx->GetNativeGlobal() : nullptr); + if (!global || !js::GetObjectParent(global)) { continue; } - MOZ_ASSERT(js::IsOuterObject(obj)); // Grab the inner from the outer. - obj = JS_ObjectToInnerObject(cx, obj); - MOZ_ASSERT(!js::GetObjectParent(obj)); - if (JS::GCThingIsMarkedGray(obj) && - !js::IsSystemCompartment(js::GetObjectCompartment(obj))) { + global = JS_ObjectToInnerObject(cx, global); + MOZ_ASSERT(!js::GetObjectParent(global)); + if (JS::GCThingIsMarkedGray(global) && + !js::IsSystemCompartment(js::GetObjectCompartment(global))) { return true; } } @@ -1094,7 +1187,21 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus) { switch (aStatus) { case JSGC_BEGIN: + { + // XXXkhuey do we still need this? + // We seem to sometime lose the unrooted global flag. Restore it + // here. FIXME: bug 584495. + if (mExpectUnrootedGlobals){ + JSContext* iter = nullptr; + while (JSContext* acx = JS_ContextIterator(Runtime(), &iter)) { + if (!js::HasUnrootedGlobal(acx)) { + JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL); + } + } + } + break; + } case JSGC_END: { /* @@ -1123,5 +1230,11 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus) bool CycleCollectedJSRuntime::OnContext(JSContext* aCx, unsigned aOperation) { + if (mExpectUnrootedGlobals && aOperation == JSCONTEXT_NEW) { + // XXXkhuey bholley is going to make this go away, but for now XPConnect + // needs it. + JS_ToggleOptions(aCx, JSOPTION_UNROOTED_GLOBAL); + } + return CustomContextCallback(aCx, aOperation); } diff --git a/xpcom/base/CycleCollectedJSRuntime.h b/xpcom/base/CycleCollectedJSRuntime.h index bf3ee4773259..eac03ee2d468 100644 --- a/xpcom/base/CycleCollectedJSRuntime.h +++ b/xpcom/base/CycleCollectedJSRuntime.h @@ -83,7 +83,8 @@ class CycleCollectedJSRuntime friend class IncrementalFinalizeRunnable; protected: CycleCollectedJSRuntime(uint32_t aMaxbytes, - JSUseHelperThreads aUseHelperThreads); + JSUseHelperThreads aUseHelperThreads, + bool aExpectUnrootedGlobals); virtual ~CycleCollectedJSRuntime(); JSRuntime* Runtime() const @@ -148,8 +149,13 @@ private: static void TraverseObjectShim(void* aData, void* aThing); + void MaybeTraverseGlobals(nsCycleCollectionNoteRootCallback& aCb) const; + void TraverseNativeRoots(nsCycleCollectionNoteRootCallback& aCb); + void MaybeTraceGlobals(JSTracer* aTracer) const; + + static void TraceBlackJS(JSTracer* aTracer, void* aData); static void TraceGrayJS(JSTracer* aTracer, void* aData); static void GCCallback(JSRuntime* aRuntime, JSGCStatus aStatus, void* aData); @@ -178,6 +184,9 @@ public: void AssertNoObjectsToTrace(void* aPossibleJSHolder); #endif + // This returns the singleton nsCycleCollectionParticipant for JSContexts. + static nsCycleCollectionParticipant* JSContextParticipant(); + nsCycleCollectionParticipant* GCThingParticipant(); nsCycleCollectionParticipant* ZoneParticipant(); @@ -215,6 +224,8 @@ private: nsRefPtr mFinalizeRunnable; + bool mExpectUnrootedGlobals; + #ifdef DEBUG void* mObjectToUnlink; #endif