js/xpconnect/loader/mozJSComponentLoader.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "mozilla/Attributes.h"
michael@0 8
michael@0 9 #ifdef MOZ_LOGGING
michael@0 10 #define FORCE_PR_LOG
michael@0 11 #endif
michael@0 12
michael@0 13 #include <cstdarg>
michael@0 14
michael@0 15 #include "prlog.h"
michael@0 16 #ifdef ANDROID
michael@0 17 #include <android/log.h>
michael@0 18 #endif
michael@0 19 #ifdef XP_WIN
michael@0 20 #include <windows.h>
michael@0 21 #endif
michael@0 22
michael@0 23 #include "jsapi.h"
michael@0 24 #include "nsCOMPtr.h"
michael@0 25 #include "nsAutoPtr.h"
michael@0 26 #include "nsIComponentManager.h"
michael@0 27 #include "mozilla/Module.h"
michael@0 28 #include "nsIFile.h"
michael@0 29 #include "mozJSComponentLoader.h"
michael@0 30 #include "mozJSLoaderUtils.h"
michael@0 31 #include "nsIJSRuntimeService.h"
michael@0 32 #include "nsIXPConnect.h"
michael@0 33 #include "nsIObserverService.h"
michael@0 34 #include "nsIScriptSecurityManager.h"
michael@0 35 #include "nsIFileURL.h"
michael@0 36 #include "nsIJARURI.h"
michael@0 37 #include "nsNetUtil.h"
michael@0 38 #include "nsDOMBlobBuilder.h"
michael@0 39 #include "jsprf.h"
michael@0 40 #include "nsJSPrincipals.h"
michael@0 41 #include "xpcprivate.h"
michael@0 42 #include "xpcpublic.h"
michael@0 43 #include "nsContentUtils.h"
michael@0 44 #include "nsCxPusher.h"
michael@0 45 #include "WrapperFactory.h"
michael@0 46
michael@0 47 #include "mozilla/scache/StartupCache.h"
michael@0 48 #include "mozilla/scache/StartupCacheUtils.h"
michael@0 49 #include "mozilla/Preferences.h"
michael@0 50
michael@0 51 #include "js/OldDebugAPI.h"
michael@0 52
michael@0 53 using namespace mozilla;
michael@0 54 using namespace mozilla::scache;
michael@0 55 using namespace xpc;
michael@0 56 using namespace JS;
michael@0 57
michael@0 58 // This JSClass exists to trick silly code that expects toString()ing the
michael@0 59 // global in a component scope to return something with "BackstagePass" in it
michael@0 60 // to continue working.
michael@0 61 static const JSClass kFakeBackstagePassJSClass =
michael@0 62 {
michael@0 63 "FakeBackstagePass",
michael@0 64 0,
michael@0 65 JS_PropertyStub,
michael@0 66 JS_DeletePropertyStub,
michael@0 67 JS_PropertyStub,
michael@0 68 JS_StrictPropertyStub,
michael@0 69 JS_EnumerateStub,
michael@0 70 JS_ResolveStub,
michael@0 71 JS_ConvertStub
michael@0 72 };
michael@0 73
michael@0 74 static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1";
michael@0 75 static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
michael@0 76 static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
michael@0 77 static const char kJSCachePrefix[] = "jsloader";
michael@0 78
michael@0 79 #define HAVE_PR_MEMMAP
michael@0 80
michael@0 81 /**
michael@0 82 * Buffer sizes for serialization and deserialization of scripts.
michael@0 83 * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
michael@0 84 */
michael@0 85 #define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
michael@0 86 #define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
michael@0 87
michael@0 88 #ifdef PR_LOGGING
michael@0 89 // NSPR_LOG_MODULES=JSComponentLoader:5
michael@0 90 static PRLogModuleInfo *gJSCLLog;
michael@0 91 #endif
michael@0 92
michael@0 93 #define LOG(args) PR_LOG(gJSCLLog, PR_LOG_DEBUG, args)
michael@0 94
michael@0 95 // Components.utils.import error messages
michael@0 96 #define ERROR_SCOPE_OBJ "%s - Second argument must be an object."
michael@0 97 #define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present."
michael@0 98 #define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array."
michael@0 99 #define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS."
michael@0 100 #define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string."
michael@0 101 #define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'."
michael@0 102 #define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object."
michael@0 103
michael@0 104 static bool
michael@0 105 Dump(JSContext *cx, unsigned argc, Value *vp)
michael@0 106 {
michael@0 107 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 108
michael@0 109 if (args.length() == 0)
michael@0 110 return true;
michael@0 111
michael@0 112 JSString *str = JS::ToString(cx, args[0]);
michael@0 113 if (!str)
michael@0 114 return false;
michael@0 115
michael@0 116 size_t length;
michael@0 117 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
michael@0 118 if (!chars)
michael@0 119 return false;
michael@0 120
michael@0 121 NS_ConvertUTF16toUTF8 utf8str(reinterpret_cast<const char16_t*>(chars),
michael@0 122 length);
michael@0 123 #ifdef ANDROID
michael@0 124 __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
michael@0 125 #endif
michael@0 126 #ifdef XP_WIN
michael@0 127 if (IsDebuggerPresent()) {
michael@0 128 OutputDebugStringW(reinterpret_cast<const wchar_t*>(chars));
michael@0 129 }
michael@0 130 #endif
michael@0 131 fputs(utf8str.get(), stdout);
michael@0 132 fflush(stdout);
michael@0 133 return true;
michael@0 134 }
michael@0 135
michael@0 136 static bool
michael@0 137 Debug(JSContext *cx, unsigned argc, jsval *vp)
michael@0 138 {
michael@0 139 #ifdef DEBUG
michael@0 140 return Dump(cx, argc, vp);
michael@0 141 #else
michael@0 142 return true;
michael@0 143 #endif
michael@0 144 }
michael@0 145
michael@0 146 static bool
michael@0 147 File(JSContext *cx, unsigned argc, Value *vp)
michael@0 148 {
michael@0 149 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 150
michael@0 151 if (args.length() == 0) {
michael@0 152 XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
michael@0 153 return false;
michael@0 154 }
michael@0 155
michael@0 156 nsCOMPtr<nsISupports> native;
michael@0 157 nsresult rv = nsDOMMultipartFile::NewFile(getter_AddRefs(native));
michael@0 158 if (NS_FAILED(rv)) {
michael@0 159 XPCThrower::Throw(rv, cx);
michael@0 160 return false;
michael@0 161 }
michael@0 162
michael@0 163 nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
michael@0 164 MOZ_ASSERT(initializer);
michael@0 165
michael@0 166 rv = initializer->Initialize(nullptr, cx, nullptr, args);
michael@0 167 if (NS_FAILED(rv)) {
michael@0 168 XPCThrower::Throw(rv, cx);
michael@0 169 return false;
michael@0 170 }
michael@0 171
michael@0 172 nsXPConnect *xpc = nsXPConnect::XPConnect();
michael@0 173 JSObject *glob = CurrentGlobalOrNull(cx);
michael@0 174
michael@0 175 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
michael@0 176 rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
michael@0 177 &NS_GET_IID(nsISupports),
michael@0 178 true, args.rval());
michael@0 179 if (NS_FAILED(rv)) {
michael@0 180 XPCThrower::Throw(rv, cx);
michael@0 181 return false;
michael@0 182 }
michael@0 183 return true;
michael@0 184 }
michael@0 185
michael@0 186 static bool
michael@0 187 Blob(JSContext *cx, unsigned argc, Value *vp)
michael@0 188 {
michael@0 189 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 190
michael@0 191 nsCOMPtr<nsISupports> native;
michael@0 192 nsresult rv = nsDOMMultipartFile::NewBlob(getter_AddRefs(native));
michael@0 193 if (NS_FAILED(rv)) {
michael@0 194 XPCThrower::Throw(rv, cx);
michael@0 195 return false;
michael@0 196 }
michael@0 197
michael@0 198 nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
michael@0 199 MOZ_ASSERT(initializer);
michael@0 200
michael@0 201 rv = initializer->Initialize(nullptr, cx, nullptr, args);
michael@0 202 if (NS_FAILED(rv)) {
michael@0 203 XPCThrower::Throw(rv, cx);
michael@0 204 return false;
michael@0 205 }
michael@0 206
michael@0 207 nsXPConnect *xpc = nsXPConnect::XPConnect();
michael@0 208 JSObject *glob = CurrentGlobalOrNull(cx);
michael@0 209
michael@0 210 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
michael@0 211 rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
michael@0 212 &NS_GET_IID(nsISupports),
michael@0 213 true, args.rval());
michael@0 214 if (NS_FAILED(rv)) {
michael@0 215 XPCThrower::Throw(rv, cx);
michael@0 216 return false;
michael@0 217 }
michael@0 218 return true;
michael@0 219 }
michael@0 220
michael@0 221 static const JSFunctionSpec gGlobalFun[] = {
michael@0 222 JS_FS("dump", Dump, 1,0),
michael@0 223 JS_FS("debug", Debug, 1,0),
michael@0 224 JS_FS("atob", Atob, 1,0),
michael@0 225 JS_FS("btoa", Btoa, 1,0),
michael@0 226 JS_FS("File", File, 1,JSFUN_CONSTRUCTOR),
michael@0 227 JS_FS("Blob", Blob, 2,JSFUN_CONSTRUCTOR),
michael@0 228 JS_FS_END
michael@0 229 };
michael@0 230
michael@0 231 class MOZ_STACK_CLASS JSCLContextHelper
michael@0 232 {
michael@0 233 public:
michael@0 234 JSCLContextHelper(JSContext* aCx);
michael@0 235 ~JSCLContextHelper();
michael@0 236
michael@0 237 void reportErrorAfterPop(char *buf);
michael@0 238
michael@0 239 operator JSContext*() const {return mContext;}
michael@0 240
michael@0 241 private:
michael@0 242
michael@0 243 JSContext* mContext;
michael@0 244 nsCxPusher mPusher;
michael@0 245 char* mBuf;
michael@0 246
michael@0 247 // prevent copying and assignment
michael@0 248 JSCLContextHelper(const JSCLContextHelper &) MOZ_DELETE;
michael@0 249 const JSCLContextHelper& operator=(const JSCLContextHelper &) MOZ_DELETE;
michael@0 250 };
michael@0 251
michael@0 252
michael@0 253 class JSCLAutoErrorReporterSetter
michael@0 254 {
michael@0 255 public:
michael@0 256 JSCLAutoErrorReporterSetter(JSContext* cx, JSErrorReporter reporter)
michael@0 257 {mContext = cx; mOldReporter = JS_SetErrorReporter(cx, reporter);}
michael@0 258 ~JSCLAutoErrorReporterSetter()
michael@0 259 {JS_SetErrorReporter(mContext, mOldReporter);}
michael@0 260 private:
michael@0 261 JSContext* mContext;
michael@0 262 JSErrorReporter mOldReporter;
michael@0 263
michael@0 264 JSCLAutoErrorReporterSetter(const JSCLAutoErrorReporterSetter &) MOZ_DELETE;
michael@0 265 const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &) MOZ_DELETE;
michael@0 266 };
michael@0 267
michael@0 268 static nsresult
michael@0 269 ReportOnCaller(JSContext *callerContext,
michael@0 270 const char *format, ...) {
michael@0 271 if (!callerContext) {
michael@0 272 return NS_ERROR_FAILURE;
michael@0 273 }
michael@0 274
michael@0 275 va_list ap;
michael@0 276 va_start(ap, format);
michael@0 277
michael@0 278 char *buf = JS_vsmprintf(format, ap);
michael@0 279 if (!buf) {
michael@0 280 return NS_ERROR_OUT_OF_MEMORY;
michael@0 281 }
michael@0 282
michael@0 283 JS_ReportError(callerContext, buf);
michael@0 284 JS_smprintf_free(buf);
michael@0 285
michael@0 286 return NS_OK;
michael@0 287 }
michael@0 288
michael@0 289 static nsresult
michael@0 290 ReportOnCaller(JSCLContextHelper &helper,
michael@0 291 const char *format, ...)
michael@0 292 {
michael@0 293 va_list ap;
michael@0 294 va_start(ap, format);
michael@0 295
michael@0 296 char *buf = JS_vsmprintf(format, ap);
michael@0 297 if (!buf) {
michael@0 298 return NS_ERROR_OUT_OF_MEMORY;
michael@0 299 }
michael@0 300
michael@0 301 helper.reportErrorAfterPop(buf);
michael@0 302
michael@0 303 return NS_OK;
michael@0 304 }
michael@0 305
michael@0 306 mozJSComponentLoader::mozJSComponentLoader()
michael@0 307 : mRuntime(nullptr),
michael@0 308 mContext(nullptr),
michael@0 309 mModules(32),
michael@0 310 mImports(32),
michael@0 311 mInProgressImports(32),
michael@0 312 mThisObjects(32),
michael@0 313 mInitialized(false),
michael@0 314 mReuseLoaderGlobal(false)
michael@0 315 {
michael@0 316 MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton");
michael@0 317
michael@0 318 #ifdef PR_LOGGING
michael@0 319 if (!gJSCLLog) {
michael@0 320 gJSCLLog = PR_NewLogModule("JSComponentLoader");
michael@0 321 }
michael@0 322 #endif
michael@0 323
michael@0 324 sSelf = this;
michael@0 325 }
michael@0 326
michael@0 327 mozJSComponentLoader::~mozJSComponentLoader()
michael@0 328 {
michael@0 329 if (mInitialized) {
michael@0 330 NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader");
michael@0 331 UnloadModules();
michael@0 332 }
michael@0 333
michael@0 334 sSelf = nullptr;
michael@0 335 }
michael@0 336
michael@0 337 mozJSComponentLoader*
michael@0 338 mozJSComponentLoader::sSelf;
michael@0 339
michael@0 340 NS_IMPL_ISUPPORTS(mozJSComponentLoader,
michael@0 341 mozilla::ModuleLoader,
michael@0 342 xpcIJSModuleLoader,
michael@0 343 nsIObserver)
michael@0 344
michael@0 345 nsresult
michael@0 346 mozJSComponentLoader::ReallyInit()
michael@0 347 {
michael@0 348 nsresult rv;
michael@0 349
michael@0 350 mReuseLoaderGlobal = Preferences::GetBool("jsloader.reuseGlobal");
michael@0 351
michael@0 352 // XXXkhuey B2G child processes have some sort of preferences race that
michael@0 353 // results in getting the wrong value.
michael@0 354 #ifdef MOZ_B2G
michael@0 355 mReuseLoaderGlobal = true;
michael@0 356 #endif
michael@0 357
michael@0 358 /*
michael@0 359 * Get the JSRuntime from the runtime svc, if possible.
michael@0 360 * We keep a reference around, because it's a Bad Thing if the runtime
michael@0 361 * service gets shut down before we're done. Bad!
michael@0 362 */
michael@0 363
michael@0 364 mRuntimeService = do_GetService(kJSRuntimeServiceContractID, &rv);
michael@0 365 if (NS_FAILED(rv) ||
michael@0 366 NS_FAILED(rv = mRuntimeService->GetRuntime(&mRuntime)))
michael@0 367 return rv;
michael@0 368
michael@0 369 // Create our compilation context.
michael@0 370 mContext = JS_NewContext(mRuntime, 256);
michael@0 371 if (!mContext)
michael@0 372 return NS_ERROR_OUT_OF_MEMORY;
michael@0 373
michael@0 374 nsCOMPtr<nsIScriptSecurityManager> secman =
michael@0 375 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
michael@0 376 if (!secman)
michael@0 377 return NS_ERROR_FAILURE;
michael@0 378
michael@0 379 rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
michael@0 380 if (NS_FAILED(rv) || !mSystemPrincipal)
michael@0 381 return NS_ERROR_FAILURE;
michael@0 382
michael@0 383 nsCOMPtr<nsIObserverService> obsSvc =
michael@0 384 do_GetService(kObserverServiceContractID, &rv);
michael@0 385 NS_ENSURE_SUCCESS(rv, rv);
michael@0 386
michael@0 387 rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false);
michael@0 388 NS_ENSURE_SUCCESS(rv, rv);
michael@0 389
michael@0 390 mInitialized = true;
michael@0 391
michael@0 392 return NS_OK;
michael@0 393 }
michael@0 394
michael@0 395 const mozilla::Module*
michael@0 396 mozJSComponentLoader::LoadModule(FileLocation &aFile)
michael@0 397 {
michael@0 398 nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
michael@0 399
michael@0 400 nsCString spec;
michael@0 401 aFile.GetURIString(spec);
michael@0 402
michael@0 403 nsCOMPtr<nsIURI> uri;
michael@0 404 nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
michael@0 405 if (NS_FAILED(rv))
michael@0 406 return nullptr;
michael@0 407
michael@0 408 if (!mInitialized) {
michael@0 409 rv = ReallyInit();
michael@0 410 if (NS_FAILED(rv))
michael@0 411 return nullptr;
michael@0 412 }
michael@0 413
michael@0 414 ModuleEntry* mod;
michael@0 415 if (mModules.Get(spec, &mod))
michael@0 416 return mod;
michael@0 417
michael@0 418 nsAutoPtr<ModuleEntry> entry(new ModuleEntry(mContext));
michael@0 419
michael@0 420 JSAutoRequest ar(mContext);
michael@0 421 RootedValue dummy(mContext);
michael@0 422 rv = ObjectForLocation(file, uri, &entry->obj, &entry->thisObjectKey,
michael@0 423 &entry->location, false, &dummy);
michael@0 424 if (NS_FAILED(rv)) {
michael@0 425 return nullptr;
michael@0 426 }
michael@0 427
michael@0 428 nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
michael@0 429 &rv);
michael@0 430 if (NS_FAILED(rv))
michael@0 431 return nullptr;
michael@0 432
michael@0 433 nsCOMPtr<nsIComponentManager> cm;
michael@0 434 rv = NS_GetComponentManager(getter_AddRefs(cm));
michael@0 435 if (NS_FAILED(rv))
michael@0 436 return nullptr;
michael@0 437
michael@0 438 JSCLContextHelper cx(mContext);
michael@0 439 JSAutoCompartment ac(cx, entry->obj);
michael@0 440
michael@0 441 nsCOMPtr<nsIXPConnectJSObjectHolder> cm_holder;
michael@0 442 rv = xpc->WrapNative(cx, entry->obj, cm,
michael@0 443 NS_GET_IID(nsIComponentManager),
michael@0 444 getter_AddRefs(cm_holder));
michael@0 445
michael@0 446 if (NS_FAILED(rv)) {
michael@0 447 return nullptr;
michael@0 448 }
michael@0 449
michael@0 450 JSObject* cm_jsobj = cm_holder->GetJSObject();
michael@0 451 if (!cm_jsobj) {
michael@0 452 return nullptr;
michael@0 453 }
michael@0 454
michael@0 455 nsCOMPtr<nsIXPConnectJSObjectHolder> file_holder;
michael@0 456 RootedObject entryObj(cx, entry->obj);
michael@0 457 rv = xpc->WrapNative(cx, entryObj, file,
michael@0 458 NS_GET_IID(nsIFile),
michael@0 459 getter_AddRefs(file_holder));
michael@0 460
michael@0 461 if (NS_FAILED(rv)) {
michael@0 462 return nullptr;
michael@0 463 }
michael@0 464
michael@0 465 JSObject* file_jsobj = file_holder->GetJSObject();
michael@0 466 if (!file_jsobj) {
michael@0 467 return nullptr;
michael@0 468 }
michael@0 469
michael@0 470 JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter);
michael@0 471
michael@0 472 RootedValue NSGetFactory_val(cx);
michael@0 473 if (!JS_GetProperty(cx, entryObj, "NSGetFactory", &NSGetFactory_val) ||
michael@0 474 JSVAL_IS_VOID(NSGetFactory_val)) {
michael@0 475 return nullptr;
michael@0 476 }
michael@0 477
michael@0 478 if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
michael@0 479 nsAutoCString spec;
michael@0 480 uri->GetSpec(spec);
michael@0 481 JS_ReportError(cx, "%s has NSGetFactory property that is not a function",
michael@0 482 spec.get());
michael@0 483 return nullptr;
michael@0 484 }
michael@0 485
michael@0 486 RootedObject jsGetFactoryObj(cx);
michael@0 487 if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
michael@0 488 !jsGetFactoryObj) {
michael@0 489 /* XXX report error properly */
michael@0 490 return nullptr;
michael@0 491 }
michael@0 492
michael@0 493 rv = xpc->WrapJS(cx, jsGetFactoryObj,
michael@0 494 NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj));
michael@0 495 if (NS_FAILED(rv)) {
michael@0 496 /* XXX report error properly */
michael@0 497 #ifdef DEBUG
michael@0 498 fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
michael@0 499 #endif
michael@0 500 return nullptr;
michael@0 501 }
michael@0 502
michael@0 503 // Cache this module for later
michael@0 504 mModules.Put(spec, entry);
michael@0 505
michael@0 506 // Set the location information for the new global, so that tools like
michael@0 507 // about:memory may use that information
michael@0 508 if (!mReuseLoaderGlobal) {
michael@0 509 xpc::SetLocationForGlobal(entryObj, spec);
michael@0 510 }
michael@0 511
michael@0 512 // The hash owns the ModuleEntry now, forget about it
michael@0 513 return entry.forget();
michael@0 514 }
michael@0 515
michael@0 516 nsresult
michael@0 517 mozJSComponentLoader::FindTargetObject(JSContext* aCx,
michael@0 518 MutableHandleObject aTargetObject)
michael@0 519 {
michael@0 520 aTargetObject.set(nullptr);
michael@0 521
michael@0 522 RootedObject targetObject(aCx);
michael@0 523 if (mReuseLoaderGlobal) {
michael@0 524 JSScript* script =
michael@0 525 js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
michael@0 526 if (script) {
michael@0 527 targetObject = mThisObjects.Get(script);
michael@0 528 }
michael@0 529 }
michael@0 530
michael@0 531 // The above could fail, even if mReuseLoaderGlobal, if the scripted
michael@0 532 // caller is not a component/JSM (it could be a DOM scope, for
michael@0 533 // instance).
michael@0 534 if (!targetObject) {
michael@0 535 // Our targetObject is the caller's global object. Let's get it.
michael@0 536 nsresult rv;
michael@0 537 nsCOMPtr<nsIXPConnect> xpc =
michael@0 538 do_GetService(kXPConnectServiceContractID, &rv);
michael@0 539 NS_ENSURE_SUCCESS(rv, rv);
michael@0 540
michael@0 541 nsAXPCNativeCallContext *cc = nullptr;
michael@0 542 rv = xpc->GetCurrentNativeCallContext(&cc);
michael@0 543 NS_ENSURE_SUCCESS(rv, rv);
michael@0 544
michael@0 545 nsCOMPtr<nsIXPConnectWrappedNative> wn;
michael@0 546 rv = cc->GetCalleeWrapper(getter_AddRefs(wn));
michael@0 547 NS_ENSURE_SUCCESS(rv, rv);
michael@0 548
michael@0 549 targetObject = wn->GetJSObject();
michael@0 550 if (!targetObject) {
michael@0 551 NS_ERROR("null calling object");
michael@0 552 return NS_ERROR_FAILURE;
michael@0 553 }
michael@0 554
michael@0 555 targetObject = JS_GetGlobalForObject(aCx, targetObject);
michael@0 556 }
michael@0 557
michael@0 558 aTargetObject.set(targetObject);
michael@0 559 return NS_OK;
michael@0 560 }
michael@0 561
michael@0 562 void
michael@0 563 mozJSComponentLoader::NoteSubScript(HandleScript aScript, HandleObject aThisObject)
michael@0 564 {
michael@0 565 if (!mInitialized && NS_FAILED(ReallyInit())) {
michael@0 566 MOZ_CRASH();
michael@0 567 }
michael@0 568
michael@0 569 if (js::GetObjectJSClass(aThisObject) == &kFakeBackstagePassJSClass) {
michael@0 570 mThisObjects.Put(aScript, aThisObject);
michael@0 571 }
michael@0 572 }
michael@0 573
michael@0 574 /* static */ size_t
michael@0 575 mozJSComponentLoader::DataEntrySizeOfExcludingThis(const nsACString& aKey,
michael@0 576 ModuleEntry* const& aData,
michael@0 577 MallocSizeOf aMallocSizeOf, void*)
michael@0 578 {
michael@0 579 return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
michael@0 580 aData->SizeOfIncludingThis(aMallocSizeOf);
michael@0 581 }
michael@0 582
michael@0 583 /* static */ size_t
michael@0 584 mozJSComponentLoader::ClassEntrySizeOfExcludingThis(const nsACString& aKey,
michael@0 585 const nsAutoPtr<ModuleEntry>& aData,
michael@0 586 MallocSizeOf aMallocSizeOf, void*)
michael@0 587 {
michael@0 588 return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
michael@0 589 aData->SizeOfIncludingThis(aMallocSizeOf);
michael@0 590 }
michael@0 591
michael@0 592 size_t
michael@0 593 mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
michael@0 594 {
michael@0 595 size_t amount = aMallocSizeOf(this);
michael@0 596
michael@0 597 amount += mModules.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf);
michael@0 598 amount += mImports.SizeOfExcludingThis(ClassEntrySizeOfExcludingThis, aMallocSizeOf);
michael@0 599 amount += mInProgressImports.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf);
michael@0 600 amount += mThisObjects.SizeOfExcludingThis(nullptr, aMallocSizeOf);
michael@0 601
michael@0 602 return amount;
michael@0 603 }
michael@0 604
michael@0 605 // Some stack based classes for cleaning up on early return
michael@0 606 #ifdef HAVE_PR_MEMMAP
michael@0 607 class FileAutoCloser
michael@0 608 {
michael@0 609 public:
michael@0 610 explicit FileAutoCloser(PRFileDesc *file) : mFile(file) {}
michael@0 611 ~FileAutoCloser() { PR_Close(mFile); }
michael@0 612 private:
michael@0 613 PRFileDesc *mFile;
michael@0 614 };
michael@0 615
michael@0 616 class FileMapAutoCloser
michael@0 617 {
michael@0 618 public:
michael@0 619 explicit FileMapAutoCloser(PRFileMap *map) : mMap(map) {}
michael@0 620 ~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
michael@0 621 private:
michael@0 622 PRFileMap *mMap;
michael@0 623 };
michael@0 624 #else
michael@0 625 class ANSIFileAutoCloser
michael@0 626 {
michael@0 627 public:
michael@0 628 explicit ANSIFileAutoCloser(FILE *file) : mFile(file) {}
michael@0 629 ~ANSIFileAutoCloser() { fclose(mFile); }
michael@0 630 private:
michael@0 631 FILE *mFile;
michael@0 632 };
michael@0 633 #endif
michael@0 634
michael@0 635 JSObject*
michael@0 636 mozJSComponentLoader::PrepareObjectForLocation(JSCLContextHelper& aCx,
michael@0 637 nsIFile *aComponentFile,
michael@0 638 nsIURI *aURI,
michael@0 639 bool aReuseLoaderGlobal,
michael@0 640 bool *aRealFile)
michael@0 641 {
michael@0 642 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
michael@0 643 if (aReuseLoaderGlobal) {
michael@0 644 holder = mLoaderGlobal;
michael@0 645 }
michael@0 646
michael@0 647 nsresult rv = NS_OK;
michael@0 648 nsCOMPtr<nsIXPConnect> xpc =
michael@0 649 do_GetService(kXPConnectServiceContractID, &rv);
michael@0 650 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 651 bool createdNewGlobal = false;
michael@0 652
michael@0 653 if (!mLoaderGlobal) {
michael@0 654 nsRefPtr<BackstagePass> backstagePass;
michael@0 655 rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
michael@0 656 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 657
michael@0 658 CompartmentOptions options;
michael@0 659 options.setZone(SystemZone)
michael@0 660 .setVersion(JSVERSION_LATEST);
michael@0 661 // Defer firing OnNewGlobalObject until after the __URI__ property has
michael@0 662 // been defined so the JS debugger can tell what module the global is
michael@0 663 // for
michael@0 664 rv = xpc->InitClassesWithNewWrappedGlobal(aCx,
michael@0 665 static_cast<nsIGlobalObject *>(backstagePass),
michael@0 666 mSystemPrincipal,
michael@0 667 nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
michael@0 668 options,
michael@0 669 getter_AddRefs(holder));
michael@0 670 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 671 createdNewGlobal = true;
michael@0 672
michael@0 673 RootedObject global(aCx, holder->GetJSObject());
michael@0 674 NS_ENSURE_TRUE(global, nullptr);
michael@0 675
michael@0 676 backstagePass->SetGlobalObject(global);
michael@0 677
michael@0 678 JSAutoCompartment ac(aCx, global);
michael@0 679 if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
michael@0 680 !JS_DefineProfilingFunctions(aCx, global)) {
michael@0 681 return nullptr;
michael@0 682 }
michael@0 683
michael@0 684 if (aReuseLoaderGlobal) {
michael@0 685 mLoaderGlobal = holder;
michael@0 686 }
michael@0 687 }
michael@0 688
michael@0 689 RootedObject obj(aCx, holder->GetJSObject());
michael@0 690 NS_ENSURE_TRUE(obj, nullptr);
michael@0 691
michael@0 692 JSAutoCompartment ac(aCx, obj);
michael@0 693
michael@0 694 if (aReuseLoaderGlobal) {
michael@0 695 // If we're reusing the loader global, we don't actually use the
michael@0 696 // global, but rather we use a different object as the 'this' object.
michael@0 697 obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass, NullPtr(), NullPtr());
michael@0 698 NS_ENSURE_TRUE(obj, nullptr);
michael@0 699 }
michael@0 700
michael@0 701 *aRealFile = false;
michael@0 702
michael@0 703 // need to be extra careful checking for URIs pointing to files
michael@0 704 // EnsureFile may not always get called, especially on resource URIs
michael@0 705 // so we need to call GetFile to make sure this is a valid file
michael@0 706 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
michael@0 707 nsCOMPtr<nsIFile> testFile;
michael@0 708 if (NS_SUCCEEDED(rv)) {
michael@0 709 fileURL->GetFile(getter_AddRefs(testFile));
michael@0 710 }
michael@0 711
michael@0 712 if (testFile) {
michael@0 713 *aRealFile = true;
michael@0 714
michael@0 715 nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
michael@0 716 rv = xpc->WrapNative(aCx, obj, aComponentFile,
michael@0 717 NS_GET_IID(nsIFile),
michael@0 718 getter_AddRefs(locationHolder));
michael@0 719 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 720
michael@0 721 RootedObject locationObj(aCx, locationHolder->GetJSObject());
michael@0 722 NS_ENSURE_TRUE(locationObj, nullptr);
michael@0 723
michael@0 724 if (!JS_DefineProperty(aCx, obj, "__LOCATION__", locationObj, 0))
michael@0 725 return nullptr;
michael@0 726 }
michael@0 727
michael@0 728 nsAutoCString nativePath;
michael@0 729 rv = aURI->GetSpec(nativePath);
michael@0 730 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 731
michael@0 732 // Expose the URI from which the script was imported through a special
michael@0 733 // variable that we insert into the JSM.
michael@0 734 RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length()));
michael@0 735 NS_ENSURE_TRUE(exposedUri, nullptr);
michael@0 736
michael@0 737 if (!JS_DefineProperty(aCx, obj, "__URI__", exposedUri, 0))
michael@0 738 return nullptr;
michael@0 739
michael@0 740 if (createdNewGlobal) {
michael@0 741 RootedObject global(aCx, holder->GetJSObject());
michael@0 742 JS_FireOnNewGlobalObject(aCx, global);
michael@0 743 }
michael@0 744
michael@0 745 return obj;
michael@0 746 }
michael@0 747
michael@0 748 nsresult
michael@0 749 mozJSComponentLoader::ObjectForLocation(nsIFile *aComponentFile,
michael@0 750 nsIURI *aURI,
michael@0 751 MutableHandleObject aObject,
michael@0 752 MutableHandleScript aTableScript,
michael@0 753 char **aLocation,
michael@0 754 bool aPropagateExceptions,
michael@0 755 MutableHandleValue aException)
michael@0 756 {
michael@0 757 JSCLContextHelper cx(mContext);
michael@0 758
michael@0 759 JS_AbortIfWrongThread(JS_GetRuntime(cx));
michael@0 760
michael@0 761 JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter);
michael@0 762
michael@0 763 bool realFile = false;
michael@0 764 RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aURI,
michael@0 765 mReuseLoaderGlobal, &realFile));
michael@0 766 NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
michael@0 767
michael@0 768 JSAutoCompartment ac(cx, obj);
michael@0 769
michael@0 770 RootedScript script(cx);
michael@0 771 RootedFunction function(cx);
michael@0 772
michael@0 773 nsAutoCString nativePath;
michael@0 774 nsresult rv = aURI->GetSpec(nativePath);
michael@0 775 NS_ENSURE_SUCCESS(rv, rv);
michael@0 776
michael@0 777 // Before compiling the script, first check to see if we have it in
michael@0 778 // the startupcache. Note: as a rule, startupcache errors are not fatal
michael@0 779 // to loading the script, since we can always slow-load.
michael@0 780
michael@0 781 bool writeToCache = false;
michael@0 782 StartupCache* cache = StartupCache::GetSingleton();
michael@0 783
michael@0 784 nsAutoCString cachePath(kJSCachePrefix);
michael@0 785 rv = PathifyURI(aURI, cachePath);
michael@0 786 NS_ENSURE_SUCCESS(rv, rv);
michael@0 787
michael@0 788 if (cache) {
michael@0 789 if (!mReuseLoaderGlobal) {
michael@0 790 rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
michael@0 791 } else {
michael@0 792 rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
michael@0 793 function.address());
michael@0 794 }
michael@0 795
michael@0 796 if (NS_SUCCEEDED(rv)) {
michael@0 797 LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
michael@0 798 } else {
michael@0 799 // This is ok, it just means the script is not yet in the
michael@0 800 // cache. Could mean that the cache was corrupted and got removed,
michael@0 801 // but either way we're going to write this out.
michael@0 802 writeToCache = true;
michael@0 803 }
michael@0 804 }
michael@0 805
michael@0 806 if (!script && !function) {
michael@0 807 // The script wasn't in the cache , so compile it now.
michael@0 808 LOG(("Slow loading %s\n", nativePath.get()));
michael@0 809
michael@0 810 // If aPropagateExceptions is true, then our caller wants us to propagate
michael@0 811 // any exceptions out to our caller. Ensure that the engine doesn't
michael@0 812 // eagerly report the exception.
michael@0 813 AutoSaveContextOptions asco(cx);
michael@0 814 if (aPropagateExceptions)
michael@0 815 ContextOptionsRef(cx).setDontReportUncaught(true);
michael@0 816
michael@0 817 // Note - if mReuseLoaderGlobal is true, then we can't do lazy source,
michael@0 818 // because we compile things as functions (rather than script), and lazy
michael@0 819 // source isn't supported in that configuration. That's ok though,
michael@0 820 // because we only do mReuseLoaderGlobal on b2g, where we invoke
michael@0 821 // setDiscardSource(true) on the entire global.
michael@0 822 CompileOptions options(cx);
michael@0 823 options.setNoScriptRval(mReuseLoaderGlobal ? false : true)
michael@0 824 .setVersion(JSVERSION_LATEST)
michael@0 825 .setFileAndLine(nativePath.get(), 1)
michael@0 826 .setSourceIsLazy(!mReuseLoaderGlobal);
michael@0 827
michael@0 828 if (realFile) {
michael@0 829 #ifdef HAVE_PR_MEMMAP
michael@0 830 int64_t fileSize;
michael@0 831 rv = aComponentFile->GetFileSize(&fileSize);
michael@0 832 if (NS_FAILED(rv)) {
michael@0 833 return rv;
michael@0 834 }
michael@0 835
michael@0 836 int64_t maxSize = UINT32_MAX;
michael@0 837 if (fileSize > maxSize) {
michael@0 838 NS_ERROR("file too large");
michael@0 839 return NS_ERROR_FAILURE;
michael@0 840 }
michael@0 841
michael@0 842 PRFileDesc *fileHandle;
michael@0 843 rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
michael@0 844 if (NS_FAILED(rv)) {
michael@0 845 return NS_ERROR_FILE_NOT_FOUND;
michael@0 846 }
michael@0 847
michael@0 848 // Make sure the file is closed, no matter how we return.
michael@0 849 FileAutoCloser fileCloser(fileHandle);
michael@0 850
michael@0 851 // We don't provide the file size here. If we did, PR_CreateFileMap
michael@0 852 // would simply stat() the file to verify that the size we provided
michael@0 853 // didn't require extending the file. We know that the file doesn't
michael@0 854 // need to be extended, so skip the extra work by not providing the
michael@0 855 // size.
michael@0 856 PRFileMap *map = PR_CreateFileMap(fileHandle, 0, PR_PROT_READONLY);
michael@0 857 if (!map) {
michael@0 858 NS_ERROR("Failed to create file map");
michael@0 859 return NS_ERROR_FAILURE;
michael@0 860 }
michael@0 861
michael@0 862 // Make sure the file map is closed, no matter how we return.
michael@0 863 FileMapAutoCloser mapCloser(map);
michael@0 864
michael@0 865 uint32_t fileSize32 = fileSize;
michael@0 866
michael@0 867 char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
michael@0 868 if (!buf) {
michael@0 869 NS_WARNING("Failed to map file");
michael@0 870 return NS_ERROR_FAILURE;
michael@0 871 }
michael@0 872
michael@0 873 if (!mReuseLoaderGlobal) {
michael@0 874 script = Compile(cx, obj, options, buf,
michael@0 875 fileSize32);
michael@0 876 } else {
michael@0 877 function = CompileFunction(cx, obj, options,
michael@0 878 nullptr, 0, nullptr,
michael@0 879 buf, fileSize32);
michael@0 880 }
michael@0 881
michael@0 882 PR_MemUnmap(buf, fileSize32);
michael@0 883
michael@0 884 #else /* HAVE_PR_MEMMAP */
michael@0 885
michael@0 886 /**
michael@0 887 * No memmap implementation, so fall back to
michael@0 888 * reading in the file
michael@0 889 */
michael@0 890
michael@0 891 FILE *fileHandle;
michael@0 892 rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle);
michael@0 893 if (NS_FAILED(rv)) {
michael@0 894 return NS_ERROR_FILE_NOT_FOUND;
michael@0 895 }
michael@0 896
michael@0 897 // Ensure file fclose
michael@0 898 ANSIFileAutoCloser fileCloser(fileHandle);
michael@0 899
michael@0 900 int64_t len;
michael@0 901 rv = aComponentFile->GetFileSize(&len);
michael@0 902 if (NS_FAILED(rv) || len < 0) {
michael@0 903 NS_WARNING("Failed to get file size");
michael@0 904 return NS_ERROR_FAILURE;
michael@0 905 }
michael@0 906
michael@0 907 char *buf = (char *) malloc(len * sizeof(char));
michael@0 908 if (!buf) {
michael@0 909 return NS_ERROR_FAILURE;
michael@0 910 }
michael@0 911
michael@0 912 size_t rlen = fread(buf, 1, len, fileHandle);
michael@0 913 if (rlen != (uint64_t)len) {
michael@0 914 free(buf);
michael@0 915 NS_WARNING("Failed to read file");
michael@0 916 return NS_ERROR_FAILURE;
michael@0 917 }
michael@0 918
michael@0 919 if (!mReuseLoaderGlobal) {
michael@0 920 script = Compile(cx, obj, options, buf,
michael@0 921 fileSize32);
michael@0 922 } else {
michael@0 923 function = CompileFunction(cx, obj, options,
michael@0 924 nullptr, 0, nullptr,
michael@0 925 buf, fileSize32);
michael@0 926 }
michael@0 927
michael@0 928 free(buf);
michael@0 929
michael@0 930 #endif /* HAVE_PR_MEMMAP */
michael@0 931 } else {
michael@0 932 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
michael@0 933 NS_ENSURE_SUCCESS(rv, rv);
michael@0 934
michael@0 935 nsCOMPtr<nsIChannel> scriptChannel;
michael@0 936 rv = ioService->NewChannelFromURI(aURI, getter_AddRefs(scriptChannel));
michael@0 937 NS_ENSURE_SUCCESS(rv, rv);
michael@0 938
michael@0 939 nsCOMPtr<nsIInputStream> scriptStream;
michael@0 940 rv = scriptChannel->Open(getter_AddRefs(scriptStream));
michael@0 941 NS_ENSURE_SUCCESS(rv, rv);
michael@0 942
michael@0 943 uint64_t len64;
michael@0 944 uint32_t bytesRead;
michael@0 945
michael@0 946 rv = scriptStream->Available(&len64);
michael@0 947 NS_ENSURE_SUCCESS(rv, rv);
michael@0 948 NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
michael@0 949 if (!len64)
michael@0 950 return NS_ERROR_FAILURE;
michael@0 951 uint32_t len = (uint32_t)len64;
michael@0 952
michael@0 953 /* malloc an internal buf the size of the file */
michael@0 954 nsAutoArrayPtr<char> buf(new char[len + 1]);
michael@0 955 if (!buf)
michael@0 956 return NS_ERROR_OUT_OF_MEMORY;
michael@0 957
michael@0 958 /* read the file in one swoop */
michael@0 959 rv = scriptStream->Read(buf, len, &bytesRead);
michael@0 960 if (bytesRead != len)
michael@0 961 return NS_BASE_STREAM_OSERROR;
michael@0 962
michael@0 963 buf[len] = '\0';
michael@0 964
michael@0 965 if (!mReuseLoaderGlobal) {
michael@0 966 script = Compile(cx, obj, options, buf, bytesRead);
michael@0 967 } else {
michael@0 968 function = CompileFunction(cx, obj, options,
michael@0 969 nullptr, 0, nullptr,
michael@0 970 buf, bytesRead);
michael@0 971 }
michael@0 972 }
michael@0 973 // Propagate the exception, if one exists. Also, don't leave the stale
michael@0 974 // exception on this context.
michael@0 975 if (!script && !function && aPropagateExceptions) {
michael@0 976 JS_GetPendingException(cx, aException);
michael@0 977 JS_ClearPendingException(cx);
michael@0 978 }
michael@0 979 }
michael@0 980
michael@0 981 if (!script && !function) {
michael@0 982 return NS_ERROR_FAILURE;
michael@0 983 }
michael@0 984
michael@0 985 if (writeToCache) {
michael@0 986 // We successfully compiled the script, so cache it.
michael@0 987 if (script) {
michael@0 988 rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal,
michael@0 989 script);
michael@0 990 } else {
michael@0 991 rv = WriteCachedFunction(cache, cachePath, cx, mSystemPrincipal,
michael@0 992 function);
michael@0 993 }
michael@0 994
michael@0 995 // Don't treat failure to write as fatal, since we might be working
michael@0 996 // with a read-only cache.
michael@0 997 if (NS_SUCCEEDED(rv)) {
michael@0 998 LOG(("Successfully wrote to cache\n"));
michael@0 999 } else {
michael@0 1000 LOG(("Failed to write to cache\n"));
michael@0 1001 }
michael@0 1002 }
michael@0 1003
michael@0 1004 // Assign aObject here so that it's available to recursive imports.
michael@0 1005 // See bug 384168.
michael@0 1006 aObject.set(obj);
michael@0 1007
michael@0 1008 RootedScript tableScript(cx, script);
michael@0 1009 if (!tableScript) {
michael@0 1010 tableScript = JS_GetFunctionScript(cx, function);
michael@0 1011 MOZ_ASSERT(tableScript);
michael@0 1012 }
michael@0 1013
michael@0 1014 aTableScript.set(tableScript);
michael@0 1015
michael@0 1016 if (js::GetObjectJSClass(obj) == &kFakeBackstagePassJSClass) {
michael@0 1017 MOZ_ASSERT(mReuseLoaderGlobal);
michael@0 1018 // tableScript stays in the table until shutdown. It is rooted by
michael@0 1019 // virtue of the fact that aTableScript is a handle to
michael@0 1020 // ModuleEntry::thisObjectKey, which is a PersistentRootedScript. Since
michael@0 1021 // ModuleEntries are never dynamically unloaded when mReuseLoaderGlobal
michael@0 1022 // is true, this prevents it from being collected and another script
michael@0 1023 // getting the same address.
michael@0 1024 mThisObjects.Put(tableScript, obj);
michael@0 1025 }
michael@0 1026 bool ok = false;
michael@0 1027
michael@0 1028 {
michael@0 1029 AutoSaveContextOptions asco(cx);
michael@0 1030 if (aPropagateExceptions)
michael@0 1031 ContextOptionsRef(cx).setDontReportUncaught(true);
michael@0 1032 if (script) {
michael@0 1033 ok = JS_ExecuteScriptVersion(cx, obj, script, JSVERSION_LATEST);
michael@0 1034 } else {
michael@0 1035 RootedValue rval(cx);
michael@0 1036 ok = JS_CallFunction(cx, obj, function, JS::HandleValueArray::empty(), &rval);
michael@0 1037 }
michael@0 1038 }
michael@0 1039
michael@0 1040 if (!ok) {
michael@0 1041 if (aPropagateExceptions) {
michael@0 1042 JS_GetPendingException(cx, aException);
michael@0 1043 JS_ClearPendingException(cx);
michael@0 1044 }
michael@0 1045 aObject.set(nullptr);
michael@0 1046 aTableScript.set(nullptr);
michael@0 1047 mThisObjects.Remove(tableScript);
michael@0 1048 return NS_ERROR_FAILURE;
michael@0 1049 }
michael@0 1050
michael@0 1051 /* Freed when we remove from the table. */
michael@0 1052 *aLocation = ToNewCString(nativePath);
michael@0 1053 if (!*aLocation) {
michael@0 1054 aObject.set(nullptr);
michael@0 1055 aTableScript.set(nullptr);
michael@0 1056 mThisObjects.Remove(tableScript);
michael@0 1057 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1058 }
michael@0 1059
michael@0 1060 return NS_OK;
michael@0 1061 }
michael@0 1062
michael@0 1063 /* static */ PLDHashOperator
michael@0 1064 mozJSComponentLoader::ClearModules(const nsACString& key, ModuleEntry*& entry, void* cx)
michael@0 1065 {
michael@0 1066 entry->Clear();
michael@0 1067 return PL_DHASH_REMOVE;
michael@0 1068 }
michael@0 1069
michael@0 1070 void
michael@0 1071 mozJSComponentLoader::UnloadModules()
michael@0 1072 {
michael@0 1073 mInitialized = false;
michael@0 1074
michael@0 1075 if (mLoaderGlobal) {
michael@0 1076 MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?");
michael@0 1077
michael@0 1078 JSAutoRequest ar(mContext);
michael@0 1079 RootedObject global(mContext, mLoaderGlobal->GetJSObject());
michael@0 1080 if (global) {
michael@0 1081 JSAutoCompartment ac(mContext, global);
michael@0 1082 JS_SetAllNonReservedSlotsToUndefined(mContext, global);
michael@0 1083 } else {
michael@0 1084 NS_WARNING("Going to leak!");
michael@0 1085 }
michael@0 1086
michael@0 1087 mLoaderGlobal = nullptr;
michael@0 1088 }
michael@0 1089
michael@0 1090 mInProgressImports.Clear();
michael@0 1091 mImports.Clear();
michael@0 1092 mThisObjects.Clear();
michael@0 1093
michael@0 1094 mModules.Enumerate(ClearModules, nullptr);
michael@0 1095
michael@0 1096 JS_DestroyContextNoGC(mContext);
michael@0 1097 mContext = nullptr;
michael@0 1098
michael@0 1099 mRuntimeService = nullptr;
michael@0 1100 }
michael@0 1101
michael@0 1102 NS_IMETHODIMP
michael@0 1103 mozJSComponentLoader::Import(const nsACString& registryLocation,
michael@0 1104 HandleValue targetValArg,
michael@0 1105 JSContext *cx,
michael@0 1106 uint8_t optionalArgc,
michael@0 1107 MutableHandleValue retval)
michael@0 1108 {
michael@0 1109 MOZ_ASSERT(nsContentUtils::IsCallerChrome());
michael@0 1110
michael@0 1111 RootedValue targetVal(cx, targetValArg);
michael@0 1112 RootedObject targetObject(cx, nullptr);
michael@0 1113 if (optionalArgc) {
michael@0 1114 // The caller passed in the optional second argument. Get it.
michael@0 1115 if (targetVal.isObject()) {
michael@0 1116 // If we're passing in something like a content DOM window, chances
michael@0 1117 // are the caller expects the properties to end up on the object
michael@0 1118 // proper and not on the Xray holder. This is dubious, but can be used
michael@0 1119 // during testing. Given that dumb callers can already leak JSMs into
michael@0 1120 // content by passing a raw content JS object (where Xrays aren't
michael@0 1121 // possible), we aim for consistency here. Waive xray.
michael@0 1122 if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
michael@0 1123 !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
michael@0 1124 {
michael@0 1125 return NS_ERROR_FAILURE;
michael@0 1126 }
michael@0 1127 targetObject = &targetVal.toObject();
michael@0 1128 } else if (!targetVal.isNull()) {
michael@0 1129 // If targetVal isNull(), we actually want to leave targetObject null.
michael@0 1130 // Not doing so breaks |make package|.
michael@0 1131 return ReportOnCaller(cx, ERROR_SCOPE_OBJ,
michael@0 1132 PromiseFlatCString(registryLocation).get());
michael@0 1133 }
michael@0 1134 } else {
michael@0 1135 nsresult rv = FindTargetObject(cx, &targetObject);
michael@0 1136 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1137 }
michael@0 1138
michael@0 1139 Maybe<JSAutoCompartment> ac;
michael@0 1140 if (targetObject) {
michael@0 1141 ac.construct(cx, targetObject);
michael@0 1142 }
michael@0 1143
michael@0 1144 RootedObject global(cx);
michael@0 1145 nsresult rv = ImportInto(registryLocation, targetObject, cx, &global);
michael@0 1146
michael@0 1147 if (global) {
michael@0 1148 if (!JS_WrapObject(cx, &global)) {
michael@0 1149 NS_ERROR("can't wrap return value");
michael@0 1150 return NS_ERROR_FAILURE;
michael@0 1151 }
michael@0 1152
michael@0 1153 retval.setObject(*global);
michael@0 1154 }
michael@0 1155 return rv;
michael@0 1156 }
michael@0 1157
michael@0 1158 /* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation,
michael@0 1159 in JSObjectPtr targetObj); */
michael@0 1160 NS_IMETHODIMP
michael@0 1161 mozJSComponentLoader::ImportInto(const nsACString &aLocation,
michael@0 1162 JSObject *aTargetObj,
michael@0 1163 nsAXPCNativeCallContext *cc,
michael@0 1164 JSObject **_retval)
michael@0 1165 {
michael@0 1166 JSContext *callercx;
michael@0 1167 nsresult rv = cc->GetJSContext(&callercx);
michael@0 1168 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1169
michael@0 1170 RootedObject targetObject(callercx, aTargetObj);
michael@0 1171 RootedObject global(callercx);
michael@0 1172 rv = ImportInto(aLocation, targetObject, callercx, &global);
michael@0 1173 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1174 *_retval = global;
michael@0 1175 return NS_OK;
michael@0 1176 }
michael@0 1177
michael@0 1178 nsresult
michael@0 1179 mozJSComponentLoader::ImportInto(const nsACString &aLocation,
michael@0 1180 HandleObject targetObj,
michael@0 1181 JSContext *callercx,
michael@0 1182 MutableHandleObject vp)
michael@0 1183 {
michael@0 1184 vp.set(nullptr);
michael@0 1185
michael@0 1186 nsresult rv;
michael@0 1187 if (!mInitialized) {
michael@0 1188 rv = ReallyInit();
michael@0 1189 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1190 }
michael@0 1191
michael@0 1192 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
michael@0 1193 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1194
michael@0 1195 // Get the URI.
michael@0 1196 nsCOMPtr<nsIURI> resURI;
michael@0 1197 rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI));
michael@0 1198 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1199
michael@0 1200 // figure out the resolved URI
michael@0 1201 nsCOMPtr<nsIChannel> scriptChannel;
michael@0 1202 rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
michael@0 1203 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
michael@0 1204
michael@0 1205 nsCOMPtr<nsIURI> resolvedURI;
michael@0 1206 rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
michael@0 1207 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1208
michael@0 1209 // get the JAR if there is one
michael@0 1210 nsCOMPtr<nsIJARURI> jarURI;
michael@0 1211 jarURI = do_QueryInterface(resolvedURI, &rv);
michael@0 1212 nsCOMPtr<nsIFileURL> baseFileURL;
michael@0 1213 if (NS_SUCCEEDED(rv)) {
michael@0 1214 nsCOMPtr<nsIURI> baseURI;
michael@0 1215 while (jarURI) {
michael@0 1216 jarURI->GetJARFile(getter_AddRefs(baseURI));
michael@0 1217 jarURI = do_QueryInterface(baseURI, &rv);
michael@0 1218 }
michael@0 1219 baseFileURL = do_QueryInterface(baseURI, &rv);
michael@0 1220 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1221 } else {
michael@0 1222 baseFileURL = do_QueryInterface(resolvedURI, &rv);
michael@0 1223 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1224 }
michael@0 1225
michael@0 1226 nsCOMPtr<nsIFile> sourceFile;
michael@0 1227 rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
michael@0 1228 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1229
michael@0 1230 nsCOMPtr<nsIFile> sourceLocalFile;
michael@0 1231 sourceLocalFile = do_QueryInterface(sourceFile, &rv);
michael@0 1232 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1233
michael@0 1234 nsAutoCString key;
michael@0 1235 rv = resolvedURI->GetSpec(key);
michael@0 1236 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1237
michael@0 1238 ModuleEntry* mod;
michael@0 1239 nsAutoPtr<ModuleEntry> newEntry;
michael@0 1240 if (!mImports.Get(key, &mod) && !mInProgressImports.Get(key, &mod)) {
michael@0 1241 newEntry = new ModuleEntry(callercx);
michael@0 1242 if (!newEntry)
michael@0 1243 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1244 mInProgressImports.Put(key, newEntry);
michael@0 1245
michael@0 1246 RootedValue exception(callercx);
michael@0 1247 rv = ObjectForLocation(sourceLocalFile, resURI, &newEntry->obj,
michael@0 1248 &newEntry->thisObjectKey,
michael@0 1249 &newEntry->location, true, &exception);
michael@0 1250
michael@0 1251 mInProgressImports.Remove(key);
michael@0 1252
michael@0 1253 if (NS_FAILED(rv)) {
michael@0 1254 if (!exception.isUndefined()) {
michael@0 1255 // An exception was thrown during compilation. Propagate it
michael@0 1256 // out to our caller so they can report it.
michael@0 1257 if (!JS_WrapValue(callercx, &exception))
michael@0 1258 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1259 JS_SetPendingException(callercx, exception);
michael@0 1260 return NS_OK;
michael@0 1261 }
michael@0 1262
michael@0 1263 // Something failed, but we don't know what it is, guess.
michael@0 1264 return NS_ERROR_FILE_NOT_FOUND;
michael@0 1265 }
michael@0 1266
michael@0 1267 // Set the location information for the new global, so that tools like
michael@0 1268 // about:memory may use that information
michael@0 1269 if (!mReuseLoaderGlobal) {
michael@0 1270 xpc::SetLocationForGlobal(newEntry->obj, aLocation);
michael@0 1271 }
michael@0 1272
michael@0 1273 mod = newEntry;
michael@0 1274 }
michael@0 1275
michael@0 1276 MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
michael@0 1277 vp.set(mod->obj);
michael@0 1278
michael@0 1279 if (targetObj) {
michael@0 1280 JSCLContextHelper cxhelper(mContext);
michael@0 1281 JSAutoCompartment ac(mContext, mod->obj);
michael@0 1282
michael@0 1283 RootedValue symbols(mContext);
michael@0 1284 RootedObject modObj(mContext, mod->obj);
michael@0 1285 if (!JS_GetProperty(mContext, modObj,
michael@0 1286 "EXPORTED_SYMBOLS", &symbols)) {
michael@0 1287 return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT,
michael@0 1288 PromiseFlatCString(aLocation).get());
michael@0 1289 }
michael@0 1290
michael@0 1291 if (!JS_IsArrayObject(mContext, symbols)) {
michael@0 1292 return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY,
michael@0 1293 PromiseFlatCString(aLocation).get());
michael@0 1294 }
michael@0 1295
michael@0 1296 RootedObject symbolsObj(mContext, &symbols.toObject());
michael@0 1297
michael@0 1298 // Iterate over symbols array, installing symbols on targetObj:
michael@0 1299
michael@0 1300 uint32_t symbolCount = 0;
michael@0 1301 if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) {
michael@0 1302 return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
michael@0 1303 PromiseFlatCString(aLocation).get());
michael@0 1304 }
michael@0 1305
michael@0 1306 #ifdef DEBUG
michael@0 1307 nsAutoCString logBuffer;
michael@0 1308 #endif
michael@0 1309
michael@0 1310 RootedValue value(mContext);
michael@0 1311 RootedId symbolId(mContext);
michael@0 1312 for (uint32_t i = 0; i < symbolCount; ++i) {
michael@0 1313 if (!JS_GetElement(mContext, symbolsObj, i, &value) ||
michael@0 1314 !value.isString() ||
michael@0 1315 !JS_ValueToId(mContext, value, &symbolId)) {
michael@0 1316 return ReportOnCaller(cxhelper, ERROR_ARRAY_ELEMENT,
michael@0 1317 PromiseFlatCString(aLocation).get(), i);
michael@0 1318 }
michael@0 1319
michael@0 1320 RootedObject modObj(mContext, mod->obj);
michael@0 1321 if (!JS_GetPropertyById(mContext, modObj, symbolId, &value)) {
michael@0 1322 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
michael@0 1323 if (!bytes)
michael@0 1324 return NS_ERROR_FAILURE;
michael@0 1325 return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL,
michael@0 1326 PromiseFlatCString(aLocation).get(),
michael@0 1327 bytes.ptr());
michael@0 1328 }
michael@0 1329
michael@0 1330 JSAutoCompartment target_ac(mContext, targetObj);
michael@0 1331
michael@0 1332 if (!JS_WrapValue(mContext, &value) ||
michael@0 1333 !JS_SetPropertyById(mContext, targetObj, symbolId, value)) {
michael@0 1334 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
michael@0 1335 if (!bytes)
michael@0 1336 return NS_ERROR_FAILURE;
michael@0 1337 return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL,
michael@0 1338 PromiseFlatCString(aLocation).get(),
michael@0 1339 bytes.ptr());
michael@0 1340 }
michael@0 1341 #ifdef DEBUG
michael@0 1342 if (i == 0) {
michael@0 1343 logBuffer.AssignLiteral("Installing symbols [ ");
michael@0 1344 }
michael@0 1345 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
michael@0 1346 if (!!bytes)
michael@0 1347 logBuffer.Append(bytes.ptr());
michael@0 1348 logBuffer.AppendLiteral(" ");
michael@0 1349 if (i == symbolCount - 1) {
michael@0 1350 LOG(("%s] from %s\n", logBuffer.get(),
michael@0 1351 PromiseFlatCString(aLocation).get()));
michael@0 1352 }
michael@0 1353 #endif
michael@0 1354 }
michael@0 1355 }
michael@0 1356
michael@0 1357 // Cache this module for later
michael@0 1358 if (newEntry) {
michael@0 1359 mImports.Put(key, newEntry);
michael@0 1360 newEntry.forget();
michael@0 1361 }
michael@0 1362
michael@0 1363 return NS_OK;
michael@0 1364 }
michael@0 1365
michael@0 1366 NS_IMETHODIMP
michael@0 1367 mozJSComponentLoader::Unload(const nsACString & aLocation)
michael@0 1368 {
michael@0 1369 nsresult rv;
michael@0 1370
michael@0 1371 if (!mInitialized) {
michael@0 1372 return NS_OK;
michael@0 1373 }
michael@0 1374
michael@0 1375 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
michael@0 1376 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1377
michael@0 1378 // Get the URI.
michael@0 1379 nsCOMPtr<nsIURI> resURI;
michael@0 1380 rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI));
michael@0 1381 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1382
michael@0 1383 // figure out the resolved URI
michael@0 1384 nsCOMPtr<nsIChannel> scriptChannel;
michael@0 1385 rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
michael@0 1386 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
michael@0 1387
michael@0 1388 nsCOMPtr<nsIURI> resolvedURI;
michael@0 1389 rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
michael@0 1390 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1391
michael@0 1392 nsAutoCString key;
michael@0 1393 rv = resolvedURI->GetSpec(key);
michael@0 1394 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1395
michael@0 1396 ModuleEntry* mod;
michael@0 1397 if (mImports.Get(key, &mod)) {
michael@0 1398 mImports.Remove(key);
michael@0 1399 }
michael@0 1400
michael@0 1401 return NS_OK;
michael@0 1402 }
michael@0 1403
michael@0 1404 NS_IMETHODIMP
michael@0 1405 mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
michael@0 1406 const char16_t *data)
michael@0 1407 {
michael@0 1408 if (!strcmp(topic, "xpcom-shutdown-loaders")) {
michael@0 1409 UnloadModules();
michael@0 1410 } else {
michael@0 1411 NS_ERROR("Unexpected observer topic.");
michael@0 1412 }
michael@0 1413
michael@0 1414 return NS_OK;
michael@0 1415 }
michael@0 1416
michael@0 1417 size_t
michael@0 1418 mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 1419 {
michael@0 1420 size_t n = aMallocSizeOf(this);
michael@0 1421 n += aMallocSizeOf(location);
michael@0 1422
michael@0 1423 return n;
michael@0 1424 }
michael@0 1425
michael@0 1426 /* static */ already_AddRefed<nsIFactory>
michael@0 1427 mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
michael@0 1428 const mozilla::Module::CIDEntry& entry)
michael@0 1429 {
michael@0 1430 const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
michael@0 1431 MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?");
michael@0 1432
michael@0 1433 nsCOMPtr<nsIFactory> f;
michael@0 1434 nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
michael@0 1435 if (NS_FAILED(rv))
michael@0 1436 return nullptr;
michael@0 1437
michael@0 1438 return f.forget();
michael@0 1439 }
michael@0 1440
michael@0 1441 //----------------------------------------------------------------------
michael@0 1442
michael@0 1443 JSCLContextHelper::JSCLContextHelper(JSContext* aCx)
michael@0 1444 : mContext(aCx)
michael@0 1445 , mBuf(nullptr)
michael@0 1446 {
michael@0 1447 mPusher.Push(mContext);
michael@0 1448 JS_BeginRequest(mContext);
michael@0 1449 }
michael@0 1450
michael@0 1451 JSCLContextHelper::~JSCLContextHelper()
michael@0 1452 {
michael@0 1453 JS_EndRequest(mContext);
michael@0 1454 mPusher.Pop();
michael@0 1455 JSContext *restoredCx = nsContentUtils::GetCurrentJSContext();
michael@0 1456 if (restoredCx && mBuf) {
michael@0 1457 JS_ReportError(restoredCx, mBuf);
michael@0 1458 }
michael@0 1459
michael@0 1460 if (mBuf) {
michael@0 1461 JS_smprintf_free(mBuf);
michael@0 1462 }
michael@0 1463 }
michael@0 1464
michael@0 1465 void
michael@0 1466 JSCLContextHelper::reportErrorAfterPop(char *buf)
michael@0 1467 {
michael@0 1468 MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop");
michael@0 1469 mBuf = buf;
michael@0 1470 }

mercurial