summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/loader/mozJSComponentLoader.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/xpconnect/loader/mozJSComponentLoader.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/xpconnect/loader/mozJSComponentLoader.cpp')
-rw-r--r--js/xpconnect/loader/mozJSComponentLoader.cpp1437
1 files changed, 1437 insertions, 0 deletions
diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp
new file mode 100644
index 000000000..95c214867
--- /dev/null
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -0,0 +1,1437 @@
+
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Attributes.h"
+
+#include <cstdarg>
+
+#include "mozilla/Logging.h"
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+#include "jsapi.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIComponentManager.h"
+#include "mozilla/Module.h"
+#include "nsIFile.h"
+#include "mozJSComponentLoader.h"
+#include "mozJSLoaderUtils.h"
+#include "nsIXPConnect.h"
+#include "nsIObserverService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIFileURL.h"
+#include "nsIJARURI.h"
+#include "nsNetUtil.h"
+#include "jsprf.h"
+#include "nsJSPrincipals.h"
+#include "nsJSUtils.h"
+#include "xpcprivate.h"
+#include "xpcpublic.h"
+#include "nsContentUtils.h"
+#include "nsXULAppAPI.h"
+#include "WrapperFactory.h"
+
+#include "mozilla/AddonPathService.h"
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Unused.h"
+
+using namespace mozilla;
+using namespace mozilla::scache;
+using namespace xpc;
+using namespace JS;
+
+// This JSClass exists to trick silly code that expects toString()ing the
+// global in a component scope to return something with "BackstagePass" in it
+// to continue working.
+static const JSClass kFakeBackstagePassJSClass = { "FakeBackstagePass" };
+
+static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
+static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
+static const char kJSCachePrefix[] = "jsloader";
+
+#define HAVE_PR_MEMMAP
+
+/**
+ * Buffer sizes for serialization and deserialization of scripts.
+ * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
+ */
+#define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
+#define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
+
+// MOZ_LOG=JSComponentLoader:5
+static LazyLogModule gJSCLLog("JSComponentLoader");
+
+#define LOG(args) MOZ_LOG(gJSCLLog, mozilla::LogLevel::Debug, args)
+
+// Components.utils.import error messages
+#define ERROR_SCOPE_OBJ "%s - Second argument must be an object."
+#define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present."
+#define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array."
+#define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS."
+#define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string."
+#define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'."
+#define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object."
+
+static bool
+Dump(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (args.length() == 0)
+ return true;
+
+ RootedString str(cx, JS::ToString(cx, args[0]));
+ if (!str)
+ return false;
+
+ JSAutoByteString utf8str;
+ if (!utf8str.encodeUtf8(cx, str))
+ return false;
+
+#ifdef ANDROID
+ __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.ptr());
+#endif
+#ifdef XP_WIN
+ if (IsDebuggerPresent()) {
+ nsAutoJSString wstr;
+ if (!wstr.init(cx, str))
+ return false;
+ OutputDebugStringW(wstr.get());
+ }
+#endif
+ fputs(utf8str.ptr(), stdout);
+ fflush(stdout);
+ return true;
+}
+
+static bool
+Debug(JSContext* cx, unsigned argc, Value* vp)
+{
+#ifdef DEBUG
+ return Dump(cx, argc, vp);
+#else
+ return true;
+#endif
+}
+
+static const JSFunctionSpec gGlobalFun[] = {
+ JS_FS("dump", Dump, 1,0),
+ JS_FS("debug", Debug, 1,0),
+ JS_FS("atob", Atob, 1,0),
+ JS_FS("btoa", Btoa, 1,0),
+ JS_FS_END
+};
+
+class MOZ_STACK_CLASS JSCLContextHelper
+{
+public:
+ explicit JSCLContextHelper(JSContext* aCx);
+ ~JSCLContextHelper();
+
+ void reportErrorAfterPop(char* buf);
+
+private:
+ JSContext* mContext;
+ char* mBuf;
+
+ // prevent copying and assignment
+ JSCLContextHelper(const JSCLContextHelper&) = delete;
+ const JSCLContextHelper& operator=(const JSCLContextHelper&) = delete;
+};
+
+static nsresult
+MOZ_FORMAT_PRINTF(2, 3)
+ReportOnCallerUTF8(JSContext* callerContext,
+ const char* format, ...) {
+ if (!callerContext) {
+ return NS_ERROR_FAILURE;
+ }
+
+ va_list ap;
+ va_start(ap, format);
+
+ char* buf = JS_vsmprintf(format, ap);
+ if (!buf) {
+ va_end(ap);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ JS_ReportErrorUTF8(callerContext, "%s", buf);
+ JS_smprintf_free(buf);
+
+ va_end(ap);
+ return NS_OK;
+}
+
+static nsresult
+MOZ_FORMAT_PRINTF(2, 3)
+ReportOnCallerUTF8(JSCLContextHelper& helper,
+ const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+
+ char* buf = JS_vsmprintf(format, ap);
+ if (!buf) {
+ va_end(ap);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ helper.reportErrorAfterPop(buf);
+ va_end(ap);
+ return NS_OK;
+}
+
+mozJSComponentLoader::mozJSComponentLoader()
+ : mModules(16),
+ mImports(16),
+ mInProgressImports(16),
+ mInitialized(false),
+ mReuseLoaderGlobal(false)
+{
+ MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton");
+
+ sSelf = this;
+}
+
+#define ENSURE_DEP(name) { nsresult rv = Ensure##name(); NS_ENSURE_SUCCESS(rv, rv); }
+#define ENSURE_DEPS(...) MOZ_FOR_EACH(ENSURE_DEP, (), (__VA_ARGS__));
+#define BEGIN_ENSURE(self, ...) { \
+ if (m##self) \
+ return NS_OK; \
+ ENSURE_DEPS(__VA_ARGS__); \
+}
+
+class MOZ_STACK_CLASS ComponentLoaderInfo {
+ public:
+ explicit ComponentLoaderInfo(const nsACString& aLocation) : mLocation(aLocation) {}
+
+ nsIIOService* IOService() { MOZ_ASSERT(mIOService); return mIOService; }
+ nsresult EnsureIOService() {
+ if (mIOService)
+ return NS_OK;
+ nsresult rv;
+ mIOService = do_GetIOService(&rv);
+ return rv;
+ }
+
+ nsIURI* URI() { MOZ_ASSERT(mURI); return mURI; }
+ nsresult EnsureURI() {
+ BEGIN_ENSURE(URI, IOService);
+ return mIOService->NewURI(mLocation, nullptr, nullptr, getter_AddRefs(mURI));
+ }
+
+ nsIChannel* ScriptChannel() { MOZ_ASSERT(mScriptChannel); return mScriptChannel; }
+ nsresult EnsureScriptChannel() {
+ BEGIN_ENSURE(ScriptChannel, IOService, URI);
+ return NS_NewChannel(getter_AddRefs(mScriptChannel),
+ mURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_SCRIPT,
+ nullptr, // aLoadGroup
+ nullptr, // aCallbacks
+ nsIRequest::LOAD_NORMAL,
+ mIOService);
+ }
+
+ nsIURI* ResolvedURI() { MOZ_ASSERT(mResolvedURI); return mResolvedURI; }
+ nsresult EnsureResolvedURI() {
+ BEGIN_ENSURE(ResolvedURI, ScriptChannel);
+ return mScriptChannel->GetURI(getter_AddRefs(mResolvedURI));
+ }
+
+ nsAutoCString& Key() { return *mKey; }
+ nsresult EnsureKey() {
+ ENSURE_DEPS(ResolvedURI);
+ mKey.emplace();
+ return mResolvedURI->GetSpec(*mKey);
+ }
+
+ MOZ_MUST_USE nsresult GetLocation(nsCString& aLocation) {
+ nsresult rv = EnsureURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+ return mURI->GetSpec(aLocation);
+ }
+
+ private:
+ const nsACString& mLocation;
+ nsCOMPtr<nsIIOService> mIOService;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIChannel> mScriptChannel;
+ nsCOMPtr<nsIURI> mResolvedURI;
+ Maybe<nsAutoCString> mKey; // This is safe because we're MOZ_STACK_CLASS
+};
+
+#undef BEGIN_ENSURE
+#undef ENSURE_DEPS
+#undef ENSURE_DEP
+
+mozJSComponentLoader::~mozJSComponentLoader()
+{
+ if (mInitialized) {
+ NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader");
+ UnloadModules();
+ }
+
+ sSelf = nullptr;
+}
+
+mozJSComponentLoader*
+mozJSComponentLoader::sSelf;
+
+NS_IMPL_ISUPPORTS(mozJSComponentLoader,
+ mozilla::ModuleLoader,
+ xpcIJSModuleLoader,
+ nsIObserver)
+
+nsresult
+mozJSComponentLoader::ReallyInit()
+{
+ nsresult rv;
+
+ mReuseLoaderGlobal = Preferences::GetBool("jsloader.reuseGlobal");
+
+ // XXXkhuey B2G child processes have some sort of preferences race that
+ // results in getting the wrong value.
+ // But we don't want that on Firefox Mulet as it break most Firefox JSMs...
+ // Also disable on debug builds to break js components that rely on this.
+#if defined(MOZ_B2G) && !defined(MOZ_MULET) && !defined(DEBUG)
+ mReuseLoaderGlobal = true;
+#endif
+
+ nsCOMPtr<nsIScriptSecurityManager> secman =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
+ if (!secman)
+ return NS_ERROR_FAILURE;
+
+ rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
+ if (NS_FAILED(rv) || !mSystemPrincipal)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIObserverService> obsSvc =
+ do_GetService(kObserverServiceContractID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInitialized = true;
+
+ return NS_OK;
+}
+
+// For terrible compatibility reasons, we need to consider both the global
+// lexical environment and the global of modules when searching for exported
+// symbols.
+static JSObject*
+ResolveModuleObjectProperty(JSContext* aCx, HandleObject aModObj, const char* name)
+{
+ if (JS_HasExtensibleLexicalEnvironment(aModObj)) {
+ RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj));
+ bool found;
+ if (!JS_HasOwnProperty(aCx, lexical, name, &found)) {
+ return nullptr;
+ }
+ if (found) {
+ return lexical;
+ }
+ }
+ return aModObj;
+}
+
+const mozilla::Module*
+mozJSComponentLoader::LoadModule(FileLocation& aFile)
+{
+ if (!NS_IsMainThread()) {
+ MOZ_ASSERT(false, "Don't use JS components off the main thread");
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
+
+ nsCString spec;
+ aFile.GetURIString(spec);
+ ComponentLoaderInfo info(spec);
+ nsresult rv = info.EnsureURI();
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ if (!mInitialized) {
+ rv = ReallyInit();
+ if (NS_FAILED(rv))
+ return nullptr;
+ }
+
+ ModuleEntry* mod;
+ if (mModules.Get(spec, &mod))
+ return mod;
+
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ nsAutoPtr<ModuleEntry> entry(new ModuleEntry(RootingContext::get(cx)));
+ RootedValue dummy(cx);
+ rv = ObjectForLocation(info, file, &entry->obj, &entry->thisObjectKey,
+ &entry->location, false, &dummy);
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
+ &rv);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsCOMPtr<nsIComponentManager> cm;
+ rv = NS_GetComponentManager(getter_AddRefs(cm));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ JSAutoCompartment ac(cx, entry->obj);
+ RootedObject entryObj(cx, entry->obj);
+
+ RootedObject NSGetFactoryHolder(cx, ResolveModuleObjectProperty(cx, entryObj, "NSGetFactory"));
+ RootedValue NSGetFactory_val(cx);
+ if (!NSGetFactoryHolder ||
+ !JS_GetProperty(cx, NSGetFactoryHolder, "NSGetFactory", &NSGetFactory_val) ||
+ NSGetFactory_val.isUndefined())
+ {
+ return nullptr;
+ }
+
+ if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
+ /*
+ * spec's encoding is ASCII unless it's zip file, otherwise it's
+ * random encoding. Latin1 variant is safe for random encoding.
+ */
+ JS_ReportErrorLatin1(cx, "%s has NSGetFactory property that is not a function",
+ spec.get());
+ return nullptr;
+ }
+
+ RootedObject jsGetFactoryObj(cx);
+ if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
+ !jsGetFactoryObj) {
+ /* XXX report error properly */
+ return nullptr;
+ }
+
+ rv = xpc->WrapJS(cx, jsGetFactoryObj,
+ NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj));
+ if (NS_FAILED(rv)) {
+ /* XXX report error properly */
+#ifdef DEBUG
+ fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
+#endif
+ return nullptr;
+ }
+
+ // Cache this module for later
+ mModules.Put(spec, entry);
+
+ // Set the location information for the new global, so that tools like
+ // about:memory may use that information
+ if (!mReuseLoaderGlobal) {
+ xpc::SetLocationForGlobal(entryObj, spec);
+ }
+
+ // The hash owns the ModuleEntry now, forget about it
+ return entry.forget();
+}
+
+nsresult
+mozJSComponentLoader::FindTargetObject(JSContext* aCx,
+ MutableHandleObject aTargetObject)
+{
+ aTargetObject.set(nullptr);
+
+ RootedObject targetObject(aCx);
+ if (mReuseLoaderGlobal) {
+ JSFunction* fun = js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
+ if (fun) {
+ JSObject* funParent = js::GetNearestEnclosingWithEnvironmentObjectForFunction(fun);
+ if (JS_GetClass(funParent) == &kFakeBackstagePassJSClass)
+ targetObject = funParent;
+ }
+ }
+
+ // The above could fail, even if mReuseLoaderGlobal, if the scripted
+ // caller is not a component/JSM (it could be a DOM scope, for
+ // instance).
+ if (!targetObject) {
+ // Our targetObject is the caller's global object. Let's get it.
+ targetObject = CurrentGlobalOrNull(aCx);
+ }
+
+ aTargetObject.set(targetObject);
+ return NS_OK;
+}
+
+// This requires that the keys be strings and the values be pointers.
+template <class Key, class Data, class UserData>
+static size_t
+SizeOfTableExcludingThis(const nsBaseHashtable<Key, Data, UserData>& aTable,
+ MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+ return n;
+}
+
+size_t
+mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+ n += SizeOfTableExcludingThis(mModules, aMallocSizeOf);
+ n += SizeOfTableExcludingThis(mImports, aMallocSizeOf);
+ n += SizeOfTableExcludingThis(mInProgressImports, aMallocSizeOf);
+ return n;
+}
+
+// Some stack based classes for cleaning up on early return
+#ifdef HAVE_PR_MEMMAP
+class FileAutoCloser
+{
+ public:
+ explicit FileAutoCloser(PRFileDesc* file) : mFile(file) {}
+ ~FileAutoCloser() { PR_Close(mFile); }
+ private:
+ PRFileDesc* mFile;
+};
+
+class FileMapAutoCloser
+{
+ public:
+ explicit FileMapAutoCloser(PRFileMap* map) : mMap(map) {}
+ ~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
+ private:
+ PRFileMap* mMap;
+};
+#else
+class ANSIFileAutoCloser
+{
+ public:
+ explicit ANSIFileAutoCloser(FILE* file) : mFile(file) {}
+ ~ANSIFileAutoCloser() { fclose(mFile); }
+ private:
+ FILE* mFile;
+};
+#endif
+
+JSObject*
+mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx,
+ nsIFile* aComponentFile,
+ nsIURI* aURI,
+ bool aReuseLoaderGlobal,
+ bool* aRealFile)
+{
+ nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+ if (aReuseLoaderGlobal) {
+ holder = mLoaderGlobal;
+ }
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIXPConnect> xpc =
+ do_GetService(kXPConnectServiceContractID, &rv);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ bool createdNewGlobal = false;
+
+ if (!mLoaderGlobal) {
+ RefPtr<BackstagePass> backstagePass;
+ rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ CompartmentOptions options;
+
+ options.creationOptions()
+ .setZone(SystemZone)
+ .setAddonId(aReuseLoaderGlobal ? nullptr : MapURIToAddonID(aURI));
+
+ options.behaviors().setVersion(JSVERSION_LATEST);
+
+ if (xpc::SharedMemoryEnabled())
+ options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
+
+ // Defer firing OnNewGlobalObject until after the __URI__ property has
+ // been defined so the JS debugger can tell what module the global is
+ // for
+ rv = xpc->InitClassesWithNewWrappedGlobal(aCx,
+ static_cast<nsIGlobalObject*>(backstagePass),
+ mSystemPrincipal,
+ nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
+ options,
+ getter_AddRefs(holder));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ createdNewGlobal = true;
+
+ RootedObject global(aCx, holder->GetJSObject());
+ NS_ENSURE_TRUE(global, nullptr);
+
+ backstagePass->SetGlobalObject(global);
+
+ JSAutoCompartment ac(aCx, global);
+ if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
+ !JS_DefineProfilingFunctions(aCx, global)) {
+ return nullptr;
+ }
+
+ if (aReuseLoaderGlobal) {
+ mLoaderGlobal = holder;
+ }
+ }
+
+ RootedObject obj(aCx, holder->GetJSObject());
+ NS_ENSURE_TRUE(obj, nullptr);
+
+ JSAutoCompartment ac(aCx, obj);
+
+ if (aReuseLoaderGlobal) {
+ // If we're reusing the loader global, we don't actually use the
+ // global, but rather we use a different object as the 'this' object.
+ obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass);
+ NS_ENSURE_TRUE(obj, nullptr);
+ }
+
+ *aRealFile = false;
+
+ // need to be extra careful checking for URIs pointing to files
+ // EnsureFile may not always get called, especially on resource URIs
+ // so we need to call GetFile to make sure this is a valid file
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
+ nsCOMPtr<nsIFile> testFile;
+ if (NS_SUCCEEDED(rv)) {
+ fileURL->GetFile(getter_AddRefs(testFile));
+ }
+
+ if (testFile) {
+ *aRealFile = true;
+
+ if (XRE_IsParentProcess()) {
+ RootedObject locationObj(aCx);
+
+ rv = xpc->WrapNative(aCx, obj, aComponentFile,
+ NS_GET_IID(nsIFile),
+ locationObj.address());
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ NS_ENSURE_TRUE(locationObj, nullptr);
+
+ if (!JS_DefineProperty(aCx, obj, "__LOCATION__", locationObj, 0))
+ return nullptr;
+ }
+ }
+
+ nsAutoCString nativePath;
+ rv = aURI->GetSpec(nativePath);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ // Expose the URI from which the script was imported through a special
+ // variable that we insert into the JSM.
+ RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length()));
+ NS_ENSURE_TRUE(exposedUri, nullptr);
+
+ if (!JS_DefineProperty(aCx, obj, "__URI__", exposedUri, 0))
+ return nullptr;
+
+ if (createdNewGlobal) {
+ // AutoEntryScript required to invoke debugger hook, which is a
+ // Gecko-specific concept at present.
+ dom::AutoEntryScript aes(holder->GetJSObject(),
+ "component loader report global");
+ RootedObject global(aes.cx(), holder->GetJSObject());
+ JS_FireOnNewGlobalObject(aes.cx(), global);
+ }
+
+ return obj;
+}
+
+nsresult
+mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo,
+ nsIFile* aComponentFile,
+ MutableHandleObject aObject,
+ MutableHandleScript aTableScript,
+ char** aLocation,
+ bool aPropagateExceptions,
+ MutableHandleValue aException)
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ bool realFile = false;
+ nsresult rv = aInfo.EnsureURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+ RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aInfo.URI(),
+ mReuseLoaderGlobal, &realFile));
+ NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
+ MOZ_ASSERT(JS_IsGlobalObject(obj) == !mReuseLoaderGlobal);
+
+ JSAutoCompartment ac(cx, obj);
+
+ RootedScript script(cx);
+ RootedFunction function(cx);
+
+ nsAutoCString nativePath;
+ rv = aInfo.URI()->GetSpec(nativePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Before compiling the script, first check to see if we have it in
+ // the startupcache. Note: as a rule, startupcache errors are not fatal
+ // to loading the script, since we can always slow-load.
+
+ bool writeToCache = false;
+ StartupCache* cache = StartupCache::GetSingleton();
+
+ nsAutoCString cachePath(kJSCachePrefix);
+ rv = PathifyURI(aInfo.URI(), cachePath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (cache) {
+ if (!mReuseLoaderGlobal) {
+ rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
+ } else {
+ rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
+ function.address());
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
+ } else {
+ // This is ok, it just means the script is not yet in the
+ // cache. Could mean that the cache was corrupted and got removed,
+ // but either way we're going to write this out.
+ writeToCache = true;
+ // ReadCachedScript and ReadCachedFunction may have set a pending
+ // exception.
+ JS_ClearPendingException(cx);
+ }
+ }
+
+ if (!script && !function) {
+ // The script wasn't in the cache , so compile it now.
+ LOG(("Slow loading %s\n", nativePath.get()));
+
+ // Use lazy source if both of these conditions hold:
+ //
+ // (1) mReuseLoaderGlobal is false. If mReuseLoaderGlobal is true, we
+ // can't do lazy source because we compile things as functions
+ // (rather than script), and lazy source isn't supported in that
+ // configuration. That's ok though, because we only do
+ // mReuseLoaderGlobal on b2g, where we invoke setDiscardSource(true)
+ // on the entire global.
+ //
+ // (2) We're using the startup cache. Non-lazy source + startup cache
+ // regresses installer size (due to source code stored in XDR
+ // encoded modules in omni.ja). Also, XDR decoding is relatively
+ // fast. Content processes don't use the startup cache, so we want
+ // them to use non-lazy source code to enable lazy parsing.
+ // See bug 1303754.
+ CompileOptions options(cx);
+ options.setNoScriptRval(mReuseLoaderGlobal ? false : true)
+ .setVersion(JSVERSION_LATEST)
+ .setFileAndLine(nativePath.get(), 1)
+ .setSourceIsLazy(!mReuseLoaderGlobal && !!cache);
+
+ if (realFile) {
+#ifdef HAVE_PR_MEMMAP
+ int64_t fileSize;
+ rv = aComponentFile->GetFileSize(&fileSize);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ int64_t maxSize = UINT32_MAX;
+ if (fileSize > maxSize) {
+ NS_ERROR("file too large");
+ return NS_ERROR_FAILURE;
+ }
+
+ PRFileDesc* fileHandle;
+ rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // Make sure the file is closed, no matter how we return.
+ FileAutoCloser fileCloser(fileHandle);
+
+ // We don't provide the file size here. If we did, PR_CreateFileMap
+ // would simply stat() the file to verify that the size we provided
+ // didn't require extending the file. We know that the file doesn't
+ // need to be extended, so skip the extra work by not providing the
+ // size.
+ PRFileMap* map = PR_CreateFileMap(fileHandle, 0, PR_PROT_READONLY);
+ if (!map) {
+ NS_ERROR("Failed to create file map");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure the file map is closed, no matter how we return.
+ FileMapAutoCloser mapCloser(map);
+
+ uint32_t fileSize32 = fileSize;
+
+ char* buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
+ if (!buf) {
+ NS_WARNING("Failed to map file");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mReuseLoaderGlobal) {
+ Compile(cx, options, buf, fileSize32, &script);
+ } else {
+ // Note: exceptions will get handled further down;
+ // don't early return for them here.
+ AutoObjectVector envChain(cx);
+ if (envChain.append(obj)) {
+ CompileFunction(cx, envChain,
+ options, nullptr, 0, nullptr,
+ buf, fileSize32, &function);
+ }
+ }
+
+ PR_MemUnmap(buf, fileSize32);
+
+#else /* HAVE_PR_MEMMAP */
+
+ /**
+ * No memmap implementation, so fall back to
+ * reading in the file
+ */
+
+ FILE* fileHandle;
+ rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // Ensure file fclose
+ ANSIFileAutoCloser fileCloser(fileHandle);
+
+ int64_t len;
+ rv = aComponentFile->GetFileSize(&len);
+ if (NS_FAILED(rv) || len < 0) {
+ NS_WARNING("Failed to get file size");
+ return NS_ERROR_FAILURE;
+ }
+
+ char* buf = (char*) malloc(len * sizeof(char));
+ if (!buf) {
+ return NS_ERROR_FAILURE;
+ }
+
+ size_t rlen = fread(buf, 1, len, fileHandle);
+ if (rlen != (uint64_t)len) {
+ free(buf);
+ NS_WARNING("Failed to read file");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mReuseLoaderGlobal) {
+ script = Compile(cx, options, buf, fileSize32);
+ } else {
+ // Note: exceptions will get handled further down;
+ // don't early return for them here.
+ AutoObjectVector envChain(cx);
+ if (envChain.append(obj)) {
+ CompileFunction(cx, envChain,
+ options, nullptr, 0, nullptr,
+ buf, fileSize32, &function);
+ }
+ }
+
+ free(buf);
+
+#endif /* HAVE_PR_MEMMAP */
+ } else {
+ rv = aInfo.EnsureScriptChannel();
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIInputStream> scriptStream;
+ rv = NS_MaybeOpenChannelUsingOpen2(aInfo.ScriptChannel(),
+ getter_AddRefs(scriptStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint64_t len64;
+ uint32_t bytesRead;
+
+ rv = scriptStream->Available(&len64);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+ if (!len64)
+ return NS_ERROR_FAILURE;
+ uint32_t len = (uint32_t)len64;
+
+ /* malloc an internal buf the size of the file */
+ auto buf = MakeUniqueFallible<char[]>(len + 1);
+ if (!buf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ /* read the file in one swoop */
+ rv = scriptStream->Read(buf.get(), len, &bytesRead);
+ if (bytesRead != len)
+ return NS_BASE_STREAM_OSERROR;
+
+ buf[len] = '\0';
+
+ if (!mReuseLoaderGlobal) {
+ Compile(cx, options, buf.get(), bytesRead, &script);
+ } else {
+ // Note: exceptions will get handled further down;
+ // don't early return for them here.
+ AutoObjectVector envChain(cx);
+ if (envChain.append(obj)) {
+ CompileFunction(cx, envChain,
+ options, nullptr, 0, nullptr,
+ buf.get(), bytesRead, &function);
+ }
+ }
+ }
+ // Propagate the exception, if one exists. Also, don't leave the stale
+ // exception on this context.
+ if (!script && !function && aPropagateExceptions &&
+ jsapi.HasException()) {
+ if (!jsapi.StealException(aException))
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ if (!script && !function) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // We must have a script or a function (but not both!) here. We have a
+ // script when we're not reusing the loader global, and a function
+ // otherwise.
+ MOZ_ASSERT(!!script != !!function);
+ MOZ_ASSERT(!!script == JS_IsGlobalObject(obj));
+
+ if (writeToCache) {
+ // We successfully compiled the script, so cache it.
+ if (script) {
+ rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal,
+ script);
+ } else {
+ rv = WriteCachedFunction(cache, cachePath, cx, mSystemPrincipal,
+ function);
+ }
+
+ // Don't treat failure to write as fatal, since we might be working
+ // with a read-only cache.
+ if (NS_SUCCEEDED(rv)) {
+ LOG(("Successfully wrote to cache\n"));
+ } else {
+ LOG(("Failed to write to cache\n"));
+ }
+ }
+
+ // Assign aObject here so that it's available to recursive imports.
+ // See bug 384168.
+ aObject.set(obj);
+
+ RootedScript tableScript(cx, script);
+ if (!tableScript) {
+ tableScript = JS_GetFunctionScript(cx, function);
+ MOZ_ASSERT(tableScript);
+ }
+
+ aTableScript.set(tableScript);
+
+
+ { // Scope for AutoEntryScript
+
+ // We're going to run script via JS_ExecuteScript or
+ // JS_CallFunction, so we need an AutoEntryScript.
+ // This is Gecko-specific and not in any spec.
+ dom::AutoEntryScript aes(CurrentGlobalOrNull(cx),
+ "component loader load module");
+ JSContext* aescx = aes.cx();
+ bool ok;
+ if (script) {
+ ok = JS_ExecuteScript(aescx, script);
+ } else {
+ RootedValue rval(cx);
+ ok = JS_CallFunction(aescx, obj, function,
+ JS::HandleValueArray::empty(), &rval);
+ }
+
+ if (!ok) {
+ if (aPropagateExceptions && aes.HasException()) {
+ // Ignore return value because we're returning an error code
+ // anyway.
+ Unused << aes.StealException(aException);
+ }
+ aObject.set(nullptr);
+ aTableScript.set(nullptr);
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ /* Freed when we remove from the table. */
+ *aLocation = ToNewCString(nativePath);
+ if (!*aLocation) {
+ aObject.set(nullptr);
+ aTableScript.set(nullptr);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+void
+mozJSComponentLoader::UnloadModules()
+{
+ mInitialized = false;
+
+ if (mLoaderGlobal) {
+ MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?");
+
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+ RootedObject global(cx, mLoaderGlobal->GetJSObject());
+ if (global) {
+ JSAutoCompartment ac(cx, global);
+ if (JS_HasExtensibleLexicalEnvironment(global)) {
+ JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalEnvironment(global));
+ }
+ JS_SetAllNonReservedSlotsToUndefined(cx, global);
+ } else {
+ NS_WARNING("Going to leak!");
+ }
+
+ mLoaderGlobal = nullptr;
+ }
+
+ mInProgressImports.Clear();
+ mImports.Clear();
+
+ for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) {
+ iter.Data()->Clear();
+ iter.Remove();
+ }
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::Import(const nsACString& registryLocation,
+ HandleValue targetValArg,
+ JSContext* cx,
+ uint8_t optionalArgc,
+ MutableHandleValue retval)
+{
+ MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+ RootedValue targetVal(cx, targetValArg);
+ RootedObject targetObject(cx, nullptr);
+ if (optionalArgc) {
+ // The caller passed in the optional second argument. Get it.
+ if (targetVal.isObject()) {
+ // If we're passing in something like a content DOM window, chances
+ // are the caller expects the properties to end up on the object
+ // proper and not on the Xray holder. This is dubious, but can be used
+ // during testing. Given that dumb callers can already leak JSMs into
+ // content by passing a raw content JS object (where Xrays aren't
+ // possible), we aim for consistency here. Waive xray.
+ if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
+ !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
+ {
+ return NS_ERROR_FAILURE;
+ }
+ targetObject = &targetVal.toObject();
+ } else if (!targetVal.isNull()) {
+ // If targetVal isNull(), we actually want to leave targetObject null.
+ // Not doing so breaks |make package|.
+ return ReportOnCallerUTF8(cx, ERROR_SCOPE_OBJ,
+ PromiseFlatCString(registryLocation).get());
+ }
+ } else {
+ nsresult rv = FindTargetObject(cx, &targetObject);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ Maybe<JSAutoCompartment> ac;
+ if (targetObject) {
+ ac.emplace(cx, targetObject);
+ }
+
+ RootedObject global(cx);
+ nsresult rv = ImportInto(registryLocation, targetObject, cx, &global);
+
+ if (global) {
+ if (!JS_WrapObject(cx, &global)) {
+ NS_ERROR("can't wrap return value");
+ return NS_ERROR_FAILURE;
+ }
+
+ retval.setObject(*global);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::ImportInto(const nsACString& aLocation,
+ JSObject* aTargetObj,
+ nsAXPCNativeCallContext* cc,
+ JSObject** _retval)
+{
+ JSContext* callercx;
+ nsresult rv = cc->GetJSContext(&callercx);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RootedObject targetObject(callercx, aTargetObj);
+ RootedObject global(callercx);
+ rv = ImportInto(aLocation, targetObject, callercx, &global);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *_retval = global;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::IsModuleLoaded(const nsACString& aLocation,
+ bool* retval)
+{
+ MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+ nsresult rv;
+ if (!mInitialized) {
+ rv = ReallyInit();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ ComponentLoaderInfo info(aLocation);
+ rv = info.EnsureKey();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *retval = !!mImports.Get(info.Key());
+ return NS_OK;
+}
+
+static JSObject*
+ResolveModuleObjectPropertyById(JSContext* aCx, HandleObject aModObj, HandleId id)
+{
+ if (JS_HasExtensibleLexicalEnvironment(aModObj)) {
+ RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj));
+ bool found;
+ if (!JS_HasOwnPropertyById(aCx, lexical, id, &found)) {
+ return nullptr;
+ }
+ if (found) {
+ return lexical;
+ }
+ }
+ return aModObj;
+}
+
+nsresult
+mozJSComponentLoader::ImportInto(const nsACString& aLocation,
+ HandleObject targetObj,
+ JSContext* callercx,
+ MutableHandleObject vp)
+{
+ vp.set(nullptr);
+
+ nsresult rv;
+ if (!mInitialized) {
+ rv = ReallyInit();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ ComponentLoaderInfo info(aLocation);
+ rv = info.EnsureResolvedURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the JAR if there is one
+ nsCOMPtr<nsIJARURI> jarURI;
+ jarURI = do_QueryInterface(info.ResolvedURI(), &rv);
+ nsCOMPtr<nsIFileURL> baseFileURL;
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIURI> baseURI;
+ while (jarURI) {
+ jarURI->GetJARFile(getter_AddRefs(baseURI));
+ jarURI = do_QueryInterface(baseURI, &rv);
+ }
+ baseFileURL = do_QueryInterface(baseURI, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ baseFileURL = do_QueryInterface(info.ResolvedURI(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIFile> sourceFile;
+ rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> sourceLocalFile;
+ sourceLocalFile = do_QueryInterface(sourceFile, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = info.EnsureKey();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ ModuleEntry* mod;
+ nsAutoPtr<ModuleEntry> newEntry;
+ if (!mImports.Get(info.Key(), &mod) && !mInProgressImports.Get(info.Key(), &mod)) {
+ newEntry = new ModuleEntry(RootingContext::get(callercx));
+ if (!newEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+ mInProgressImports.Put(info.Key(), newEntry);
+
+ rv = info.EnsureURI();
+ NS_ENSURE_SUCCESS(rv, rv);
+ RootedValue exception(callercx);
+ rv = ObjectForLocation(info, sourceLocalFile, &newEntry->obj,
+ &newEntry->thisObjectKey,
+ &newEntry->location, true, &exception);
+
+ mInProgressImports.Remove(info.Key());
+
+ if (NS_FAILED(rv)) {
+ if (!exception.isUndefined()) {
+ // An exception was thrown during compilation. Propagate it
+ // out to our caller so they can report it.
+ if (!JS_WrapValue(callercx, &exception))
+ return NS_ERROR_OUT_OF_MEMORY;
+ JS_SetPendingException(callercx, exception);
+ return NS_OK;
+ }
+
+ // Something failed, but we don't know what it is, guess.
+ return NS_ERROR_FILE_NOT_FOUND;
+ }
+
+ // Set the location information for the new global, so that tools like
+ // about:memory may use that information
+ if (!mReuseLoaderGlobal) {
+ xpc::SetLocationForGlobal(newEntry->obj, aLocation);
+ }
+
+ mod = newEntry;
+ }
+
+ MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
+ vp.set(mod->obj);
+
+ if (targetObj) {
+ // cxhelper must be created before jsapi, so that jsapi is detroyed and
+ // pops any context it has pushed before we report to the caller context.
+ JSCLContextHelper cxhelper(callercx);
+
+ // Even though we are calling JS_SetPropertyById on targetObj, we want
+ // to ensure that we never run script here, so we use an AutoJSAPI and
+ // not an AutoEntryScript.
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+ JSAutoCompartment ac(cx, mod->obj);
+
+ RootedValue symbols(cx);
+ RootedObject exportedSymbolsHolder(cx, ResolveModuleObjectProperty(cx, mod->obj,
+ "EXPORTED_SYMBOLS"));
+ if (!exportedSymbolsHolder ||
+ !JS_GetProperty(cx, exportedSymbolsHolder,
+ "EXPORTED_SYMBOLS", &symbols)) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT,
+ location.get());
+ }
+
+ bool isArray;
+ if (!JS_IsArrayObject(cx, symbols, &isArray)) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!isArray) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY,
+ location.get());
+ }
+
+ RootedObject symbolsObj(cx, &symbols.toObject());
+
+ // Iterate over symbols array, installing symbols on targetObj:
+
+ uint32_t symbolCount = 0;
+ if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
+ location.get());
+ }
+
+#ifdef DEBUG
+ nsAutoCString logBuffer;
+#endif
+
+ RootedValue value(cx);
+ RootedId symbolId(cx);
+ RootedObject symbolHolder(cx);
+ for (uint32_t i = 0; i < symbolCount; ++i) {
+ if (!JS_GetElement(cx, symbolsObj, i, &value) ||
+ !value.isString() ||
+ !JS_ValueToId(cx, value, &symbolId)) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT,
+ location.get(), i);
+ }
+
+ symbolHolder = ResolveModuleObjectPropertyById(cx, mod->obj, symbolId);
+ if (!symbolHolder ||
+ !JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
+ JSAutoByteString bytes;
+ RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
+ if (!bytes.encodeUtf8(cx, symbolStr))
+ return NS_ERROR_FAILURE;
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
+ location.get(), bytes.ptr());
+ }
+
+ JSAutoCompartment target_ac(cx, targetObj);
+
+ if (!JS_WrapValue(cx, &value) ||
+ !JS_SetPropertyById(cx, targetObj, symbolId, value)) {
+ JSAutoByteString bytes;
+ RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
+ if (!bytes.encodeUtf8(cx, symbolStr))
+ return NS_ERROR_FAILURE;
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ReportOnCallerUTF8(cxhelper, ERROR_SETTING_SYMBOL,
+ location.get(), bytes.ptr());
+ }
+#ifdef DEBUG
+ if (i == 0) {
+ logBuffer.AssignLiteral("Installing symbols [ ");
+ }
+ JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
+ if (!!bytes)
+ logBuffer.Append(bytes.ptr());
+ logBuffer.Append(' ');
+ if (i == symbolCount - 1) {
+ nsCString location;
+ rv = info.GetLocation(location);
+ NS_ENSURE_SUCCESS(rv, rv);
+ LOG(("%s] from %s\n", logBuffer.get(), location.get()));
+ }
+#endif
+ }
+ }
+
+ // Cache this module for later
+ if (newEntry) {
+ mImports.Put(info.Key(), newEntry);
+ newEntry.forget();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::Unload(const nsACString & aLocation)
+{
+ nsresult rv;
+
+ if (!mInitialized) {
+ return NS_OK;
+ }
+
+ MOZ_RELEASE_ASSERT(!mReuseLoaderGlobal, "Module unloading not supported when "
+ "compartment sharing is enabled");
+
+ ComponentLoaderInfo info(aLocation);
+ rv = info.EnsureKey();
+ NS_ENSURE_SUCCESS(rv, rv);
+ ModuleEntry* mod;
+ if (mImports.Get(info.Key(), &mod)) {
+ mImports.Remove(info.Key());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+mozJSComponentLoader::Observe(nsISupports* subject, const char* topic,
+ const char16_t* data)
+{
+ if (!strcmp(topic, "xpcom-shutdown-loaders")) {
+ UnloadModules();
+ } else {
+ NS_ERROR("Unexpected observer topic.");
+ }
+
+ return NS_OK;
+}
+
+size_t
+mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+ size_t n = aMallocSizeOf(this);
+ n += aMallocSizeOf(location);
+
+ return n;
+}
+
+/* static */ already_AddRefed<nsIFactory>
+mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
+ const mozilla::Module::CIDEntry& entry)
+{
+ const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
+ MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?");
+
+ nsCOMPtr<nsIFactory> f;
+ nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ return f.forget();
+}
+
+//----------------------------------------------------------------------
+
+JSCLContextHelper::JSCLContextHelper(JSContext* aCx)
+ : mContext(aCx)
+ , mBuf(nullptr)
+{
+}
+
+JSCLContextHelper::~JSCLContextHelper()
+{
+ if (mBuf) {
+ JS_ReportErrorUTF8(mContext, "%s", mBuf);
+ JS_smprintf_free(mBuf);
+ }
+}
+
+void
+JSCLContextHelper::reportErrorAfterPop(char* buf)
+{
+ MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop");
+ mBuf = buf;
+}