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