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 +}