Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ModuleLoaderBase.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "GeckoProfiler.h"
#include "LoadedScript.h"
#include "ModuleLoadRequest.h"
#include "ScriptLoadRequest.h"
#include "mozilla/dom/ScriptSettings.h"  // AutoJSAPI
#include "mozilla/dom/ScriptTrace.h"

#include "js/Array.h"  // JS::GetArrayLength
#include "js/CompilationAndEvaluation.h"
#include "js/ColumnNumber.h"          // JS::ColumnNumberOneOrigin
#include "js/ContextOptions.h"        // JS::ContextOptionsRef
#include "js/ErrorReport.h"           // JSErrorBase
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/Modules.h"  // JS::FinishLoadingImportedModule, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{Load,Metadata}Hook
#include "js/PropertyAndElement.h"  // JS_DefineProperty, JS_GetElement
#include "js/SourceText.h"
#include "mozilla/Assertions.h"  // MOZ_ASSERT
#include "mozilla/BasePrincipal.h"
#include "mozilla/dom/AutoEntryScript.h"
#include "mozilla/dom/ScriptLoadContext.h"
#include "mozilla/CycleCollectedJSContext.h"  // nsAutoMicroTask
#include "mozilla/Preferences.h"
#include "mozilla/RefPtr.h"  // mozilla::StaticRefPtr
#include "mozilla/StaticPrefs_dom.h"
#include "nsContentUtils.h"
#include "nsICacheInfoChannel.h"  // nsICacheInfoChannel
#include "nsNetUtil.h"            // NS_NewURI
#include "xpcpublic.h"

using mozilla::AutoSlowOperation;
using mozilla::CycleCollectedJSContext;
using mozilla::Err;
using mozilla::MakeUnique;
using mozilla::MicroTaskRunnable;
using mozilla::Preferences;
using mozilla::UniquePtr;
using mozilla::dom::AutoJSAPI;
using mozilla::dom::ReferrerPolicy;

namespace JS::loader {

mozilla::LazyLogModule ModuleLoaderBase::gCspPRLog("CSP");
mozilla::LazyLogModule ModuleLoaderBase::gModuleLoaderBaseLog(
    "ModuleLoaderBase");

#undef LOG
#define LOG(args)                                                           \
  MOZ_LOG(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug, \
          args)

#define LOG_ENABLED() \
  MOZ_LOG_TEST(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug)

//////////////////////////////////////////////////////////////
// ModuleLoaderBase::LoadingRequest
//////////////////////////////////////////////////////////////

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase::LoadingRequest)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase::LoadingRequest, mRequest, mWaiting)

NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase::LoadingRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase::LoadingRequest)

//////////////////////////////////////////////////////////////
// ModuleLoaderBase
//////////////////////////////////////////////////////////////

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoaderBase)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTION(ModuleLoaderBase, mFetchingModules, mFetchedModules,
                         mDynamicImportRequests, mGlobalObject, mOverriddenBy,
                         mLoader)

NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleLoaderBase)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleLoaderBase)

// static
void ModuleLoaderBase::EnsureModuleHooksInitialized() {
  AutoJSAPI jsapi;
  jsapi.Init();
  JSRuntime* rt = JS_GetRuntime(jsapi.cx());
  if (GetModuleLoadHook(rt)) {
    return;
  }

  SetModuleLoadHook(rt, HostLoadImportedModule);
  SetModuleMetadataHook(rt, HostPopulateImportMeta);
  SetScriptPrivateReferenceHooks(rt, HostAddRefScriptFetchInfo,
                                 HostReleaseScriptFetchInfo);
}

static bool CreateBadModuleTypeError(JSContext* aCx,
                                     ScriptFetchInfo* aFetchInfo, nsIURI* aURI,
                                     MutableHandle<Value> aErrorOut) {
  Rooted<JSString*> filename(aCx);
  if (aFetchInfo) {
    nsAutoCString url;
    aFetchInfo->BaseURL()->GetAsciiSpec(url);
    filename = JS_NewStringCopyZ(aCx, url.get());
  } else {
    filename = JS_NewStringCopyZ(aCx, "(unknown)");
  }

  if (!filename) {
    return false;
  }

  MOZ_ASSERT(aURI);
  nsAutoCString url;
  aURI->GetSpec(url);

  Rooted<JSString*> uri(aCx, JS_NewStringCopyZ(aCx, url.get()));
  if (!uri) {
    return false;
  }

  Rooted<JSString*> msg(aCx, JS_NewStringCopyZ(aCx, ": invalid module type"));
  if (!msg) {
    return false;
  }

  Rooted<JSString*> errMsg(aCx, JS_ConcatStrings(aCx, uri, msg));
  if (!errMsg) {
    return false;
  }

  return CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0,
                     ColumnNumberOneOrigin(), nullptr, errMsg,
                     NothingHandleValue, aErrorOut);
}

// https://html.spec.whatwg.org/#hostloadimportedmodule
// static
bool ModuleLoaderBase::HostLoadImportedModule(
    JSContext* aCx, Handle<JSScript*> aReferrer,
    Handle<JSObject*> aModuleRequest, Handle<Value> aHostDefined,
    Handle<Value> aPayload, uint32_t aLineNumber,
    JS::ColumnNumberOneOrigin aColumnNumber) {
  Rooted<JSObject*> object(aCx);
  if (aPayload.isObject()) {
    object = &aPayload.toObject();
  }
  bool isDynamicImport = object && IsPromiseObject(object);

  Rooted<JSString*> specifierString(
      aCx, GetModuleRequestSpecifier(aCx, aModuleRequest));
  if (!specifierString) {
    JS_ReportOutOfMemory(aCx);
    return false;
  }

  // Let url be the result of resolving a module specifier given referencing
  // module script and specifier.
  nsAutoJSString string;
  if (!string.init(aCx, specifierString)) {
    JS_ReportOutOfMemory(aCx);
    return false;
  }

  RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
  if (!loader) {
    return false;
  }

  if (isDynamicImport && !loader->IsDynamicImportSupported()) {
    JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
                              JSMSG_DYNAMIC_IMPORT_NOT_SUPPORTED);
    return false;
  }

  RefPtr<ScriptFetchInfo> fetchInfo(GetScriptFetchInfoOrNull(aReferrer));

  // Step 8. Let url be the result of resolving a module specifier given
  //   referencingScript and moduleRequest.[[Specifier]], catching any
  //   exceptions. If they throw an exception, let resolutionError be the
  //   thrown exception.
  auto result = loader->ResolveModuleSpecifier(fetchInfo, string);

  // Step 9. If the previous step threw an exception, then:
  if (result.isErr()) {
    Rooted<Value> error(aCx);
    nsresult rv =
        loader->HandleResolveFailure(aCx, fetchInfo, string, result.unwrapErr(),
                                     aLineNumber, aColumnNumber, &error);
    if (NS_FAILED(rv)) {
      JS_ReportOutOfMemory(aCx);
      return false;
    }

    // Step 2. Perform FinishLoadingImportedModule(referrer, moduleRequest,
    //   payload, ThrowCompletion(resolutionError)).
    FinishLoadingImportedModuleFailed(aCx, aPayload, error);

    // Step 3. Return.
    return true;
  }

  MOZ_ASSERT(result.isOk());
  auto record = result.unwrap();
  nsCOMPtr<nsIURI> uri = record->Result();
  MOZ_ASSERT(uri, "Failed to resolve module specifier");

  if (ImportMap::IsMultipleImportMapsSupported()) {
    // This implements the 'Add module to resolved module set' part defined in
    // 'resolve a module specifier'. This is done here because we need to
    // process the resolved specifiers differently for preloading module
    // scripts.
    //
    // See https://html.spec.whatwg.org/#resolve-a-module-specifier
    loader->AddToResolvedModuleSet(std::move(record), fetchInfo, aHostDefined);
  }

  ModuleType moduleType = GetModuleRequestType(aCx, aModuleRequest);
  if (!loader->IsModuleTypeAllowed(moduleType)) {
    LOG(("ModuleLoaderBase::HostLoadImportedModule uri %s, bad module type",
         uri->GetSpecOrDefault().get()));
    Rooted<Value> error(aCx);
    if (!CreateBadModuleTypeError(aCx, fetchInfo, uri, &error)) {
      JS_ReportOutOfMemory(aCx);
      return false;
    }
    JS_SetPendingException(aCx, error);
    return false;
  }

  RefPtr<ScriptFetchOptions> options = nullptr;
  ReferrerPolicy referrerPolicy;
  nsIURI* fetchReferrer = nullptr;
  if (fetchInfo) {
    options = fetchInfo->FetchOptions();
    referrerPolicy = fetchInfo->ReferrerPolicy();
    fetchReferrer = fetchInfo->BaseURL();
  } else {
    options = loader->CreateDefaultScriptFetchOptions();
    referrerPolicy = ReferrerPolicy::_empty;
    fetchReferrer = loader->GetClientReferrerURI();
  }

  mozilla::dom::SRIMetadata sriMetadata;
  loader->GetImportMapSRI(
      uri, fetchReferrer,
      loader->GetScriptLoaderInterface()->GetConsoleReportCollector(),
      &sriMetadata);

  RefPtr<ModuleLoadRequest> request = loader->CreateRequest(
      aCx, uri, aModuleRequest, aHostDefined, aPayload, isDynamicImport,
      options, referrerPolicy, fetchReferrer, sriMetadata);
  if (!request) {
    MOZ_ASSERT(isDynamicImport);
    nsAutoCString url;
    uri->GetSpec(url);
    JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
                              JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
    return false;
  }

  LOG(
      ("ModuleLoaderBase::HostLoadImportedModule loader (%p) uri %s referrer "
       "(%p) request (%p)",
       loader.get(), uri->GetSpecOrDefault().get(), aReferrer.get(),
       request.get()));

  request->SetImport(aReferrer, aModuleRequest, aPayload);

  if (isDynamicImport) {
    loader->AppendDynamicImport(request);
  }

  nsresult rv = loader->StartModuleLoad(request);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    MOZ_ASSERT(!request->mModuleScript);
    loader->GetScriptLoaderInterface()->ReportErrorToConsole(request, rv);
    if (isDynamicImport) {
      loader->RemoveDynamicImport(request);

      nsAutoCString url;
      uri->GetSpec(url);
      JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
                                JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
    } else {
      loader->OnFetchFailed(request);
      return true;
    }

    return false;
  }

  if (isDynamicImport) {
    loader->OnDynamicImportStarted(request);
  }

  return true;
}

// static
bool ModuleLoaderBase::FinishLoadingImportedModule(
    JSContext* aCx, ModuleLoadRequest* aRequest) {
  // The request should been removed from mDynamicImportRequests.
  MOZ_ASSERT_IF(aRequest->IsDynamicImport(),
                !aRequest->mLoader->HasDynamicImport(aRequest));

  Rooted<JSObject*> module(aCx);
  {
    ModuleScript* moduleScript = aRequest->mModuleScript;
    MOZ_ASSERT(moduleScript);
    MOZ_ASSERT(moduleScript->ModuleRecord());
    module.set(moduleScript->ModuleRecord());
  }
  MOZ_ASSERT(module);

  Rooted<JSScript*> referrer(aCx, aRequest->mReferrerScript);
  Rooted<JSObject*> moduleReqObj(aCx, aRequest->mModuleRequestObj);
  Rooted<Value> statePrivate(aCx, aRequest->mPayload);
  Rooted<Value> payload(aCx, aRequest->mPayload);

  LOG(("ScriptLoadRequest (%p): FinishLoadingImportedModule module (%p)",
       aRequest, module.get()));
  bool usePromise = aRequest->HasScriptLoadContext();
  MOZ_ALWAYS_TRUE(JS::FinishLoadingImportedModule(aCx, referrer, moduleReqObj,
                                                  payload, module, usePromise));
  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
  aRequest->ClearImport();

  return true;
}

// static
bool ModuleLoaderBase::ImportMetaResolve(JSContext* cx, unsigned argc,
                                         Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  RootedValue moduleValue(
      cx, js::GetFunctionNativeReserved(
              &args.callee(),
              static_cast<size_t>(ImportMetaSlots::ModuleRecordSlot)));
  MOZ_ASSERT(!moduleValue.isUndefined());
  RootedObject moduleRecord(cx, &moduleValue.toObject());
  RootedValue modulePrivate(cx, GetModulePrivate(moduleRecord));
  MOZ_ASSERT(!modulePrivate.isUndefined());

  // https://html.spec.whatwg.org/#hostgetimportmetaproperties
  // Step 4.1. Set specifier to ? ToString(specifier).
  //
  // https://tc39.es/ecma262/#sec-tostring
  RootedValue v(cx, args.get(ImportMetaResolveSpecifierArg));
  RootedString specifier(cx, ToString(cx, v));
  if (!specifier) {
    return false;
  }

  // Step 4.2, 4.3 are implemented in ImportMetaResolveImpl.
  RootedString url(cx, ImportMetaResolveImpl(cx, modulePrivate, specifier));
  if (!url) {
    return false;
  }

  // Step 4.4. Return the serialization of url.
  args.rval().setString(url);
  return true;
}

// static
JSString* ModuleLoaderBase::ImportMetaResolveImpl(
    JSContext* aCx, Handle<Value> aReferencingPrivate,
    Handle<JSString*> aSpecifier) {
  RootedString urlString(aCx);

  // The destructor of RefPtr local variables can GC, and they should be
  // performed before returning the raw JSString* pointer.
  {
    RefPtr<ScriptFetchInfo> fetchInfo =
        static_cast<ScriptFetchInfo*>(aReferencingPrivate.toPrivate());
    MOZ_ASSERT(fetchInfo->IsForModuleScript());

    RefPtr<ModuleLoaderBase> loader = GetCurrentModuleLoader(aCx);
    if (!loader) {
      return nullptr;
    }

    nsAutoJSString specifier;
    if (!specifier.init(aCx, aSpecifier)) {
      return nullptr;
    }

    auto result = loader->ResolveModuleSpecifier(fetchInfo, specifier);
    if (result.isErr()) {
      Rooted<Value> error(aCx);
      nsresult rv = loader->HandleResolveFailure(
          aCx, fetchInfo, specifier, result.unwrapErr(), 0,
          ColumnNumberOneOrigin(), &error);
      if (NS_FAILED(rv)) {
        JS_ReportOutOfMemory(aCx);
        return nullptr;
      }

      JS_SetPendingException(aCx, error);

      return nullptr;
    }

    MOZ_ASSERT(result.isOk());
    auto record = result.unwrap();

    nsCOMPtr<nsIURI> uri = record->Result();
    if (ImportMap::IsMultipleImportMapsSupported()) {
      // This implements the 'Add module to resolved module set' part defined in
      // 'resolve a module specifier'.
      loader->AddToResolvedModuleSet(std::move(record));
    }

    nsAutoCString url;
    MOZ_ALWAYS_SUCCEEDS(uri->GetAsciiSpec(url));

    urlString.set(JS_NewStringCopyZ(aCx, url.get()));
  }

  return urlString;
}

// static
bool ModuleLoaderBase::HostPopulateImportMeta(JSContext* aCx,
                                              Handle<JSObject*> aModuleRecord,
                                              Handle<JSObject*> aMetaObject) {
  RefPtr<ScriptFetchInfo> fetchInfo = static_cast<ScriptFetchInfo*>(
      JS::GetModulePrivate(aModuleRecord).toPrivate());
  MOZ_ASSERT(fetchInfo->IsForModuleScript());

  nsAutoCString url;
  MOZ_DIAGNOSTIC_ASSERT(fetchInfo->BaseURL());
  MOZ_ALWAYS_SUCCEEDS(fetchInfo->BaseURL()->GetAsciiSpec(url));

  Rooted<JSString*> urlString(aCx, JS_NewStringCopyZ(aCx, url.get()));
  if (!urlString) {
    JS_ReportOutOfMemory(aCx);
    return false;
  }

  // https://html.spec.whatwg.org/#import-meta-url
  if (!JS_DefineProperty(aCx, aMetaObject, "url", urlString,
                         JSPROP_ENUMERATE)) {
    return false;
  }

  // https://html.spec.whatwg.org/#import-meta-resolve
  // Define 'resolve' function on the import.meta object.
  JSFunction* resolveFunc = js::DefineFunctionWithReserved(
      aCx, aMetaObject, "resolve", ImportMetaResolve, ImportMetaResolveNumArgs,
      JSPROP_ENUMERATE);
  if (!resolveFunc) {
    return false;
  }

  // Store the 'active script' of the meta object into the function slot.
  // See https://html.spec.whatwg.org/#active-script
  //
  // Note: Hold a reference to the module record which in turn keeps the
  // ModuleScript alive when import.resolve is called.
  RootedObject resolveFuncObj(aCx, JS_GetFunctionObject(resolveFunc));
  js::SetFunctionNativeReserved(
      resolveFuncObj, static_cast<size_t>(ImportMetaSlots::ModuleRecordSlot),
      JS::ObjectValue(*aModuleRecord));

  return true;
}

AutoOverrideModuleLoader::AutoOverrideModuleLoader(ModuleLoaderBase* aTarget,
                                                   ModuleLoaderBase* aLoader)
    : mTarget(aTarget) {
  mTarget->SetOverride(aLoader);
}

AutoOverrideModuleLoader::~AutoOverrideModuleLoader() {
  mTarget->ResetOverride();
}

void ModuleLoaderBase::SetOverride(ModuleLoaderBase* aLoader) {
  MOZ_ASSERT(!mOverriddenBy);
  MOZ_ASSERT(!aLoader->mOverriddenBy);
  MOZ_ASSERT(mGlobalObject == aLoader->mGlobalObject);
  mOverriddenBy = aLoader;
}

bool ModuleLoaderBase::IsOverridden() { return !!mOverriddenBy; }

bool ModuleLoaderBase::IsOverriddenBy(ModuleLoaderBase* aLoader) {
  return mOverriddenBy == aLoader;
}

void ModuleLoaderBase::ResetOverride() {
  MOZ_ASSERT(mOverriddenBy);
  mOverriddenBy = nullptr;
}

