michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* High level class and public functions implementation. */ michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Base64.h" michael@0: #include "mozilla/Likely.h" michael@0: michael@0: #include "xpcprivate.h" michael@0: #include "XPCWrapper.h" michael@0: #include "jsfriendapi.h" michael@0: #include "js/OldDebugAPI.h" michael@0: #include "nsJSEnvironment.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsDOMJSUtils.h" michael@0: michael@0: #include "WrapperFactory.h" michael@0: #include "AccessCheck.h" michael@0: michael@0: #ifdef MOZ_JSDEBUGGER michael@0: #include "jsdIDebuggerService.h" michael@0: #endif michael@0: michael@0: #include "XPCQuickStubs.h" michael@0: michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include "mozilla/dom/Exceptions.h" michael@0: #include "mozilla/dom/PromiseBinding.h" michael@0: #include "mozilla/dom/TextDecoderBinding.h" michael@0: #include "mozilla/dom/TextEncoderBinding.h" michael@0: #include "mozilla/dom/DOMErrorBinding.h" michael@0: michael@0: #include "nsDOMMutationObserver.h" michael@0: #include "nsICycleCollectorListener.h" michael@0: #include "nsThread.h" michael@0: #include "mozilla/XPTInterfaceInfoManager.h" michael@0: #include "nsIObjectInputStream.h" michael@0: #include "nsIObjectOutputStream.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace xpc; michael@0: using namespace JS; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsXPConnect, michael@0: nsIXPConnect, michael@0: nsISupportsWeakReference, michael@0: nsIThreadObserver, michael@0: nsIJSRuntimeService) michael@0: michael@0: nsXPConnect* nsXPConnect::gSelf = nullptr; michael@0: bool nsXPConnect::gOnceAliveNowDead = false; michael@0: uint32_t nsXPConnect::gReportAllJSExceptions = 0; michael@0: michael@0: bool xpc::gDebugMode = false; michael@0: bool xpc::gDesiredDebugMode = false; michael@0: michael@0: // Global cache of the default script security manager (QI'd to michael@0: // nsIScriptSecurityManager) michael@0: nsIScriptSecurityManager *nsXPConnect::gScriptSecurityManager = nullptr; michael@0: michael@0: const char XPC_CONTEXT_STACK_CONTRACTID[] = "@mozilla.org/js/xpc/ContextStack;1"; michael@0: const char XPC_RUNTIME_CONTRACTID[] = "@mozilla.org/js/xpc/RuntimeService;1"; michael@0: const char XPC_EXCEPTION_CONTRACTID[] = "@mozilla.org/js/xpc/Exception;1"; michael@0: const char XPC_CONSOLE_CONTRACTID[] = "@mozilla.org/consoleservice;1"; michael@0: const char XPC_SCRIPT_ERROR_CONTRACTID[] = "@mozilla.org/scripterror;1"; michael@0: const char XPC_ID_CONTRACTID[] = "@mozilla.org/js/xpc/ID;1"; michael@0: const char XPC_XPCONNECT_CONTRACTID[] = "@mozilla.org/js/xpc/XPConnect;1"; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: nsXPConnect::nsXPConnect() michael@0: : mRuntime(nullptr), michael@0: mShuttingDown(false), michael@0: mEventDepth(0) michael@0: { michael@0: mRuntime = XPCJSRuntime::newXPCJSRuntime(this); michael@0: michael@0: char* reportableEnv = PR_GetEnv("MOZ_REPORT_ALL_JS_EXCEPTIONS"); michael@0: if (reportableEnv && *reportableEnv) michael@0: gReportAllJSExceptions = 1; michael@0: } michael@0: michael@0: nsXPConnect::~nsXPConnect() michael@0: { michael@0: mRuntime->DeleteSingletonScopes(); michael@0: mRuntime->DestroyJSContextStack(); michael@0: michael@0: // In order to clean up everything properly, we need to GC twice: once now, michael@0: // to clean anything that can go away on its own (like the Junk Scope, which michael@0: // we unrooted above), and once after forcing a bunch of shutdown in michael@0: // XPConnect, to clean the stuff we forcibly disconnected. The forced michael@0: // shutdown code defaults to leaking in a number of situations, so we can't michael@0: // get by with only the second GC. :-( michael@0: JS_GC(mRuntime->Runtime()); michael@0: michael@0: mShuttingDown = true; michael@0: XPCWrappedNativeScope::SystemIsBeingShutDown(); michael@0: mRuntime->SystemIsBeingShutDown(); michael@0: michael@0: // The above causes us to clean up a bunch of XPConnect data structures, michael@0: // after which point we need to GC to clean everything up. We need to do michael@0: // this before deleting the XPCJSRuntime, because doing so destroys the michael@0: // maps that our finalize callback depends on. michael@0: JS_GC(mRuntime->Runtime()); michael@0: michael@0: mDefaultSecurityManager = nullptr; michael@0: gScriptSecurityManager = nullptr; michael@0: michael@0: // shutdown the logging system michael@0: XPC_LOG_FINISH(); michael@0: michael@0: delete mRuntime; michael@0: michael@0: gSelf = nullptr; michael@0: gOnceAliveNowDead = true; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsXPConnect::InitStatics() michael@0: { michael@0: gSelf = new nsXPConnect(); michael@0: gOnceAliveNowDead = false; michael@0: if (!gSelf->mRuntime) { michael@0: NS_RUNTIMEABORT("Couldn't create XPCJSRuntime."); michael@0: } michael@0: michael@0: // Initial extra ref to keep the singleton alive michael@0: // balanced by explicit call to ReleaseXPConnectSingleton() michael@0: NS_ADDREF(gSelf); michael@0: michael@0: // Set XPConnect as the main thread observer. michael@0: if (NS_FAILED(nsThread::SetMainThreadObserver(gSelf))) { michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: nsXPConnect* michael@0: nsXPConnect::GetSingleton() michael@0: { michael@0: nsXPConnect* xpc = nsXPConnect::XPConnect(); michael@0: NS_IF_ADDREF(xpc); michael@0: return xpc; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsXPConnect::ReleaseXPConnectSingleton() michael@0: { michael@0: nsXPConnect* xpc = gSelf; michael@0: if (xpc) { michael@0: nsThread::SetMainThreadObserver(nullptr); michael@0: michael@0: nsrefcnt cnt; michael@0: NS_RELEASE2(xpc, cnt); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: XPCJSRuntime* michael@0: nsXPConnect::GetRuntimeInstance() michael@0: { michael@0: nsXPConnect* xpc = XPConnect(); michael@0: return xpc->GetRuntime(); michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: nsXPConnect::IsISupportsDescendant(nsIInterfaceInfo* info) michael@0: { michael@0: bool found = false; michael@0: if (info) michael@0: info->HasAncestor(&NS_GET_IID(nsISupports), &found); michael@0: return found; michael@0: } michael@0: michael@0: void michael@0: xpc::SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep) michael@0: { michael@0: // It would be nice to assert !DescribeScriptedCaller here, to be sure michael@0: // that there isn't any script running that could catch the exception. But michael@0: // the JS engine invokes the error reporter directly if someone reports an michael@0: // ErrorReport that it doesn't know how to turn into an exception. Arguably michael@0: // it should just learn how to throw everything. But either way, if the michael@0: // exception is ending here, it's not going to get propagated to a caller, michael@0: // so it's up to us to make it known. michael@0: michael@0: nsresult rv; michael@0: michael@0: /* Use the console service to register the error. */ michael@0: nsCOMPtr consoleService = michael@0: do_GetService(NS_CONSOLESERVICE_CONTRACTID); michael@0: michael@0: /* michael@0: * Make an nsIScriptError, populate it with information from this michael@0: * error, then log it with the console service. michael@0: */ michael@0: nsCOMPtr errorObject = michael@0: do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); michael@0: michael@0: if (consoleService && errorObject) { michael@0: uint32_t column = rep->uctokenptr - rep->uclinebuf; michael@0: michael@0: const char16_t* ucmessage = michael@0: static_cast(rep->ucmessage); michael@0: const char16_t* uclinebuf = michael@0: static_cast(rep->uclinebuf); michael@0: michael@0: rv = errorObject->Init( michael@0: ucmessage ? nsDependentString(ucmessage) : EmptyString(), michael@0: NS_ConvertASCIItoUTF16(rep->filename), michael@0: uclinebuf ? nsDependentString(uclinebuf) : EmptyString(), michael@0: rep->lineno, column, rep->flags, michael@0: "system javascript"); michael@0: if (NS_SUCCEEDED(rv)) michael@0: consoleService->LogMessage(errorObject); michael@0: } michael@0: michael@0: if (nsContentUtils::DOMWindowDumpEnabled()) { michael@0: fprintf(stderr, "System JS : %s %s:%d - %s\n", michael@0: JSREPORT_IS_WARNING(rep->flags) ? "WARNING" : "ERROR", michael@0: rep->filename, rep->lineno, michael@0: message ? message : ""); michael@0: } michael@0: michael@0: } michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: michael@0: nsresult michael@0: nsXPConnect::GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info) michael@0: { michael@0: return XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(aIID, info); michael@0: } michael@0: michael@0: nsresult michael@0: nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info) michael@0: { michael@0: nsresult rv = XPTInterfaceInfoManager::GetSingleton()->GetInfoForName(name, info); michael@0: return NS_FAILED(rv) ? NS_OK : NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::GarbageCollect(uint32_t reason) michael@0: { michael@0: GetRuntime()->Collect(reason); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: xpc_GCThingIsGrayCCThing(void *thing) michael@0: { michael@0: return AddToCCKind(js::GCThingTraceKind(thing)) && michael@0: xpc_IsGrayGCThing(thing); michael@0: } michael@0: michael@0: void michael@0: xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration) michael@0: { michael@0: nsCOMPtr variant = do_QueryInterface(aVariant); michael@0: if (variant) { michael@0: variant->SetCCGeneration(aGeneration); michael@0: variant->GetJSVal(); // Unmarks gray JSObject. michael@0: XPCVariant* weak = variant.get(); michael@0: variant = nullptr; michael@0: if (weak->IsPurple()) { michael@0: weak->RemovePurple(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS) michael@0: { michael@0: nsCOMPtr wjs = do_QueryInterface(aWrappedJS); michael@0: if (wjs) { michael@0: // Unmarks gray JSObject. michael@0: static_cast(wjs.get())->GetJSObject(); michael@0: } michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: /***************************************************************************/ michael@0: // nsIXPConnect interface methods... michael@0: michael@0: template michael@0: static inline T UnexpectedFailure(T rv) michael@0: { michael@0: NS_ERROR("This is not supposed to fail!"); michael@0: return rv; michael@0: } michael@0: michael@0: /* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj) michael@0: { michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aGlobalJSObj, "bad param"); michael@0: RootedObject globalJSObj(aJSContext, aGlobalJSObj); michael@0: michael@0: JSAutoCompartment ac(aJSContext, globalJSObj); michael@0: michael@0: XPCWrappedNativeScope* scope = michael@0: XPCWrappedNativeScope::GetNewOrUsed(aJSContext, globalJSObj); michael@0: michael@0: if (!scope) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: scope->RemoveWrappedNativeProtos(); michael@0: michael@0: if (!XPCNativeWrapper::AttachNewConstructorObject(aJSContext, globalJSObj)) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: VerifyTraceXPCGlobalCalled(JSTracer *trc, void **thingp, JSGCTraceKind kind) michael@0: { michael@0: // We don't do anything here, we only want to verify that TraceXPCGlobal michael@0: // was called. michael@0: } michael@0: michael@0: struct VerifyTraceXPCGlobalCalledTracer : public JSTracer michael@0: { michael@0: bool ok; michael@0: michael@0: VerifyTraceXPCGlobalCalledTracer(JSRuntime *rt) michael@0: : JSTracer(rt, VerifyTraceXPCGlobalCalled), ok(false) michael@0: {} michael@0: }; michael@0: #endif michael@0: michael@0: void michael@0: xpc::TraceXPCGlobal(JSTracer *trc, JSObject *obj) michael@0: { michael@0: #ifdef DEBUG michael@0: if (trc->callback == VerifyTraceXPCGlobalCalled) { michael@0: // We don't do anything here, we only want to verify that TraceXPCGlobal michael@0: // was called. michael@0: reinterpret_cast(trc)->ok = true; michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: if (js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL) michael@0: mozilla::dom::TraceProtoAndIfaceCache(trc, obj); michael@0: michael@0: // We might be called from a GC during the creation of a global, before we've michael@0: // been able to set up the compartment private or the XPCWrappedNativeScope, michael@0: // so we need to null-check those. michael@0: xpc::CompartmentPrivate* compartmentPrivate = GetCompartmentPrivate(obj); michael@0: if (compartmentPrivate && compartmentPrivate->scope) michael@0: compartmentPrivate->scope->TraceInside(trc); michael@0: } michael@0: michael@0: namespace xpc { michael@0: michael@0: JSObject* michael@0: CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal, michael@0: JS::CompartmentOptions& aOptions) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "using a principal off the main thread?"); michael@0: MOZ_ASSERT(principal); michael@0: michael@0: RootedObject global(cx, michael@0: JS_NewGlobalObject(cx, clasp, nsJSPrincipals::get(principal), michael@0: JS::DontFireOnNewGlobalHook, aOptions)); michael@0: if (!global) michael@0: return nullptr; michael@0: JSAutoCompartment ac(cx, global); michael@0: michael@0: // The constructor automatically attaches the scope to the compartment private michael@0: // of |global|. michael@0: (void) new XPCWrappedNativeScope(cx, global); michael@0: michael@0: #ifdef DEBUG michael@0: // Verify that the right trace hook is called. Note that this doesn't michael@0: // work right for wrapped globals, since the tracing situation there is michael@0: // more complicated. Manual inspection shows that they do the right thing. michael@0: if (!((const js::Class*)clasp)->ext.isWrappedNative) michael@0: { michael@0: VerifyTraceXPCGlobalCalledTracer trc(JS_GetRuntime(cx)); michael@0: JS_TraceChildren(&trc, global, JSTRACE_OBJECT); michael@0: MOZ_ASSERT(trc.ok, "Trace hook on global needs to call TraceXPCGlobal for XPConnect compartments."); michael@0: } michael@0: #endif michael@0: michael@0: if (clasp->flags & JSCLASS_DOM_GLOBAL) { michael@0: const char* className = clasp->name; michael@0: AllocateProtoAndIfaceCache(global, michael@0: (strcmp(className, "Window") == 0 || michael@0: strcmp(className, "ChromeWindow") == 0) michael@0: ? ProtoAndIfaceCache::WindowLike michael@0: : ProtoAndIfaceCache::NonWindowLike); michael@0: } michael@0: michael@0: return global; michael@0: } michael@0: michael@0: bool michael@0: InitGlobalObject(JSContext* aJSContext, JS::Handle aGlobal, uint32_t aFlags) michael@0: { michael@0: // Immediately enter the global's compartment, so that everything else we michael@0: // create ends up there. michael@0: JSAutoCompartment ac(aJSContext, aGlobal); michael@0: if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) { michael@0: // XPCCallContext gives us an active request needed to save/restore. michael@0: if (!GetCompartmentPrivate(aGlobal)->scope->AttachComponentsObject(aJSContext) || michael@0: !XPCNativeWrapper::AttachNewConstructorObject(aJSContext, aGlobal)) { michael@0: return UnexpectedFailure(false); michael@0: } michael@0: } michael@0: michael@0: if (ShouldDiscardSystemSource()) { michael@0: nsIPrincipal *prin = GetObjectPrincipal(aGlobal); michael@0: bool isSystem = nsContentUtils::IsSystemPrincipal(prin); michael@0: if (!isSystem) { michael@0: short status = prin->GetAppStatus(); michael@0: isSystem = status == nsIPrincipal::APP_STATUS_PRIVILEGED || michael@0: status == nsIPrincipal::APP_STATUS_CERTIFIED; michael@0: } michael@0: JS::CompartmentOptionsRef(aGlobal).setDiscardSource(isSystem); michael@0: } michael@0: michael@0: // Stuff coming through this path always ends up as a DOM global. michael@0: MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL); michael@0: michael@0: // Init WebIDL binding constructors wanted on all XPConnect globals. michael@0: // michael@0: // XXX Please do not add any additional classes here without the approval of michael@0: // the XPConnect module owner. michael@0: if (!PromiseBinding::GetConstructorObject(aJSContext, aGlobal) || michael@0: !TextDecoderBinding::GetConstructorObject(aJSContext, aGlobal) || michael@0: !TextEncoderBinding::GetConstructorObject(aJSContext, aGlobal) || michael@0: !DOMErrorBinding::GetConstructorObject(aJSContext, aGlobal)) { michael@0: return UnexpectedFailure(false); michael@0: } michael@0: michael@0: if (!(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK)) michael@0: JS_FireOnNewGlobalObject(aJSContext, aGlobal); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // namespace xpc michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, michael@0: nsISupports *aCOMObj, michael@0: nsIPrincipal * aPrincipal, michael@0: uint32_t aFlags, michael@0: JS::CompartmentOptions& aOptions, michael@0: nsIXPConnectJSObjectHolder **_retval) michael@0: { michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aCOMObj, "bad param"); michael@0: MOZ_ASSERT(_retval, "bad param"); michael@0: michael@0: // We pass null for the 'extra' pointer during global object creation, so michael@0: // we need to have a principal. michael@0: MOZ_ASSERT(aPrincipal); michael@0: michael@0: // Call into XPCWrappedNative to make a new global object, scope, and global michael@0: // prototype. michael@0: xpcObjectHelper helper(aCOMObj); michael@0: MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT); michael@0: nsRefPtr wrappedGlobal; michael@0: nsresult rv = michael@0: XPCWrappedNative::WrapNewGlobal(helper, aPrincipal, michael@0: aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES, michael@0: aOptions, getter_AddRefs(wrappedGlobal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Grab a copy of the global and enter its compartment. michael@0: RootedObject global(aJSContext, wrappedGlobal->GetFlatJSObject()); michael@0: MOZ_ASSERT(!js::GetObjectParent(global)); michael@0: michael@0: if (!InitGlobalObject(aJSContext, global, aFlags)) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: wrappedGlobal.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: NativeInterface2JSObject(HandleObject aScope, michael@0: nsISupports *aCOMObj, michael@0: nsWrapperCache *aCache, michael@0: const nsIID * aIID, michael@0: bool aAllowWrapping, michael@0: MutableHandleValue aVal, michael@0: nsIXPConnectJSObjectHolder **aHolder) michael@0: { michael@0: AutoJSContext cx; michael@0: JSAutoCompartment ac(cx, aScope); michael@0: michael@0: nsresult rv; michael@0: xpcObjectHelper helper(aCOMObj, aCache); michael@0: if (!XPCConvert::NativeInterface2JSObject(aVal, aHolder, helper, aIID, michael@0: nullptr, aAllowWrapping, &rv)) michael@0: return rv; michael@0: michael@0: MOZ_ASSERT(aAllowWrapping || !xpc::WrapperFactory::IsXrayWrapper(&aVal.toObject()), michael@0: "Shouldn't be returning a xray wrapper here"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIXPConnectJSObjectHolder wrapNative (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDRef aIID); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::WrapNative(JSContext * aJSContext, michael@0: JSObject * aScopeArg, michael@0: nsISupports *aCOMObj, michael@0: const nsIID & aIID, michael@0: nsIXPConnectJSObjectHolder **aHolder) michael@0: { michael@0: MOZ_ASSERT(aHolder, "bad param"); michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aScopeArg, "bad param"); michael@0: MOZ_ASSERT(aCOMObj, "bad param"); michael@0: michael@0: RootedObject aScope(aJSContext, aScopeArg); michael@0: RootedValue v(aJSContext); michael@0: return NativeInterface2JSObject(aScope, aCOMObj, nullptr, &aIID, michael@0: true, &v, aHolder); michael@0: } michael@0: michael@0: /* void wrapNativeToJSVal (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDPtr aIID, out jsval aVal, out nsIXPConnectJSObjectHolder aHolder); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::WrapNativeToJSVal(JSContext *aJSContext, michael@0: JSObject *aScopeArg, michael@0: nsISupports *aCOMObj, michael@0: nsWrapperCache *aCache, michael@0: const nsIID *aIID, michael@0: bool aAllowWrapping, michael@0: MutableHandleValue aVal) michael@0: { michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aScopeArg, "bad param"); michael@0: MOZ_ASSERT(aCOMObj, "bad param"); michael@0: michael@0: RootedObject aScope(aJSContext, aScopeArg); michael@0: return NativeInterface2JSObject(aScope, aCOMObj, aCache, aIID, michael@0: aAllowWrapping, aVal, nullptr); michael@0: } michael@0: michael@0: /* void wrapJS (in JSContextPtr aJSContext, in JSObjectPtr aJSObj, in nsIIDRef aIID, [iid_is (aIID), retval] out nsQIResult result); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::WrapJS(JSContext * aJSContext, michael@0: JSObject * aJSObjArg, michael@0: const nsIID & aIID, michael@0: void * *result) michael@0: { michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aJSObjArg, "bad param"); michael@0: MOZ_ASSERT(result, "bad param"); michael@0: michael@0: *result = nullptr; michael@0: michael@0: RootedObject aJSObj(aJSContext, aJSObjArg); michael@0: JSAutoCompartment ac(aJSContext, aJSObj); michael@0: michael@0: nsresult rv = NS_ERROR_UNEXPECTED; michael@0: if (!XPCConvert::JSObject2NativeInterface(result, aJSObj, michael@0: &aIID, nullptr, &rv)) michael@0: return rv; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::JSValToVariant(JSContext *cx, michael@0: HandleValue aJSVal, michael@0: nsIVariant **aResult) michael@0: { michael@0: NS_PRECONDITION(aResult, "bad param"); michael@0: michael@0: nsRefPtr variant = XPCVariant::newVariant(cx, aJSVal); michael@0: variant.forget(aResult); michael@0: NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void wrapJSAggregatedToNative (in nsISupports aOuter, in JSContextPtr aJSContext, in JSObjectPtr aJSObj, in nsIIDRef aIID, [iid_is (aIID), retval] out nsQIResult result); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::WrapJSAggregatedToNative(nsISupports *aOuter, michael@0: JSContext *aJSContext, michael@0: JSObject *aJSObjArg, michael@0: const nsIID &aIID, michael@0: void **result) michael@0: { michael@0: MOZ_ASSERT(aOuter, "bad param"); michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aJSObjArg, "bad param"); michael@0: MOZ_ASSERT(result, "bad param"); michael@0: michael@0: *result = nullptr; michael@0: michael@0: RootedObject aJSObj(aJSContext, aJSObjArg); michael@0: nsresult rv; michael@0: if (!XPCConvert::JSObject2NativeInterface(result, aJSObj, michael@0: &aIID, aOuter, &rv)) michael@0: return rv; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIXPConnectWrappedNative getWrappedNativeOfJSObject (in JSContextPtr aJSContext, in JSObjectPtr aJSObj); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::GetWrappedNativeOfJSObject(JSContext * aJSContext, michael@0: JSObject * aJSObjArg, michael@0: nsIXPConnectWrappedNative **_retval) michael@0: { michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aJSObjArg, "bad param"); michael@0: MOZ_ASSERT(_retval, "bad param"); michael@0: michael@0: RootedObject aJSObj(aJSContext, aJSObjArg); michael@0: aJSObj = js::CheckedUnwrap(aJSObj, /* stopAtOuter = */ false); michael@0: if (!aJSObj || !IS_WN_REFLECTOR(aJSObj)) { michael@0: *_retval = nullptr; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr temp = XPCWrappedNative::Get(aJSObj); michael@0: temp.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsISupports getNativeOfWrapper(in JSContextPtr aJSContext, in JSObjectPtr aJSObj); */ michael@0: NS_IMETHODIMP_(nsISupports*) michael@0: nsXPConnect::GetNativeOfWrapper(JSContext *aJSContext, michael@0: JSObject *aJSObj) michael@0: { michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aJSObj, "bad param"); michael@0: michael@0: aJSObj = js::CheckedUnwrap(aJSObj, /* stopAtOuter = */ false); michael@0: if (!aJSObj) { michael@0: JS_ReportError(aJSContext, "Permission denied to get native of security wrapper"); michael@0: return nullptr; michael@0: } michael@0: if (IS_WN_REFLECTOR(aJSObj)) { michael@0: if (XPCWrappedNative *wn = XPCWrappedNative::Get(aJSObj)) michael@0: return wn->Native(); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr canonical = michael@0: do_QueryInterface(mozilla::dom::UnwrapDOMObjectToISupports(aJSObj)); michael@0: return canonical; michael@0: } michael@0: michael@0: /* nsIXPConnectWrappedNative getWrappedNativeOfNativeObject (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDRef aIID); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::GetWrappedNativeOfNativeObject(JSContext * aJSContext, michael@0: JSObject * aScopeArg, michael@0: nsISupports *aCOMObj, michael@0: const nsIID & aIID, michael@0: nsIXPConnectWrappedNative **_retval) michael@0: { michael@0: MOZ_ASSERT(aJSContext, "bad param"); michael@0: MOZ_ASSERT(aScopeArg, "bad param"); michael@0: MOZ_ASSERT(aCOMObj, "bad param"); michael@0: MOZ_ASSERT(_retval, "bad param"); michael@0: michael@0: *_retval = nullptr; michael@0: michael@0: RootedObject aScope(aJSContext, aScopeArg); michael@0: michael@0: XPCWrappedNativeScope* scope = GetObjectScope(aScope); michael@0: if (!scope) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: AutoMarkingNativeInterfacePtr iface(aJSContext); michael@0: iface = XPCNativeInterface::GetNewOrUsed(&aIID); michael@0: if (!iface) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: XPCWrappedNative* wrapper; michael@0: michael@0: nsresult rv = XPCWrappedNative::GetUsedOnly(aCOMObj, scope, iface, &wrapper); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: *_retval = static_cast(wrapper); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void reparentWrappedNativeIfFound (in JSContextPtr aJSContext, michael@0: * in JSObjectPtr aScope, michael@0: * in JSObjectPtr aNewParent, michael@0: * in nsISupports aCOMObj); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::ReparentWrappedNativeIfFound(JSContext * aJSContext, michael@0: JSObject * aScopeArg, michael@0: JSObject * aNewParentArg, michael@0: nsISupports *aCOMObj) michael@0: { michael@0: RootedObject aScope(aJSContext, aScopeArg); michael@0: RootedObject aNewParent(aJSContext, aNewParentArg); michael@0: michael@0: XPCWrappedNativeScope* scope = GetObjectScope(aScope); michael@0: XPCWrappedNativeScope* scope2 = GetObjectScope(aNewParent); michael@0: if (!scope || !scope2) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: RootedObject newParent(aJSContext, aNewParent); michael@0: return XPCWrappedNative:: michael@0: ReparentWrapperIfFound(scope, scope2, newParent, aCOMObj); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: MoveableWrapperFinder(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: nsTArray > *array = michael@0: static_cast > *>(arg); michael@0: XPCWrappedNative *wn = ((Native2WrappedNativeMap::Entry*)hdr)->value; michael@0: michael@0: // If a wrapper is expired, then there are no references to it from JS, so michael@0: // we don't have to move it. michael@0: if (!wn->IsWrapperExpired()) michael@0: array->AppendElement(wn); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: /* void rescueOrphansInScope(in JSContextPtr aJSContext, in JSObjectPtr aScope); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::RescueOrphansInScope(JSContext *aJSContext, JSObject *aScopeArg) michael@0: { michael@0: RootedObject aScope(aJSContext, aScopeArg); michael@0: michael@0: XPCWrappedNativeScope *scope = GetObjectScope(aScope); michael@0: if (!scope) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: // First, look through the old scope and find all of the wrappers that we michael@0: // might need to rescue. michael@0: nsTArray > wrappersToMove; michael@0: michael@0: Native2WrappedNativeMap *map = scope->GetWrappedNativeMap(); michael@0: wrappersToMove.SetCapacity(map->Count()); michael@0: map->Enumerate(MoveableWrapperFinder, &wrappersToMove); michael@0: michael@0: // Now that we have the wrappers, reparent them to the new scope. michael@0: for (uint32_t i = 0, stop = wrappersToMove.Length(); i < stop; ++i) { michael@0: nsresult rv = wrappersToMove[i]->RescueOrphans(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void setDefaultSecurityManager (in nsIXPCSecurityManager aManager); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::SetDefaultSecurityManager(nsIXPCSecurityManager *aManager) michael@0: { michael@0: mDefaultSecurityManager = aManager; michael@0: michael@0: nsCOMPtr ssm = michael@0: do_QueryInterface(mDefaultSecurityManager); michael@0: michael@0: // Remember the result of the above QI for fast access to the michael@0: // script securityt manager. michael@0: gScriptSecurityManager = ssm; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIStackFrame createStackFrameLocation (in uint32_t aLanguage, in string aFilename, in string aFunctionName, in int32_t aLineNumber, in nsIStackFrame aCaller); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::CreateStackFrameLocation(uint32_t aLanguage, michael@0: const char *aFilename, michael@0: const char *aFunctionName, michael@0: int32_t aLineNumber, michael@0: nsIStackFrame *aCaller, michael@0: nsIStackFrame **_retval) michael@0: { michael@0: MOZ_ASSERT(_retval, "bad param"); michael@0: michael@0: nsCOMPtr stackFrame = michael@0: exceptions::CreateStackFrameLocation(aLanguage, michael@0: aFilename, michael@0: aFunctionName, michael@0: aLineNumber, michael@0: aCaller); michael@0: stackFrame.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute nsIStackFrame CurrentJSStack; */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::GetCurrentJSStack(nsIStackFrame * *aCurrentJSStack) michael@0: { michael@0: MOZ_ASSERT(aCurrentJSStack, "bad param"); michael@0: michael@0: nsCOMPtr currentStack = dom::GetCurrentJSStack(); michael@0: currentStack.forget(aCurrentJSStack); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute nsIXPCNativeCallContext CurrentNativeCallContext; */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::GetCurrentNativeCallContext(nsAXPCNativeCallContext * *aCurrentNativeCallContext) michael@0: { michael@0: MOZ_ASSERT(aCurrentNativeCallContext, "bad param"); michael@0: michael@0: *aCurrentNativeCallContext = XPCJSRuntime::Get()->GetCallContext(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void setFunctionThisTranslator (in nsIIDRef aIID, in nsIXPCFunctionThisTranslator aTranslator); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::SetFunctionThisTranslator(const nsIID & aIID, michael@0: nsIXPCFunctionThisTranslator *aTranslator) michael@0: { michael@0: XPCJSRuntime* rt = GetRuntime(); michael@0: IID2ThisTranslatorMap* map = rt->GetThisTranslatorMap(); michael@0: map->Add(aIID, aTranslator); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::CreateSandbox(JSContext *cx, nsIPrincipal *principal, michael@0: nsIXPConnectJSObjectHolder **_retval) michael@0: { michael@0: *_retval = nullptr; michael@0: michael@0: RootedValue rval(cx); michael@0: SandboxOptions options; michael@0: nsresult rv = CreateSandboxObject(cx, &rval, principal, options); michael@0: MOZ_ASSERT(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval), michael@0: "Bad return value from xpc_CreateSandboxObject()!"); michael@0: michael@0: if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(rval)) { michael@0: *_retval = XPCJSObjectHolder::newHolder(JSVAL_TO_OBJECT(rval)); michael@0: NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: NS_ADDREF(*_retval); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::EvalInSandboxObject(const nsAString& source, const char *filename, michael@0: JSContext *cx, JSObject *sandboxArg, michael@0: bool returnStringOnly, MutableHandleValue rval) michael@0: { michael@0: if (!sandboxArg) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: RootedObject sandbox(cx, sandboxArg); michael@0: nsCString filenameStr; michael@0: if (filename) { michael@0: filenameStr.Assign(filename); michael@0: } else { michael@0: filenameStr = NS_LITERAL_CSTRING("x-bogus://XPConnect/Sandbox"); michael@0: } michael@0: return EvalInSandbox(cx, sandbox, source, filenameStr, 1, michael@0: JSVERSION_DEFAULT, returnStringOnly, rval); michael@0: } michael@0: michael@0: /* nsIXPConnectJSObjectHolder getWrappedNativePrototype (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsIClassInfo aClassInfo); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, michael@0: JSObject * aScopeArg, michael@0: nsIClassInfo *aClassInfo, michael@0: nsIXPConnectJSObjectHolder **_retval) michael@0: { michael@0: RootedObject aScope(aJSContext, aScopeArg); michael@0: JSAutoCompartment ac(aJSContext, aScope); michael@0: michael@0: XPCWrappedNativeScope* scope = GetObjectScope(aScope); michael@0: if (!scope) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: XPCNativeScriptableCreateInfo sciProto; michael@0: XPCWrappedNative::GatherProtoScriptableCreateInfo(aClassInfo, sciProto); michael@0: michael@0: AutoMarkingWrappedNativeProtoPtr proto(aJSContext); michael@0: proto = XPCWrappedNativeProto::GetNewOrUsed(scope, aClassInfo, &sciProto); michael@0: if (!proto) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: nsIXPConnectJSObjectHolder* holder; michael@0: *_retval = holder = XPCJSObjectHolder::newHolder(proto->GetJSProtoObject()); michael@0: if (!holder) michael@0: return UnexpectedFailure(NS_ERROR_FAILURE); michael@0: michael@0: NS_ADDREF(holder); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void debugDump (in short depth); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::DebugDump(int16_t depth) michael@0: { michael@0: #ifdef DEBUG michael@0: depth-- ; michael@0: XPC_LOG_ALWAYS(("nsXPConnect @ %x with mRefCnt = %d", this, mRefCnt.get())); michael@0: XPC_LOG_INDENT(); michael@0: XPC_LOG_ALWAYS(("gSelf @ %x", gSelf)); michael@0: XPC_LOG_ALWAYS(("gOnceAliveNowDead is %d", (int)gOnceAliveNowDead)); michael@0: XPC_LOG_ALWAYS(("mDefaultSecurityManager @ %x", mDefaultSecurityManager.get())); michael@0: if (mRuntime) { michael@0: if (depth) michael@0: mRuntime->DebugDump(depth); michael@0: else michael@0: XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", mRuntime)); michael@0: } else michael@0: XPC_LOG_ALWAYS(("mRuntime is null")); michael@0: XPCWrappedNativeScope::DebugDumpAllScopes(depth); michael@0: XPC_LOG_OUTDENT(); michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void debugDumpObject (in nsISupports aCOMObj, in short depth); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::DebugDumpObject(nsISupports *p, int16_t depth) michael@0: { michael@0: #ifdef DEBUG michael@0: if (!depth) michael@0: return NS_OK; michael@0: if (!p) { michael@0: XPC_LOG_ALWAYS(("*** Cound not dump object with NULL address")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIXPConnect* xpc; michael@0: nsIXPCWrappedJSClass* wjsc; michael@0: nsIXPConnectWrappedNative* wn; michael@0: nsIXPConnectWrappedJS* wjs; michael@0: michael@0: if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnect), michael@0: (void**)&xpc))) { michael@0: XPC_LOG_ALWAYS(("Dumping a nsIXPConnect...")); michael@0: xpc->DebugDump(depth); michael@0: NS_RELEASE(xpc); michael@0: } else if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPCWrappedJSClass), michael@0: (void**)&wjsc))) { michael@0: XPC_LOG_ALWAYS(("Dumping a nsIXPCWrappedJSClass...")); michael@0: wjsc->DebugDump(depth); michael@0: NS_RELEASE(wjsc); michael@0: } else if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnectWrappedNative), michael@0: (void**)&wn))) { michael@0: XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedNative...")); michael@0: wn->DebugDump(depth); michael@0: NS_RELEASE(wn); michael@0: } else if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnectWrappedJS), michael@0: (void**)&wjs))) { michael@0: XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedJS...")); michael@0: wjs->DebugDump(depth); michael@0: NS_RELEASE(wjs); michael@0: } else michael@0: XPC_LOG_ALWAYS(("*** Could not dump the nsISupports @ %x", p)); michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void debugDumpJSStack (in bool showArgs, in bool showLocals, in bool showThisProps); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::DebugDumpJSStack(bool showArgs, michael@0: bool showLocals, michael@0: bool showThisProps) michael@0: { michael@0: JSContext* cx = GetCurrentJSContext(); michael@0: if (!cx) michael@0: printf("there is no JSContext on the nsIThreadJSContextStack!\n"); michael@0: else michael@0: xpc_DumpJSStack(cx, showArgs, showLocals, showThisProps); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: char* michael@0: nsXPConnect::DebugPrintJSStack(bool showArgs, michael@0: bool showLocals, michael@0: bool showThisProps) michael@0: { michael@0: JSContext* cx = GetCurrentJSContext(); michael@0: if (!cx) michael@0: printf("there is no JSContext on the nsIThreadJSContextStack!\n"); michael@0: else michael@0: return xpc_PrintJSStack(cx, showArgs, showLocals, showThisProps); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* void debugDumpEvalInJSStackFrame (in uint32_t aFrameNumber, in string aSourceText); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::DebugDumpEvalInJSStackFrame(uint32_t aFrameNumber, const char *aSourceText) michael@0: { michael@0: JSContext* cx = GetCurrentJSContext(); michael@0: if (!cx) michael@0: printf("there is no JSContext on the nsIThreadJSContextStack!\n"); michael@0: else michael@0: xpc_DumpEvalInJSStackFrame(cx, aFrameNumber, aSourceText); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* jsval variantToJS (in JSContextPtr ctx, in JSObjectPtr scope, in nsIVariant value); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::VariantToJS(JSContext* ctx, JSObject* scopeArg, nsIVariant* value, michael@0: MutableHandleValue _retval) michael@0: { michael@0: NS_PRECONDITION(ctx, "bad param"); michael@0: NS_PRECONDITION(scopeArg, "bad param"); michael@0: NS_PRECONDITION(value, "bad param"); michael@0: michael@0: RootedObject scope(ctx, scopeArg); michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(scope, ctx)); michael@0: michael@0: nsresult rv = NS_OK; michael@0: if (!XPCVariant::VariantDataToJS(value, &rv, _retval)) { michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* nsIVariant JSToVariant (in JSContextPtr ctx, in jsval value); */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::JSToVariant(JSContext* ctx, HandleValue value, nsIVariant** _retval) michael@0: { michael@0: NS_PRECONDITION(ctx, "bad param"); michael@0: NS_PRECONDITION(_retval, "bad param"); michael@0: michael@0: nsRefPtr variant = XPCVariant::newVariant(ctx, value); michael@0: variant.forget(_retval); michael@0: if (!(*_retval)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait, michael@0: uint32_t aRecursionDepth) michael@0: { michael@0: // Record this event. michael@0: mEventDepth++; michael@0: michael@0: // Push a null JSContext so that we don't see any script during michael@0: // event processing. michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: bool ok = PushJSContextNoScriptContext(nullptr); michael@0: NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::AfterProcessNextEvent(nsIThreadInternal *aThread, michael@0: uint32_t aRecursionDepth, michael@0: bool aEventWasProcessed) michael@0: { michael@0: // Watch out for unpaired events during observer registration. michael@0: if (MOZ_UNLIKELY(mEventDepth == 0)) michael@0: return NS_OK; michael@0: mEventDepth--; michael@0: michael@0: // Now that we're back to the event loop, reset the slow script checkpoint. michael@0: mRuntime->OnAfterProcessNextEvent(); michael@0: michael@0: // Call cycle collector occasionally. michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: nsJSContext::MaybePokeCC(); michael@0: nsDOMMutationObserver::HandleMutations(); michael@0: michael@0: PopJSContextNoScriptContext(); michael@0: michael@0: // If the cx stack is empty, that means we're at the an un-nested event michael@0: // loop. This is a good time to make changes to debug mode. michael@0: if (XPCJSRuntime::Get()->GetJSContextStack()->Count() == 0) { michael@0: MOZ_ASSERT(mEventDepth == 0); michael@0: CheckForDebugMode(XPCJSRuntime::Get()->Runtime()); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread) michael@0: { michael@0: NS_NOTREACHED("Why tell us?"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::SetReportAllJSExceptions(bool newval) michael@0: { michael@0: // Ignore if the environment variable was set. michael@0: if (gReportAllJSExceptions != 1) michael@0: gReportAllJSExceptions = newval ? 2 : 0; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* attribute JSRuntime runtime; */ michael@0: NS_IMETHODIMP michael@0: nsXPConnect::GetRuntime(JSRuntime **runtime) michael@0: { michael@0: if (!runtime) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: JSRuntime *rt = GetRuntime()->Runtime(); michael@0: JS_AbortIfWrongThread(rt); michael@0: *runtime = rt; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* [noscript, notxpcom] void registerGCCallback(in xpcGCCallback func); */ michael@0: NS_IMETHODIMP_(void) michael@0: nsXPConnect::RegisterGCCallback(xpcGCCallback func) michael@0: { michael@0: mRuntime->AddGCCallback(func); michael@0: } michael@0: michael@0: /* [noscript, notxpcom] void unregisterGCCallback(in xpcGCCallback func); */ michael@0: NS_IMETHODIMP_(void) michael@0: nsXPConnect::UnregisterGCCallback(xpcGCCallback func) michael@0: { michael@0: mRuntime->RemoveGCCallback(func); michael@0: } michael@0: michael@0: /* [noscript, notxpcom] void registerContextCallback(in xpcContextCallback func); */ michael@0: NS_IMETHODIMP_(void) michael@0: nsXPConnect::RegisterContextCallback(xpcContextCallback func) michael@0: { michael@0: mRuntime->AddContextCallback(func); michael@0: } michael@0: michael@0: /* [noscript, notxpcom] void unregisterContextCallback(in xpcContextCallback func); */ michael@0: NS_IMETHODIMP_(void) michael@0: nsXPConnect::UnregisterContextCallback(xpcContextCallback func) michael@0: { michael@0: mRuntime->RemoveContextCallback(func); michael@0: } michael@0: michael@0: #ifdef MOZ_JSDEBUGGER michael@0: void michael@0: nsXPConnect::CheckForDebugMode(JSRuntime *rt) michael@0: { michael@0: if (gDebugMode == gDesiredDebugMode) { michael@0: return; michael@0: } michael@0: michael@0: // This can happen if a Worker is running, but we don't have the ability to michael@0: // debug workers right now, so just return. michael@0: if (!NS_IsMainThread()) michael@0: MOZ_CRASH(); michael@0: michael@0: AutoSafeJSContext cx; michael@0: JS_SetRuntimeDebugMode(rt, gDesiredDebugMode); michael@0: michael@0: nsresult rv; michael@0: const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1"; michael@0: nsCOMPtr jsds = do_GetService(jsdServiceCtrID, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: goto fail; michael@0: } michael@0: michael@0: if (!JS_SetDebugModeForAllCompartments(cx, gDesiredDebugMode)) michael@0: goto fail; michael@0: michael@0: if (gDesiredDebugMode) { michael@0: rv = jsds->ActivateDebugger(rt); michael@0: } michael@0: michael@0: gDebugMode = gDesiredDebugMode; michael@0: return; michael@0: michael@0: fail: michael@0: if (jsds) michael@0: jsds->DeactivateDebugger(); michael@0: michael@0: /* michael@0: * If an attempt to turn debug mode on fails, cancel the request. It's michael@0: * always safe to turn debug mode off, since DeactivateDebugger prevents michael@0: * debugger callbacks from having any effect. michael@0: */ michael@0: if (gDesiredDebugMode) michael@0: JS_SetRuntimeDebugMode(rt, false); michael@0: gDesiredDebugMode = gDebugMode = false; michael@0: } michael@0: #else //MOZ_JSDEBUGGER not defined michael@0: void michael@0: nsXPConnect::CheckForDebugMode(JSRuntime *rt) michael@0: { michael@0: gDesiredDebugMode = gDebugMode = false; michael@0: } michael@0: #endif //#ifdef MOZ_JSDEBUGGER michael@0: michael@0: michael@0: void michael@0: xpc_ActivateDebugMode() michael@0: { michael@0: XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); michael@0: nsXPConnect::XPConnect()->SetDebugModeWhenPossible(true, true); michael@0: nsXPConnect::CheckForDebugMode(rt->Runtime()); michael@0: } michael@0: michael@0: /* virtual */ michael@0: JSContext* michael@0: nsXPConnect::GetCurrentJSContext() michael@0: { michael@0: return GetRuntime()->GetJSContextStack()->Peek(); michael@0: } michael@0: michael@0: /* virtual */ michael@0: JSContext* michael@0: nsXPConnect::InitSafeJSContext() michael@0: { michael@0: return GetRuntime()->GetJSContextStack()->InitSafeJSContext(); michael@0: } michael@0: michael@0: /* virtual */ michael@0: JSContext* michael@0: nsXPConnect::GetSafeJSContext() michael@0: { michael@0: return GetRuntime()->GetJSContextStack()->GetSafeJSContext(); michael@0: } michael@0: michael@0: namespace xpc { michael@0: michael@0: bool michael@0: PushJSContextNoScriptContext(JSContext *aCx) michael@0: { michael@0: MOZ_ASSERT_IF(aCx, !GetScriptContextFromJSContext(aCx)); michael@0: return XPCJSRuntime::Get()->GetJSContextStack()->Push(aCx); michael@0: } michael@0: michael@0: void michael@0: PopJSContextNoScriptContext() michael@0: { michael@0: XPCJSRuntime::Get()->GetJSContextStack()->Pop(); michael@0: } michael@0: michael@0: } // namespace xpc michael@0: michael@0: nsIPrincipal* michael@0: nsXPConnect::GetPrincipal(JSObject* obj, bool allowShortCircuit) const michael@0: { michael@0: MOZ_ASSERT(IS_WN_REFLECTOR(obj), "What kind of wrapper is this?"); michael@0: michael@0: XPCWrappedNative *xpcWrapper = XPCWrappedNative::Get(obj); michael@0: if (xpcWrapper) { michael@0: if (allowShortCircuit) { michael@0: nsIPrincipal *result = xpcWrapper->GetObjectPrincipal(); michael@0: if (result) { michael@0: return result; michael@0: } michael@0: } michael@0: michael@0: // If not, check if it points to an nsIScriptObjectPrincipal michael@0: nsCOMPtr objPrin = michael@0: do_QueryInterface(xpcWrapper->Native()); michael@0: if (objPrin) { michael@0: nsIPrincipal *result = objPrin->GetPrincipal(); michael@0: if (result) { michael@0: return result; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::HoldObject(JSContext *aJSContext, JSObject *aObjectArg, michael@0: nsIXPConnectJSObjectHolder **aHolder) michael@0: { michael@0: RootedObject aObject(aJSContext, aObjectArg); michael@0: XPCJSObjectHolder* objHolder = XPCJSObjectHolder::newHolder(aObject); michael@0: if (!objHolder) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(*aHolder = objHolder); michael@0: return NS_OK; michael@0: } michael@0: michael@0: namespace xpc { michael@0: michael@0: bool michael@0: Base64Encode(JSContext *cx, HandleValue val, MutableHandleValue out) michael@0: { michael@0: MOZ_ASSERT(cx); michael@0: michael@0: JS::RootedValue root(cx, val); michael@0: xpc_qsACString encodedString(cx, root, &root, false, michael@0: xpc_qsACString::eStringify, michael@0: xpc_qsACString::eStringify); michael@0: if (!encodedString.IsValid()) michael@0: return false; michael@0: michael@0: nsAutoCString result; michael@0: if (NS_FAILED(mozilla::Base64Encode(encodedString, result))) { michael@0: JS_ReportError(cx, "Failed to encode base64 data!"); michael@0: return false; michael@0: } michael@0: michael@0: JSString *str = JS_NewStringCopyN(cx, result.get(), result.Length()); michael@0: if (!str) michael@0: return false; michael@0: michael@0: out.setString(str); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: Base64Decode(JSContext *cx, HandleValue val, MutableHandleValue out) michael@0: { michael@0: MOZ_ASSERT(cx); michael@0: michael@0: JS::RootedValue root(cx, val); michael@0: xpc_qsACString encodedString(cx, root, &root, false, michael@0: xpc_qsACString::eStringify, michael@0: xpc_qsACString::eStringify); michael@0: if (!encodedString.IsValid()) michael@0: return false; michael@0: michael@0: nsAutoCString result; michael@0: if (NS_FAILED(mozilla::Base64Decode(encodedString, result))) { michael@0: JS_ReportError(cx, "Failed to decode base64 string!"); michael@0: return false; michael@0: } michael@0: michael@0: JSString *str = JS_NewStringCopyN(cx, result.get(), result.Length()); michael@0: if (!str) michael@0: return false; michael@0: michael@0: out.setString(str); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: SetLocationForGlobal(JSObject *global, const nsACString& location) michael@0: { michael@0: MOZ_ASSERT(global); michael@0: EnsureCompartmentPrivate(global)->SetLocation(location); michael@0: } michael@0: michael@0: void michael@0: SetLocationForGlobal(JSObject *global, nsIURI *locationURI) michael@0: { michael@0: MOZ_ASSERT(global); michael@0: EnsureCompartmentPrivate(global)->SetLocationURI(locationURI); michael@0: } michael@0: michael@0: } // namespace xpc michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::SetDebugModeWhenPossible(bool mode, bool allowSyncDisable) michael@0: { michael@0: gDesiredDebugMode = mode; michael@0: if (!mode && allowSyncDisable) michael@0: CheckForDebugMode(mRuntime->Runtime()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::NotifyDidPaint() michael@0: { michael@0: JS::NotifyDidPaint(GetRuntime()->Runtime()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Note - We used to have HAS_PRINCIPALS_FLAG = 1 here, so reusing that flag michael@0: // will require bumping the XDR version number. michael@0: static const uint8_t HAS_ORIGIN_PRINCIPALS_FLAG = 2; michael@0: michael@0: static nsresult michael@0: WriteScriptOrFunction(nsIObjectOutputStream *stream, JSContext *cx, michael@0: JSScript *scriptArg, HandleObject functionObj) michael@0: { michael@0: // Exactly one of script or functionObj must be given michael@0: MOZ_ASSERT(!scriptArg != !functionObj); michael@0: michael@0: RootedScript script(cx, scriptArg); michael@0: if (!script) { michael@0: RootedFunction fun(cx, JS_GetObjectFunction(functionObj)); michael@0: script.set(JS_GetFunctionScript(cx, fun)); michael@0: } michael@0: michael@0: nsIPrincipal *principal = michael@0: nsJSPrincipals::get(JS_GetScriptPrincipals(script)); michael@0: nsIPrincipal *originPrincipal = michael@0: nsJSPrincipals::get(JS_GetScriptOriginPrincipals(script)); michael@0: michael@0: uint8_t flags = 0; michael@0: michael@0: // Optimize for the common case when originPrincipals == principals. As michael@0: // originPrincipals is set to principals when the former is null we can michael@0: // simply skip the originPrincipals when they are the same as principals. michael@0: if (originPrincipal && originPrincipal != principal) michael@0: flags |= HAS_ORIGIN_PRINCIPALS_FLAG; michael@0: michael@0: nsresult rv = stream->Write8(flags); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (flags & HAS_ORIGIN_PRINCIPALS_FLAG) { michael@0: rv = stream->WriteObject(originPrincipal, true); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t size; michael@0: void* data; michael@0: { michael@0: if (functionObj) michael@0: data = JS_EncodeInterpretedFunction(cx, functionObj, &size); michael@0: else michael@0: data = JS_EncodeScript(cx, script, &size); michael@0: } michael@0: michael@0: if (!data) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: MOZ_ASSERT(size); michael@0: rv = stream->Write32(size); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = stream->WriteBytes(static_cast(data), size); michael@0: js_free(data); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static nsresult michael@0: ReadScriptOrFunction(nsIObjectInputStream *stream, JSContext *cx, michael@0: JSScript **scriptp, JSObject **functionObjp) michael@0: { michael@0: // Exactly one of script or functionObj must be given michael@0: MOZ_ASSERT(!scriptp != !functionObjp); michael@0: michael@0: uint8_t flags; michael@0: nsresult rv = stream->Read8(&flags); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsJSPrincipals* originPrincipal = nullptr; michael@0: nsCOMPtr readOriginPrincipal; michael@0: if (flags & HAS_ORIGIN_PRINCIPALS_FLAG) { michael@0: nsCOMPtr supports; michael@0: rv = stream->ReadObject(true, getter_AddRefs(supports)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: readOriginPrincipal = do_QueryInterface(supports); michael@0: originPrincipal = nsJSPrincipals::get(readOriginPrincipal); michael@0: } michael@0: michael@0: uint32_t size; michael@0: rv = stream->Read32(&size); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: char* data; michael@0: rv = stream->ReadBytes(size, &data); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: { michael@0: if (scriptp) { michael@0: JSScript *script = JS_DecodeScript(cx, data, size, originPrincipal); michael@0: if (!script) michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: else michael@0: *scriptp = script; michael@0: } else { michael@0: JSObject *funobj = JS_DecodeInterpretedFunction(cx, data, size, michael@0: originPrincipal); michael@0: if (!funobj) michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: else michael@0: *functionObjp = funobj; michael@0: } michael@0: } michael@0: michael@0: nsMemory::Free(data); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::WriteScript(nsIObjectOutputStream *stream, JSContext *cx, JSScript *script) michael@0: { michael@0: return WriteScriptOrFunction(stream, cx, script, NullPtr()); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::ReadScript(nsIObjectInputStream *stream, JSContext *cx, JSScript **scriptp) michael@0: { michael@0: return ReadScriptOrFunction(stream, cx, scriptp, nullptr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::WriteFunction(nsIObjectOutputStream *stream, JSContext *cx, JSObject *functionObjArg) michael@0: { michael@0: RootedObject functionObj(cx, functionObjArg); michael@0: return WriteScriptOrFunction(stream, cx, nullptr, functionObj); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::ReadFunction(nsIObjectInputStream *stream, JSContext *cx, JSObject **functionObjp) michael@0: { michael@0: return ReadScriptOrFunction(stream, cx, nullptr, functionObjp); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXPConnect::MarkErrorUnreported(JSContext *cx) michael@0: { michael@0: XPCContext *xpcc = XPCContext::GetXPCContext(cx); michael@0: xpcc->MarkErrorUnreported(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* These are here to be callable from a debugger */ michael@0: extern "C" { michael@0: JS_EXPORT_API(void) DumpJSStack() michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); michael@0: if (NS_SUCCEEDED(rv) && xpc) michael@0: xpc->DebugDumpJSStack(true, true, false); michael@0: else michael@0: printf("failed to get XPConnect service!\n"); michael@0: } michael@0: michael@0: JS_EXPORT_API(char*) PrintJSStack() michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); michael@0: return (NS_SUCCEEDED(rv) && xpc) ? michael@0: xpc->DebugPrintJSStack(true, true, false) : michael@0: nullptr; michael@0: } michael@0: michael@0: JS_EXPORT_API(void) DumpJSEval(uint32_t frameno, const char* text) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); michael@0: if (NS_SUCCEEDED(rv) && xpc) michael@0: xpc->DebugDumpEvalInJSStackFrame(frameno, text); michael@0: else michael@0: printf("failed to get XPConnect service!\n"); michael@0: } michael@0: michael@0: JS_EXPORT_API(void) DumpCompleteHeap() michael@0: { michael@0: nsCOMPtr listener = michael@0: do_CreateInstance("@mozilla.org/cycle-collector-logger;1"); michael@0: if (!listener) { michael@0: NS_WARNING("Failed to create CC logger"); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr alltracesListener; michael@0: listener->AllTraces(getter_AddRefs(alltracesListener)); michael@0: if (!alltracesListener) { michael@0: NS_WARNING("Failed to get all traces logger"); michael@0: return; michael@0: } michael@0: michael@0: nsJSContext::CycleCollectNow(alltracesListener); michael@0: } michael@0: michael@0: } // extern "C" michael@0: michael@0: namespace xpc { michael@0: michael@0: bool michael@0: Atob(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (!args.length()) michael@0: return true; michael@0: michael@0: return xpc::Base64Decode(cx, args[0], args.rval()); michael@0: } michael@0: michael@0: bool michael@0: Btoa(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (!args.length()) michael@0: return true; michael@0: michael@0: return xpc::Base64Encode(cx, args[0], args.rval()); michael@0: } michael@0: michael@0: bool michael@0: IsXrayWrapper(JSObject *obj) michael@0: { michael@0: return WrapperFactory::IsXrayWrapper(obj); michael@0: } michael@0: michael@0: } // namespace xpc michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: bool michael@0: IsChromeOrXBL(JSContext* cx, JSObject* /* unused */) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: JSCompartment* c = js::GetContextCompartment(cx); michael@0: michael@0: // For remote XUL, we run XBL in the XUL scope. Given that we care about michael@0: // compat and not security for remote XUL, we just always claim to be XBL. michael@0: // michael@0: // Note that, for performance, we don't check AllowXULXBLForPrincipal here, michael@0: // and instead rely on the fact that AllowXBLScope() only returns false in michael@0: // remote XUL situations. michael@0: return AccessCheck::isChrome(c) || IsXBLScope(c) || !AllowXBLScope(c); michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla