js/xpconnect/loader/mozJSComponentLoader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1470 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/Attributes.h"
    1.11 +
    1.12 +#ifdef MOZ_LOGGING
    1.13 +#define FORCE_PR_LOG
    1.14 +#endif
    1.15 +
    1.16 +#include <cstdarg>
    1.17 +
    1.18 +#include "prlog.h"
    1.19 +#ifdef ANDROID
    1.20 +#include <android/log.h>
    1.21 +#endif
    1.22 +#ifdef XP_WIN
    1.23 +#include <windows.h>
    1.24 +#endif
    1.25 +
    1.26 +#include "jsapi.h"
    1.27 +#include "nsCOMPtr.h"
    1.28 +#include "nsAutoPtr.h"
    1.29 +#include "nsIComponentManager.h"
    1.30 +#include "mozilla/Module.h"
    1.31 +#include "nsIFile.h"
    1.32 +#include "mozJSComponentLoader.h"
    1.33 +#include "mozJSLoaderUtils.h"
    1.34 +#include "nsIJSRuntimeService.h"
    1.35 +#include "nsIXPConnect.h"
    1.36 +#include "nsIObserverService.h"
    1.37 +#include "nsIScriptSecurityManager.h"
    1.38 +#include "nsIFileURL.h"
    1.39 +#include "nsIJARURI.h"
    1.40 +#include "nsNetUtil.h"
    1.41 +#include "nsDOMBlobBuilder.h"
    1.42 +#include "jsprf.h"
    1.43 +#include "nsJSPrincipals.h"
    1.44 +#include "xpcprivate.h"
    1.45 +#include "xpcpublic.h"
    1.46 +#include "nsContentUtils.h"
    1.47 +#include "nsCxPusher.h"
    1.48 +#include "WrapperFactory.h"
    1.49 +
    1.50 +#include "mozilla/scache/StartupCache.h"
    1.51 +#include "mozilla/scache/StartupCacheUtils.h"
    1.52 +#include "mozilla/Preferences.h"
    1.53 +
    1.54 +#include "js/OldDebugAPI.h"
    1.55 +
    1.56 +using namespace mozilla;
    1.57 +using namespace mozilla::scache;
    1.58 +using namespace xpc;
    1.59 +using namespace JS;
    1.60 +
    1.61 +// This JSClass exists to trick silly code that expects toString()ing the
    1.62 +// global in a component scope to return something with "BackstagePass" in it
    1.63 +// to continue working.
    1.64 +static const JSClass kFakeBackstagePassJSClass =
    1.65 +{
    1.66 +    "FakeBackstagePass",
    1.67 +    0,
    1.68 +    JS_PropertyStub,
    1.69 +    JS_DeletePropertyStub,
    1.70 +    JS_PropertyStub,
    1.71 +    JS_StrictPropertyStub,
    1.72 +    JS_EnumerateStub,
    1.73 +    JS_ResolveStub,
    1.74 +    JS_ConvertStub
    1.75 +};
    1.76 +
    1.77 +static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1";
    1.78 +static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
    1.79 +static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
    1.80 +static const char kJSCachePrefix[] = "jsloader";
    1.81 +
    1.82 +#define HAVE_PR_MEMMAP
    1.83 +
    1.84 +/**
    1.85 + * Buffer sizes for serialization and deserialization of scripts.
    1.86 + * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
    1.87 + */
    1.88 +#define XPC_SERIALIZATION_BUFFER_SIZE   (64 * 1024)
    1.89 +#define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
    1.90 +
    1.91 +#ifdef PR_LOGGING
    1.92 +// NSPR_LOG_MODULES=JSComponentLoader:5
    1.93 +static PRLogModuleInfo *gJSCLLog;
    1.94 +#endif
    1.95 +
    1.96 +#define LOG(args) PR_LOG(gJSCLLog, PR_LOG_DEBUG, args)
    1.97 +
    1.98 +// Components.utils.import error messages
    1.99 +#define ERROR_SCOPE_OBJ "%s - Second argument must be an object."
   1.100 +#define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present."
   1.101 +#define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array."
   1.102 +#define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS."
   1.103 +#define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string."
   1.104 +#define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'."
   1.105 +#define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object."
   1.106 +
   1.107 +static bool
   1.108 +Dump(JSContext *cx, unsigned argc, Value *vp)
   1.109 +{
   1.110 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.111 +
   1.112 +    if (args.length() == 0)
   1.113 +        return true;
   1.114 +
   1.115 +    JSString *str = JS::ToString(cx, args[0]);
   1.116 +    if (!str)
   1.117 +        return false;
   1.118 +
   1.119 +    size_t length;
   1.120 +    const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
   1.121 +    if (!chars)
   1.122 +        return false;
   1.123 +
   1.124 +    NS_ConvertUTF16toUTF8 utf8str(reinterpret_cast<const char16_t*>(chars),
   1.125 +                                  length);
   1.126 +#ifdef ANDROID
   1.127 +    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
   1.128 +#endif
   1.129 +#ifdef XP_WIN
   1.130 +    if (IsDebuggerPresent()) {
   1.131 +      OutputDebugStringW(reinterpret_cast<const wchar_t*>(chars));
   1.132 +    }
   1.133 +#endif
   1.134 +    fputs(utf8str.get(), stdout);
   1.135 +    fflush(stdout);
   1.136 +    return true;
   1.137 +}
   1.138 +
   1.139 +static bool
   1.140 +Debug(JSContext *cx, unsigned argc, jsval *vp)
   1.141 +{
   1.142 +#ifdef DEBUG
   1.143 +    return Dump(cx, argc, vp);
   1.144 +#else
   1.145 +    return true;
   1.146 +#endif
   1.147 +}
   1.148 +
   1.149 +static bool
   1.150 +File(JSContext *cx, unsigned argc, Value *vp)
   1.151 +{
   1.152 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.153 +
   1.154 +    if (args.length() == 0) {
   1.155 +        XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
   1.156 +        return false;
   1.157 +    }
   1.158 +
   1.159 +    nsCOMPtr<nsISupports> native;
   1.160 +    nsresult rv = nsDOMMultipartFile::NewFile(getter_AddRefs(native));
   1.161 +    if (NS_FAILED(rv)) {
   1.162 +        XPCThrower::Throw(rv, cx);
   1.163 +        return false;
   1.164 +    }
   1.165 +
   1.166 +    nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
   1.167 +    MOZ_ASSERT(initializer);
   1.168 +
   1.169 +    rv = initializer->Initialize(nullptr, cx, nullptr, args);
   1.170 +    if (NS_FAILED(rv)) {
   1.171 +        XPCThrower::Throw(rv, cx);
   1.172 +        return false;
   1.173 +    }
   1.174 +
   1.175 +    nsXPConnect *xpc = nsXPConnect::XPConnect();
   1.176 +    JSObject *glob = CurrentGlobalOrNull(cx);
   1.177 +
   1.178 +    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
   1.179 +    rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
   1.180 +                                &NS_GET_IID(nsISupports),
   1.181 +                                true, args.rval());
   1.182 +    if (NS_FAILED(rv)) {
   1.183 +        XPCThrower::Throw(rv, cx);
   1.184 +        return false;
   1.185 +    }
   1.186 +    return true;
   1.187 +}
   1.188 +
   1.189 +static bool
   1.190 +Blob(JSContext *cx, unsigned argc, Value *vp)
   1.191 +{
   1.192 +    CallArgs args = CallArgsFromVp(argc, vp);
   1.193 +
   1.194 +    nsCOMPtr<nsISupports> native;
   1.195 +    nsresult rv = nsDOMMultipartFile::NewBlob(getter_AddRefs(native));
   1.196 +    if (NS_FAILED(rv)) {
   1.197 +        XPCThrower::Throw(rv, cx);
   1.198 +        return false;
   1.199 +    }
   1.200 +
   1.201 +    nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
   1.202 +    MOZ_ASSERT(initializer);
   1.203 +
   1.204 +    rv = initializer->Initialize(nullptr, cx, nullptr, args);
   1.205 +    if (NS_FAILED(rv)) {
   1.206 +        XPCThrower::Throw(rv, cx);
   1.207 +        return false;
   1.208 +    }
   1.209 +
   1.210 +    nsXPConnect *xpc = nsXPConnect::XPConnect();
   1.211 +    JSObject *glob = CurrentGlobalOrNull(cx);
   1.212 +
   1.213 +    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
   1.214 +    rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
   1.215 +                                &NS_GET_IID(nsISupports),
   1.216 +                                true, args.rval());
   1.217 +    if (NS_FAILED(rv)) {
   1.218 +        XPCThrower::Throw(rv, cx);
   1.219 +        return false;
   1.220 +    }
   1.221 +    return true;
   1.222 +}
   1.223 +
   1.224 +static const JSFunctionSpec gGlobalFun[] = {
   1.225 +    JS_FS("dump",    Dump,   1,0),
   1.226 +    JS_FS("debug",   Debug,  1,0),
   1.227 +    JS_FS("atob",    Atob,   1,0),
   1.228 +    JS_FS("btoa",    Btoa,   1,0),
   1.229 +    JS_FS("File",    File,   1,JSFUN_CONSTRUCTOR),
   1.230 +    JS_FS("Blob",    Blob,   2,JSFUN_CONSTRUCTOR),
   1.231 +    JS_FS_END
   1.232 +};
   1.233 +
   1.234 +class MOZ_STACK_CLASS JSCLContextHelper
   1.235 +{
   1.236 +public:
   1.237 +    JSCLContextHelper(JSContext* aCx);
   1.238 +    ~JSCLContextHelper();
   1.239 +
   1.240 +    void reportErrorAfterPop(char *buf);
   1.241 +
   1.242 +    operator JSContext*() const {return mContext;}
   1.243 +
   1.244 +private:
   1.245 +
   1.246 +    JSContext* mContext;
   1.247 +    nsCxPusher mPusher;
   1.248 +    char*      mBuf;
   1.249 +
   1.250 +    // prevent copying and assignment
   1.251 +    JSCLContextHelper(const JSCLContextHelper &) MOZ_DELETE;
   1.252 +    const JSCLContextHelper& operator=(const JSCLContextHelper &) MOZ_DELETE;
   1.253 +};
   1.254 +
   1.255 +
   1.256 +class JSCLAutoErrorReporterSetter
   1.257 +{
   1.258 +public:
   1.259 +    JSCLAutoErrorReporterSetter(JSContext* cx, JSErrorReporter reporter)
   1.260 +        {mContext = cx; mOldReporter = JS_SetErrorReporter(cx, reporter);}
   1.261 +    ~JSCLAutoErrorReporterSetter()
   1.262 +        {JS_SetErrorReporter(mContext, mOldReporter);}
   1.263 +private:
   1.264 +    JSContext* mContext;
   1.265 +    JSErrorReporter mOldReporter;
   1.266 +
   1.267 +    JSCLAutoErrorReporterSetter(const JSCLAutoErrorReporterSetter &) MOZ_DELETE;
   1.268 +    const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &) MOZ_DELETE;
   1.269 +};
   1.270 +
   1.271 +static nsresult
   1.272 +ReportOnCaller(JSContext *callerContext,
   1.273 +               const char *format, ...) {
   1.274 +    if (!callerContext) {
   1.275 +        return NS_ERROR_FAILURE;
   1.276 +    }
   1.277 +
   1.278 +    va_list ap;
   1.279 +    va_start(ap, format);
   1.280 +
   1.281 +    char *buf = JS_vsmprintf(format, ap);
   1.282 +    if (!buf) {
   1.283 +        return NS_ERROR_OUT_OF_MEMORY;
   1.284 +    }
   1.285 +
   1.286 +    JS_ReportError(callerContext, buf);
   1.287 +    JS_smprintf_free(buf);
   1.288 +
   1.289 +    return NS_OK;
   1.290 +}
   1.291 +
   1.292 +static nsresult
   1.293 +ReportOnCaller(JSCLContextHelper &helper,
   1.294 +               const char *format, ...)
   1.295 +{
   1.296 +    va_list ap;
   1.297 +    va_start(ap, format);
   1.298 +
   1.299 +    char *buf = JS_vsmprintf(format, ap);
   1.300 +    if (!buf) {
   1.301 +        return NS_ERROR_OUT_OF_MEMORY;
   1.302 +    }
   1.303 +
   1.304 +    helper.reportErrorAfterPop(buf);
   1.305 +
   1.306 +    return NS_OK;
   1.307 +}
   1.308 +
   1.309 +mozJSComponentLoader::mozJSComponentLoader()
   1.310 +    : mRuntime(nullptr),
   1.311 +      mContext(nullptr),
   1.312 +      mModules(32),
   1.313 +      mImports(32),
   1.314 +      mInProgressImports(32),
   1.315 +      mThisObjects(32),
   1.316 +      mInitialized(false),
   1.317 +      mReuseLoaderGlobal(false)
   1.318 +{
   1.319 +    MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton");
   1.320 +
   1.321 +#ifdef PR_LOGGING
   1.322 +    if (!gJSCLLog) {
   1.323 +        gJSCLLog = PR_NewLogModule("JSComponentLoader");
   1.324 +    }
   1.325 +#endif
   1.326 +
   1.327 +    sSelf = this;
   1.328 +}
   1.329 +
   1.330 +mozJSComponentLoader::~mozJSComponentLoader()
   1.331 +{
   1.332 +    if (mInitialized) {
   1.333 +        NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader");
   1.334 +        UnloadModules();
   1.335 +    }
   1.336 +
   1.337 +    sSelf = nullptr;
   1.338 +}
   1.339 +
   1.340 +mozJSComponentLoader*
   1.341 +mozJSComponentLoader::sSelf;
   1.342 +
   1.343 +NS_IMPL_ISUPPORTS(mozJSComponentLoader,
   1.344 +                  mozilla::ModuleLoader,
   1.345 +                  xpcIJSModuleLoader,
   1.346 +                  nsIObserver)
   1.347 +
   1.348 +nsresult
   1.349 +mozJSComponentLoader::ReallyInit()
   1.350 +{
   1.351 +    nsresult rv;
   1.352 +
   1.353 +    mReuseLoaderGlobal = Preferences::GetBool("jsloader.reuseGlobal");
   1.354 +
   1.355 +    // XXXkhuey B2G child processes have some sort of preferences race that
   1.356 +    // results in getting the wrong value.
   1.357 +#ifdef MOZ_B2G
   1.358 +    mReuseLoaderGlobal = true;
   1.359 +#endif
   1.360 +
   1.361 +    /*
   1.362 +     * Get the JSRuntime from the runtime svc, if possible.
   1.363 +     * We keep a reference around, because it's a Bad Thing if the runtime
   1.364 +     * service gets shut down before we're done.  Bad!
   1.365 +     */
   1.366 +
   1.367 +    mRuntimeService = do_GetService(kJSRuntimeServiceContractID, &rv);
   1.368 +    if (NS_FAILED(rv) ||
   1.369 +        NS_FAILED(rv = mRuntimeService->GetRuntime(&mRuntime)))
   1.370 +        return rv;
   1.371 +
   1.372 +    // Create our compilation context.
   1.373 +    mContext = JS_NewContext(mRuntime, 256);
   1.374 +    if (!mContext)
   1.375 +        return NS_ERROR_OUT_OF_MEMORY;
   1.376 +
   1.377 +    nsCOMPtr<nsIScriptSecurityManager> secman =
   1.378 +        do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
   1.379 +    if (!secman)
   1.380 +        return NS_ERROR_FAILURE;
   1.381 +
   1.382 +    rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
   1.383 +    if (NS_FAILED(rv) || !mSystemPrincipal)
   1.384 +        return NS_ERROR_FAILURE;
   1.385 +
   1.386 +    nsCOMPtr<nsIObserverService> obsSvc =
   1.387 +        do_GetService(kObserverServiceContractID, &rv);
   1.388 +    NS_ENSURE_SUCCESS(rv, rv);
   1.389 +
   1.390 +    rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false);
   1.391 +    NS_ENSURE_SUCCESS(rv, rv);
   1.392 +
   1.393 +    mInitialized = true;
   1.394 +
   1.395 +    return NS_OK;
   1.396 +}
   1.397 +
   1.398 +const mozilla::Module*
   1.399 +mozJSComponentLoader::LoadModule(FileLocation &aFile)
   1.400 +{
   1.401 +    nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
   1.402 +
   1.403 +    nsCString spec;
   1.404 +    aFile.GetURIString(spec);
   1.405 +
   1.406 +    nsCOMPtr<nsIURI> uri;
   1.407 +    nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
   1.408 +    if (NS_FAILED(rv))
   1.409 +        return nullptr;
   1.410 +
   1.411 +    if (!mInitialized) {
   1.412 +        rv = ReallyInit();
   1.413 +        if (NS_FAILED(rv))
   1.414 +            return nullptr;
   1.415 +    }
   1.416 +
   1.417 +    ModuleEntry* mod;
   1.418 +    if (mModules.Get(spec, &mod))
   1.419 +    return mod;
   1.420 +
   1.421 +    nsAutoPtr<ModuleEntry> entry(new ModuleEntry(mContext));
   1.422 +
   1.423 +    JSAutoRequest ar(mContext);
   1.424 +    RootedValue dummy(mContext);
   1.425 +    rv = ObjectForLocation(file, uri, &entry->obj, &entry->thisObjectKey,
   1.426 +                           &entry->location, false, &dummy);
   1.427 +    if (NS_FAILED(rv)) {
   1.428 +        return nullptr;
   1.429 +    }
   1.430 +
   1.431 +    nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
   1.432 +                                               &rv);
   1.433 +    if (NS_FAILED(rv))
   1.434 +        return nullptr;
   1.435 +
   1.436 +    nsCOMPtr<nsIComponentManager> cm;
   1.437 +    rv = NS_GetComponentManager(getter_AddRefs(cm));
   1.438 +    if (NS_FAILED(rv))
   1.439 +        return nullptr;
   1.440 +
   1.441 +    JSCLContextHelper cx(mContext);
   1.442 +    JSAutoCompartment ac(cx, entry->obj);
   1.443 +
   1.444 +    nsCOMPtr<nsIXPConnectJSObjectHolder> cm_holder;
   1.445 +    rv = xpc->WrapNative(cx, entry->obj, cm,
   1.446 +                         NS_GET_IID(nsIComponentManager),
   1.447 +                         getter_AddRefs(cm_holder));
   1.448 +
   1.449 +    if (NS_FAILED(rv)) {
   1.450 +        return nullptr;
   1.451 +    }
   1.452 +
   1.453 +    JSObject* cm_jsobj = cm_holder->GetJSObject();
   1.454 +    if (!cm_jsobj) {
   1.455 +        return nullptr;
   1.456 +    }
   1.457 +
   1.458 +    nsCOMPtr<nsIXPConnectJSObjectHolder> file_holder;
   1.459 +    RootedObject entryObj(cx, entry->obj);
   1.460 +    rv = xpc->WrapNative(cx, entryObj, file,
   1.461 +                         NS_GET_IID(nsIFile),
   1.462 +                         getter_AddRefs(file_holder));
   1.463 +
   1.464 +    if (NS_FAILED(rv)) {
   1.465 +        return nullptr;
   1.466 +    }
   1.467 +
   1.468 +    JSObject* file_jsobj = file_holder->GetJSObject();
   1.469 +    if (!file_jsobj) {
   1.470 +        return nullptr;
   1.471 +    }
   1.472 +
   1.473 +    JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter);
   1.474 +
   1.475 +    RootedValue NSGetFactory_val(cx);
   1.476 +    if (!JS_GetProperty(cx, entryObj, "NSGetFactory", &NSGetFactory_val) ||
   1.477 +        JSVAL_IS_VOID(NSGetFactory_val)) {
   1.478 +        return nullptr;
   1.479 +    }
   1.480 +
   1.481 +    if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
   1.482 +        nsAutoCString spec;
   1.483 +        uri->GetSpec(spec);
   1.484 +        JS_ReportError(cx, "%s has NSGetFactory property that is not a function",
   1.485 +                       spec.get());
   1.486 +        return nullptr;
   1.487 +    }
   1.488 +
   1.489 +    RootedObject jsGetFactoryObj(cx);
   1.490 +    if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
   1.491 +        !jsGetFactoryObj) {
   1.492 +        /* XXX report error properly */
   1.493 +        return nullptr;
   1.494 +    }
   1.495 +
   1.496 +    rv = xpc->WrapJS(cx, jsGetFactoryObj,
   1.497 +                     NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj));
   1.498 +    if (NS_FAILED(rv)) {
   1.499 +        /* XXX report error properly */
   1.500 +#ifdef DEBUG
   1.501 +        fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
   1.502 +#endif
   1.503 +        return nullptr;
   1.504 +    }
   1.505 +
   1.506 +    // Cache this module for later
   1.507 +    mModules.Put(spec, entry);
   1.508 +
   1.509 +    // Set the location information for the new global, so that tools like
   1.510 +    // about:memory may use that information
   1.511 +    if (!mReuseLoaderGlobal) {
   1.512 +        xpc::SetLocationForGlobal(entryObj, spec);
   1.513 +    }
   1.514 +
   1.515 +    // The hash owns the ModuleEntry now, forget about it
   1.516 +    return entry.forget();
   1.517 +}
   1.518 +
   1.519 +nsresult
   1.520 +mozJSComponentLoader::FindTargetObject(JSContext* aCx,
   1.521 +                                       MutableHandleObject aTargetObject)
   1.522 +{
   1.523 +    aTargetObject.set(nullptr);
   1.524 +
   1.525 +    RootedObject targetObject(aCx);
   1.526 +    if (mReuseLoaderGlobal) {
   1.527 +        JSScript* script =
   1.528 +            js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
   1.529 +        if (script) {
   1.530 +            targetObject = mThisObjects.Get(script);
   1.531 +        }
   1.532 +    }
   1.533 +
   1.534 +    // The above could fail, even if mReuseLoaderGlobal, if the scripted
   1.535 +    // caller is not a component/JSM (it could be a DOM scope, for
   1.536 +    // instance).
   1.537 +    if (!targetObject) {
   1.538 +        // Our targetObject is the caller's global object. Let's get it.
   1.539 +        nsresult rv;
   1.540 +        nsCOMPtr<nsIXPConnect> xpc =
   1.541 +            do_GetService(kXPConnectServiceContractID, &rv);
   1.542 +        NS_ENSURE_SUCCESS(rv, rv);
   1.543 +
   1.544 +        nsAXPCNativeCallContext *cc = nullptr;
   1.545 +        rv = xpc->GetCurrentNativeCallContext(&cc);
   1.546 +        NS_ENSURE_SUCCESS(rv, rv);
   1.547 +
   1.548 +        nsCOMPtr<nsIXPConnectWrappedNative> wn;
   1.549 +        rv = cc->GetCalleeWrapper(getter_AddRefs(wn));
   1.550 +        NS_ENSURE_SUCCESS(rv, rv);
   1.551 +
   1.552 +        targetObject = wn->GetJSObject();
   1.553 +        if (!targetObject) {
   1.554 +            NS_ERROR("null calling object");
   1.555 +            return NS_ERROR_FAILURE;
   1.556 +        }
   1.557 +
   1.558 +        targetObject = JS_GetGlobalForObject(aCx, targetObject);
   1.559 +    }
   1.560 +
   1.561 +    aTargetObject.set(targetObject);
   1.562 +    return NS_OK;
   1.563 +}
   1.564 +
   1.565 +void
   1.566 +mozJSComponentLoader::NoteSubScript(HandleScript aScript, HandleObject aThisObject)
   1.567 +{
   1.568 +  if (!mInitialized && NS_FAILED(ReallyInit())) {
   1.569 +      MOZ_CRASH();
   1.570 +  }
   1.571 +
   1.572 +  if (js::GetObjectJSClass(aThisObject) == &kFakeBackstagePassJSClass) {
   1.573 +    mThisObjects.Put(aScript, aThisObject);
   1.574 +  }
   1.575 +}
   1.576 +
   1.577 +/* static */ size_t
   1.578 +mozJSComponentLoader::DataEntrySizeOfExcludingThis(const nsACString& aKey,
   1.579 +                                                   ModuleEntry* const& aData,
   1.580 +                                                   MallocSizeOf aMallocSizeOf, void*)
   1.581 +{
   1.582 +    return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
   1.583 +        aData->SizeOfIncludingThis(aMallocSizeOf);
   1.584 +}
   1.585 +
   1.586 +/* static */ size_t
   1.587 +mozJSComponentLoader::ClassEntrySizeOfExcludingThis(const nsACString& aKey,
   1.588 +                                                    const nsAutoPtr<ModuleEntry>& aData,
   1.589 +                                                    MallocSizeOf aMallocSizeOf, void*)
   1.590 +{
   1.591 +    return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
   1.592 +        aData->SizeOfIncludingThis(aMallocSizeOf);
   1.593 +}
   1.594 +
   1.595 +size_t
   1.596 +mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
   1.597 +{
   1.598 +    size_t amount = aMallocSizeOf(this);
   1.599 +
   1.600 +    amount += mModules.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf);
   1.601 +    amount += mImports.SizeOfExcludingThis(ClassEntrySizeOfExcludingThis, aMallocSizeOf);
   1.602 +    amount += mInProgressImports.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf);
   1.603 +    amount += mThisObjects.SizeOfExcludingThis(nullptr, aMallocSizeOf);
   1.604 +
   1.605 +    return amount;
   1.606 +}
   1.607 +
   1.608 +// Some stack based classes for cleaning up on early return
   1.609 +#ifdef HAVE_PR_MEMMAP
   1.610 +class FileAutoCloser
   1.611 +{
   1.612 + public:
   1.613 +    explicit FileAutoCloser(PRFileDesc *file) : mFile(file) {}
   1.614 +    ~FileAutoCloser() { PR_Close(mFile); }
   1.615 + private:
   1.616 +    PRFileDesc *mFile;
   1.617 +};
   1.618 +
   1.619 +class FileMapAutoCloser
   1.620 +{
   1.621 + public:
   1.622 +    explicit FileMapAutoCloser(PRFileMap *map) : mMap(map) {}
   1.623 +    ~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
   1.624 + private:
   1.625 +    PRFileMap *mMap;
   1.626 +};
   1.627 +#else
   1.628 +class ANSIFileAutoCloser
   1.629 +{
   1.630 + public:
   1.631 +    explicit ANSIFileAutoCloser(FILE *file) : mFile(file) {}
   1.632 +    ~ANSIFileAutoCloser() { fclose(mFile); }
   1.633 + private:
   1.634 +    FILE *mFile;
   1.635 +};
   1.636 +#endif
   1.637 +
   1.638 +JSObject*
   1.639 +mozJSComponentLoader::PrepareObjectForLocation(JSCLContextHelper& aCx,
   1.640 +                                               nsIFile *aComponentFile,
   1.641 +                                               nsIURI *aURI,
   1.642 +                                               bool aReuseLoaderGlobal,
   1.643 +                                               bool *aRealFile)
   1.644 +{
   1.645 +    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
   1.646 +    if (aReuseLoaderGlobal) {
   1.647 +        holder = mLoaderGlobal;
   1.648 +    }
   1.649 +
   1.650 +    nsresult rv = NS_OK;
   1.651 +    nsCOMPtr<nsIXPConnect> xpc =
   1.652 +        do_GetService(kXPConnectServiceContractID, &rv);
   1.653 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.654 +    bool createdNewGlobal = false;
   1.655 +
   1.656 +    if (!mLoaderGlobal) {
   1.657 +        nsRefPtr<BackstagePass> backstagePass;
   1.658 +        rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
   1.659 +        NS_ENSURE_SUCCESS(rv, nullptr);
   1.660 +
   1.661 +        CompartmentOptions options;
   1.662 +        options.setZone(SystemZone)
   1.663 +               .setVersion(JSVERSION_LATEST);
   1.664 +        // Defer firing OnNewGlobalObject until after the __URI__ property has
   1.665 +        // been defined so the JS debugger can tell what module the global is
   1.666 +        // for
   1.667 +        rv = xpc->InitClassesWithNewWrappedGlobal(aCx,
   1.668 +                                                  static_cast<nsIGlobalObject *>(backstagePass),
   1.669 +                                                  mSystemPrincipal,
   1.670 +                                                  nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
   1.671 +                                                  options,
   1.672 +                                                  getter_AddRefs(holder));
   1.673 +        NS_ENSURE_SUCCESS(rv, nullptr);
   1.674 +        createdNewGlobal = true;
   1.675 +
   1.676 +        RootedObject global(aCx, holder->GetJSObject());
   1.677 +        NS_ENSURE_TRUE(global, nullptr);
   1.678 +
   1.679 +        backstagePass->SetGlobalObject(global);
   1.680 +
   1.681 +        JSAutoCompartment ac(aCx, global);
   1.682 +        if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
   1.683 +            !JS_DefineProfilingFunctions(aCx, global)) {
   1.684 +            return nullptr;
   1.685 +        }
   1.686 +
   1.687 +        if (aReuseLoaderGlobal) {
   1.688 +            mLoaderGlobal = holder;
   1.689 +        }
   1.690 +    }
   1.691 +
   1.692 +    RootedObject obj(aCx, holder->GetJSObject());
   1.693 +    NS_ENSURE_TRUE(obj, nullptr);
   1.694 +
   1.695 +    JSAutoCompartment ac(aCx, obj);
   1.696 +
   1.697 +    if (aReuseLoaderGlobal) {
   1.698 +        // If we're reusing the loader global, we don't actually use the
   1.699 +        // global, but rather we use a different object as the 'this' object.
   1.700 +        obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass, NullPtr(), NullPtr());
   1.701 +        NS_ENSURE_TRUE(obj, nullptr);
   1.702 +    }
   1.703 +
   1.704 +    *aRealFile = false;
   1.705 +
   1.706 +    // need to be extra careful checking for URIs pointing to files
   1.707 +    // EnsureFile may not always get called, especially on resource URIs
   1.708 +    // so we need to call GetFile to make sure this is a valid file
   1.709 +    nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
   1.710 +    nsCOMPtr<nsIFile> testFile;
   1.711 +    if (NS_SUCCEEDED(rv)) {
   1.712 +        fileURL->GetFile(getter_AddRefs(testFile));
   1.713 +    }
   1.714 +
   1.715 +    if (testFile) {
   1.716 +        *aRealFile = true;
   1.717 +
   1.718 +        nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
   1.719 +        rv = xpc->WrapNative(aCx, obj, aComponentFile,
   1.720 +                             NS_GET_IID(nsIFile),
   1.721 +                             getter_AddRefs(locationHolder));
   1.722 +        NS_ENSURE_SUCCESS(rv, nullptr);
   1.723 +
   1.724 +        RootedObject locationObj(aCx, locationHolder->GetJSObject());
   1.725 +        NS_ENSURE_TRUE(locationObj, nullptr);
   1.726 +
   1.727 +        if (!JS_DefineProperty(aCx, obj, "__LOCATION__", locationObj, 0))
   1.728 +            return nullptr;
   1.729 +    }
   1.730 +
   1.731 +    nsAutoCString nativePath;
   1.732 +    rv = aURI->GetSpec(nativePath);
   1.733 +    NS_ENSURE_SUCCESS(rv, nullptr);
   1.734 +
   1.735 +    // Expose the URI from which the script was imported through a special
   1.736 +    // variable that we insert into the JSM.
   1.737 +    RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length()));
   1.738 +    NS_ENSURE_TRUE(exposedUri, nullptr);
   1.739 +
   1.740 +    if (!JS_DefineProperty(aCx, obj, "__URI__", exposedUri, 0))
   1.741 +        return nullptr;
   1.742 +
   1.743 +    if (createdNewGlobal) {
   1.744 +        RootedObject global(aCx, holder->GetJSObject());
   1.745 +        JS_FireOnNewGlobalObject(aCx, global);
   1.746 +    }
   1.747 +
   1.748 +    return obj;
   1.749 +}
   1.750 +
   1.751 +nsresult
   1.752 +mozJSComponentLoader::ObjectForLocation(nsIFile *aComponentFile,
   1.753 +                                        nsIURI *aURI,
   1.754 +                                        MutableHandleObject aObject,
   1.755 +                                        MutableHandleScript aTableScript,
   1.756 +                                        char **aLocation,
   1.757 +                                        bool aPropagateExceptions,
   1.758 +                                        MutableHandleValue aException)
   1.759 +{
   1.760 +    JSCLContextHelper cx(mContext);
   1.761 +
   1.762 +    JS_AbortIfWrongThread(JS_GetRuntime(cx));
   1.763 +
   1.764 +    JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter);
   1.765 +
   1.766 +    bool realFile = false;
   1.767 +    RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aURI,
   1.768 +                                                  mReuseLoaderGlobal, &realFile));
   1.769 +    NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
   1.770 +
   1.771 +    JSAutoCompartment ac(cx, obj);
   1.772 +
   1.773 +    RootedScript script(cx);
   1.774 +    RootedFunction function(cx);
   1.775 +
   1.776 +    nsAutoCString nativePath;
   1.777 +    nsresult rv = aURI->GetSpec(nativePath);
   1.778 +    NS_ENSURE_SUCCESS(rv, rv);
   1.779 +
   1.780 +    // Before compiling the script, first check to see if we have it in
   1.781 +    // the startupcache.  Note: as a rule, startupcache errors are not fatal
   1.782 +    // to loading the script, since we can always slow-load.
   1.783 +
   1.784 +    bool writeToCache = false;
   1.785 +    StartupCache* cache = StartupCache::GetSingleton();
   1.786 +
   1.787 +    nsAutoCString cachePath(kJSCachePrefix);
   1.788 +    rv = PathifyURI(aURI, cachePath);
   1.789 +    NS_ENSURE_SUCCESS(rv, rv);
   1.790 +
   1.791 +    if (cache) {
   1.792 +        if (!mReuseLoaderGlobal) {
   1.793 +            rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
   1.794 +        } else {
   1.795 +            rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
   1.796 +                                    function.address());
   1.797 +        }
   1.798 +
   1.799 +        if (NS_SUCCEEDED(rv)) {
   1.800 +            LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
   1.801 +        } else {
   1.802 +            // This is ok, it just means the script is not yet in the
   1.803 +            // cache. Could mean that the cache was corrupted and got removed,
   1.804 +            // but either way we're going to write this out.
   1.805 +            writeToCache = true;
   1.806 +        }
   1.807 +    }
   1.808 +
   1.809 +    if (!script && !function) {
   1.810 +        // The script wasn't in the cache , so compile it now.
   1.811 +        LOG(("Slow loading %s\n", nativePath.get()));
   1.812 +
   1.813 +        // If aPropagateExceptions is true, then our caller wants us to propagate
   1.814 +        // any exceptions out to our caller. Ensure that the engine doesn't
   1.815 +        // eagerly report the exception.
   1.816 +        AutoSaveContextOptions asco(cx);
   1.817 +        if (aPropagateExceptions)
   1.818 +            ContextOptionsRef(cx).setDontReportUncaught(true);
   1.819 +
   1.820 +        // Note - if mReuseLoaderGlobal is true, then we can't do lazy source,
   1.821 +        // because we compile things as functions (rather than script), and lazy
   1.822 +        // source isn't supported in that configuration. That's ok though,
   1.823 +        // because we only do mReuseLoaderGlobal on b2g, where we invoke
   1.824 +        // setDiscardSource(true) on the entire global.
   1.825 +        CompileOptions options(cx);
   1.826 +        options.setNoScriptRval(mReuseLoaderGlobal ? false : true)
   1.827 +               .setVersion(JSVERSION_LATEST)
   1.828 +               .setFileAndLine(nativePath.get(), 1)
   1.829 +               .setSourceIsLazy(!mReuseLoaderGlobal);
   1.830 +
   1.831 +        if (realFile) {
   1.832 +#ifdef HAVE_PR_MEMMAP
   1.833 +            int64_t fileSize;
   1.834 +            rv = aComponentFile->GetFileSize(&fileSize);
   1.835 +            if (NS_FAILED(rv)) {
   1.836 +                return rv;
   1.837 +            }
   1.838 +
   1.839 +            int64_t maxSize = UINT32_MAX;
   1.840 +            if (fileSize > maxSize) {
   1.841 +                NS_ERROR("file too large");
   1.842 +                return NS_ERROR_FAILURE;
   1.843 +            }
   1.844 +
   1.845 +            PRFileDesc *fileHandle;
   1.846 +            rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
   1.847 +            if (NS_FAILED(rv)) {
   1.848 +                return NS_ERROR_FILE_NOT_FOUND;
   1.849 +            }
   1.850 +
   1.851 +            // Make sure the file is closed, no matter how we return.
   1.852 +            FileAutoCloser fileCloser(fileHandle);
   1.853 +
   1.854 +            // We don't provide the file size here.  If we did, PR_CreateFileMap
   1.855 +            // would simply stat() the file to verify that the size we provided
   1.856 +            // didn't require extending the file.  We know that the file doesn't
   1.857 +            // need to be extended, so skip the extra work by not providing the
   1.858 +            // size.
   1.859 +            PRFileMap *map = PR_CreateFileMap(fileHandle, 0, PR_PROT_READONLY);
   1.860 +            if (!map) {
   1.861 +                NS_ERROR("Failed to create file map");
   1.862 +                return NS_ERROR_FAILURE;
   1.863 +            }
   1.864 +
   1.865 +            // Make sure the file map is closed, no matter how we return.
   1.866 +            FileMapAutoCloser mapCloser(map);
   1.867 +
   1.868 +            uint32_t fileSize32 = fileSize;
   1.869 +
   1.870 +            char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
   1.871 +            if (!buf) {
   1.872 +                NS_WARNING("Failed to map file");
   1.873 +                return NS_ERROR_FAILURE;
   1.874 +            }
   1.875 +
   1.876 +            if (!mReuseLoaderGlobal) {
   1.877 +                script = Compile(cx, obj, options, buf,
   1.878 +                                     fileSize32);
   1.879 +            } else {
   1.880 +                function = CompileFunction(cx, obj, options,
   1.881 +                                               nullptr, 0, nullptr,
   1.882 +                                               buf, fileSize32);
   1.883 +            }
   1.884 +
   1.885 +            PR_MemUnmap(buf, fileSize32);
   1.886 +
   1.887 +#else  /* HAVE_PR_MEMMAP */
   1.888 +
   1.889 +            /**
   1.890 +             * No memmap implementation, so fall back to
   1.891 +             * reading in the file
   1.892 +             */
   1.893 +
   1.894 +            FILE *fileHandle;
   1.895 +            rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle);
   1.896 +            if (NS_FAILED(rv)) {
   1.897 +                return NS_ERROR_FILE_NOT_FOUND;
   1.898 +            }
   1.899 +
   1.900 +            // Ensure file fclose
   1.901 +            ANSIFileAutoCloser fileCloser(fileHandle);
   1.902 +
   1.903 +            int64_t len;
   1.904 +            rv = aComponentFile->GetFileSize(&len);
   1.905 +            if (NS_FAILED(rv) || len < 0) {
   1.906 +                NS_WARNING("Failed to get file size");
   1.907 +                return NS_ERROR_FAILURE;
   1.908 +            }
   1.909 +
   1.910 +            char *buf = (char *) malloc(len * sizeof(char));
   1.911 +            if (!buf) {
   1.912 +                return NS_ERROR_FAILURE;
   1.913 +            }
   1.914 +
   1.915 +            size_t rlen = fread(buf, 1, len, fileHandle);
   1.916 +            if (rlen != (uint64_t)len) {
   1.917 +                free(buf);
   1.918 +                NS_WARNING("Failed to read file");
   1.919 +                return NS_ERROR_FAILURE;
   1.920 +            }
   1.921 +
   1.922 +            if (!mReuseLoaderGlobal) {
   1.923 +                script = Compile(cx, obj, options, buf,
   1.924 +                                     fileSize32);
   1.925 +            } else {
   1.926 +                function = CompileFunction(cx, obj, options,
   1.927 +                                           nullptr, 0, nullptr,
   1.928 +                                           buf, fileSize32);
   1.929 +            }
   1.930 +
   1.931 +            free(buf);
   1.932 +
   1.933 +#endif /* HAVE_PR_MEMMAP */
   1.934 +        } else {
   1.935 +            nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   1.936 +            NS_ENSURE_SUCCESS(rv, rv);
   1.937 +
   1.938 +            nsCOMPtr<nsIChannel> scriptChannel;
   1.939 +            rv = ioService->NewChannelFromURI(aURI, getter_AddRefs(scriptChannel));
   1.940 +            NS_ENSURE_SUCCESS(rv, rv);
   1.941 +
   1.942 +            nsCOMPtr<nsIInputStream> scriptStream;
   1.943 +            rv = scriptChannel->Open(getter_AddRefs(scriptStream));
   1.944 +            NS_ENSURE_SUCCESS(rv, rv);
   1.945 +
   1.946 +            uint64_t len64;
   1.947 +            uint32_t bytesRead;
   1.948 +
   1.949 +            rv = scriptStream->Available(&len64);
   1.950 +            NS_ENSURE_SUCCESS(rv, rv);
   1.951 +            NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
   1.952 +            if (!len64)
   1.953 +                return NS_ERROR_FAILURE;
   1.954 +            uint32_t len = (uint32_t)len64;
   1.955 +
   1.956 +            /* malloc an internal buf the size of the file */
   1.957 +            nsAutoArrayPtr<char> buf(new char[len + 1]);
   1.958 +            if (!buf)
   1.959 +                return NS_ERROR_OUT_OF_MEMORY;
   1.960 +
   1.961 +            /* read the file in one swoop */
   1.962 +            rv = scriptStream->Read(buf, len, &bytesRead);
   1.963 +            if (bytesRead != len)
   1.964 +                return NS_BASE_STREAM_OSERROR;
   1.965 +
   1.966 +            buf[len] = '\0';
   1.967 +
   1.968 +            if (!mReuseLoaderGlobal) {
   1.969 +                script = Compile(cx, obj, options, buf, bytesRead);
   1.970 +            } else {
   1.971 +                function = CompileFunction(cx, obj, options,
   1.972 +                                               nullptr, 0, nullptr,
   1.973 +                                               buf, bytesRead);
   1.974 +            }
   1.975 +        }
   1.976 +        // Propagate the exception, if one exists. Also, don't leave the stale
   1.977 +        // exception on this context.
   1.978 +        if (!script && !function && aPropagateExceptions) {
   1.979 +            JS_GetPendingException(cx, aException);
   1.980 +            JS_ClearPendingException(cx);
   1.981 +        }
   1.982 +    }
   1.983 +
   1.984 +    if (!script && !function) {
   1.985 +        return NS_ERROR_FAILURE;
   1.986 +    }
   1.987 +
   1.988 +    if (writeToCache) {
   1.989 +        // We successfully compiled the script, so cache it.
   1.990 +        if (script) {
   1.991 +            rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal,
   1.992 +                                   script);
   1.993 +        } else {
   1.994 +            rv = WriteCachedFunction(cache, cachePath, cx, mSystemPrincipal,
   1.995 +                                     function);
   1.996 +        }
   1.997 +
   1.998 +        // Don't treat failure to write as fatal, since we might be working
   1.999 +        // with a read-only cache.
  1.1000 +        if (NS_SUCCEEDED(rv)) {
  1.1001 +            LOG(("Successfully wrote to cache\n"));
  1.1002 +        } else {
  1.1003 +            LOG(("Failed to write to cache\n"));
  1.1004 +        }
  1.1005 +    }
  1.1006 +
  1.1007 +    // Assign aObject here so that it's available to recursive imports.
  1.1008 +    // See bug 384168.
  1.1009 +    aObject.set(obj);
  1.1010 +
  1.1011 +    RootedScript tableScript(cx, script);
  1.1012 +    if (!tableScript) {
  1.1013 +        tableScript = JS_GetFunctionScript(cx, function);
  1.1014 +        MOZ_ASSERT(tableScript);
  1.1015 +    }
  1.1016 +
  1.1017 +    aTableScript.set(tableScript);
  1.1018 +
  1.1019 +    if (js::GetObjectJSClass(obj) == &kFakeBackstagePassJSClass) {
  1.1020 +        MOZ_ASSERT(mReuseLoaderGlobal);
  1.1021 +        // tableScript stays in the table until shutdown.  It is rooted by
  1.1022 +        // virtue of the fact that aTableScript is a handle to
  1.1023 +        // ModuleEntry::thisObjectKey, which is a PersistentRootedScript.  Since
  1.1024 +        // ModuleEntries are never dynamically unloaded when mReuseLoaderGlobal
  1.1025 +        // is true, this prevents it from being collected and another script
  1.1026 +        // getting the same address.
  1.1027 +        mThisObjects.Put(tableScript, obj);
  1.1028 +    }
  1.1029 +    bool ok = false;
  1.1030 +
  1.1031 +    {
  1.1032 +        AutoSaveContextOptions asco(cx);
  1.1033 +        if (aPropagateExceptions)
  1.1034 +            ContextOptionsRef(cx).setDontReportUncaught(true);
  1.1035 +        if (script) {
  1.1036 +            ok = JS_ExecuteScriptVersion(cx, obj, script, JSVERSION_LATEST);
  1.1037 +        } else {
  1.1038 +            RootedValue rval(cx);
  1.1039 +            ok = JS_CallFunction(cx, obj, function, JS::HandleValueArray::empty(), &rval);
  1.1040 +        }
  1.1041 +     }
  1.1042 +
  1.1043 +    if (!ok) {
  1.1044 +        if (aPropagateExceptions) {
  1.1045 +            JS_GetPendingException(cx, aException);
  1.1046 +            JS_ClearPendingException(cx);
  1.1047 +        }
  1.1048 +        aObject.set(nullptr);
  1.1049 +        aTableScript.set(nullptr);
  1.1050 +        mThisObjects.Remove(tableScript);
  1.1051 +        return NS_ERROR_FAILURE;
  1.1052 +    }
  1.1053 +
  1.1054 +    /* Freed when we remove from the table. */
  1.1055 +    *aLocation = ToNewCString(nativePath);
  1.1056 +    if (!*aLocation) {
  1.1057 +        aObject.set(nullptr);
  1.1058 +        aTableScript.set(nullptr);
  1.1059 +        mThisObjects.Remove(tableScript);
  1.1060 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1061 +    }
  1.1062 +
  1.1063 +    return NS_OK;
  1.1064 +}
  1.1065 +
  1.1066 +/* static */ PLDHashOperator
  1.1067 +mozJSComponentLoader::ClearModules(const nsACString& key, ModuleEntry*& entry, void* cx)
  1.1068 +{
  1.1069 +    entry->Clear();
  1.1070 +    return PL_DHASH_REMOVE;
  1.1071 +}
  1.1072 +
  1.1073 +void
  1.1074 +mozJSComponentLoader::UnloadModules()
  1.1075 +{
  1.1076 +    mInitialized = false;
  1.1077 +
  1.1078 +    if (mLoaderGlobal) {
  1.1079 +        MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?");
  1.1080 +
  1.1081 +        JSAutoRequest ar(mContext);
  1.1082 +        RootedObject global(mContext, mLoaderGlobal->GetJSObject());
  1.1083 +        if (global) {
  1.1084 +            JSAutoCompartment ac(mContext, global);
  1.1085 +            JS_SetAllNonReservedSlotsToUndefined(mContext, global);
  1.1086 +        } else {
  1.1087 +            NS_WARNING("Going to leak!");
  1.1088 +        }
  1.1089 +
  1.1090 +        mLoaderGlobal = nullptr;
  1.1091 +    }
  1.1092 +
  1.1093 +    mInProgressImports.Clear();
  1.1094 +    mImports.Clear();
  1.1095 +    mThisObjects.Clear();
  1.1096 +
  1.1097 +    mModules.Enumerate(ClearModules, nullptr);
  1.1098 +
  1.1099 +    JS_DestroyContextNoGC(mContext);
  1.1100 +    mContext = nullptr;
  1.1101 +
  1.1102 +    mRuntimeService = nullptr;
  1.1103 +}
  1.1104 +
  1.1105 +NS_IMETHODIMP
  1.1106 +mozJSComponentLoader::Import(const nsACString& registryLocation,
  1.1107 +                             HandleValue targetValArg,
  1.1108 +                             JSContext *cx,
  1.1109 +                             uint8_t optionalArgc,
  1.1110 +                             MutableHandleValue retval)
  1.1111 +{
  1.1112 +    MOZ_ASSERT(nsContentUtils::IsCallerChrome());
  1.1113 +
  1.1114 +    RootedValue targetVal(cx, targetValArg);
  1.1115 +    RootedObject targetObject(cx, nullptr);
  1.1116 +    if (optionalArgc) {
  1.1117 +        // The caller passed in the optional second argument. Get it.
  1.1118 +        if (targetVal.isObject()) {
  1.1119 +            // If we're passing in something like a content DOM window, chances
  1.1120 +            // are the caller expects the properties to end up on the object
  1.1121 +            // proper and not on the Xray holder. This is dubious, but can be used
  1.1122 +            // during testing. Given that dumb callers can already leak JSMs into
  1.1123 +            // content by passing a raw content JS object (where Xrays aren't
  1.1124 +            // possible), we aim for consistency here. Waive xray.
  1.1125 +            if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
  1.1126 +                !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
  1.1127 +            {
  1.1128 +                return NS_ERROR_FAILURE;
  1.1129 +            }
  1.1130 +            targetObject = &targetVal.toObject();
  1.1131 +        } else if (!targetVal.isNull()) {
  1.1132 +            // If targetVal isNull(), we actually want to leave targetObject null.
  1.1133 +            // Not doing so breaks |make package|.
  1.1134 +            return ReportOnCaller(cx, ERROR_SCOPE_OBJ,
  1.1135 +                                  PromiseFlatCString(registryLocation).get());
  1.1136 +        }
  1.1137 +    } else {
  1.1138 +        nsresult rv = FindTargetObject(cx, &targetObject);
  1.1139 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1140 +    }
  1.1141 +
  1.1142 +    Maybe<JSAutoCompartment> ac;
  1.1143 +    if (targetObject) {
  1.1144 +        ac.construct(cx, targetObject);
  1.1145 +    }
  1.1146 +
  1.1147 +    RootedObject global(cx);
  1.1148 +    nsresult rv = ImportInto(registryLocation, targetObject, cx, &global);
  1.1149 +
  1.1150 +    if (global) {
  1.1151 +        if (!JS_WrapObject(cx, &global)) {
  1.1152 +            NS_ERROR("can't wrap return value");
  1.1153 +            return NS_ERROR_FAILURE;
  1.1154 +        }
  1.1155 +
  1.1156 +        retval.setObject(*global);
  1.1157 +    }
  1.1158 +    return rv;
  1.1159 +}
  1.1160 +
  1.1161 +/* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation,
  1.1162 +                                     in JSObjectPtr targetObj); */
  1.1163 +NS_IMETHODIMP
  1.1164 +mozJSComponentLoader::ImportInto(const nsACString &aLocation,
  1.1165 +                                 JSObject *aTargetObj,
  1.1166 +                                 nsAXPCNativeCallContext *cc,
  1.1167 +                                 JSObject **_retval)
  1.1168 +{
  1.1169 +    JSContext *callercx;
  1.1170 +    nsresult rv = cc->GetJSContext(&callercx);
  1.1171 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1172 +
  1.1173 +    RootedObject targetObject(callercx, aTargetObj);
  1.1174 +    RootedObject global(callercx);
  1.1175 +    rv = ImportInto(aLocation, targetObject, callercx, &global);
  1.1176 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1177 +    *_retval = global;
  1.1178 +    return NS_OK;
  1.1179 +}
  1.1180 +
  1.1181 +nsresult
  1.1182 +mozJSComponentLoader::ImportInto(const nsACString &aLocation,
  1.1183 +                                 HandleObject targetObj,
  1.1184 +                                 JSContext *callercx,
  1.1185 +                                 MutableHandleObject vp)
  1.1186 +{
  1.1187 +    vp.set(nullptr);
  1.1188 +
  1.1189 +    nsresult rv;
  1.1190 +    if (!mInitialized) {
  1.1191 +        rv = ReallyInit();
  1.1192 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1193 +    }
  1.1194 +
  1.1195 +    nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
  1.1196 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1197 +
  1.1198 +    // Get the URI.
  1.1199 +    nsCOMPtr<nsIURI> resURI;
  1.1200 +    rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI));
  1.1201 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1202 +
  1.1203 +    // figure out the resolved URI
  1.1204 +    nsCOMPtr<nsIChannel> scriptChannel;
  1.1205 +    rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
  1.1206 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
  1.1207 +
  1.1208 +    nsCOMPtr<nsIURI> resolvedURI;
  1.1209 +    rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
  1.1210 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1211 +
  1.1212 +    // get the JAR if there is one
  1.1213 +    nsCOMPtr<nsIJARURI> jarURI;
  1.1214 +    jarURI = do_QueryInterface(resolvedURI, &rv);
  1.1215 +    nsCOMPtr<nsIFileURL> baseFileURL;
  1.1216 +    if (NS_SUCCEEDED(rv)) {
  1.1217 +        nsCOMPtr<nsIURI> baseURI;
  1.1218 +        while (jarURI) {
  1.1219 +            jarURI->GetJARFile(getter_AddRefs(baseURI));
  1.1220 +            jarURI = do_QueryInterface(baseURI, &rv);
  1.1221 +        }
  1.1222 +        baseFileURL = do_QueryInterface(baseURI, &rv);
  1.1223 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1224 +    } else {
  1.1225 +        baseFileURL = do_QueryInterface(resolvedURI, &rv);
  1.1226 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1227 +    }
  1.1228 +
  1.1229 +    nsCOMPtr<nsIFile> sourceFile;
  1.1230 +    rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
  1.1231 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1232 +
  1.1233 +    nsCOMPtr<nsIFile> sourceLocalFile;
  1.1234 +    sourceLocalFile = do_QueryInterface(sourceFile, &rv);
  1.1235 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1236 +
  1.1237 +    nsAutoCString key;
  1.1238 +    rv = resolvedURI->GetSpec(key);
  1.1239 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1240 +
  1.1241 +    ModuleEntry* mod;
  1.1242 +    nsAutoPtr<ModuleEntry> newEntry;
  1.1243 +    if (!mImports.Get(key, &mod) && !mInProgressImports.Get(key, &mod)) {
  1.1244 +        newEntry = new ModuleEntry(callercx);
  1.1245 +        if (!newEntry)
  1.1246 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1247 +        mInProgressImports.Put(key, newEntry);
  1.1248 +
  1.1249 +        RootedValue exception(callercx);
  1.1250 +        rv = ObjectForLocation(sourceLocalFile, resURI, &newEntry->obj,
  1.1251 +                               &newEntry->thisObjectKey,
  1.1252 +                               &newEntry->location, true, &exception);
  1.1253 +
  1.1254 +        mInProgressImports.Remove(key);
  1.1255 +
  1.1256 +        if (NS_FAILED(rv)) {
  1.1257 +            if (!exception.isUndefined()) {
  1.1258 +                // An exception was thrown during compilation. Propagate it
  1.1259 +                // out to our caller so they can report it.
  1.1260 +                if (!JS_WrapValue(callercx, &exception))
  1.1261 +                    return NS_ERROR_OUT_OF_MEMORY;
  1.1262 +                JS_SetPendingException(callercx, exception);
  1.1263 +                return NS_OK;
  1.1264 +            }
  1.1265 +
  1.1266 +            // Something failed, but we don't know what it is, guess.
  1.1267 +            return NS_ERROR_FILE_NOT_FOUND;
  1.1268 +        }
  1.1269 +
  1.1270 +        // Set the location information for the new global, so that tools like
  1.1271 +        // about:memory may use that information
  1.1272 +        if (!mReuseLoaderGlobal) {
  1.1273 +            xpc::SetLocationForGlobal(newEntry->obj, aLocation);
  1.1274 +        }
  1.1275 +
  1.1276 +        mod = newEntry;
  1.1277 +    }
  1.1278 +
  1.1279 +    MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
  1.1280 +    vp.set(mod->obj);
  1.1281 +
  1.1282 +    if (targetObj) {
  1.1283 +        JSCLContextHelper cxhelper(mContext);
  1.1284 +        JSAutoCompartment ac(mContext, mod->obj);
  1.1285 +
  1.1286 +        RootedValue symbols(mContext);
  1.1287 +        RootedObject modObj(mContext, mod->obj);
  1.1288 +        if (!JS_GetProperty(mContext, modObj,
  1.1289 +                            "EXPORTED_SYMBOLS", &symbols)) {
  1.1290 +            return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT,
  1.1291 +                                  PromiseFlatCString(aLocation).get());
  1.1292 +        }
  1.1293 +
  1.1294 +        if (!JS_IsArrayObject(mContext, symbols)) {
  1.1295 +            return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY,
  1.1296 +                                  PromiseFlatCString(aLocation).get());
  1.1297 +        }
  1.1298 +
  1.1299 +        RootedObject symbolsObj(mContext, &symbols.toObject());
  1.1300 +
  1.1301 +        // Iterate over symbols array, installing symbols on targetObj:
  1.1302 +
  1.1303 +        uint32_t symbolCount = 0;
  1.1304 +        if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) {
  1.1305 +            return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
  1.1306 +                                  PromiseFlatCString(aLocation).get());
  1.1307 +        }
  1.1308 +
  1.1309 +#ifdef DEBUG
  1.1310 +        nsAutoCString logBuffer;
  1.1311 +#endif
  1.1312 +
  1.1313 +        RootedValue value(mContext);
  1.1314 +        RootedId symbolId(mContext);
  1.1315 +        for (uint32_t i = 0; i < symbolCount; ++i) {
  1.1316 +            if (!JS_GetElement(mContext, symbolsObj, i, &value) ||
  1.1317 +                !value.isString() ||
  1.1318 +                !JS_ValueToId(mContext, value, &symbolId)) {
  1.1319 +                return ReportOnCaller(cxhelper, ERROR_ARRAY_ELEMENT,
  1.1320 +                                      PromiseFlatCString(aLocation).get(), i);
  1.1321 +            }
  1.1322 +
  1.1323 +            RootedObject modObj(mContext, mod->obj);
  1.1324 +            if (!JS_GetPropertyById(mContext, modObj, symbolId, &value)) {
  1.1325 +                JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
  1.1326 +                if (!bytes)
  1.1327 +                    return NS_ERROR_FAILURE;
  1.1328 +                return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL,
  1.1329 +                                      PromiseFlatCString(aLocation).get(),
  1.1330 +                                      bytes.ptr());
  1.1331 +            }
  1.1332 +
  1.1333 +            JSAutoCompartment target_ac(mContext, targetObj);
  1.1334 +
  1.1335 +            if (!JS_WrapValue(mContext, &value) ||
  1.1336 +                !JS_SetPropertyById(mContext, targetObj, symbolId, value)) {
  1.1337 +                JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
  1.1338 +                if (!bytes)
  1.1339 +                    return NS_ERROR_FAILURE;
  1.1340 +                return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL,
  1.1341 +                                      PromiseFlatCString(aLocation).get(),
  1.1342 +                                      bytes.ptr());
  1.1343 +            }
  1.1344 +#ifdef DEBUG
  1.1345 +            if (i == 0) {
  1.1346 +                logBuffer.AssignLiteral("Installing symbols [ ");
  1.1347 +            }
  1.1348 +            JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
  1.1349 +            if (!!bytes)
  1.1350 +                logBuffer.Append(bytes.ptr());
  1.1351 +            logBuffer.AppendLiteral(" ");
  1.1352 +            if (i == symbolCount - 1) {
  1.1353 +                LOG(("%s] from %s\n", logBuffer.get(),
  1.1354 +                     PromiseFlatCString(aLocation).get()));
  1.1355 +            }
  1.1356 +#endif
  1.1357 +        }
  1.1358 +    }
  1.1359 +
  1.1360 +    // Cache this module for later
  1.1361 +    if (newEntry) {
  1.1362 +        mImports.Put(key, newEntry);
  1.1363 +        newEntry.forget();
  1.1364 +    }
  1.1365 +
  1.1366 +    return NS_OK;
  1.1367 +}
  1.1368 +
  1.1369 +NS_IMETHODIMP
  1.1370 +mozJSComponentLoader::Unload(const nsACString & aLocation)
  1.1371 +{
  1.1372 +    nsresult rv;
  1.1373 +
  1.1374 +    if (!mInitialized) {
  1.1375 +        return NS_OK;
  1.1376 +    }
  1.1377 +
  1.1378 +    nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
  1.1379 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1380 +
  1.1381 +    // Get the URI.
  1.1382 +    nsCOMPtr<nsIURI> resURI;
  1.1383 +    rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI));
  1.1384 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1385 +
  1.1386 +    // figure out the resolved URI
  1.1387 +    nsCOMPtr<nsIChannel> scriptChannel;
  1.1388 +    rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
  1.1389 +    NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
  1.1390 +
  1.1391 +    nsCOMPtr<nsIURI> resolvedURI;
  1.1392 +    rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
  1.1393 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1394 +
  1.1395 +    nsAutoCString key;
  1.1396 +    rv = resolvedURI->GetSpec(key);
  1.1397 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1398 +
  1.1399 +    ModuleEntry* mod;
  1.1400 +    if (mImports.Get(key, &mod)) {
  1.1401 +        mImports.Remove(key);
  1.1402 +    }
  1.1403 +
  1.1404 +    return NS_OK;
  1.1405 +}
  1.1406 +
  1.1407 +NS_IMETHODIMP
  1.1408 +mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
  1.1409 +                              const char16_t *data)
  1.1410 +{
  1.1411 +    if (!strcmp(topic, "xpcom-shutdown-loaders")) {
  1.1412 +        UnloadModules();
  1.1413 +    } else {
  1.1414 +        NS_ERROR("Unexpected observer topic.");
  1.1415 +    }
  1.1416 +
  1.1417 +    return NS_OK;
  1.1418 +}
  1.1419 +
  1.1420 +size_t
  1.1421 +mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  1.1422 +{
  1.1423 +    size_t n = aMallocSizeOf(this);
  1.1424 +    n += aMallocSizeOf(location);
  1.1425 +
  1.1426 +    return n;
  1.1427 +}
  1.1428 +
  1.1429 +/* static */ already_AddRefed<nsIFactory>
  1.1430 +mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
  1.1431 +                                              const mozilla::Module::CIDEntry& entry)
  1.1432 +{
  1.1433 +    const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
  1.1434 +    MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?");
  1.1435 +
  1.1436 +    nsCOMPtr<nsIFactory> f;
  1.1437 +    nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
  1.1438 +    if (NS_FAILED(rv))
  1.1439 +        return nullptr;
  1.1440 +
  1.1441 +    return f.forget();
  1.1442 +}
  1.1443 +
  1.1444 +//----------------------------------------------------------------------
  1.1445 +
  1.1446 +JSCLContextHelper::JSCLContextHelper(JSContext* aCx)
  1.1447 +    : mContext(aCx)
  1.1448 +    , mBuf(nullptr)
  1.1449 +{
  1.1450 +    mPusher.Push(mContext);
  1.1451 +    JS_BeginRequest(mContext);
  1.1452 +}
  1.1453 +
  1.1454 +JSCLContextHelper::~JSCLContextHelper()
  1.1455 +{
  1.1456 +    JS_EndRequest(mContext);
  1.1457 +    mPusher.Pop();
  1.1458 +    JSContext *restoredCx = nsContentUtils::GetCurrentJSContext();
  1.1459 +    if (restoredCx && mBuf) {
  1.1460 +        JS_ReportError(restoredCx, mBuf);
  1.1461 +    }
  1.1462 +
  1.1463 +    if (mBuf) {
  1.1464 +        JS_smprintf_free(mBuf);
  1.1465 +    }
  1.1466 +}
  1.1467 +
  1.1468 +void
  1.1469 +JSCLContextHelper::reportErrorAfterPop(char *buf)
  1.1470 +{
  1.1471 +    MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop");
  1.1472 +    mBuf = buf;
  1.1473 +}

mercurial