// static
ModuleLoaderBase* ModuleLoaderBase::GetCurrentModuleLoader(JSContext* aCx) {
  auto reportError = mozilla::MakeScopeExit([aCx]() {
    JS_ReportErrorASCII(aCx, "No ScriptLoader found for the current context");
  });

  Rooted<JSObject*> object(aCx, CurrentGlobalOrNull(aCx));
  if (!object) {
    return nullptr;
  }

  nsIGlobalObject* global = xpc::NativeGlobal(object);
  if (!global) {
    return nullptr;
  }

  ModuleLoaderBase* loader = global->GetModuleLoader(aCx);
  if (!loader) {
    return nullptr;
  }

  MOZ_ASSERT(loader->mGlobalObject == global);

  reportError.release();

  if (loader->mOverriddenBy) {
    MOZ_ASSERT(loader->mOverriddenBy->mGlobalObject == global);
    return loader->mOverriddenBy;
  }
  return loader;
}

// static
ScriptFetchInfo* ModuleLoaderBase::GetScriptFetchInfoOrNull(
    Handle<JSScript*> aReferrer) {
  if (!aReferrer) {
    return nullptr;
  }

  Value value = GetScriptPrivate(aReferrer);
  if (value.isUndefined()) {
    return nullptr;
  }

  return static_cast<ScriptFetchInfo*>(value.toPrivate());
}

nsresult ModuleLoaderBase::StartModuleLoad(ModuleLoadRequest* aRequest) {
  return StartOrRestartModuleLoad(aRequest, RestartRequest::No);
}

nsresult ModuleLoaderBase::RestartModuleLoad(ModuleLoadRequest* aRequest) {
  return StartOrRestartModuleLoad(aRequest, RestartRequest::Yes);
}

nsresult ModuleLoaderBase::StartOrRestartModuleLoad(ModuleLoadRequest* aRequest,
                                                    RestartRequest aRestart) {
  MOZ_ASSERT(aRequest->mLoader == this);
  MOZ_ASSERT(aRequest->IsFetching());

  MOZ_ASSERT_IF(aRequest->IsRetrievedFromMemoryCache(),
                aRestart == RestartRequest::No);

  if (!aRequest->IsRetrievedFromMemoryCache()) {
    aRequest->SetUnknownDataType();
  }

  if (LOG_ENABLED()) {
    nsAutoCString url;
    aRequest->URI()->GetAsciiSpec(url);
    LOG(("ScriptLoadRequest (%p): Start module load %s", aRequest, url.get()));
  }

  // If we're restarting the request, the module should already be in the
  // "fetching" map.
  MOZ_ASSERT_IF(
      aRestart == RestartRequest::Yes,
      IsModuleFetching(ModuleMapKey(aRequest->URI(), aRequest->mModuleType)));

  // Check with the derived class whether we should load this module.
  nsresult rv = NS_OK;
  if (!CanStartLoad(aRequest, &rv)) {
    return rv;
  }

  // Check whether the module has been fetched or is currently being fetched,
  // and if so wait for it rather than starting a new fetch.
  if (aRestart == RestartRequest::No &&
      ModuleMapContainsURL(
          ModuleMapKey(aRequest->URI(), aRequest->mModuleType))) {
    LOG(("ScriptLoadRequest (%p): Waiting for module fetch", aRequest));
    WaitForModuleFetch(aRequest);
    return NS_OK;
  }

  rv = StartFetch(aRequest);
  NS_ENSURE_SUCCESS(rv, rv);

  if (aRequest->IsRetrievedFromMemoryCache()) {
    MOZ_ASSERT(
        IsModuleFetched(ModuleMapKey(aRequest->URI(), aRequest->mModuleType)));
    return NS_OK;
  }

  // We successfully started fetching a module so put its URL in the module
  // map and mark it as fetching.
  if (aRestart == RestartRequest::No) {
    SetModuleFetchStarted(aRequest);
  }

  return NS_OK;
}

bool ModuleLoaderBase::ModuleMapContainsURL(const ModuleMapKey& key) const {
  return IsModuleFetching(key) || IsModuleFetched(key);
}

bool ModuleLoaderBase::IsModuleFetching(const ModuleMapKey& key) const {
  return mFetchingModules.Contains(key);
}

bool ModuleLoaderBase::IsModuleFetched(const ModuleMapKey& key) const {
  return mFetchedModules.Contains(key);
}

nsresult ModuleLoaderBase::GetFetchedModuleURLs(nsTArray<nsCString>& aURLs) {
  for (const auto& entry : mFetchedModules) {
    nsAutoCString spec;
    nsresult rv = entry.mUri->GetSpec(spec);
    NS_ENSURE_SUCCESS(rv, rv);

    aURLs.AppendElement(spec);
  }

  return NS_OK;
}

void ModuleLoaderBase::SetModuleFetchStarted(ModuleLoadRequest* aRequest) {
  // Update the module map to indicate that a module is currently being fetched.

  ModuleMapKey moduleMapKey(aRequest->URI(), aRequest->mModuleType);

  MOZ_ASSERT(aRequest->IsFetching());
  MOZ_ASSERT(!ModuleMapContainsURL(moduleMapKey));

  RefPtr<LoadingRequest> loadingRequest = new LoadingRequest();
  loadingRequest->mRequest = aRequest;
  mFetchingModules.InsertOrUpdate(moduleMapKey, loadingRequest);
}

already_AddRefed<ModuleLoaderBase::LoadingRequest>
ModuleLoaderBase::SetModuleFetchFinishedAndGetWaitingRequests(
    ModuleLoadRequest* aRequest, nsresult aResult) {
  // Update module map with the result of fetching a single module script.
  //
  // If any requests for the same URL are waiting on this one to complete, call
  // ModuleLoaded or LoadFailed to resume or fail them as appropriate.

  MOZ_ASSERT(aRequest->mLoader == this);

  LOG(
      ("ScriptLoadRequest (%p): Module fetch finished (script == %p, result == "
       "%u)",
       aRequest, aRequest->mModuleScript.get(), unsigned(aResult)));

  ModuleMapKey moduleMapKey(aRequest->URI(), aRequest->mModuleType);

  auto entry = mFetchingModules.Lookup(moduleMapKey);
  if (!entry) {
    LOG(
        ("ScriptLoadRequest (%p): Key not found in mFetchingModules, "
         "assuming we have an inline module or have finished fetching already",
         aRequest));
    return nullptr;
  }

  // It's possible for a request to be cancelled and removed from the fetching
  // modules map and a new request started for the same URI and added to the
  // map. In this case we don't want the first cancelled request to complete the
  // later request (which will cause it to fail) so we ignore it.
  RefPtr<LoadingRequest> loadingRequest = entry.Data();
  if (loadingRequest->mRequest != aRequest) {
    MOZ_ASSERT(aRequest->IsCanceled());
    LOG(
        ("ScriptLoadRequest (%p): Ignoring completion of cancelled request "
         "that was removed from the map",
         aRequest));
    return nullptr;
  }

  MOZ_ALWAYS_TRUE(mFetchingModules.Remove(moduleMapKey));

  RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript);
  MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript);

  mFetchedModules.InsertOrUpdate(moduleMapKey, RefPtr{moduleScript});

  return loadingRequest.forget();
}

void ModuleLoaderBase::ResumeWaitingRequests(LoadingRequest* aLoadingRequest,
                                             bool aSuccess) {
  for (ModuleLoadRequest* request : aLoadingRequest->mWaiting) {
    ResumeWaitingRequest(request, aSuccess);
  }
}

void ModuleLoaderBase::ResumeWaitingRequest(ModuleLoadRequest* aRequest,
                                            bool aSuccess) {
  if (aSuccess) {
    aRequest->ModuleLoaded();
  }

  if (!aRequest->IsErrored()) {
    OnFetchSucceeded(aRequest);
  } else {
    OnFetchFailed(aRequest);
  }
}

void ModuleLoaderBase::WaitForModuleFetch(ModuleLoadRequest* aRequest) {
  ModuleMapKey moduleMapKey(aRequest->URI(), aRequest->mModuleType);
  MOZ_ASSERT(ModuleMapContainsURL(moduleMapKey));

  if (auto entry = mFetchingModules.Lookup(moduleMapKey)) {
    RefPtr<LoadingRequest> loadingRequest = entry.Data();
    loadingRequest->mWaiting.AppendElement(aRequest);
    return;
  }

  RefPtr<ModuleScript> ms;
  MOZ_ALWAYS_TRUE(mFetchedModules.Get(moduleMapKey, getter_AddRefs(ms)));

  ResumeWaitingRequest(aRequest, bool(ms));
}

ModuleScript* ModuleLoaderBase::GetFetchedModule(
    const ModuleMapKey& moduleMapKey) const {
  if (LOG_ENABLED()) {
    nsAutoCString url;
    moduleMapKey.mUri->GetAsciiSpec(url);
    LOG(("GetFetchedModule %s", url.get()));
  }

  bool found;
  ModuleScript* ms = mFetchedModules.GetWeak(moduleMapKey, &found);
  MOZ_ASSERT(found);
  return ms;
}

