Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/Attributes.h"
8
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
9
10
#include <cstdarg>
11
12
#include "mozilla/Logging.h"
13
#ifdef ANDROID
14
# include <android/log.h>
15
#endif
16
#ifdef XP_WIN
17
# include <windows.h>
18
#endif
19
20
#include "jsapi.h"
21
#include "js/CharacterEncoding.h"
22
#include "js/CompilationAndEvaluation.h"
23
#include "js/Printf.h"
24
#include "js/PropertySpec.h"
25
#include "js/SourceText.h" // JS::SourceText
26
#include "nsCOMPtr.h"
27
#include "nsAutoPtr.h"
28
#include "nsExceptionHandler.h"
29
#include "nsIComponentManager.h"
30
#include "mozilla/Module.h"
31
#include "nsIFile.h"
32
#include "mozJSComponentLoader.h"
33
#include "mozJSLoaderUtils.h"
34
#include "nsIXPConnect.h"
35
#include "nsIScriptSecurityManager.h"
36
#include "nsIFileURL.h"
37
#include "nsIJARURI.h"
38
#include "nsNetUtil.h"
39
#include "nsJSPrincipals.h"
40
#include "nsJSUtils.h"
41
#include "xpcprivate.h"
42
#include "xpcpublic.h"
43
#include "nsContentUtils.h"
44
#include "nsReadableUtils.h"
45
#include "nsXULAppAPI.h"
46
#include "GeckoProfiler.h"
47
#include "WrapperFactory.h"
48
49
#include "AutoMemMap.h"
50
#include "ScriptPreloader-inl.h"
51
52
#include "mozilla/scache/StartupCache.h"
53
#include "mozilla/scache/StartupCacheUtils.h"
54
#include "mozilla/MacroForEach.h"
55
#include "mozilla/Preferences.h"
56
#include "mozilla/ResultExtensions.h"
57
#include "mozilla/ScriptPreloader.h"
58
#include "mozilla/dom/ScriptSettings.h"
59
#include "mozilla/ResultExtensions.h"
60
#include "mozilla/UniquePtrExtensions.h"
61
#include "mozilla/Unused.h"
62
63
using namespace mozilla;
64
using namespace mozilla::scache;
65
using namespace mozilla::loader;
66
using namespace xpc;
67
using namespace JS;
68
69
#define JS_CACHE_PREFIX(aType) "jsloader/" aType
70
71
/**
72
* Buffer sizes for serialization and deserialization of scripts.
73
* FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
74
*/
75
#define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
76
#define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
77
78
// MOZ_LOG=JSComponentLoader:5
79
static LazyLogModule gJSCLLog("JSComponentLoader");
80
81
#define LOG(args) MOZ_LOG(gJSCLLog, mozilla::LogLevel::Debug, args)
82
83
// Components.utils.import error messages
84
#define ERROR_SCOPE_OBJ "%s - Second argument must be an object."
85
#define ERROR_NO_TARGET_OBJECT "%s - Couldn't find target object for import."
86
#define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present."
87
#define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array."
88
#define ERROR_GETTING_ARRAY_LENGTH \
89
"%s - Error getting array length of EXPORTED_SYMBOLS."
90
#define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string."
91
#define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'."
92
#define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object."
93
94
static bool Dump(JSContext* cx, unsigned argc, Value* vp) {
95
if (!nsJSUtils::DumpEnabled()) {
96
return true;
97
}
98
99
CallArgs args = CallArgsFromVp(argc, vp);
100
101
if (args.length() == 0) {
102
return true;
103
}
104
105
RootedString str(cx, JS::ToString(cx, args[0]));
106
if (!str) {
107
return false;
108
}
109
110
JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
111
if (!utf8str) {
112
return false;
113
}
114
115
#ifdef ANDROID
116
__android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
117
#endif
118
#ifdef XP_WIN
119
if (IsDebuggerPresent()) {
120
nsAutoJSString wstr;
121
if (!wstr.init(cx, str)) {
122
return false;
123
}
124
OutputDebugStringW(wstr.get());
125
}
126
#endif
127
fputs(utf8str.get(), stdout);
128
fflush(stdout);
129
return true;
130
}
131
132
static bool Debug(JSContext* cx, unsigned argc, Value* vp) {
133
#ifdef DEBUG
134
return Dump(cx, argc, vp);
135
#else
136
return true;
137
#endif
138
}
139
140
static const JSFunctionSpec gGlobalFun[] = {
141
JS_FN("dump", Dump, 1, 0), JS_FN("debug", Debug, 1, 0),
142
JS_FN("atob", Atob, 1, 0), JS_FN("btoa", Btoa, 1, 0), JS_FS_END};
143
144
class MOZ_STACK_CLASS JSCLContextHelper {
145
public:
146
explicit JSCLContextHelper(JSContext* aCx);
147
~JSCLContextHelper();
148
149
void reportErrorAfterPop(UniqueChars&& buf);
150
151
private:
152
JSContext* mContext;
153
UniqueChars mBuf;
154
155
// prevent copying and assignment
156
JSCLContextHelper(const JSCLContextHelper&) = delete;
157
const JSCLContextHelper& operator=(const JSCLContextHelper&) = delete;
158
};
159
160
static nsresult MOZ_FORMAT_PRINTF(2, 3)
161
ReportOnCallerUTF8(JSContext* callerContext, const char* format, ...) {
162
if (!callerContext) {
163
return NS_ERROR_FAILURE;
164
}
165
166
va_list ap;
167
va_start(ap, format);
168
169
UniqueChars buf = JS_vsmprintf(format, ap);
170
if (!buf) {
171
va_end(ap);
172
return NS_ERROR_OUT_OF_MEMORY;
173
}
174
175
JS_ReportErrorUTF8(callerContext, "%s", buf.get());
176
177
va_end(ap);
178
return NS_OK;
179
}
180
181
mozJSComponentLoader::mozJSComponentLoader()
182
: mModules(16),
183
mImports(16),
184
mInProgressImports(16),
185
mLocations(16),
186
mInitialized(false),
187
mShareLoaderGlobal(false),
188
mLoaderGlobal(dom::RootingCx()) {
189
MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton");
190
}
191
192
#define ENSURE_DEP(name) \
193
{ \
194
nsresult rv = Ensure##name(); \
195
NS_ENSURE_SUCCESS(rv, rv); \
196
}
197
#define ENSURE_DEPS(...) MOZ_FOR_EACH(ENSURE_DEP, (), (__VA_ARGS__));
198
#define BEGIN_ENSURE(self, ...) \
199
{ \
200
if (m##self) return NS_OK; \
201
ENSURE_DEPS(__VA_ARGS__); \
202
}
203
204
class MOZ_STACK_CLASS ComponentLoaderInfo {
205
public:
206
explicit ComponentLoaderInfo(const nsACString& aLocation)
207
: mLocation(aLocation) {}
208
209
nsIIOService* IOService() {
210
MOZ_ASSERT(mIOService);
211
return mIOService;
212
}
213
nsresult EnsureIOService() {
214
if (mIOService) {
215
return NS_OK;
216
}
217
nsresult rv;
218
mIOService = do_GetIOService(&rv);
219
return rv;
220
}
221
222
nsIURI* URI() {
223
MOZ_ASSERT(mURI);
224
return mURI;
225
}
226
nsresult EnsureURI() {
227
BEGIN_ENSURE(URI, IOService);
228
return mIOService->NewURI(mLocation, nullptr, nullptr,
229
getter_AddRefs(mURI));
230
}
231
232
nsIChannel* ScriptChannel() {
233
MOZ_ASSERT(mScriptChannel);
234
return mScriptChannel;
235
}
236
nsresult EnsureScriptChannel() {
237
BEGIN_ENSURE(ScriptChannel, IOService, URI);
238
return NS_NewChannel(getter_AddRefs(mScriptChannel), mURI,
239
nsContentUtils::GetSystemPrincipal(),
240
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
241
nsIContentPolicy::TYPE_SCRIPT,
242
nullptr, // nsICookieSettings
243
nullptr, // aPerformanceStorage
244
nullptr, // aLoadGroup
245
nullptr, // aCallbacks
246
nsIRequest::LOAD_NORMAL, mIOService);
247
}
248
249
nsIURI* ResolvedURI() {
250
MOZ_ASSERT(mResolvedURI);
251
return mResolvedURI;
252
}
253
nsresult EnsureResolvedURI() {
254
BEGIN_ENSURE(ResolvedURI, URI);
255
return ResolveURI(mURI, getter_AddRefs(mResolvedURI));
256
}
257
258
const nsACString& Key() { return mLocation; }
259
nsresult EnsureKey() { return NS_OK; }
260
261
MOZ_MUST_USE nsresult GetLocation(nsCString& aLocation) {
262
nsresult rv = EnsureURI();
263
NS_ENSURE_SUCCESS(rv, rv);
264
return mURI->GetSpec(aLocation);
265
}
266
267
private:
268
const nsACString& mLocation;
269
nsCOMPtr<nsIIOService> mIOService;
270
nsCOMPtr<nsIURI> mURI;
271
nsCOMPtr<nsIChannel> mScriptChannel;
272
nsCOMPtr<nsIURI> mResolvedURI;
273
};
274
275
template <typename... Args>
276
static nsresult ReportOnCallerUTF8(JSCLContextHelper& helper,
277
const char* format,
278
ComponentLoaderInfo& info, Args... args) {
279
nsCString location;
280
MOZ_TRY(info.GetLocation(location));
281
282
UniqueChars buf = JS_smprintf(format, location.get(), args...);
283
if (!buf) {
284
return NS_ERROR_OUT_OF_MEMORY;
285
}
286
287
helper.reportErrorAfterPop(std::move(buf));
288
return NS_ERROR_FAILURE;
289
}
290
291
#undef BEGIN_ENSURE
292
#undef ENSURE_DEPS
293
#undef ENSURE_DEP
294
295
mozJSComponentLoader::~mozJSComponentLoader() {
296
if (mInitialized) {
297
NS_ERROR(
298
"UnloadModules() was not explicitly called before cleaning up "
299
"mozJSComponentLoader");
300
UnloadModules();
301
}
302
303
sSelf = nullptr;
304
}
305
306
StaticRefPtr<mozJSComponentLoader> mozJSComponentLoader::sSelf;
307
308
nsresult mozJSComponentLoader::ReallyInit() {
309
MOZ_ASSERT(!mInitialized);
310
311
const char* shareGlobal = PR_GetEnv("MOZ_LOADER_SHARE_GLOBAL");
312
if (shareGlobal && *shareGlobal) {
313
nsDependentCString val(shareGlobal);
314
mShareLoaderGlobal =
315
!(val.EqualsLiteral("0") || val.LowerCaseEqualsLiteral("no") ||
316
val.LowerCaseEqualsLiteral("false") ||
317
val.LowerCaseEqualsLiteral("off"));
318
} else {
319
mShareLoaderGlobal = Preferences::GetBool("jsloader.shareGlobal");
320
}
321
322
mInitialized = true;
323
324
return NS_OK;
325
}
326
327
// For terrible compatibility reasons, we need to consider both the global
328
// lexical environment and the global of modules when searching for exported
329
// symbols.
330
static JSObject* ResolveModuleObjectProperty(JSContext* aCx,
331
HandleObject aModObj,
332
const char* name) {
333
if (JS_HasExtensibleLexicalEnvironment(aModObj)) {
334
RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj));
335
bool found;
336
if (!JS_HasOwnProperty(aCx, lexical, name, &found)) {
337
return nullptr;
338
}
339
if (found) {
340
return lexical;
341
}
342
}
343
return aModObj;
344
}
345
346
static mozilla::Result<nsCString, nsresult> ReadScript(
347
ComponentLoaderInfo& aInfo);
348
349
static nsresult AnnotateScriptContents(CrashReporter::Annotation aName,
350
const nsACString& aURI) {
351
ComponentLoaderInfo info(aURI);
352
353
nsCString str;
354
MOZ_TRY_VAR(str, ReadScript(info));
355
356
// The crash reporter won't accept any strings with embedded nuls. We
357
// shouldn't have any here, but if we do because of data corruption, we
358
// still want the annotation. So replace any embedded nuls before
359
// annotating.
360
str.ReplaceSubstring(NS_LITERAL_CSTRING("\0"), NS_LITERAL_CSTRING("\\0"));
361
362
CrashReporter::AnnotateCrashReport(aName, str);
363
364
return NS_OK;
365
}
366
367
nsresult mozJSComponentLoader::AnnotateCrashReport() {
368
Unused << AnnotateScriptContents(
369
CrashReporter::Annotation::nsAsyncShutdownComponent,
370
NS_LITERAL_CSTRING("resource://gre/components/nsAsyncShutdown.js"));
371
372
Unused << AnnotateScriptContents(
373
CrashReporter::Annotation::AsyncShutdownModule,
374
NS_LITERAL_CSTRING("resource://gre/modules/AsyncShutdown.jsm"));
375
376
return NS_OK;
377
}
378
379
const mozilla::Module* mozJSComponentLoader::LoadModule(FileLocation& aFile) {
380
if (!NS_IsMainThread()) {
381
MOZ_ASSERT(false, "Don't use JS components off the main thread");
382
return nullptr;
383
}
384
385
nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
386
387
nsCString spec;
388
aFile.GetURIString(spec);
389
ComponentLoaderInfo info(spec);
390
nsresult rv = info.EnsureURI();
391
NS_ENSURE_SUCCESS(rv, nullptr);
392
393
if (!mInitialized) {
394
rv = ReallyInit();
395
if (NS_FAILED(rv)) {
396
return nullptr;
397
}
398
}
399
400
AUTO_PROFILER_TEXT_MARKER_CAUSE("JS XPCOM", spec, JS,
401
profiler_get_backtrace());
402
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("mozJSComponentLoader::LoadModule",
403
OTHER, spec);
404
405
ModuleEntry* mod;
406
if (mModules.Get(spec, &mod)) {
407
return mod;
408
}
409
410
dom::AutoJSAPI jsapi;
411
jsapi.Init();
412
JSContext* cx = jsapi.cx();
413
414
bool isCriticalModule =
415
StringEndsWith(spec, NS_LITERAL_CSTRING("/nsAsyncShutdown.js"));
416
417
nsAutoPtr<ModuleEntry> entry(new ModuleEntry(RootingContext::get(cx)));
418
RootedValue exn(cx);
419
rv = ObjectForLocation(info, file, &entry->obj, &entry->thisObjectKey,
420
&entry->location, isCriticalModule, &exn);
421
if (NS_FAILED(rv)) {
422
// Temporary debugging assertion for bug 1403348:
423
if (isCriticalModule && !exn.isUndefined()) {
424
AnnotateCrashReport();
425
426
JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
427
JS_WrapValue(cx, &exn);
428
429
nsAutoCString file;
430
uint32_t line;
431
uint32_t column;
432
nsAutoString msg;
433
nsContentUtils::ExtractErrorValues(cx, exn, file, &line, &column, msg);
434
435
NS_ConvertUTF16toUTF8 cMsg(msg);
436
MOZ_CRASH_UNSAFE_PRINTF(
437
"Failed to load module \"%s\": "
438
"[\"%s\" {file: \"%s\", line: %u}]",
439
spec.get(), cMsg.get(), file.get(), line);
440
}
441
return nullptr;
442
}
443
444
nsCOMPtr<nsIComponentManager> cm;
445
rv = NS_GetComponentManager(getter_AddRefs(cm));
446
if (NS_FAILED(rv)) {
447
return nullptr;
448
}
449
450
JSAutoRealm ar(cx, entry->obj);
451
RootedObject entryObj(cx, entry->obj);
452
453
RootedObject NSGetFactoryHolder(
454
cx, ResolveModuleObjectProperty(cx, entryObj, "NSGetFactory"));
455
RootedValue NSGetFactory_val(cx);
456
if (!NSGetFactoryHolder ||
457
!JS_GetProperty(cx, NSGetFactoryHolder, "NSGetFactory",
458
&NSGetFactory_val) ||
459
NSGetFactory_val.isUndefined()) {
460
return nullptr;
461
}
462
463
if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
464
/*
465
* spec's encoding is ASCII unless it's zip file, otherwise it's
466
* random encoding. Latin1 variant is safe for random encoding.
467
*/
468
JS_ReportErrorLatin1(
469
cx, "%s has NSGetFactory property that is not a function", spec.get());
470
return nullptr;
471
}
472
473
RootedObject jsGetFactoryObj(cx);
474
if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
475
!jsGetFactoryObj) {
476
/* XXX report error properly */
477
return nullptr;
478
}
479
480
rv = nsXPConnect::XPConnect()->WrapJS(cx, jsGetFactoryObj,
481
NS_GET_IID(xpcIJSGetFactory),
482
getter_AddRefs(entry->getfactoryobj));
483
if (NS_FAILED(rv)) {
484
/* XXX report error properly */
485
#ifdef DEBUG
486
fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
487
#endif
488
return nullptr;
489
}
490
491
#if defined(NIGHTLY_BUILD) || defined(DEBUG)
492
if (Preferences::GetBool("browser.startup.record", false)) {
493
entry->importStack = xpc_PrintJSStack(cx, false, false, false).get();
494
}
495
#endif
496
497
// Cache this module for later
498
mModules.Put(spec, entry);
499
500
// The hash owns the ModuleEntry now, forget about it
501
return entry.forget();
502
}
503
504
void mozJSComponentLoader::FindTargetObject(JSContext* aCx,
505
MutableHandleObject aTargetObject) {
506
aTargetObject.set(js::GetJSMEnvironmentOfScriptedCaller(aCx));
507
508
// The above could fail if the scripted caller is not a component/JSM (it
509
// could be a DOM scope, for instance).
510
//
511
// If the target object was not in the JSM shared global, return the global
512
// instead. This is needed when calling the subscript loader within a frame
513
// script, since it the FrameScript NSVO will have been found.
514
if (!aTargetObject ||
515
!IsLoaderGlobal(JS::GetNonCCWObjectGlobal(aTargetObject))) {
516
aTargetObject.set(JS::GetScriptedCallerGlobal(aCx));
517
518
// Return nullptr if the scripted caller is in a different compartment.
519
if (js::GetObjectCompartment(aTargetObject) !=
520
js::GetContextCompartment(aCx)) {
521
aTargetObject.set(nullptr);
522
}
523
}
524
}
525
526
void mozJSComponentLoader::InitStatics() {
527
MOZ_ASSERT(!sSelf);
528
sSelf = new mozJSComponentLoader();
529
}
530
531
void mozJSComponentLoader::Unload() {
532
if (sSelf) {
533
sSelf->UnloadModules();
534
}
535
}
536
537
void mozJSComponentLoader::Shutdown() {
538
MOZ_ASSERT(sSelf);
539
sSelf = nullptr;
540
}
541
542
// This requires that the keys be strings and the values be pointers.
543
template <class Key, class Data, class UserData>
544
static size_t SizeOfTableExcludingThis(
545
const nsBaseHashtable<Key, Data, UserData>& aTable,
546
MallocSizeOf aMallocSizeOf) {
547
size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
548
for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
549
n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
550
n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
551
}
552
return n;
553
}
554
555
size_t mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
556
size_t n = aMallocSizeOf(this);
557
n += SizeOfTableExcludingThis(mModules, aMallocSizeOf);
558
n += SizeOfTableExcludingThis(mImports, aMallocSizeOf);
559
n += mLocations.ShallowSizeOfExcludingThis(aMallocSizeOf);
560
n += SizeOfTableExcludingThis(mInProgressImports, aMallocSizeOf);
561
return n;
562
}
563
564
void mozJSComponentLoader::CreateLoaderGlobal(JSContext* aCx,
565
const nsACString& aLocation,
566
MutableHandleObject aGlobal) {
567
RefPtr<BackstagePass> backstagePass;
568
nsresult rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
569
NS_ENSURE_SUCCESS_VOID(rv);
570
571
RealmOptions options;
572
573
options.creationOptions().setNewCompartmentInSystemZone();
574
xpc::SetPrefableRealmOptions(options);
575
576
// Defer firing OnNewGlobalObject until after the __URI__ property has
577
// been defined so the JS debugger can tell what module the global is
578
// for
579
RootedObject global(aCx);
580
rv = xpc::InitClassesWithNewWrappedGlobal(
581
aCx, static_cast<nsIGlobalObject*>(backstagePass),
582
nsContentUtils::GetSystemPrincipal(), xpc::DONT_FIRE_ONNEWGLOBALHOOK,
583
options, &global);
584
NS_ENSURE_SUCCESS_VOID(rv);
585
586
NS_ENSURE_TRUE_VOID(global);
587
588
backstagePass->SetGlobalObject(global);
589
590
JSAutoRealm ar(aCx, global);
591
if (!JS_DefineFunctions(aCx, global, gGlobalFun)) {
592
return;
593
}
594
595
// Set the location information for the new global, so that tools like
596
// about:memory may use that information
597
xpc::SetLocationForGlobal(global, aLocation);
598
599
aGlobal.set(global);
600
}
601
602
bool mozJSComponentLoader::ReuseGlobal(nsIURI* aURI) {
603
if (!mShareLoaderGlobal) {
604
return false;
605
}
606
607
nsCString spec;
608
NS_ENSURE_SUCCESS(aURI->GetSpec(spec), false);
609
610
// The loader calls Object.freeze on global properties, which
611
// causes problems if the global is shared with other code.
612
if (spec.EqualsASCII("resource://gre/modules/commonjs/toolkit/loader.js")) {
613
return false;
614
}
615
616
// Various tests call addDebuggerToGlobal on the result of
617
// importing this JSM, which would be annoying to fix.
618
if (spec.EqualsASCII("resource://gre/modules/jsdebugger.jsm")) {
619
return false;
620
}
621
622
// BrowserTestUtils.jsm calls Cu.permitCPOWsInScope(this) which sets a
623
// per-compartment flag to permit CPOWs. We don't want to set this flag for
624
// all other JSMs.
625
if (spec.EqualsASCII("resource://testing-common/BrowserTestUtils.jsm")) {
626
return false;
627
}
628
629
// Some SpecialPowers jsms call Cu.forcePermissiveCOWs(),
630
// which sets a per-compartment flag that disables certain
631
// security wrappers, so don't use the shared global for them
632
// to avoid breaking tests.
633
if (FindInReadable(NS_LITERAL_CSTRING("resource://specialpowers/"), spec)) {
634
return false;
635
}
636
637
return true;
638
}
639
640
JSObject* mozJSComponentLoader::GetSharedGlobal(JSContext* aCx) {
641
if (!mLoaderGlobal) {
642
JS::RootedObject globalObj(aCx);
643
CreateLoaderGlobal(aCx, NS_LITERAL_CSTRING("shared JSM global"),
644
&globalObj);
645
646
// If we fail to create a module global this early, we're not going to
647
// get very far, so just bail out now.
648
MOZ_RELEASE_ASSERT(globalObj);
649
mLoaderGlobal = globalObj;
650
651
// AutoEntryScript required to invoke debugger hook, which is a
652
// Gecko-specific concept at present.
653
dom::AutoEntryScript aes(globalObj, "component loader report global");
654
JS_FireOnNewGlobalObject(aes.cx(), globalObj);
655
}
656
657
return mLoaderGlobal;
658
}
659
660
JSObject* mozJSComponentLoader::PrepareObjectForLocation(
661
JSContext* aCx, nsIFile* aComponentFile, nsIURI* aURI, bool* aReuseGlobal,
662
bool* aRealFile) {
663
nsAutoCString nativePath;
664
NS_ENSURE_SUCCESS(aURI->GetSpec(nativePath), nullptr);
665
666
bool reuseGlobal = ReuseGlobal(aURI);
667
668
*aReuseGlobal = reuseGlobal;
669
670
bool createdNewGlobal = false;
671
RootedObject globalObj(aCx);
672
if (reuseGlobal) {
673
globalObj = GetSharedGlobal(aCx);
674
} else if (!globalObj) {
675
CreateLoaderGlobal(aCx, nativePath, &globalObj);
676
createdNewGlobal = true;
677
}
678
679
// |thisObj| is the object we set properties on for a particular .jsm.
680
RootedObject thisObj(aCx, globalObj);
681
NS_ENSURE_TRUE(thisObj, nullptr);
682
683
JSAutoRealm ar(aCx, thisObj);
684
685
if (reuseGlobal) {
686
thisObj = js::NewJSMEnvironment(aCx);
687
NS_ENSURE_TRUE(thisObj, nullptr);
688
}
689
690
*aRealFile = false;
691
692
// need to be extra careful checking for URIs pointing to files
693
// EnsureFile may not always get called, especially on resource URIs
694
// so we need to call GetFile to make sure this is a valid file
695
{
696
// Create an extra scope so that ~nsCOMPtr will run before the returned
697
// JSObject* is placed on the stack, since otherwise a GC in the destructor
698
// would invalidate the return value.
699
nsresult rv = NS_OK;
700
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
701
nsCOMPtr<nsIFile> testFile;
702
if (NS_SUCCEEDED(rv)) {
703
fileURL->GetFile(getter_AddRefs(testFile));
704
}
705
706
if (testFile) {
707
*aRealFile = true;
708
709
if (XRE_IsParentProcess()) {
710
RootedObject locationObj(aCx);
711
712
rv = nsXPConnect::XPConnect()->WrapNative(aCx, thisObj, aComponentFile,
713
NS_GET_IID(nsIFile),
714
locationObj.address());
715
NS_ENSURE_SUCCESS(rv, nullptr);
716
NS_ENSURE_TRUE(locationObj, nullptr);
717
718
if (!JS_DefineProperty(aCx, thisObj, "__LOCATION__", locationObj, 0)) {
719
return nullptr;
720
}
721
}
722
}
723
}
724
725
// Expose the URI from which the script was imported through a special
726
// variable that we insert into the JSM.
727
RootedString exposedUri(
728
aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length()));
729
NS_ENSURE_TRUE(exposedUri, nullptr);
730
731
if (!JS_DefineProperty(aCx, thisObj, "__URI__", exposedUri, 0)) {
732
return nullptr;
733
}
734
735
if (createdNewGlobal) {
736
// AutoEntryScript required to invoke debugger hook, which is a
737
// Gecko-specific concept at present.
738
dom::AutoEntryScript aes(globalObj, "component loader report global");
739
JS_FireOnNewGlobalObject(aes.cx(), globalObj);
740
}
741
742
return thisObj;
743
}
744
745
static mozilla::Result<nsCString, nsresult> ReadScript(
746
ComponentLoaderInfo& aInfo) {
747
MOZ_TRY(aInfo.EnsureScriptChannel());
748
749
nsCOMPtr<nsIInputStream> scriptStream;
750
MOZ_TRY(NS_MaybeOpenChannelUsingOpen(aInfo.ScriptChannel(),
751
getter_AddRefs(scriptStream)));
752
753
uint64_t len64;
754
uint32_t bytesRead;
755
756
MOZ_TRY(scriptStream->Available(&len64));
757
NS_ENSURE_TRUE(len64 < UINT32_MAX, Err(NS_ERROR_FILE_TOO_BIG));
758
NS_ENSURE_TRUE(len64, Err(NS_ERROR_FAILURE));
759
uint32_t len = (uint32_t)len64;
760
761
/* malloc an internal buf the size of the file */
762
nsCString str;
763
if (!str.SetLength(len, fallible)) {
764
return Err(NS_ERROR_OUT_OF_MEMORY);
765
}
766
767
/* read the file in one swoop */
768
MOZ_TRY(scriptStream->Read(str.BeginWriting(), len, &bytesRead));
769
if (bytesRead != len) {
770
return Err(NS_BASE_STREAM_OSERROR);
771
}
772
773
return std::move(str);
774
}
775
776
nsresult mozJSComponentLoader::ObjectForLocation(
777
ComponentLoaderInfo& aInfo, nsIFile* aComponentFile,
778
MutableHandleObject aObject, MutableHandleScript aTableScript,
779
char** aLocation, bool aPropagateExceptions,
780
MutableHandleValue aException) {
781
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
782
783
dom::AutoJSAPI jsapi;
784
jsapi.Init();
785
JSContext* cx = jsapi.cx();
786
787
bool realFile = false;
788
nsresult rv = aInfo.EnsureURI();
789
NS_ENSURE_SUCCESS(rv, rv);
790
bool reuseGlobal = false;
791
RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aInfo.URI(),
792
&reuseGlobal, &realFile));
793
NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
794
MOZ_ASSERT(JS_IsGlobalObject(obj) == !reuseGlobal);
795
796
JSAutoRealm ar(cx, obj);
797
798
RootedScript script(cx);
799
800
nsAutoCString nativePath;
801
rv = aInfo.URI()->GetSpec(nativePath);
802
NS_ENSURE_SUCCESS(rv, rv);
803
804
// Before compiling the script, first check to see if we have it in
805
// the startupcache. Note: as a rule, startupcache errors are not fatal
806
// to loading the script, since we can always slow-load.
807
808
bool writeToCache = false;
809
StartupCache* cache = StartupCache::GetSingleton();
810
811
aInfo.EnsureResolvedURI();
812
813
nsAutoCString cachePath(reuseGlobal ? JS_CACHE_PREFIX("non-syntactic")
814
: JS_CACHE_PREFIX("global"));
815
rv = PathifyURI(aInfo.ResolvedURI(), cachePath);
816
NS_ENSURE_SUCCESS(rv, rv);
817
818
script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
819
if (!script && cache) {
820
ReadCachedScript(cache, cachePath, cx, &script);
821
}
822
823
if (script) {
824
LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
825
} else if (cache) {
826
// This is ok, it just means the script is not yet in the
827
// cache. Could mean that the cache was corrupted and got removed,
828
// but either way we're going to write this out.
829
writeToCache = true;
830
// ReadCachedScript may have set a pending exception.
831
JS_ClearPendingException(cx);
832
}
833
834
if (!script) {
835
// The script wasn't in the cache , so compile it now.
836
LOG(("Slow loading %s\n", nativePath.get()));
837
838
// If we are debugging a replaying process and have diverged from the
839
// recording, trying to load and compile new code will cause the
840
// debugger operation to fail, so just abort now.
841
if (recordreplay::HasDivergedFromRecording()) {
842
return NS_ERROR_FAILURE;
843
}
844
845
// Use lazy source if we're using the startup cache. Non-lazy source +
846
// startup cache regresses installer size (due to source code stored in
847
// XDR encoded modules in omni.ja). Also, XDR decoding is relatively
848
// fast. When we're not using the startup cache, we want to use non-lazy
849
// source code so that we can use lazy parsing.
850
// See bug 1303754.
851
CompileOptions options(cx);
852
options.setNoScriptRval(true)
853
.setForceStrictMode()
854
.setFileAndLine(nativePath.get(), 1)
855
.setSourceIsLazy(cache || ScriptPreloader::GetSingleton().Active());
856
857
if (realFile) {
858
AutoMemMap map;
859
MOZ_TRY(map.init(aComponentFile));
860
861
// Note: exceptions will get handled further down;
862
// don't early return for them here.
863
auto buf = map.get<char>();
864
865
JS::SourceText<mozilla::Utf8Unit> srcBuf;
866
if (srcBuf.init(cx, buf.get(), map.size(),
867
JS::SourceOwnership::Borrowed)) {
868
script = reuseGlobal ? CompileForNonSyntacticScopeDontInflate(
869
cx, options, srcBuf)
870
: CompileDontInflate(cx, options, srcBuf);
871
} else {
872
MOZ_ASSERT(!script);
873
}
874
} else {
875
nsCString str;
876
MOZ_TRY_VAR(str, ReadScript(aInfo));
877
878
JS::SourceText<mozilla::Utf8Unit> srcBuf;
879
if (srcBuf.init(cx, str.get(), str.Length(),
880
JS::SourceOwnership::Borrowed)) {
881
script = reuseGlobal ? CompileForNonSyntacticScopeDontInflate(
882
cx, options, srcBuf)
883
: CompileDontInflate(cx, options, srcBuf);
884
} else {
885
MOZ_ASSERT(!script);
886
}
887
}
888
// Propagate the exception, if one exists. Also, don't leave the stale
889
// exception on this context.
890
if (!script && aPropagateExceptions && jsapi.HasException()) {
891
if (!jsapi.StealException(aException)) {
892
return NS_ERROR_OUT_OF_MEMORY;
893
}
894
}
895
}
896
897
if (!script) {
898
return NS_ERROR_FAILURE;
899
}
900
901
ScriptPreloader::GetSingleton().NoteScript(nativePath, cachePath, script);
902
903
if (writeToCache) {
904
// We successfully compiled the script, so cache it.
905
rv = WriteCachedScript(cache, cachePath, cx, script);
906
907
// Don't treat failure to write as fatal, since we might be working
908
// with a read-only cache.
909
if (NS_SUCCEEDED(rv)) {
910
LOG(("Successfully wrote to cache\n"));
911
} else {
912
LOG(("Failed to write to cache\n"));
913
}
914
}
915
916
// Assign aObject here so that it's available to recursive imports.
917
// See bug 384168.
918
aObject.set(obj);
919
920
aTableScript.set(script);
921
922
{ // Scope for AutoEntryScript
923
924
// We're going to run script via JS_ExecuteScript, so we need an
925
// AutoEntryScript. This is Gecko-specific and not in any spec.
926
dom::AutoEntryScript aes(CurrentGlobalOrNull(cx),
927
"component loader load module");
928
JSContext* aescx = aes.cx();
929
930
bool executeOk = false;
931
if (JS_IsGlobalObject(obj)) {
932
JS::RootedValue rval(cx);
933
executeOk = JS::CloneAndExecuteScript(aescx, script, &rval);
934
} else {
935
executeOk = js::ExecuteInJSMEnvironment(aescx, script, obj);
936
}
937
938
if (!executeOk) {
939
if (aPropagateExceptions && aes.HasException()) {
940
// Ignore return value because we're returning an error code
941
// anyway.
942
Unused << aes.StealException(aException);
943
}
944
aObject.set(nullptr);
945
aTableScript.set(nullptr);
946
return NS_ERROR_FAILURE;
947
}
948
}
949
950
/* Freed when we remove from the table. */
951
*aLocation = ToNewCString(nativePath);
952
if (!*aLocation) {
953
aObject.set(nullptr);
954
aTableScript.set(nullptr);
955
return NS_ERROR_OUT_OF_MEMORY;
956
}
957
958
return NS_OK;
959
}
960
961
void mozJSComponentLoader::UnloadModules() {
962
mInitialized = false;
963
964
if (mLoaderGlobal) {
965
MOZ_ASSERT(JS_HasExtensibleLexicalEnvironment(mLoaderGlobal));
966
JS::RootedObject lexicalEnv(dom::RootingCx(),
967
JS_ExtensibleLexicalEnvironment(mLoaderGlobal));
968
JS_SetAllNonReservedSlotsToUndefined(lexicalEnv);
969
JS_SetAllNonReservedSlotsToUndefined(mLoaderGlobal);
970
mLoaderGlobal = nullptr;
971
}
972
973
mInProgressImports.Clear();
974
mImports.Clear();
975
mLocations.Clear();
976
977
for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) {
978
iter.Data()->Clear();
979
iter.Remove();
980
}
981
}
982
983
nsresult mozJSComponentLoader::ImportInto(const nsACString& registryLocation,
984
HandleValue targetValArg,
985
JSContext* cx, uint8_t optionalArgc,
986
MutableHandleValue retval) {
987
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
988
989
RootedValue targetVal(cx, targetValArg);
990
RootedObject targetObject(cx, nullptr);
991
992
if (optionalArgc) {
993
// The caller passed in the optional second argument. Get it.
994
if (targetVal.isObject()) {
995
// If we're passing in something like a content DOM window, chances
996
// are the caller expects the properties to end up on the object
997
// proper and not on the Xray holder. This is dubious, but can be used
998
// during testing. Given that dumb callers can already leak JSMs into
999
// content by passing a raw content JS object (where Xrays aren't
1000
// possible), we aim for consistency here. Waive xray.
1001
if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
1002
!WrapperFactory::WaiveXrayAndWrap(cx, &targetVal)) {
1003
return NS_ERROR_FAILURE;
1004
}
1005
targetObject = &targetVal.toObject();
1006
} else if (!targetVal.isNull()) {
1007
// If targetVal isNull(), we actually want to leave targetObject null.
1008
// Not doing so breaks |make package|.
1009
return ReportOnCallerUTF8(cx, ERROR_SCOPE_OBJ,
1010
PromiseFlatCString(registryLocation).get());
1011
}
1012
} else {
1013
FindTargetObject(cx, &targetObject);
1014
if (!targetObject) {
1015
return ReportOnCallerUTF8(cx, ERROR_NO_TARGET_OBJECT,
1016
PromiseFlatCString(registryLocation).get());
1017
}
1018
}
1019
1020
js::AssertSameCompartment(cx, targetObject);
1021
1022
RootedObject global(cx);
1023
nsresult rv = ImportInto(registryLocation, targetObject, cx, &global);
1024
1025
if (global) {
1026
if (!JS_WrapObject(cx, &global)) {
1027
NS_ERROR("can't wrap return value");
1028
return NS_ERROR_FAILURE;
1029
}
1030
1031
retval.setObject(*global);
1032
}
1033
return rv;
1034
}
1035
1036
nsresult mozJSComponentLoader::IsModuleLoaded(const nsACString& aLocation,
1037
bool* retval) {
1038
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
1039
1040
nsresult rv;
1041
if (!mInitialized) {
1042
rv = ReallyInit();
1043
NS_ENSURE_SUCCESS(rv, rv);
1044
}
1045
1046
ComponentLoaderInfo info(aLocation);
1047
rv = info.EnsureKey();
1048
NS_ENSURE_SUCCESS(rv, rv);
1049
1050
*retval = !!mImports.Get(info.Key());
1051
return NS_OK;
1052
}
1053
1054
void mozJSComponentLoader::GetLoadedModules(
1055
nsTArray<nsCString>& aLoadedModules) {
1056
aLoadedModules.SetCapacity(mImports.Count());
1057
for (auto iter = mImports.Iter(); !iter.Done(); iter.Next()) {
1058
aLoadedModules.AppendElement(iter.Data()->location);
1059
}
1060
}
1061
1062
void mozJSComponentLoader::GetLoadedComponents(
1063
nsTArray<nsCString>& aLoadedComponents) {
1064
aLoadedComponents.SetCapacity(mModules.Count());
1065
for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) {
1066
aLoadedComponents.AppendElement(iter.Data()->location);
1067
}
1068
}
1069
1070
nsresult mozJSComponentLoader::GetModuleImportStack(const nsACString& aLocation,
1071
nsACString& retval) {
1072
#ifdef STARTUP_RECORDER_ENABLED
1073
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
1074
MOZ_ASSERT(mInitialized);
1075
1076
ComponentLoaderInfo info(aLocation);
1077
nsresult rv = info.EnsureKey();
1078
NS_ENSURE_SUCCESS(rv, rv);
1079
1080
ModuleEntry* mod;
1081
if (!mImports.Get(info.Key(), &mod)) {
1082
return NS_ERROR_FAILURE;
1083
}
1084
1085
retval = mod->importStack;
1086
return NS_OK;
1087
#else
1088
return NS_ERROR_NOT_IMPLEMENTED;
1089
#endif
1090
}
1091
1092
nsresult mozJSComponentLoader::GetComponentLoadStack(
1093
const nsACString& aLocation, nsACString& retval) {
1094
#ifdef STARTUP_RECORDER_ENABLED
1095
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
1096
MOZ_ASSERT(mInitialized);
1097
1098
ComponentLoaderInfo info(aLocation);
1099
nsresult rv = info.EnsureURI();
1100
NS_ENSURE_SUCCESS(rv, rv);
1101
1102
ModuleEntry* mod;
1103
if (!mModules.Get(info.Key(), &mod)) {
1104
return NS_ERROR_FAILURE;
1105
}
1106
1107
retval = mod->importStack;
1108
return NS_OK;
1109
#else
1110
return NS_ERROR_NOT_IMPLEMENTED;
1111
#endif
1112
}
1113
1114
static JSObject* ResolveModuleObjectPropertyById(JSContext* aCx,
1115
HandleObject aModObj,
1116
HandleId id) {
1117
if (JS_HasExtensibleLexicalEnvironment(aModObj)) {
1118
RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj));
1119
bool found;
1120
if (!JS_HasOwnPropertyById(aCx, lexical, id, &found)) {
1121
return nullptr;
1122
}
1123
if (found) {
1124
return lexical;
1125
}
1126
}
1127
return aModObj;
1128
}
1129
1130
nsresult mozJSComponentLoader::ImportInto(const nsACString& aLocation,
1131
HandleObject targetObj, JSContext* cx,
1132
MutableHandleObject vp) {
1133
vp.set(nullptr);
1134
1135
JS::RootedObject exports(cx);
1136
MOZ_TRY(Import(cx, aLocation, vp, &exports, !targetObj));
1137
1138
if (targetObj) {
1139
JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
1140
if (!JS_Enumerate(cx, exports, &ids)) {
1141
return NS_ERROR_OUT_OF_MEMORY;
1142
}
1143
1144
JS::RootedValue value(cx);
1145
JS::RootedId id(cx);
1146
for (jsid idVal : ids) {
1147
id = idVal;
1148
if (!JS_GetPropertyById(cx, exports, id, &value) ||
1149
!JS_SetPropertyById(cx, targetObj, id, value)) {
1150
return NS_ERROR_FAILURE;
1151
}
1152
}
1153
}
1154
1155
return NS_OK;
1156
}
1157
1158
nsresult mozJSComponentLoader::ExtractExports(
1159
JSContext* aCx, ComponentLoaderInfo& aInfo, ModuleEntry* aMod,
1160
JS::MutableHandleObject aExports) {
1161
// cxhelper must be created before jsapi, so that jsapi is destroyed and
1162
// pops any context it has pushed before we report to the caller context.
1163
JSCLContextHelper cxhelper(aCx);
1164
1165
// Even though we are calling JS_SetPropertyById on targetObj, we want
1166
// to ensure that we never run script here, so we use an AutoJSAPI and
1167
// not an AutoEntryScript.
1168
dom::AutoJSAPI jsapi;
1169
jsapi.Init();
1170
JSContext* cx = jsapi.cx();
1171
JSAutoRealm ar(cx, aMod->obj);
1172
1173
RootedValue symbols(cx);
1174
{
1175
RootedObject obj(
1176
cx, ResolveModuleObjectProperty(cx, aMod->obj, "EXPORTED_SYMBOLS"));
1177
if (!obj || !JS_GetProperty(cx, obj, "EXPORTED_SYMBOLS", &symbols)) {
1178
return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT, aInfo);
1179
}
1180
}
1181
1182
bool isArray;
1183
if (!JS_IsArrayObject(cx, symbols, &isArray)) {
1184
return NS_ERROR_FAILURE;
1185
}
1186
if (!isArray) {
1187
return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY, aInfo);
1188
}
1189
1190
RootedObject symbolsObj(cx, &symbols.toObject());
1191
1192
// Iterate over symbols array, installing symbols on targetObj:
1193
1194
uint32_t symbolCount = 0;
1195
if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) {
1196
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH, aInfo);
1197
}
1198
1199
#ifdef DEBUG
1200
nsAutoCString logBuffer;
1201
#endif
1202
1203
aExports.set(JS_NewPlainObject(cx));
1204
if (!aExports) {
1205
return NS_ERROR_OUT_OF_MEMORY;
1206
}
1207
1208
bool missing = false;
1209
1210
RootedValue value(cx);
1211
RootedId symbolId(cx);
1212
RootedObject symbolHolder(cx);
1213
for (uint32_t i = 0; i < symbolCount; ++i) {
1214
if (!JS_GetElement(cx, symbolsObj, i, &value) || !value.isString() ||
1215
!JS_ValueToId(cx, value, &symbolId)) {
1216
return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT, aInfo, i);
1217
}
1218
1219
symbolHolder = ResolveModuleObjectPropertyById(cx, aMod->obj, symbolId);
1220
if (!symbolHolder ||
1221
!JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
1222
RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
1223
JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr);
1224
if (!bytes) {
1225
return NS_ERROR_FAILURE;
1226
}
1227
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL, aInfo,
1228
bytes.get());
1229
}
1230
1231
if (value.isUndefined()) {
1232
missing = true;
1233
}
1234
1235
if (!JS_SetPropertyById(cx, aExports, symbolId, value)) {
1236
RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
1237
JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr);
1238
if (!bytes) {
1239
return NS_ERROR_FAILURE;
1240
}
1241
return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL, aInfo,
1242
bytes.get());
1243
}
1244
#ifdef DEBUG
1245
if (i == 0) {
1246
logBuffer.AssignLiteral("Installing symbols [ ");
1247
}
1248
JS::UniqueChars bytes =
1249
JS_EncodeStringToLatin1(cx, JSID_TO_STRING(symbolId));
1250
if (!!bytes) {
1251
logBuffer.Append(bytes.get());
1252
}
1253
logBuffer.Append(' ');
1254
if (i == symbolCount - 1) {
1255
nsCString location;
1256
MOZ_TRY(aInfo.GetLocation(location));
1257
LOG(("%s] from %s\n", logBuffer.get(), location.get()));
1258
}
1259
#endif
1260
}
1261
1262
// Don't cache the exports object if any of its exported symbols are
1263
// missing. If the module hasn't finished loading yet, they may be
1264
// defined the next time we try to import it.
1265
if (!missing) {
1266
aMod->exports = aExports;
1267
}
1268
return NS_OK;
1269
}
1270
1271
nsresult mozJSComponentLoader::Import(JSContext* aCx,
1272
const nsACString& aLocation,
1273
JS::MutableHandleObject aModuleGlobal,
1274
JS::MutableHandleObject aModuleExports,
1275
bool aIgnoreExports) {
1276
nsresult rv;
1277
if (!mInitialized) {
1278
rv = ReallyInit();
1279
NS_ENSURE_SUCCESS(rv, rv);
1280
}
1281
1282
AUTO_PROFILER_TEXT_MARKER_CAUSE("ChromeUtils.import", aLocation, JS,
1283
profiler_get_backtrace());
1284
1285
ComponentLoaderInfo info(aLocation);
1286
1287
rv = info.EnsureKey();
1288
NS_ENSURE_SUCCESS(rv, rv);
1289
1290
ModuleEntry* mod;
1291
nsAutoPtr<ModuleEntry> newEntry;
1292
if (!mImports.Get(info.Key(), &mod) &&
1293
!mInProgressImports.Get(info.Key(), &mod)) {
1294
newEntry = new ModuleEntry(RootingContext::get(aCx));
1295
if (!newEntry) {
1296
return NS_ERROR_OUT_OF_MEMORY;
1297
}
1298
1299
// Note: This implies EnsureURI().
1300
MOZ_TRY(info.EnsureResolvedURI());
1301
1302
// get the JAR if there is one
1303
nsCOMPtr<nsIJARURI> jarURI;
1304
jarURI = do_QueryInterface(info.ResolvedURI(), &rv);
1305
nsCOMPtr<nsIFileURL> baseFileURL;
1306
if (NS_SUCCEEDED(rv)) {
1307
nsCOMPtr<nsIURI> baseURI;
1308
while (jarURI) {
1309
jarURI->GetJARFile(getter_AddRefs(baseURI));
1310
jarURI = do_QueryInterface(baseURI, &rv);
1311
}
1312
baseFileURL = do_QueryInterface(baseURI, &rv);
1313
NS_ENSURE_SUCCESS(rv, rv);
1314
} else {
1315
baseFileURL = do_QueryInterface(info.ResolvedURI(), &rv);
1316
NS_ENSURE_SUCCESS(rv, rv);
1317
}
1318
1319
nsCOMPtr<nsIFile> sourceFile;
1320
rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
1321
NS_ENSURE_SUCCESS(rv, rv);
1322
1323
rv = info.ResolvedURI()->GetSpec(newEntry->resolvedURL);
1324
NS_ENSURE_SUCCESS(rv, rv);
1325
1326
nsCString* existingPath;
1327
if (mLocations.Get(newEntry->resolvedURL, &existingPath) &&
1328
*existingPath != info.Key()) {
1329
return NS_ERROR_UNEXPECTED;
1330
}
1331
1332
mLocations.Put(newEntry->resolvedURL, new nsCString(info.Key()));
1333
1334
RootedValue exception(aCx);
1335
{
1336
mInProgressImports.Put(info.Key(), newEntry);
1337
auto cleanup =
1338
MakeScopeExit([&]() { mInProgressImports.Remove(info.Key()); });
1339
1340
rv = ObjectForLocation(info, sourceFile, &newEntry->obj,
1341
&newEntry->thisObjectKey, &newEntry->location,
1342
true, &exception);
1343
}
1344
1345
if (NS_FAILED(rv)) {
1346
if (!exception.isUndefined()) {
1347
// An exception was thrown during compilation. Propagate it
1348
// out to our caller so they can report it.
1349
if (!JS_WrapValue(aCx, &exception)) {
1350
return NS_ERROR_OUT_OF_MEMORY;
1351
}
1352
JS_SetPendingException(aCx, exception);
1353
return NS_ERROR_FAILURE;
1354
}
1355
1356
// Something failed, but we don't know what it is, guess.
1357
return NS_ERROR_FILE_NOT_FOUND;
1358
}
1359
1360
#ifdef STARTUP_RECORDER_ENABLED
1361
if (Preferences::GetBool("browser.startup.record", false)) {
1362
newEntry->importStack = xpc_PrintJSStack(aCx, false, false, false).get();
1363
}
1364
#endif
1365
1366
mod = newEntry;
1367
}
1368
1369
MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
1370
aModuleGlobal.set(mod->obj);
1371
1372
JS::RootedObject exports(aCx, mod->exports);
1373
if (!exports && !aIgnoreExports) {
1374
MOZ_TRY(ExtractExports(aCx, info, mod, &exports));
1375
}
1376
1377
if (exports && !JS_WrapObject(aCx, &exports)) {
1378
return NS_ERROR_FAILURE;
1379
}
1380
aModuleExports.set(exports);
1381
1382
// Cache this module for later
1383
if (newEntry) {
1384
mImports.Put(info.Key(), newEntry);
1385
newEntry.forget();
1386
}
1387
1388
return NS_OK;
1389
}
1390
1391
nsresult mozJSComponentLoader::Unload(const nsACString& aLocation) {
1392
nsresult rv;
1393
1394
if (!mInitialized) {
1395
return NS_OK;
1396
}
1397
1398
ComponentLoaderInfo info(aLocation);
1399
rv = info.EnsureKey();
1400
NS_ENSURE_SUCCESS(rv, rv);
1401
ModuleEntry* mod;
1402
if (mImports.Get(info.Key(), &mod)) {
1403
mLocations.Remove(mod->resolvedURL);
1404
mImports.Remove(info.Key());
1405
}
1406
1407
// If this is the last module to be unloaded, we will leak mLoaderGlobal
1408
// until UnloadModules is called. So be it.
1409
1410
return NS_OK;
1411
}
1412
1413
size_t mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(
1414
MallocSizeOf aMallocSizeOf) const {
1415
size_t n = aMallocSizeOf(this);
1416
n += aMallocSizeOf(location);
1417
1418
return n;
1419
}
1420
1421
/* static */
1422
already_AddRefed<nsIFactory> mozJSComponentLoader::ModuleEntry::GetFactory(
1423
const mozilla::Module& module, const mozilla::Module::CIDEntry& entry) {
1424
const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
1425
MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?");
1426
1427
nsCOMPtr<nsIFactory> f;
1428
nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
1429
if (NS_FAILED(rv)) {
1430
return nullptr;
1431
}
1432
1433
return f.forget();
1434
}
1435
1436
//----------------------------------------------------------------------
1437
1438
JSCLContextHelper::JSCLContextHelper(JSContext* aCx)
1439
: mContext(aCx), mBuf(nullptr) {}
1440
1441
JSCLContextHelper::~JSCLContextHelper() {
1442
if (mBuf) {
1443
JS_ReportErrorUTF8(mContext, "%s", mBuf.get());
1444
}
1445
}
1446
1447
void JSCLContextHelper::reportErrorAfterPop(UniqueChars&& buf) {
1448
MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop");
1449
mBuf = std::move(buf);
1450
}