Coverage Report

Created: 2018-09-25 14:53

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