nsresult ModuleLoaderBase::OnFetchComplete(ModuleLoadRequest* aRequest,
                                           nsresult aRv) {
  LOG(("ScriptLoadRequest (%p): OnFetchComplete result %x", aRequest,
       (unsigned)aRv));
  MOZ_ASSERT(aRequest->mLoader == this);
  MOZ_ASSERT(!aRequest->mModuleScript);

  nsresult rv = aRv;
  if (NS_SUCCEEDED(rv)) {
    rv = CreateModuleScript(aRequest);

#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
    // If a module script was created, it should either have a module record
    // object or a parse error.
    if (ModuleScript* ms = aRequest->mModuleScript) {
      MOZ_DIAGNOSTIC_ASSERT(bool(ms->ModuleRecord()) != ms->HasParseError());
    }
#endif

    if (aRequest->getLoadedScript()->IsTextSource()) {
      aRequest->getLoadedScript()->ClearScriptText();
    }

    if (NS_FAILED(rv)) {
      aRequest->LoadFailed();
      return rv;
    }
  }

  RefPtr<LoadingRequest> waitingRequests =
      SetModuleFetchFinishedAndGetWaitingRequests(aRequest, rv);
  MOZ_ASSERT_IF(waitingRequests, waitingRequests->mRequest == aRequest);

  bool success = bool(aRequest->mModuleScript);
  MOZ_ASSERT(NS_SUCCEEDED(rv) == success);

  // TODO: https://github.com/whatwg/html/issues/11755
  // Should we check if the module script has an error to rethrow as well?
  //
  // https://html.spec.whatwg.org/#hostloadimportedmodule:fetch-a-single-imported-module-script
  // Step 14. onSingleFetchComplete: step 2, and step 3
  //   return ThrowCompletion if moduleScript is null or its parse error is not
  //   null:
  //   Step 4. Otherwise, set completion to NormalCompletion(moduleScript's
  //     record).
  if (!aRequest->IsErrored()) {
    OnFetchSucceeded(aRequest);
  } else {
    OnFetchFailed(aRequest);
  }

  if (!waitingRequests) {
    return NS_OK;
  }

  ResumeWaitingRequests(waitingRequests, success);
  return NS_OK;
}

void ModuleLoaderBase::OnFetchSucceeded(ModuleLoadRequest* aRequest) {
  // TODO, Bug 1990416: Align the loading descendants behavior of dynamic import
  // According to the spec, dynamic import should trigger fetching of
  // descendant modules in the JS engine. However, when the dynamic import’s
  // module graph is loaded, ScriptLoaders move the request to a Loaded event
  // queue and invoke ProcessDynamicImport. To account for this behavior,
  // we perform the fetching of submodules in the host layer instead.
  if (aRequest->IsTopLevel() || aRequest->IsDynamicImport()) {
    StartFetchingModuleDependencies(aRequest);
  } else {
    MOZ_ASSERT(!aRequest->IsDynamicImport());
    AutoJSAPI jsapi;
    if (!jsapi.Init(mGlobalObject)) {
      return;
    }
    JSContext* cx = jsapi.cx();
    FinishLoadingImportedModule(cx, aRequest);

    aRequest->SetReady();
    aRequest->LoadFinished();
  }
}

void ModuleLoaderBase::OnFetchFailed(ModuleLoadRequest* aRequest) {
  MOZ_ASSERT(aRequest->IsErrored());
  AutoJSAPI jsapi;
  if (!jsapi.Init(mGlobalObject)) {
    return;
  }
  JSContext* cx = jsapi.cx();

  if (aRequest->IsTopLevel()) {
    // https://html.spec.whatwg.org/#fetch-the-descendants-of-and-link-a-module-script
    // Step 2. If record is null, then:
    // Step 2.1. Set moduleScript's error to rethrow to moduleScript's parse
    //           error.
    if (aRequest->mModuleScript && !aRequest->mModuleScript->ModuleRecord()) {
      MOZ_ASSERT(aRequest->mModuleScript->HasParseError());
      Value parseError = aRequest->mModuleScript->ParseError();
      LOG(("ScriptLoadRequest (%p): found parse error", aRequest));
      aRequest->mModuleScript->SetErrorToRethrow(parseError);
    }
    DispatchModuleErrored(aRequest);

    return;
  }

  // The remaining case is static/dynamic import.
  MOZ_ASSERT(aRequest->IsStaticImport() || aRequest->IsDynamicImport());
  MOZ_ASSERT(!aRequest->mPayload.isUndefined());
  Rooted<Value> payload(cx, aRequest->mPayload);

  // https://html.spec.whatwg.org/#hostloadimportedmodule
  //
  // Step 14.2. If moduleScript is null, then set completion to Completion
  //            Record { [[Type]]: throw, [[Value]]: a new TypeError,
  //            [[Target]]: empty }.
  if (!aRequest->mModuleScript) {
    LOG(
        ("ScriptLoadRequest (%p): FinishLoadingImportedModule: module script "
         "is null",
         aRequest));

    // Impl note:
    // For dynamic import, the TypeError will be pass to the rejected handler of
    // the LoadRequestedModules from the dynamic import.
    //
    // For static import/top-level import, the TypeError will be ignore in the
    // rejected handler of LoadRequestedModules (the state.[[ErrorToRethrow]]
    // isn't set). So we don't actually create a TypeError for the these two
    // cases.
    if (aRequest->GetRootModule()->IsDynamicImport()) {
      nsAutoCString url;
      aRequest->URI()->GetSpec(url);
      JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
                                JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
      FinishLoadingImportedModuleFailedWithPendingException(cx, payload);
    } else {
      Rooted<Value> error(cx, UndefinedValue());
      FinishLoadingImportedModuleFailed(cx, payload, error);
    }

    aRequest->ModuleErrored();
    aRequest->ClearImport();
    return;
  }

  // Step 14.3. Otherwise, if moduleScript's parse error is not null, then:
  //   1. Let parseError be moduleScript's parse error.
  //   2. Set completion to Completion Record { [[Type]]: throw,
  //      [[Value]]: parseError, [[Target]]: empty }.
  //   3. If loadState is not undefined and loadState.[[ErrorToRethrow]]
  //      is null, set loadState.[[ErrorToRethrow]] to parseError.
  MOZ_ASSERT(aRequest->mModuleScript->HasParseError());
  Rooted<Value> parseError(cx, aRequest->mModuleScript->ParseError());
  LOG(
      ("ScriptLoadRequest (%p): FinishLoadingImportedModule: found parse "
       "error",
       aRequest));
  // Step 14.5. Perform FinishLoadingImportedModule(referrer, moduleRequest,
  //            payload, completion).
  FinishLoadingImportedModuleFailed(cx, payload, parseError);
  aRequest->ModuleErrored();
  aRequest->ClearImport();
}

void ModuleLoaderBase::Cancel(ModuleLoadRequest* aRequest) {
  if (aRequest->IsFinished()) {
    return;
  }

  aRequest->ScriptLoadRequest::Cancel();
  aRequest->mModuleScript = nullptr;

  if (aRequest->mPayload.isUndefined()) {
    return;
  }

  AutoJSAPI jsapi;
  if (!jsapi.Init(mGlobalObject)) {
    return;
  }
  JSContext* cx = jsapi.cx();

  Rooted<Value> payload(cx, aRequest->mPayload);
  Rooted<Value> error(cx, UndefinedValue());

  LOG(
      ("ScriptLoadRequest (%p): Canceled, calling "
       "FinishLoadingImportedModuleFailed",
       aRequest));

  FinishLoadingImportedModuleFailed(cx, payload, error);
  aRequest->ModuleErrored();
  aRequest->ClearImport();
}

class ModuleErroredRunnable : public MicroTaskRunnable {
 public:
  explicit ModuleErroredRunnable(ModuleLoadRequest* aRequest)
      : mRequest(aRequest) {}

  virtual void Run(AutoSlowOperation& aAso) override {
    mRequest->ModuleErrored();
  }

 private:
  RefPtr<ModuleLoadRequest> mRequest;
};

void ModuleLoaderBase::DispatchModuleErrored(ModuleLoadRequest* aRequest) {
  if (aRequest->HasScriptLoadContext() &&
      aRequest->GetScriptLoadContext()->mIsInline) {
    // https://html.spec.whatwg.org/#prepare-the-script-element
    // Step 32. If el does not have a src content attribute:
    //   2. "module".3.1
    //   Queue an element task on the networking task source given el to
    //   perform the following steps:
    CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
    RefPtr<ModuleErroredRunnable> runnable =
        new ModuleErroredRunnable(aRequest);
    context->DispatchToMicroTask(runnable.forget());
  } else {
    aRequest->ModuleErrored();
  }
}

