/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * John Bandhauer <jband@netscape.com> (original author) * Pierre Phaneuf <pp@ludusdesign.com> * Nate Nielsen <nielsen@memberwebs.com> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* High level class and public functions implementation. */ #include "xpcprivate.h" #include "XPCNativeWrapper.h" #include "XPCWrapper.h" #include "nsBaseHashtable.h" #include "nsHashKeys.h" #include "jsatom.h" #include "jsfun.h" #include "jsscript.h" #include "nsThreadUtilsInternal.h" #include "dom_quickstubs.h" NS_IMPL_THREADSAFE_ISUPPORTS6(nsXPConnect, nsIXPConnect, nsISupportsWeakReference, nsIThreadObserver, nsIJSRuntimeService, nsIJSContextStack, nsIThreadJSContextStack) nsXPConnect* nsXPConnect::gSelf = nsnull; JSBool nsXPConnect::gOnceAliveNowDead = JS_FALSE; PRUint32 nsXPConnect::gReportAllJSExceptions = 0; // Global cache of the default script security manager (QI'd to // nsIScriptSecurityManager) nsIScriptSecurityManager_1_9_2 *gScriptSecurityManager = nsnull; const char XPC_CONTEXT_STACK_CONTRACTID[] = "@mozilla.org/js/xpc/ContextStack;1"; const char XPC_RUNTIME_CONTRACTID[] = "@mozilla.org/js/xpc/RuntimeService;1"; const char XPC_EXCEPTION_CONTRACTID[] = "@mozilla.org/js/xpc/Exception;1"; const char XPC_CONSOLE_CONTRACTID[] = "@mozilla.org/consoleservice;1"; const char XPC_SCRIPT_ERROR_CONTRACTID[] = "@mozilla.org/scripterror;1"; const char XPC_ID_CONTRACTID[] = "@mozilla.org/js/xpc/ID;1"; const char XPC_XPCONNECT_CONTRACTID[] = "@mozilla.org/js/xpc/XPConnect;1"; /***************************************************************************/ nsXPConnect::nsXPConnect() : mRuntime(nsnull), mInterfaceInfoManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)), mDefaultSecurityManager(nsnull), mDefaultSecurityManagerFlags(0), mShuttingDown(JS_FALSE), mCycleCollectionContext(nsnull), mCycleCollecting(PR_FALSE) { mRuntime = XPCJSRuntime::newXPCJSRuntime(this); nsCycleCollector_registerRuntime(nsIProgrammingLanguage::JAVASCRIPT, this); #ifdef DEBUG_CC mJSRoots.ops = nsnull; #endif #ifdef XPC_TOOLS_SUPPORT { char* filename = PR_GetEnv("MOZILLA_JS_PROFILER_OUTPUT"); if(filename && *filename) { mProfilerOutputFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); if(mProfilerOutputFile && NS_SUCCEEDED(mProfilerOutputFile->InitWithNativePath(nsDependentCString(filename)))) { mProfiler = do_GetService(XPCTOOLS_PROFILER_CONTRACTID); if(mProfiler) { if(NS_SUCCEEDED(mProfiler->Start())) { #ifdef DEBUG printf("***** profiling JavaScript. Output to: %s\n", filename); #endif } } } } } #endif char* reportableEnv = PR_GetEnv("MOZ_REPORT_ALL_JS_EXCEPTIONS"); if(reportableEnv && *reportableEnv) gReportAllJSExceptions = 1; } nsXPConnect::~nsXPConnect() { NS_ASSERTION(!mCycleCollectionContext, "Didn't call FinishCycleCollection?"); nsCycleCollector_forgetRuntime(nsIProgrammingLanguage::JAVASCRIPT); JSContext *cx = nsnull; if (mRuntime) { // Create our own JSContext rather than an XPCCallContext, since // otherwise we will create a new safe JS context and attach a // components object that won't get GCed. // And do this before calling CleanupAllThreads, so that we // don't create an extra xpcPerThreadData. cx = JS_NewContext(mRuntime->GetJSRuntime(), 8192); } XPCPerThreadData::CleanupAllThreads(); mShuttingDown = JS_TRUE; if (cx) { JS_BeginRequest(cx); // XXX Call even if |mRuntime| null? XPCWrappedNativeScope::SystemIsBeingShutDown(cx); mRuntime->SystemIsBeingShutDown(cx); JS_EndRequest(cx); JS_DestroyContext(cx); } NS_IF_RELEASE(mDefaultSecurityManager); gScriptSecurityManager = nsnull; // shutdown the logging system XPC_LOG_FINISH(); delete mRuntime; gSelf = nsnull; gOnceAliveNowDead = JS_TRUE; } // static nsXPConnect* nsXPConnect::GetXPConnect() { if(!gSelf) { if(gOnceAliveNowDead) return nsnull; gSelf = new nsXPConnect(); if(!gSelf) return nsnull; if(!gSelf->mRuntime || !gSelf->mInterfaceInfoManager) { // ctor failed to create an acceptable instance delete gSelf; gSelf = nsnull; } else { // Initial extra ref to keep the singleton alive // balanced by explicit call to ReleaseXPConnectSingleton() NS_ADDREF(gSelf); if (NS_FAILED(NS_SetGlobalThreadObserver(gSelf))) { NS_RELEASE(gSelf); // Fall through to returning null } } } return gSelf; } // static nsXPConnect* nsXPConnect::GetSingleton() { nsXPConnect* xpc = nsXPConnect::GetXPConnect(); NS_IF_ADDREF(xpc); return xpc; } // static void nsXPConnect::ReleaseXPConnectSingleton() { nsXPConnect* xpc = gSelf; if(xpc) { NS_SetGlobalThreadObserver(nsnull); #ifdef XPC_TOOLS_SUPPORT if(xpc->mProfiler) { xpc->mProfiler->Stop(); xpc->mProfiler->WriteResults(xpc->mProfilerOutputFile); } #endif #ifdef DEBUG // force a dump of the JavaScript gc heap if JS is still alive // if requested through XPC_SHUTDOWN_HEAP_DUMP environment variable { // autoscope XPCCallContext ccx(NATIVE_CALLER); if(ccx.IsValid()) { const char* dumpName = getenv("XPC_SHUTDOWN_HEAP_DUMP"); if(dumpName) { FILE* dumpFile = (*dumpName == '\0' || strcmp(dumpName, "stdout") == 0) ? stdout : fopen(dumpName, "w"); if(dumpFile) { JS_DumpHeap(ccx, dumpFile, nsnull, 0, nsnull, static_cast<size_t>(-1), nsnull); if(dumpFile != stdout) fclose(dumpFile); } } } } #endif #ifdef XPC_DUMP_AT_SHUTDOWN // NOTE: to see really interesting stuff turn on the prlog stuff. // See the comment at the top of xpclog.h to see how to do that. xpc->DebugDump(7); #endif nsrefcnt cnt; NS_RELEASE2(xpc, cnt); #ifdef XPC_DUMP_AT_SHUTDOWN if(0 != cnt) printf("*** dangling reference to nsXPConnect: refcnt=%d\n", cnt); else printf("+++ XPConnect had no dangling references.\n"); #endif } } // static nsresult nsXPConnect::GetInterfaceInfoManager(nsIInterfaceInfoSuperManager** iim, nsXPConnect* xpc /*= nsnull*/) { if(!xpc && !(xpc = GetXPConnect())) return NS_ERROR_FAILURE; *iim = xpc->mInterfaceInfoManager; NS_IF_ADDREF(*iim); return NS_OK; } // static XPCJSRuntime* nsXPConnect::GetRuntimeInstance() { nsXPConnect* xpc = GetXPConnect(); NS_ASSERTION(xpc, "Must not be called if XPC failed to initialize"); return xpc->GetRuntime(); } // static JSBool nsXPConnect::IsISupportsDescendant(nsIInterfaceInfo* info) { PRBool found = PR_FALSE; if(info) info->HasAncestor(&NS_GET_IID(nsISupports), &found); return found; } /***************************************************************************/ typedef PRBool (*InfoTester)(nsIInterfaceInfoManager* manager, const void* data, nsIInterfaceInfo** info); static PRBool IIDTester(nsIInterfaceInfoManager* manager, const void* data, nsIInterfaceInfo** info) { return NS_SUCCEEDED(manager->GetInfoForIID((const nsIID *) data, info)) && *info; } static PRBool NameTester(nsIInterfaceInfoManager* manager, const void* data, nsIInterfaceInfo** info) { return NS_SUCCEEDED(manager->GetInfoForName((const char *) data, info)) && *info; } static nsresult FindInfo(InfoTester tester, const void* data, nsIInterfaceInfoSuperManager* iism, nsIInterfaceInfo** info) { if(tester(iism, data, info)) return NS_OK; // If not found, then let's ask additional managers. PRBool yes; nsCOMPtr<nsISimpleEnumerator> list; if(NS_SUCCEEDED(iism->HasAdditionalManagers(&yes)) && yes && NS_SUCCEEDED(iism->EnumerateAdditionalManagers(getter_AddRefs(list))) && list) { PRBool more; nsCOMPtr<nsIInterfaceInfoManager> current; while(NS_SUCCEEDED(list->HasMoreElements(&more)) && more && NS_SUCCEEDED(list->GetNext(getter_AddRefs(current))) && current) { if(tester(current, data, info)) return NS_OK; } } return NS_ERROR_NO_INTERFACE; } nsresult nsXPConnect::GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info) { return FindInfo(IIDTester, aIID, mInterfaceInfoManager, info); } nsresult nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info) { return FindInfo(NameTester, name, mInterfaceInfoManager, info); } static JSGCCallback gOldJSGCCallback; // Whether cycle collection was run. static PRBool gDidCollection; // Whether starting cycle collection was successful. static PRBool gInCollection; // Whether cycle collection collected anything. static PRBool gCollected; static JSBool XPCCycleCollectGCCallback(JSContext *cx, JSGCStatus status) { // Launch the cycle collector. switch(status) { case JSGC_BEGIN: nsXPConnect::GetRuntimeInstance()->UnrootContextGlobals(); break; case JSGC_MARK_END: // This is the hook between marking and sweeping in the JS GC. Do cycle // collection. if(!gDidCollection) { NS_ASSERTION(!gInCollection, "Recursing?"); gDidCollection = PR_TRUE; gInCollection = nsCycleCollector_beginCollection(); } // Mark JS objects that are held by XPCOM objects that are in cycles // that will not be collected. nsXPConnect::GetRuntimeInstance()-> TraceXPConnectRoots(cx->runtime->gcMarkingTracer, JS_TRUE); break; case JSGC_END: if(gInCollection) { gInCollection = PR_FALSE; gCollected = nsCycleCollector_finishCollection(); } break; default: break; } return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; } PRBool nsXPConnect::Collect() { // We're dividing JS objects into 2 categories: // // 1. "real" roots, held by the JS engine itself or rooted through the root // and lock JS APIs. Roots from this category are considered black in the // cycle collector, any cycle they participate in is uncollectable. // // 2. roots held by C++ objects that participate in cycle collection, // held by XPConnect (see XPCJSRuntime::TraceXPConnectRoots). Roots from // this category are considered grey in the cycle collector, their final // color depends on the objects that hold them. It is thus very important // to always traverse the objects that hold these objects during cycle // collection (see XPCJSRuntime::AddXPConnectRoots). // // Note that if a root is in both categories it is the fact that it is in // category 1 that takes precedence, so it will be considered black. // // // We split up garbage collection into 3 phases (1, 3 and 4) and do cycle // collection between the first 2 phases of garbage collection: // // 1. marking of the roots in category 1 by having the JS GC do its marking // 2. cycle collection // 3. marking of the roots in category 2 by // XPCJSRuntime::TraceXPConnectRoots // 4. sweeping of unmarked JS objects // // During cycle collection, marked JS objects (and the objects they hold) // will be colored black. White objects holding roots from category 2 will // be forgotten by XPConnect (in the unlink callback of the white objects). // During phase 3 we'll only mark black objects holding JS objects (white // objects were forgotten) and white JS objects will be swept during // phase 4. // Because splitting up the JS GC itself is hard, we're going to use a GC // callback to do phase 2 and 3 after phase 1 has ended (see // XPCCycleCollectGCCallback). // // If DEBUG_CC is not defined the cycle collector will not traverse roots // from category 1 or any JS objects held by them. Any JS objects they hold // will already be marked by the JS GC and will thus be colored black // themselves. Any C++ objects they hold will have a missing (untraversed) // edge from the JS object to the C++ object and so it will be marked black // too. This decreases the number of objects that the cycle collector has to // deal with. // To improve debugging, if DEBUG_CC is defined all JS objects are // traversed. XPCCallContext cycleCollectionContext(NATIVE_CALLER); if(!cycleCollectionContext.IsValid()) { return PR_FALSE; } mCycleCollecting = PR_TRUE; mCycleCollectionContext = &cycleCollectionContext; gDidCollection = PR_FALSE; gInCollection = PR_FALSE; gCollected = PR_FALSE; JSContext *cx = mCycleCollectionContext->GetJSContext(); gOldJSGCCallback = JS_SetGCCallback(cx, XPCCycleCollectGCCallback); JS_GC(cx); JS_SetGCCallback(cx, gOldJSGCCallback); gOldJSGCCallback = nsnull; mCycleCollectionContext = nsnull; mCycleCollecting = PR_FALSE; return gCollected; } // JSTRACE_XML can recursively hold on to more JSTRACE_XML objects, adding it to // the cycle collector avoids stack overflow. #define ADD_TO_CC(_kind) ((_kind) == JSTRACE_OBJECT || (_kind) == JSTRACE_XML) #ifdef DEBUG_CC struct NoteJSRootTracer : public JSTracer { NoteJSRootTracer(PLDHashTable *aObjects, nsCycleCollectionTraversalCallback& cb) : mObjects(aObjects), mCb(cb) { } PLDHashTable* mObjects; nsCycleCollectionTraversalCallback& mCb; }; static void NoteJSRoot(JSTracer *trc, void *thing, uint32 kind) { if(ADD_TO_CC(kind)) { NoteJSRootTracer *tracer = static_cast<NoteJSRootTracer*>(trc); PLDHashEntryHdr *entry = PL_DHashTableOperate(tracer->mObjects, thing, PL_DHASH_ADD); if(entry && !reinterpret_cast<PLDHashEntryStub*>(entry)->key) { reinterpret_cast<PLDHashEntryStub*>(entry)->key = thing; tracer->mCb.NoteRoot(nsIProgrammingLanguage::JAVASCRIPT, thing, nsXPConnect::GetXPConnect()); } } else if(kind != JSTRACE_DOUBLE && kind != JSTRACE_STRING) { JS_TraceChildren(trc, thing, kind); } } #endif nsresult nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb) { #ifdef DEBUG_CC NS_ASSERTION(!mJSRoots.ops, "Didn't call FinishCollection?"); if(!mCycleCollectionContext) { // Being called from nsCycleCollector::ExplainLiveExpectedGarbage. mExplainCycleCollectionContext = new XPCCallContext(NATIVE_CALLER); if(!mExplainCycleCollectionContext || !mExplainCycleCollectionContext->IsValid()) { mExplainCycleCollectionContext = nsnull; return PR_FALSE; } mCycleCollectionContext = mExplainCycleCollectionContext; // Record all objects held by the JS runtime. This avoids doing a // complete GC if we're just tracing to explain (from // ExplainLiveExpectedGarbage), which makes the results of cycle // collection identical for DEBUG_CC and non-DEBUG_CC builds. if(!PL_DHashTableInit(&mJSRoots, PL_DHashGetStubOps(), nsnull, sizeof(PLDHashEntryStub), PL_DHASH_MIN_SIZE)) { mJSRoots.ops = nsnull; return NS_ERROR_OUT_OF_MEMORY; } GetRuntime()->UnrootContextGlobals(); PRBool alreadyCollecting = mCycleCollecting; mCycleCollecting = PR_TRUE; NoteJSRootTracer trc(&mJSRoots, cb); JS_TRACER_INIT(&trc, mCycleCollectionContext->GetJSContext(), NoteJSRoot); JS_TraceRuntime(&trc); mCycleCollecting = alreadyCollecting; } #else NS_ASSERTION(mCycleCollectionContext, "Didn't call nsXPConnect::Collect()?"); #endif GetRuntime()->AddXPConnectRoots(mCycleCollectionContext->GetJSContext(), cb); return NS_OK; } nsresult nsXPConnect::FinishCycleCollection() { #ifdef DEBUG_CC if(mExplainCycleCollectionContext) { mCycleCollectionContext = nsnull; mExplainCycleCollectionContext = nsnull; GetRuntime()->RootContextGlobals(); } #endif #ifdef DEBUG_CC if(mJSRoots.ops) { PL_DHashTableFinish(&mJSRoots); mJSRoots.ops = nsnull; } #endif return NS_OK; } nsCycleCollectionParticipant * nsXPConnect::ToParticipant(void *p) { return this; } void nsXPConnect::CommenceShutdown() { #ifdef DEBUG fprintf(stderr, "nsXPConnect::CommenceShutdown()\n"); #endif // Tell the JS engine that we are about to destroy the runtime. JS_CommenceRuntimeShutDown(mRuntime->GetJSRuntime()); } NS_IMETHODIMP nsXPConnect::RootAndUnlinkJSObjects(void *p) { return NS_OK; } #ifdef DEBUG_CC void nsXPConnect::PrintAllReferencesTo(void *p) { #ifdef DEBUG if(!mCycleCollectionContext) { NS_NOTREACHED("no context"); return; } JS_DumpHeap(*mCycleCollectionContext, stdout, nsnull, 0, p, 0x7fffffff, nsnull); #endif } #endif NS_IMETHODIMP nsXPConnect::Unlink(void *p) { return NS_OK; } NS_IMETHODIMP nsXPConnect::Unroot(void *p) { return NS_OK; } struct TraversalTracer : public JSTracer { TraversalTracer(nsCycleCollectionTraversalCallback &aCb) : cb(aCb) { } nsCycleCollectionTraversalCallback &cb; }; static void NoteJSChild(JSTracer *trc, void *thing, uint32 kind) { if(ADD_TO_CC(kind)) { TraversalTracer *tracer = static_cast<TraversalTracer*>(trc); #if defined(DEBUG) if (NS_UNLIKELY(tracer->cb.WantDebugInfo())) { // based on DumpNotify in jsapi.c if (tracer->debugPrinter) { char buffer[200]; tracer->debugPrinter(trc, buffer, sizeof(buffer)); tracer->cb.NoteNextEdgeName(buffer); } else if (tracer->debugPrintIndex != (size_t)-1) { char buffer[200]; JS_snprintf(buffer, sizeof(buffer), "%s[%lu]", static_cast<const char *>(tracer->debugPrintArg), tracer->debugPrintIndex); tracer->cb.NoteNextEdgeName(buffer); } else { tracer->cb.NoteNextEdgeName( static_cast<const char*>(tracer->debugPrintArg)); } } #endif tracer->cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, thing); } else if(kind != JSTRACE_DOUBLE && kind != JSTRACE_STRING) { JS_TraceChildren(trc, thing, kind); } } static JSBool WrapperIsNotMainThreadOnly(XPCWrappedNative *wrapper) { XPCWrappedNativeProto *proto = wrapper->GetProto(); if(proto && proto->ClassIsMainThreadOnly()) return PR_FALSE; // If the native participates in cycle collection then we know it can only // be used on the main thread, in that case we assume the wrapped native // can only be used on the main thread too. nsXPCOMCycleCollectionParticipant* participant; return NS_FAILED(CallQueryInterface(wrapper->Native(), &participant)); } NS_IMETHODIMP nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) { if(!mCycleCollectionContext) return NS_ERROR_FAILURE; JSContext *cx = mCycleCollectionContext->GetJSContext(); uint32 traceKind = js_GetGCThingTraceKind(p); JSObject *obj; JSClass *clazz; // We do not want to add wrappers to the cycle collector if they're not // explicitly marked as main thread only, because the cycle collector isn't // able to deal with objects that might be used off of the main thread. We // do want to explicitly mark them for cycle collection if the wrapper has // an external reference, because the wrapper would mark the JS object if // we did add the wrapper to the cycle collector. JSBool dontTraverse = PR_FALSE; JSBool markJSObject = PR_FALSE; if(traceKind == JSTRACE_OBJECT) { obj = static_cast<JSObject*>(p); clazz = OBJ_GET_CLASS(cx, obj); if(clazz == &XPC_WN_Tearoff_JSClass) { XPCWrappedNative *wrapper = (XPCWrappedNative*)xpc_GetJSPrivate(STOBJ_GET_PARENT(obj)); dontTraverse = WrapperIsNotMainThreadOnly(wrapper); } else if(IS_WRAPPER_CLASS(clazz)) { XPCWrappedNative *wrapper = (XPCWrappedNative*)xpc_GetJSPrivate(obj); dontTraverse = WrapperIsNotMainThreadOnly(wrapper); markJSObject = dontTraverse && wrapper->HasExternalReference(); } } CCNodeType type; #ifdef DEBUG_CC // Note that the conditions under which we specify GCMarked vs. // GCUnmarked are different between ExplainLiveExpectedGarbage and // the normal case. In the normal case, we're saying that anything // reachable from a JS runtime root is itself such a root. This // doesn't actually break anything; it really just does some of the // cycle collector's work for it. However, when debugging, we // (1) actually need to know what the root is and (2) don't want to // do an extra GC, so we use mJSRoots, built from JS_TraceRuntime, // which produces a different result because we didn't call // JS_TraceChildren to trace everything that was reachable. if(mJSRoots.ops) { // ExplainLiveExpectedGarbage codepath PLDHashEntryHdr* entry = PL_DHashTableOperate(&mJSRoots, p, PL_DHASH_LOOKUP); type = markJSObject || PL_DHASH_ENTRY_IS_BUSY(entry) ? GCMarked : GCUnmarked; } else #endif { // Normal codepath (matches non-DEBUG_CC codepath). type = !markJSObject && JS_IsAboutToBeFinalized(cx, p) ? GCUnmarked : GCMarked; } if (cb.WantDebugInfo()) { char name[72]; if(traceKind == JSTRACE_OBJECT) { JSObject *obj = static_cast<JSObject*>(p); JSClass *clazz = OBJ_GET_CLASS(cx, obj); if(XPCNativeWrapper::IsNativeWrapperClass(clazz)) { XPCWrappedNative* wn; if(XPCNativeWrapper::GetWrappedNative(cx, obj, &wn) && wn) { XPCNativeScriptableInfo* si = wn->GetScriptableInfo(); if(si) { JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)", si->GetJSClass()->name); } else { nsIClassInfo* ci = wn->GetClassInfo(); char* className = nsnull; if(ci) ci->GetClassDescription(&className); if(className) { JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)", className); PR_Free(className); } else { XPCNativeSet* set = wn->GetSet(); XPCNativeInterface** array = set->GetInterfaceArray(); PRUint16 count = set->GetInterfaceCount(); if(count > 0) JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)", array[0]->GetNameString()); else JS_snprintf(name, sizeof(name), "XPCNativeWrapper"); } } } else { JS_snprintf(name, sizeof(name), "XPCNativeWrapper"); } } else { XPCNativeScriptableInfo* si = nsnull; if(IS_PROTO_CLASS(clazz)) { XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); si = p->GetScriptableInfo(); } if(si) { JS_snprintf(name, sizeof(name), "JS Object (%s - %s)", clazz->name, si->GetJSClass()->name); } else if(clazz == &js_ScriptClass) { JSScript* script = (JSScript*) xpc_GetJSPrivate(obj); if(script->filename) { JS_snprintf(name, sizeof(name), "JS Object (Script - %s)", script->filename); } else { JS_snprintf(name, sizeof(name), "JS Object (Script)"); } } else if(clazz == &js_FunctionClass) { JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(obj); JSString* str = JS_GetFunctionId(fun); if(str) { NS_ConvertUTF16toUTF8 fname(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str))); JS_snprintf(name, sizeof(name), "JS Object (Function - %s)", fname.get()); } else { JS_snprintf(name, sizeof(name), "JS Object (Function)"); } } else { JS_snprintf(name, sizeof(name), "JS Object (%s)", clazz->name); } } } else { static const char trace_types[JSTRACE_LIMIT][7] = { "Object", "Double", "String", "Xml" }; JS_snprintf(name, sizeof(name), "JS %s", trace_types[traceKind]); } if(traceKind == JSTRACE_OBJECT) { JSObject *global = static_cast<JSObject*>(p), *parent; while((parent = JS_GetParent(cx, global))) global = parent; char fullname[100]; JS_snprintf(fullname, sizeof(fullname), "%s (global=%p)", name, global); cb.DescribeNode(type, 0, sizeof(JSObject), fullname); } else { cb.DescribeNode(type, 0, sizeof(JSObject), name); } } else { cb.DescribeNode(type, 0, sizeof(JSObject), "JS Object"); } if(!ADD_TO_CC(traceKind)) return NS_OK; // There's no need to trace objects that have already been marked by the JS // GC. Any JS objects hanging from them will already be marked. Only do this // if DEBUG_CC is not defined, else we do want to know about all JS objects // to get better graphs and explanations. if(!cb.WantAllTraces() && type == GCMarked) return NS_OK; TraversalTracer trc(cb); JS_TRACER_INIT(&trc, cx, NoteJSChild); JS_TraceChildren(&trc, p, traceKind); if(traceKind != JSTRACE_OBJECT || dontTraverse) return NS_OK; if(clazz == &XPC_WN_Tearoff_JSClass) { // A tearoff holds a strong reference to its native object // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative // will be held alive through the parent of the JSObject of the tearoff. XPCWrappedNativeTearOff *to = (XPCWrappedNativeTearOff*) xpc_GetJSPrivate(obj); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative"); cb.NoteXPCOMChild(to->GetNative()); } // XXX XPCNativeWrapper seems to be the only class that doesn't hold a // strong reference to its nsISupports private. This test does seem // fragile though, we should probably whitelist classes that do hold // a strong reference, but that might not be possible. else if(clazz->flags & JSCLASS_HAS_PRIVATE && clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS && !XPCNativeWrapper::IsNativeWrapperClass(clazz)) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)"); cb.NoteXPCOMChild(static_cast<nsISupports*>(xpc_GetJSPrivate(obj))); } return NS_OK; } PRInt32 nsXPConnect::GetRequestDepth(JSContext* cx) { PRInt32 requestDepth = cx->outstandingRequests; XPCCallContext* context = GetCycleCollectionContext(); if(context && cx == context->GetJSContext()) // Ignore the request from the XPCCallContext we created for cycle // collection. --requestDepth; return requestDepth; } class JSContextParticipant : public nsCycleCollectionParticipant { public: NS_IMETHOD RootAndUnlinkJSObjects(void *n) { return NS_OK; } NS_IMETHOD Unlink(void *n) { // We must not unlink a JSContext because Root/Unroot don't ensure that // the pointer is still valid. return NS_OK; } NS_IMETHOD Unroot(void *n) { return NS_OK; } NS_IMETHODIMP Traverse(void *n, nsCycleCollectionTraversalCallback &cb) { JSContext *cx = static_cast<JSContext*>(n); // Add cx->requestDepth to the refcount, if there are outstanding // requests the context needs to be kept alive and adding unknown // edges will ensure that any cycles this context is in won't be // collected. PRInt32 refCount = nsXPConnect::GetXPConnect()->GetRequestDepth(cx) + 1; cb.DescribeNode(RefCounted, refCount, sizeof(JSContext), "JSContext"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]"); cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, cx->globalObject); return NS_OK; } }; static JSContextParticipant JSContext_cycleCollectorGlobal; // static nsCycleCollectionParticipant* nsXPConnect::JSContextParticipant() { return &JSContext_cycleCollectorGlobal; } NS_IMETHODIMP_(void) nsXPConnect::NoteJSContext(JSContext *aJSContext, nsCycleCollectionTraversalCallback &aCb) { aCb.NoteNativeChild(aJSContext, &JSContext_cycleCollectorGlobal); } /***************************************************************************/ /***************************************************************************/ // nsIXPConnect interface methods... inline nsresult UnexpectedFailure(nsresult rv) { NS_ERROR("This is not supposed to fail!"); return rv; } class SaveFrame { public: SaveFrame(JSContext *cx) : mJSContext(cx) { mFrame = JS_SaveFrameChain(mJSContext); } ~SaveFrame() { JS_RestoreFrameChain(mJSContext, mFrame); } private: JSContext *mJSContext; JSStackFrame *mFrame; }; /* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */ NS_IMETHODIMP nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aGlobalJSObj, "bad param"); SaveFrame sf(aJSContext); XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); if(!xpc_InitJSxIDClassObjects()) return UnexpectedFailure(NS_ERROR_FAILURE); if(!xpc_InitWrappedNativeJSOps()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeScope* scope = XPCWrappedNativeScope::GetNewOrUsed(ccx, aGlobalJSObj); if(!scope) return UnexpectedFailure(NS_ERROR_FAILURE); scope->RemoveWrappedNativeProtos(); if(!nsXPCComponents::AttachNewComponentsObject(ccx, scope, aGlobalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); if(XPCPerThreadData::IsMainThread(ccx)) { if (!XPCNativeWrapper::AttachNewConstructorObject(ccx, aGlobalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); if (!XPC_SJOW_AttachNewConstructorObject(ccx, aGlobalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); } return NS_OK; } static JSBool TempGlobalResolve(JSContext *aJSContext, JSObject *obj, jsval id) { JSBool resolved; return JS_ResolveStandardClass(aJSContext, obj, id, &resolved); } static JSClass xpcTempGlobalClass = { "xpcTempGlobalClass", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, TempGlobalResolve, JS_ConvertStub, nsnull, JSCLASS_NO_OPTIONAL_MEMBERS }; /* nsIXPConnectJSObjectHolder initClassesWithNewWrappedGlobal (in JSContextPtr aJSContext, in nsISupports aCOMObj, in nsIIDRef aIID, in PRUint32 aFlags); */ NS_IMETHODIMP nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext, nsISupports *aCOMObj, const nsIID & aIID, PRUint32 aFlags, nsIXPConnectJSObjectHolder **_retval) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aCOMObj, "bad param"); NS_ASSERTION(_retval, "bad param"); // XXX This is not pretty. We make a temporary global object and // init it with all the Components object junk just so we have a // parent with an xpc scope to use when wrapping the object that will // become the 'real' global. XPCCallContext ccx(NATIVE_CALLER, aJSContext); PRBool system = (aFlags & nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT) != 0; JSObject* tempGlobal = JS_NewSystemObject(aJSContext, &xpcTempGlobalClass, nsnull, nsnull, system); if(!tempGlobal || !JS_SetParent(aJSContext, tempGlobal, nsnull) || !JS_SetPrototype(aJSContext, tempGlobal, nsnull)) return UnexpectedFailure(NS_ERROR_FAILURE); jsval v; nsCOMPtr<nsIXPConnectJSObjectHolder> holder; { // Scope for our auto-marker; it just needs to keep tempGlobal alive // long enough for InitClasses and WrapNative to do their work AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(tempGlobal)); if(NS_FAILED(InitClasses(aJSContext, tempGlobal))) return UnexpectedFailure(NS_ERROR_FAILURE); nsresult rv; if(!XPCConvert::NativeInterface2JSObject(ccx, &v, getter_AddRefs(holder), aCOMObj, &aIID, nsnull, nsnull, tempGlobal, PR_FALSE, OBJ_IS_GLOBAL, &rv)) return UnexpectedFailure(rv); NS_ASSERTION(NS_SUCCEEDED(rv) && holder, "Didn't wrap properly"); } JSObject* globalJSObj = JSVAL_TO_OBJECT(v); if(!globalJSObj) return UnexpectedFailure(NS_ERROR_FAILURE); if(aFlags & nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT) NS_ASSERTION(JS_IsSystemObject(aJSContext, globalJSObj), "huh?!"); // voodoo to fixup scoping and parenting... JS_SetParent(aJSContext, globalJSObj, nsnull); JSObject* oldGlobal = JS_GetGlobalObject(aJSContext); if(!oldGlobal || oldGlobal == tempGlobal) JS_SetGlobalObject(aJSContext, globalJSObj); if((aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES) && !JS_InitStandardClasses(aJSContext, globalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNative* wrapper = reinterpret_cast<XPCWrappedNative*>(holder.get()); XPCWrappedNativeScope* scope = wrapper->GetScope(); if(!scope) return UnexpectedFailure(NS_ERROR_FAILURE); NS_ASSERTION(scope->GetGlobalJSObject() == tempGlobal, "stealing scope!"); scope->SetGlobal(ccx, globalJSObj); JSObject* protoJSObject = wrapper->HasProto() ? wrapper->GetProto()->GetJSProtoObject() : globalJSObj; if(protoJSObject) { if(protoJSObject != globalJSObj) JS_SetParent(aJSContext, protoJSObject, globalJSObj); JS_SetPrototype(aJSContext, protoJSObject, scope->GetPrototypeJSObject()); } if(!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) { SaveFrame sf(ccx); if(!nsXPCComponents::AttachNewComponentsObject(ccx, scope, globalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); if(XPCPerThreadData::IsMainThread(ccx)) { if(!XPCNativeWrapper::AttachNewConstructorObject(ccx, globalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); if(!XPC_SJOW_AttachNewConstructorObject(ccx, globalJSObj)) return UnexpectedFailure(NS_ERROR_FAILURE); } } NS_ADDREF(*_retval = holder); return NS_OK; } /* nsIXPConnectJSObjectHolder wrapNative (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDRef aIID); */ NS_IMETHODIMP nsXPConnect::WrapNative(JSContext * aJSContext, JSObject * aScope, nsISupports *aCOMObj, const nsIID & aIID, nsIXPConnectJSObjectHolder **aHolder) { NS_ASSERTION(aHolder, "bad param"); jsval v; return WrapNativeToJSVal(aJSContext, aScope, aCOMObj, &aIID, PR_FALSE, &v, aHolder); } /* void wrapNativeToJSVal (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDPtr aIID, out JSVal aVal, out nsIXPConnectJSObjectHolder aHolder); */ NS_IMETHODIMP nsXPConnect::WrapNativeToJSVal(JSContext * aJSContext, JSObject * aScope, nsISupports *aCOMObj, const nsIID * aIID, PRBool aAllowWrapping, jsval *aVal, nsIXPConnectJSObjectHolder **aHolder) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aScope, "bad param"); NS_ASSERTION(aCOMObj, "bad param"); if(aHolder) *aHolder = nsnull; XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCLazyCallContext lccx(ccx); nsresult rv; if(!XPCConvert::NativeInterface2JSObject(ccx, aVal, aHolder, aCOMObj, aIID, nsnull, nsnull, aScope, aAllowWrapping, OBJ_IS_NOT_GLOBAL, &rv)) return rv; #ifdef DEBUG NS_ASSERTION(!XPCNativeWrapper::IsNativeWrapper(JSVAL_TO_OBJECT(*aVal)), "Shouldn't be returning a native wrapper here"); #endif return NS_OK; } /* void wrapJS (in JSContextPtr aJSContext, in JSObjectPtr aJSObj, in nsIIDRef aIID, [iid_is (aIID), retval] out nsQIResult result); */ NS_IMETHODIMP nsXPConnect::WrapJS(JSContext * aJSContext, JSObject * aJSObj, const nsIID & aIID, void * *result) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aJSObj, "bad param"); NS_ASSERTION(result, "bad param"); *result = nsnull; XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); nsresult rv; if(!XPCConvert::JSObject2NativeInterface(ccx, result, aJSObj, &aIID, nsnull, &rv)) return rv; return NS_OK; } /* void wrapJSAggregatedToNative (in nsISupports aOuter, in JSContextPtr aJSContext, in JSObjectPtr aJSObj, in nsIIDRef aIID, [iid_is (aIID), retval] out nsQIResult result); */ NS_IMETHODIMP nsXPConnect::WrapJSAggregatedToNative(nsISupports *aOuter, JSContext * aJSContext, JSObject * aJSObj, const nsIID & aIID, void * *result) { NS_ASSERTION(aOuter, "bad param"); NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aJSObj, "bad param"); NS_ASSERTION(result, "bad param"); *result = nsnull; XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); nsresult rv; if(!XPCConvert::JSObject2NativeInterface(ccx, result, aJSObj, &aIID, aOuter, &rv)) return rv; return NS_OK; } /* nsIXPConnectWrappedNative getWrappedNativeOfJSObject (in JSContextPtr aJSContext, in JSObjectPtr aJSObj); */ NS_IMETHODIMP nsXPConnect::GetWrappedNativeOfJSObject(JSContext * aJSContext, JSObject * aJSObj, nsIXPConnectWrappedNative **_retval) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aJSObj, "bad param"); NS_ASSERTION(_retval, "bad param"); XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); SLIM_LOG_WILL_MORPH(aJSContext, aJSObj); if(!IS_SLIM_WRAPPER(aJSObj) || MorphSlimWrapper(aJSContext, aJSObj)) { nsIXPConnectWrappedNative* wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(aJSContext, aJSObj); if(wrapper) { NS_ADDREF(wrapper); *_retval = wrapper; return NS_OK; } } // else... *_retval = nsnull; return NS_ERROR_FAILURE; } /* nsISupports getNativeOfWrapper(in JSContextPtr aJSContext, in JSObjectPtr aJSObj); */ NS_IMETHODIMP_(nsISupports*) nsXPConnect::GetNativeOfWrapper(JSContext * aJSContext, JSObject * aJSObj) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aJSObj, "bad param"); XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) { UnexpectedFailure(NS_ERROR_FAILURE); return nsnull; } JSObject* obj2 = nsnull; nsIXPConnectWrappedNative* wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(aJSContext, aJSObj, nsnull, &obj2); return wrapper ? wrapper->Native() : (obj2 ? (nsISupports*)xpc_GetJSPrivate(obj2) : nsnull); } /* JSObjectPtr getJSObjectOfWrapper (in JSContextPtr aJSContext, in JSObjectPtr aJSObj); */ NS_IMETHODIMP nsXPConnect::GetJSObjectOfWrapper(JSContext * aJSContext, JSObject * aJSObj, JSObject **_retval) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aJSObj, "bad param"); NS_ASSERTION(_retval, "bad param"); XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); JSObject* obj2 = nsnull; nsIXPConnectWrappedNative* wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(aJSContext, aJSObj, nsnull, &obj2); if(wrapper) { wrapper->GetJSObject(_retval); return NS_OK; } if(obj2) { *_retval = obj2; return NS_OK; } // else... *_retval = nsnull; return NS_ERROR_FAILURE; } /* nsIXPConnectWrappedNative getWrappedNativeOfNativeObject (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDRef aIID); */ NS_IMETHODIMP nsXPConnect::GetWrappedNativeOfNativeObject(JSContext * aJSContext, JSObject * aScope, nsISupports *aCOMObj, const nsIID & aIID, nsIXPConnectWrappedNative **_retval) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aScope, "bad param"); NS_ASSERTION(aCOMObj, "bad param"); NS_ASSERTION(_retval, "bad param"); *_retval = nsnull; XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeScope* scope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, aScope); if(!scope) return UnexpectedFailure(NS_ERROR_FAILURE); AutoMarkingNativeInterfacePtr iface(ccx); iface = XPCNativeInterface::GetNewOrUsed(ccx, &aIID); if(!iface) return NS_ERROR_FAILURE; XPCWrappedNative* wrapper; nsresult rv = XPCWrappedNative::GetUsedOnly(ccx, aCOMObj, scope, iface, &wrapper); if(NS_FAILED(rv)) return NS_ERROR_FAILURE; *_retval = static_cast<nsIXPConnectWrappedNative*>(wrapper); return NS_OK; } /* nsIXPConnectJSObjectHolder reparentWrappedNativeIfFound (in JSContextPtr aJSContext, in JSObjectPtr aScope, in JSObjectPtr aNewParent, in nsISupports aCOMObj); */ NS_IMETHODIMP nsXPConnect::ReparentWrappedNativeIfFound(JSContext * aJSContext, JSObject * aScope, JSObject * aNewParent, nsISupports *aCOMObj, nsIXPConnectJSObjectHolder **_retval) { XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeScope* scope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, aScope); if(!scope) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeScope* scope2 = XPCWrappedNativeScope::FindInJSObjectScope(ccx, aNewParent); if(!scope2) return UnexpectedFailure(NS_ERROR_FAILURE); return XPCWrappedNative:: ReparentWrapperIfFound(ccx, scope, scope2, aNewParent, aCOMObj, (XPCWrappedNative**) _retval); } static JSDHashOperator MoveableWrapperFinder(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { // Every element counts. nsTArray<nsRefPtr<XPCWrappedNative> > *array = static_cast<nsTArray<nsRefPtr<XPCWrappedNative> > *>(arg); array->AppendElement(((Native2WrappedNativeMap::Entry*)hdr)->value); return JS_DHASH_NEXT; } /* void reparentScopeAwareWrappers(in JSContextPtr aJSContext, in JSObjectPtr aOldScope, in JSObjectPtr aNewScope); */ NS_IMETHODIMP nsXPConnect::ReparentScopeAwareWrappers(JSContext *aJSContext, JSObject *aOldScope, JSObject *aNewScope) { XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeScope *oldScope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, aOldScope); if(!oldScope) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeScope *newScope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, aNewScope); if(!newScope) return UnexpectedFailure(NS_ERROR_FAILURE); // First, look through the old scope and find all of the wrappers that // we're going to move. nsTArray<nsRefPtr<XPCWrappedNative> > wrappersToMove; { // scoped lock XPCAutoLock lock(GetRuntime()->GetMapLock()); Native2WrappedNativeMap *map = oldScope->GetWrappedNativeMap(); wrappersToMove.SetCapacity(map->Count()); map->Enumerate(MoveableWrapperFinder, &wrappersToMove); } // Now that we have the wrappers, reparent them to the new scope. for(PRUint32 i = 0, stop = wrappersToMove.Length(); i < stop; ++i) { // First, check to see if this wrapper really needs to be // reparented. XPCWrappedNative *wrapper = wrappersToMove[i]; nsISupports *identity = wrapper->GetIdentityObject(); nsCOMPtr<nsIClassInfo> info(do_QueryInterface(identity)); // ClassInfo is implemented as singleton objects. If the identity // object here is the same object as returned by the QI, then it // is the singleton classinfo, so we don't need to reparent it. if(SameCOMIdentity(identity, info)) info = nsnull; if(!info) continue; XPCNativeScriptableCreateInfo sciProto; XPCNativeScriptableCreateInfo sciWrapper; nsresult rv = XPCWrappedNative::GatherScriptableCreateInfo(identity, info.get(), &sciProto, &sciWrapper); if(NS_FAILED(rv)) return NS_ERROR_FAILURE; // If the wrapper doesn't want precreate, then we don't need to // worry about reparenting it. if(!sciWrapper.GetFlags().WantPreCreate()) continue; JSObject *newParent = aOldScope; rv = sciWrapper.GetCallback()->PreCreate(identity, ccx, aOldScope, &newParent); if(NS_FAILED(rv)) return rv; if(newParent != aOldScope) { // The wrapper returned a new parent. If the new parent is in // a different scope, then we need to reparent it, otherwise, // the old scope is fine. XPCWrappedNativeScope *betterScope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, newParent); if(betterScope == oldScope) continue; NS_ASSERTION(betterScope == newScope, "Weird scope returned"); } else { // The old scope still works for this wrapper. continue; } // Now, reparent the wrapper, since we know that it wants to be // reparented. nsRefPtr<XPCWrappedNative> junk; rv = XPCWrappedNative::ReparentWrapperIfFound(ccx, oldScope, newScope, newParent, wrapper->GetIdentityObject(), getter_AddRefs(junk)); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } /* void setSecurityManagerForJSContext (in JSContextPtr aJSContext, in nsIXPCSecurityManager aManager, in PRUint16 flags); */ NS_IMETHODIMP nsXPConnect::SetSecurityManagerForJSContext(JSContext * aJSContext, nsIXPCSecurityManager *aManager, PRUint16 flags) { NS_ASSERTION(aJSContext, "bad param"); XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCContext* xpcc = ccx.GetXPCContext(); NS_IF_ADDREF(aManager); nsIXPCSecurityManager* oldManager = xpcc->GetSecurityManager(); NS_IF_RELEASE(oldManager); xpcc->SetSecurityManager(aManager); xpcc->SetSecurityManagerFlags(flags); return NS_OK; } /* void getSecurityManagerForJSContext (in JSContextPtr aJSContext, out nsIXPCSecurityManager aManager, out PRUint16 flags); */ NS_IMETHODIMP nsXPConnect::GetSecurityManagerForJSContext(JSContext * aJSContext, nsIXPCSecurityManager **aManager, PRUint16 *flags) { NS_ASSERTION(aJSContext, "bad param"); NS_ASSERTION(aManager, "bad param"); NS_ASSERTION(flags, "bad param"); XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCContext* xpcc = ccx.GetXPCContext(); nsIXPCSecurityManager* manager = xpcc->GetSecurityManager(); NS_IF_ADDREF(manager); *aManager = manager; *flags = xpcc->GetSecurityManagerFlags(); return NS_OK; } /* void setDefaultSecurityManager (in nsIXPCSecurityManager aManager, in PRUint16 flags); */ NS_IMETHODIMP nsXPConnect::SetDefaultSecurityManager(nsIXPCSecurityManager *aManager, PRUint16 flags) { NS_IF_ADDREF(aManager); NS_IF_RELEASE(mDefaultSecurityManager); mDefaultSecurityManager = aManager; mDefaultSecurityManagerFlags = flags; nsCOMPtr<nsIScriptSecurityManager_1_9_2> ssm = do_QueryInterface(mDefaultSecurityManager); // Remember the result of the above QI for fast access to the // script securityt manager. gScriptSecurityManager = ssm; return NS_OK; } /* void getDefaultSecurityManager (out nsIXPCSecurityManager aManager, out PRUint16 flags); */ NS_IMETHODIMP nsXPConnect::GetDefaultSecurityManager(nsIXPCSecurityManager **aManager, PRUint16 *flags) { NS_ASSERTION(aManager, "bad param"); NS_ASSERTION(flags, "bad param"); NS_IF_ADDREF(mDefaultSecurityManager); *aManager = mDefaultSecurityManager; *flags = mDefaultSecurityManagerFlags; return NS_OK; } /* nsIStackFrame createStackFrameLocation (in PRUint32 aLanguage, in string aFilename, in string aFunctionName, in PRInt32 aLineNumber, in nsIStackFrame aCaller); */ NS_IMETHODIMP nsXPConnect::CreateStackFrameLocation(PRUint32 aLanguage, const char *aFilename, const char *aFunctionName, PRInt32 aLineNumber, nsIStackFrame *aCaller, nsIStackFrame **_retval) { NS_ASSERTION(_retval, "bad param"); return XPCJSStack::CreateStackFrameLocation(aLanguage, aFilename, aFunctionName, aLineNumber, aCaller, _retval); } /* readonly attribute nsIStackFrame CurrentJSStack; */ NS_IMETHODIMP nsXPConnect::GetCurrentJSStack(nsIStackFrame * *aCurrentJSStack) { NS_ASSERTION(aCurrentJSStack, "bad param"); *aCurrentJSStack = nsnull; JSContext* cx; // is there a current context available? if(NS_SUCCEEDED(Peek(&cx)) && cx) { nsCOMPtr<nsIStackFrame> stack; XPCJSStack::CreateStack(cx, getter_AddRefs(stack)); if(stack) { // peel off native frames... PRUint32 language; nsCOMPtr<nsIStackFrame> caller; while(stack && NS_SUCCEEDED(stack->GetLanguage(&language)) && language != nsIProgrammingLanguage::JAVASCRIPT && NS_SUCCEEDED(stack->GetCaller(getter_AddRefs(caller))) && caller) { stack = caller; } NS_IF_ADDREF(*aCurrentJSStack = stack); } } return NS_OK; } /* readonly attribute nsIXPCNativeCallContext CurrentNativeCallContext; */ NS_IMETHODIMP nsXPConnect::GetCurrentNativeCallContext(nsAXPCNativeCallContext * *aCurrentNativeCallContext) { NS_ASSERTION(aCurrentNativeCallContext, "bad param"); XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull); if(data) { *aCurrentNativeCallContext = data->GetCallContext(); return NS_OK; } //else... *aCurrentNativeCallContext = nsnull; return UnexpectedFailure(NS_ERROR_FAILURE); } /* attribute nsIException PendingException; */ NS_IMETHODIMP nsXPConnect::GetPendingException(nsIException * *aPendingException) { NS_ASSERTION(aPendingException, "bad param"); XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull); if(!data) { *aPendingException = nsnull; return UnexpectedFailure(NS_ERROR_FAILURE); } return data->GetException(aPendingException); } NS_IMETHODIMP nsXPConnect::SetPendingException(nsIException * aPendingException) { XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull); if(!data) return UnexpectedFailure(NS_ERROR_FAILURE); data->SetException(aPendingException); return NS_OK; } NS_IMETHODIMP nsXPConnect::SyncJSContexts(void) { // Do-nothing compatibility function return NS_OK; } /* nsIXPCFunctionThisTranslator setFunctionThisTranslator (in nsIIDRef aIID, in nsIXPCFunctionThisTranslator aTranslator); */ NS_IMETHODIMP nsXPConnect::SetFunctionThisTranslator(const nsIID & aIID, nsIXPCFunctionThisTranslator *aTranslator, nsIXPCFunctionThisTranslator **_retval) { XPCJSRuntime* rt = GetRuntime(); nsIXPCFunctionThisTranslator* old; IID2ThisTranslatorMap* map = rt->GetThisTranslatorMap(); { XPCAutoLock lock(rt->GetMapLock()); // scoped lock if(_retval) { old = map->Find(aIID); NS_IF_ADDREF(old); *_retval = old; } map->Add(aIID, aTranslator); } return NS_OK; } /* nsIXPCFunctionThisTranslator getFunctionThisTranslator (in nsIIDRef aIID); */ NS_IMETHODIMP nsXPConnect::GetFunctionThisTranslator(const nsIID & aIID, nsIXPCFunctionThisTranslator **_retval) { XPCJSRuntime* rt = GetRuntime(); nsIXPCFunctionThisTranslator* old; IID2ThisTranslatorMap* map = rt->GetThisTranslatorMap(); { XPCAutoLock lock(rt->GetMapLock()); // scoped lock old = map->Find(aIID); NS_IF_ADDREF(old); *_retval = old; } return NS_OK; } /* void setSafeJSContextForCurrentThread (in JSContextPtr cx); */ NS_IMETHODIMP nsXPConnect::SetSafeJSContextForCurrentThread(JSContext * cx) { XPCCallContext ccx(NATIVE_CALLER); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); return ccx.GetThreadData()->GetJSContextStack()->SetSafeJSContext(cx); } /* void clearAllWrappedNativeSecurityPolicies (); */ NS_IMETHODIMP nsXPConnect::ClearAllWrappedNativeSecurityPolicies() { XPCCallContext ccx(NATIVE_CALLER); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); return XPCWrappedNativeScope::ClearAllWrappedNativeSecurityPolicies(ccx); } /* void restoreWrappedNativePrototype (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsIClassInfo aClassInfo, in nsIXPConnectJSObjectHolder aPrototype); */ NS_IMETHODIMP nsXPConnect::RestoreWrappedNativePrototype(JSContext * aJSContext, JSObject * aScope, nsIClassInfo * aClassInfo, nsIXPConnectJSObjectHolder * aPrototype) { XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); if(!aClassInfo || !aPrototype) return UnexpectedFailure(NS_ERROR_INVALID_ARG); JSObject *protoJSObject; nsresult rv = aPrototype->GetJSObject(&protoJSObject); if(NS_FAILED(rv)) return UnexpectedFailure(rv); if(!IS_PROTO_CLASS(STOBJ_GET_CLASS(protoJSObject))) return UnexpectedFailure(NS_ERROR_INVALID_ARG); XPCWrappedNativeScope* scope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, aScope); if(!scope) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeProto *proto = (XPCWrappedNativeProto*)xpc_GetJSPrivate(protoJSObject); if(!proto) return UnexpectedFailure(NS_ERROR_FAILURE); if(scope != proto->GetScope()) { NS_ERROR("Attempt to reset prototype to a prototype from a" "different scope!"); return UnexpectedFailure(NS_ERROR_INVALID_ARG); } XPCNativeScriptableInfo *si = proto->GetScriptableInfo(); if(si && si->GetFlags().DontSharePrototype()) return UnexpectedFailure(NS_ERROR_INVALID_ARG); ClassInfo2WrappedNativeProtoMap* map = scope->GetWrappedNativeProtoMap(proto->ClassIsMainThreadOnly()); XPCLock* lock = GetRuntime()->GetMapLock(); { // scoped lock XPCAutoLock al(lock); XPCWrappedNativeProtoMap* detachedMap = GetRuntime()->GetDetachedWrappedNativeProtoMap(); // If we're replacing an old proto, make sure to put it on the // map of detached wrapped native protos so that the old proto // gets properly cleaned up, especially during shutdown. XPCWrappedNativeProto *oldProto = map->Find(aClassInfo); if(oldProto) { detachedMap->Add(oldProto); // ClassInfo2WrappedNativeProtoMap doesn't ever replace // entries in the map, so now since we know there's an // entry for aClassInfo in the map we have to remove it to // be able to add the new one. map->Remove(aClassInfo); // This code should do the right thing even if we're // restoring the current proto, but warn in that case // since doing that is pointless. NS_ASSERTION(proto != oldProto, "Restoring current prototype, fix caller!"); } map->Add(aClassInfo, proto); // Remove the prototype from the map of detached wrapped // native prototypes now that the prototype is part of a scope // again. detachedMap->Remove(proto); } // The global in this scope didn't change, but a prototype did // (most likely the global object's prototype), which means the // scope needs to get a chance to update its cached // Object.prototype pointers etc. scope->SetGlobal(ccx, aScope); return NS_OK; } NS_IMETHODIMP nsXPConnect::CreateSandbox(JSContext *cx, nsIPrincipal *principal, nsIXPConnectJSObjectHolder **_retval) { #ifdef XPCONNECT_STANDALONE return NS_ERROR_NOT_AVAILABLE; #else /* XPCONNECT_STANDALONE */ XPCCallContext ccx(NATIVE_CALLER, cx); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); *_retval = nsnull; jsval rval = JSVAL_VOID; AUTO_MARK_JSVAL(ccx, &rval); nsresult rv = xpc_CreateSandboxObject(cx, &rval, principal); NS_ASSERTION(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval), "Bad return value from xpc_CreateSandboxObject()!"); if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(rval)) { *_retval = XPCJSObjectHolder::newHolder(ccx, JSVAL_TO_OBJECT(rval)); NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY); NS_ADDREF(*_retval); } return rv; #endif /* XPCONNECT_STANDALONE */ } NS_IMETHODIMP nsXPConnect::EvalInSandboxObject(const nsAString& source, JSContext *cx, nsIXPConnectJSObjectHolder *sandbox, PRBool returnStringOnly, jsval *rval) { #ifdef XPCONNECT_STANDALONE return NS_ERROR_NOT_AVAILABLE; #else /* XPCONNECT_STANDALONE */ if (!sandbox) return NS_ERROR_INVALID_ARG; JSObject *obj; nsresult rv = sandbox->GetJSObject(&obj); NS_ENSURE_SUCCESS(rv, rv); return xpc_EvalInSandbox(cx, obj, source, NS_ConvertUTF16toUTF8(source).get(), 1, JSVERSION_DEFAULT, returnStringOnly, rval); #endif /* XPCONNECT_STANDALONE */ } /* void GetXPCWrappedNativeJSClassInfo(out JSEqualityOp equality1, JSEqualityOp *equality2); */ NS_IMETHODIMP nsXPConnect::GetXPCWrappedNativeJSClassInfo(JSEqualityOp *equality1, JSEqualityOp *equality2) { // Expose the equality pointer used by IS_WRAPPER_CLASS(). If that macro // ever changes, this function needs to stay in sync. *equality1 = &XPC_WN_Equality; *equality2 = &XPC_SWN_Equality; return NS_OK; } /* nsIXPConnectJSObjectHolder getWrappedNativePrototype (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsIClassInfo aClassInfo); */ NS_IMETHODIMP nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext, JSObject * aScope, nsIClassInfo *aClassInfo, nsIXPConnectJSObjectHolder **_retval) { XPCCallContext ccx(NATIVE_CALLER, aJSContext); if(!ccx.IsValid()) return UnexpectedFailure(NS_ERROR_FAILURE); XPCWrappedNativeScope* scope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, aScope); if(!scope) return UnexpectedFailure(NS_ERROR_FAILURE); XPCNativeScriptableCreateInfo sciProto; XPCWrappedNative::GatherProtoScriptableCreateInfo(aClassInfo, &sciProto); AutoMarkingWrappedNativeProtoPtr proto(ccx); proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, scope, aClassInfo, &sciProto, JS_FALSE, OBJ_IS_NOT_GLOBAL); if(!proto) return UnexpectedFailure(NS_ERROR_FAILURE); nsIXPConnectJSObjectHolder* holder; *_retval = holder = XPCJSObjectHolder::newHolder(ccx, proto->GetJSProtoObject()); if(!holder) return UnexpectedFailure(NS_ERROR_FAILURE); NS_ADDREF(holder); return NS_OK; } /* [noscript] JSVal GetCrossOriginWrapperForValue(in JSContextPtr aJSContext, in JSVal aCurrentVal); */ NS_IMETHODIMP nsXPConnect::GetXOWForObject(JSContext * aJSContext, JSObject * aParent, JSObject * aWrappedObj, jsval * rval) { *rval = OBJECT_TO_JSVAL(aWrappedObj); return XPC_XOW_WrapObject(aJSContext, aParent, rval) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsXPConnect::GetCOWForObject(JSContext * aJSContext, JSObject * aParent, JSObject * aWrappedObj, jsval * rval) { *rval = OBJECT_TO_JSVAL(aWrappedObj); return XPC_COW_WrapObject(aJSContext, aParent, *rval, rval) ? NS_OK : NS_ERROR_FAILURE; } static inline PRBool PerformOp(JSContext *cx, PRUint32 aWay, JSObject *obj) { NS_ASSERTION(aWay == nsIXPConnect::XPC_XOW_CLEARSCOPE, "Nothing else is implemented yet"); JS_ClearScope(cx, obj); return PR_TRUE; } /* [noscript] void updateXOWs (in JSContextPtr aJSContext, * in nsIXPConnectJSObjectHolder aObject, * in PRUint32 aWay); */ NS_IMETHODIMP nsXPConnect::UpdateXOWs(JSContext* aJSContext, nsIXPConnectWrappedNative* aObject, PRUint32 aWay) { typedef WrappedNative2WrapperMap::Link Link; XPCWrappedNative* wn = static_cast<XPCWrappedNative *>(aObject); XPCWrappedNativeScope* scope = wn->GetScope(); WrappedNative2WrapperMap* map = scope->GetWrapperMap(); Link* list; { XPCAutoLock al(GetRuntime()->GetMapLock()); list = map->FindLink(wn->GetFlatJSObject()); } if(!list) return NS_OK; // No wrappers to update. AutoJSRequestWithNoCallContext req(aJSContext); Link* cur = list; if(cur->obj && !PerformOp(aJSContext, aWay, cur->obj)) return NS_ERROR_FAILURE; for(cur = (Link *)PR_NEXT_LINK(list); cur != list; cur = (Link *)PR_NEXT_LINK(cur)) { if(!PerformOp(aJSContext, aWay, cur->obj)) return NS_ERROR_FAILURE; } return NS_OK; } /* void releaseJSContext (in JSContextPtr aJSContext, in PRBool noGC); */ NS_IMETHODIMP nsXPConnect::ReleaseJSContext(JSContext * aJSContext, PRBool noGC) { NS_ASSERTION(aJSContext, "bad param"); XPCPerThreadData* tls = XPCPerThreadData::GetData(aJSContext); if(tls) { XPCCallContext* ccx = nsnull; for(XPCCallContext* cur = tls->GetCallContext(); cur; cur = cur->GetPrevCallContext()) { if(cur->GetJSContext() == aJSContext) { ccx = cur; // Keep looping to find the deepest matching call context. } } if(ccx) { #ifdef DEBUG_xpc_hacker printf("!xpc - deferring destruction of JSContext @ %p\n", (void *)aJSContext); #endif ccx->SetDestroyJSContextInDestructor(JS_TRUE); JS_ClearNewbornRoots(aJSContext); return NS_OK; } // else continue on and synchronously destroy the JSContext ... NS_ASSERTION(!tls->GetJSContextStack() || !tls->GetJSContextStack()-> DEBUG_StackHasJSContext(aJSContext), "JSContext still in threadjscontextstack!"); } if(noGC) JS_DestroyContextNoGC(aJSContext); else JS_DestroyContext(aJSContext); return NS_OK; } /* void debugDump (in short depth); */ NS_IMETHODIMP nsXPConnect::DebugDump(PRInt16 depth) { #ifdef DEBUG depth-- ; XPC_LOG_ALWAYS(("nsXPConnect @ %x with mRefCnt = %d", this, mRefCnt.get())); XPC_LOG_INDENT(); XPC_LOG_ALWAYS(("gSelf @ %x", gSelf)); XPC_LOG_ALWAYS(("gOnceAliveNowDead is %d", (int)gOnceAliveNowDead)); XPC_LOG_ALWAYS(("mDefaultSecurityManager @ %x", mDefaultSecurityManager)); XPC_LOG_ALWAYS(("mDefaultSecurityManagerFlags of %x", mDefaultSecurityManagerFlags)); XPC_LOG_ALWAYS(("mInterfaceInfoManager @ %x", mInterfaceInfoManager.get())); if(mRuntime) { if(depth) mRuntime->DebugDump(depth); else XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", mRuntime)); } else XPC_LOG_ALWAYS(("mRuntime is null")); XPCWrappedNativeScope::DebugDumpAllScopes(depth); XPC_LOG_OUTDENT(); #endif return NS_OK; } /* void debugDumpObject (in nsISupports aCOMObj, in short depth); */ NS_IMETHODIMP nsXPConnect::DebugDumpObject(nsISupports *p, PRInt16 depth) { #ifdef DEBUG if(!depth) return NS_OK; if(!p) { XPC_LOG_ALWAYS(("*** Cound not dump object with NULL address")); return NS_OK; } nsIXPConnect* xpc; nsIXPCWrappedJSClass* wjsc; nsIXPConnectWrappedNative* wn; nsIXPConnectWrappedJS* wjs; if(NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnect), (void**)&xpc))) { XPC_LOG_ALWAYS(("Dumping a nsIXPConnect...")); xpc->DebugDump(depth); NS_RELEASE(xpc); } else if(NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPCWrappedJSClass), (void**)&wjsc))) { XPC_LOG_ALWAYS(("Dumping a nsIXPCWrappedJSClass...")); wjsc->DebugDump(depth); NS_RELEASE(wjsc); } else if(NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnectWrappedNative), (void**)&wn))) { XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedNative...")); wn->DebugDump(depth); NS_RELEASE(wn); } else if(NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnectWrappedJS), (void**)&wjs))) { XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedJS...")); wjs->DebugDump(depth); NS_RELEASE(wjs); } else XPC_LOG_ALWAYS(("*** Could not dump the nsISupports @ %x", p)); #endif return NS_OK; } /* void debugDumpJSStack (in PRBool showArgs, in PRBool showLocals, in PRBool showThisProps); */ NS_IMETHODIMP nsXPConnect::DebugDumpJSStack(PRBool showArgs, PRBool showLocals, PRBool showThisProps) { JSContext* cx; if(NS_FAILED(Peek(&cx))) printf("failed to peek into nsIThreadJSContextStack service!\n"); else if(!cx) printf("there is no JSContext on the nsIThreadJSContextStack!\n"); else xpc_DumpJSStack(cx, showArgs, showLocals, showThisProps); return NS_OK; } /* void debugDumpEvalInJSStackFrame (in PRUint32 aFrameNumber, in string aSourceText); */ NS_IMETHODIMP nsXPConnect::DebugDumpEvalInJSStackFrame(PRUint32 aFrameNumber, const char *aSourceText) { JSContext* cx; if(NS_FAILED(Peek(&cx))) printf("failed to peek into nsIThreadJSContextStack service!\n"); else if(!cx) printf("there is no JSContext on the nsIThreadJSContextStack!\n"); else xpc_DumpEvalInJSStackFrame(cx, aFrameNumber, aSourceText); return NS_OK; } /* JSVal variantToJS (in JSContextPtr ctx, in JSObjectPtr scope, in nsIVariant value); */ NS_IMETHODIMP nsXPConnect::VariantToJS(JSContext* ctx, JSObject* scope, nsIVariant* value, jsval* _retval) { NS_PRECONDITION(ctx, "bad param"); NS_PRECONDITION(scope, "bad param"); NS_PRECONDITION(value, "bad param"); NS_PRECONDITION(_retval, "bad param"); XPCCallContext ccx(NATIVE_CALLER, ctx); if(!ccx.IsValid()) return NS_ERROR_FAILURE; XPCLazyCallContext lccx(ccx); nsresult rv = NS_OK; if(!XPCVariant::VariantDataToJS(lccx, value, scope, &rv, _retval)) { if(NS_FAILED(rv)) return rv; return NS_ERROR_FAILURE; } return NS_OK; } /* nsIVariant JSToVariant (in JSContextPtr ctx, in JSVal value); */ NS_IMETHODIMP nsXPConnect::JSToVariant(JSContext* ctx, jsval value, nsIVariant** _retval) { NS_PRECONDITION(ctx, "bad param"); NS_PRECONDITION(value, "bad param"); NS_PRECONDITION(_retval, "bad param"); XPCCallContext ccx(NATIVE_CALLER, ctx); if(!ccx.IsValid()) return NS_ERROR_FAILURE; *_retval = XPCVariant::newVariant(ccx, value); if(!(*_retval)) return NS_ERROR_FAILURE; return NS_OK; } /* void flagSystemFilenamePrefix (in string filenamePrefix, * in PRBool aWantNativeWrappers); */ NS_IMETHODIMP nsXPConnect::FlagSystemFilenamePrefix(const char *aFilenamePrefix, PRBool aWantNativeWrappers) { NS_PRECONDITION(aFilenamePrefix, "bad param"); JSRuntime* rt = GetRuntime()->GetJSRuntime();; uint32 flags = JSFILENAME_SYSTEM; if(aWantNativeWrappers) flags |= JSFILENAME_PROTECTED; if(!JS_FlagScriptFilenamePrefix(rt, aFilenamePrefix, flags)) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } NS_IMETHODIMP nsXPConnect::OnProcessNextEvent(nsIThreadInternal *aThread, PRBool aMayWait, PRUint32 aRecursionDepth) { // Push a null JSContext so that we don't see any script during // event processing. return Push(nsnull); } NS_IMETHODIMP nsXPConnect::AfterProcessNextEvent(nsIThreadInternal *aThread, PRUint32 aRecursionDepth) { return Pop(nsnull); } NS_IMETHODIMP nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread) { NS_NOTREACHED("Why tell us?"); return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP nsXPConnect::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer) { return mRuntime->AddJSHolder(aHolder, aTracer); } NS_IMETHODIMP nsXPConnect::RemoveJSHolder(void* aHolder) { return mRuntime->RemoveJSHolder(aHolder); } NS_IMETHODIMP nsXPConnect::SetReportAllJSExceptions(PRBool newval) { // Ignore if the environment variable was set. if (gReportAllJSExceptions != 1) gReportAllJSExceptions = newval ? 2 : 0; return NS_OK; } /* [noscript, notxpcom] PRBool defineDOMQuickStubs (in JSContextPtr cx, in JSObjectPtr proto, in PRUint32 flags, in PRUint32 interfaceCount, [array, size_is (interfaceCount)] in nsIIDPtr interfaceArray); */ NS_IMETHODIMP_(PRBool) nsXPConnect::DefineDOMQuickStubs(JSContext * cx, JSObject * proto, PRUint32 flags, PRUint32 interfaceCount, const nsIID * *interfaceArray) { return DOM_DefineQuickStubs(cx, proto, flags, interfaceCount, interfaceArray); } NS_IMETHODIMP nsXPConnect::GetWrapperForObject(JSContext* aJSContext, JSObject* aObject, JSObject* aScope, nsIPrincipal* aPrincipal, PRUint32 aFilenameFlags, jsval* _retval) { NS_ASSERTION(aFilenameFlags != JSFILENAME_NULL, "Null filename!"); NS_ASSERTION(XPCPerThreadData::IsMainThread(aJSContext), "Must only be called from the main thread as these wrappers " "are not threadsafe!"); JSAutoRequest ar(aJSContext); XPCWrappedNativeScope *objectscope; XPCWrappedNative *wrapper = nsnull; if(IS_SLIM_WRAPPER(aObject)) { objectscope = GetSlimWrapperProto(aObject)->GetScope(); } else { wrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(aJSContext, aObject); if(!wrapper) { // Couldn't get the wrapped native (maybe a prototype?) so just return // the original object. *_retval = OBJECT_TO_JSVAL(aObject); return NS_OK; } objectscope = wrapper->GetScope(); } XPCWrappedNativeScope *xpcscope = XPCWrappedNativeScope::FindInJSObjectScope(aJSContext, aScope); if(!xpcscope) return NS_ERROR_FAILURE; #ifdef DEBUG_mrbkap { JSObject *scopeobj = xpcscope->GetGlobalJSObject(); JSObject *toInnerize = scopeobj; OBJ_TO_INNER_OBJECT(aJSContext, toInnerize); NS_ASSERTION(toInnerize == scopeobj, "Scope chain ending in outer object?"); } #endif { JSObject *possibleOuter = objectscope->GetGlobalJSObject(); OBJ_TO_INNER_OBJECT(aJSContext, possibleOuter); if(!possibleOuter) return NS_ERROR_FAILURE; if(objectscope->GetGlobalJSObject() != possibleOuter) { objectscope = XPCWrappedNativeScope::FindInJSObjectScope(aJSContext, possibleOuter); NS_ASSERTION(objectscope, "Unable to find a scope from an object"); } } *_retval = OBJECT_TO_JSVAL(aObject); JSBool sameOrigin; JSBool sameScope = xpc_SameScope(objectscope, xpcscope, &sameOrigin); JSBool forceXOW = XPC_XOW_ClassNeedsXOW(STOBJ_GET_CLASS(aObject)->name); // We can do nothing if: // - We're wrapping a system object // or // - We're from the same *scope* AND // - We're not about to force a XOW (e.g. for "window") OR // - We're not actually going to create a XOW (we're wrapping for // chrome). if(aObject->isSystem() || (sameScope && (!forceXOW || (aFilenameFlags & JSFILENAME_SYSTEM)))) return NS_OK; if(!wrapper) { SLIM_LOG_WILL_MORPH(aJSContext, aObject); if(!MorphSlimWrapper(aJSContext, aObject)) return NS_ERROR_FAILURE; wrapper = static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(aObject)); } JSObject* wrappedObj = nsnull; if(aFilenameFlags & JSFILENAME_PROTECTED) { wrappedObj = XPCNativeWrapper::GetNewOrUsed(aJSContext, wrapper, aPrincipal); } else if(aFilenameFlags & JSFILENAME_SYSTEM) { jsval val = OBJECT_TO_JSVAL(aObject); if(XPC_SJOW_Construct(aJSContext, nsnull, 1, &val, &val)) wrappedObj = JSVAL_TO_OBJECT(val); } else { // We don't wrap anything same origin unless the class name requires // it. if(sameOrigin && !forceXOW) return NS_OK; jsval val = OBJECT_TO_JSVAL(aObject); if(XPC_XOW_WrapObject(aJSContext, aScope, &val, wrapper)) wrappedObj = JSVAL_TO_OBJECT(val); } if(!wrappedObj) return NS_ERROR_FAILURE; *_retval = OBJECT_TO_JSVAL(wrappedObj); if(wrapper->NeedsChromeWrapper() && !XPC_SOW_WrapObject(aJSContext, aScope, *_retval, _retval)) return NS_ERROR_FAILURE; return NS_OK; } /* attribute JSRuntime runtime; */ NS_IMETHODIMP nsXPConnect::GetRuntime(JSRuntime **runtime) { if(!runtime) return NS_ERROR_NULL_POINTER; *runtime = GetRuntime()->GetJSRuntime(); return NS_OK; } /* attribute nsIXPCScriptable backstagePass; */ NS_IMETHODIMP nsXPConnect::GetBackstagePass(nsIXPCScriptable **bsp) { if(!mBackstagePass) { #ifndef XPCONNECT_STANDALONE nsCOMPtr<nsIPrincipal> sysprin; nsCOMPtr<nsIScriptSecurityManager> secman = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); if(!secman) return NS_ERROR_NOT_AVAILABLE; if(NS_FAILED(secman->GetSystemPrincipal(getter_AddRefs(sysprin)))) return NS_ERROR_NOT_AVAILABLE; mBackstagePass = new BackstagePass(sysprin); #else mBackstagePass = new BackstagePass(); #endif if(!mBackstagePass) return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(*bsp = mBackstagePass); return NS_OK; } // nsIJSContextStack and nsIThreadJSContextStack implementations /* readonly attribute PRInt32 Count; */ NS_IMETHODIMP nsXPConnect::GetCount(PRInt32 *aCount) { if(!aCount) return NS_ERROR_NULL_POINTER; XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull); if(!data) { *aCount = 0; return NS_ERROR_FAILURE; } return data->GetJSContextStack()->GetCount(aCount); } /* JSContext Peek (); */ NS_IMETHODIMP nsXPConnect::Peek(JSContext * *_retval) { if(!_retval) return NS_ERROR_NULL_POINTER; XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull); if(!data) { *_retval = nsnull; return NS_ERROR_FAILURE; } return data->GetJSContextStack()->Peek(_retval); } /* JSContext Pop (); */ NS_IMETHODIMP nsXPConnect::Pop(JSContext * *_retval) { XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull); if(!data) { if(_retval) *_retval = nsnull; return NS_ERROR_FAILURE; } return data->GetJSContextStack()->Pop(_retval); } /* void Push (in JSContext cx); */ NS_IMETHODIMP nsXPConnect::Push(JSContext * cx) { XPCPerThreadData* data = XPCPerThreadData::GetData(cx); if(!data) return NS_ERROR_FAILURE; return data->GetJSContextStack()->Push(cx); } /* attribute JSContext SafeJSContext; */ NS_IMETHODIMP nsXPConnect::GetSafeJSContext(JSContext * *aSafeJSContext) { NS_ASSERTION(aSafeJSContext, "loser!"); XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull); if(!data) { *aSafeJSContext = nsnull; return NS_ERROR_FAILURE; } return data->GetJSContextStack()->GetSafeJSContext(aSafeJSContext); } /* attribute JSContext SafeJSContext; */ NS_IMETHODIMP nsXPConnect::SetSafeJSContext(JSContext * aSafeJSContext) { XPCPerThreadData* data = XPCPerThreadData::GetData(aSafeJSContext); if(!data) return NS_ERROR_FAILURE; return data->GetJSContextStack()->SetSafeJSContext(aSafeJSContext); } nsIPrincipal* nsXPConnect::GetPrincipal(JSObject* obj, PRBool allowShortCircuit) const { NS_ASSERTION(IS_WRAPPER_CLASS(STOBJ_GET_CLASS(obj)) || IS_SLIM_WRAPPER(obj), "What kind of wrapper is this?"); if(IS_WRAPPER_CLASS(STOBJ_GET_CLASS(obj))) { XPCWrappedNative *xpcWrapper = (XPCWrappedNative *)xpc_GetJSPrivate(obj); if (xpcWrapper) { if (allowShortCircuit) { nsIPrincipal *result = xpcWrapper->GetObjectPrincipal(); if (result) { return result; } } // If not, check if it points to an nsIScriptObjectPrincipal nsCOMPtr<nsIScriptObjectPrincipal> objPrin = do_QueryInterface(xpcWrapper->Native()); if (objPrin) { nsIPrincipal *result = objPrin->GetPrincipal(); if (result) { return result; } } } } else { if (allowShortCircuit) { nsIPrincipal *result = GetSlimWrapperProto(obj)->GetScope()->GetPrincipal(); if (result) { return result; } } nsCOMPtr<nsIScriptObjectPrincipal> objPrin = do_QueryInterface((nsISupports*)xpc_GetJSPrivate(obj)); if (objPrin) { nsIPrincipal *result = objPrin->GetPrincipal(); if (result) { return result; } } } return nsnull; } /* These are here to be callable from a debugger */ JS_BEGIN_EXTERN_C JS_EXPORT_API(void) DumpJSStack() { nsresult rv; nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); if(NS_SUCCEEDED(rv) && xpc) xpc->DebugDumpJSStack(PR_TRUE, PR_TRUE, PR_FALSE); else printf("failed to get XPConnect service!\n"); } JS_EXPORT_API(void) DumpJSEval(PRUint32 frameno, const char* text) { nsresult rv; nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv)); if(NS_SUCCEEDED(rv) && xpc) xpc->DebugDumpEvalInJSStackFrame(frameno, text); else printf("failed to get XPConnect service!\n"); } JS_EXPORT_API(void) DumpJSObject(JSObject* obj) { xpc_DumpJSObject(obj); } JS_EXPORT_API(void) DumpJSValue(jsval val) { printf("Dumping 0x%p. Value tag is %u.\n", (void *) val, (PRUint32) JSVAL_TAG(val)); if(JSVAL_IS_NULL(val)) { printf("Value is null\n"); } else if(JSVAL_IS_OBJECT(val)) { printf("Value is an object\n"); JSObject* obj = JSVAL_TO_OBJECT(val); DumpJSObject(obj); } else if(JSVAL_IS_NUMBER(val)) { printf("Value is a number: "); if(JSVAL_IS_INT(val)) printf("Integer %i\n", JSVAL_TO_INT(val)); else if(JSVAL_IS_DOUBLE(val)) printf("Floating-point value %f\n", *JSVAL_TO_DOUBLE(val)); } else if(JSVAL_IS_STRING(val)) { printf("Value is a string: "); JSString* string = JSVAL_TO_STRING(val); char* bytes = JS_GetStringBytes(string); printf("<%s>\n", bytes); } else if(JSVAL_IS_BOOLEAN(val)) { printf("Value is boolean: "); printf(JSVAL_TO_BOOLEAN(val) ? "true" : "false"); } else if(JSVAL_IS_VOID(val)) { printf("Value is undefined\n"); } else { printf("No idea what this value is.\n"); } } JS_END_EXTERN_C