Bug 1851372 [wpt PR 41785] - HTML: search setter always uses UTF-8, a=testonly
[gecko.git] / js / loader / ModuleLoaderBase.cpp
blob22950368f01d0f7041d913c90c9a851af072a8fb
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::ColumnNumberZeroOrigin, 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::ColumnNumberZeroOrigin::zero(), &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 = loader->HandleResolveFailure(
321 aCx, script, specifier, result.unwrapErr(), 0,
322 JS::ColumnNumberZeroOrigin::zero(), &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 = loader->CreateDynamicImport(
335 aCx, uri, script, aReferencingPrivate, 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());
384 if (script->IsEventScript()) {
385 return nullptr;
388 MOZ_ASSERT_IF(
389 script->IsModuleScript(),
390 JS::GetModulePrivate(script->AsModuleScript()->ModuleRecord()) ==
391 aReferencingPrivate);
393 return script;
396 nsresult ModuleLoaderBase::StartModuleLoad(ModuleLoadRequest* aRequest) {
397 return StartOrRestartModuleLoad(aRequest, RestartRequest::No);
400 nsresult ModuleLoaderBase::RestartModuleLoad(ModuleLoadRequest* aRequest) {
401 return StartOrRestartModuleLoad(aRequest, RestartRequest::Yes);
404 nsresult ModuleLoaderBase::StartOrRestartModuleLoad(ModuleLoadRequest* aRequest,
405 RestartRequest aRestart) {
406 MOZ_ASSERT(aRequest->mLoader == this);
407 MOZ_ASSERT(aRequest->IsFetching());
409 aRequest->SetUnknownDataType();
411 // If we're restarting the request, the module should already be in the
412 // "fetching" map.
413 MOZ_ASSERT_IF(aRestart == RestartRequest::Yes,
414 IsModuleFetching(aRequest->mURI));
416 // Check with the derived class whether we should load this module.
417 nsresult rv = NS_OK;
418 if (!CanStartLoad(aRequest, &rv)) {
419 return rv;
422 // Check whether the module has been fetched or is currently being fetched,
423 // and if so wait for it rather than starting a new fetch.
424 ModuleLoadRequest* request = aRequest->AsModuleRequest();
426 if (aRestart == RestartRequest::No && ModuleMapContainsURL(request->mURI)) {
427 LOG(("ScriptLoadRequest (%p): Waiting for module fetch", aRequest));
428 WaitForModuleFetch(request);
429 return NS_OK;
432 rv = StartFetch(aRequest);
433 NS_ENSURE_SUCCESS(rv, rv);
435 // We successfully started fetching a module so put its URL in the module
436 // map and mark it as fetching.
437 if (aRestart == RestartRequest::No) {
438 SetModuleFetchStarted(aRequest->AsModuleRequest());
441 return NS_OK;
444 bool ModuleLoaderBase::ModuleMapContainsURL(nsIURI* aURL) const {
445 return IsModuleFetching(aURL) || IsModuleFetched(aURL);
448 bool ModuleLoaderBase::IsModuleFetching(nsIURI* aURL) const {
449 return mFetchingModules.Contains(aURL);
452 bool ModuleLoaderBase::IsModuleFetched(nsIURI* aURL) const {
453 return mFetchedModules.Contains(aURL);
456 nsresult ModuleLoaderBase::GetFetchedModuleURLs(nsTArray<nsCString>& aURLs) {
457 for (const auto& entry : mFetchedModules) {
458 nsIURI* uri = entry.GetData()->BaseURL();
460 nsAutoCString spec;
461 nsresult rv = uri->GetSpec(spec);
462 NS_ENSURE_SUCCESS(rv, rv);
464 aURLs.AppendElement(spec);
467 return NS_OK;
470 bool ModuleLoaderBase::RemoveFetchedModule(nsIURI* aURL) {
471 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
472 RefPtr<ModuleScript> ms;
473 MOZ_ALWAYS_TRUE(mFetchedModules.Get(aURL, getter_AddRefs(ms)));
474 if (ms && ms->ModuleRecord()) {
475 JS::AssertModuleUnlinked(ms->ModuleRecord());
477 #endif
479 return mFetchedModules.Remove(aURL);
482 void ModuleLoaderBase::SetModuleFetchStarted(ModuleLoadRequest* aRequest) {
483 // Update the module map to indicate that a module is currently being fetched.
485 MOZ_ASSERT(aRequest->IsFetching());
486 MOZ_ASSERT(!ModuleMapContainsURL(aRequest->mURI));
488 mFetchingModules.InsertOrUpdate(aRequest->mURI, nullptr);
491 void ModuleLoaderBase::SetModuleFetchFinishedAndResumeWaitingRequests(
492 ModuleLoadRequest* aRequest, nsresult aResult) {
493 // Update module map with the result of fetching a single module script.
495 // If any requests for the same URL are waiting on this one to complete, call
496 // ModuleLoaded or LoadFailed to resume or fail them as appropriate.
498 MOZ_ASSERT(aRequest->mLoader == this);
500 LOG(
501 ("ScriptLoadRequest (%p): Module fetch finished (script == %p, result == "
502 "%u)",
503 aRequest, aRequest->mModuleScript.get(), unsigned(aResult)));
505 RefPtr<WaitingRequests> waitingRequests;
506 if (!mFetchingModules.Remove(aRequest->mURI,
507 getter_AddRefs(waitingRequests))) {
508 LOG(
509 ("ScriptLoadRequest (%p): Key not found in mFetchingModules, "
510 "assuming we have an inline module or have finished fetching already",
511 aRequest));
512 return;
515 RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript);
516 MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript);
518 mFetchedModules.InsertOrUpdate(aRequest->mURI, RefPtr{moduleScript});
520 if (waitingRequests) {
521 LOG(("ScriptLoadRequest (%p): Resuming waiting requests", aRequest));
522 ResumeWaitingRequests(waitingRequests, bool(moduleScript));
526 void ModuleLoaderBase::ResumeWaitingRequests(WaitingRequests* aWaitingRequests,
527 bool aSuccess) {
528 for (ModuleLoadRequest* request : aWaitingRequests->mWaiting) {
529 ResumeWaitingRequest(request, aSuccess);
533 void ModuleLoaderBase::ResumeWaitingRequest(ModuleLoadRequest* aRequest,
534 bool aSuccess) {
535 if (aSuccess) {
536 aRequest->ModuleLoaded();
537 } else {
538 aRequest->LoadFailed();
542 void ModuleLoaderBase::WaitForModuleFetch(ModuleLoadRequest* aRequest) {
543 nsIURI* uri = aRequest->mURI;
544 MOZ_ASSERT(ModuleMapContainsURL(uri));
546 if (auto entry = mFetchingModules.Lookup(uri)) {
547 RefPtr<WaitingRequests> waitingRequests = entry.Data();
548 if (!waitingRequests) {
549 waitingRequests = new WaitingRequests();
550 mFetchingModules.InsertOrUpdate(uri, waitingRequests);
553 waitingRequests->mWaiting.AppendElement(aRequest);
554 return;
557 RefPtr<ModuleScript> ms;
558 MOZ_ALWAYS_TRUE(mFetchedModules.Get(uri, getter_AddRefs(ms)));
560 ResumeWaitingRequest(aRequest, bool(ms));
563 ModuleScript* ModuleLoaderBase::GetFetchedModule(nsIURI* aURL) const {
564 if (LOG_ENABLED()) {
565 nsAutoCString url;
566 aURL->GetAsciiSpec(url);
567 LOG(("GetFetchedModule %s", url.get()));
570 bool found;
571 ModuleScript* ms = mFetchedModules.GetWeak(aURL, &found);
572 MOZ_ASSERT(found);
573 return ms;
576 nsresult ModuleLoaderBase::OnFetchComplete(ModuleLoadRequest* aRequest,
577 nsresult aRv) {
578 MOZ_ASSERT(aRequest->mLoader == this);
579 MOZ_ASSERT(!aRequest->mModuleScript);
581 nsresult rv = aRv;
582 if (NS_SUCCEEDED(rv)) {
583 rv = CreateModuleScript(aRequest);
585 // If a module script was created, it should either have a module record
586 // object or a parse error.
587 if (ModuleScript* ms = aRequest->mModuleScript) {
588 MOZ_DIAGNOSTIC_ASSERT(bool(ms->ModuleRecord()) != ms->HasParseError());
591 aRequest->ClearScriptSource();
593 if (NS_FAILED(rv)) {
594 aRequest->LoadFailed();
595 return rv;
599 MOZ_ASSERT(NS_SUCCEEDED(rv) == bool(aRequest->mModuleScript));
600 SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv);
602 if (!aRequest->IsErrored()) {
603 StartFetchingModuleDependencies(aRequest);
606 return NS_OK;
609 nsresult ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest* aRequest) {
610 MOZ_ASSERT(!aRequest->mModuleScript);
611 MOZ_ASSERT(aRequest->mBaseURL);
613 LOG(("ScriptLoadRequest (%p): Create module script", aRequest));
615 AutoJSAPI jsapi;
616 if (!jsapi.Init(mGlobalObject)) {
617 return NS_ERROR_FAILURE;
620 nsresult rv;
622 JSContext* cx = jsapi.cx();
623 JS::Rooted<JSObject*> module(cx);
625 JS::CompileOptions options(cx);
626 JS::RootedScript introductionScript(cx);
627 rv = mLoader->FillCompileOptionsForRequest(cx, aRequest, &options,
628 &introductionScript);
630 if (NS_SUCCEEDED(rv)) {
631 JS::Rooted<JSObject*> global(cx, mGlobalObject->GetGlobalJSObject());
632 rv = CompileFetchedModule(cx, global, options, aRequest, &module);
635 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
637 if (module) {
638 JS::RootedValue privateValue(cx);
639 JS::RootedScript moduleScript(cx, JS::GetModuleScript(module));
640 JS::InstantiateOptions instantiateOptions(options);
641 if (!JS::UpdateDebugMetadata(cx, moduleScript, instantiateOptions,
642 privateValue, nullptr, introductionScript,
643 nullptr)) {
644 return NS_ERROR_OUT_OF_MEMORY;
648 RefPtr<ModuleScript> moduleScript =
649 new ModuleScript(aRequest->mFetchOptions, aRequest->mBaseURL);
650 aRequest->mModuleScript = moduleScript;
652 if (!module) {
653 LOG(("ScriptLoadRequest (%p): compilation failed (%d)", aRequest,
654 unsigned(rv)));
656 JS::Rooted<JS::Value> error(cx);
657 if (!jsapi.HasException() || !jsapi.StealException(&error) ||
658 error.isUndefined()) {
659 aRequest->mModuleScript = nullptr;
660 return NS_ERROR_FAILURE;
663 moduleScript->SetParseError(error);
664 aRequest->ModuleErrored();
665 return NS_OK;
668 moduleScript->SetModuleRecord(module);
670 // Validate requested modules and treat failure to resolve module specifiers
671 // the same as a parse error.
672 rv = ResolveRequestedModules(aRequest, nullptr);
673 if (NS_FAILED(rv)) {
674 if (!aRequest->IsErrored()) {
675 aRequest->mModuleScript = nullptr;
676 return rv;
678 aRequest->ModuleErrored();
679 return NS_OK;
683 LOG(("ScriptLoadRequest (%p): module script == %p", aRequest,
684 aRequest->mModuleScript.get()));
686 return rv;
689 nsresult ModuleLoaderBase::GetResolveFailureMessage(ResolveError aError,
690 const nsAString& aSpecifier,
691 nsAString& aResult) {
692 AutoTArray<nsString, 1> errorParams;
693 errorParams.AppendElement(aSpecifier);
695 nsresult rv = nsContentUtils::FormatLocalizedString(
696 nsContentUtils::eDOM_PROPERTIES, ResolveErrorInfo::GetString(aError),
697 errorParams, aResult);
698 NS_ENSURE_SUCCESS(rv, rv);
699 return NS_OK;
702 nsresult ModuleLoaderBase::HandleResolveFailure(
703 JSContext* aCx, LoadedScript* aScript, const nsAString& aSpecifier,
704 ResolveError aError, uint32_t aLineNumber,
705 JS::ColumnNumberZeroOrigin aColumnNumber,
706 JS::MutableHandle<JS::Value> aErrorOut) {
707 JS::Rooted<JSString*> filename(aCx);
708 if (aScript) {
709 nsAutoCString url;
710 aScript->BaseURL()->GetAsciiSpec(url);
711 filename = JS_NewStringCopyZ(aCx, url.get());
712 } else {
713 filename = JS_NewStringCopyZ(aCx, "(unknown)");
716 if (!filename) {
717 return NS_ERROR_OUT_OF_MEMORY;
720 nsAutoString errorText;
721 nsresult rv = GetResolveFailureMessage(aError, aSpecifier, errorText);
722 NS_ENSURE_SUCCESS(rv, rv);
724 JS::Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, errorText.get()));
725 if (!string) {
726 return NS_ERROR_OUT_OF_MEMORY;
729 if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
730 JS::ColumnNumberOneOrigin(aColumnNumber), nullptr,
731 string, JS::NothingHandleValue, aErrorOut)) {
732 return NS_ERROR_OUT_OF_MEMORY;
735 return NS_OK;
738 // Helper for getting import maps pref across main thread and workers
739 bool ImportMapsEnabled() {
740 if (NS_IsMainThread()) {
741 return mozilla::StaticPrefs::dom_importMaps_enabled();
743 return false;
746 ResolveResult ModuleLoaderBase::ResolveModuleSpecifier(
747 LoadedScript* aScript, const nsAString& aSpecifier) {
748 // If import map is enabled, forward to the updated 'Resolve a module
749 // specifier' algorithm defined in Import maps spec.
751 // Once import map is enabled by default,
752 // ModuleLoaderBase::ResolveModuleSpecifier should be replaced by
753 // ImportMap::ResolveModuleSpecifier.
754 if (ImportMapsEnabled()) {
755 return ImportMap::ResolveModuleSpecifier(mImportMap.get(), mLoader, aScript,
756 aSpecifier);
759 // The following module specifiers are allowed by the spec:
760 // - a valid absolute URL
761 // - a valid relative URL that starts with "/", "./" or "../"
763 // Bareword module specifiers are handled in Import maps.
765 nsCOMPtr<nsIURI> uri;
766 nsresult rv = NS_NewURI(getter_AddRefs(uri), aSpecifier);
767 if (NS_SUCCEEDED(rv)) {
768 return WrapNotNull(uri);
771 if (rv != NS_ERROR_MALFORMED_URI) {
772 return Err(ResolveError::Failure);
775 if (!StringBeginsWith(aSpecifier, u"/"_ns) &&
776 !StringBeginsWith(aSpecifier, u"./"_ns) &&
777 !StringBeginsWith(aSpecifier, u"../"_ns)) {
778 return Err(ResolveError::FailureMayBeBare);
781 // Get the document's base URL if we don't have a referencing script here.
782 nsCOMPtr<nsIURI> baseURL;
783 if (aScript) {
784 baseURL = aScript->BaseURL();
785 } else {
786 baseURL = GetBaseURI();
789 rv = NS_NewURI(getter_AddRefs(uri), aSpecifier, nullptr, baseURL);
790 if (NS_SUCCEEDED(rv)) {
791 return WrapNotNull(uri);
794 return Err(ResolveError::Failure);
797 nsresult ModuleLoaderBase::ResolveRequestedModules(
798 ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>* aUrlsOut) {
799 ModuleScript* ms = aRequest->mModuleScript;
801 AutoJSAPI jsapi;
802 if (!jsapi.Init(mGlobalObject)) {
803 return NS_ERROR_FAILURE;
806 JSContext* cx = jsapi.cx();
807 JS::Rooted<JSObject*> moduleRecord(cx, ms->ModuleRecord());
808 uint32_t length = JS::GetRequestedModulesCount(cx, moduleRecord);
810 for (uint32_t i = 0; i < length; i++) {
811 JS::Rooted<JSString*> str(
812 cx, JS::GetRequestedModuleSpecifier(cx, moduleRecord, i));
813 MOZ_ASSERT(str);
815 nsAutoJSString specifier;
816 if (!specifier.init(cx, str)) {
817 return NS_ERROR_FAILURE;
820 // Let url be the result of resolving a module specifier given module script
821 // and requested.
822 ModuleLoaderBase* loader = aRequest->mLoader;
823 auto result = loader->ResolveModuleSpecifier(ms, specifier);
824 if (result.isErr()) {
825 uint32_t lineNumber = 0;
826 JS::ColumnNumberZeroOrigin columnNumber;
827 JS::GetRequestedModuleSourcePos(cx, moduleRecord, i, &lineNumber,
828 &columnNumber);
830 JS::Rooted<JS::Value> error(cx);
831 nsresult rv =
832 loader->HandleResolveFailure(cx, ms, specifier, result.unwrapErr(),
833 lineNumber, columnNumber, &error);
834 NS_ENSURE_SUCCESS(rv, rv);
836 ms->SetParseError(error);
837 return NS_ERROR_FAILURE;
840 nsCOMPtr<nsIURI> uri = result.unwrap();
841 if (aUrlsOut) {
842 aUrlsOut->AppendElement(uri.forget());
846 return NS_OK;
849 void ModuleLoaderBase::StartFetchingModuleDependencies(
850 ModuleLoadRequest* aRequest) {
851 LOG(("ScriptLoadRequest (%p): Start fetching module dependencies", aRequest));
853 if (aRequest->IsCanceled()) {
854 return;
857 MOZ_ASSERT(aRequest->mModuleScript);
858 MOZ_ASSERT(!aRequest->mModuleScript->HasParseError());
859 MOZ_ASSERT(aRequest->IsFetching() || aRequest->IsCompiling());
861 auto visitedSet = aRequest->mVisitedSet;
862 MOZ_ASSERT(visitedSet->Contains(aRequest->mURI));
864 aRequest->mState = ModuleLoadRequest::State::LoadingImports;
866 nsCOMArray<nsIURI> urls;
867 nsresult rv = ResolveRequestedModules(aRequest, &urls);
868 if (NS_FAILED(rv)) {
869 aRequest->mModuleScript = nullptr;
870 aRequest->ModuleErrored();
871 return;
874 // Remove already visited URLs from the list. Put unvisited URLs into the
875 // visited set.
876 int32_t i = 0;
877 while (i < urls.Count()) {
878 nsIURI* url = urls[i];
879 if (visitedSet->Contains(url)) {
880 urls.RemoveObjectAt(i);
881 } else {
882 visitedSet->PutEntry(url);
883 i++;
887 if (urls.Count() == 0) {
888 // There are no descendants to load so this request is ready.
889 aRequest->DependenciesLoaded();
890 return;
893 MOZ_ASSERT(aRequest->mAwaitingImports == 0);
894 aRequest->mAwaitingImports = urls.Count();
896 // For each url in urls, fetch a module script graph given url, module
897 // script's CORS setting, and module script's settings object.
898 for (auto* url : urls) {
899 StartFetchingModuleAndDependencies(aRequest, url);
903 void ModuleLoaderBase::StartFetchingModuleAndDependencies(
904 ModuleLoadRequest* aParent, nsIURI* aURI) {
905 MOZ_ASSERT(aURI);
907 RefPtr<ModuleLoadRequest> childRequest = CreateStaticImport(aURI, aParent);
909 aParent->mImports.AppendElement(childRequest);
911 if (LOG_ENABLED()) {
912 nsAutoCString url1;
913 aParent->mURI->GetAsciiSpec(url1);
915 nsAutoCString url2;
916 aURI->GetAsciiSpec(url2);
918 LOG(("ScriptLoadRequest (%p): Start fetching dependency %p", aParent,
919 childRequest.get()));
920 LOG(("StartFetchingModuleAndDependencies \"%s\" -> \"%s\"", url1.get(),
921 url2.get()));
924 MOZ_ASSERT(!childRequest->mWaitingParentRequest);
925 childRequest->mWaitingParentRequest = aParent;
927 nsresult rv = StartModuleLoad(childRequest);
928 if (NS_FAILED(rv)) {
929 MOZ_ASSERT(!childRequest->mModuleScript);
930 LOG(("ScriptLoadRequest (%p): rejecting %p", aParent,
931 childRequest.get()));
933 mLoader->ReportErrorToConsole(childRequest, rv);
934 childRequest->LoadFailed();
938 void ModuleLoadRequest::ChildLoadComplete(bool aSuccess) {
939 RefPtr<ModuleLoadRequest> parent = mWaitingParentRequest;
940 MOZ_ASSERT(parent);
941 MOZ_ASSERT(parent->mAwaitingImports != 0);
943 mWaitingParentRequest = nullptr;
944 parent->mAwaitingImports--;
946 if (parent->IsFinished()) {
947 MOZ_ASSERT_IF(!aSuccess, parent->IsErrored());
948 return;
951 if (!aSuccess) {
952 parent->ModuleErrored();
953 } else if (parent->mAwaitingImports == 0) {
954 parent->DependenciesLoaded();
958 void ModuleLoaderBase::StartDynamicImport(ModuleLoadRequest* aRequest) {
959 MOZ_ASSERT(aRequest->mLoader == this);
961 LOG(("ScriptLoadRequest (%p): Start dynamic import", aRequest));
963 mDynamicImportRequests.AppendElement(aRequest);
965 nsresult rv = StartModuleLoad(aRequest);
966 if (NS_FAILED(rv)) {
967 mLoader->ReportErrorToConsole(aRequest, rv);
968 FinishDynamicImportAndReject(aRequest, rv);
972 void ModuleLoaderBase::FinishDynamicImportAndReject(ModuleLoadRequest* aRequest,
973 nsresult aResult) {
974 AutoJSAPI jsapi;
975 MOZ_ASSERT(NS_FAILED(aResult));
976 if (!jsapi.Init(mGlobalObject)) {
977 return;
980 FinishDynamicImport(jsapi.cx(), aRequest, aResult, nullptr);
983 /* static */
984 void ModuleLoaderBase::FinishDynamicImport(
985 JSContext* aCx, ModuleLoadRequest* aRequest, nsresult aResult,
986 JS::Handle<JSObject*> aEvaluationPromise) {
987 LOG(("ScriptLoadRequest (%p): Finish dynamic import %x %d", aRequest,
988 unsigned(aResult), JS_IsExceptionPending(aCx)));
990 MOZ_ASSERT(GetCurrentModuleLoader(aCx) == aRequest->mLoader);
992 // If aResult is a failed result, we don't have an EvaluationPromise. If it
993 // succeeded, evaluationPromise may still be null, but in this case it will
994 // be handled by rejecting the dynamic module import promise in the JSAPI.
995 MOZ_ASSERT_IF(NS_FAILED(aResult), !aEvaluationPromise);
997 // Complete the dynamic import, report failures indicated by aResult or as a
998 // pending exception on the context.
1000 if (!aRequest->mDynamicPromise) {
1001 // Import has already been completed.
1002 return;
1005 if (NS_FAILED(aResult) &&
1006 aResult != NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
1007 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
1008 nsAutoCString url;
1009 aRequest->mURI->GetSpec(url);
1010 JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
1011 JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
1014 JS::Rooted<JS::Value> referencingScript(aCx,
1015 aRequest->mDynamicReferencingPrivate);
1016 JS::Rooted<JSString*> specifier(aCx, aRequest->mDynamicSpecifier);
1017 JS::Rooted<JSObject*> promise(aCx, aRequest->mDynamicPromise);
1019 JS::Rooted<JSObject*> moduleRequest(aCx,
1020 JS::CreateModuleRequest(aCx, specifier));
1022 JS::FinishDynamicModuleImport(aCx, aEvaluationPromise, referencingScript,
1023 moduleRequest, promise);
1025 // FinishDynamicModuleImport clears any pending exception.
1026 MOZ_ASSERT(!JS_IsExceptionPending(aCx));
1028 aRequest->ClearDynamicImport();
1031 ModuleLoaderBase::ModuleLoaderBase(ScriptLoaderInterface* aLoader,
1032 nsIGlobalObject* aGlobalObject,
1033 nsISerialEventTarget* aEventTarget)
1034 : mGlobalObject(aGlobalObject),
1035 mEventTarget(aEventTarget),
1036 mLoader(aLoader) {
1037 MOZ_ASSERT(mGlobalObject);
1038 MOZ_ASSERT(mEventTarget);
1039 MOZ_ASSERT(mLoader);
1041 EnsureModuleHooksInitialized();
1044 ModuleLoaderBase::~ModuleLoaderBase() {
1045 mDynamicImportRequests.CancelRequestsAndClear();
1047 LOG(("ModuleLoaderBase::~ModuleLoaderBase %p", this));
1050 void ModuleLoaderBase::Shutdown() {
1051 CancelAndClearDynamicImports();
1053 for (const auto& entry : mFetchingModules) {
1054 RefPtr<WaitingRequests> waitingRequests(entry.GetData());
1055 if (waitingRequests) {
1056 ResumeWaitingRequests(waitingRequests, false);
1060 for (const auto& entry : mFetchedModules) {
1061 if (entry.GetData()) {
1062 entry.GetData()->Shutdown();
1066 mFetchingModules.Clear();
1067 mFetchedModules.Clear();
1068 mGlobalObject = nullptr;
1069 mEventTarget = nullptr;
1070 mLoader = nullptr;
1073 bool ModuleLoaderBase::HasPendingDynamicImports() const {
1074 return !mDynamicImportRequests.isEmpty();
1077 void ModuleLoaderBase::CancelDynamicImport(ModuleLoadRequest* aRequest,
1078 nsresult aResult) {
1079 MOZ_ASSERT(aRequest->mLoader == this);
1081 RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
1082 if (!aRequest->IsCanceled()) {
1083 aRequest->Cancel();
1084 // FinishDynamicImport must happen exactly once for each dynamic import
1085 // request. If the load is aborted we do it when we remove the request
1086 // from mDynamicImportRequests.
1087 FinishDynamicImportAndReject(aRequest, aResult);
1091 void ModuleLoaderBase::RemoveDynamicImport(ModuleLoadRequest* aRequest) {
1092 MOZ_ASSERT(aRequest->IsDynamicImport());
1093 mDynamicImportRequests.Remove(aRequest);
1096 #ifdef DEBUG
1097 bool ModuleLoaderBase::HasDynamicImport(
1098 const ModuleLoadRequest* aRequest) const {
1099 MOZ_ASSERT(aRequest->mLoader == this);
1100 return mDynamicImportRequests.Contains(
1101 const_cast<ModuleLoadRequest*>(aRequest));
1103 #endif
1105 JS::Value ModuleLoaderBase::FindFirstParseError(ModuleLoadRequest* aRequest) {
1106 MOZ_ASSERT(aRequest);
1108 ModuleScript* moduleScript = aRequest->mModuleScript;
1109 MOZ_ASSERT(moduleScript);
1111 if (moduleScript->HasParseError()) {
1112 return moduleScript->ParseError();
1115 for (ModuleLoadRequest* childRequest : aRequest->mImports) {
1116 JS::Value error = FindFirstParseError(childRequest);
1117 if (!error.isUndefined()) {
1118 return error;
1122 return JS::UndefinedValue();
1125 bool ModuleLoaderBase::InstantiateModuleGraph(ModuleLoadRequest* aRequest) {
1126 // Instantiate a top-level module and record any error.
1128 MOZ_ASSERT(aRequest);
1129 MOZ_ASSERT(aRequest->mLoader == this);
1130 MOZ_ASSERT(aRequest->IsTopLevel());
1132 LOG(("ScriptLoadRequest (%p): Instantiate module graph", aRequest));
1134 AUTO_PROFILER_LABEL("ModuleLoaderBase::InstantiateModuleGraph", JS);
1136 ModuleScript* moduleScript = aRequest->mModuleScript;
1137 MOZ_ASSERT(moduleScript);
1139 JS::Value parseError = FindFirstParseError(aRequest);
1140 if (!parseError.isUndefined()) {
1141 moduleScript->SetErrorToRethrow(parseError);
1142 LOG(("ScriptLoadRequest (%p): found parse error", aRequest));
1143 return true;
1146 MOZ_ASSERT(moduleScript->ModuleRecord());
1148 AutoJSAPI jsapi;
1149 if (NS_WARN_IF(!jsapi.Init(mGlobalObject))) {
1150 return false;
1153 JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord());
1154 if (!xpc::Scriptability::AllowedIfExists(module)) {
1155 return true;
1158 if (!JS::ModuleLink(jsapi.cx(), module)) {
1159 LOG(("ScriptLoadRequest (%p): Instantiate failed", aRequest));
1160 MOZ_ASSERT(jsapi.HasException());
1161 JS::RootedValue exception(jsapi.cx());
1162 if (!jsapi.StealException(&exception)) {
1163 return false;
1165 MOZ_ASSERT(!exception.isUndefined());
1166 moduleScript->SetErrorToRethrow(exception);
1169 return true;
1172 nsresult ModuleLoaderBase::InitDebuggerDataForModuleGraph(
1173 JSContext* aCx, ModuleLoadRequest* aRequest) {
1174 // JS scripts can be associated with a DOM element for use by the debugger,
1175 // but preloading can cause scripts to be compiled before DOM script element
1176 // nodes have been created. This method ensures that this association takes
1177 // place before the first time a module script is run.
1179 MOZ_ASSERT(aRequest);
1181 ModuleScript* moduleScript = aRequest->mModuleScript;
1182 if (moduleScript->DebuggerDataInitialized()) {
1183 return NS_OK;
1186 for (ModuleLoadRequest* childRequest : aRequest->mImports) {
1187 nsresult rv = InitDebuggerDataForModuleGraph(aCx, childRequest);
1188 NS_ENSURE_SUCCESS(rv, rv);
1191 JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
1192 MOZ_ASSERT(module);
1194 // The script is now ready to be exposed to the debugger.
1195 JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(module));
1196 JS::ExposeScriptToDebugger(aCx, script);
1198 moduleScript->SetDebuggerDataInitialized();
1199 return NS_OK;
1202 void ModuleLoaderBase::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
1203 if (!aRequest->mModuleScript) {
1204 FinishDynamicImportAndReject(aRequest, NS_ERROR_FAILURE);
1205 return;
1208 InstantiateAndEvaluateDynamicImport(aRequest);
1211 void ModuleLoaderBase::InstantiateAndEvaluateDynamicImport(
1212 ModuleLoadRequest* aRequest) {
1213 MOZ_ASSERT(aRequest->mModuleScript);
1215 if (!InstantiateModuleGraph(aRequest)) {
1216 aRequest->mModuleScript = nullptr;
1219 nsresult rv = NS_ERROR_FAILURE;
1220 if (aRequest->mModuleScript) {
1221 rv = EvaluateModule(aRequest);
1224 if (NS_FAILED(rv)) {
1225 FinishDynamicImportAndReject(aRequest, rv);
1229 nsresult ModuleLoaderBase::EvaluateModule(ModuleLoadRequest* aRequest) {
1230 MOZ_ASSERT(aRequest->mLoader == this);
1232 mozilla::nsAutoMicroTask mt;
1233 mozilla::dom::AutoEntryScript aes(mGlobalObject, "EvaluateModule",
1234 NS_IsMainThread());
1236 return EvaluateModuleInContext(aes.cx(), aRequest,
1237 JS::ReportModuleErrorsAsync);
1240 nsresult ModuleLoaderBase::EvaluateModuleInContext(
1241 JSContext* aCx, ModuleLoadRequest* aRequest,
1242 JS::ModuleErrorBehaviour errorBehaviour) {
1243 MOZ_ASSERT(aRequest->mLoader == this);
1244 MOZ_ASSERT(mGlobalObject->GetModuleLoader(aCx) == this);
1246 AUTO_PROFILER_LABEL("ModuleLoaderBase::EvaluateModule", JS);
1248 nsAutoCString profilerLabelString;
1249 if (aRequest->HasScriptLoadContext()) {
1250 aRequest->GetScriptLoadContext()->GetProfilerLabel(profilerLabelString);
1253 LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
1254 AUTO_PROFILER_MARKER_TEXT("ModuleEvaluation", JS,
1255 MarkerInnerWindowIdFromJSContext(aCx),
1256 profilerLabelString);
1258 ModuleLoadRequest* request = aRequest->AsModuleRequest();
1259 MOZ_ASSERT(request->mModuleScript);
1260 MOZ_ASSERT_IF(request->HasScriptLoadContext(),
1261 !request->GetScriptLoadContext()->mCompileOrDecodeTask);
1263 ModuleScript* moduleScript = request->mModuleScript;
1264 if (moduleScript->HasErrorToRethrow()) {
1265 LOG(("ScriptLoadRequest (%p): module has error to rethrow", aRequest));
1266 JS::Rooted<JS::Value> error(aCx, moduleScript->ErrorToRethrow());
1267 JS_SetPendingException(aCx, error);
1268 // For a dynamic import, the promise is rejected. Otherwise an error
1269 // is either reported by AutoEntryScript.
1270 if (request->IsDynamicImport()) {
1271 FinishDynamicImport(aCx, request, NS_OK, nullptr);
1273 return NS_OK;
1276 JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
1277 MOZ_ASSERT(module);
1278 MOZ_ASSERT(CurrentGlobalOrNull(aCx) == GetNonCCWObjectGlobal(module));
1280 if (!xpc::Scriptability::AllowedIfExists(module)) {
1281 return NS_OK;
1284 nsresult rv = InitDebuggerDataForModuleGraph(aCx, request);
1285 NS_ENSURE_SUCCESS(rv, rv);
1287 if (request->HasScriptLoadContext()) {
1288 TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
1289 "scriptloader_evaluate_module");
1292 JS::Rooted<JS::Value> rval(aCx);
1294 mLoader->MaybePrepareModuleForBytecodeEncodingBeforeExecute(aCx, request);
1296 bool ok = JS::ModuleEvaluate(aCx, module, &rval);
1298 // ModuleEvaluate will usually set a pending exception if it returns false,
1299 // unless the user cancels execution.
1300 MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aCx));
1302 if (!ok || IsModuleEvaluationAborted(request)) {
1303 LOG(("ScriptLoadRequest (%p): evaluation failed", aRequest));
1304 // For a dynamic import, the promise is rejected. Otherwise an error is
1305 // reported by AutoEntryScript.
1306 rv = NS_ERROR_ABORT;
1309 // ModuleEvaluate returns a promise unless the user cancels the execution in
1310 // which case rval will be undefined. We should treat it as a failed
1311 // evaluation, and reject appropriately.
1312 JS::Rooted<JSObject*> evaluationPromise(aCx);
1313 if (rval.isObject()) {
1314 evaluationPromise.set(&rval.toObject());
1317 if (request->IsDynamicImport()) {
1318 if (NS_FAILED(rv)) {
1319 FinishDynamicImportAndReject(request, rv);
1320 } else {
1321 FinishDynamicImport(aCx, request, NS_OK, evaluationPromise);
1323 } else {
1324 // If this is not a dynamic import, and if the promise is rejected,
1325 // the value is unwrapped from the promise value.
1326 if (!JS::ThrowOnModuleEvaluationFailure(aCx, evaluationPromise,
1327 errorBehaviour)) {
1328 LOG(("ScriptLoadRequest (%p): evaluation failed on throw", aRequest));
1329 // For a dynamic import, the promise is rejected. Otherwise an error is
1330 // reported by AutoEntryScript.
1334 rv = mLoader->MaybePrepareModuleForBytecodeEncodingAfterExecute(request,
1335 NS_OK);
1337 mLoader->MaybeTriggerBytecodeEncoding();
1339 return rv;
1342 void ModuleLoaderBase::CancelAndClearDynamicImports() {
1343 while (ScriptLoadRequest* req = mDynamicImportRequests.getFirst()) {
1344 // This also removes the request from the list.
1345 CancelDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
1349 UniquePtr<ImportMap> ModuleLoaderBase::ParseImportMap(
1350 ScriptLoadRequest* aRequest) {
1351 AutoJSAPI jsapi;
1352 if (!jsapi.Init(GetGlobalObject())) {
1353 return nullptr;
1356 MOZ_ASSERT(aRequest->IsTextSource());
1357 MaybeSourceText maybeSource;
1358 nsresult rv = aRequest->GetScriptSource(jsapi.cx(), &maybeSource);
1359 if (NS_FAILED(rv)) {
1360 return nullptr;
1363 JS::SourceText<char16_t>& text = maybeSource.ref<SourceText<char16_t>>();
1364 ReportWarningHelper warning{mLoader, aRequest};
1366 // https://html.spec.whatwg.org/multipage/webappapis.html#create-an-import-map-parse-result
1367 // Step 2. Parse an import map string given input and baseURL, catching any
1368 // exceptions. If this threw an exception, then set result's error to rethrow
1369 // to that exception. Otherwise, set result's import map to the return value.
1371 // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
1372 // Step 1. If result's error to rethrow is not null, then report the exception
1373 // given by result's error to rethrow and return.
1375 // Impl note: We didn't implement 'Import map parse result' from the spec,
1376 // https://html.spec.whatwg.org/multipage/webappapis.html#import-map-parse-result
1377 // As the struct has another item called 'error to rethow' to store the
1378 // exception thrown during parsing import-maps, and report that exception
1379 // while registering an import map. Currently only inline import-maps are
1380 // supported, therefore parsing and registering import-maps will be executed
1381 // consecutively. To simplify the implementation, we didn't create the 'error
1382 // to rethow' item and report the exception immediately(done in ~AutoJSAPI).
1383 return ImportMap::ParseString(jsapi.cx(), text, aRequest->mBaseURL, warning);
1386 void ModuleLoaderBase::RegisterImportMap(UniquePtr<ImportMap> aImportMap) {
1387 // Check for aImportMap is done in ScriptLoader.
1388 MOZ_ASSERT(aImportMap);
1390 // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
1391 // The step 1(report the exception if there's an error) is done in
1392 // ParseImportMap.
1394 // Step 2. Assert: global's import map is an empty import map.
1395 // Impl note: The default import map from the spec is an empty import map, but
1396 // from the implementation it defaults to nullptr, so we check if the global's
1397 // import map is null here.
1399 // Also see
1400 // https://html.spec.whatwg.org/multipage/webappapis.html#empty-import-map
1401 MOZ_ASSERT(!mImportMap);
1403 // Step 3. Set global's import map to result's import map.
1404 mImportMap = std::move(aImportMap);
1407 #undef LOG
1408 #undef LOG_ENABLED
1410 } // namespace JS::loader