nsresult ModuleLoaderBase::CreateModuleScript(ModuleLoadRequest* aRequest) {
  MOZ_ASSERT(!aRequest->mModuleScript);
  MOZ_ASSERT(aRequest->BaseURL());

  LOG(("ScriptLoadRequest (%p): Create module script", aRequest));

  AutoJSAPI jsapi;
  if (!jsapi.Init(mGlobalObject)) {
    return NS_ERROR_FAILURE;
  }

  nsresult rv;
  {
    JSContext* cx = jsapi.cx();
    Rooted<JSObject*> module(cx);

    CompileOptions options(cx);
    RootedScript introductionScript(cx);
    rv = mLoader->FillCompileOptionsForRequest(cx, aRequest, &options,
                                               &introductionScript);

    if (NS_SUCCEEDED(rv)) {
      Rooted<JSObject*> global(cx, mGlobalObject->GetGlobalJSObject());
      rv = CompileFetchedModule(cx, global, options, aRequest, &module);
    }

    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));

    if (module) {
      RootedScript moduleScript(cx, GetModuleScript(module));
      if (moduleScript) {
        RootedValue privateValue(cx);
        InstantiateOptions instantiateOptions(options);
        if (!UpdateDebugMetadata(cx, moduleScript, instantiateOptions,
                                 privateValue, nullptr, introductionScript,
                                 nullptr)) {
          return NS_ERROR_OUT_OF_MEMORY;
        }
      }
    }

    MOZ_ASSERT(aRequest->mLoadedScript->IsModuleScript());

    RefPtr<ModuleScript> moduleScript = new ModuleScript(aRequest->FetchInfo());

    aRequest->mModuleScript = moduleScript;

    moduleScript->SetForPreload(aRequest->mLoadContext->IsPreload());
    moduleScript->SetHadImportMap(HasImportMapRegistered());

    if (!module) {
      LOG(("ScriptLoadRequest (%p):   compilation failed (%d)", aRequest,
           unsigned(rv)));

      Rooted<Value> error(cx);
      if (!jsapi.HasException() || !jsapi.StealException(&error) ||
          error.isUndefined()) {
        aRequest->mModuleScript = nullptr;
        return NS_ERROR_FAILURE;
      }

      moduleScript->SetParseError(error);
      return NS_OK;
    }

    moduleScript->SetModuleRecord(module);

    if (IsCyclicModule(module)) {
      aRequest->FetchInfo()->AssociateWithModule(module);
    }
  }

  LOG(("ScriptLoadRequest (%p):   module script == %p ForPreload %d", aRequest,
       aRequest->mModuleScript.get(), aRequest->mModuleScript->ForPreload()));

  return rv;
}

nsresult ModuleLoaderBase::GetResolveFailureMessage(ResolveError aError,
                                                    const nsAString& aSpecifier,
                                                    nsAString& aResult) {
  AutoTArray<nsString, 1> errorParams;
  errorParams.AppendElement(aSpecifier);

  nsresult rv = nsContentUtils::FormatLocalizedString(
      PropertiesFile::DOM_PROPERTIES, ResolveErrorInfo::GetString(aError),
      errorParams, aResult);
  NS_ENSURE_SUCCESS(rv, rv);
  return NS_OK;
}

nsresult ModuleLoaderBase::HandleResolveFailure(
    JSContext* aCx, ScriptFetchInfo* aFetchInfo, const nsAString& aSpecifier,
    ResolveError aError, uint32_t aLineNumber,
    ColumnNumberOneOrigin aColumnNumber, MutableHandle<Value> aErrorOut) {
  Rooted<JSString*> filename(aCx);
  if (aFetchInfo) {
    nsAutoCString url;
    aFetchInfo->BaseURL()->GetAsciiSpec(url);
    filename = JS_NewStringCopyZ(aCx, url.get());
  } else {
    filename = JS_NewStringCopyZ(aCx, "(unknown)");
  }

  if (!filename) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  nsAutoString errorText;
  nsresult rv = GetResolveFailureMessage(aError, aSpecifier, errorText);
  NS_ENSURE_SUCCESS(rv, rv);

  Rooted<JSString*> string(aCx, JS_NewUCStringCopyZ(aCx, errorText.get()));
  if (!string) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  if (!CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, aLineNumber,
                   aColumnNumber, nullptr, string, NothingHandleValue,
                   aErrorOut)) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  return NS_OK;
}

ResolveResult ModuleLoaderBase::ResolveModuleSpecifier(
    ScriptFetchInfo* aFetchInfo, const nsAString& aSpecifier) {
  // Import Maps are not supported on workers/worklets.
  // See https://github.com/WICG/import-maps/issues/2
  MOZ_ASSERT_IF(!NS_IsMainThread(), mImportMap == nullptr);

  // Forward to the updated 'Resolve a module specifier' algorithm defined in
  // the Import Maps spec.
  return ImportMap::ResolveModuleSpecifier(mImportMap.get(), mLoader,
                                           aFetchInfo, aSpecifier);
}

ResolvedModuleSet* ModuleLoaderBase::GetResolvedModuleSet() {
  MOZ_ASSERT(ImportMap::IsMultipleImportMapsSupported());
  if (!mResolvedModuleSet) {
    mResolvedModuleSet = MakeUnique<ResolvedModuleSet>();
  }

  return mResolvedModuleSet.get();
}

static void AddToResolvedSet(ResolvedModuleSet* aSet,
                             UniquePtr<SpecifierResolutionRecord> aRecord) {
  MOZ_ASSERT(ImportMap::IsMultipleImportMapsSupported());
  auto ptr = aSet->lookupForAdd(aRecord);
  if (ptr) {
    return;
  }

  MOZ_ALWAYS_TRUE(aSet->add(ptr, std::move(aRecord)));
}

void ModuleLoaderBase::AddToPreloadedResolvedSet(
    ModuleLoadRequest* aRootRequest,
    UniquePtr<SpecifierResolutionRecord> aRecord) {
  MOZ_ASSERT(aRootRequest);
  MOZ_ASSERT(aRootRequest->mModuleScript);

  ResolvedModuleSet* set =
      aRootRequest->mModuleScript->GetPreloadedResolvedSet();
  MOZ_ASSERT(set);
  AddToResolvedSet(set, std::move(aRecord));
}

void ModuleLoaderBase::AddToGlobalResolvedSet(
    UniquePtr<SpecifierResolutionRecord> aRecord) {
  AddToResolvedSet(GetResolvedModuleSet(), std::move(aRecord));
}

static ModuleLoadRequest* GetPreloadRootModuleRequest(
    JS::Handle<JS::Value> aHostDefined) {
  MOZ_ASSERT(!aHostDefined.isUndefined());
  ModuleLoadRequest* parent =
      static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate());
  MOZ_ASSERT(parent);
  return parent->GetRootModule();
}

void ModuleLoaderBase::AddToResolvedModuleSet(
    UniquePtr<SpecifierResolutionRecord> aRecord,
    ScriptFetchInfo* aFetchInfo /* = nullptr */,
    Handle<Value> aHostDefined /* = UndefinedHandleValue */) {
  // 2. If global does not implement Window, then return.
  if (!mLoader->IsImportMapSupported()) {
    return;
  }

  bool isPreloadModule = aFetchInfo && aFetchInfo->IsForModuleScript() &&
                         aFetchInfo->IsForModulePreload();
  if (isPreloadModule) {
    RefPtr<ModuleLoadRequest> root = GetPreloadRootModuleRequest(aHostDefined);
    AddToPreloadedResolvedSet(root, std::move(aRecord));
    return;
  }

  // release the mResult from the record as it is not needed.
  nsCOMPtr<nsIURI> _ = aRecord->TakeResult();
  AddToGlobalResolvedSet(std::move(aRecord));
}

// This just flip the preload flag in the module script and ModuleObject.
void ModuleLoaderBase::ResetPreloadFlag(nsIURI* aURI) {
  MOZ_ASSERT(aURI);
  ModuleMapKey key(aURI, ModuleType::JavaScript);
  if (!IsModuleFetched(key)) {
    return;
  }

  RefPtr<ModuleScript> ms;
  MOZ_ALWAYS_TRUE(mFetchedModules.Get(key, getter_AddRefs(ms)));

  // This function is called from a preloaded module graph, but it may have a
  // module script from another preloaded module graph which has been turned
  // into a normal load, so we don't assert the module script is a preloaded
  // one.
  ms->SetForPreload(false);
}

// This is called when a preloaded module request is reused, so we need to move
// the resolved set into the global's set.
// Also, we need to flip the preload flag in the module script.
void ModuleLoaderBase::MovePreloadedSetToResolvedSet(
    ModuleLoadRequest* aRootRequest) {
  LOG(("ScriptLoadRequest (%p): MovePreloadedSetToResolvedSet", aRootRequest));
  MOZ_ASSERT(ImportMap::IsMultipleImportMapsSupported());
  MOZ_ASSERT(aRootRequest);

  // ScriptLoader has flipped the preload flag to false.
  MOZ_ASSERT(!aRootRequest->mLoadContext->IsPreload());

  ModuleScript* ms = aRootRequest->mModuleScript;
  if (!ms) {
    return;
  }

  ms->SetForPreload(false);
  if (!ms->HasPreloadedResolvedSet()) {
    return;
  }

  ResolvedModuleSet* set = ms->GetPreloadedResolvedSet();
  for (auto iter = set->modIter(); !iter.done(); iter.next()) {
    auto record = std::move(iter.getMutable());
    nsCOMPtr<nsIURI> uri = record->TakeResult();
    ResetPreloadFlag(uri);
    AddToGlobalResolvedSet(std::move(record));
  }

  set->clear();
  ms->ReleasePreloadedResolvedSet();
}

