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