tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ModuleLoaderBase.cpp (57962B)


      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/. */
      6 
      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"
     13 
     14 #include "js/Array.h"  // JS::GetArrayLength
     15 #include "js/CompilationAndEvaluation.h"
     16 #include "js/ColumnNumber.h"          // JS::ColumnNumberOneOrigin
     17 #include "js/ContextOptions.h"        // JS::ContextOptionsRef
     18 #include "js/ErrorReport.h"           // JSErrorBase
     19 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     20 #include "js/Modules.h"  // JS::FinishLoadingImportedModule, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{Load,Metadata}Hook
     21 #include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_GetElement
     22 #include "js/SourceText.h"
     23 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     24 #include "mozilla/BasePrincipal.h"
     25 #include "mozilla/dom/AutoEntryScript.h"
     26 #include "mozilla/dom/ScriptLoadContext.h"
     27 #include "mozilla/CycleCollectedJSContext.h"  // nsAutoMicroTask
     28 #include "mozilla/Preferences.h"
     29 #include "mozilla/RefPtr.h"  // mozilla::StaticRefPtr
     30 #include "mozilla/StaticPrefs_dom.h"
     31 #include "nsContentUtils.h"
     32 #include "nsICacheInfoChannel.h"  // nsICacheInfoChannel
     33 #include "nsNetUtil.h"            // NS_NewURI
     34 #include "xpcpublic.h"
     35 
     36 using mozilla::AutoSlowOperation;
     37 using mozilla::CycleCollectedJSContext;
     38 using mozilla::Err;
     39 using mozilla::MicroTaskRunnable;
     40 using mozilla::Preferences;
     41 using mozilla::UniquePtr;
     42 using mozilla::WrapNotNull;
     43 using mozilla::dom::AutoJSAPI;
     44 using mozilla::dom::ReferrerPolicy;
     45 
     46 namespace JS::loader {
     47 
     48 mozilla::LazyLogModule ModuleLoaderBase::gCspPRLog("CSP");
     49 mozilla::LazyLogModule ModuleLoaderBase::gModuleLoaderBaseLog(
     50    "ModuleLoaderBase");
     51 
     52 #undef LOG
     53 #define LOG(args)                                                           \
     54  MOZ_LOG(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug, \
     55          args)
     56 
     57 #define LOG_ENABLED() \
     58  MOZ_LOG_TEST(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug)
     59 
     60 //////////////////////////////////////////////////////////////
     61 // ModuleLoaderBase::LoadingRequest
     62 //////////////////////////////////////////////////////////////
     63 
     64 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase::LoadingRequest)
     65  NS_INTERFACE_MAP_ENTRY(nsISupports)
     66 NS_INTERFACE_MAP_END
     67 
     68 NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase::LoadingRequest, mRequest, mWaiting)
     69 
     70 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase::LoadingRequest)
     71 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase::LoadingRequest)
     72 
     73 //////////////////////////////////////////////////////////////
     74 // ModuleLoaderBase
     75 //////////////////////////////////////////////////////////////
     76 
     77 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase)
     78  NS_INTERFACE_MAP_ENTRY(nsISupports)
     79 NS_INTERFACE_MAP_END
     80 
     81 NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase, mFetchingModules, mFetchedModules,
     82                         mDynamicImportRequests, mGlobalObject, mOverriddenBy,
     83                         mLoader)
     84 
     85 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase)
     86 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase)
     87 
     88 // static
     89 void ModuleLoaderBase::EnsureModuleHooksInitialized() {
     90  AutoJSAPI jsapi;
     91  jsapi.Init();
     92  JSRuntime* rt = JS_GetRuntime(jsapi.cx());
     93  if (GetModuleLoadHook(rt)) {
     94    return;
     95  }
     96 
     97  SetModuleLoadHook(rt, HostLoadImportedModule);
     98  SetModuleMetadataHook(rt, HostPopulateImportMeta);
     99  SetScriptPrivateReferenceHooks(rt, HostAddRefTopLevelScript,
    100                                 HostReleaseTopLevelScript);
    101 }
    102 
    103 static bool CreateBadModuleTypeError(JSContext* aCx, LoadedScript* aScript,
    104                                     nsIURI* aURI,
    105                                     MutableHandle<Value> aErrorOut) {
    106  Rooted<JSString*> filename(aCx);
    107  if (aScript) {
    108    nsAutoCString url;
    109    aScript->BaseURL()->GetAsciiSpec(url);
    110    filename = JS_NewStringCopyZ(aCx, url.get());
    111  } else {
    112    filename = JS_NewStringCopyZ(aCx, "(unknown)");
    113  }
    114 
    115  if (!filename) {
    116    return false;
    117  }
    118 
    119  MOZ_ASSERT(aURI);
    120  nsAutoCString url;
    121  aURI->GetSpec(url);
    122 
    123  Rooted<JSString*> uri(aCx, JS_NewStringCopyZ(aCx, url.get()));
    124  if (!uri) {
    125    return false;
    126  }
    127 
    128  Rooted<JSString*> msg(aCx, JS_NewStringCopyZ(aCx, ": invalid module type"));
    129  if (!msg) {
    130    return false;
    131  }
    132 
    133  Rooted<JSString*> errMsg(aCx, JS_ConcatStrings(aCx, uri, msg));
    134  if (!errMsg) {
    135    return false;
    136  }
    137 
    138  return CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0,
    139                     ColumnNumberOneOrigin(), nullptr, errMsg,
    140                     NothingHandleValue, aErrorOut);
    141 }
    142 
    143 // https://html.spec.whatwg.org/#hostloadimportedmodule
    144 // static
    145 bool ModuleLoaderBase::HostLoadImportedModule(
    146    JSContext* aCx, Handle<JSScript*> aReferrer,
    147    Handle<JSObject*> aModuleRequest, Handle<Value> aHostDefined,
    148    Handle<Value> aPayload, uint32_t aLineNumber,
    149    JS::ColumnNumberOneOrigin aColumnNumber) {
    150  Rooted<JSObject*> object(aCx);
    151  if (aPayload.isObject()) {
    152    object = &aPayload.toObject();
    153  }
    154  bool isDynamicImport = object && IsPromiseObject(object);
    155 
    156  Rooted<JSString*> specifierString(
    157      aCx, GetModuleRequestSpecifier(aCx, aModuleRequest));
    158  if (!specifierString) {
    159    JS_ReportOutOfMemory(aCx);
    160    return false;
    161  }
    162 
    163  // Let url be the result of resolving a module specifier given referencing
    164  // module script and specifier.
    165  nsAutoJSString string;
    166  if (!string.init(aCx, specifierString)) {
    167    JS_ReportOutOfMemory(aCx);
    168    return false;
    169  }
    170 
    171  RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
    172  if (!loader) {
    173    return false;
    174  }
    175 
    176  if (isDynamicImport && !loader->IsDynamicImportSupported()) {
    177    JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
    178                              JSMSG_DYNAMIC_IMPORT_NOT_SUPPORTED);
    179    return false;
    180  }
    181 
    182  {
    183    // LoadedScript should only live in this block, otherwise it will be a GC
    184    // hazard
    185    RefPtr<LoadedScript> script(GetLoadedScriptOrNull(aReferrer));
    186 
    187    // Step 8. Let url be the result of resolving a module specifier given
    188    //   referencingScript and moduleRequest.[[Specifier]], catching any
    189    //   exceptions. If they throw an exception, let resolutionError be the
    190    //   thrown exception.
    191    auto result = loader->ResolveModuleSpecifier(script, string);
    192 
    193    // Step 9. If the previous step threw an exception, then:
    194    if (result.isErr()) {
    195      Rooted<Value> error(aCx);
    196      nsresult rv =
    197          loader->HandleResolveFailure(aCx, script, string, result.unwrapErr(),
    198                                       aLineNumber, aColumnNumber, &error);
    199      if (NS_FAILED(rv)) {
    200        JS_ReportOutOfMemory(aCx);
    201        return false;
    202      }
    203 
    204      // Step 2. Perform FinishLoadingImportedModule(referrer, moduleRequest,
    205      //   payload, ThrowCompletion(resolutionError)).
    206      FinishLoadingImportedModuleFailed(aCx, aPayload, error);
    207 
    208      // Step 3. Return.
    209      return true;
    210    }
    211 
    212    MOZ_ASSERT(result.isOk());
    213    nsCOMPtr<nsIURI> uri = result.unwrap();
    214    MOZ_ASSERT(uri, "Failed to resolve module specifier");
    215 
    216    ModuleType moduleType = GetModuleRequestType(aCx, aModuleRequest);
    217    if (!loader->IsModuleTypeAllowed(moduleType)) {
    218      LOG(("ModuleLoaderBase::HostLoadImportedModule uri %s, bad module type",
    219           uri->GetSpecOrDefault().get()));
    220      Rooted<Value> error(aCx);
    221      if (!CreateBadModuleTypeError(aCx, script, uri, &error)) {
    222        JS_ReportOutOfMemory(aCx);
    223        return false;
    224      }
    225      JS_SetPendingException(aCx, error);
    226      return false;
    227    }
    228 
    229    RefPtr<ScriptFetchOptions> options = nullptr;
    230    ReferrerPolicy referrerPolicy;
    231    nsIURI* fetchReferrer = nullptr;
    232    if (script) {
    233      options = script->GetFetchOptions();
    234      referrerPolicy = script->ReferrerPolicy();
    235      fetchReferrer = script->BaseURL();
    236    } else {
    237      options = loader->CreateDefaultScriptFetchOptions();
    238      referrerPolicy = ReferrerPolicy::_empty;
    239      fetchReferrer = loader->GetClientReferrerURI();
    240    }
    241 
    242    mozilla::dom::SRIMetadata sriMetadata;
    243    loader->GetImportMapSRI(
    244        uri, fetchReferrer,
    245        loader->GetScriptLoaderInterface()->GetConsoleReportCollector(),
    246        &sriMetadata);
    247 
    248    RefPtr<ModuleLoadRequest> request = loader->CreateRequest(
    249        aCx, uri, aModuleRequest, aHostDefined, aPayload, isDynamicImport,
    250        options, referrerPolicy, fetchReferrer, sriMetadata);
    251    if (!request) {
    252      MOZ_ASSERT(isDynamicImport);
    253      nsAutoCString url;
    254      uri->GetSpec(url);
    255      JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
    256                                JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
    257      return false;
    258    }
    259 
    260    LOG(
    261        ("ModuleLoaderBase::HostLoadImportedModule loader (%p) uri %s referrer "
    262         "(%p) request (%p)",
    263         loader.get(), uri->GetSpecOrDefault().get(), aReferrer.get(),
    264         request.get()));
    265 
    266    request->SetImport(aReferrer, aModuleRequest, aPayload);
    267 
    268    if (isDynamicImport) {
    269      loader->AppendDynamicImport(request);
    270    }
    271 
    272    nsresult rv = loader->StartModuleLoad(request);
    273    if (NS_WARN_IF(NS_FAILED(rv))) {
    274      MOZ_ASSERT(!request->mModuleScript);
    275      loader->GetScriptLoaderInterface()->ReportErrorToConsole(request, rv);
    276      if (isDynamicImport) {
    277        loader->RemoveDynamicImport(request);
    278 
    279        nsAutoCString url;
    280        uri->GetSpec(url);
    281        JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
    282                                  JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
    283      } else {
    284        loader->OnFetchFailed(request);
    285        return true;
    286      }
    287 
    288      return false;
    289    }
    290 
    291    if (isDynamicImport) {
    292      loader->OnDynamicImportStarted(request);
    293    }
    294  }
    295 
    296  return true;
    297 }
    298 
    299 // static
    300 bool ModuleLoaderBase::FinishLoadingImportedModule(
    301    JSContext* aCx, ModuleLoadRequest* aRequest) {
    302  // The request should been removed from mDynamicImportRequests.
    303  MOZ_ASSERT_IF(aRequest->IsDynamicImport(),
    304                !aRequest->mLoader->HasDynamicImport(aRequest));
    305 
    306  Rooted<JSObject*> module(aCx);
    307  {
    308    ModuleScript* moduleScript = aRequest->mModuleScript;
    309    MOZ_ASSERT(moduleScript);
    310    MOZ_ASSERT(moduleScript->ModuleRecord());
    311    module.set(moduleScript->ModuleRecord());
    312  }
    313  MOZ_ASSERT(module);
    314 
    315  Rooted<JSScript*> referrer(aCx, aRequest->mReferrerScript);
    316  Rooted<JSObject*> moduleReqObj(aCx, aRequest->mModuleRequestObj);
    317  Rooted<Value> statePrivate(aCx, aRequest->mPayload);
    318  Rooted<Value> payload(aCx, aRequest->mPayload);
    319 
    320  LOG(("ScriptLoadRequest (%p): FinishLoadingImportedModule module (%p)",
    321       aRequest, module.get()));
    322  bool usePromise = aRequest->HasScriptLoadContext();
    323  MOZ_ALWAYS_TRUE(JS::FinishLoadingImportedModule(aCx, referrer, moduleReqObj,
    324                                                  payload, module, usePromise));
    325  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
    326  aRequest->ClearImport();
    327 
    328  return true;
    329 }
    330 
    331 // static
    332 bool ModuleLoaderBase::ImportMetaResolve(JSContext* cx, unsigned argc,
    333                                         Value* vp) {
    334  CallArgs args = CallArgsFromVp(argc, vp);
    335  RootedValue modulePrivate(
    336      cx, js::GetFunctionNativeReserved(
    337              &args.callee(),
    338              static_cast<size_t>(ImportMetaSlots::ModulePrivateSlot)));
    339 
    340  // https://html.spec.whatwg.org/#hostgetimportmetaproperties
    341  // Step 4.1. Set specifier to ? ToString(specifier).
    342  //
    343  // https://tc39.es/ecma262/#sec-tostring
    344  RootedValue v(cx, args.get(ImportMetaResolveSpecifierArg));
    345  RootedString specifier(cx, ToString(cx, v));
    346  if (!specifier) {
    347    return false;
    348  }
    349 
    350  // Step 4.2, 4.3 are implemented in ImportMetaResolveImpl.
    351  RootedString url(cx, ImportMetaResolveImpl(cx, modulePrivate, specifier));
    352  if (!url) {
    353    return false;
    354  }
    355 
    356  // Step 4.4. Return the serialization of url.
    357  args.rval().setString(url);
    358  return true;
    359 }
    360 
    361 // static
    362 JSString* ModuleLoaderBase::ImportMetaResolveImpl(
    363    JSContext* aCx, Handle<Value> aReferencingPrivate,
    364    Handle<JSString*> aSpecifier) {
    365  RootedString urlString(aCx);
    366 
    367  {
    368    // ModuleScript should only live in this block, otherwise it will be a GC
    369    // hazard
    370    RefPtr<ModuleScript> script =
    371        static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
    372    MOZ_ASSERT(script->IsModuleScript());
    373    MOZ_ASSERT(GetModulePrivate(script->ModuleRecord()) == aReferencingPrivate);
    374 
    375    RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
    376    if (!loader) {
    377      return nullptr;
    378    }
    379 
    380    nsAutoJSString specifier;
    381    if (!specifier.init(aCx, aSpecifier)) {
    382      return nullptr;
    383    }
    384 
    385    auto result = loader->ResolveModuleSpecifier(script, specifier);
    386    if (result.isErr()) {
    387      Rooted<Value> error(aCx);
    388      nsresult rv = loader->HandleResolveFailure(
    389          aCx, script, specifier, result.unwrapErr(), 0,
    390          ColumnNumberOneOrigin(), &error);
    391      if (NS_FAILED(rv)) {
    392        JS_ReportOutOfMemory(aCx);
    393        return nullptr;
    394      }
    395 
    396      JS_SetPendingException(aCx, error);
    397 
    398      return nullptr;
    399    }
    400 
    401    nsCOMPtr<nsIURI> uri = result.unwrap();
    402    nsAutoCString url;
    403    MOZ_ALWAYS_SUCCEEDS(uri->GetAsciiSpec(url));
    404 
    405    urlString.set(JS_NewStringCopyZ(aCx, url.get()));
    406  }
    407 
    408  return urlString;
    409 }
    410 
    411 // static
    412 bool ModuleLoaderBase::HostPopulateImportMeta(JSContext* aCx,
    413                                              Handle<Value> aReferencingPrivate,
    414                                              Handle<JSObject*> aMetaObject) {
    415  RefPtr<ModuleScript> script =
    416      static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
    417  MOZ_ASSERT(script->IsModuleScript());
    418  MOZ_ASSERT(GetModulePrivate(script->ModuleRecord()) == aReferencingPrivate);
    419 
    420  nsAutoCString url;
    421  MOZ_DIAGNOSTIC_ASSERT(script->BaseURL());
    422  MOZ_ALWAYS_SUCCEEDS(script->BaseURL()->GetAsciiSpec(url));
    423 
    424  Rooted<JSString*> urlString(aCx, JS_NewStringCopyZ(aCx, url.get()));
    425  if (!urlString) {
    426    JS_ReportOutOfMemory(aCx);
    427    return false;
    428  }
    429 
    430  // https://html.spec.whatwg.org/#import-meta-url
    431  if (!JS_DefineProperty(aCx, aMetaObject, "url", urlString,
    432                         JSPROP_ENUMERATE)) {
    433    return false;
    434  }
    435 
    436  // https://html.spec.whatwg.org/#import-meta-resolve
    437  // Define 'resolve' function on the import.meta object.
    438  JSFunction* resolveFunc = js::DefineFunctionWithReserved(
    439      aCx, aMetaObject, "resolve", ImportMetaResolve, ImportMetaResolveNumArgs,
    440      JSPROP_ENUMERATE);
    441  if (!resolveFunc) {
    442    return false;
    443  }
    444 
    445  // Store the 'active script' of the meta object into the function slot.
    446  // https://html.spec.whatwg.org/#active-script
    447  RootedObject resolveFuncObj(aCx, JS_GetFunctionObject(resolveFunc));
    448  js::SetFunctionNativeReserved(
    449      resolveFuncObj, static_cast<size_t>(ImportMetaSlots::ModulePrivateSlot),
    450      aReferencingPrivate);
    451 
    452  return true;
    453 }
    454 
    455 AutoOverrideModuleLoader::AutoOverrideModuleLoader(ModuleLoaderBase* aTarget,
    456                                                   ModuleLoaderBase* aLoader)
    457    : mTarget(aTarget) {
    458  mTarget->SetOverride(aLoader);
    459 }
    460 
    461 AutoOverrideModuleLoader::~AutoOverrideModuleLoader() {
    462  mTarget->ResetOverride();
    463 }
    464 
    465 void ModuleLoaderBase::SetOverride(ModuleLoaderBase* aLoader) {
    466  MOZ_ASSERT(!mOverriddenBy);
    467  MOZ_ASSERT(!aLoader->mOverriddenBy);
    468  MOZ_ASSERT(mGlobalObject == aLoader->mGlobalObject);
    469  mOverriddenBy = aLoader;
    470 }
    471 
    472 bool ModuleLoaderBase::IsOverridden() { return !!mOverriddenBy; }
    473 
    474 bool ModuleLoaderBase::IsOverriddenBy(ModuleLoaderBase* aLoader) {
    475  return mOverriddenBy == aLoader;
    476 }
    477 
    478 void ModuleLoaderBase::ResetOverride() {
    479  MOZ_ASSERT(mOverriddenBy);
    480  mOverriddenBy = nullptr;
    481 }
    482 
    483 // static
    484 ModuleLoaderBase* ModuleLoaderBase::GetCurrentModuleLoader(JSContext* aCx) {
    485  auto reportError = mozilla::MakeScopeExit([aCx]() {
    486    JS_ReportErrorASCII(aCx, "No ScriptLoader found for the current context");
    487  });
    488 
    489  Rooted<JSObject*> object(aCx, CurrentGlobalOrNull(aCx));
    490  if (!object) {
    491    return nullptr;
    492  }
    493 
    494  nsIGlobalObject* global = xpc::NativeGlobal(object);
    495  if (!global) {
    496    return nullptr;
    497  }
    498 
    499  ModuleLoaderBase* loader = global->GetModuleLoader(aCx);
    500  if (!loader) {
    501    return nullptr;
    502  }
    503 
    504  MOZ_ASSERT(loader->mGlobalObject == global);
    505 
    506  reportError.release();
    507 
    508  if (loader->mOverriddenBy) {
    509    MOZ_ASSERT(loader->mOverriddenBy->mGlobalObject == global);
    510    return loader->mOverriddenBy;
    511  }
    512  return loader;
    513 }
    514 
    515 // static
    516 LoadedScript* ModuleLoaderBase::GetLoadedScriptOrNull(
    517    Handle<JSScript*> aReferrer) {
    518  if (!aReferrer) {
    519    return nullptr;
    520  }
    521 
    522  Value value = GetScriptPrivate(aReferrer);
    523  if (value.isUndefined()) {
    524    return nullptr;
    525  }
    526 
    527  return static_cast<LoadedScript*>(value.toPrivate());
    528 }
    529 
    530 nsresult ModuleLoaderBase::StartModuleLoad(ModuleLoadRequest* aRequest) {
    531  return StartOrRestartModuleLoad(aRequest, RestartRequest::No);
    532 }
    533 
    534 nsresult ModuleLoaderBase::RestartModuleLoad(ModuleLoadRequest* aRequest) {
    535  return StartOrRestartModuleLoad(aRequest, RestartRequest::Yes);
    536 }
    537 
    538 nsresult ModuleLoaderBase::StartOrRestartModuleLoad(ModuleLoadRequest* aRequest,
    539                                                    RestartRequest aRestart) {
    540  MOZ_ASSERT(aRequest->mLoader == this);
    541  MOZ_ASSERT(aRequest->IsFetching());
    542 
    543  // NOTE: The LoadedScript::mDataType field used by the IsStencil call can be
    544  //       modified asynchronously after the StartFetch call.
    545  //       In order to avoid the race condition, cache the value here.
    546  bool isCachedStencil = aRequest->IsCachedStencil();
    547 
    548  MOZ_ASSERT_IF(isCachedStencil, aRestart == RestartRequest::No);
    549 
    550  if (!isCachedStencil) {
    551    aRequest->SetUnknownDataType();
    552  }
    553 
    554  if (LOG_ENABLED()) {
    555    nsAutoCString url;
    556    aRequest->URI()->GetAsciiSpec(url);
    557    LOG(("ScriptLoadRequest (%p): Start module load %s", aRequest, url.get()));
    558  }
    559 
    560  // If we're restarting the request, the module should already be in the
    561  // "fetching" map.
    562  MOZ_ASSERT_IF(
    563      aRestart == RestartRequest::Yes,
    564      IsModuleFetching(ModuleMapKey(aRequest->URI(), aRequest->mModuleType)));
    565 
    566  // Check with the derived class whether we should load this module.
    567  nsresult rv = NS_OK;
    568  if (!CanStartLoad(aRequest, &rv)) {
    569    return rv;
    570  }
    571 
    572  // Check whether the module has been fetched or is currently being fetched,
    573  // and if so wait for it rather than starting a new fetch.
    574  if (aRestart == RestartRequest::No &&
    575      ModuleMapContainsURL(
    576          ModuleMapKey(aRequest->URI(), aRequest->mModuleType))) {
    577    LOG(("ScriptLoadRequest (%p): Waiting for module fetch", aRequest));
    578    WaitForModuleFetch(aRequest);
    579    return NS_OK;
    580  }
    581 
    582  rv = StartFetch(aRequest);
    583  NS_ENSURE_SUCCESS(rv, rv);
    584 
    585  if (isCachedStencil) {
    586    MOZ_ASSERT(
    587        IsModuleFetched(ModuleMapKey(aRequest->URI(), aRequest->mModuleType)));
    588    return NS_OK;
    589  }
    590 
    591  // We successfully started fetching a module so put its URL in the module
    592  // map and mark it as fetching.
    593  if (aRestart == RestartRequest::No) {
    594    SetModuleFetchStarted(aRequest);
    595  }
    596 
    597  return NS_OK;
    598 }
    599 
    600 bool ModuleLoaderBase::ModuleMapContainsURL(const ModuleMapKey& key) const {
    601  return IsModuleFetching(key) || IsModuleFetched(key);
    602 }
    603 
    604 bool ModuleLoaderBase::IsModuleFetching(const ModuleMapKey& key) const {
    605  return mFetchingModules.Contains(key);
    606 }
    607 
    608 bool ModuleLoaderBase::IsModuleFetched(const ModuleMapKey& key) const {
    609  return mFetchedModules.Contains(key);
    610 }
    611 
    612 nsresult ModuleLoaderBase::GetFetchedModuleURLs(nsTArray<nsCString>& aURLs) {
    613  for (const auto& entry : mFetchedModules) {
    614    nsIURI* uri = entry.GetData()->BaseURL();
    615 
    616    nsAutoCString spec;
    617    nsresult rv = uri->GetSpec(spec);
    618    NS_ENSURE_SUCCESS(rv, rv);
    619 
    620    aURLs.AppendElement(spec);
    621  }
    622 
    623  return NS_OK;
    624 }
    625 
    626 void ModuleLoaderBase::SetModuleFetchStarted(ModuleLoadRequest* aRequest) {
    627  // Update the module map to indicate that a module is currently being fetched.
    628 
    629  ModuleMapKey moduleMapKey(aRequest->URI(), aRequest->mModuleType);
    630 
    631  MOZ_ASSERT(aRequest->IsFetching());
    632  MOZ_ASSERT(!ModuleMapContainsURL(moduleMapKey));
    633 
    634  RefPtr<LoadingRequest> loadingRequest = new LoadingRequest();
    635  loadingRequest->mRequest = aRequest;
    636  mFetchingModules.InsertOrUpdate(moduleMapKey, loadingRequest);
    637 }
    638 
    639 already_AddRefed<ModuleLoaderBase::LoadingRequest>
    640 ModuleLoaderBase::SetModuleFetchFinishedAndGetWaitingRequests(
    641    ModuleLoadRequest* aRequest, nsresult aResult) {
    642  // Update module map with the result of fetching a single module script.
    643  //
    644  // If any requests for the same URL are waiting on this one to complete, call
    645  // ModuleLoaded or LoadFailed to resume or fail them as appropriate.
    646 
    647  MOZ_ASSERT(aRequest->mLoader == this);
    648 
    649  LOG(
    650      ("ScriptLoadRequest (%p): Module fetch finished (script == %p, result == "
    651       "%u)",
    652       aRequest, aRequest->mModuleScript.get(), unsigned(aResult)));
    653 
    654  ModuleMapKey moduleMapKey(aRequest->URI(), aRequest->mModuleType);
    655 
    656  auto entry = mFetchingModules.Lookup(moduleMapKey);
    657  if (!entry) {
    658    LOG(
    659        ("ScriptLoadRequest (%p): Key not found in mFetchingModules, "
    660         "assuming we have an inline module or have finished fetching already",
    661         aRequest));
    662    return nullptr;
    663  }
    664 
    665  // It's possible for a request to be cancelled and removed from the fetching
    666  // modules map and a new request started for the same URI and added to the
    667  // map. In this case we don't want the first cancelled request to complete the
    668  // later request (which will cause it to fail) so we ignore it.
    669  RefPtr<LoadingRequest> loadingRequest = entry.Data();
    670  if (loadingRequest->mRequest != aRequest) {
    671    MOZ_ASSERT(aRequest->IsCanceled());
    672    LOG(
    673        ("ScriptLoadRequest (%p): Ignoring completion of cancelled request "
    674         "that was removed from the map",
    675         aRequest));
    676    return nullptr;
    677  }
    678 
    679  MOZ_ALWAYS_TRUE(mFetchingModules.Remove(moduleMapKey));
    680 
    681  RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript);
    682  MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript);
    683 
    684  mFetchedModules.InsertOrUpdate(moduleMapKey, RefPtr{moduleScript});
    685 
    686  return loadingRequest.forget();
    687 }
    688 
    689 void ModuleLoaderBase::ResumeWaitingRequests(LoadingRequest* aLoadingRequest,
    690                                             bool aSuccess) {
    691  for (ModuleLoadRequest* request : aLoadingRequest->mWaiting) {
    692    ResumeWaitingRequest(request, aSuccess);
    693  }
    694 }
    695 
    696 void ModuleLoaderBase::ResumeWaitingRequest(ModuleLoadRequest* aRequest,
    697                                            bool aSuccess) {
    698  if (aSuccess) {
    699    aRequest->ModuleLoaded();
    700  }
    701 
    702  if (!aRequest->IsErrored()) {
    703    OnFetchSucceeded(aRequest);
    704  } else {
    705    OnFetchFailed(aRequest);
    706  }
    707 }
    708 
    709 void ModuleLoaderBase::WaitForModuleFetch(ModuleLoadRequest* aRequest) {
    710  ModuleMapKey moduleMapKey(aRequest->URI(), aRequest->mModuleType);
    711  MOZ_ASSERT(ModuleMapContainsURL(moduleMapKey));
    712 
    713  if (auto entry = mFetchingModules.Lookup(moduleMapKey)) {
    714    RefPtr<LoadingRequest> loadingRequest = entry.Data();
    715    loadingRequest->mWaiting.AppendElement(aRequest);
    716    return;
    717  }
    718 
    719  RefPtr<ModuleScript> ms;
    720  MOZ_ALWAYS_TRUE(mFetchedModules.Get(moduleMapKey, getter_AddRefs(ms)));
    721 
    722  ResumeWaitingRequest(aRequest, bool(ms));
    723 }
    724 
    725 ModuleScript* ModuleLoaderBase::GetFetchedModule(
    726    const ModuleMapKey& moduleMapKey) const {
    727  if (LOG_ENABLED()) {
    728    nsAutoCString url;
    729    moduleMapKey.mUri->GetAsciiSpec(url);
    730    LOG(("GetFetchedModule %s", url.get()));
    731  }
    732 
    733  bool found;
    734  ModuleScript* ms = mFetchedModules.GetWeak(moduleMapKey, &found);
    735  MOZ_ASSERT(found);
    736  return ms;
    737 }
    738 
    739 nsresult ModuleLoaderBase::OnFetchComplete(ModuleLoadRequest* aRequest,
    740                                           nsresult aRv) {
    741  LOG(("ScriptLoadRequest (%p): OnFetchComplete result %x", aRequest,
    742       (unsigned)aRv));
    743  MOZ_ASSERT(aRequest->mLoader == this);
    744  MOZ_ASSERT(!aRequest->mModuleScript);
    745 
    746  nsresult rv = aRv;
    747  if (NS_SUCCEEDED(rv)) {
    748    rv = CreateModuleScript(aRequest);
    749 
    750 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
    751    // If a module script was created, it should either have a module record
    752    // object or a parse error.
    753    if (ModuleScript* ms = aRequest->mModuleScript) {
    754      MOZ_DIAGNOSTIC_ASSERT(bool(ms->ModuleRecord()) != ms->HasParseError());
    755    }
    756 #endif
    757 
    758    if (aRequest->IsTextSource()) {
    759      aRequest->ClearScriptText();
    760    }
    761 
    762    if (NS_FAILED(rv)) {
    763      aRequest->LoadFailed();
    764      return rv;
    765    }
    766  }
    767 
    768  RefPtr<LoadingRequest> waitingRequests =
    769      SetModuleFetchFinishedAndGetWaitingRequests(aRequest, rv);
    770  MOZ_ASSERT_IF(waitingRequests, waitingRequests->mRequest == aRequest);
    771 
    772  bool success = bool(aRequest->mModuleScript);
    773  MOZ_ASSERT(NS_SUCCEEDED(rv) == success);
    774 
    775  // TODO: https://github.com/whatwg/html/issues/11755
    776  // Should we check if the module script has an error to rethrow as well?
    777  //
    778  // https://html.spec.whatwg.org/#hostloadimportedmodule:fetch-a-single-imported-module-script
    779  // Step 14. onSingleFetchComplete: step 2, and step 3
    780  //   return ThrowCompletion if moduleScript is null or its parse error is not
    781  //   null:
    782  //   Step 4. Otherwise, set completion to NormalCompletion(moduleScript's
    783  //     record).
    784  if (!aRequest->IsErrored()) {
    785    OnFetchSucceeded(aRequest);
    786  } else {
    787    OnFetchFailed(aRequest);
    788  }
    789 
    790  if (!waitingRequests) {
    791    return NS_OK;
    792  }
    793 
    794  ResumeWaitingRequests(waitingRequests, success);
    795  return NS_OK;
    796 }
    797 
    798 void ModuleLoaderBase::OnFetchSucceeded(ModuleLoadRequest* aRequest) {
    799  // TODO, Bug 1990416: Align the loading descendants behavior of dynamic import
    800  // According to the spec, dynamic import should trigger fetching of
    801  // descendant modules in the JS engine. However, when the dynamic import’s
    802  // module graph is loaded, ScriptLoaders move the request to a Loaded event
    803  // queue and invoke ProcessDynamicImport. To account for this behavior,
    804  // we perform the fetching of submodules in the host layer instead.
    805  if (aRequest->IsTopLevel() || aRequest->IsDynamicImport()) {
    806    StartFetchingModuleDependencies(aRequest);
    807  } else {
    808    MOZ_ASSERT(!aRequest->IsDynamicImport());
    809    AutoJSAPI jsapi;
    810    if (!jsapi.Init(mGlobalObject)) {
    811      return;
    812    }
    813    JSContext* cx = jsapi.cx();
    814    FinishLoadingImportedModule(cx, aRequest);
    815 
    816    aRequest->SetReady();
    817    aRequest->LoadFinished();
    818  }
    819 }
    820 
    821 void ModuleLoaderBase::OnFetchFailed(ModuleLoadRequest* aRequest) {
    822  MOZ_ASSERT(aRequest->IsErrored());
    823  AutoJSAPI jsapi;
    824  if (!jsapi.Init(mGlobalObject)) {
    825    return;
    826  }
    827  JSContext* cx = jsapi.cx();
    828 
    829  if (aRequest->IsTopLevel()) {
    830    // https://html.spec.whatwg.org/#fetch-the-descendants-of-and-link-a-module-script
    831    // Step 2. If record is null, then:
    832    // Step 2.1. Set moduleScript's error to rethrow to moduleScript's parse
    833    //           error.
    834    if (aRequest->mModuleScript && !aRequest->mModuleScript->ModuleRecord()) {
    835      MOZ_ASSERT(aRequest->mModuleScript->HasParseError());
    836      Value parseError = aRequest->mModuleScript->ParseError();
    837      LOG(("ScriptLoadRequest (%p): found parse error", aRequest));
    838      aRequest->mModuleScript->SetErrorToRethrow(parseError);
    839    }
    840    DispatchModuleErrored(aRequest);
    841 
    842    return;
    843  }
    844 
    845  // The remaining case is static/dynamic import.
    846  MOZ_ASSERT(aRequest->IsStaticImport() || aRequest->IsDynamicImport());
    847  MOZ_ASSERT(!aRequest->mPayload.isUndefined());
    848  Rooted<Value> payload(cx, aRequest->mPayload);
    849 
    850  // https://html.spec.whatwg.org/#hostloadimportedmodule
    851  //
    852  // Step 14.2. If moduleScript is null, then set completion to Completion
    853  //            Record { [[Type]]: throw, [[Value]]: a new TypeError,
    854  //            [[Target]]: empty }.
    855  if (!aRequest->mModuleScript) {
    856    LOG(
    857        ("ScriptLoadRequest (%p): FinishLoadingImportedModule: module script "
    858         "is null",
    859         aRequest));
    860 
    861    // Impl note:
    862    // For dynamic import, the TypeError will be pass to the rejected handler of
    863    // the LoadRequestedModules from the dynamic import.
    864    //
    865    // For static import/top-level import, the TypeError will be ignore in the
    866    // rejected handler of LoadRequestedModules (the state.[[ErrorToRethrow]]
    867    // isn't set). So we don't actually create a TypeError for the these two
    868    // cases.
    869    if (aRequest->GetRootModule()->IsDynamicImport()) {
    870      nsAutoCString url;
    871      aRequest->URI()->GetSpec(url);
    872      JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
    873                                JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
    874      FinishLoadingImportedModuleFailedWithPendingException(cx, payload);
    875    } else {
    876      Rooted<Value> error(cx, UndefinedValue());
    877      FinishLoadingImportedModuleFailed(cx, payload, error);
    878    }
    879 
    880    aRequest->ModuleErrored();
    881    aRequest->ClearImport();
    882    return;
    883  }
    884 
    885  // Step 14.3. Otherwise, if moduleScript's parse error is not null, then:
    886  //   1. Let parseError be moduleScript's parse error.
    887  //   2. Set completion to Completion Record { [[Type]]: throw,
    888  //      [[Value]]: parseError, [[Target]]: empty }.
    889  //   3. If loadState is not undefined and loadState.[[ErrorToRethrow]]
    890  //      is null, set loadState.[[ErrorToRethrow]] to parseError.
    891  MOZ_ASSERT(aRequest->mModuleScript->HasParseError());
    892  Rooted<Value> parseError(cx, aRequest->mModuleScript->ParseError());
    893  LOG(
    894      ("ScriptLoadRequest (%p): FinishLoadingImportedModule: found parse "
    895       "error",
    896       aRequest));
    897  // Step 14.5. Perform FinishLoadingImportedModule(referrer, moduleRequest,
    898  //            payload, completion).
    899  FinishLoadingImportedModuleFailed(cx, payload, parseError);
    900  aRequest->ModuleErrored();
    901  aRequest->ClearImport();
    902 }
    903 
    904 void ModuleLoaderBase::Cancel(ModuleLoadRequest* aRequest) {
    905  if (aRequest->IsFinished()) {
    906    return;
    907  }
    908 
    909  aRequest->ScriptLoadRequest::Cancel();
    910  aRequest->mModuleScript = nullptr;
    911 
    912  if (aRequest->mPayload.isUndefined()) {
    913    return;
    914  }
    915 
    916  AutoJSAPI jsapi;
    917  if (!jsapi.Init(mGlobalObject)) {
    918    return;
    919  }
    920  JSContext* cx = jsapi.cx();
    921 
    922  Rooted<Value> payload(cx, aRequest->mPayload);
    923  Rooted<Value> error(cx, UndefinedValue());
    924 
    925  LOG(
    926      ("ScriptLoadRequest (%p): Canceled, calling "
    927       "FinishLoadingImportedModuleFailed",
    928       aRequest));
    929 
    930  FinishLoadingImportedModuleFailed(cx, payload, error);
    931  aRequest->ModuleErrored();
    932  aRequest->ClearImport();
    933 }
    934 
    935 class ModuleErroredRunnable : public MicroTaskRunnable {
    936 public:
    937  explicit ModuleErroredRunnable(ModuleLoadRequest* aRequest)
    938      : mRequest(aRequest) {}
    939 
    940  virtual void Run(AutoSlowOperation& aAso) override {
    941    mRequest->ModuleErrored();
    942  }
    943 
    944 private:
    945  RefPtr<ModuleLoadRequest> mRequest;
    946 };
    947 
    948 void ModuleLoaderBase::DispatchModuleErrored(ModuleLoadRequest* aRequest) {
    949  if (aRequest->HasScriptLoadContext() &&
    950      aRequest->GetScriptLoadContext()->mIsInline) {
    951    // https://html.spec.whatwg.org/#prepare-the-script-element
    952    // Step 32. If el does not have a src content attribute:
    953    //   2. "module".3.1
    954    //   Queue an element task on the networking task source given el to
    955    //   perform the following steps:
    956    CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
    957    RefPtr<ModuleErroredRunnable> runnable =
    958        new ModuleErroredRunnable(aRequest);
    959    context->DispatchToMicroTask(runnable.forget());
    960  } else {
    961    aRequest->ModuleErrored();
    962  }
    963 }
    964 
    965 nsresult ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest* aRequest) {
    966  MOZ_ASSERT(!aRequest->mModuleScript);
    967  MOZ_ASSERT(aRequest->BaseURL());
    968 
    969  LOG(("ScriptLoadRequest (%p): Create module script", aRequest));
    970 
    971  AutoJSAPI jsapi;
    972  if (!jsapi.Init(mGlobalObject)) {
    973    return NS_ERROR_FAILURE;
    974  }
    975 
    976  nsresult rv;
    977  {
    978    JSContext* cx = jsapi.cx();
    979    Rooted<JSObject*> module(cx);
    980 
    981    CompileOptions options(cx);
    982    RootedScript introductionScript(cx);
    983    rv = mLoader->FillCompileOptionsForRequest(cx, aRequest, &options,
    984                                               &introductionScript);
    985 
    986    if (NS_SUCCEEDED(rv)) {
    987      Rooted<JSObject*> global(cx, mGlobalObject->GetGlobalJSObject());
    988      rv = CompileFetchedModule(cx, global, options, aRequest, &module);
    989    }
    990 
    991    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
    992 
    993    if (module) {
    994      RootedScript moduleScript(cx, GetModuleScript(module));
    995      if (moduleScript) {
    996        RootedValue privateValue(cx);
    997        InstantiateOptions instantiateOptions(options);
    998        if (!UpdateDebugMetadata(cx, moduleScript, instantiateOptions,
    999                                 privateValue, nullptr, introductionScript,
   1000                                 nullptr)) {
   1001          return NS_ERROR_OUT_OF_MEMORY;
   1002        }
   1003      }
   1004    }
   1005 
   1006    MOZ_ASSERT(aRequest->mLoadedScript->IsModuleScript());
   1007    RefPtr<ModuleScript> moduleScript =
   1008        aRequest->mLoadedScript->AsModuleScript();
   1009 
   1010    // Update the module script's referrer policy to reflect any changes made
   1011    // to the ModuleLoadRequest during HTTP response parsing.
   1012    if (moduleScript->ReferrerPolicy() != aRequest->ReferrerPolicy()) {
   1013      moduleScript->UpdateReferrerPolicy(aRequest->ReferrerPolicy());
   1014    }
   1015    aRequest->mModuleScript = moduleScript;
   1016 
   1017    moduleScript->SetForPreload(aRequest->mLoadContext->IsPreload());
   1018    moduleScript->SetHadImportMap(HasImportMapRegistered());
   1019 
   1020    if (!module) {
   1021      LOG(("ScriptLoadRequest (%p):   compilation failed (%d)", aRequest,
   1022           unsigned(rv)));
   1023 
   1024      Rooted<Value> error(cx);
   1025      if (!jsapi.HasException() || !jsapi.StealException(&error) ||
   1026          error.isUndefined()) {
   1027        aRequest->mModuleScript = nullptr;
   1028        return NS_ERROR_FAILURE;
   1029      }
   1030 
   1031      moduleScript->SetParseError(error);
   1032      return NS_OK;
   1033    }
   1034 
   1035    moduleScript->SetModuleRecord(module);
   1036  }
   1037 
   1038  LOG(("ScriptLoadRequest (%p):   module script == %p", aRequest,
   1039       aRequest->mModuleScript.get()));
   1040 
   1041  return rv;
   1042 }
   1043 
   1044 nsresult ModuleLoaderBase::GetResolveFailureMessage(ResolveError aError,
   1045                                                    const nsAString& aSpecifier,
   1046                                                    nsAString& aResult) {
   1047  AutoTArray<nsString, 1> errorParams;
   1048  errorParams.AppendElement(aSpecifier);
   1049 
   1050  nsresult rv = nsContentUtils::FormatLocalizedString(
   1051      nsContentUtils::eDOM_PROPERTIES, ResolveErrorInfo::GetString(aError),
   1052      errorParams, aResult);
   1053  NS_ENSURE_SUCCESS(rv, rv);
   1054  return NS_OK;
   1055 }
   1056 
   1057 nsresult ModuleLoaderBase::HandleResolveFailure(
   1058    JSContext* aCx, LoadedScript* aScript, const nsAString& aSpecifier,
   1059    ResolveError aError, uint32_t aLineNumber,
   1060    ColumnNumberOneOrigin aColumnNumber, MutableHandle<Value> aErrorOut) {
   1061  Rooted<JSString*> filename(aCx);
   1062  if (aScript) {
   1063    nsAutoCString url;
   1064    aScript->BaseURL()->GetAsciiSpec(url);
   1065    filename = JS_NewStringCopyZ(aCx, url.get());
   1066  } else {
   1067    filename = JS_NewStringCopyZ(aCx, "(unknown)");
   1068  }
   1069 
   1070  if (!filename) {
   1071    return NS_ERROR_OUT_OF_MEMORY;
   1072  }
   1073 
   1074  nsAutoString errorText;
   1075  nsresult rv = GetResolveFailureMessage(aError, aSpecifier, errorText);
   1076  NS_ENSURE_SUCCESS(rv, rv);
   1077 
   1078  Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, errorText.get()));
   1079  if (!string) {
   1080    return NS_ERROR_OUT_OF_MEMORY;
   1081  }
   1082 
   1083  if (!CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
   1084                   aColumnNumber, nullptr, string, NothingHandleValue,
   1085                   aErrorOut)) {
   1086    return NS_ERROR_OUT_OF_MEMORY;
   1087  }
   1088 
   1089  return NS_OK;
   1090 }
   1091 
   1092 ResolveResult ModuleLoaderBase::ResolveModuleSpecifier(
   1093    LoadedScript* aScript, const nsAString& aSpecifier) {
   1094  // Import Maps are not supported on workers/worklets.
   1095  // See https://github.com/WICG/import-maps/issues/2
   1096  MOZ_ASSERT_IF(!NS_IsMainThread(), mImportMap == nullptr);
   1097 
   1098  // Forward to the updated 'Resolve a module specifier' algorithm defined in
   1099  // the Import Maps spec.
   1100  return ImportMap::ResolveModuleSpecifier(mImportMap.get(), mLoader, aScript,
   1101                                           aSpecifier);
   1102 }
   1103 
   1104 void ModuleLoaderBase::StartFetchingModuleDependencies(
   1105    ModuleLoadRequest* aRequest) {
   1106  if (aRequest->IsCanceled()) {
   1107    return;
   1108  }
   1109 
   1110  MOZ_ASSERT(aRequest->mModuleScript);
   1111  MOZ_ASSERT(!aRequest->mModuleScript->HasParseError());
   1112  ModuleScript* moduleScript = aRequest->mModuleScript;
   1113  MOZ_ASSERT(moduleScript->ModuleRecord());
   1114  MOZ_ASSERT(aRequest->IsFetching() || aRequest->IsCompiling());
   1115  MOZ_ASSERT(aRequest->IsTopLevel() || aRequest->IsDynamicImport());
   1116 
   1117  AutoJSAPI jsapi;
   1118  if (NS_WARN_IF(!jsapi.Init(mGlobalObject))) {
   1119    return;
   1120  }
   1121  JSContext* cx = jsapi.cx();
   1122 
   1123  Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());
   1124 
   1125  LOG(
   1126      ("ScriptLoadRequest (%p): module record (%p) Start fetching module "
   1127       "dependencies",
   1128       aRequest, module.get()));
   1129 
   1130  // Wrap the request into a JS::Value, and AddRef() it.
   1131  // The Release() will be called in the resolved/rejected handlers.
   1132  Rooted<Value> hostDefinedVal(cx, PrivateValue(aRequest));
   1133  aRequest->AddRef();
   1134 
   1135  bool result = false;
   1136 
   1137  // PromiseJobRunnable::Call() is not executed if the global is being
   1138  // destroyed. As a result, the promise returned by LoadRequestedModules may
   1139  // neither resolve nor reject. To ensure module loading completes reliably in
   1140  // chrome pages, we use the synchronous variant of LoadRequestedModules.
   1141  bool isSync = aRequest->URI()->SchemeIs("chrome") ||
   1142                aRequest->URI()->SchemeIs("resource");
   1143 
   1144  // TODO: Bug1973660: Use Promise version of LoadRequestedModules on Workers.
   1145  if (aRequest->HasScriptLoadContext() && !isSync) {
   1146    Rooted<JSFunction*> onResolved(
   1147        cx, js::NewFunctionWithReserved(cx, OnLoadRequestedModulesResolved,
   1148                                        OnLoadRequestedModulesResolvedNumArgs,
   1149                                        0, "resolved"));
   1150    if (!onResolved) {
   1151      JS_ReportOutOfMemory(cx);
   1152      return;
   1153    }
   1154 
   1155    RootedFunction onRejected(
   1156        cx, js::NewFunctionWithReserved(cx, OnLoadRequestedModulesRejected,
   1157                                        OnLoadRequestedModulesRejectedNumArgs,
   1158                                        0, "rejected"));
   1159    if (!onRejected) {
   1160      JS_ReportOutOfMemory(cx);
   1161      return;
   1162    }
   1163 
   1164    RootedObject resolveFuncObj(cx, JS_GetFunctionObject(onResolved));
   1165    js::SetFunctionNativeReserved(resolveFuncObj, LoadReactionHostDefinedSlot,
   1166                                  hostDefinedVal);
   1167 
   1168    RootedObject rejectFuncObj(cx, JS_GetFunctionObject(onRejected));
   1169    js::SetFunctionNativeReserved(rejectFuncObj, LoadReactionHostDefinedSlot,
   1170                                  hostDefinedVal);
   1171 
   1172    Rooted<JSObject*> loadPromise(cx);
   1173    result = LoadRequestedModules(cx, module, hostDefinedVal, &loadPromise);
   1174    AddPromiseReactions(cx, loadPromise, resolveFuncObj, rejectFuncObj);
   1175  } else {
   1176    result = LoadRequestedModules(cx, module, hostDefinedVal,
   1177                                  OnLoadRequestedModulesResolved,
   1178                                  OnLoadRequestedModulesRejected);
   1179  }
   1180 
   1181  if (!result) {
   1182    LOG(("ScriptLoadRequest (%p): LoadRequestedModules failed", aRequest));
   1183    OnLoadRequestedModulesRejected(cx, aRequest, UndefinedHandleValue);
   1184  }
   1185 }
   1186 
   1187 // static
   1188 bool ModuleLoaderBase::OnLoadRequestedModulesResolved(JSContext* aCx,
   1189                                                      unsigned aArgc,
   1190                                                      Value* aVp) {
   1191  CallArgs args = CallArgsFromVp(aArgc, aVp);
   1192  Rooted<Value> hostDefined(aCx);
   1193  hostDefined = js::GetFunctionNativeReserved(&args.callee(),
   1194                                              LoadReactionHostDefinedSlot);
   1195  return OnLoadRequestedModulesResolved(aCx, hostDefined);
   1196 }
   1197 
   1198 // static
   1199 bool ModuleLoaderBase::OnLoadRequestedModulesResolved(
   1200    JSContext* aCx, Handle<Value> aHostDefined) {
   1201  auto* request = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate());
   1202  MOZ_ASSERT(request);
   1203  return OnLoadRequestedModulesResolved(request);
   1204 }
   1205 
   1206 // static
   1207 bool ModuleLoaderBase::OnLoadRequestedModulesResolved(
   1208    ModuleLoadRequest* aRequest) {
   1209  LOG(("ScriptLoadRequest (%p): LoadRequestedModules resolved", aRequest));
   1210  if (!aRequest->IsCanceled()) {
   1211    aRequest->SetReady();
   1212    aRequest->LoadFinished();
   1213  }
   1214 
   1215  // Decrease the reference 'AddRef'ed when converting the hostDefined.
   1216  aRequest->Release();
   1217  return true;
   1218 }
   1219 
   1220 // static
   1221 bool ModuleLoaderBase::OnLoadRequestedModulesRejected(JSContext* aCx,
   1222                                                      unsigned aArgc,
   1223                                                      Value* aVp) {
   1224  CallArgs args = CallArgsFromVp(aArgc, aVp);
   1225  Rooted<Value> error(aCx, args.get(OnLoadRequestedModulesRejectedErrorArg));
   1226  Rooted<Value> hostDefined(aCx);
   1227  hostDefined = js::GetFunctionNativeReserved(&args.callee(),
   1228                                              LoadReactionHostDefinedSlot);
   1229  return OnLoadRequestedModulesRejected(aCx, hostDefined, error);
   1230 }
   1231 
   1232 // static
   1233 bool ModuleLoaderBase::OnLoadRequestedModulesRejected(
   1234    JSContext* aCx, Handle<Value> aHostDefined, Handle<Value> aError) {
   1235  auto* request = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate());
   1236  MOZ_ASSERT(request);
   1237  return OnLoadRequestedModulesRejected(aCx, request, aError);
   1238 }
   1239 
   1240 // static
   1241 bool ModuleLoaderBase::OnLoadRequestedModulesRejected(
   1242    JSContext* aCx, ModuleLoadRequest* aRequest, Handle<Value> error) {
   1243  ModuleScript* moduleScript = aRequest->mModuleScript;
   1244 
   1245  if (aRequest->IsCanceled()) {
   1246    aRequest->Release();
   1247    return true;
   1248  }
   1249 
   1250  // TODO, Bug 1990416: Align the loading descendants behavior of dynamic import
   1251  if (aRequest->IsDynamicImport()) {
   1252    LOG(
   1253        ("ScriptLoadRequest (%p): LoadRequestedModules rejected for dynamic "
   1254         "import",
   1255         aRequest));
   1256    // https://tc39.es/ecma262/#sec-ContinueDynamicImport
   1257    // Step 4.a. Perform ! Call(promiseCapability.[[Reject]], undefined,
   1258    //   « reason »).
   1259    Rooted<Value> payload(aCx, aRequest->mPayload);
   1260    if (!error.isUndefined()) {
   1261      FinishLoadingImportedModuleFailed(aCx, payload, error);
   1262    } else {
   1263      nsAutoCString url;
   1264      aRequest->URI()->GetSpec(url);
   1265      JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
   1266                                JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
   1267      FinishLoadingImportedModuleFailedWithPendingException(aCx, payload);
   1268    }
   1269    aRequest->SetErroredLoadingImports();
   1270  } else if (moduleScript && !error.isUndefined()) {
   1271    LOG(
   1272        ("ScriptLoadRequest (%p): LoadRequestedModules rejected: set error to "
   1273         "rethrow",
   1274         aRequest));
   1275    // https://html.spec.whatwg.org/#fetch-the-descendants-of-and-link-a-module-script
   1276    // Step 7. Upon rejection of loadingPromise, run the following
   1277    //         steps:
   1278    // Step 7.1. If state.[[ErrorToRethrow]] is not null, set moduleScript's
   1279    //           error to rethrow to state.[[ErrorToRethrow]] and run
   1280    //           onComplete given moduleScript.
   1281    moduleScript->SetErrorToRethrow(error);
   1282  } else {
   1283    LOG(
   1284        ("ScriptLoadRequest (%p): LoadRequestedModules rejected: set module "
   1285         "script to null",
   1286         aRequest));
   1287    // Step 7.2. Otherwise, run onComplete given null.
   1288    aRequest->mModuleScript = nullptr;
   1289  }
   1290 
   1291  aRequest->ModuleErrored();
   1292 
   1293  // Decrease the reference 'AddRef'ed when converting the hostDefined.
   1294  aRequest->Release();
   1295  return true;
   1296 }
   1297 
   1298 bool ModuleLoaderBase::GetImportMapSRI(
   1299    nsIURI* aURI, nsIURI* aSourceURI, nsIConsoleReportCollector* aReporter,
   1300    mozilla::dom::SRIMetadata* aMetadataOut) {
   1301  MOZ_ASSERT(aMetadataOut->IsEmpty());
   1302  MOZ_ASSERT(aURI);
   1303 
   1304  if (!HasImportMapRegistered()) {
   1305    return false;
   1306  }
   1307 
   1308  mozilla::Maybe<nsString> entry =
   1309      ImportMap::LookupIntegrity(mImportMap.get(), aURI);
   1310  if (entry.isNothing()) {
   1311    return false;
   1312  }
   1313 
   1314  mozilla::dom::SRICheck::IntegrityMetadata(
   1315      *entry, aSourceURI->GetSpecOrDefault(), aReporter, aMetadataOut);
   1316  return true;
   1317 }
   1318 
   1319 void ModuleLoaderBase::AppendDynamicImport(ModuleLoadRequest* aRequest) {
   1320  MOZ_ASSERT(aRequest->mLoader == this);
   1321  mDynamicImportRequests.AppendElement(aRequest);
   1322 }
   1323 
   1324 ModuleLoaderBase::ModuleLoaderBase(ScriptLoaderInterface* aLoader,
   1325                                   nsIGlobalObject* aGlobalObject)
   1326    : mGlobalObject(aGlobalObject), mLoader(aLoader) {
   1327  MOZ_ASSERT(mGlobalObject);
   1328  MOZ_ASSERT(mLoader);
   1329 
   1330  EnsureModuleHooksInitialized();
   1331 }
   1332 
   1333 ModuleLoaderBase::~ModuleLoaderBase() {
   1334  mDynamicImportRequests.CancelRequestsAndClear();
   1335 
   1336  LOG(("ModuleLoaderBase::~ModuleLoaderBase %p", this));
   1337 }
   1338 
   1339 void ModuleLoaderBase::CancelFetchingModules() {
   1340  for (const auto& entry : mFetchingModules) {
   1341    RefPtr<LoadingRequest> loadingRequest = entry.GetData();
   1342    loadingRequest->mRequest->Cancel();
   1343 
   1344    for (const auto& request : loadingRequest->mWaiting) {
   1345      request->Cancel();
   1346    }
   1347  }
   1348 
   1349  // We don't clear mFetchingModules here, as the fetching requests might arrive
   1350  // after the global is still shutting down.
   1351 }
   1352 
   1353 void ModuleLoaderBase::Shutdown() {
   1354  CancelAndClearDynamicImports();
   1355 
   1356  for (const auto& entry : mFetchingModules) {
   1357    RefPtr<LoadingRequest> loadingRequest(entry.GetData());
   1358    if (loadingRequest) {
   1359      ResumeWaitingRequests(loadingRequest, false);
   1360    }
   1361  }
   1362 
   1363  for (const auto& entry : mFetchedModules) {
   1364    if (entry.GetData()) {
   1365      entry.GetData()->Shutdown();
   1366    }
   1367  }
   1368 
   1369  mFetchingModules.Clear();
   1370  mFetchedModules.Clear();
   1371  mGlobalObject = nullptr;
   1372  mLoader = nullptr;
   1373 }
   1374 
   1375 bool ModuleLoaderBase::HasFetchingModules() const {
   1376  return !mFetchingModules.IsEmpty();
   1377 }
   1378 
   1379 bool ModuleLoaderBase::HasPendingDynamicImports() const {
   1380  return !mDynamicImportRequests.isEmpty();
   1381 }
   1382 
   1383 void ModuleLoaderBase::CancelDynamicImport(ModuleLoadRequest* aRequest,
   1384                                           nsresult aResult) {
   1385  // aRequest may have already been unlinked by CC.
   1386  MOZ_ASSERT(aRequest->mLoader == this || !aRequest->mLoader);
   1387 
   1388  RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
   1389  if (!aRequest->IsCanceled()) {
   1390    // If the ClearDynamicImport() has been called, then it should have been
   1391    // removed from mDynamicImportRequests as well.
   1392    MOZ_ASSERT(!aRequest->mPayload.isUndefined());
   1393    aRequest->Cancel();
   1394  }
   1395 }
   1396 
   1397 void ModuleLoaderBase::RemoveDynamicImport(ModuleLoadRequest* aRequest) {
   1398  MOZ_ASSERT(aRequest->IsDynamicImport());
   1399  mDynamicImportRequests.Remove(aRequest);
   1400 }
   1401 
   1402 #ifdef DEBUG
   1403 bool ModuleLoaderBase::HasDynamicImport(
   1404    const ModuleLoadRequest* aRequest) const {
   1405  MOZ_ASSERT(aRequest->mLoader == this);
   1406  return mDynamicImportRequests.Contains(
   1407      const_cast<ModuleLoadRequest*>(aRequest));
   1408 }
   1409 #endif
   1410 
   1411 bool ModuleLoaderBase::InstantiateModuleGraph(ModuleLoadRequest* aRequest) {
   1412  // Instantiate a top-level module and record any error.
   1413 
   1414  MOZ_ASSERT(aRequest);
   1415  MOZ_ASSERT(aRequest->mLoader == this);
   1416  MOZ_ASSERT(aRequest->IsTopLevel());
   1417 
   1418  LOG(("ScriptLoadRequest (%p): Instantiate module graph", aRequest));
   1419 
   1420  AUTO_PROFILER_LABEL("ModuleLoaderBase::InstantiateModuleGraph", JS);
   1421 
   1422  ModuleScript* moduleScript = aRequest->mModuleScript;
   1423  MOZ_ASSERT(moduleScript);
   1424 
   1425  MOZ_ASSERT(!moduleScript->HasParseError());
   1426  MOZ_ASSERT(moduleScript->ModuleRecord());
   1427 
   1428  AutoJSAPI jsapi;
   1429  if (NS_WARN_IF(!jsapi.Init(mGlobalObject))) {
   1430    return false;
   1431  }
   1432 
   1433  JSContext* cx = jsapi.cx();
   1434  Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());
   1435  if (!xpc::Scriptability::AllowedIfExists(module)) {
   1436    return true;
   1437  }
   1438 
   1439  if (!ModuleLink(jsapi.cx(), module)) {
   1440    LOG(("ScriptLoadRequest (%p): Instantiate failed", aRequest));
   1441    MOZ_ASSERT(jsapi.HasException());
   1442    RootedValue exception(jsapi.cx());
   1443    if (!jsapi.StealException(&exception)) {
   1444      return false;
   1445    }
   1446    MOZ_ASSERT(!exception.isUndefined());
   1447    moduleScript->SetErrorToRethrow(exception);
   1448  }
   1449 
   1450  return true;
   1451 }
   1452 
   1453 void ModuleLoaderBase::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
   1454  AutoJSAPI jsapi;
   1455  if (!jsapi.Init(GetGlobalObject())) {
   1456    return;
   1457  }
   1458  JSContext* cx = jsapi.cx();
   1459  MOZ_ASSERT(aRequest->IsDynamicImport());
   1460 
   1461  if (aRequest->IsErrored()) {
   1462    LOG(("ScriptLoadRequest (%p): ProcessDynamicImport, request has an error",
   1463         aRequest));
   1464    // The error is already processed in OnLoadRequestedModulesRejected.
   1465    return;
   1466  }
   1467 
   1468  LOG(("ScriptLoadRequest (%p): ProcessDynamicImport", aRequest));
   1469  FinishLoadingImportedModule(cx, aRequest);
   1470 
   1471  (void)mLoader->MaybePrepareModuleForDiskCacheAfterExecute(aRequest, NS_OK);
   1472 
   1473  mLoader->MaybeUpdateDiskCache();
   1474 }
   1475 
   1476 nsresult ModuleLoaderBase::EvaluateModule(ModuleLoadRequest* aRequest) {
   1477  MOZ_ASSERT(aRequest->mLoader == this);
   1478 
   1479  mozilla::nsAutoMicroTask mt;
   1480  mozilla::dom::AutoEntryScript aes(mGlobalObject, "EvaluateModule",
   1481                                    NS_IsMainThread());
   1482 
   1483  // We would like to handle any evaluation errors synchronously for service
   1484  // workers immediately such that if we are performing service worker
   1485  // registration we can fail it right away rather than having it fail later on
   1486  // an event loop turn which might be too late and the registration process
   1487  // would have succeeded by then.
   1488  return EvaluateModuleInContext(
   1489      aes.cx(), aRequest,
   1490      IsForServiceWorker() ? ThrowModuleErrorsSync : ReportModuleErrorsAsync);
   1491 }
   1492 
   1493 nsresult ModuleLoaderBase::EvaluateModuleInContext(
   1494    JSContext* aCx, ModuleLoadRequest* aRequest,
   1495    ModuleErrorBehaviour errorBehaviour) {
   1496  MOZ_ASSERT(aRequest->mLoader == this);
   1497  MOZ_ASSERT_IF(!mGlobalObject->GetModuleLoader(aCx)->IsOverridden(),
   1498                mGlobalObject->GetModuleLoader(aCx) == this);
   1499  MOZ_ASSERT_IF(mGlobalObject->GetModuleLoader(aCx)->IsOverridden(),
   1500                mGlobalObject->GetModuleLoader(aCx)->IsOverriddenBy(this));
   1501  MOZ_ASSERT(!aRequest->IsDynamicImport());
   1502 
   1503  AUTO_PROFILER_LABEL("ModuleLoaderBase::EvaluateModule", JS);
   1504 
   1505  nsAutoCString profilerLabelString;
   1506  if (aRequest->HasScriptLoadContext()) {
   1507    aRequest->GetScriptLoadContext()->GetProfilerLabel(profilerLabelString);
   1508  }
   1509 
   1510  LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
   1511  AUTO_PROFILER_MARKER_TEXT("ModuleEvaluation", JS,
   1512                            MarkerInnerWindowIdFromJSContext(aCx),
   1513                            profilerLabelString);
   1514 
   1515  MOZ_ASSERT(aRequest->mModuleScript);
   1516  MOZ_ASSERT_IF(aRequest->HasScriptLoadContext(),
   1517                !aRequest->GetScriptLoadContext()->mCompileOrDecodeTask);
   1518 
   1519  ModuleScript* moduleScript = aRequest->mModuleScript;
   1520  if (moduleScript->HasErrorToRethrow()) {
   1521    LOG(("ScriptLoadRequest (%p):   module has error to rethrow", aRequest));
   1522    Rooted<Value> error(aCx, moduleScript->ErrorToRethrow());
   1523    JS_SetPendingException(aCx, error);
   1524    return NS_OK;
   1525  }
   1526 
   1527  Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
   1528  MOZ_ASSERT(module);
   1529  MOZ_ASSERT(CurrentGlobalOrNull(aCx) == GetNonCCWObjectGlobal(module));
   1530 
   1531  if (!xpc::Scriptability::AllowedIfExists(module)) {
   1532    return NS_OK;
   1533  }
   1534 
   1535  if (aRequest->HasScriptLoadContext()) {
   1536    TRACE_FOR_TEST(aRequest, "evaluate:module");
   1537  }
   1538 
   1539  Rooted<Value> rval(aCx);
   1540 
   1541  bool ok = ModuleEvaluate(aCx, module, &rval);
   1542 
   1543  // ModuleEvaluate will usually set a pending exception if it returns false,
   1544  // unless the user cancels execution.
   1545  MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aCx));
   1546 
   1547  nsresult rv = NS_OK;
   1548  if (!ok || IsModuleEvaluationAborted(aRequest)) {
   1549    LOG(("ScriptLoadRequest (%p):   evaluation failed", aRequest));
   1550    // For a dynamic import, the promise is rejected. Otherwise an error is
   1551    // reported by AutoEntryScript.
   1552    rv = NS_ERROR_ABORT;
   1553  }
   1554 
   1555  // ModuleEvaluate returns a promise unless the user cancels the execution in
   1556  // which case rval will be undefined. We should treat it as a failed
   1557  // evaluation, and reject appropriately.
   1558  Rooted<JSObject*> evaluationPromise(aCx);
   1559  if (rval.isObject()) {
   1560    evaluationPromise.set(&rval.toObject());
   1561  }
   1562 
   1563  // If the promise is rejected, the value is unwrapped from the promise value.
   1564  if (!ThrowOnModuleEvaluationFailure(aCx, evaluationPromise, errorBehaviour)) {
   1565    LOG(("ScriptLoadRequest (%p):   evaluation failed on throw", aRequest));
   1566 
   1567    if (IsForServiceWorker()) {
   1568      // If this module eval is being done for service workers, then we would
   1569      // like to throw/return right from here, such that if we are in
   1570      // registration phase, we can fail it synchronously right away.
   1571      return NS_ERROR_ABORT;
   1572    }
   1573  }
   1574 
   1575  rv = mLoader->MaybePrepareModuleForDiskCacheAfterExecute(aRequest, NS_OK);
   1576 
   1577  mLoader->MaybeUpdateDiskCache();
   1578 
   1579  return rv;
   1580 }
   1581 
   1582 void ModuleLoaderBase::CancelAndClearDynamicImports() {
   1583  while (ScriptLoadRequest* req = mDynamicImportRequests.getFirst()) {
   1584    // This also removes the request from the list.
   1585    CancelDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
   1586  }
   1587 }
   1588 
   1589 UniquePtr<ImportMap> ModuleLoaderBase::ParseImportMap(
   1590    ScriptLoadRequest* aRequest) {
   1591  AutoJSAPI jsapi;
   1592  if (!jsapi.Init(GetGlobalObject())) {
   1593    return nullptr;
   1594  }
   1595 
   1596  MOZ_ASSERT(aRequest->IsTextSource());
   1597  MaybeSourceText maybeSource;
   1598  nsresult rv = aRequest->GetScriptSource(jsapi.cx(), &maybeSource,
   1599                                          aRequest->mLoadContext.get());
   1600  if (NS_FAILED(rv)) {
   1601    return nullptr;
   1602  }
   1603 
   1604  SourceText<char16_t>& text = maybeSource.ref<SourceText<char16_t>>();
   1605  ReportWarningHelper warning{mLoader, aRequest};
   1606 
   1607  // https://html.spec.whatwg.org/multipage/webappapis.html#create-an-import-map-parse-result
   1608  // Step 2. Parse an import map string given input and baseURL, catching any
   1609  // exceptions. If this threw an exception, then set result's error to rethrow
   1610  // to that exception. Otherwise, set result's import map to the return value.
   1611  //
   1612  // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
   1613  // Step 1. If result's error to rethrow is not null, then report the exception
   1614  // given by result's error to rethrow and return.
   1615  //
   1616  // Impl note: We didn't implement 'Import map parse result' from the spec,
   1617  // https://html.spec.whatwg.org/multipage/webappapis.html#import-map-parse-result
   1618  // As the struct has another item called 'error to rethow' to store the
   1619  // exception thrown during parsing import-maps, and report that exception
   1620  // while registering an import map. Currently only inline import-maps are
   1621  // supported, therefore parsing and registering import-maps will be executed
   1622  // consecutively. To simplify the implementation, we didn't create the 'error
   1623  // to rethow' item and report the exception immediately(done in ~AutoJSAPI).
   1624  return ImportMap::ParseString(jsapi.cx(), text, aRequest->BaseURL(), warning);
   1625 }
   1626 
   1627 void ModuleLoaderBase::RegisterImportMap(UniquePtr<ImportMap> aImportMap) {
   1628  // Check for aImportMap is done in ScriptLoader.
   1629  MOZ_ASSERT(aImportMap);
   1630 
   1631  // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
   1632  // The step 1(report the exception if there's an error) is done in
   1633  // ParseImportMap.
   1634  //
   1635  // Step 2. Assert: global's import map is an empty import map.
   1636  // Impl note: The default import map from the spec is an empty import map, but
   1637  // from the implementation it defaults to nullptr, so we check if the global's
   1638  // import map is null here.
   1639  //
   1640  // Also see
   1641  // https://html.spec.whatwg.org/multipage/webappapis.html#empty-import-map
   1642  MOZ_ASSERT(!mImportMap);
   1643 
   1644  // Step 3. Set global's import map to result's import map.
   1645  mImportMap = std::move(aImportMap);
   1646 
   1647  // Any import resolution has been invalidated by the addition of the import
   1648  // map. If speculative preloading is currently fetching any modules then
   1649  // cancel their requests and remove them from the map.
   1650  //
   1651  // The cancelled requests will still complete later so we have to check this
   1652  // in SetModuleFetchFinishedAndGetWaitingRequests.
   1653  for (const auto& entry : mFetchingModules) {
   1654    LoadingRequest* loadingRequest = entry.GetData();
   1655    MOZ_DIAGNOSTIC_ASSERT(loadingRequest->mRequest->mLoadContext->IsPreload());
   1656    loadingRequest->mRequest->Cancel();
   1657    for (const auto& request : loadingRequest->mWaiting) {
   1658      MOZ_DIAGNOSTIC_ASSERT(request->mLoadContext->IsPreload());
   1659      request->Cancel();
   1660    }
   1661  }
   1662  mFetchingModules.Clear();
   1663 
   1664  // If speculative preloading has added modules to the module map, remove
   1665  // them.
   1666  for (const auto& entry : mFetchedModules) {
   1667    ModuleScript* script = entry.GetData();
   1668    if (script) {
   1669      MOZ_DIAGNOSTIC_ASSERT(
   1670          script->ForPreload(),
   1671          "Non-preload module loads should block import maps");
   1672      MOZ_DIAGNOSTIC_ASSERT(!script->HadImportMap(),
   1673                            "Only one import map can be registered");
   1674 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
   1675      if (JSObject* module = script->ModuleRecord()) {
   1676        MOZ_DIAGNOSTIC_ASSERT(!ModuleIsLinked(module));
   1677      }
   1678 #endif
   1679      script->Shutdown();
   1680    }
   1681  }
   1682  mFetchedModules.Clear();
   1683 }
   1684 
   1685 void ModuleLoaderBase::CopyModulesTo(ModuleLoaderBase* aDest) {
   1686  MOZ_ASSERT(aDest->mFetchingModules.IsEmpty());
   1687  MOZ_ASSERT(aDest->mFetchedModules.IsEmpty());
   1688  MOZ_ASSERT(mFetchingModules.IsEmpty());
   1689 
   1690  for (const auto& entry : mFetchedModules) {
   1691    RefPtr<ModuleScript> moduleScript = entry.GetData();
   1692    if (!moduleScript) {
   1693      continue;
   1694    }
   1695    aDest->mFetchedModules.InsertOrUpdate(entry, moduleScript);
   1696  }
   1697 }
   1698 
   1699 void ModuleLoaderBase::MoveModulesTo(ModuleLoaderBase* aDest) {
   1700  MOZ_ASSERT(mFetchingModules.IsEmpty());
   1701  MOZ_ASSERT(aDest->mFetchingModules.IsEmpty());
   1702 
   1703  for (const auto& entry : mFetchedModules) {
   1704    RefPtr<ModuleScript> moduleScript = entry.GetData();
   1705    if (!moduleScript) {
   1706      continue;
   1707    }
   1708 
   1709 #ifdef DEBUG
   1710    if (auto existingEntry = aDest->mFetchedModules.Lookup(entry)) {
   1711      MOZ_ASSERT(moduleScript == existingEntry.Data());
   1712    }
   1713 #endif
   1714 
   1715    aDest->mFetchedModules.InsertOrUpdate(entry, moduleScript);
   1716  }
   1717 
   1718  mFetchedModules.Clear();
   1719 }
   1720 
   1721 #undef LOG
   1722 #undef LOG_ENABLED
   1723 
   1724 }  // namespace JS::loader