// If the module script is preloaded, reset the [[LoadedModules]]
//
void ModuleLoaderBase::ResetPreloadedModule(nsIURI* aURI) {
  MOZ_ASSERT(aURI);
  ModuleMapKey key(aURI, ModuleType::JavaScript);
  if (!IsModuleFetched(key)) {
    return;
  }

  RefPtr<ModuleScript> ms;
  MOZ_ALWAYS_TRUE(mFetchedModules.Get(key, getter_AddRefs(ms)));
  if (!ms->ForPreload()) {
    return;
  }

  LOG(
      ("ModuleLoaderBase::ResetPreloadedModule: module script (%p) reset "
       "preloaded info",
       ms.get()));
  ms->ResetPreload();
}

// This is called before registering an import map, so we clear the remaing
// mPreloads from the ScriptLoader
void ModuleLoaderBase::ClearPreloadedModuleGraph(
    ModuleLoadRequest* aRootRequest) {
  MOZ_ASSERT(ImportMap::IsMultipleImportMapsSupported());
  MOZ_ASSERT(aRootRequest);
  MOZ_ASSERT(aRootRequest->mLoadContext->IsPreload());
  LOG(("ScriptLoadRequest (%p): ClearPreloadedModuleGraph", aRootRequest));

  ModuleScript* ms = aRootRequest->mModuleScript;
  if (!ms) {
    return;
  }

  // If the preloaded request has a module script which has been used previously
  // in another preloaed module graph, which has been reused and is a normal
  // load now, we don't need to reset its data anymore.
  if (!ms->ForPreload()) {
    return;
  }

  ms->ResetPreload();

  if (!ms->HasPreloadedResolvedSet()) {
    return;
  }

  ResolvedModuleSet* set = ms->GetPreloadedResolvedSet();
  for (auto iter = set->iter(); !iter.done(); iter.next()) {
    nsCOMPtr<nsIURI> uri = iter.get()->TakeResult();
    MOZ_ASSERT(uri);
    ResetPreloadedModule(uri);
  }
  set->clear();
  ms->ReleasePreloadedResolvedSet();
}

void ModuleLoaderBase::StartFetchingModuleDependencies(
    ModuleLoadRequest* aRequest) {
  if (aRequest->IsCanceled()) {
    return;
  }

  MOZ_ASSERT(aRequest->mModuleScript);
  MOZ_ASSERT(!aRequest->mModuleScript->HasParseError());
  ModuleScript* moduleScript = aRequest->mModuleScript;
  MOZ_ASSERT(moduleScript->ModuleRecord());
  MOZ_ASSERT(aRequest->IsFetching() || aRequest->IsCompiling());
  MOZ_ASSERT(aRequest->IsTopLevel() || aRequest->IsDynamicImport());

  AutoJSAPI jsapi;
  if (NS_WARN_IF(!jsapi.Init(mGlobalObject))) {
    return;
  }
  JSContext* cx = jsapi.cx();

  Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());

  LOG(
      ("ScriptLoadRequest (%p): module record (%p) Start fetching module "
       "dependencies",
       aRequest, module.get()));

  // Wrap the request into a JS::Value, and AddRef() it.
  // The Release() will be called in the resolved/rejected handlers.
  Rooted<Value> hostDefinedVal(cx, PrivateValue(aRequest));
  aRequest->AddRef();

  bool result = false;

  // A microtask job is not executed if the global is being
  // destroyed. As a result, the promise returned by LoadRequestedModules may
  // neither resolve nor reject. To ensure module loading completes reliably in
  // chrome pages, we use the synchronous variant of LoadRequestedModules.
  bool isSync = aRequest->URI()->SchemeIs("chrome") ||
                aRequest->URI()->SchemeIs("resource");

  // TODO: Bug1973660: Use Promise version of LoadRequestedModules on Workers.
  if (aRequest->HasScriptLoadContext() && !isSync) {
    Rooted<JSFunction*> onResolved(
        cx, js::NewFunctionWithReserved(cx, OnLoadRequestedModulesResolved,
                                        OnLoadRequestedModulesResolvedNumArgs,
                                        0"resolved"));
    if (!onResolved) {
      JS_ReportOutOfMemory(cx);
      return;
    }

    RootedFunction onRejected(
        cx, js::NewFunctionWithReserved(cx, OnLoadRequestedModulesRejected,
                                        OnLoadRequestedModulesRejectedNumArgs,
                                        0"rejected"));
    if (!onRejected) {
      JS_ReportOutOfMemory(cx);
      return;
    }

    RootedObject resolveFuncObj(cx, JS_GetFunctionObject(onResolved));
    js::SetFunctionNativeReserved(resolveFuncObj, LoadReactionHostDefinedSlot,
                                  hostDefinedVal);

    RootedObject rejectFuncObj(cx, JS_GetFunctionObject(onRejected));
    js::SetFunctionNativeReserved(rejectFuncObj, LoadReactionHostDefinedSlot,
                                  hostDefinedVal);

    Rooted<JSObject*> loadPromise(cx);
    result = LoadRequestedModules(cx, module, hostDefinedVal, &loadPromise);
    AddPromiseReactions(cx, loadPromise, resolveFuncObj, rejectFuncObj);
  } else {
    result = LoadRequestedModules(cx, module, hostDefinedVal,
                                  OnLoadRequestedModulesResolved,
                                  OnLoadRequestedModulesRejected);
  }

  if (!result) {
    LOG(("ScriptLoadRequest (%p): LoadRequestedModules failed", aRequest));
    OnLoadRequestedModulesRejected(cx, aRequest, UndefinedHandleValue);
  }
}

// static
bool ModuleLoaderBase::OnLoadRequestedModulesResolved(JSContext* aCx,
                                                      unsigned aArgc,
                                                      Value* aVp) {
  CallArgs args = CallArgsFromVp(aArgc, aVp);
  Rooted<Value> hostDefined(aCx);
  hostDefined = js::GetFunctionNativeReserved(&args.callee(),
                                              LoadReactionHostDefinedSlot);
  return OnLoadRequestedModulesResolved(aCx, hostDefined);
}

// static
bool ModuleLoaderBase::OnLoadRequestedModulesResolved(
    JSContext* aCx, Handle<Value> aHostDefined) {
  auto* request = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate());
  MOZ_ASSERT(request);
  return OnLoadRequestedModulesResolved(request);
}

// static
bool ModuleLoaderBase::OnLoadRequestedModulesResolved(
    ModuleLoadRequest* aRequest) {
  LOG(("ScriptLoadRequest (%p): LoadRequestedModules resolved", aRequest));
  if (!aRequest->IsCanceled()) {
    aRequest->SetReady();
    aRequest->LoadFinished();
  }

  // Decrease the reference 'AddRef'ed when converting the hostDefined.
  aRequest->Release();
  return true;
}

// static
bool ModuleLoaderBase::OnLoadRequestedModulesRejected(JSContext* aCx,
                                                      unsigned aArgc,
                                                      Value* aVp) {
  CallArgs args = CallArgsFromVp(aArgc, aVp);
  Rooted<Value> error(aCx, args.get(OnLoadRequestedModulesRejectedErrorArg));
  Rooted<Value> hostDefined(aCx);
  hostDefined = js::GetFunctionNativeReserved(&args.callee(),
                                              LoadReactionHostDefinedSlot);
  return OnLoadRequestedModulesRejected(aCx, hostDefined, error);
}

// static
bool ModuleLoaderBase::OnLoadRequestedModulesRejected(
    JSContext* aCx, Handle<Value> aHostDefined, Handle<Value> aError) {
  auto* request = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate());
  MOZ_ASSERT(request);
  return OnLoadRequestedModulesRejected(aCx, request, aError);
}

// static
bool ModuleLoaderBase::OnLoadRequestedModulesRejected(
    JSContext* aCx, ModuleLoadRequest* aRequest, Handle<Value> error) {
  ModuleScript* moduleScript = aRequest->mModuleScript;

  if (aRequest->IsCanceled()) {
    aRequest->Release();
    return true;
  }

  // TODO, Bug 1990416: Align the loading descendants behavior of dynamic import
  if (aRequest->IsDynamicImport()) {
    LOG(
        ("ScriptLoadRequest (%p): LoadRequestedModules rejected for dynamic "
         "import",
         aRequest));
    // https://tc39.es/ecma262/#sec-ContinueDynamicImport
    // Step 4.a. Perform ! Call(promiseCapability.[[Reject]], undefined,
    //   « reason »).
    Rooted<Value> payload(aCx, aRequest->mPayload);
    if (!error.isUndefined()) {
      FinishLoadingImportedModuleFailed(aCx, payload, error);
    } else {
      nsAutoCString url;
      aRequest->URI()->GetSpec(url);
      JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr,
                                JSMSG_DYNAMIC_IMPORT_FAILED, url.get());
      FinishLoadingImportedModuleFailedWithPendingException(aCx, payload);
    }
    aRequest->SetErroredLoadingImports();
  } else if (moduleScript && !error.isUndefined()) {
    LOG(
        ("ScriptLoadRequest (%p): LoadRequestedModules rejected: set error to "
         "rethrow",
         aRequest));
    // https://html.spec.whatwg.org/#fetch-the-descendants-of-and-link-a-module-script
    // Step 7. Upon rejection of loadingPromise, run the following
    //         steps:
    // Step 7.1. If state.[[ErrorToRethrow]] is not null, set moduleScript's
    //           error to rethrow to state.[[ErrorToRethrow]] and run
    //           onComplete given moduleScript.
    moduleScript->SetErrorToRethrow(error);
  } else {
    LOG(
        ("ScriptLoadRequest (%p): LoadRequestedModules rejected: set module "
         "script to null",
         aRequest));
    // Step 7.2. Otherwise, run onComplete given null.
    aRequest->mModuleScript = nullptr;
  }

  aRequest->ModuleErrored();

  // Decrease the reference 'AddRef'ed when converting the hostDefined.
  aRequest->Release();
  return true;
}

