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
;
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(
49 MOZ_LOG(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug, \
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
)
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 //////////////////////////////////////////////////////////////
70 //////////////////////////////////////////////////////////////
72 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase
)
73 NS_INTERFACE_MAP_ENTRY(nsISupports
)
76 NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase
, mFetchingModules
, mFetchedModules
,
77 mDynamicImportRequests
, mGlobalObject
, mEventTarget
,
80 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase
)
81 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase
)
84 void ModuleLoaderBase::EnsureModuleHooksInitialized() {
87 JSRuntime
* rt
= JS_GetRuntime(jsapi
.cx());
88 if (JS::GetModuleResolveHook(rt
)) {
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
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.
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
128 RefPtr
<LoadedScript
> script(
129 GetLoadedScriptOrNull(aCx
, aReferencingPrivate
));
131 JS::Rooted
<JSString
*> specifierString(
132 aCx
, JS::GetModuleRequestSpecifier(aCx
, aModuleRequest
));
133 if (!specifierString
) {
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
)) {
144 RefPtr
<ModuleLoaderBase
> loader
= GetCurrentModuleLoader(aCx
);
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());
169 bool ModuleLoaderBase::ImportMetaResolve(JSContext
* cx
, unsigned argc
,
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
));
185 // Step 4.2, 4.3 are implemented in ImportMetaResolveImpl.
186 RootedString
url(cx
, ImportMetaResolveImpl(cx
, modulePrivate
, specifier
));
191 // Step 4.4. Return the serialization of url.
192 args
.rval().setString(url
);
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
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
);
216 nsAutoJSString specifier
;
217 if (!specifier
.init(aCx
, aSpecifier
)) {
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
);
228 JS_ReportOutOfMemory(aCx
);
232 JS_SetPendingException(aCx
, error
);
237 nsCOMPtr
<nsIURI
> uri
= result
.unwrap();
239 MOZ_ALWAYS_SUCCEEDS(uri
->GetAsciiSpec(url
));
241 urlString
.set(JS_NewStringCopyZ(aCx
, url
.get()));
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
);
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()));
263 JS_ReportOutOfMemory(aCx
);
267 // https://html.spec.whatwg.org/#import-meta-url
268 if (!JS_DefineProperty(aCx
, aMetaObject
, "url", urlString
,
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
,
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
);
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
) {
306 // Attempt to resolve the module specifier.
307 nsAutoJSString specifier
;
308 if (!specifier
.init(aCx
, specifierString
)) {
312 RefPtr
<ModuleLoaderBase
> loader
= GetCurrentModuleLoader(aCx
);
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
);
324 JS_ReportOutOfMemory(aCx
);
328 JS_SetPendingException(aCx
, error
);
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
);
338 // Throws TypeError if CreateDynamicImport returns nullptr.
339 JS_ReportErrorNumberASCII(aCx
, js::GetErrorMessage
, nullptr,
340 JSMSG_DYNAMIC_IMPORT_NOT_SUPPORTED
);
345 loader
->StartDynamicImport(request
);
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
));
360 nsIGlobalObject
* global
= xpc::NativeGlobal(object
);
365 ModuleLoaderBase
* loader
= global
->GetModuleLoader(aCx
);
370 MOZ_ASSERT(loader
->mGlobalObject
== global
);
372 reportError
.release();
377 LoadedScript
* ModuleLoaderBase::GetLoadedScriptOrNull(
378 JSContext
* aCx
, JS::Handle
<JS::Value
> aReferencingPrivate
) {
379 if (aReferencingPrivate
.isUndefined()) {
383 auto* script
= static_cast<LoadedScript
*>(aReferencingPrivate
.toPrivate());
384 if (script
->IsEventScript()) {
389 script
->IsModuleScript(),
390 JS::GetModulePrivate(script
->AsModuleScript()->ModuleRecord()) ==
391 aReferencingPrivate
);
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
413 MOZ_ASSERT_IF(aRestart
== RestartRequest::Yes
,
414 IsModuleFetching(aRequest
->mURI
));
416 // Check with the derived class whether we should load this module.
418 if (!CanStartLoad(aRequest
, &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
);
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());
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();
461 nsresult rv
= uri
->GetSpec(spec
);
462 NS_ENSURE_SUCCESS(rv
, rv
);
464 aURLs
.AppendElement(spec
);
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());
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);
501 ("ScriptLoadRequest (%p): Module fetch finished (script == %p, result == "
503 aRequest
, aRequest
->mModuleScript
.get(), unsigned(aResult
)));
505 RefPtr
<WaitingRequests
> waitingRequests
;
506 if (!mFetchingModules
.Remove(aRequest
->mURI
,
507 getter_AddRefs(waitingRequests
))) {
509 ("ScriptLoadRequest (%p): Key not found in mFetchingModules, "
510 "assuming we have an inline module or have finished fetching already",
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
,
528 for (ModuleLoadRequest
* request
: aWaitingRequests
->mWaiting
) {
529 ResumeWaitingRequest(request
, aSuccess
);
533 void ModuleLoaderBase::ResumeWaitingRequest(ModuleLoadRequest
* aRequest
,
536 aRequest
->ModuleLoaded();
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
);
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 {
566 aURL
->GetAsciiSpec(url
);
567 LOG(("GetFetchedModule %s", url
.get()));
571 ModuleScript
* ms
= mFetchedModules
.GetWeak(aURL
, &found
);
576 nsresult
ModuleLoaderBase::OnFetchComplete(ModuleLoadRequest
* aRequest
,
578 MOZ_ASSERT(aRequest
->mLoader
== this);
579 MOZ_ASSERT(!aRequest
->mModuleScript
);
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();
594 aRequest
->LoadFailed();
599 MOZ_ASSERT(NS_SUCCEEDED(rv
) == bool(aRequest
->mModuleScript
));
600 SetModuleFetchFinishedAndResumeWaitingRequests(aRequest
, rv
);
602 if (!aRequest
->IsErrored()) {
603 StartFetchingModuleDependencies(aRequest
);
609 nsresult
ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest
* aRequest
) {
610 MOZ_ASSERT(!aRequest
->mModuleScript
);
611 MOZ_ASSERT(aRequest
->mBaseURL
);
613 LOG(("ScriptLoadRequest (%p): Create module script", aRequest
));
616 if (!jsapi
.Init(mGlobalObject
)) {
617 return NS_ERROR_FAILURE
;
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));
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
,
644 return NS_ERROR_OUT_OF_MEMORY
;
648 RefPtr
<ModuleScript
> moduleScript
=
649 new ModuleScript(aRequest
->mFetchOptions
, aRequest
->mBaseURL
);
650 aRequest
->mModuleScript
= moduleScript
;
653 LOG(("ScriptLoadRequest (%p): compilation failed (%d)", aRequest
,
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();
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);
674 if (!aRequest
->IsErrored()) {
675 aRequest
->mModuleScript
= nullptr;
678 aRequest
->ModuleErrored();
683 LOG(("ScriptLoadRequest (%p): module script == %p", aRequest
,
684 aRequest
->mModuleScript
.get()));
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
);
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
);
710 aScript
->BaseURL()->GetAsciiSpec(url
);
711 filename
= JS_NewStringCopyZ(aCx
, url
.get());
713 filename
= JS_NewStringCopyZ(aCx
, "(unknown)");
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()));
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
;
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();
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
,
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
;
784 baseURL
= aScript
->BaseURL();
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
;
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
));
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
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
,
830 JS::Rooted
<JS::Value
> error(cx
);
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();
842 aUrlsOut
->AppendElement(uri
.forget());
849 void ModuleLoaderBase::StartFetchingModuleDependencies(
850 ModuleLoadRequest
* aRequest
) {
851 LOG(("ScriptLoadRequest (%p): Start fetching module dependencies", aRequest
));
853 if (aRequest
->IsCanceled()) {
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
);
869 aRequest
->mModuleScript
= nullptr;
870 aRequest
->ModuleErrored();
874 // Remove already visited URLs from the list. Put unvisited URLs into the
877 while (i
< urls
.Count()) {
878 nsIURI
* url
= urls
[i
];
879 if (visitedSet
->Contains(url
)) {
880 urls
.RemoveObjectAt(i
);
882 visitedSet
->PutEntry(url
);
887 if (urls
.Count() == 0) {
888 // There are no descendants to load so this request is ready.
889 aRequest
->DependenciesLoaded();
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
) {
907 RefPtr
<ModuleLoadRequest
> childRequest
= CreateStaticImport(aURI
, aParent
);
909 aParent
->mImports
.AppendElement(childRequest
);
913 aParent
->mURI
->GetAsciiSpec(url1
);
916 aURI
->GetAsciiSpec(url2
);
918 LOG(("ScriptLoadRequest (%p): Start fetching dependency %p", aParent
,
919 childRequest
.get()));
920 LOG(("StartFetchingModuleAndDependencies \"%s\" -> \"%s\"", url1
.get(),
924 MOZ_ASSERT(!childRequest
->mWaitingParentRequest
);
925 childRequest
->mWaitingParentRequest
= aParent
;
927 nsresult rv
= StartModuleLoad(childRequest
);
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
;
941 MOZ_ASSERT(parent
->mAwaitingImports
!= 0);
943 mWaitingParentRequest
= nullptr;
944 parent
->mAwaitingImports
--;
946 if (parent
->IsFinished()) {
947 MOZ_ASSERT_IF(!aSuccess
, parent
->IsErrored());
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
);
967 mLoader
->ReportErrorToConsole(aRequest
, rv
);
968 FinishDynamicImportAndReject(aRequest
, rv
);
972 void ModuleLoaderBase::FinishDynamicImportAndReject(ModuleLoadRequest
* aRequest
,
975 MOZ_ASSERT(NS_FAILED(aResult
));
976 if (!jsapi
.Init(mGlobalObject
)) {
980 FinishDynamicImport(jsapi
.cx(), aRequest
, aResult
, nullptr);
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.
1005 if (NS_FAILED(aResult
) &&
1006 aResult
!= NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE
) {
1007 MOZ_ASSERT(!JS_IsExceptionPending(aCx
));
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
),
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;
1073 bool ModuleLoaderBase::HasPendingDynamicImports() const {
1074 return !mDynamicImportRequests
.isEmpty();
1077 void ModuleLoaderBase::CancelDynamicImport(ModuleLoadRequest
* aRequest
,
1079 MOZ_ASSERT(aRequest
->mLoader
== this);
1081 RefPtr
<ScriptLoadRequest
> req
= mDynamicImportRequests
.Steal(aRequest
);
1082 if (!aRequest
->IsCanceled()) {
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
);
1097 bool ModuleLoaderBase::HasDynamicImport(
1098 const ModuleLoadRequest
* aRequest
) const {
1099 MOZ_ASSERT(aRequest
->mLoader
== this);
1100 return mDynamicImportRequests
.Contains(
1101 const_cast<ModuleLoadRequest
*>(aRequest
));
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()) {
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
));
1146 MOZ_ASSERT(moduleScript
->ModuleRecord());
1149 if (NS_WARN_IF(!jsapi
.Init(mGlobalObject
))) {
1153 JS::Rooted
<JSObject
*> module(jsapi
.cx(), moduleScript
->ModuleRecord());
1154 if (!xpc::Scriptability::AllowedIfExists(module
)) {
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
)) {
1165 MOZ_ASSERT(!exception
.isUndefined());
1166 moduleScript
->SetErrorToRethrow(exception
);
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()) {
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());
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();
1202 void ModuleLoaderBase::ProcessDynamicImport(ModuleLoadRequest
* aRequest
) {
1203 if (!aRequest
->mModuleScript
) {
1204 FinishDynamicImportAndReject(aRequest
, NS_ERROR_FAILURE
);
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",
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);
1276 JS::Rooted
<JSObject
*> module(aCx
, moduleScript
->ModuleRecord());
1278 MOZ_ASSERT(CurrentGlobalOrNull(aCx
) == GetNonCCWObjectGlobal(module
));
1280 if (!xpc::Scriptability::AllowedIfExists(module
)) {
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
);
1321 FinishDynamicImport(aCx
, request
, NS_OK
, evaluationPromise
);
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
,
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
,
1337 mLoader
->MaybeTriggerBytecodeEncoding();
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
) {
1352 if (!jsapi
.Init(GetGlobalObject())) {
1356 MOZ_ASSERT(aRequest
->IsTextSource());
1357 MaybeSourceText maybeSource
;
1358 nsresult rv
= aRequest
->GetScriptSource(jsapi
.cx(), &maybeSource
);
1359 if (NS_FAILED(rv
)) {
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
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.
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
);
1410 } // namespace JS::loader