Bug 1843838 - Rename ScriptLoadRequest::IsReadyToRun to IsFinished since this returns...
[gecko.git] / js / loader / ModuleLoaderBase.cpp
blob63bbe1311f472cd0ad1c47b9829d0354946627ae
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/. */
7 #include "GeckoProfiler.h"
8 #include "LoadedScript.h"
9 #include "ModuleLoadRequest.h"
10 #include "ScriptLoadRequest.h"
11 #include "mozilla/dom/ScriptSettings.h" // AutoJSAPI
12 #include "mozilla/dom/ScriptTrace.h"
14 #include "js/Array.h" // JS::GetArrayLength
15 #include "js/CompilationAndEvaluation.h"
16 #include "js/ContextOptions.h" // JS::ContextOptionsRef
17 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
18 #include "js/Modules.h" // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
19 #include "js/OffThreadScriptCompilation.h"
20 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetElement
21 #include "js/SourceText.h"
22 #include "mozilla/BasePrincipal.h"
23 #include "mozilla/dom/AutoEntryScript.h"
24 #include "mozilla/dom/ScriptLoadContext.h"
25 #include "mozilla/CycleCollectedJSContext.h" // nsAutoMicroTask
26 #include "mozilla/Preferences.h"
27 #include "mozilla/StaticPrefs_dom.h"
28 #include "nsContentUtils.h"
29 #include "nsICacheInfoChannel.h" // nsICacheInfoChannel
30 #include "nsNetUtil.h" // NS_NewURI
31 #include "xpcpublic.h"
33 using mozilla::CycleCollectedJSContext;
34 using mozilla::Err;
35 using mozilla::Preferences;
36 using mozilla::UniquePtr;
37 using mozilla::WrapNotNull;
38 using mozilla::dom::AutoJSAPI;
40 namespace JS::loader {
42 mozilla::LazyLogModule ModuleLoaderBase::gCspPRLog("CSP");
43 mozilla::LazyLogModule ModuleLoaderBase::gModuleLoaderBaseLog(
44 "ModuleLoaderBase");
46 #undef LOG
47 #define LOG(args) \
48 MOZ_LOG(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug, \
49 args)
51 #define LOG_ENABLED() \
52 MOZ_LOG_TEST(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug)
54 //////////////////////////////////////////////////////////////
55 // ModuleLoaderBase::WaitingRequests
56 //////////////////////////////////////////////////////////////
58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase::WaitingRequests)
59 NS_INTERFACE_MAP_ENTRY(nsISupports)
60 NS_INTERFACE_MAP_END
62 NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase::WaitingRequests, mWaiting)
64 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase::WaitingRequests)
65 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase::WaitingRequests)
67 //////////////////////////////////////////////////////////////
68 // ModuleLoaderBase
69 //////////////////////////////////////////////////////////////
71 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase)
72 NS_INTERFACE_MAP_ENTRY(nsISupports)
73 NS_INTERFACE_MAP_END
75 NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase, mFetchingModules, mFetchedModules,
76 mDynamicImportRequests, mGlobalObject, mEventTarget,
77 mLoader)
79 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase)
80 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase)
82 // static
83 void ModuleLoaderBase::EnsureModuleHooksInitialized() {
84 AutoJSAPI jsapi;
85 jsapi.Init();
86 JSRuntime* rt = JS_GetRuntime(jsapi.cx());
87 if (JS::GetModuleResolveHook(rt)) {
88 return;
91 JS::SetModuleResolveHook(rt, HostResolveImportedModule);
92 JS::SetModuleMetadataHook(rt, HostPopulateImportMeta);
93 JS::SetScriptPrivateReferenceHooks(rt, HostAddRefTopLevelScript,
94 HostReleaseTopLevelScript);
95 JS::SetModuleDynamicImportHook(rt, HostImportModuleDynamically);
97 JS::ImportAssertionVector assertions;
98 // ImportAssertionVector has inline storage for one element so this cannot
99 // fail.
100 MOZ_ALWAYS_TRUE(assertions.reserve(1));
101 assertions.infallibleAppend(JS::ImportAssertion::Type);
102 JS::SetSupportedImportAssertions(rt, assertions);
105 // 8.1.3.8.1 HostResolveImportedModule(referencingModule, moduleRequest)
107 * Implement the HostResolveImportedModule abstract operation.
109 * Resolve a module specifier string and look this up in the module
110 * map, returning the result. This is only called for previously
111 * loaded modules and always succeeds.
113 * @param aReferencingPrivate A JS::Value which is either undefined
114 * or contains a LoadedScript private pointer.
115 * @param aModuleRequest A module request object.
116 * @returns module This is set to the module found.
118 // static
119 JSObject* ModuleLoaderBase::HostResolveImportedModule(
120 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
121 JS::Handle<JSObject*> aModuleRequest) {
122 JS::Rooted<JSObject*> module(aCx);
125 // LoadedScript should only live in this block, otherwise it will be a GC
126 // hazard
127 RefPtr<LoadedScript> script(
128 GetLoadedScriptOrNull(aCx, aReferencingPrivate));
130 JS::Rooted<JSString*> specifierString(
131 aCx, JS::GetModuleRequestSpecifier(aCx, aModuleRequest));
132 if (!specifierString) {
133 return nullptr;
136 // Let url be the result of resolving a module specifier given referencing
137 // module script and specifier.
138 nsAutoJSString string;
139 if (!string.init(aCx, specifierString)) {
140 return nullptr;
143 RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
144 if (!loader) {
145 return nullptr;
148 auto result = loader->ResolveModuleSpecifier(script, string);
149 // This cannot fail because resolving a module specifier must have been
150 // previously successful with these same two arguments.
151 MOZ_ASSERT(result.isOk());
152 nsCOMPtr<nsIURI> uri = result.unwrap();
153 MOZ_ASSERT(uri, "Failed to resolve previously-resolved module specifier");
155 // Let resolved module script be moduleMap[url]. (This entry must exist for
156 // us to have gotten to this point.)
157 ModuleScript* ms = loader->GetFetchedModule(uri);
158 MOZ_ASSERT(ms, "Resolved module not found in module map");
159 MOZ_ASSERT(!ms->HasParseError());
160 MOZ_ASSERT(ms->ModuleRecord());
162 module.set(ms->ModuleRecord());
164 return module;
167 // static
168 bool ModuleLoaderBase::ImportMetaResolve(JSContext* cx, unsigned argc,
169 Value* vp) {
170 CallArgs args = CallArgsFromVp(argc, vp);
171 RootedValue modulePrivate(
172 cx, js::GetFunctionNativeReserved(&args.callee(), ModulePrivateSlot));
174 // https://html.spec.whatwg.org/#hostgetimportmetaproperties
175 // Step 4.1. Set specifier to ? ToString(specifier).
177 // https://tc39.es/ecma262/#sec-tostring
178 RootedValue v(cx, args.get(ImportMetaResolveSpecifierArg));
179 RootedString specifier(cx, JS::ToString(cx, v));
180 if (!specifier) {
181 return false;
184 // Step 4.2, 4.3 are implemented in ImportMetaResolveImpl.
185 RootedString url(cx, ImportMetaResolveImpl(cx, modulePrivate, specifier));
186 if (!url) {
187 return false;
190 // Step 4.4. Return the serialization of url.
191 args.rval().setString(url);
192 return true;
195 // static
196 JSString* ModuleLoaderBase::ImportMetaResolveImpl(
197 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
198 JS::Handle<JSString*> aSpecifier) {
199 RootedString urlString(aCx);
202 // ModuleScript should only live in this block, otherwise it will be a GC
203 // hazard
204 RefPtr<ModuleScript> script =
205 static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
206 MOZ_ASSERT(script->IsModuleScript());
207 MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
208 aReferencingPrivate);
210 RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
211 if (!loader) {
212 return nullptr;
215 nsAutoJSString specifier;
216 if (!specifier.init(aCx, aSpecifier)) {
217 return nullptr;
220 auto result = loader->ResolveModuleSpecifier(script, specifier);
221 if (result.isErr()) {
222 JS::Rooted<JS::Value> error(aCx);
223 nsresult rv = loader->HandleResolveFailure(
224 aCx, script, specifier, result.unwrapErr(), 0, 0, &error);
225 if (NS_FAILED(rv)) {
226 JS_ReportOutOfMemory(aCx);
227 return nullptr;
230 JS_SetPendingException(aCx, error);
232 return nullptr;
235 nsCOMPtr<nsIURI> uri = result.unwrap();
236 nsAutoCString url;
237 MOZ_ALWAYS_SUCCEEDS(uri->GetAsciiSpec(url));
239 urlString.set(JS_NewStringCopyZ(aCx, url.get()));
242 return urlString;
245 // static
246 bool ModuleLoaderBase::HostPopulateImportMeta(
247 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
248 JS::Handle<JSObject*> aMetaObject) {
249 RefPtr<ModuleScript> script =
250 static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
251 MOZ_ASSERT(script->IsModuleScript());
252 MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
253 aReferencingPrivate);
255 nsAutoCString url;
256 MOZ_DIAGNOSTIC_ASSERT(script->BaseURL());
257 MOZ_ALWAYS_SUCCEEDS(script->BaseURL()->GetAsciiSpec(url));
259 JS::Rooted<JSString*> urlString(aCx, JS_NewStringCopyZ(aCx, url.get()));
260 if (!urlString) {
261 JS_ReportOutOfMemory(aCx);
262 return false;
265 // https://html.spec.whatwg.org/#import-meta-url
266 if (!JS_DefineProperty(aCx, aMetaObject, "url", urlString,
267 JSPROP_ENUMERATE)) {
268 return false;
271 // https://html.spec.whatwg.org/#import-meta-resolve
272 // Define 'resolve' function on the import.meta object.
273 JSFunction* resolveFunc = js::DefineFunctionWithReserved(
274 aCx, aMetaObject, "resolve", ImportMetaResolve, ImportMetaResolveNumArgs,
275 JSPROP_ENUMERATE);
276 if (!resolveFunc) {
277 return false;
280 // Store the 'active script' of the meta object into the function slot.
281 // https://html.spec.whatwg.org/#active-script
282 RootedObject resolveFuncObj(aCx, JS_GetFunctionObject(resolveFunc));
283 js::SetFunctionNativeReserved(resolveFuncObj, ModulePrivateSlot,
284 aReferencingPrivate);
286 return true;
289 // static
290 bool ModuleLoaderBase::HostImportModuleDynamically(
291 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
292 JS::Handle<JSObject*> aModuleRequest, JS::Handle<JSObject*> aPromise) {
293 MOZ_DIAGNOSTIC_ASSERT(aModuleRequest);
294 MOZ_DIAGNOSTIC_ASSERT(aPromise);
296 RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aCx, aReferencingPrivate));
298 JS::Rooted<JSString*> specifierString(
299 aCx, JS::GetModuleRequestSpecifier(aCx, aModuleRequest));
300 if (!specifierString) {
301 return false;
304 // Attempt to resolve the module specifier.
305 nsAutoJSString specifier;
306 if (!specifier.init(aCx, specifierString)) {
307 return false;
310 RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
311 if (!loader) {
312 return false;
315 auto result = loader->ResolveModuleSpecifier(script, specifier);
316 if (result.isErr()) {
317 JS::Rooted<JS::Value> error(aCx);
318 nsresult rv = loader->HandleResolveFailure(
319 aCx, script, specifier, result.unwrapErr(), 0, 0, &error);
320 if (NS_FAILED(rv)) {
321 JS_ReportOutOfMemory(aCx);
322 return false;
325 JS_SetPendingException(aCx, error);
326 return false;
329 // Create a new top-level load request.
330 nsCOMPtr<nsIURI> uri = result.unwrap();
331 RefPtr<ModuleLoadRequest> request = loader->CreateDynamicImport(
332 aCx, uri, script, aReferencingPrivate, specifierString, aPromise);
334 if (!request) {
335 // Throws TypeError if CreateDynamicImport returns nullptr.
336 JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
337 JSMSG_DYNAMIC_IMPORT_NOT_SUPPORTED);
339 return false;
342 loader->StartDynamicImport(request);
343 return true;
346 // static
347 ModuleLoaderBase* ModuleLoaderBase::GetCurrentModuleLoader(JSContext* aCx) {
348 auto reportError = mozilla::MakeScopeExit([aCx]() {
349 JS_ReportErrorASCII(aCx, "No ScriptLoader found for the current context");
352 JS::Rooted<JSObject*> object(aCx, JS::CurrentGlobalOrNull(aCx));
353 if (!object) {
354 return nullptr;
357 nsIGlobalObject* global = xpc::NativeGlobal(object);
358 if (!global) {
359 return nullptr;
362 ModuleLoaderBase* loader = global->GetModuleLoader(aCx);
363 if (!loader) {
364 return nullptr;
367 MOZ_ASSERT(loader->mGlobalObject == global);
369 reportError.release();
370 return loader;
373 // static
374 LoadedScript* ModuleLoaderBase::GetLoadedScriptOrNull(
375 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate) {
376 if (aReferencingPrivate.isUndefined()) {
377 return nullptr;
380 auto* script = static_cast<LoadedScript*>(aReferencingPrivate.toPrivate());
381 if (script->IsEventScript()) {
382 return nullptr;
385 MOZ_ASSERT_IF(
386 script->IsModuleScript(),
387 JS::GetModulePrivate(script->AsModuleScript()->ModuleRecord()) ==
388 aReferencingPrivate);
390 return script;
393 nsresult ModuleLoaderBase::StartModuleLoad(ModuleLoadRequest* aRequest) {
394 return StartOrRestartModuleLoad(aRequest, RestartRequest::No);
397 nsresult ModuleLoaderBase::RestartModuleLoad(ModuleLoadRequest* aRequest) {
398 return StartOrRestartModuleLoad(aRequest, RestartRequest::Yes);
401 nsresult ModuleLoaderBase::StartOrRestartModuleLoad(ModuleLoadRequest* aRequest,
402 RestartRequest aRestart) {
403 MOZ_ASSERT(aRequest->mLoader == this);
404 MOZ_ASSERT(aRequest->IsFetching());
406 aRequest->SetUnknownDataType();
408 // If we're restarting the request, the module should already be in the
409 // "fetching" map.
410 MOZ_ASSERT_IF(aRestart == RestartRequest::Yes,
411 IsModuleFetching(aRequest->mURI));
413 // Check with the derived class whether we should load this module.
414 nsresult rv = NS_OK;
415 if (!CanStartLoad(aRequest, &rv)) {
416 return rv;
419 // Check whether the module has been fetched or is currently being fetched,
420 // and if so wait for it rather than starting a new fetch.
421 ModuleLoadRequest* request = aRequest->AsModuleRequest();
423 if (aRestart == RestartRequest::No && ModuleMapContainsURL(request->mURI)) {
424 LOG(("ScriptLoadRequest (%p): Waiting for module fetch", aRequest));
425 WaitForModuleFetch(request);
426 return NS_OK;
429 rv = StartFetch(aRequest);
430 NS_ENSURE_SUCCESS(rv, rv);
432 // We successfully started fetching a module so put its URL in the module
433 // map and mark it as fetching.
434 if (aRestart == RestartRequest::No) {
435 SetModuleFetchStarted(aRequest->AsModuleRequest());
438 return NS_OK;
441 bool ModuleLoaderBase::ModuleMapContainsURL(nsIURI* aURL) const {
442 return IsModuleFetching(aURL) || IsModuleFetched(aURL);
445 bool ModuleLoaderBase::IsModuleFetching(nsIURI* aURL) const {
446 return mFetchingModules.Contains(aURL);
449 bool ModuleLoaderBase::IsModuleFetched(nsIURI* aURL) const {
450 return mFetchedModules.Contains(aURL);
453 nsresult ModuleLoaderBase::GetFetchedModuleURLs(nsTArray<nsCString>& aURLs) {
454 for (const auto& entry : mFetchedModules) {
455 nsIURI* uri = entry.GetData()->BaseURL();
457 nsAutoCString spec;
458 nsresult rv = uri->GetSpec(spec);
459 NS_ENSURE_SUCCESS(rv, rv);
461 aURLs.AppendElement(spec);
464 return NS_OK;
467 bool ModuleLoaderBase::RemoveFetchedModule(nsIURI* aURL) {
468 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
469 RefPtr<ModuleScript> ms;
470 MOZ_ALWAYS_TRUE(mFetchedModules.Get(aURL, getter_AddRefs(ms)));
471 if (ms && ms->ModuleRecord()) {
472 JS::AssertModuleUnlinked(ms->ModuleRecord());
474 #endif
476 return mFetchedModules.Remove(aURL);
479 void ModuleLoaderBase::SetModuleFetchStarted(ModuleLoadRequest* aRequest) {
480 // Update the module map to indicate that a module is currently being fetched.
482 MOZ_ASSERT(aRequest->IsFetching());
483 MOZ_ASSERT(!ModuleMapContainsURL(aRequest->mURI));
485 mFetchingModules.InsertOrUpdate(aRequest->mURI, nullptr);
488 void ModuleLoaderBase::SetModuleFetchFinishedAndResumeWaitingRequests(
489 ModuleLoadRequest* aRequest, nsresult aResult) {
490 // Update module map with the result of fetching a single module script.
492 // If any requests for the same URL are waiting on this one to complete, call
493 // ModuleLoaded or LoadFailed to resume or fail them as appropriate.
495 MOZ_ASSERT(aRequest->mLoader == this);
497 LOG(
498 ("ScriptLoadRequest (%p): Module fetch finished (script == %p, result == "
499 "%u)",
500 aRequest, aRequest->mModuleScript.get(), unsigned(aResult)));
502 RefPtr<WaitingRequests> waitingRequests;
503 if (!mFetchingModules.Remove(aRequest->mURI,
504 getter_AddRefs(waitingRequests))) {
505 LOG(
506 ("ScriptLoadRequest (%p): Key not found in mFetchingModules, "
507 "assuming we have an inline module or have finished fetching already",
508 aRequest));
509 return;
512 RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript);
513 MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript);
515 mFetchedModules.InsertOrUpdate(aRequest->mURI, RefPtr{moduleScript});
517 if (waitingRequests) {
518 LOG(("ScriptLoadRequest (%p): Resuming waiting requests", aRequest));
519 ResumeWaitingRequests(waitingRequests, bool(moduleScript));
523 void ModuleLoaderBase::ResumeWaitingRequests(WaitingRequests* aWaitingRequests,
524 bool aSuccess) {
525 for (ModuleLoadRequest* request : aWaitingRequests->mWaiting) {
526 ResumeWaitingRequest(request, aSuccess);
530 void ModuleLoaderBase::ResumeWaitingRequest(ModuleLoadRequest* aRequest,
531 bool aSuccess) {
532 if (aSuccess) {
533 aRequest->ModuleLoaded();
534 } else {
535 aRequest->LoadFailed();
539 void ModuleLoaderBase::WaitForModuleFetch(ModuleLoadRequest* aRequest) {
540 nsIURI* uri = aRequest->mURI;
541 MOZ_ASSERT(ModuleMapContainsURL(uri));
543 if (auto entry = mFetchingModules.Lookup(uri)) {
544 RefPtr<WaitingRequests> waitingRequests = entry.Data();
545 if (!waitingRequests) {
546 waitingRequests = new WaitingRequests();
547 mFetchingModules.InsertOrUpdate(uri, waitingRequests);
550 waitingRequests->mWaiting.AppendElement(aRequest);
551 return;
554 RefPtr<ModuleScript> ms;
555 MOZ_ALWAYS_TRUE(mFetchedModules.Get(uri, getter_AddRefs(ms)));
557 ResumeWaitingRequest(aRequest, bool(ms));
560 ModuleScript* ModuleLoaderBase::GetFetchedModule(nsIURI* aURL) const {
561 if (LOG_ENABLED()) {
562 nsAutoCString url;
563 aURL->GetAsciiSpec(url);
564 LOG(("GetFetchedModule %s", url.get()));
567 bool found;
568 ModuleScript* ms = mFetchedModules.GetWeak(aURL, &found);
569 MOZ_ASSERT(found);
570 return ms;
573 nsresult ModuleLoaderBase::OnFetchComplete(ModuleLoadRequest* aRequest,
574 nsresult aRv) {
575 MOZ_ASSERT(aRequest->mLoader == this);
576 MOZ_ASSERT(!aRequest->mModuleScript);
578 nsresult rv = aRv;
579 if (NS_SUCCEEDED(rv)) {
580 rv = CreateModuleScript(aRequest);
582 // If a module script was created, it should either have a module record
583 // object or a parse error.
584 if (ModuleScript* ms = aRequest->mModuleScript) {
585 MOZ_DIAGNOSTIC_ASSERT(bool(ms->ModuleRecord()) != ms->HasParseError());
588 aRequest->ClearScriptSource();
590 if (NS_FAILED(rv)) {
591 aRequest->LoadFailed();
592 return rv;
596 MOZ_ASSERT(NS_SUCCEEDED(rv) == bool(aRequest->mModuleScript));
597 SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv);
599 if (!aRequest->IsErrored()) {
600 StartFetchingModuleDependencies(aRequest);
603 return NS_OK;
606 nsresult ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest* aRequest) {
607 MOZ_ASSERT(!aRequest->mModuleScript);
608 MOZ_ASSERT(aRequest->mBaseURL);
610 LOG(("ScriptLoadRequest (%p): Create module script", aRequest));
612 AutoJSAPI jsapi;
613 if (!jsapi.Init(mGlobalObject)) {
614 return NS_ERROR_FAILURE;
617 nsresult rv;
619 JSContext* cx = jsapi.cx();
620 JS::Rooted<JSObject*> module(cx);
622 JS::CompileOptions options(cx);
623 JS::RootedScript introductionScript(cx);
624 rv = mLoader->FillCompileOptionsForRequest(cx, aRequest, &options,
625 &introductionScript);
627 if (NS_SUCCEEDED(rv)) {
628 JS::Rooted<JSObject*> global(cx, mGlobalObject->GetGlobalJSObject());
629 rv = CompileFetchedModule(cx, global, options, aRequest, &module);
632 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
634 if (module) {
635 JS::RootedValue privateValue(cx);
636 JS::RootedScript moduleScript(cx, JS::GetModuleScript(module));
637 JS::InstantiateOptions instantiateOptions(options);
638 if (!JS::UpdateDebugMetadata(cx, moduleScript, instantiateOptions,
639 privateValue, nullptr, introductionScript,
640 nullptr)) {
641 return NS_ERROR_OUT_OF_MEMORY;
645 RefPtr<ModuleScript> moduleScript =
646 new ModuleScript(aRequest->mFetchOptions, aRequest->mBaseURL);
647 aRequest->mModuleScript = moduleScript;
649 if (!module) {
650 LOG(("ScriptLoadRequest (%p): compilation failed (%d)", aRequest,
651 unsigned(rv)));
653 JS::Rooted<JS::Value> error(cx);
654 if (!jsapi.HasException() || !jsapi.StealException(&error) ||
655 error.isUndefined()) {
656 aRequest->mModuleScript = nullptr;
657 return NS_ERROR_FAILURE;
660 moduleScript->SetParseError(error);
661 aRequest->ModuleErrored();
662 return NS_OK;
665 moduleScript->SetModuleRecord(module);
667 // Validate requested modules and treat failure to resolve module specifiers
668 // the same as a parse error.
669 rv = ResolveRequestedModules(aRequest, nullptr);
670 if (NS_FAILED(rv)) {
671 if (!aRequest->IsErrored()) {
672 aRequest->mModuleScript = nullptr;
673 return rv;
675 aRequest->ModuleErrored();
676 return NS_OK;
680 LOG(("ScriptLoadRequest (%p): module script == %p", aRequest,
681 aRequest->mModuleScript.get()));
683 return rv;
686 nsresult ModuleLoaderBase::GetResolveFailureMessage(ResolveError aError,
687 const nsAString& aSpecifier,
688 nsAString& aResult) {
689 AutoTArray<nsString, 1> errorParams;
690 errorParams.AppendElement(aSpecifier);
692 nsresult rv = nsContentUtils::FormatLocalizedString(
693 nsContentUtils::eDOM_PROPERTIES, ResolveErrorInfo::GetString(aError),
694 errorParams, aResult);
695 NS_ENSURE_SUCCESS(rv, rv);
696 return NS_OK;
699 nsresult ModuleLoaderBase::HandleResolveFailure(
700 JSContext* aCx, LoadedScript* aScript, const nsAString& aSpecifier,
701 ResolveError aError, uint32_t aLineNumber, uint32_t aColumnNumber,
702 JS::MutableHandle<JS::Value> aErrorOut) {
703 JS::Rooted<JSString*> filename(aCx);
704 if (aScript) {
705 nsAutoCString url;
706 aScript->BaseURL()->GetAsciiSpec(url);
707 filename = JS_NewStringCopyZ(aCx, url.get());
708 } else {
709 filename = JS_NewStringCopyZ(aCx, "(unknown)");
712 if (!filename) {
713 return NS_ERROR_OUT_OF_MEMORY;
716 nsAutoString errorText;
717 nsresult rv = GetResolveFailureMessage(aError, aSpecifier, errorText);
718 NS_ENSURE_SUCCESS(rv, rv);
720 JS::Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, errorText.get()));
721 if (!string) {
722 return NS_ERROR_OUT_OF_MEMORY;
725 if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
726 aColumnNumber, nullptr, string, JS::NothingHandleValue,
727 aErrorOut)) {
728 return NS_ERROR_OUT_OF_MEMORY;
731 return NS_OK;
734 // Helper for getting import maps pref across main thread and workers
735 bool ImportMapsEnabled() {
736 if (NS_IsMainThread()) {
737 return mozilla::StaticPrefs::dom_importMaps_enabled();
739 return false;
742 ResolveResult ModuleLoaderBase::ResolveModuleSpecifier(
743 LoadedScript* aScript, const nsAString& aSpecifier) {
744 // If import map is enabled, forward to the updated 'Resolve a module
745 // specifier' algorithm defined in Import maps spec.
747 // Once import map is enabled by default,
748 // ModuleLoaderBase::ResolveModuleSpecifier should be replaced by
749 // ImportMap::ResolveModuleSpecifier.
750 if (ImportMapsEnabled()) {
751 return ImportMap::ResolveModuleSpecifier(mImportMap.get(), mLoader, aScript,
752 aSpecifier);
755 // The following module specifiers are allowed by the spec:
756 // - a valid absolute URL
757 // - a valid relative URL that starts with "/", "./" or "../"
759 // Bareword module specifiers are handled in Import maps.
761 nsCOMPtr<nsIURI> uri;
762 nsresult rv = NS_NewURI(getter_AddRefs(uri), aSpecifier);
763 if (NS_SUCCEEDED(rv)) {
764 return WrapNotNull(uri);
767 if (rv != NS_ERROR_MALFORMED_URI) {
768 return Err(ResolveError::Failure);
771 if (!StringBeginsWith(aSpecifier, u"/"_ns) &&
772 !StringBeginsWith(aSpecifier, u"./"_ns) &&
773 !StringBeginsWith(aSpecifier, u"../"_ns)) {
774 return Err(ResolveError::FailureMayBeBare);
777 // Get the document's base URL if we don't have a referencing script here.
778 nsCOMPtr<nsIURI> baseURL;
779 if (aScript) {
780 baseURL = aScript->BaseURL();
781 } else {
782 baseURL = GetBaseURI();
785 rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, baseURL);
786 if (NS_SUCCEEDED(rv)) {
787 return WrapNotNull(uri);
790 return Err(ResolveError::Failure);
793 nsresult ModuleLoaderBase::ResolveRequestedModules(
794 ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOut) {
795 ModuleScript* ms = aRequest->mModuleScript;
797 AutoJSAPI jsapi;
798 if (!jsapi.Init(mGlobalObject)) {
799 return NS_ERROR_FAILURE;
802 JSContext* cx = jsapi.cx();
803 JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord());
804 uint32_t length = JS::GetRequestedModulesCount(cx, moduleRecord);
806 for (uint32_t i = 0; i < length; i++) {
807 JS::Rooted<JSString*> str(
808 cx, JS::GetRequestedModuleSpecifier(cx, moduleRecord, i));
809 MOZ_ASSERT(str);
811 nsAutoJSString specifier;
812 if (!specifier.init(cx, str)) {
813 return NS_ERROR_FAILURE;
816 // Let url be the result of resolving a module specifier given module script
817 // and requested.
818 ModuleLoaderBase* loader = aRequest->mLoader;
819 auto result = loader->ResolveModuleSpecifier(ms, specifier);
820 if (result.isErr()) {
821 uint32_t lineNumber = 0;
822 uint32_t columnNumber = 0;
823 JS::GetRequestedModuleSourcePos(cx, moduleRecord, i, &lineNumber,
824 &columnNumber);
826 JS::Rooted<JS::Value> error(cx);
827 nsresult rv =
828 loader->HandleResolveFailure(cx, ms, specifier, result.unwrapErr(),
829 lineNumber, columnNumber, &error);
830 NS_ENSURE_SUCCESS(rv, rv);
832 ms->SetParseError(error);
833 return NS_ERROR_FAILURE;
836 nsCOMPtr<nsIURI> uri = result.unwrap();
837 if (aUrlsOut) {
838 aUrlsOut->AppendElement(uri.forget());
842 return NS_OK;
845 void ModuleLoaderBase::StartFetchingModuleDependencies(
846 ModuleLoadRequest* aRequest) {
847 LOG(("ScriptLoadRequest (%p): Start fetching module dependencies", aRequest));
849 if (aRequest->IsCanceled()) {
850 return;
853 MOZ_ASSERT(aRequest->mModuleScript);
854 MOZ_ASSERT(!aRequest->mModuleScript->HasParseError());
855 MOZ_ASSERT(aRequest->IsFetching() || aRequest->IsCompiling());
857 auto visitedSet = aRequest->mVisitedSet;
858 MOZ_ASSERT(visitedSet->Contains(aRequest->mURI));
860 aRequest->mState = ModuleLoadRequest::State::LoadingImports;
862 nsCOMArray<nsIURI> urls;
863 nsresult rv = ResolveRequestedModules(aRequest, &urls);
864 if (NS_FAILED(rv)) {
865 aRequest->mModuleScript = nullptr;
866 aRequest->ModuleErrored();
867 return;
870 // Remove already visited URLs from the list. Put unvisited URLs into the
871 // visited set.
872 int32_t i = 0;
873 while (i < urls.Count()) {
874 nsIURI* url = urls[i];
875 if (visitedSet->Contains(url)) {
876 urls.RemoveObjectAt(i);
877 } else {
878 visitedSet->PutEntry(url);
879 i++;
883 if (urls.Count() == 0) {
884 // There are no descendants to load so this request is ready.
885 aRequest->DependenciesLoaded();
886 return;
889 MOZ_ASSERT(aRequest->mAwaitingImports == 0);
890 aRequest->mAwaitingImports = urls.Count();
892 // For each url in urls, fetch a module script graph given url, module
893 // script's CORS setting, and module script's settings object.
894 for (auto* url : urls) {
895 StartFetchingModuleAndDependencies(aRequest, url);
899 void ModuleLoaderBase::StartFetchingModuleAndDependencies(
900 ModuleLoadRequest* aParent, nsIURI* aURI) {
901 MOZ_ASSERT(aURI);
903 RefPtr<ModuleLoadRequest> childRequest = CreateStaticImport(aURI, aParent);
905 aParent->mImports.AppendElement(childRequest);
907 if (LOG_ENABLED()) {
908 nsAutoCString url1;
909 aParent->mURI->GetAsciiSpec(url1);
911 nsAutoCString url2;
912 aURI->GetAsciiSpec(url2);
914 LOG(("ScriptLoadRequest (%p): Start fetching dependency %p", aParent,
915 childRequest.get()));
916 LOG(("StartFetchingModuleAndDependencies \"%s\" -> \"%s\"", url1.get(),
917 url2.get()));
920 MOZ_ASSERT(!childRequest->mWaitingParentRequest);
921 childRequest->mWaitingParentRequest = aParent;
923 nsresult rv = StartModuleLoad(childRequest);
924 if (NS_FAILED(rv)) {
925 MOZ_ASSERT(!childRequest->mModuleScript);
926 LOG(("ScriptLoadRequest (%p): rejecting %p", aParent,
927 childRequest.get()));
929 mLoader->ReportErrorToConsole(childRequest, rv);
930 childRequest->LoadFailed();
934 void ModuleLoadRequest::ChildLoadComplete(bool aSuccess) {
935 RefPtr<ModuleLoadRequest> parent = mWaitingParentRequest;
936 MOZ_ASSERT(parent);
937 MOZ_ASSERT(parent->mAwaitingImports != 0);
939 mWaitingParentRequest = nullptr;
940 parent->mAwaitingImports--;
942 if (parent->IsFinished()) {
943 MOZ_ASSERT_IF(!aSuccess, parent->IsErrored());
944 return;
947 if (!aSuccess) {
948 parent->ModuleErrored();
949 } else if (parent->mAwaitingImports == 0) {
950 parent->DependenciesLoaded();
954 void ModuleLoaderBase::StartDynamicImport(ModuleLoadRequest* aRequest) {
955 MOZ_ASSERT(aRequest->mLoader == this);
957 LOG(("ScriptLoadRequest (%p): Start dynamic import", aRequest));
959 mDynamicImportRequests.AppendElement(aRequest);
961 nsresult rv = StartModuleLoad(aRequest);
962 if (NS_FAILED(rv)) {
963 mLoader->ReportErrorToConsole(aRequest, rv);
964 FinishDynamicImportAndReject(aRequest, rv);
968 void ModuleLoaderBase::FinishDynamicImportAndReject(ModuleLoadRequest* aRequest,
969 nsresult aResult) {
970 AutoJSAPI jsapi;
971 MOZ_ASSERT(NS_FAILED(aResult));
972 if (!jsapi.Init(mGlobalObject)) {
973 return;
976 FinishDynamicImport(jsapi.cx(), aRequest, aResult, nullptr);
979 /* static */
980 void ModuleLoaderBase::FinishDynamicImport(
981 JSContext* aCx, ModuleLoadRequest* aRequest, nsresult aResult,
982 JS::Handle<JSObject*> aEvaluationPromise) {
983 LOG(("ScriptLoadRequest (%p): Finish dynamic import %x %d", aRequest,
984 unsigned(aResult), JS_IsExceptionPending(aCx)));
986 MOZ_ASSERT(GetCurrentModuleLoader(aCx) == aRequest->mLoader);
988 // If aResult is a failed result, we don't have an EvaluationPromise. If it
989 // succeeded, evaluationPromise may still be null, but in this case it will
990 // be handled by rejecting the dynamic module import promise in the JSAPI.
991 MOZ_ASSERT_IF(NS_FAILED(aResult), !aEvaluationPromise);
993 // Complete the dynamic import, report failures indicated by aResult or as a
994 // pending exception on the context.
996 if (!aRequest->mDynamicPromise) {
997 // Import has already been completed.
998 return;
1001 if (NS_FAILED(aResult) &&
1002 aResult != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
1003 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
1004 nsAutoCString url;
1005 aRequest->mURI->GetSpec(url);
1006 JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
1007 JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
1010 JS::Rooted<JS::Value> referencingScript(aCx,
1011 aRequest->mDynamicReferencingPrivate);
1012 JS::Rooted<JSString*> specifier(aCx, aRequest->mDynamicSpecifier);
1013 JS::Rooted<JSObject*> promise(aCx, aRequest->mDynamicPromise);
1015 JS::Rooted<JSObject*> moduleRequest(aCx,
1016 JS::CreateModuleRequest(aCx, specifier));
1018 JS::FinishDynamicModuleImport(aCx, aEvaluationPromise, referencingScript,
1019 moduleRequest, promise);
1021 // FinishDynamicModuleImport clears any pending exception.
1022 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
1024 aRequest->ClearDynamicImport();
1027 ModuleLoaderBase::ModuleLoaderBase(ScriptLoaderInterface* aLoader,
1028 nsIGlobalObject* aGlobalObject,
1029 nsISerialEventTarget* aEventTarget)
1030 : mGlobalObject(aGlobalObject),
1031 mEventTarget(aEventTarget),
1032 mLoader(aLoader) {
1033 MOZ_ASSERT(mGlobalObject);
1034 MOZ_ASSERT(mEventTarget);
1035 MOZ_ASSERT(mLoader);
1037 EnsureModuleHooksInitialized();
1040 ModuleLoaderBase::~ModuleLoaderBase() {
1041 mDynamicImportRequests.CancelRequestsAndClear();
1043 LOG(("ModuleLoaderBase::~ModuleLoaderBase %p", this));
1046 void ModuleLoaderBase::Shutdown() {
1047 CancelAndClearDynamicImports();
1049 for (const auto& entry : mFetchingModules) {
1050 RefPtr<WaitingRequests> waitingRequests(entry.GetData());
1051 if (waitingRequests) {
1052 ResumeWaitingRequests(waitingRequests, false);
1056 for (const auto& entry : mFetchedModules) {
1057 if (entry.GetData()) {
1058 entry.GetData()->Shutdown();
1062 mFetchingModules.Clear();
1063 mFetchedModules.Clear();
1064 mGlobalObject = nullptr;
1065 mEventTarget = nullptr;
1066 mLoader = nullptr;
1069 bool ModuleLoaderBase::HasPendingDynamicImports() const {
1070 return !mDynamicImportRequests.isEmpty();
1073 void ModuleLoaderBase::CancelDynamicImport(ModuleLoadRequest* aRequest,
1074 nsresult aResult) {
1075 MOZ_ASSERT(aRequest->mLoader == this);
1077 RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
1078 if (!aRequest->IsCanceled()) {
1079 aRequest->Cancel();
1080 // FinishDynamicImport must happen exactly once for each dynamic import
1081 // request. If the load is aborted we do it when we remove the request
1082 // from mDynamicImportRequests.
1083 FinishDynamicImportAndReject(aRequest, aResult);
1087 void ModuleLoaderBase::RemoveDynamicImport(ModuleLoadRequest* aRequest) {
1088 MOZ_ASSERT(aRequest->IsDynamicImport());
1089 mDynamicImportRequests.Remove(aRequest);
1092 #ifdef DEBUG
1093 bool ModuleLoaderBase::HasDynamicImport(
1094 const ModuleLoadRequest* aRequest) const {
1095 MOZ_ASSERT(aRequest->mLoader == this);
1096 return mDynamicImportRequests.Contains(
1097 const_cast<ModuleLoadRequest*>(aRequest));
1099 #endif
1101 JS::Value ModuleLoaderBase::FindFirstParseError(ModuleLoadRequest* aRequest) {
1102 MOZ_ASSERT(aRequest);
1104 ModuleScript* moduleScript = aRequest->mModuleScript;
1105 MOZ_ASSERT(moduleScript);
1107 if (moduleScript->HasParseError()) {
1108 return moduleScript->ParseError();
1111 for (ModuleLoadRequest* childRequest : aRequest->mImports) {
1112 JS::Value error = FindFirstParseError(childRequest);
1113 if (!error.isUndefined()) {
1114 return error;
1118 return JS::UndefinedValue();
1121 bool ModuleLoaderBase::InstantiateModuleGraph(ModuleLoadRequest* aRequest) {
1122 // Instantiate a top-level module and record any error.
1124 MOZ_ASSERT(aRequest);
1125 MOZ_ASSERT(aRequest->mLoader == this);
1126 MOZ_ASSERT(aRequest->IsTopLevel());
1128 LOG(("ScriptLoadRequest (%p): Instantiate module graph", aRequest));
1130 AUTO_PROFILER_LABEL("ModuleLoaderBase::InstantiateModuleGraph", JS);
1132 ModuleScript* moduleScript = aRequest->mModuleScript;
1133 MOZ_ASSERT(moduleScript);
1135 JS::Value parseError = FindFirstParseError(aRequest);
1136 if (!parseError.isUndefined()) {
1137 moduleScript->SetErrorToRethrow(parseError);
1138 LOG(("ScriptLoadRequest (%p): found parse error", aRequest));
1139 return true;
1142 MOZ_ASSERT(moduleScript->ModuleRecord());
1144 AutoJSAPI jsapi;
1145 if (NS_WARN_IF(!jsapi.Init(mGlobalObject))) {
1146 return false;
1149 JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord());
1150 if (!xpc::Scriptability::AllowedIfExists(module)) {
1151 return true;
1154 if (!JS::ModuleLink(jsapi.cx(), module)) {
1155 LOG(("ScriptLoadRequest (%p): Instantiate failed", aRequest));
1156 MOZ_ASSERT(jsapi.HasException());
1157 JS::RootedValue exception(jsapi.cx());
1158 if (!jsapi.StealException(&exception)) {
1159 return false;
1161 MOZ_ASSERT(!exception.isUndefined());
1162 moduleScript->SetErrorToRethrow(exception);
1165 return true;
1168 nsresult ModuleLoaderBase::InitDebuggerDataForModuleGraph(
1169 JSContext* aCx, ModuleLoadRequest* aRequest) {
1170 // JS scripts can be associated with a DOM element for use by the debugger,
1171 // but preloading can cause scripts to be compiled before DOM script element
1172 // nodes have been created. This method ensures that this association takes
1173 // place before the first time a module script is run.
1175 MOZ_ASSERT(aRequest);
1177 ModuleScript* moduleScript = aRequest->mModuleScript;
1178 if (moduleScript->DebuggerDataInitialized()) {
1179 return NS_OK;
1182 for (ModuleLoadRequest* childRequest : aRequest->mImports) {
1183 nsresult rv = InitDebuggerDataForModuleGraph(aCx, childRequest);
1184 NS_ENSURE_SUCCESS(rv, rv);
1187 JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
1188 MOZ_ASSERT(module);
1190 // The script is now ready to be exposed to the debugger.
1191 JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(module));
1192 JS::ExposeScriptToDebugger(aCx, script);
1194 moduleScript->SetDebuggerDataInitialized();
1195 return NS_OK;
1198 void ModuleLoaderBase::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
1199 // Instantiate and evaluate the imported module.
1200 // See: https://tc39.es/ecma262/#sec-ContinueDynamicImport
1202 // Since this is specced as happening on promise resolution (step 8) this must
1203 // at least run as part of a microtask. We don't create the unobservable
1204 // promise.
1206 class DynamicImportMicroTask : public mozilla::MicroTaskRunnable {
1207 public:
1208 explicit DynamicImportMicroTask(ModuleLoadRequest* aRequest)
1209 : MicroTaskRunnable(), mRequest(aRequest) {}
1211 virtual void Run(mozilla::AutoSlowOperation& aAso) override {
1212 mRequest->mLoader->InstantiateAndEvaluateDynamicImport(mRequest);
1213 mRequest = nullptr;
1216 virtual bool Suppressed() override {
1217 return mRequest->mLoader->mGlobalObject->IsInSyncOperation();
1220 private:
1221 RefPtr<ModuleLoadRequest> mRequest;
1224 MOZ_ASSERT(aRequest->mLoader == this);
1226 if (!aRequest->mModuleScript) {
1227 FinishDynamicImportAndReject(aRequest, NS_ERROR_FAILURE);
1228 return;
1231 CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
1232 RefPtr<DynamicImportMicroTask> runnable =
1233 new DynamicImportMicroTask(aRequest);
1234 context->DispatchToMicroTask(do_AddRef(runnable));
1237 void ModuleLoaderBase::InstantiateAndEvaluateDynamicImport(
1238 ModuleLoadRequest* aRequest) {
1239 MOZ_ASSERT(aRequest->mModuleScript);
1241 if (!InstantiateModuleGraph(aRequest)) {
1242 aRequest->mModuleScript = nullptr;
1245 nsresult rv = NS_ERROR_FAILURE;
1246 if (aRequest->mModuleScript) {
1247 rv = EvaluateModule(aRequest);
1250 if (NS_FAILED(rv)) {
1251 FinishDynamicImportAndReject(aRequest, rv);
1255 nsresult ModuleLoaderBase::EvaluateModule(ModuleLoadRequest* aRequest) {
1256 MOZ_ASSERT(aRequest->mLoader == this);
1258 mozilla::nsAutoMicroTask mt;
1259 mozilla::dom::AutoEntryScript aes(mGlobalObject, "EvaluateModule",
1260 NS_IsMainThread());
1262 return EvaluateModuleInContext(aes.cx(), aRequest,
1263 JS::ReportModuleErrorsAsync);
1266 nsresult ModuleLoaderBase::EvaluateModuleInContext(
1267 JSContext* aCx, ModuleLoadRequest* aRequest,
1268 JS::ModuleErrorBehaviour errorBehaviour) {
1269 MOZ_ASSERT(aRequest->mLoader == this);
1270 MOZ_ASSERT(mGlobalObject->GetModuleLoader(aCx) == this);
1272 AUTO_PROFILER_LABEL("ModuleLoaderBase::EvaluateModule", JS);
1274 nsAutoCString profilerLabelString;
1275 if (aRequest->HasScriptLoadContext()) {
1276 aRequest->GetScriptLoadContext()->GetProfilerLabel(profilerLabelString);
1279 LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
1280 AUTO_PROFILER_MARKER_TEXT("ModuleEvaluation", JS,
1281 MarkerInnerWindowIdFromJSContext(aCx),
1282 profilerLabelString);
1284 ModuleLoadRequest* request = aRequest->AsModuleRequest();
1285 MOZ_ASSERT(request->mModuleScript);
1286 MOZ_ASSERT_IF(request->HasScriptLoadContext(),
1287 !request->GetScriptLoadContext()->mOffThreadToken);
1289 ModuleScript* moduleScript = request->mModuleScript;
1290 if (moduleScript->HasErrorToRethrow()) {
1291 LOG(("ScriptLoadRequest (%p): module has error to rethrow", aRequest));
1292 JS::Rooted<JS::Value> error(aCx, moduleScript->ErrorToRethrow());
1293 JS_SetPendingException(aCx, error);
1294 // For a dynamic import, the promise is rejected. Otherwise an error
1295 // is either reported by AutoEntryScript.
1296 if (request->IsDynamicImport()) {
1297 FinishDynamicImport(aCx, request, NS_OK, nullptr);
1299 return NS_OK;
1302 JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
1303 MOZ_ASSERT(module);
1304 MOZ_ASSERT(CurrentGlobalOrNull(aCx) == GetNonCCWObjectGlobal(module));
1306 if (!xpc::Scriptability::AllowedIfExists(module)) {
1307 return NS_OK;
1310 nsresult rv = InitDebuggerDataForModuleGraph(aCx, request);
1311 NS_ENSURE_SUCCESS(rv, rv);
1313 if (request->HasScriptLoadContext()) {
1314 TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
1315 "scriptloader_evaluate_module");
1318 JS::Rooted<JS::Value> rval(aCx);
1320 mLoader->MaybePrepareModuleForBytecodeEncodingBeforeExecute(aCx, request);
1322 bool ok = JS::ModuleEvaluate(aCx, module, &rval);
1324 // ModuleEvaluate will usually set a pending exception if it returns false,
1325 // unless the user cancels execution.
1326 MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aCx));
1328 if (!ok || IsModuleEvaluationAborted(request)) {
1329 LOG(("ScriptLoadRequest (%p): evaluation failed", aRequest));
1330 // For a dynamic import, the promise is rejected. Otherwise an error is
1331 // reported by AutoEntryScript.
1332 rv = NS_ERROR_ABORT;
1335 // ModuleEvaluate returns a promise unless the user cancels the execution in
1336 // which case rval will be undefined. We should treat it as a failed
1337 // evaluation, and reject appropriately.
1338 JS::Rooted<JSObject*> evaluationPromise(aCx);
1339 if (rval.isObject()) {
1340 evaluationPromise.set(&rval.toObject());
1343 if (request->IsDynamicImport()) {
1344 if (NS_FAILED(rv)) {
1345 FinishDynamicImportAndReject(request, rv);
1346 } else {
1347 FinishDynamicImport(aCx, request, NS_OK, evaluationPromise);
1349 } else {
1350 // If this is not a dynamic import, and if the promise is rejected,
1351 // the value is unwrapped from the promise value.
1352 if (!JS::ThrowOnModuleEvaluationFailure(aCx, evaluationPromise,
1353 errorBehaviour)) {
1354 LOG(("ScriptLoadRequest (%p): evaluation failed on throw", aRequest));
1355 // For a dynamic import, the promise is rejected. Otherwise an error is
1356 // reported by AutoEntryScript.
1360 rv = mLoader->MaybePrepareModuleForBytecodeEncodingAfterExecute(request,
1361 NS_OK);
1363 mLoader->MaybeTriggerBytecodeEncoding();
1365 return rv;
1368 void ModuleLoaderBase::CancelAndClearDynamicImports() {
1369 while (ScriptLoadRequest* req = mDynamicImportRequests.getFirst()) {
1370 // This also removes the request from the list.
1371 CancelDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
1375 UniquePtr<ImportMap> ModuleLoaderBase::ParseImportMap(
1376 ScriptLoadRequest* aRequest) {
1377 AutoJSAPI jsapi;
1378 if (!jsapi.Init(GetGlobalObject())) {
1379 return nullptr;
1382 MOZ_ASSERT(aRequest->IsTextSource());
1383 MaybeSourceText maybeSource;
1384 nsresult rv = aRequest->GetScriptSource(jsapi.cx(), &maybeSource);
1385 if (NS_FAILED(rv)) {
1386 return nullptr;
1389 JS::SourceText<char16_t>& text = maybeSource.ref<SourceText<char16_t>>();
1390 ReportWarningHelper warning{mLoader, aRequest};
1392 // https://html.spec.whatwg.org/multipage/webappapis.html#create-an-import-map-parse-result
1393 // Step 2. Parse an import map string given input and baseURL, catching any
1394 // exceptions. If this threw an exception, then set result's error to rethrow
1395 // to that exception. Otherwise, set result's import map to the return value.
1397 // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
1398 // Step 1. If result's error to rethrow is not null, then report the exception
1399 // given by result's error to rethrow and return.
1401 // Impl note: We didn't implement 'Import map parse result' from the spec,
1402 // https://html.spec.whatwg.org/multipage/webappapis.html#import-map-parse-result
1403 // As the struct has another item called 'error to rethow' to store the
1404 // exception thrown during parsing import-maps, and report that exception
1405 // while registering an import map. Currently only inline import-maps are
1406 // supported, therefore parsing and registering import-maps will be executed
1407 // consecutively. To simplify the implementation, we didn't create the 'error
1408 // to rethow' item and report the exception immediately(done in ~AutoJSAPI).
1409 return ImportMap::ParseString(jsapi.cx(), text, aRequest->mBaseURL, warning);
1412 void ModuleLoaderBase::RegisterImportMap(UniquePtr<ImportMap> aImportMap) {
1413 // Check for aImportMap is done in ScriptLoader.
1414 MOZ_ASSERT(aImportMap);
1416 // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
1417 // The step 1(report the exception if there's an error) is done in
1418 // ParseImportMap.
1420 // Step 2. Assert: global's import map is an empty import map.
1421 // Impl note: The default import map from the spec is an empty import map, but
1422 // from the implementation it defaults to nullptr, so we check if the global's
1423 // import map is null here.
1425 // Also see
1426 // https://html.spec.whatwg.org/multipage/webappapis.html#empty-import-map
1427 MOZ_ASSERT(!mImportMap);
1429 // Step 3. Set global's import map to result's import map.
1430 mImportMap = std::move(aImportMap);
1433 #undef LOG
1434 #undef LOG_ENABLED
1436 } // namespace JS::loader