bool ModuleLoaderBase::GetImportMapSRI(
    nsIURI* aURI, nsIURI* aSourceURI, nsIConsoleReportCollector* aReporter,
    mozilla::dom::SRIMetadata* aMetadataOut) {
  MOZ_ASSERT(aMetadataOut->IsEmpty());
  MOZ_ASSERT(aURI);

  if (!HasImportMapRegistered()) {
    return false;
  }

  mozilla::Maybe<nsString> entry =
      ImportMap::LookupIntegrity(mImportMap.get(), aURI);
  if (entry.isNothing()) {
    return false;
  }

  mozilla::dom::SRICheck::IntegrityMetadata(
      *entry, aSourceURI->GetSpecOrDefault(), aReporter, aMetadataOut);
  return true;
}

void ModuleLoaderBase::AppendDynamicImport(ModuleLoadRequest* aRequest) {
  MOZ_ASSERT(aRequest->mLoader == this);
  mDynamicImportRequests.AppendElement(aRequest);
}

ModuleLoaderBase::ModuleLoaderBase(ScriptLoaderInterface* aLoader,
                                   nsIGlobalObject* aGlobalObject)
    : mGlobalObject(aGlobalObject), mLoader(aLoader) {
  MOZ_ASSERT(mGlobalObject);
  MOZ_ASSERT(mLoader);

  EnsureModuleHooksInitialized();
}

ModuleLoaderBase::~ModuleLoaderBase() {
  mDynamicImportRequests.CancelRequestsAndClear();

  LOG(("ModuleLoaderBase::~ModuleLoaderBase %p"this));
}

void ModuleLoaderBase::CancelFetchingModules() {
  for (const auto& entry : mFetchingModules) {
    RefPtr<LoadingRequest> loadingRequest = entry.GetData();
    loadingRequest->mRequest->Cancel();

    for (const auto& request : loadingRequest->mWaiting) {
      request->Cancel();
    }
  }

  // We don't clear mFetchingModules here, as the fetching requests might arrive
  // after the global is still shutting down.
}

void ModuleLoaderBase::Shutdown() {
  CancelAndClearDynamicImports();

  for (const auto& entry : mFetchingModules) {
    RefPtr<LoadingRequest> loadingRequest(entry.GetData());
    if (loadingRequest) {
      ResumeWaitingRequests(loadingRequest, false);
    }
  }

  for (const auto& entry : mFetchedModules) {
    if (entry.GetData()) {
      entry.GetData()->Shutdown();
    }
  }

  mFetchingModules.Clear();
  mFetchedModules.Clear();
  mGlobalObject = nullptr;
  mLoader = nullptr;
}

bool ModuleLoaderBase::HasFetchingModules() const {
  return !mFetchingModules.IsEmpty();
}

bool ModuleLoaderBase::HasPendingDynamicImports() const {
  return !mDynamicImportRequests.isEmpty();
}

void ModuleLoaderBase::CancelDynamicImport(ModuleLoadRequest* aRequest,
                                           nsresult aResult) {
  // aRequest may have already been unlinked by CC.
  MOZ_ASSERT(aRequest->mLoader == this || !aRequest->mLoader);

  RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
  if (!aRequest->IsCanceled()) {
    // If the ClearDynamicImport() has been called, then it should have been
    // removed from mDynamicImportRequests as well.
    MOZ_ASSERT(!aRequest->mPayload.isUndefined());
    aRequest->Cancel();
  }
}

void ModuleLoaderBase::RemoveDynamicImport(ModuleLoadRequest* aRequest) {
  MOZ_ASSERT(aRequest->IsDynamicImport());
  mDynamicImportRequests.Remove(aRequest);
}

#ifdef DEBUG
bool ModuleLoaderBase::HasDynamicImport(
    const ModuleLoadRequest* aRequest) const {
  MOZ_ASSERT(aRequest->mLoader == this);
  return mDynamicImportRequests.Contains(
      const_cast<ModuleLoadRequest*>(aRequest));
}
#endif

bool ModuleLoaderBase::InstantiateModuleGraph(ModuleLoadRequest* aRequest) {
  // Instantiate a top-level module and record any error.

  MOZ_ASSERT(aRequest);
  MOZ_ASSERT(aRequest->mLoader == this);
  MOZ_ASSERT(aRequest->IsTopLevel());

  LOG(("ScriptLoadRequest (%p): Instantiate module graph", aRequest));

  AUTO_PROFILER_LABEL("ModuleLoaderBase::InstantiateModuleGraph", JS);

  ModuleScript* moduleScript = aRequest->mModuleScript;
  MOZ_ASSERT(moduleScript);

  MOZ_ASSERT(!moduleScript->HasParseError());
  MOZ_ASSERT(moduleScript->ModuleRecord());

  AutoJSAPI jsapi;
  if (NS_WARN_IF(!jsapi.Init(mGlobalObject))) {
    return false;
  }

  JSContext* cx = jsapi.cx();
  Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());
  if (!xpc::Scriptability::AllowedIfExists(module)) {
    return true;
  }

  if (!ModuleLink(jsapi.cx(), module)) {
    LOG(("ScriptLoadRequest (%p): Instantiate failed", aRequest));
    MOZ_ASSERT(jsapi.HasException());
    RootedValue exception(jsapi.cx());
    if (!jsapi.StealException(&exception)) {
      return false;
    }
    MOZ_ASSERT(!exception.isUndefined());
    moduleScript->SetErrorToRethrow(exception);
  }

  return true;
}

void ModuleLoaderBase::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
  AutoJSAPI jsapi;
  if (!jsapi.Init(GetGlobalObject())) {
    return;
  }
  JSContext* cx = jsapi.cx();
  MOZ_ASSERT(aRequest->IsDynamicImport());

  if (aRequest->IsErrored()) {
    LOG(("ScriptLoadRequest (%p): ProcessDynamicImport, request has an error",
         aRequest));
    // The error is already processed in OnLoadRequestedModulesRejected.
    return;
  }

  LOG(("ScriptLoadRequest (%p): ProcessDynamicImport", aRequest));
  FinishLoadingImportedModule(cx, aRequest);

  // TODO: Implement caching for wasm modules (Bug 1998240).
  if (!aRequest->IsWasmBytes()) {
    (void)mLoader->MaybePrepareModuleForDiskCacheAfterExecute(aRequest, NS_OK);
  }

  mLoader->MaybeUpdateDiskCache();
}

nsresult ModuleLoaderBase::EvaluateModule(ModuleLoadRequest* aRequest) {
  MOZ_ASSERT(aRequest->mLoader == this);

  mozilla::nsAutoMicroTask mt;
  mozilla::dom::AutoEntryScript aes(mGlobalObject, "EvaluateModule",
                                    NS_IsMainThread());

  // We would like to handle any evaluation errors synchronously for service
  // workers immediately such that if we are performing service worker
  // registration we can fail it right away rather than having it fail later on
  // an event loop turn which might be too late and the registration process
  // would have succeeded by then.
  return EvaluateModuleInContext(
      aes.cx(), aRequest,
      IsForServiceWorker() ? ThrowModuleErrorsSync : ReportModuleErrorsAsync);
}

