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: #include "mozilla/Attributes.h" michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: #include "prlog.h" michael@0: #ifdef ANDROID michael@0: #include michael@0: #endif michael@0: #ifdef XP_WIN michael@0: #include michael@0: #endif michael@0: michael@0: #include "jsapi.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "mozilla/Module.h" michael@0: #include "nsIFile.h" michael@0: #include "mozJSComponentLoader.h" michael@0: #include "mozJSLoaderUtils.h" michael@0: #include "nsIJSRuntimeService.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIFileURL.h" michael@0: #include "nsIJARURI.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsDOMBlobBuilder.h" michael@0: #include "jsprf.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "xpcprivate.h" michael@0: #include "xpcpublic.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "WrapperFactory.h" michael@0: michael@0: #include "mozilla/scache/StartupCache.h" michael@0: #include "mozilla/scache/StartupCacheUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: #include "js/OldDebugAPI.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::scache; michael@0: using namespace xpc; michael@0: using namespace JS; michael@0: michael@0: // This JSClass exists to trick silly code that expects toString()ing the michael@0: // global in a component scope to return something with "BackstagePass" in it michael@0: // to continue working. michael@0: static const JSClass kFakeBackstagePassJSClass = michael@0: { michael@0: "FakeBackstagePass", michael@0: 0, michael@0: JS_PropertyStub, michael@0: JS_DeletePropertyStub, michael@0: JS_PropertyStub, michael@0: JS_StrictPropertyStub, michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub michael@0: }; michael@0: michael@0: static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1"; michael@0: static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1"; michael@0: static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1"; michael@0: static const char kJSCachePrefix[] = "jsloader"; michael@0: michael@0: #define HAVE_PR_MEMMAP michael@0: michael@0: /** michael@0: * Buffer sizes for serialization and deserialization of scripts. michael@0: * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008 michael@0: */ michael@0: #define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024) michael@0: #define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192) michael@0: michael@0: #ifdef PR_LOGGING michael@0: // NSPR_LOG_MODULES=JSComponentLoader:5 michael@0: static PRLogModuleInfo *gJSCLLog; michael@0: #endif michael@0: michael@0: #define LOG(args) PR_LOG(gJSCLLog, PR_LOG_DEBUG, args) michael@0: michael@0: // Components.utils.import error messages michael@0: #define ERROR_SCOPE_OBJ "%s - Second argument must be an object." michael@0: #define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present." michael@0: #define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array." michael@0: #define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS." michael@0: #define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string." michael@0: #define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'." michael@0: #define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object." michael@0: michael@0: static bool michael@0: Dump(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() == 0) michael@0: return true; michael@0: michael@0: JSString *str = JS::ToString(cx, args[0]); michael@0: if (!str) michael@0: return false; michael@0: michael@0: size_t length; michael@0: const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length); michael@0: if (!chars) michael@0: return false; michael@0: michael@0: NS_ConvertUTF16toUTF8 utf8str(reinterpret_cast(chars), michael@0: length); michael@0: #ifdef ANDROID michael@0: __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get()); michael@0: #endif michael@0: #ifdef XP_WIN michael@0: if (IsDebuggerPresent()) { michael@0: OutputDebugStringW(reinterpret_cast(chars)); michael@0: } michael@0: #endif michael@0: fputs(utf8str.get(), stdout); michael@0: fflush(stdout); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Debug(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: #ifdef DEBUG michael@0: return Dump(cx, argc, vp); michael@0: #else michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: static bool michael@0: File(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() == 0) { michael@0: XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr native; michael@0: nsresult rv = nsDOMMultipartFile::NewFile(getter_AddRefs(native)); michael@0: if (NS_FAILED(rv)) { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr initializer = do_QueryInterface(native); michael@0: MOZ_ASSERT(initializer); michael@0: michael@0: rv = initializer->Initialize(nullptr, cx, nullptr, args); michael@0: if (NS_FAILED(rv)) { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: nsXPConnect *xpc = nsXPConnect::XPConnect(); michael@0: JSObject *glob = CurrentGlobalOrNull(cx); michael@0: michael@0: nsCOMPtr holder; michael@0: rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr, michael@0: &NS_GET_IID(nsISupports), michael@0: true, args.rval()); michael@0: if (NS_FAILED(rv)) { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Blob(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: nsCOMPtr native; michael@0: nsresult rv = nsDOMMultipartFile::NewBlob(getter_AddRefs(native)); michael@0: if (NS_FAILED(rv)) { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr initializer = do_QueryInterface(native); michael@0: MOZ_ASSERT(initializer); michael@0: michael@0: rv = initializer->Initialize(nullptr, cx, nullptr, args); michael@0: if (NS_FAILED(rv)) { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: michael@0: nsXPConnect *xpc = nsXPConnect::XPConnect(); michael@0: JSObject *glob = CurrentGlobalOrNull(cx); michael@0: michael@0: nsCOMPtr holder; michael@0: rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr, michael@0: &NS_GET_IID(nsISupports), michael@0: true, args.rval()); michael@0: if (NS_FAILED(rv)) { michael@0: XPCThrower::Throw(rv, cx); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static const JSFunctionSpec gGlobalFun[] = { michael@0: JS_FS("dump", Dump, 1,0), michael@0: JS_FS("debug", Debug, 1,0), michael@0: JS_FS("atob", Atob, 1,0), michael@0: JS_FS("btoa", Btoa, 1,0), michael@0: JS_FS("File", File, 1,JSFUN_CONSTRUCTOR), michael@0: JS_FS("Blob", Blob, 2,JSFUN_CONSTRUCTOR), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS JSCLContextHelper michael@0: { michael@0: public: michael@0: JSCLContextHelper(JSContext* aCx); michael@0: ~JSCLContextHelper(); michael@0: michael@0: void reportErrorAfterPop(char *buf); michael@0: michael@0: operator JSContext*() const {return mContext;} michael@0: michael@0: private: michael@0: michael@0: JSContext* mContext; michael@0: nsCxPusher mPusher; michael@0: char* mBuf; michael@0: michael@0: // prevent copying and assignment michael@0: JSCLContextHelper(const JSCLContextHelper &) MOZ_DELETE; michael@0: const JSCLContextHelper& operator=(const JSCLContextHelper &) MOZ_DELETE; michael@0: }; michael@0: michael@0: michael@0: class JSCLAutoErrorReporterSetter michael@0: { michael@0: public: michael@0: JSCLAutoErrorReporterSetter(JSContext* cx, JSErrorReporter reporter) michael@0: {mContext = cx; mOldReporter = JS_SetErrorReporter(cx, reporter);} michael@0: ~JSCLAutoErrorReporterSetter() michael@0: {JS_SetErrorReporter(mContext, mOldReporter);} michael@0: private: michael@0: JSContext* mContext; michael@0: JSErrorReporter mOldReporter; michael@0: michael@0: JSCLAutoErrorReporterSetter(const JSCLAutoErrorReporterSetter &) MOZ_DELETE; michael@0: const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &) MOZ_DELETE; michael@0: }; michael@0: michael@0: static nsresult michael@0: ReportOnCaller(JSContext *callerContext, michael@0: const char *format, ...) { michael@0: if (!callerContext) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: michael@0: char *buf = JS_vsmprintf(format, ap); michael@0: if (!buf) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: JS_ReportError(callerContext, buf); michael@0: JS_smprintf_free(buf); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: ReportOnCaller(JSCLContextHelper &helper, michael@0: const char *format, ...) michael@0: { michael@0: va_list ap; michael@0: va_start(ap, format); michael@0: michael@0: char *buf = JS_vsmprintf(format, ap); michael@0: if (!buf) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: helper.reportErrorAfterPop(buf); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: mozJSComponentLoader::mozJSComponentLoader() michael@0: : mRuntime(nullptr), michael@0: mContext(nullptr), michael@0: mModules(32), michael@0: mImports(32), michael@0: mInProgressImports(32), michael@0: mThisObjects(32), michael@0: mInitialized(false), michael@0: mReuseLoaderGlobal(false) michael@0: { michael@0: MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton"); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (!gJSCLLog) { michael@0: gJSCLLog = PR_NewLogModule("JSComponentLoader"); michael@0: } michael@0: #endif michael@0: michael@0: sSelf = this; michael@0: } michael@0: michael@0: mozJSComponentLoader::~mozJSComponentLoader() michael@0: { michael@0: if (mInitialized) { michael@0: NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader"); michael@0: UnloadModules(); michael@0: } michael@0: michael@0: sSelf = nullptr; michael@0: } michael@0: michael@0: mozJSComponentLoader* michael@0: mozJSComponentLoader::sSelf; michael@0: michael@0: NS_IMPL_ISUPPORTS(mozJSComponentLoader, michael@0: mozilla::ModuleLoader, michael@0: xpcIJSModuleLoader, michael@0: nsIObserver) michael@0: michael@0: nsresult michael@0: mozJSComponentLoader::ReallyInit() michael@0: { michael@0: nsresult rv; michael@0: michael@0: mReuseLoaderGlobal = Preferences::GetBool("jsloader.reuseGlobal"); michael@0: michael@0: // XXXkhuey B2G child processes have some sort of preferences race that michael@0: // results in getting the wrong value. michael@0: #ifdef MOZ_B2G michael@0: mReuseLoaderGlobal = true; michael@0: #endif michael@0: michael@0: /* michael@0: * Get the JSRuntime from the runtime svc, if possible. michael@0: * We keep a reference around, because it's a Bad Thing if the runtime michael@0: * service gets shut down before we're done. Bad! michael@0: */ michael@0: michael@0: mRuntimeService = do_GetService(kJSRuntimeServiceContractID, &rv); michael@0: if (NS_FAILED(rv) || michael@0: NS_FAILED(rv = mRuntimeService->GetRuntime(&mRuntime))) michael@0: return rv; michael@0: michael@0: // Create our compilation context. michael@0: mContext = JS_NewContext(mRuntime, 256); michael@0: if (!mContext) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsCOMPtr secman = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); michael@0: if (!secman) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal)); michael@0: if (NS_FAILED(rv) || !mSystemPrincipal) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr obsSvc = michael@0: do_GetService(kObserverServiceContractID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mInitialized = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: const mozilla::Module* michael@0: mozJSComponentLoader::LoadModule(FileLocation &aFile) michael@0: { michael@0: nsCOMPtr file = aFile.GetBaseFile(); michael@0: michael@0: nsCString spec; michael@0: aFile.GetURIString(spec); michael@0: michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(uri), spec); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: if (!mInitialized) { michael@0: rv = ReallyInit(); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: } michael@0: michael@0: ModuleEntry* mod; michael@0: if (mModules.Get(spec, &mod)) michael@0: return mod; michael@0: michael@0: nsAutoPtr entry(new ModuleEntry(mContext)); michael@0: michael@0: JSAutoRequest ar(mContext); michael@0: RootedValue dummy(mContext); michael@0: rv = ObjectForLocation(file, uri, &entry->obj, &entry->thisObjectKey, michael@0: &entry->location, false, &dummy); michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr xpc = do_GetService(kXPConnectServiceContractID, michael@0: &rv); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr cm; michael@0: rv = NS_GetComponentManager(getter_AddRefs(cm)); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: JSCLContextHelper cx(mContext); michael@0: JSAutoCompartment ac(cx, entry->obj); michael@0: michael@0: nsCOMPtr cm_holder; michael@0: rv = xpc->WrapNative(cx, entry->obj, cm, michael@0: NS_GET_IID(nsIComponentManager), michael@0: getter_AddRefs(cm_holder)); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject* cm_jsobj = cm_holder->GetJSObject(); michael@0: if (!cm_jsobj) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr file_holder; michael@0: RootedObject entryObj(cx, entry->obj); michael@0: rv = xpc->WrapNative(cx, entryObj, file, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(file_holder)); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: JSObject* file_jsobj = file_holder->GetJSObject(); michael@0: if (!file_jsobj) { michael@0: return nullptr; michael@0: } michael@0: michael@0: JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter); michael@0: michael@0: RootedValue NSGetFactory_val(cx); michael@0: if (!JS_GetProperty(cx, entryObj, "NSGetFactory", &NSGetFactory_val) || michael@0: JSVAL_IS_VOID(NSGetFactory_val)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) { michael@0: nsAutoCString spec; michael@0: uri->GetSpec(spec); michael@0: JS_ReportError(cx, "%s has NSGetFactory property that is not a function", michael@0: spec.get()); michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedObject jsGetFactoryObj(cx); michael@0: if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) || michael@0: !jsGetFactoryObj) { michael@0: /* XXX report error properly */ michael@0: return nullptr; michael@0: } michael@0: michael@0: rv = xpc->WrapJS(cx, jsGetFactoryObj, michael@0: NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj)); michael@0: if (NS_FAILED(rv)) { michael@0: /* XXX report error properly */ michael@0: #ifdef DEBUG michael@0: fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: // Cache this module for later michael@0: mModules.Put(spec, entry); michael@0: michael@0: // Set the location information for the new global, so that tools like michael@0: // about:memory may use that information michael@0: if (!mReuseLoaderGlobal) { michael@0: xpc::SetLocationForGlobal(entryObj, spec); michael@0: } michael@0: michael@0: // The hash owns the ModuleEntry now, forget about it michael@0: return entry.forget(); michael@0: } michael@0: michael@0: nsresult michael@0: mozJSComponentLoader::FindTargetObject(JSContext* aCx, michael@0: MutableHandleObject aTargetObject) michael@0: { michael@0: aTargetObject.set(nullptr); michael@0: michael@0: RootedObject targetObject(aCx); michael@0: if (mReuseLoaderGlobal) { michael@0: JSScript* script = michael@0: js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx); michael@0: if (script) { michael@0: targetObject = mThisObjects.Get(script); michael@0: } michael@0: } michael@0: michael@0: // The above could fail, even if mReuseLoaderGlobal, if the scripted michael@0: // caller is not a component/JSM (it could be a DOM scope, for michael@0: // instance). michael@0: if (!targetObject) { michael@0: // Our targetObject is the caller's global object. Let's get it. michael@0: nsresult rv; michael@0: nsCOMPtr xpc = michael@0: do_GetService(kXPConnectServiceContractID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAXPCNativeCallContext *cc = nullptr; michael@0: rv = xpc->GetCurrentNativeCallContext(&cc); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr wn; michael@0: rv = cc->GetCalleeWrapper(getter_AddRefs(wn)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: targetObject = wn->GetJSObject(); michael@0: if (!targetObject) { michael@0: NS_ERROR("null calling object"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: targetObject = JS_GetGlobalForObject(aCx, targetObject); michael@0: } michael@0: michael@0: aTargetObject.set(targetObject); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: mozJSComponentLoader::NoteSubScript(HandleScript aScript, HandleObject aThisObject) michael@0: { michael@0: if (!mInitialized && NS_FAILED(ReallyInit())) { michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: if (js::GetObjectJSClass(aThisObject) == &kFakeBackstagePassJSClass) { michael@0: mThisObjects.Put(aScript, aThisObject); michael@0: } michael@0: } michael@0: michael@0: /* static */ size_t michael@0: mozJSComponentLoader::DataEntrySizeOfExcludingThis(const nsACString& aKey, michael@0: ModuleEntry* const& aData, michael@0: MallocSizeOf aMallocSizeOf, void*) michael@0: { michael@0: return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + michael@0: aData->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: /* static */ size_t michael@0: mozJSComponentLoader::ClassEntrySizeOfExcludingThis(const nsACString& aKey, michael@0: const nsAutoPtr& aData, michael@0: MallocSizeOf aMallocSizeOf, void*) michael@0: { michael@0: return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) + michael@0: aData->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) michael@0: { michael@0: size_t amount = aMallocSizeOf(this); michael@0: michael@0: amount += mModules.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf); michael@0: amount += mImports.SizeOfExcludingThis(ClassEntrySizeOfExcludingThis, aMallocSizeOf); michael@0: amount += mInProgressImports.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf); michael@0: amount += mThisObjects.SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: michael@0: return amount; michael@0: } michael@0: michael@0: // Some stack based classes for cleaning up on early return michael@0: #ifdef HAVE_PR_MEMMAP michael@0: class FileAutoCloser michael@0: { michael@0: public: michael@0: explicit FileAutoCloser(PRFileDesc *file) : mFile(file) {} michael@0: ~FileAutoCloser() { PR_Close(mFile); } michael@0: private: michael@0: PRFileDesc *mFile; michael@0: }; michael@0: michael@0: class FileMapAutoCloser michael@0: { michael@0: public: michael@0: explicit FileMapAutoCloser(PRFileMap *map) : mMap(map) {} michael@0: ~FileMapAutoCloser() { PR_CloseFileMap(mMap); } michael@0: private: michael@0: PRFileMap *mMap; michael@0: }; michael@0: #else michael@0: class ANSIFileAutoCloser michael@0: { michael@0: public: michael@0: explicit ANSIFileAutoCloser(FILE *file) : mFile(file) {} michael@0: ~ANSIFileAutoCloser() { fclose(mFile); } michael@0: private: michael@0: FILE *mFile; michael@0: }; michael@0: #endif michael@0: michael@0: JSObject* michael@0: mozJSComponentLoader::PrepareObjectForLocation(JSCLContextHelper& aCx, michael@0: nsIFile *aComponentFile, michael@0: nsIURI *aURI, michael@0: bool aReuseLoaderGlobal, michael@0: bool *aRealFile) michael@0: { michael@0: nsCOMPtr holder; michael@0: if (aReuseLoaderGlobal) { michael@0: holder = mLoaderGlobal; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr xpc = michael@0: do_GetService(kXPConnectServiceContractID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: bool createdNewGlobal = false; michael@0: michael@0: if (!mLoaderGlobal) { michael@0: nsRefPtr backstagePass; michael@0: rv = NS_NewBackstagePass(getter_AddRefs(backstagePass)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: CompartmentOptions options; michael@0: options.setZone(SystemZone) michael@0: .setVersion(JSVERSION_LATEST); michael@0: // Defer firing OnNewGlobalObject until after the __URI__ property has michael@0: // been defined so the JS debugger can tell what module the global is michael@0: // for michael@0: rv = xpc->InitClassesWithNewWrappedGlobal(aCx, michael@0: static_cast(backstagePass), michael@0: mSystemPrincipal, michael@0: nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK, michael@0: options, michael@0: getter_AddRefs(holder)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: createdNewGlobal = true; michael@0: michael@0: RootedObject global(aCx, holder->GetJSObject()); michael@0: NS_ENSURE_TRUE(global, nullptr); michael@0: michael@0: backstagePass->SetGlobalObject(global); michael@0: michael@0: JSAutoCompartment ac(aCx, global); michael@0: if (!JS_DefineFunctions(aCx, global, gGlobalFun) || michael@0: !JS_DefineProfilingFunctions(aCx, global)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aReuseLoaderGlobal) { michael@0: mLoaderGlobal = holder; michael@0: } michael@0: } michael@0: michael@0: RootedObject obj(aCx, holder->GetJSObject()); michael@0: NS_ENSURE_TRUE(obj, nullptr); michael@0: michael@0: JSAutoCompartment ac(aCx, obj); michael@0: michael@0: if (aReuseLoaderGlobal) { michael@0: // If we're reusing the loader global, we don't actually use the michael@0: // global, but rather we use a different object as the 'this' object. michael@0: obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass, NullPtr(), NullPtr()); michael@0: NS_ENSURE_TRUE(obj, nullptr); michael@0: } michael@0: michael@0: *aRealFile = false; michael@0: michael@0: // need to be extra careful checking for URIs pointing to files michael@0: // EnsureFile may not always get called, especially on resource URIs michael@0: // so we need to call GetFile to make sure this is a valid file michael@0: nsCOMPtr fileURL = do_QueryInterface(aURI, &rv); michael@0: nsCOMPtr testFile; michael@0: if (NS_SUCCEEDED(rv)) { michael@0: fileURL->GetFile(getter_AddRefs(testFile)); michael@0: } michael@0: michael@0: if (testFile) { michael@0: *aRealFile = true; michael@0: michael@0: nsCOMPtr locationHolder; michael@0: rv = xpc->WrapNative(aCx, obj, aComponentFile, michael@0: NS_GET_IID(nsIFile), michael@0: getter_AddRefs(locationHolder)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: RootedObject locationObj(aCx, locationHolder->GetJSObject()); michael@0: NS_ENSURE_TRUE(locationObj, nullptr); michael@0: michael@0: if (!JS_DefineProperty(aCx, obj, "__LOCATION__", locationObj, 0)) michael@0: return nullptr; michael@0: } michael@0: michael@0: nsAutoCString nativePath; michael@0: rv = aURI->GetSpec(nativePath); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: // Expose the URI from which the script was imported through a special michael@0: // variable that we insert into the JSM. michael@0: RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length())); michael@0: NS_ENSURE_TRUE(exposedUri, nullptr); michael@0: michael@0: if (!JS_DefineProperty(aCx, obj, "__URI__", exposedUri, 0)) michael@0: return nullptr; michael@0: michael@0: if (createdNewGlobal) { michael@0: RootedObject global(aCx, holder->GetJSObject()); michael@0: JS_FireOnNewGlobalObject(aCx, global); michael@0: } michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: nsresult michael@0: mozJSComponentLoader::ObjectForLocation(nsIFile *aComponentFile, michael@0: nsIURI *aURI, michael@0: MutableHandleObject aObject, michael@0: MutableHandleScript aTableScript, michael@0: char **aLocation, michael@0: bool aPropagateExceptions, michael@0: MutableHandleValue aException) michael@0: { michael@0: JSCLContextHelper cx(mContext); michael@0: michael@0: JS_AbortIfWrongThread(JS_GetRuntime(cx)); michael@0: michael@0: JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter); michael@0: michael@0: bool realFile = false; michael@0: RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aURI, michael@0: mReuseLoaderGlobal, &realFile)); michael@0: NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE); michael@0: michael@0: JSAutoCompartment ac(cx, obj); michael@0: michael@0: RootedScript script(cx); michael@0: RootedFunction function(cx); michael@0: michael@0: nsAutoCString nativePath; michael@0: nsresult rv = aURI->GetSpec(nativePath); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Before compiling the script, first check to see if we have it in michael@0: // the startupcache. Note: as a rule, startupcache errors are not fatal michael@0: // to loading the script, since we can always slow-load. michael@0: michael@0: bool writeToCache = false; michael@0: StartupCache* cache = StartupCache::GetSingleton(); michael@0: michael@0: nsAutoCString cachePath(kJSCachePrefix); michael@0: rv = PathifyURI(aURI, cachePath); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (cache) { michael@0: if (!mReuseLoaderGlobal) { michael@0: rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script); michael@0: } else { michael@0: rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal, michael@0: function.address()); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: LOG(("Successfully loaded %s from startupcache\n", nativePath.get())); michael@0: } else { michael@0: // This is ok, it just means the script is not yet in the michael@0: // cache. Could mean that the cache was corrupted and got removed, michael@0: // but either way we're going to write this out. michael@0: writeToCache = true; michael@0: } michael@0: } michael@0: michael@0: if (!script && !function) { michael@0: // The script wasn't in the cache , so compile it now. michael@0: LOG(("Slow loading %s\n", nativePath.get())); michael@0: michael@0: // If aPropagateExceptions is true, then our caller wants us to propagate michael@0: // any exceptions out to our caller. Ensure that the engine doesn't michael@0: // eagerly report the exception. michael@0: AutoSaveContextOptions asco(cx); michael@0: if (aPropagateExceptions) michael@0: ContextOptionsRef(cx).setDontReportUncaught(true); michael@0: michael@0: // Note - if mReuseLoaderGlobal is true, then we can't do lazy source, michael@0: // because we compile things as functions (rather than script), and lazy michael@0: // source isn't supported in that configuration. That's ok though, michael@0: // because we only do mReuseLoaderGlobal on b2g, where we invoke michael@0: // setDiscardSource(true) on the entire global. michael@0: CompileOptions options(cx); michael@0: options.setNoScriptRval(mReuseLoaderGlobal ? false : true) michael@0: .setVersion(JSVERSION_LATEST) michael@0: .setFileAndLine(nativePath.get(), 1) michael@0: .setSourceIsLazy(!mReuseLoaderGlobal); michael@0: michael@0: if (realFile) { michael@0: #ifdef HAVE_PR_MEMMAP michael@0: int64_t fileSize; michael@0: rv = aComponentFile->GetFileSize(&fileSize); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: int64_t maxSize = UINT32_MAX; michael@0: if (fileSize > maxSize) { michael@0: NS_ERROR("file too large"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: PRFileDesc *fileHandle; michael@0: rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle); michael@0: if (NS_FAILED(rv)) { michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: } michael@0: michael@0: // Make sure the file is closed, no matter how we return. michael@0: FileAutoCloser fileCloser(fileHandle); michael@0: michael@0: // We don't provide the file size here. If we did, PR_CreateFileMap michael@0: // would simply stat() the file to verify that the size we provided michael@0: // didn't require extending the file. We know that the file doesn't michael@0: // need to be extended, so skip the extra work by not providing the michael@0: // size. michael@0: PRFileMap *map = PR_CreateFileMap(fileHandle, 0, PR_PROT_READONLY); michael@0: if (!map) { michael@0: NS_ERROR("Failed to create file map"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Make sure the file map is closed, no matter how we return. michael@0: FileMapAutoCloser mapCloser(map); michael@0: michael@0: uint32_t fileSize32 = fileSize; michael@0: michael@0: char *buf = static_cast(PR_MemMap(map, 0, fileSize32)); michael@0: if (!buf) { michael@0: NS_WARNING("Failed to map file"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!mReuseLoaderGlobal) { michael@0: script = Compile(cx, obj, options, buf, michael@0: fileSize32); michael@0: } else { michael@0: function = CompileFunction(cx, obj, options, michael@0: nullptr, 0, nullptr, michael@0: buf, fileSize32); michael@0: } michael@0: michael@0: PR_MemUnmap(buf, fileSize32); michael@0: michael@0: #else /* HAVE_PR_MEMMAP */ michael@0: michael@0: /** michael@0: * No memmap implementation, so fall back to michael@0: * reading in the file michael@0: */ michael@0: michael@0: FILE *fileHandle; michael@0: rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle); michael@0: if (NS_FAILED(rv)) { michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: } michael@0: michael@0: // Ensure file fclose michael@0: ANSIFileAutoCloser fileCloser(fileHandle); michael@0: michael@0: int64_t len; michael@0: rv = aComponentFile->GetFileSize(&len); michael@0: if (NS_FAILED(rv) || len < 0) { michael@0: NS_WARNING("Failed to get file size"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: char *buf = (char *) malloc(len * sizeof(char)); michael@0: if (!buf) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: size_t rlen = fread(buf, 1, len, fileHandle); michael@0: if (rlen != (uint64_t)len) { michael@0: free(buf); michael@0: NS_WARNING("Failed to read file"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!mReuseLoaderGlobal) { michael@0: script = Compile(cx, obj, options, buf, michael@0: fileSize32); michael@0: } else { michael@0: function = CompileFunction(cx, obj, options, michael@0: nullptr, 0, nullptr, michael@0: buf, fileSize32); michael@0: } michael@0: michael@0: free(buf); michael@0: michael@0: #endif /* HAVE_PR_MEMMAP */ michael@0: } else { michael@0: nsCOMPtr ioService = do_GetIOService(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr scriptChannel; michael@0: rv = ioService->NewChannelFromURI(aURI, getter_AddRefs(scriptChannel)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr scriptStream; michael@0: rv = scriptChannel->Open(getter_AddRefs(scriptStream)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint64_t len64; michael@0: uint32_t bytesRead; michael@0: michael@0: rv = scriptStream->Available(&len64); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_TOO_BIG); michael@0: if (!len64) michael@0: return NS_ERROR_FAILURE; michael@0: uint32_t len = (uint32_t)len64; michael@0: michael@0: /* malloc an internal buf the size of the file */ michael@0: nsAutoArrayPtr buf(new char[len + 1]); michael@0: if (!buf) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: /* read the file in one swoop */ michael@0: rv = scriptStream->Read(buf, len, &bytesRead); michael@0: if (bytesRead != len) michael@0: return NS_BASE_STREAM_OSERROR; michael@0: michael@0: buf[len] = '\0'; michael@0: michael@0: if (!mReuseLoaderGlobal) { michael@0: script = Compile(cx, obj, options, buf, bytesRead); michael@0: } else { michael@0: function = CompileFunction(cx, obj, options, michael@0: nullptr, 0, nullptr, michael@0: buf, bytesRead); michael@0: } michael@0: } michael@0: // Propagate the exception, if one exists. Also, don't leave the stale michael@0: // exception on this context. michael@0: if (!script && !function && aPropagateExceptions) { michael@0: JS_GetPendingException(cx, aException); michael@0: JS_ClearPendingException(cx); michael@0: } michael@0: } michael@0: michael@0: if (!script && !function) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (writeToCache) { michael@0: // We successfully compiled the script, so cache it. michael@0: if (script) { michael@0: rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, michael@0: script); michael@0: } else { michael@0: rv = WriteCachedFunction(cache, cachePath, cx, mSystemPrincipal, michael@0: function); michael@0: } michael@0: michael@0: // Don't treat failure to write as fatal, since we might be working michael@0: // with a read-only cache. michael@0: if (NS_SUCCEEDED(rv)) { michael@0: LOG(("Successfully wrote to cache\n")); michael@0: } else { michael@0: LOG(("Failed to write to cache\n")); michael@0: } michael@0: } michael@0: michael@0: // Assign aObject here so that it's available to recursive imports. michael@0: // See bug 384168. michael@0: aObject.set(obj); michael@0: michael@0: RootedScript tableScript(cx, script); michael@0: if (!tableScript) { michael@0: tableScript = JS_GetFunctionScript(cx, function); michael@0: MOZ_ASSERT(tableScript); michael@0: } michael@0: michael@0: aTableScript.set(tableScript); michael@0: michael@0: if (js::GetObjectJSClass(obj) == &kFakeBackstagePassJSClass) { michael@0: MOZ_ASSERT(mReuseLoaderGlobal); michael@0: // tableScript stays in the table until shutdown. It is rooted by michael@0: // virtue of the fact that aTableScript is a handle to michael@0: // ModuleEntry::thisObjectKey, which is a PersistentRootedScript. Since michael@0: // ModuleEntries are never dynamically unloaded when mReuseLoaderGlobal michael@0: // is true, this prevents it from being collected and another script michael@0: // getting the same address. michael@0: mThisObjects.Put(tableScript, obj); michael@0: } michael@0: bool ok = false; michael@0: michael@0: { michael@0: AutoSaveContextOptions asco(cx); michael@0: if (aPropagateExceptions) michael@0: ContextOptionsRef(cx).setDontReportUncaught(true); michael@0: if (script) { michael@0: ok = JS_ExecuteScriptVersion(cx, obj, script, JSVERSION_LATEST); michael@0: } else { michael@0: RootedValue rval(cx); michael@0: ok = JS_CallFunction(cx, obj, function, JS::HandleValueArray::empty(), &rval); michael@0: } michael@0: } michael@0: michael@0: if (!ok) { michael@0: if (aPropagateExceptions) { michael@0: JS_GetPendingException(cx, aException); michael@0: JS_ClearPendingException(cx); michael@0: } michael@0: aObject.set(nullptr); michael@0: aTableScript.set(nullptr); michael@0: mThisObjects.Remove(tableScript); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: /* Freed when we remove from the table. */ michael@0: *aLocation = ToNewCString(nativePath); michael@0: if (!*aLocation) { michael@0: aObject.set(nullptr); michael@0: aTableScript.set(nullptr); michael@0: mThisObjects.Remove(tableScript); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ PLDHashOperator michael@0: mozJSComponentLoader::ClearModules(const nsACString& key, ModuleEntry*& entry, void* cx) michael@0: { michael@0: entry->Clear(); michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: void michael@0: mozJSComponentLoader::UnloadModules() michael@0: { michael@0: mInitialized = false; michael@0: michael@0: if (mLoaderGlobal) { michael@0: MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?"); michael@0: michael@0: JSAutoRequest ar(mContext); michael@0: RootedObject global(mContext, mLoaderGlobal->GetJSObject()); michael@0: if (global) { michael@0: JSAutoCompartment ac(mContext, global); michael@0: JS_SetAllNonReservedSlotsToUndefined(mContext, global); michael@0: } else { michael@0: NS_WARNING("Going to leak!"); michael@0: } michael@0: michael@0: mLoaderGlobal = nullptr; michael@0: } michael@0: michael@0: mInProgressImports.Clear(); michael@0: mImports.Clear(); michael@0: mThisObjects.Clear(); michael@0: michael@0: mModules.Enumerate(ClearModules, nullptr); michael@0: michael@0: JS_DestroyContextNoGC(mContext); michael@0: mContext = nullptr; michael@0: michael@0: mRuntimeService = nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: mozJSComponentLoader::Import(const nsACString& registryLocation, michael@0: HandleValue targetValArg, michael@0: JSContext *cx, michael@0: uint8_t optionalArgc, michael@0: MutableHandleValue retval) michael@0: { michael@0: MOZ_ASSERT(nsContentUtils::IsCallerChrome()); michael@0: michael@0: RootedValue targetVal(cx, targetValArg); michael@0: RootedObject targetObject(cx, nullptr); michael@0: if (optionalArgc) { michael@0: // The caller passed in the optional second argument. Get it. michael@0: if (targetVal.isObject()) { michael@0: // If we're passing in something like a content DOM window, chances michael@0: // are the caller expects the properties to end up on the object michael@0: // proper and not on the Xray holder. This is dubious, but can be used michael@0: // during testing. Given that dumb callers can already leak JSMs into michael@0: // content by passing a raw content JS object (where Xrays aren't michael@0: // possible), we aim for consistency here. Waive xray. michael@0: if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) && michael@0: !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal)) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: targetObject = &targetVal.toObject(); michael@0: } else if (!targetVal.isNull()) { michael@0: // If targetVal isNull(), we actually want to leave targetObject null. michael@0: // Not doing so breaks |make package|. michael@0: return ReportOnCaller(cx, ERROR_SCOPE_OBJ, michael@0: PromiseFlatCString(registryLocation).get()); michael@0: } michael@0: } else { michael@0: nsresult rv = FindTargetObject(cx, &targetObject); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: Maybe ac; michael@0: if (targetObject) { michael@0: ac.construct(cx, targetObject); michael@0: } michael@0: michael@0: RootedObject global(cx); michael@0: nsresult rv = ImportInto(registryLocation, targetObject, cx, &global); michael@0: michael@0: if (global) { michael@0: if (!JS_WrapObject(cx, &global)) { michael@0: NS_ERROR("can't wrap return value"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: retval.setObject(*global); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation, michael@0: in JSObjectPtr targetObj); */ michael@0: NS_IMETHODIMP michael@0: mozJSComponentLoader::ImportInto(const nsACString &aLocation, michael@0: JSObject *aTargetObj, michael@0: nsAXPCNativeCallContext *cc, michael@0: JSObject **_retval) michael@0: { michael@0: JSContext *callercx; michael@0: nsresult rv = cc->GetJSContext(&callercx); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: RootedObject targetObject(callercx, aTargetObj); michael@0: RootedObject global(callercx); michael@0: rv = ImportInto(aLocation, targetObject, callercx, &global); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: *_retval = global; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: mozJSComponentLoader::ImportInto(const nsACString &aLocation, michael@0: HandleObject targetObj, michael@0: JSContext *callercx, michael@0: MutableHandleObject vp) michael@0: { michael@0: vp.set(nullptr); michael@0: michael@0: nsresult rv; michael@0: if (!mInitialized) { michael@0: rv = ReallyInit(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: nsCOMPtr ioService = do_GetIOService(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Get the URI. michael@0: nsCOMPtr resURI; michael@0: rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // figure out the resolved URI michael@0: nsCOMPtr scriptChannel; michael@0: rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel)); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG); michael@0: michael@0: nsCOMPtr resolvedURI; michael@0: rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // get the JAR if there is one michael@0: nsCOMPtr jarURI; michael@0: jarURI = do_QueryInterface(resolvedURI, &rv); michael@0: nsCOMPtr baseFileURL; michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr baseURI; michael@0: while (jarURI) { michael@0: jarURI->GetJARFile(getter_AddRefs(baseURI)); michael@0: jarURI = do_QueryInterface(baseURI, &rv); michael@0: } michael@0: baseFileURL = do_QueryInterface(baseURI, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else { michael@0: baseFileURL = do_QueryInterface(resolvedURI, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: nsCOMPtr sourceFile; michael@0: rv = baseFileURL->GetFile(getter_AddRefs(sourceFile)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr sourceLocalFile; michael@0: sourceLocalFile = do_QueryInterface(sourceFile, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString key; michael@0: rv = resolvedURI->GetSpec(key); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: ModuleEntry* mod; michael@0: nsAutoPtr newEntry; michael@0: if (!mImports.Get(key, &mod) && !mInProgressImports.Get(key, &mod)) { michael@0: newEntry = new ModuleEntry(callercx); michael@0: if (!newEntry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: mInProgressImports.Put(key, newEntry); michael@0: michael@0: RootedValue exception(callercx); michael@0: rv = ObjectForLocation(sourceLocalFile, resURI, &newEntry->obj, michael@0: &newEntry->thisObjectKey, michael@0: &newEntry->location, true, &exception); michael@0: michael@0: mInProgressImports.Remove(key); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: if (!exception.isUndefined()) { michael@0: // An exception was thrown during compilation. Propagate it michael@0: // out to our caller so they can report it. michael@0: if (!JS_WrapValue(callercx, &exception)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: JS_SetPendingException(callercx, exception); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Something failed, but we don't know what it is, guess. michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: } michael@0: michael@0: // Set the location information for the new global, so that tools like michael@0: // about:memory may use that information michael@0: if (!mReuseLoaderGlobal) { michael@0: xpc::SetLocationForGlobal(newEntry->obj, aLocation); michael@0: } michael@0: michael@0: mod = newEntry; michael@0: } michael@0: michael@0: MOZ_ASSERT(mod->obj, "Import table contains entry with no object"); michael@0: vp.set(mod->obj); michael@0: michael@0: if (targetObj) { michael@0: JSCLContextHelper cxhelper(mContext); michael@0: JSAutoCompartment ac(mContext, mod->obj); michael@0: michael@0: RootedValue symbols(mContext); michael@0: RootedObject modObj(mContext, mod->obj); michael@0: if (!JS_GetProperty(mContext, modObj, michael@0: "EXPORTED_SYMBOLS", &symbols)) { michael@0: return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT, michael@0: PromiseFlatCString(aLocation).get()); michael@0: } michael@0: michael@0: if (!JS_IsArrayObject(mContext, symbols)) { michael@0: return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY, michael@0: PromiseFlatCString(aLocation).get()); michael@0: } michael@0: michael@0: RootedObject symbolsObj(mContext, &symbols.toObject()); michael@0: michael@0: // Iterate over symbols array, installing symbols on targetObj: michael@0: michael@0: uint32_t symbolCount = 0; michael@0: if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) { michael@0: return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH, michael@0: PromiseFlatCString(aLocation).get()); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: nsAutoCString logBuffer; michael@0: #endif michael@0: michael@0: RootedValue value(mContext); michael@0: RootedId symbolId(mContext); michael@0: for (uint32_t i = 0; i < symbolCount; ++i) { michael@0: if (!JS_GetElement(mContext, symbolsObj, i, &value) || michael@0: !value.isString() || michael@0: !JS_ValueToId(mContext, value, &symbolId)) { michael@0: return ReportOnCaller(cxhelper, ERROR_ARRAY_ELEMENT, michael@0: PromiseFlatCString(aLocation).get(), i); michael@0: } michael@0: michael@0: RootedObject modObj(mContext, mod->obj); michael@0: if (!JS_GetPropertyById(mContext, modObj, symbolId, &value)) { michael@0: JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId)); michael@0: if (!bytes) michael@0: return NS_ERROR_FAILURE; michael@0: return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL, michael@0: PromiseFlatCString(aLocation).get(), michael@0: bytes.ptr()); michael@0: } michael@0: michael@0: JSAutoCompartment target_ac(mContext, targetObj); michael@0: michael@0: if (!JS_WrapValue(mContext, &value) || michael@0: !JS_SetPropertyById(mContext, targetObj, symbolId, value)) { michael@0: JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId)); michael@0: if (!bytes) michael@0: return NS_ERROR_FAILURE; michael@0: return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL, michael@0: PromiseFlatCString(aLocation).get(), michael@0: bytes.ptr()); michael@0: } michael@0: #ifdef DEBUG michael@0: if (i == 0) { michael@0: logBuffer.AssignLiteral("Installing symbols [ "); michael@0: } michael@0: JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId)); michael@0: if (!!bytes) michael@0: logBuffer.Append(bytes.ptr()); michael@0: logBuffer.AppendLiteral(" "); michael@0: if (i == symbolCount - 1) { michael@0: LOG(("%s] from %s\n", logBuffer.get(), michael@0: PromiseFlatCString(aLocation).get())); michael@0: } michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: // Cache this module for later michael@0: if (newEntry) { michael@0: mImports.Put(key, newEntry); michael@0: newEntry.forget(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: mozJSComponentLoader::Unload(const nsACString & aLocation) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!mInitialized) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr ioService = do_GetIOService(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Get the URI. michael@0: nsCOMPtr resURI; michael@0: rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // figure out the resolved URI michael@0: nsCOMPtr scriptChannel; michael@0: rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel)); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG); michael@0: michael@0: nsCOMPtr resolvedURI; michael@0: rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString key; michael@0: rv = resolvedURI->GetSpec(key); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: ModuleEntry* mod; michael@0: if (mImports.Get(key, &mod)) { michael@0: mImports.Remove(key); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: mozJSComponentLoader::Observe(nsISupports *subject, const char *topic, michael@0: const char16_t *data) michael@0: { michael@0: if (!strcmp(topic, "xpcom-shutdown-loaders")) { michael@0: UnloadModules(); michael@0: } else { michael@0: NS_ERROR("Unexpected observer topic."); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: size_t michael@0: mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: n += aMallocSizeOf(location); michael@0: michael@0: return n; michael@0: } michael@0: michael@0: /* static */ already_AddRefed michael@0: mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module, michael@0: const mozilla::Module::CIDEntry& entry) michael@0: { michael@0: const ModuleEntry& self = static_cast(module); michael@0: MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?"); michael@0: michael@0: nsCOMPtr f; michael@0: nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f)); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: return f.forget(); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: JSCLContextHelper::JSCLContextHelper(JSContext* aCx) michael@0: : mContext(aCx) michael@0: , mBuf(nullptr) michael@0: { michael@0: mPusher.Push(mContext); michael@0: JS_BeginRequest(mContext); michael@0: } michael@0: michael@0: JSCLContextHelper::~JSCLContextHelper() michael@0: { michael@0: JS_EndRequest(mContext); michael@0: mPusher.Pop(); michael@0: JSContext *restoredCx = nsContentUtils::GetCurrentJSContext(); michael@0: if (restoredCx && mBuf) { michael@0: JS_ReportError(restoredCx, mBuf); michael@0: } michael@0: michael@0: if (mBuf) { michael@0: JS_smprintf_free(mBuf); michael@0: } michael@0: } michael@0: michael@0: void michael@0: JSCLContextHelper::reportErrorAfterPop(char *buf) michael@0: { michael@0: MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop"); michael@0: mBuf = buf; michael@0: }