Bug 1864587 - [angle] Vendor mozilla/angle/firefox-123. r=gfx-reviewers,aosmond
[gecko.git] / js / loader / ModuleLoaderBase.cpp
blob0f340d6aa1641977a93e1bb507853a343ec89001
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/ColumnNumber.h" // JS::ColumnNumberOneOrigin
17 #include "js/ContextOptions.h" // JS::ContextOptionsRef
18 #include "js/ErrorReport.h" // JSErrorBase
19 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
20 #include "js/Modules.h" // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
21 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetElement
22 #include "js/SourceText.h"
23 #include "mozilla/BasePrincipal.h"
24 #include "mozilla/dom/AutoEntryScript.h"
25 #include "mozilla/dom/ScriptLoadContext.h"
26 #include "mozilla/CycleCollectedJSContext.h" // nsAutoMicroTask
27 #include "mozilla/Preferences.h"
28 #include "mozilla/StaticPrefs_dom.h"
29 #include "nsContentUtils.h"
30 #include "nsICacheInfoChannel.h" // nsICacheInfoChannel
31 #include "nsNetUtil.h" // NS_NewURI
32 #include "xpcpublic.h"
34 using mozilla::CycleCollectedJSContext;
35 using mozilla::Err;
36 using mozilla::Preferences;
37 using mozilla::UniquePtr;
38 using mozilla::WrapNotNull;
39 using mozilla::dom::AutoJSAPI;
41 namespace JS::loader {
43 mozilla::LazyLogModule ModuleLoaderBase::gCspPRLog("CSP");
44 mozilla::LazyLogModule ModuleLoaderBase::gModuleLoaderBaseLog(
45 "ModuleLoaderBase");
47 #undef LOG
48 #define LOG(args) \
49 MOZ_LOG(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug, \
50 args)
52 #define LOG_ENABLED() \
53 MOZ_LOG_TEST(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug)
55 //////////////////////////////////////////////////////////////
56 // ModuleLoaderBase::WaitingRequests
57 //////////////////////////////////////////////////////////////
59 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase::WaitingRequests)
60 NS_INTERFACE_MAP_ENTRY(nsISupports)
61 NS_INTERFACE_MAP_END
63 NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase::WaitingRequests, mWaiting)
65 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase::WaitingRequests)
66 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase::WaitingRequests)
68 //////////////////////////////////////////////////////////////
69 // ModuleLoaderBase
70 //////////////////////////////////////////////////////////////
72 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase)
73 NS_INTERFACE_MAP_ENTRY(nsISupports)
74 NS_INTERFACE_MAP_END
76 NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase, mFetchingModules, mFetchedModules,
77 mDynamicImportRequests, mGlobalObject, mEventTarget,
78 mLoader)
80 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase)
81 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase)
83 // static
84 void ModuleLoaderBase::EnsureModuleHooksInitialized() {
85 AutoJSAPI jsapi;
86 jsapi.Init();
87 JSRuntime* rt = JS_GetRuntime(jsapi.cx());
88 if (JS::GetModuleResolveHook(rt)) {
89 return;
92 JS::SetModuleResolveHook(rt, HostResolveImportedModule);
93 JS::SetModuleMetadataHook(rt, HostPopulateImportMeta);
94 JS::SetScriptPrivateReferenceHooks(rt, HostAddRefTopLevelScript,
95 HostReleaseTopLevelScript);
96 JS::SetModuleDynamicImportHook(rt, HostImportModuleDynamically);
98 JS::ImportAssertionVector assertions;
99 // ImportAssertionVector has inline storage for one element so this cannot
100 // fail.
101 MOZ_ALWAYS_TRUE(assertions.reserve(1));
102 assertions.infallibleAppend(JS::ImportAssertion::Type);
103 JS::SetSupportedImportAssertions(rt, assertions);
106 // 8.1.3.8.1 HostResolveImportedModule(referencingModule, moduleRequest)
108 * Implement the HostResolveImportedModule abstract operation.
110 * Resolve a module specifier string and look this up in the module
111 * map, returning the result. This is only called for previously
112 * loaded modules and always succeeds.
114 * @param aReferencingPrivate A JS::Value which is either undefined
115 * or contains a LoadedScript private pointer.
116 * @param aModuleRequest A module request object.
117 * @returns module This is set to the module found.
119 // static
120 JSObject* ModuleLoaderBase::HostResolveImportedModule(
121 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
122 JS::Handle<JSObject*> aModuleRequest) {
123 JS::Rooted<JSObject*> module(aCx);
126 // LoadedScript should only live in this block, otherwise it will be a GC
127 // hazard
128 RefPtr<LoadedScript> script(
129 GetLoadedScriptOrNull(aCx, aReferencingPrivate));
131 JS::Rooted<JSString*> specifierString(
132 aCx, JS::GetModuleRequestSpecifier(aCx, aModuleRequest));
133 if (!specifierString) {
134 return nullptr;
137 // Let url be the result of resolving a module specifier given referencing
138 // module script and specifier.
139 nsAutoJSString string;
140 if (!string.init(aCx, specifierString)) {
141 return nullptr;
144 RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
145 if (!loader) {
146 return nullptr;
149 auto result = loader->ResolveModuleSpecifier(script, string);
150 // This cannot fail because resolving a module specifier must have been
151 // previously successful with these same two arguments.
152 MOZ_ASSERT(result.isOk());
153 nsCOMPtr<nsIURI> uri = result.unwrap();
154 MOZ_ASSERT(uri, "Failed to resolve previously-resolved module specifier");
156 // Let resolved module script be moduleMap[url]. (This entry must exist for
157 // us to have gotten to this point.)
158 ModuleScript* ms = loader->GetFetchedModule(uri);
159 MOZ_ASSERT(ms, "Resolved module not found in module map");
160 MOZ_ASSERT(!ms->HasParseError());
161 MOZ_ASSERT(ms->ModuleRecord());
163 module.set(ms->ModuleRecord());
165 return module;
168 // static
169 bool ModuleLoaderBase::ImportMetaResolve(JSContext* cx, unsigned argc,
170 Value* vp) {
171 CallArgs args = CallArgsFromVp(argc, vp);
172 RootedValue modulePrivate(
173 cx, js::GetFunctionNativeReserved(&args.callee(), ModulePrivateSlot));
175 // https://html.spec.whatwg.org/#hostgetimportmetaproperties
176 // Step 4.1. Set specifier to ? ToString(specifier).
178 // https://tc39.es/ecma262/#sec-tostring
179 RootedValue v(cx, args.get(ImportMetaResolveSpecifierArg));
180 RootedString specifier(cx, JS::ToString(cx, v));
181 if (!specifier) {
182 return false;
185 // Step 4.2, 4.3 are implemented in ImportMetaResolveImpl.
186 RootedString url(cx, ImportMetaResolveImpl(cx, modulePrivate, specifier));
187 if (!url) {
188 return false;
191 // Step 4.4. Return the serialization of url.
192 args.rval().setString(url);
193 return true;
196 // static
197 JSString* ModuleLoaderBase::ImportMetaResolveImpl(
198 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
199 JS::Handle<JSString*> aSpecifier) {
200 RootedString urlString(aCx);
203 // ModuleScript should only live in this block, otherwise it will be a GC
204 // hazard
205 RefPtr<ModuleScript> script =
206 static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
207 MOZ_ASSERT(script->IsModuleScript());
208 MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
209 aReferencingPrivate);
211 RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
212 if (!loader) {
213 return nullptr;
216 nsAutoJSString specifier;
217 if (!specifier.init(aCx, aSpecifier)) {
218 return nullptr;
221 auto result = loader->ResolveModuleSpecifier(script, specifier);
222 if (result.isErr()) {
223 JS::Rooted<JS::Value> error(aCx);
224 nsresult rv = loader->HandleResolveFailure(
225 aCx, script, specifier, result.unwrapErr(), 0,
226 JS::ColumnNumberOneOrigin(), &error);
227 if (NS_FAILED(rv)) {
228 JS_ReportOutOfMemory(aCx);
229 return nullptr;
232 JS_SetPendingException(aCx, error);
234 return nullptr;
237 nsCOMPtr<nsIURI> uri = result.unwrap();
238 nsAutoCString url;
239 MOZ_ALWAYS_SUCCEEDS(uri->GetAsciiSpec(url));
241 urlString.set(JS_NewStringCopyZ(aCx, url.get()));
244 return urlString;
247 // static
248 bool ModuleLoaderBase::HostPopulateImportMeta(
249 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
250 JS::Handle<JSObject*> aMetaObject) {
251 RefPtr<ModuleScript> script =
252 static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
253 MOZ_ASSERT(script->IsModuleScript());
254 MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
255 aReferencingPrivate);
257 nsAutoCString url;
258 MOZ_DIAGNOSTIC_ASSERT(script->BaseURL());
259 MOZ_ALWAYS_SUCCEEDS(script->BaseURL()->GetAsciiSpec(url));
261 JS::Rooted<JSString*> urlString(aCx, JS_NewStringCopyZ(aCx, url.get()));
262 if (!urlString) {
263 JS_ReportOutOfMemory(aCx);
264 return false;
267 // https://html.spec.whatwg.org/#import-meta-url
268 if (!JS_DefineProperty(aCx, aMetaObject, "url", urlString,
269 JSPROP_ENUMERATE)) {
270 return false;
273 // https://html.spec.whatwg.org/#import-meta-resolve
274 // Define 'resolve' function on the import.meta object.
275 JSFunction* resolveFunc = js::DefineFunctionWithReserved(
276 aCx, aMetaObject, "resolve", ImportMetaResolve, ImportMetaResolveNumArgs,
277 JSPROP_ENUMERATE);
278 if (!resolveFunc) {
279 return false;
282 // Store the 'active script' of the meta object into the function slot.
283 // https://html.spec.whatwg.org/#active-script
284 RootedObject resolveFuncObj(aCx, JS_GetFunctionObject(resolveFunc));
285 js::SetFunctionNativeReserved(resolveFuncObj, ModulePrivateSlot,
286 aReferencingPrivate);
288 return true;
291 // static
292 bool ModuleLoaderBase::HostImportModuleDynamically(
293 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate,
294 JS::Handle<JSObject*> aModuleRequest, JS::Handle<JSObject*> aPromise) {
295 MOZ_DIAGNOSTIC_ASSERT(aModuleRequest);
296 MOZ_DIAGNOSTIC_ASSERT(aPromise);
298 RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aCx, aReferencingPrivate));
300 JS::Rooted<JSString*> specifierString(
301 aCx, JS::GetModuleRequestSpecifier(aCx, aModuleRequest));
302 if (!specifierString) {
303 return false;
306 // Attempt to resolve the module specifier.
307 nsAutoJSString specifier;
308 if (!specifier.init(aCx, specifierString)) {
309 return false;
312 RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
313 if (!loader) {
314 return false;
317 auto result = loader->ResolveModuleSpecifier(script, specifier);
318 if (result.isErr()) {
319 JS::Rooted<JS::Value> error(aCx);
320 nsresult rv =
321 loader->HandleResolveFailure(aCx, script, specifier, result.unwrapErr(),
322 0, JS::ColumnNumberOneOrigin(), &error);
323 if (NS_FAILED(rv)) {
324 JS_ReportOutOfMemory(aCx);
325 return false;
328 JS_SetPendingException(aCx, error);
329 return false;
332 // Create a new top-level load request.
333 nsCOMPtr<nsIURI> uri = result.unwrap();
334 RefPtr<ModuleLoadRequest> request =
335 loader->CreateDynamicImport(aCx, uri, script, specifierString, aPromise);
337 if (!request) {
338 // Throws TypeError if CreateDynamicImport returns nullptr.
339 JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
340 JSMSG_DYNAMIC_IMPORT_NOT_SUPPORTED);
342 return false;
345 loader->StartDynamicImport(request);
346 return true;
349 // static
350 ModuleLoaderBase* ModuleLoaderBase::GetCurrentModuleLoader(JSContext* aCx) {
351 auto reportError = mozilla::MakeScopeExit([aCx]() {
352 JS_ReportErrorASCII(aCx, "No ScriptLoader found for the current context");
355 JS::Rooted<JSObject*> object(aCx, JS::CurrentGlobalOrNull(aCx));
356 if (!object) {
357 return nullptr;
360 nsIGlobalObject* global = xpc::NativeGlobal(object);
361 if (!global) {
362 return nullptr;
365 ModuleLoaderBase* loader = global->GetModuleLoader(aCx);
366 if (!loader) {
367 return nullptr;
370 MOZ_ASSERT(loader->mGlobalObject == global);
372 reportError.release();
373 return loader;
376 // static
377 LoadedScript* ModuleLoaderBase::GetLoadedScriptOrNull(
378 JSContext* aCx, JS::Handle<JS::Value> aReferencingPrivate) {
379 if (aReferencingPrivate.isUndefined()) {
380 return nullptr;
383 auto* script = static_cast<LoadedScript*>(aReferencingPrivate.toPrivate());
385 MOZ_ASSERT_IF(
386 script->IsModuleScript(),
387 JS::GetModulePrivate(script->AsModuleScript()->ModuleRecord()) ==
388 aReferencingPrivate);
390 return script;
393 JS::Value PrivateFromLoadedScript(LoadedScript* aScript) {
394 if (!aScript) {
395 return JS::UndefinedValue();
398 return JS::PrivateValue(aScript);
401 nsresult ModuleLoaderBase::StartModuleLoad(ModuleLoadRequest* aRequest) {
402 return StartOrRestartModuleLoad(aRequest, RestartRequest::No);
405 nsresult ModuleLoaderBase::RestartModuleLoad(ModuleLoadRequest* aRequest) {
406 return StartOrRestartModuleLoad(aRequest, RestartRequest::Yes);
409 nsresult ModuleLoaderBase::StartOrRestartModuleLoad(ModuleLoadRequest* aRequest,
410 RestartRequest aRestart) {
411 MOZ_ASSERT(aRequest->mLoader == this);
412 MOZ_ASSERT(aRequest->IsFetching() || aRequest->IsPendingFetchingError());
414 aRequest->SetUnknownDataType();
416 // If we're restarting the request, the module should already be in the
417 // "fetching" map.
418 MOZ_ASSERT_IF(aRestart == RestartRequest::Yes,
419 IsModuleFetching(aRequest->mURI));
421 // Check with the derived class whether we should load this module.
422 nsresult rv = NS_OK;
423 if (!CanStartLoad(aRequest, &rv)) {
424 return rv;
427 // Check whether the module has been fetched or is currently being fetched,
428 // and if so wait for it rather than starting a new fetch.
429 ModuleLoadRequest* request = aRequest->AsModuleRequest();
431 if (aRestart == RestartRequest::No && ModuleMapContainsURL(request->mURI)) {
432 LOG(("ScriptLoadRequest (%p): Waiting for module fetch", aRequest));
433 WaitForModuleFetch(request);
434 return NS_OK;
437 rv = StartFetch(aRequest);
438 NS_ENSURE_SUCCESS(rv, rv);
440 // We successfully started fetching a module so put its URL in the module
441 // map and mark it as fetching.
442 if (aRestart == RestartRequest::No) {
443 SetModuleFetchStarted(aRequest->AsModuleRequest());
446 return NS_OK;
449 bool ModuleLoaderBase::ModuleMapContainsURL(nsIURI* aURL) const {
450 return IsModuleFetching(aURL) || IsModuleFetched(aURL);
453 bool ModuleLoaderBase::IsModuleFetching(nsIURI* aURL) const {
454 return mFetchingModules.Contains(aURL);
457 bool ModuleLoaderBase::IsModuleFetched(nsIURI* aURL) const {
458 return mFetchedModules.Contains(aURL);
461 nsresult ModuleLoaderBase::GetFetchedModuleURLs(nsTArray<nsCString>& aURLs) {
462 for (const auto& entry : mFetchedModules) {
463 nsIURI* uri = entry.GetData()->BaseURL();
465 nsAutoCString spec;
466 nsresult rv = uri->GetSpec(spec);
467 NS_ENSURE_SUCCESS(rv, rv);
469 aURLs.AppendElement(spec);
472 return NS_OK;
475 void ModuleLoaderBase::SetModuleFetchStarted(ModuleLoadRequest* aRequest) {
476 // Update the module map to indicate that a module is currently being fetched.
478 MOZ_ASSERT(aRequest->IsFetching() || aRequest->IsPendingFetchingError());
479 MOZ_ASSERT(!ModuleMapContainsURL(aRequest->mURI));
481 mFetchingModules.InsertOrUpdate(aRequest->mURI, nullptr);
484 void ModuleLoaderBase::SetModuleFetchFinishedAndResumeWaitingRequests(
485 ModuleLoadRequest* aRequest, nsresult aResult) {
486 // Update module map with the result of fetching a single module script.
488 // If any requests for the same URL are waiting on this one to complete, call
489 // ModuleLoaded or LoadFailed to resume or fail them as appropriate.
491 MOZ_ASSERT(aRequest->mLoader == this);
493 LOG(
494 ("ScriptLoadRequest (%p): Module fetch finished (script == %p, result == "
495 "%u)",
496 aRequest, aRequest->mModuleScript.get(), unsigned(aResult)));
498 RefPtr<WaitingRequests> waitingRequests;
499 if (!mFetchingModules.Remove(aRequest->mURI,
500 getter_AddRefs(waitingRequests))) {
501 LOG(
502 ("ScriptLoadRequest (%p): Key not found in mFetchingModules, "
503 "assuming we have an inline module or have finished fetching already",
504 aRequest));
505 return;
508 RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript);
509 MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript);
511 mFetchedModules.InsertOrUpdate(aRequest->mURI, RefPtr{moduleScript});
513 if (waitingRequests) {
514 LOG(("ScriptLoadRequest (%p): Resuming waiting requests", aRequest));
515 ResumeWaitingRequests(waitingRequests, bool(moduleScript));
519 void ModuleLoaderBase::ResumeWaitingRequests(WaitingRequests* aWaitingRequests,
520 bool aSuccess) {
521 for (ModuleLoadRequest* request : aWaitingRequests->mWaiting) {
522 ResumeWaitingRequest(request, aSuccess);
526 void ModuleLoaderBase::ResumeWaitingRequest(ModuleLoadRequest* aRequest,
527 bool aSuccess) {
528 if (aSuccess) {
529 aRequest->ModuleLoaded();
530 } else {
531 aRequest->LoadFailed();
535 void ModuleLoaderBase::WaitForModuleFetch(ModuleLoadRequest* aRequest) {
536 nsIURI* uri = aRequest->mURI;
537 MOZ_ASSERT(ModuleMapContainsURL(uri));
539 if (auto entry = mFetchingModules.Lookup(uri)) {
540 RefPtr<WaitingRequests> waitingRequests = entry.Data();
541 if (!waitingRequests) {
542 waitingRequests = new WaitingRequests();
543 mFetchingModules.InsertOrUpdate(uri, waitingRequests);
546 waitingRequests->mWaiting.AppendElement(aRequest);
547 return;
550 RefPtr<ModuleScript> ms;
551 MOZ_ALWAYS_TRUE(mFetchedModules.Get(uri, getter_AddRefs(ms)));
553 ResumeWaitingRequest(aRequest, bool(ms));
556 ModuleScript* ModuleLoaderBase::GetFetchedModule(nsIURI* aURL) const {
557 if (LOG_ENABLED()) {
558 nsAutoCString url;
559 aURL->GetAsciiSpec(url);
560 LOG(("GetFetchedModule %s", url.get()));
563 bool found;
564 ModuleScript* ms = mFetchedModules.GetWeak(aURL, &found);
565 MOZ_ASSERT(found);
566 return ms;
569 nsresult ModuleLoaderBase::OnFetchComplete(ModuleLoadRequest* aRequest,
570 nsresult aRv) {
571 MOZ_ASSERT(aRequest->mLoader == this);
572 MOZ_ASSERT(!aRequest->mModuleScript);
574 nsresult rv = aRv;
575 if (NS_SUCCEEDED(rv)) {
576 rv = CreateModuleScript(aRequest);
578 // If a module script was created, it should either have a module record
579 // object or a parse error.
580 if (ModuleScript* ms = aRequest->mModuleScript) {
581 MOZ_DIAGNOSTIC_ASSERT(bool(ms->ModuleRecord()) != ms->HasParseError());
584 aRequest->ClearScriptSource();
586 if (NS_FAILED(rv)) {
587 aRequest->LoadFailed();
588 return rv;
592 MOZ_ASSERT(NS_SUCCEEDED(rv) == bool(aRequest->mModuleScript));
593 SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv);
595 if (!aRequest->IsErrored()) {
596 StartFetchingModuleDependencies(aRequest);
599 return NS_OK;
602 nsresult ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest* aRequest) {
603 MOZ_ASSERT(!aRequest->mModuleScript);
604 MOZ_ASSERT(aRequest->mBaseURL);
606 LOG(("ScriptLoadRequest (%p): Create module script", aRequest));
608 AutoJSAPI jsapi;
609 if (!jsapi.Init(mGlobalObject)) {
610 return NS_ERROR_FAILURE;
613 nsresult rv;
615 JSContext* cx = jsapi.cx();
616 JS::Rooted<JSObject*> module(cx);
618 JS::CompileOptions options(cx);
619 JS::RootedScript introductionScript(cx);
620 rv = mLoader->FillCompileOptionsForRequest(cx, aRequest, &options,
621 &introductionScript);
623 if (NS_SUCCEEDED(rv)) {
624 JS::Rooted<JSObject*> global(cx, mGlobalObject->GetGlobalJSObject());
625 rv = CompileFetchedModule(cx, global, options, aRequest, &module);
628 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
630 if (module) {
631 JS::RootedValue privateValue(cx);
632 JS::RootedScript moduleScript(cx, JS::GetModuleScript(module));
633 JS::InstantiateOptions instantiateOptions(options);
634 if (!JS::UpdateDebugMetadata(cx, moduleScript, instantiateOptions,
635 privateValue, nullptr, introductionScript,
636 nullptr)) {
637 return NS_ERROR_OUT_OF_MEMORY;
641 MOZ_ASSERT(aRequest->mLoadedScript->IsModuleScript());
642 MOZ_ASSERT(aRequest->mLoadedScript->GetFetchOptions() ==
643 aRequest->mFetchOptions);
644 MOZ_ASSERT(aRequest->mLoadedScript->GetURI() == aRequest->mURI);
645 aRequest->mLoadedScript->SetBaseURL(aRequest->mBaseURL);
646 RefPtr<ModuleScript> moduleScript =
647 aRequest->mLoadedScript->AsModuleScript();
648 aRequest->mModuleScript = moduleScript;
650 if (!module) {
651 LOG(("ScriptLoadRequest (%p): compilation failed (%d)", aRequest,
652 unsigned(rv)));
654 JS::Rooted<JS::Value> error(cx);
655 if (!jsapi.HasException() || !jsapi.StealException(&error) ||
656 error.isUndefined()) {
657 aRequest->mModuleScript = nullptr;
658 return NS_ERROR_FAILURE;
661 moduleScript->SetParseError(error);
662 aRequest->ModuleErrored();
663 return NS_OK;
666 moduleScript->SetModuleRecord(module);
668 // Validate requested modules and treat failure to resolve module specifiers
669 // the same as a parse error.
670 rv = ResolveRequestedModules(aRequest, nullptr);
671 if (NS_FAILED(rv)) {
672 if (!aRequest->IsErrored()) {
673 aRequest->mModuleScript = nullptr;
674 return rv;
676 aRequest->ModuleErrored();
677 return NS_OK;
681 LOG(("ScriptLoadRequest (%p): module script == %p", aRequest,
682 aRequest->mModuleScript.get()));
684 return rv;
687 nsresult ModuleLoaderBase::GetResolveFailureMessage(ResolveError aError,
688 const nsAString& aSpecifier,
689 nsAString& aResult) {
690 AutoTArray<nsString, 1> errorParams;
691 errorParams.AppendElement(aSpecifier);
693 nsresult rv = nsContentUtils::FormatLocalizedString(
694 nsContentUtils::eDOM_PROPERTIES, ResolveErrorInfo::GetString(aError),
695 errorParams, aResult);
696 NS_ENSURE_SUCCESS(rv, rv);
697 return NS_OK;
700 nsresult ModuleLoaderBase::HandleResolveFailure(
701 JSContext* aCx, LoadedScript* aScript, const nsAString& aSpecifier,
702 ResolveError aError, uint32_t aLineNumber,
703 JS::ColumnNumberOneOrigin aColumnNumber,
704 JS::MutableHandle<JS::Value> aErrorOut) {
705 JS::Rooted<JSString*> filename(aCx);
706 if (aScript) {
707 nsAutoCString url;
708 aScript->BaseURL()->GetAsciiSpec(url);
709 filename = JS_NewStringCopyZ(aCx, url.get());
710 } else {
711 filename = JS_NewStringCopyZ(aCx, "(unknown)");
714 if (!filename) {
715 return NS_ERROR_OUT_OF_MEMORY;
718 nsAutoString errorText;
719 nsresult rv = GetResolveFailureMessage(aError, aSpecifier, errorText);
720 NS_ENSURE_SUCCESS(rv, rv);
722 JS::Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, errorText.get()));
723 if (!string) {
724 return NS_ERROR_OUT_OF_MEMORY;
727 if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
728 aColumnNumber, nullptr, string, JS::NothingHandleValue,
729 aErrorOut)) {
730 return NS_ERROR_OUT_OF_MEMORY;
733 return NS_OK;
736 ResolveResult ModuleLoaderBase::ResolveModuleSpecifier(
737 LoadedScript* aScript, const nsAString& aSpecifier) {
738 // Import Maps are not supported on workers/worklets.
739 // See https://github.com/WICG/import-maps/issues/2
740 MOZ_ASSERT_IF(!NS_IsMainThread(), mImportMap == nullptr);
741 // Forward to the updated 'Resolve a module specifier' algorithm defined in
742 // the Import Maps spec.
743 return ImportMap::ResolveModuleSpecifier(mImportMap.get(), mLoader, aScript,
744 aSpecifier);
747 nsresult ModuleLoaderBase::ResolveRequestedModules(
748 ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOut) {
749 ModuleScript* ms = aRequest->mModuleScript;
751 AutoJSAPI jsapi;
752 if (!jsapi.Init(mGlobalObject)) {
753 return NS_ERROR_FAILURE;
756 JSContext* cx = jsapi.cx();
757 JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord());
758 uint32_t length = JS::GetRequestedModulesCount(cx, moduleRecord);
760 for (uint32_t i = 0; i < length; i++) {
761 JS::Rooted<JSString*> str(
762 cx, JS::GetRequestedModuleSpecifier(cx, moduleRecord, i));
763 MOZ_ASSERT(str);
765 nsAutoJSString specifier;
766 if (!specifier.init(cx, str)) {
767 return NS_ERROR_FAILURE;
770 // Let url be the result of resolving a module specifier given module script
771 // and requested.
772 ModuleLoaderBase* loader = aRequest->mLoader;
773 auto result = loader->ResolveModuleSpecifier(ms, specifier);
774 if (result.isErr()) {
775 uint32_t lineNumber = 0;
776 JS::ColumnNumberOneOrigin columnNumber;
777 JS::GetRequestedModuleSourcePos(cx, moduleRecord, i, &lineNumber,
778 &columnNumber);
780 JS::Rooted<JS::Value> error(cx);
781 nsresult rv =
782 loader->HandleResolveFailure(cx, ms, specifier, result.unwrapErr(),
783 lineNumber, columnNumber, &error);
784 NS_ENSURE_SUCCESS(rv, rv);
786 ms->SetParseError(error);
787 return NS_ERROR_FAILURE;
790 nsCOMPtr<nsIURI> uri = result.unwrap();
791 if (aUrlsOut) {
792 aUrlsOut->AppendElement(uri.forget());
796 return NS_OK;
799 void ModuleLoaderBase::StartFetchingModuleDependencies(
800 ModuleLoadRequest* aRequest) {
801 LOG(("ScriptLoadRequest (%p): Start fetching module dependencies", aRequest));
803 if (aRequest->IsCanceled()) {
804 return;
807 MOZ_ASSERT(aRequest->mModuleScript);
808 MOZ_ASSERT(!aRequest->mModuleScript->HasParseError());
809 MOZ_ASSERT(aRequest->IsFetching() || aRequest->IsCompiling());
811 auto visitedSet = aRequest->mVisitedSet;
812 MOZ_ASSERT(visitedSet->Contains(aRequest->mURI));
814 aRequest->mState = ModuleLoadRequest::State::LoadingImports;
816 nsCOMArray<nsIURI> urls;
817 nsresult rv = ResolveRequestedModules(aRequest, &urls);
818 if (NS_FAILED(rv)) {
819 aRequest->mModuleScript = nullptr;
820 aRequest->ModuleErrored();
821 return;
824 // Remove already visited URLs from the list. Put unvisited URLs into the
825 // visited set.
826 int32_t i = 0;
827 while (i < urls.Count()) {
828 nsIURI* url = urls[i];
829 if (visitedSet->Contains(url)) {
830 urls.RemoveObjectAt(i);
831 } else {
832 visitedSet->PutEntry(url);
833 i++;
837 if (urls.Count() == 0) {
838 // There are no descendants to load so this request is ready.
839 aRequest->DependenciesLoaded();
840 return;
843 MOZ_ASSERT(aRequest->mAwaitingImports == 0);
844 aRequest->mAwaitingImports = urls.Count();
846 // For each url in urls, fetch a module script graph given url, module
847 // script's CORS setting, and module script's settings object.
848 for (auto* url : urls) {
849 StartFetchingModuleAndDependencies(aRequest, url);
853 void ModuleLoaderBase::StartFetchingModuleAndDependencies(
854 ModuleLoadRequest* aParent, nsIURI* aURI) {
855 MOZ_ASSERT(aURI);
857 RefPtr<ModuleLoadRequest> childRequest = CreateStaticImport(aURI, aParent);
859 aParent->mImports.AppendElement(childRequest);
861 if (LOG_ENABLED()) {
862 nsAutoCString url1;
863 aParent->mURI->GetAsciiSpec(url1);
865 nsAutoCString url2;
866 aURI->GetAsciiSpec(url2);
868 LOG(("ScriptLoadRequest (%p): Start fetching dependency %p", aParent,
869 childRequest.get()));
870 LOG(("StartFetchingModuleAndDependencies \"%s\" -> \"%s\"", url1.get(),
871 url2.get()));
874 MOZ_ASSERT(!childRequest->mWaitingParentRequest);
875 childRequest->mWaitingParentRequest = aParent;
877 nsresult rv = StartModuleLoad(childRequest);
878 if (NS_FAILED(rv)) {
879 MOZ_ASSERT(!childRequest->mModuleScript);
880 LOG(("ScriptLoadRequest (%p): rejecting %p", aParent,
881 childRequest.get()));
883 mLoader->ReportErrorToConsole(childRequest, rv);
884 childRequest->LoadFailed();
888 void ModuleLoadRequest::ChildLoadComplete(bool aSuccess) {
889 RefPtr<ModuleLoadRequest> parent = mWaitingParentRequest;
890 MOZ_ASSERT(parent);
891 MOZ_ASSERT(parent->mAwaitingImports != 0);
893 mWaitingParentRequest = nullptr;
894 parent->mAwaitingImports--;
896 if (parent->IsFinished()) {
897 MOZ_ASSERT_IF(!aSuccess, parent->IsErrored());
898 return;
901 if (!aSuccess) {
902 parent->ModuleErrored();
903 } else if (parent->mAwaitingImports == 0) {
904 parent->DependenciesLoaded();
908 void ModuleLoaderBase::StartDynamicImport(ModuleLoadRequest* aRequest) {
909 MOZ_ASSERT(aRequest->mLoader == this);
911 LOG(("ScriptLoadRequest (%p): Start dynamic import", aRequest));
913 mDynamicImportRequests.AppendElement(aRequest);
915 nsresult rv = StartModuleLoad(aRequest);
916 if (NS_FAILED(rv)) {
917 mLoader->ReportErrorToConsole(aRequest, rv);
918 FinishDynamicImportAndReject(aRequest, rv);
922 void ModuleLoaderBase::FinishDynamicImportAndReject(ModuleLoadRequest* aRequest,
923 nsresult aResult) {
924 AutoJSAPI jsapi;
925 MOZ_ASSERT(NS_FAILED(aResult));
926 if (!jsapi.Init(mGlobalObject)) {
927 return;
930 FinishDynamicImport(jsapi.cx(), aRequest, aResult, nullptr);
933 /* static */
934 void ModuleLoaderBase::FinishDynamicImport(
935 JSContext* aCx, ModuleLoadRequest* aRequest, nsresult aResult,
936 JS::Handle<JSObject*> aEvaluationPromise) {
937 LOG(("ScriptLoadRequest (%p): Finish dynamic import %x %d", aRequest,
938 unsigned(aResult), JS_IsExceptionPending(aCx)));
940 MOZ_ASSERT(GetCurrentModuleLoader(aCx) == aRequest->mLoader);
942 // If aResult is a failed result, we don't have an EvaluationPromise. If it
943 // succeeded, evaluationPromise may still be null, but in this case it will
944 // be handled by rejecting the dynamic module import promise in the JSAPI.
945 MOZ_ASSERT_IF(NS_FAILED(aResult), !aEvaluationPromise);
947 // Complete the dynamic import, report failures indicated by aResult or as a
948 // pending exception on the context.
950 if (!aRequest->mDynamicPromise) {
951 // Import has already been completed.
952 return;
955 if (NS_FAILED(aResult) &&
956 aResult != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
957 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
958 nsAutoCString url;
959 aRequest->mURI->GetSpec(url);
960 JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
961 JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
964 JS::Rooted<JS::Value> referencingScript(
965 aCx, PrivateFromLoadedScript(aRequest->mDynamicReferencingScript));
966 JS::Rooted<JSString*> specifier(aCx, aRequest->mDynamicSpecifier);
967 JS::Rooted<JSObject*> promise(aCx, aRequest->mDynamicPromise);
969 JS::Rooted<JSObject*> moduleRequest(aCx,
970 JS::CreateModuleRequest(aCx, specifier));
972 JS::FinishDynamicModuleImport(aCx, aEvaluationPromise, referencingScript,
973 moduleRequest, promise);
975 // FinishDynamicModuleImport clears any pending exception.
976 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
978 aRequest->ClearDynamicImport();
981 ModuleLoaderBase::ModuleLoaderBase(ScriptLoaderInterface* aLoader,
982 nsIGlobalObject* aGlobalObject,
983 nsISerialEventTarget* aEventTarget)
984 : mGlobalObject(aGlobalObject),
985 mEventTarget(aEventTarget),
986 mLoader(aLoader) {
987 MOZ_ASSERT(mGlobalObject);
988 MOZ_ASSERT(mEventTarget);
989 MOZ_ASSERT(mLoader);
991 EnsureModuleHooksInitialized();
994 ModuleLoaderBase::~ModuleLoaderBase() {
995 mDynamicImportRequests.CancelRequestsAndClear();
997 LOG(("ModuleLoaderBase::~ModuleLoaderBase %p", this));
1000 void ModuleLoaderBase::Shutdown() {
1001 CancelAndClearDynamicImports();
1003 for (const auto& entry : mFetchingModules) {
1004 RefPtr<WaitingRequests> waitingRequests(entry.GetData());
1005 if (waitingRequests) {
1006 ResumeWaitingRequests(waitingRequests, false);
1010 for (const auto& entry : mFetchedModules) {
1011 if (entry.GetData()) {
1012 entry.GetData()->Shutdown();
1016 mFetchingModules.Clear();
1017 mFetchedModules.Clear();
1018 mGlobalObject = nullptr;
1019 mEventTarget = nullptr;
1020 mLoader = nullptr;
1023 bool ModuleLoaderBase::HasPendingDynamicImports() const {
1024 return !mDynamicImportRequests.isEmpty();
1027 void ModuleLoaderBase::CancelDynamicImport(ModuleLoadRequest* aRequest,
1028 nsresult aResult) {
1029 MOZ_ASSERT(aRequest->mLoader == this);
1031 RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
1032 if (!aRequest->IsCanceled()) {
1033 aRequest->Cancel();
1034 // FinishDynamicImport must happen exactly once for each dynamic import
1035 // request. If the load is aborted we do it when we remove the request
1036 // from mDynamicImportRequests.
1037 FinishDynamicImportAndReject(aRequest, aResult);
1041 void ModuleLoaderBase::RemoveDynamicImport(ModuleLoadRequest* aRequest) {
1042 MOZ_ASSERT(aRequest->IsDynamicImport());
1043 mDynamicImportRequests.Remove(aRequest);
1046 #ifdef DEBUG
1047 bool ModuleLoaderBase::HasDynamicImport(
1048 const ModuleLoadRequest* aRequest) const {
1049 MOZ_ASSERT(aRequest->mLoader == this);
1050 return mDynamicImportRequests.Contains(
1051 const_cast<ModuleLoadRequest*>(aRequest));
1053 #endif
1055 JS::Value ModuleLoaderBase::FindFirstParseError(ModuleLoadRequest* aRequest) {
1056 MOZ_ASSERT(aRequest);
1058 ModuleScript* moduleScript = aRequest->mModuleScript;
1059 MOZ_ASSERT(moduleScript);
1061 if (moduleScript->HasParseError()) {
1062 return moduleScript->ParseError();
1065 for (ModuleLoadRequest* childRequest : aRequest->mImports) {
1066 JS::Value error = FindFirstParseError(childRequest);
1067 if (!error.isUndefined()) {
1068 return error;
1072 return JS::UndefinedValue();
1075 bool ModuleLoaderBase::InstantiateModuleGraph(ModuleLoadRequest* aRequest) {
1076 // Instantiate a top-level module and record any error.
1078 MOZ_ASSERT(aRequest);
1079 MOZ_ASSERT(aRequest->mLoader == this);
1080 MOZ_ASSERT(aRequest->IsTopLevel());
1082 LOG(("ScriptLoadRequest (%p): Instantiate module graph", aRequest));
1084 AUTO_PROFILER_LABEL("ModuleLoaderBase::InstantiateModuleGraph", JS);
1086 ModuleScript* moduleScript = aRequest->mModuleScript;
1087 MOZ_ASSERT(moduleScript);
1089 JS::Value parseError = FindFirstParseError(aRequest);
1090 if (!parseError.isUndefined()) {
1091 moduleScript->SetErrorToRethrow(parseError);
1092 LOG(("ScriptLoadRequest (%p): found parse error", aRequest));
1093 return true;
1096 MOZ_ASSERT(moduleScript->ModuleRecord());
1098 AutoJSAPI jsapi;
1099 if (NS_WARN_IF(!jsapi.Init(mGlobalObject))) {
1100 return false;
1103 JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord());
1104 if (!xpc::Scriptability::AllowedIfExists(module)) {
1105 return true;
1108 if (!JS::ModuleLink(jsapi.cx(), module)) {
1109 LOG(("ScriptLoadRequest (%p): Instantiate failed", aRequest));
1110 MOZ_ASSERT(jsapi.HasException());
1111 JS::RootedValue exception(jsapi.cx());
1112 if (!jsapi.StealException(&exception)) {
1113 return false;
1115 MOZ_ASSERT(!exception.isUndefined());
1116 moduleScript->SetErrorToRethrow(exception);
1119 return true;
1122 nsresult ModuleLoaderBase::InitDebuggerDataForModuleGraph(
1123 JSContext* aCx, ModuleLoadRequest* aRequest) {
1124 // JS scripts can be associated with a DOM element for use by the debugger,
1125 // but preloading can cause scripts to be compiled before DOM script element
1126 // nodes have been created. This method ensures that this association takes
1127 // place before the first time a module script is run.
1129 MOZ_ASSERT(aRequest);
1131 ModuleScript* moduleScript = aRequest->mModuleScript;
1132 if (moduleScript->DebuggerDataInitialized()) {
1133 return NS_OK;
1136 for (ModuleLoadRequest* childRequest : aRequest->mImports) {
1137 nsresult rv = InitDebuggerDataForModuleGraph(aCx, childRequest);
1138 NS_ENSURE_SUCCESS(rv, rv);
1141 JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
1142 MOZ_ASSERT(module);
1144 // The script is now ready to be exposed to the debugger.
1145 JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(module));
1146 JS::ExposeScriptToDebugger(aCx, script);
1148 moduleScript->SetDebuggerDataInitialized();
1149 return NS_OK;
1152 void ModuleLoaderBase::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
1153 if (!aRequest->mModuleScript) {
1154 FinishDynamicImportAndReject(aRequest, NS_ERROR_FAILURE);
1155 return;
1158 InstantiateAndEvaluateDynamicImport(aRequest);
1161 void ModuleLoaderBase::InstantiateAndEvaluateDynamicImport(
1162 ModuleLoadRequest* aRequest) {
1163 MOZ_ASSERT(aRequest->mModuleScript);
1165 if (!InstantiateModuleGraph(aRequest)) {
1166 aRequest->mModuleScript = nullptr;
1169 nsresult rv = NS_ERROR_FAILURE;
1170 if (aRequest->mModuleScript) {
1171 rv = EvaluateModule(aRequest);
1174 if (NS_FAILED(rv)) {
1175 FinishDynamicImportAndReject(aRequest, rv);
1179 nsresult ModuleLoaderBase::EvaluateModule(ModuleLoadRequest* aRequest) {
1180 MOZ_ASSERT(aRequest->mLoader == this);
1182 mozilla::nsAutoMicroTask mt;
1183 mozilla::dom::AutoEntryScript aes(mGlobalObject, "EvaluateModule",
1184 NS_IsMainThread());
1186 return EvaluateModuleInContext(aes.cx(), aRequest,
1187 JS::ReportModuleErrorsAsync);
1190 nsresult ModuleLoaderBase::EvaluateModuleInContext(
1191 JSContext* aCx, ModuleLoadRequest* aRequest,
1192 JS::ModuleErrorBehaviour errorBehaviour) {
1193 MOZ_ASSERT(aRequest->mLoader == this);
1194 MOZ_ASSERT(mGlobalObject->GetModuleLoader(aCx) == this);
1196 AUTO_PROFILER_LABEL("ModuleLoaderBase::EvaluateModule", JS);
1198 nsAutoCString profilerLabelString;
1199 if (aRequest->HasScriptLoadContext()) {
1200 aRequest->GetScriptLoadContext()->GetProfilerLabel(profilerLabelString);
1203 LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
1204 AUTO_PROFILER_MARKER_TEXT("ModuleEvaluation", JS,
1205 MarkerInnerWindowIdFromJSContext(aCx),
1206 profilerLabelString);
1208 ModuleLoadRequest* request = aRequest->AsModuleRequest();
1209 MOZ_ASSERT(request->mModuleScript);
1210 MOZ_ASSERT_IF(request->HasScriptLoadContext(),
1211 !request->GetScriptLoadContext()->mCompileOrDecodeTask);
1213 ModuleScript* moduleScript = request->mModuleScript;
1214 if (moduleScript->HasErrorToRethrow()) {
1215 LOG(("ScriptLoadRequest (%p): module has error to rethrow", aRequest));
1216 JS::Rooted<JS::Value> error(aCx, moduleScript->ErrorToRethrow());
1217 JS_SetPendingException(aCx, error);
1218 // For a dynamic import, the promise is rejected. Otherwise an error
1219 // is either reported by AutoEntryScript.
1220 if (request->IsDynamicImport()) {
1221 FinishDynamicImport(aCx, request, NS_OK, nullptr);
1223 return NS_OK;
1226 JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
1227 MOZ_ASSERT(module);
1228 MOZ_ASSERT(CurrentGlobalOrNull(aCx) == GetNonCCWObjectGlobal(module));
1230 if (!xpc::Scriptability::AllowedIfExists(module)) {
1231 return NS_OK;
1234 nsresult rv = InitDebuggerDataForModuleGraph(aCx, request);
1235 NS_ENSURE_SUCCESS(rv, rv);
1237 if (request->HasScriptLoadContext()) {
1238 TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
1239 "scriptloader_evaluate_module");
1242 JS::Rooted<JS::Value> rval(aCx);
1244 mLoader->MaybePrepareModuleForBytecodeEncodingBeforeExecute(aCx, request);
1246 bool ok = JS::ModuleEvaluate(aCx, module, &rval);
1248 // ModuleEvaluate will usually set a pending exception if it returns false,
1249 // unless the user cancels execution.
1250 MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aCx));
1252 if (!ok || IsModuleEvaluationAborted(request)) {
1253 LOG(("ScriptLoadRequest (%p): evaluation failed", aRequest));
1254 // For a dynamic import, the promise is rejected. Otherwise an error is
1255 // reported by AutoEntryScript.
1256 rv = NS_ERROR_ABORT;
1259 // ModuleEvaluate returns a promise unless the user cancels the execution in
1260 // which case rval will be undefined. We should treat it as a failed
1261 // evaluation, and reject appropriately.
1262 JS::Rooted<JSObject*> evaluationPromise(aCx);
1263 if (rval.isObject()) {
1264 evaluationPromise.set(&rval.toObject());
1267 if (request->IsDynamicImport()) {
1268 if (NS_FAILED(rv)) {
1269 FinishDynamicImportAndReject(request, rv);
1270 } else {
1271 FinishDynamicImport(aCx, request, NS_OK, evaluationPromise);
1273 } else {
1274 // If this is not a dynamic import, and if the promise is rejected,
1275 // the value is unwrapped from the promise value.
1276 if (!JS::ThrowOnModuleEvaluationFailure(aCx, evaluationPromise,
1277 errorBehaviour)) {
1278 LOG(("ScriptLoadRequest (%p): evaluation failed on throw", aRequest));
1279 // For a dynamic import, the promise is rejected. Otherwise an error is
1280 // reported by AutoEntryScript.
1284 rv = mLoader->MaybePrepareModuleForBytecodeEncodingAfterExecute(request,
1285 NS_OK);
1287 mLoader->MaybeTriggerBytecodeEncoding();
1289 return rv;
1292 void ModuleLoaderBase::CancelAndClearDynamicImports() {
1293 while (ScriptLoadRequest* req = mDynamicImportRequests.getFirst()) {
1294 // This also removes the request from the list.
1295 CancelDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
1299 UniquePtr<ImportMap> ModuleLoaderBase::ParseImportMap(
1300 ScriptLoadRequest* aRequest) {
1301 AutoJSAPI jsapi;
1302 if (!jsapi.Init(GetGlobalObject())) {
1303 return nullptr;
1306 MOZ_ASSERT(aRequest->IsTextSource());
1307 MaybeSourceText maybeSource;
1308 nsresult rv = aRequest->GetScriptSource(jsapi.cx(), &maybeSource,
1309 aRequest->mLoadContext.get());
1310 if (NS_FAILED(rv)) {
1311 return nullptr;
1314 JS::SourceText<char16_t>& text = maybeSource.ref<SourceText<char16_t>>();
1315 ReportWarningHelper warning{mLoader, aRequest};
1317 // https://html.spec.whatwg.org/multipage/webappapis.html#create-an-import-map-parse-result
1318 // Step 2. Parse an import map string given input and baseURL, catching any
1319 // exceptions. If this threw an exception, then set result's error to rethrow
1320 // to that exception. Otherwise, set result's import map to the return value.
1322 // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
1323 // Step 1. If result's error to rethrow is not null, then report the exception
1324 // given by result's error to rethrow and return.
1326 // Impl note: We didn't implement 'Import map parse result' from the spec,
1327 // https://html.spec.whatwg.org/multipage/webappapis.html#import-map-parse-result
1328 // As the struct has another item called 'error to rethow' to store the
1329 // exception thrown during parsing import-maps, and report that exception
1330 // while registering an import map. Currently only inline import-maps are
1331 // supported, therefore parsing and registering import-maps will be executed
1332 // consecutively. To simplify the implementation, we didn't create the 'error
1333 // to rethow' item and report the exception immediately(done in ~AutoJSAPI).
1334 return ImportMap::ParseString(jsapi.cx(), text, aRequest->mBaseURL, warning);
1337 void ModuleLoaderBase::RegisterImportMap(UniquePtr<ImportMap> aImportMap) {
1338 // Check for aImportMap is done in ScriptLoader.
1339 MOZ_ASSERT(aImportMap);
1341 // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
1342 // The step 1(report the exception if there's an error) is done in
1343 // ParseImportMap.
1345 // Step 2. Assert: global's import map is an empty import map.
1346 // Impl note: The default import map from the spec is an empty import map, but
1347 // from the implementation it defaults to nullptr, so we check if the global's
1348 // import map is null here.
1350 // Also see
1351 // https://html.spec.whatwg.org/multipage/webappapis.html#empty-import-map
1352 MOZ_ASSERT(!mImportMap);
1354 // Step 3. Set global's import map to result's import map.
1355 mImportMap = std::move(aImportMap);
1358 #undef LOG
1359 #undef LOG_ENABLED
1361 } // namespace JS::loader