nsresult ModuleLoaderBase::EvaluateModuleInContext(
    JSContext* aCx, ModuleLoadRequest* aRequest,
    ModuleErrorBehaviour errorBehaviour) {
  MOZ_ASSERT(aRequest->mLoader == this);
  MOZ_ASSERT_IF(!mGlobalObject->GetModuleLoader(aCx)->IsOverridden(),
                mGlobalObject->GetModuleLoader(aCx) == this);
  MOZ_ASSERT_IF(mGlobalObject->GetModuleLoader(aCx)->IsOverridden(),
                mGlobalObject->GetModuleLoader(aCx)->IsOverriddenBy(this));
  MOZ_ASSERT(!aRequest->IsDynamicImport());

  AUTO_PROFILER_LABEL("ModuleLoaderBase::EvaluateModule", JS);

  nsAutoCString profilerLabelString;
  if (aRequest->HasScriptLoadContext()) {
    aRequest->GetScriptLoadContext()->GetProfilerLabel(profilerLabelString);
  }

  LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
  AUTO_PROFILER_MARKER_TEXT("ModuleEvaluation", JS,
                            MarkerInnerWindowIdFromJSContext(aCx),
                            profilerLabelString);

  MOZ_ASSERT(aRequest->mModuleScript);
  MOZ_ASSERT_IF(aRequest->HasScriptLoadContext(),
                !aRequest->GetScriptLoadContext()->mCompileOrDecodeTask);

  ModuleScript* moduleScript = aRequest->mModuleScript;
  if (moduleScript->HasErrorToRethrow()) {
    LOG(("ScriptLoadRequest (%p):   module has error to rethrow", aRequest));
    Rooted<Value> error(aCx, moduleScript->ErrorToRethrow());
    JS_SetPendingException(aCx, error);
    return NS_OK;
  }

  Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
  MOZ_ASSERT(module);
  MOZ_ASSERT(CurrentGlobalOrNull(aCx) == GetNonCCWObjectGlobal(module));

  if (!xpc::Scriptability::AllowedIfExists(module)) {
    return NS_OK;
  }

  if (aRequest->HasScriptLoadContext()) {
    TRACE_FOR_TEST(aRequest, "evaluate:module");
  }

  Rooted<Value> rval(aCx);

  bool ok = ModuleEvaluate(aCx, module, &rval);

  // ModuleEvaluate will usually set a pending exception if it returns false,
  // unless the user cancels execution.
  MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(aCx));

  nsresult rv = NS_OK;
  if (!ok || IsModuleEvaluationAborted(aRequest)) {
    LOG(("ScriptLoadRequest (%p):   evaluation failed", aRequest));
    // For a dynamic import, the promise is rejected. Otherwise an error is
    // reported by AutoEntryScript.
    rv = NS_ERROR_ABORT;
  }

  // ModuleEvaluate returns a promise unless the user cancels the execution in
  // which case rval will be undefined. We should treat it as a failed
  // evaluation, and reject appropriately.
  Rooted<JSObject*> evaluationPromise(aCx);
  if (rval.isObject()) {
    evaluationPromise.set(&rval.toObject());
  }

  // If the promise is rejected, the value is unwrapped from the promise value.
  if (!ThrowOnModuleEvaluationFailure(aCx, evaluationPromise, errorBehaviour)) {
    LOG(("ScriptLoadRequest (%p):   evaluation failed on throw", aRequest));

    if (IsForServiceWorker()) {
      // If this module eval is being done for service workers, then we would
      // like to throw/return right from here, such that if we are in
      // registration phase, we can fail it synchronously right away.
      return NS_ERROR_ABORT;
    }
  }

  // TODO: Implement caching for wasm modules (Bug 1998240).
  if (!aRequest->IsWasmBytes()) {
    rv = mLoader->MaybePrepareModuleForDiskCacheAfterExecute(aRequest, NS_OK);
  }

  mLoader->MaybeUpdateDiskCache();

  return rv;
}

void ModuleLoaderBase::CancelAndClearDynamicImports() {
  while (ScriptLoadRequest* req = mDynamicImportRequests.getFirst()) {
    // This also removes the request from the list.
    CancelDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
  }
}

UniquePtr<ImportMap> ModuleLoaderBase::ParseImportMap(
    ScriptLoadRequest* aRequest) {
  AutoJSAPI jsapi;
  if (!jsapi.Init(GetGlobalObject())) {
    return nullptr;
  }

  MOZ_ASSERT(aRequest->IsFetchedAsTextSource());
  MaybeSourceText maybeSource;
  nsresult rv = aRequest->GetScriptSource(jsapi.cx(), &maybeSource,
                                          aRequest->mLoadContext.get());
  if (NS_FAILED(rv)) {
    return nullptr;
  }

  SourceText<char16_t>& text = maybeSource.ref<SourceText<char16_t>>();
  ReportWarningHelper warning{mLoader, aRequest};

  // https://html.spec.whatwg.org/multipage/webappapis.html#create-an-import-map-parse-result
  // Step 2. Parse an import map string given input and baseURL, catching any
  // exceptions. If this threw an exception, then set result's error to rethrow
  // to that exception. Otherwise, set result's import map to the return value.
  //
  // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
  // Step 1. If result's error to rethrow is not null, then report the exception
  // given by result's error to rethrow and return.
  //
  // Impl note: We didn't implement 'Import map parse result' from the spec,
  // https://html.spec.whatwg.org/multipage/webappapis.html#import-map-parse-result
  // As the struct has another item called 'error to rethow' to store the
  // exception thrown during parsing import-maps, and report that exception
  // while registering an import map. Currently only inline import-maps are
  // supported, therefore parsing and registering import-maps will be executed
  // consecutively. To simplify the implementation, we didn't create the 'error
  // to rethow' item and report the exception immediately(done in ~AutoJSAPI).
  return ImportMap::ParseString(jsapi.cx(), text, aRequest->BaseURL(), warning);
}

void ModuleLoaderBase::RegisterImportMap(UniquePtr<ImportMap> aImportMap,
                                         ScriptLoadRequest* aRequest) {
  LOG(("RegisterImportMap"));

  // Check for aImportMap is done in ScriptLoader.
  MOZ_ASSERT(aImportMap);

  // https://html.spec.whatwg.org/multipage/webappapis.html#register-an-import-map
  // The step 1(report the exception if there's an error) is done in
  // ParseImportMap.

  bool multiImportMapsEnabled = ImportMap::IsMultipleImportMapsSupported();
  if (!multiImportMapsEnabled) {
    MOZ_ASSERT(!mImportMap);
    mImportMap = std::move(aImportMap);
  } else {
    ReportWarningHelper warning{mLoader, aRequest};

    // Step 2. Merge existing and new import maps, given global and result's
    //         import map.
    ImportMap::Merge(this, std::move(aImportMap), warning);
    MOZ_ASSERT(mImportMap);
  }

  // If speculative preloading is currently fetching any modules then
  // cancel their requests and remove them from the map.
  //
  // The cancelled requests will still complete later so we have to check this
  // in SetModuleFetchFinishedAndGetWaitingRequests.
  mFetchingModules.RemoveIf([](auto& iter) {
    LoadingRequest* loadingRequest = iter.Data();
    bool isPreload = loadingRequest->mRequest->mLoadContext->IsPreload();
    if (!isPreload) {
      return false;
    }

    loadingRequest->mRequest->Cancel();
    for (const auto& request : loadingRequest->mWaiting) {
      MOZ_DIAGNOSTIC_ASSERT(request->mLoadContext->IsPreload());
      request->Cancel();
    }
    return true;
  });

  // When the pref of multiple import maps is enabled, for preloaded and fetched
  // module scripts, the module graphs have been cleared in
  // ClearPreloadedModuleGraph.
  if (!multiImportMapsEnabled) {
    // If speculative preloading has added modules to the module map, remove
    // them.
    for (const auto& entry : mFetchedModules) {
      ModuleScript* script = entry.GetData();
      if (script) {
        MOZ_DIAGNOSTIC_ASSERT(
            script->ForPreload(),
            "Non-preload module loads should block import maps");
        MOZ_DIAGNOSTIC_ASSERT(!script->HadImportMap(),
                              "Only one import map can be registered");
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
        if (JSObject* module = script->ModuleRecord()) {
          MOZ_DIAGNOSTIC_ASSERT(!ModuleIsLinked(module));
        }
#endif
        script->Shutdown();
      }
    }
    mFetchedModules.Clear();
  }
}

void ModuleLoaderBase::CopyModulesTo(ModuleLoaderBase* aDest) {
  MOZ_ASSERT(aDest->mFetchingModules.IsEmpty());
  MOZ_ASSERT(aDest->mFetchedModules.IsEmpty());
  MOZ_ASSERT(mFetchingModules.IsEmpty());

  for (const auto& entry : mFetchedModules) {
    RefPtr<ModuleScript> moduleScript = entry.GetData();

    // NOTE: moduleScript can be nullptr for modules that fails to import.
    //       Copy them too, so that we don't import them again.
    aDest->mFetchedModules.InsertOrUpdate(entry, moduleScript);
  }
}

void ModuleLoaderBase::MoveModulesTo(ModuleLoaderBase* aDest) {
  MOZ_ASSERT(mFetchingModules.IsEmpty());
  MOZ_ASSERT(aDest->mFetchingModules.IsEmpty());

  for (const auto& entry : mFetchedModules) {
    RefPtr<ModuleScript> moduleScript = entry.GetData();

#ifdef DEBUG
    if (auto existingEntry = aDest->mFetchedModules.Lookup(entry)) {
      MOZ_ASSERT(moduleScript == existingEntry.Data());
    }
#endif

    aDest->mFetchedModules.InsertOrUpdate(entry, moduleScript);
  }

  mFetchedModules.Clear();
}

#undef LOG
#undef LOG_ENABLED

}  // namespace JS::loader

Messung V0.5 in Prozent
C=88 H=95 G=91

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet am  2026-08-22) ¤

*© Formatika GbR, Deutschland






Normalansicht

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

letze Version des Elbe Quellennavigators


Jenseits des Üblichen ....
    

Besucher

Besucher

Statistik
#Sources=141584
#Domains=752002