/* 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 "ModuleLoader.h"

#include <type_traits>

#include "GeckoProfiler.h"
#include "ScriptLoader.h"
#include "js/CompileOptions.h"  // JS::CompileOptions, JS::InstantiateOptions
#include "js/ContextOptions.h"  // JS::ContextOptionsRef
#include "js/MemoryFunctions.h"
#include "js/Modules.h"  // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
#include "js/PropertyAndElement.h"  // JS_DefineProperty
#include "js/Realm.h"
#include "js/SourceText.h"
#include "js/experimental/JSStencil.h"  // JS::Stencil, JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/loader/LoadedScript.h"
#include "js/loader/ModuleLoadRequest.h"
#include "js/loader/ModuleLoaderBase.h"
#include "js/loader/ScriptLoadRequest.h"
#include "jsapi.h"
#include "mozilla/Assertions.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/Maybe.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/dom/AutoEntryScript.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/RequestBinding.h"
#include "nsContentSecurityManager.h"
#include "nsError.h"
#include "nsIContent.h"
#include "nsIPrincipal.h"
#include "nsJSUtils.h"
#include "xpcpublic.h"

using JS::SourceText;
using namespace JS::loader;

namespace mozilla::dom {

#undef LOG
#define LOG(args) \
  MOZ_LOG(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug, args)

#define LOG_ENABLED() \
  MOZ_LOG_TEST(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug)

//////////////////////////////////////////////////////////////
// DOM module loader
//////////////////////////////////////////////////////////////

ModuleLoader::ModuleLoader(ScriptLoader* aLoader,
                           nsIGlobalObject* aGlobalObject, Kind aKind)
    : ModuleLoaderBase(aLoader, aGlobalObject), mKind(aKind) {}

ScriptLoader* ModuleLoader::GetScriptLoader() {
  return static_cast<ScriptLoader*>(mLoader.get());
}

bool ModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest, nsresult* aRvOut) {
  if (!GetScriptLoader()->GetDocument()) {
    *aRvOut = NS_ERROR_NULL_POINTER;
    return false;
  }

  nsCOMPtr<nsIPrincipal> principal = aRequest->TriggeringPrincipal();
  if (BasePrincipal::Cast(principal)->ContentScriptAddonPolicy()) {
    // To prevent dynamic code execution, content scripts can only
    // load moz-extension URLs.
    if (!aRequest->URI()->SchemeIs("moz-extension")) {
      *aRvOut = NS_ERROR_DOM_WEBEXT_CONTENT_SCRIPT_URI;
      return false;
    }
  } else {
    // If this document is sandboxed without 'allow-scripts', abort.
    if (GetScriptLoader()->GetDocument()->HasScriptsBlockedBySandbox()) {
      *aRvOut = NS_ERROR_CONTENT_BLOCKED;
      return false;
    }
  }

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

  return true;
}

nsresult ModuleLoader::StartFetch(ModuleLoadRequest* aRequest) {
  if (aRequest->IsRetrievedFromMemoryCache()) {
    GetScriptLoader()->EmulateNetworkEvents(aRequest, Nothing());
    SetModuleFetchStarted(aRequest);
    return aRequest->OnFetchComplete(NS_OK);
  }

  // According to the spec, module scripts have different behaviour to classic
  // scripts and always use CORS. Only exception: Non linkable about: pages
  // which load local module scripts.
  bool isAboutPageLoadingChromeURI = ScriptLoader::IsAboutPageLoadingChromeURI(
      aRequest, GetScriptLoader()->GetDocument());

  nsContentSecurityManager::CORSSecurityMapping corsMapping =
      isAboutPageLoadingChromeURI
          ? nsContentSecurityManager::CORSSecurityMapping::DISABLE_CORS_CHECKS
          : nsContentSecurityManager::CORSSecurityMapping::REQUIRE_CORS_CHECKS;

  nsSecurityFlags securityFlags =
      nsContentSecurityManager::ComputeSecurityFlags(aRequest->CORSMode(),
                                                     corsMapping);

  securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;

  // Delegate Shared Behavior to base ScriptLoader
  //
  // aCharsetForPreload is passed as Nothing() because this is not a preload
  // and `StartLoadInternal` is able to find the charset by using `aRequest`
  // for this case.
  nsresult rv = GetScriptLoader()->StartLoadInternal(
      aRequest, securityFlags, Nothing() /* aCharsetForPreload */);
  NS_ENSURE_SUCCESS(rv, rv);

  // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-an-import()-module-script-graph
  // Step 1. Disallow further import maps given settings object.
  if (!aRequest->GetScriptLoadContext()->IsPreload() &&
      !StaticPrefs::dom_multiple_import_maps_enabled()) {
    LOG(("ScriptLoadRequest (%p): Disallow further import maps.", aRequest));
    DisallowImportMaps();
  }

  LOG(("ScriptLoadRequest (%p): Start fetching module", aRequest));

  return NS_OK;
}

void ModuleLoader::AsyncExecuteInlineModule(ModuleLoadRequest* aRequest) {
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
      mozilla::NewRunnableMethod<RefPtr<ModuleLoadRequest>>(
          "ModuleLoader::ExecuteInlineModule", this,
          &ModuleLoader::ExecuteInlineModule, aRequest)));
}

void ModuleLoader::ExecuteInlineModule(ModuleLoadRequest* aRequest) {
  MOZ_ASSERT(aRequest->IsFinished());
  MOZ_ASSERT(aRequest->IsTopLevel());
  MOZ_ASSERT(aRequest->GetScriptLoadContext()->mIsInline);

  if (aRequest->GetScriptLoadContext()->GetParserCreated() == NOT_FROM_PARSER) {
    GetScriptLoader()->RunScriptWhenSafe(aRequest);
  } else {
    GetScriptLoader()->MaybeMoveToLoadedList(aRequest);
    GetScriptLoader()->ProcessPendingRequests();
  }

  aRequest->GetScriptLoadContext()->MaybeUnblockOnload();
}

void ModuleLoader::OnModuleLoadComplete(ModuleLoadRequest* aRequest) {
  MOZ_ASSERT(aRequest->IsFinished());

  if (aRequest->IsTopLevel() || aRequest->IsDynamicImport()) {
    if (aRequest->GetScriptLoadContext()->mIsInline &&
        aRequest->GetScriptLoadContext()->GetParserCreated() ==
            NOT_FROM_PARSER) {
      // https://html.spec.whatwg.org/#prepare-the-script-element
      // Step 32.2.
      //    type: "module":
      //    3.1. Queue an element task on the networking task source given
      //         el to perform the following steps:
      //        1. Mark as ready el given result.
      //
      // Step 33. If ... el's type is "module":
      //    ...
      //    3. Otherwise, if el is not parser-inserted:
      //      3. Set el's steps to run when the result is ready to the
      //         following:
      //        ...
      //        2.1. Execute the script element scripts[0].
      AsyncExecuteInlineModule(aRequest);
      return;
    } else if (aRequest->GetScriptLoadContext()->mIsInline &&
               aRequest->GetScriptLoadContext()->GetParserCreated() !=
                   NOT_FROM_PARSER &&
               !nsContentUtils::IsSafeToRunScript()) {
      // Avoid giving inline async module scripts that don't have
      // external dependencies a guaranteed execution time relative
      // to the HTML parse. That is, deliberately avoid guaranteeing
      // that the script would always observe a DOM shape where the
      // parser has not added further elements to the DOM.
      // (If `nsContentUtils::IsSafeToRunScript()` returns `true`,
      // we come here synchronously from the parser. If it returns
      // `false` we come here from an external dependency completing
      // its fetch, in which case we already are at an unspecific
      // point relative to the parse.)
      AsyncExecuteInlineModule(aRequest);
      return;
    } else {
      GetScriptLoader()->MaybeMoveToLoadedList(aRequest);
      GetScriptLoader()->ProcessPendingRequestsAsync();
    }
  }

  aRequest->GetScriptLoadContext()->MaybeUnblockOnload();
}

nsresult ModuleLoader::CompileFetchedModule(
    JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::CompileOptions& aOptions,
    ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleOut) {
  if (!nsJSUtils::IsScriptable(aGlobal)) {
    return NS_ERROR_FAILURE;
  }

  switch (aRequest->mModuleType) {
    case JS::ModuleType::Unknown:
      MOZ_CRASH("Unexpected module type");
    case JS::ModuleType::JavaScriptOrWasm:
      return CompileJavaScriptOrWasmModule(aCx, aOptions, aRequest, aModuleOut);
    case JS::ModuleType::JSON:
      return CompileJsonModule(aCx, aOptions, aRequest, aModuleOut);
    case JS::ModuleType::CSS:
      return CompileCssModule(aCx, aOptions, aRequest, aModuleOut);
    case JS::ModuleType::Text:
      return CreateTextModule(aCx, aOptions, aRequest, aModuleOut);
    case JS::ModuleType::Bytes:
      MOZ_CRASH("Unexpected module type");
  }

  MOZ_CRASH("Unhandled module type");
}

nsresult ModuleLoader::CompileJavaScriptOrWasmModule(
    JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
    JS::MutableHandle<JSObject*> aModuleOut) {
  GetScriptLoader()->CalculateCacheFlag(aRequest);

#ifdef NIGHTLY_BUILD
  if (aRequest->HasWasmMimeTypeEssence()) {
    MOZ_ASSERT(aRequest->IsWasmBytes());
    JS::Rooted<JSObject*> moduleReq(aCx, aRequest->mModuleRequestObj);
    JSObject* wasmModule;
    if (moduleReq && JS::ModuleRequestIsSourcePhase(aCx, moduleReq)) {
      wasmModule =
          JS::CompileWasmModuleAsSource(aCx, aOptions, aRequest->WasmBytes());
    } else {
      wasmModule = JS::CompileWasmModule(aCx, aOptions, aRequest->WasmBytes());
    }
    if (!wasmModule) {
      return NS_ERROR_FAILURE;
    }

    aModuleOut.set(wasmModule);
    return NS_OK;
  }
#endif
  MOZ_ASSERT(!aRequest->IsWasmBytes());

  if (aRequest->IsRetrievedFromMemoryCache()) {
    JS::InstantiateOptions instantiateOptions(aOptions);
    RefPtr<JS::Stencil> stencil = aRequest->GetStencil();
    aModuleOut.set(
        JS::InstantiateModuleStencil(aCx, instantiateOptions, stencil));
    if (!aModuleOut) {
      return NS_ERROR_FAILURE;
    }

    if (!GetScriptLoader()->StartCollectingDelazifications(aCx, aModuleOut,
                                                           stencil)) {
      return NS_ERROR_FAILURE;
    }

    return NS_OK;
  }

  if (aRequest->GetScriptLoadContext()->mWasCompiledOMT) {
    JS::InstantiationStorage storage;
    RefPtr<JS::Stencil> stencil =
        aRequest->GetScriptLoadContext()->StealOffThreadResult(aCx, &storage);
    if (!stencil) {
      return NS_ERROR_FAILURE;
    }

    aRequest->SetStencil(stencil);

    JS::InstantiateOptions instantiateOptions(aOptions);
    aModuleOut.set(JS::InstantiateModuleStencil(aCx, instantiateOptions,
                                                stencil, &storage));
    if (!aModuleOut) {
      return NS_ERROR_FAILURE;
    }

    if (aRequest->PassedConditionForEitherCache()) {
      if (!GetScriptLoader()->StartCollectingDelazifications(aCx, aModuleOut,
                                                             stencil)) {
        return NS_ERROR_FAILURE;
      }
    }

    GetScriptLoader()->TryCacheRequest(aRequest);

    return NS_OK;
  }

  RefPtr<JS::Stencil> stencil;
  if (aRequest->IsFetchedAsTextSource()) {
    MaybeSourceText maybeSource;
    nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
                                            aRequest->mLoadContext.get());
    NS_ENSURE_SUCCESS(rv, rv);

    auto compile = [&](auto& source) {
      return JS::CompileModuleScriptToStencil(aCx, aOptions, source);
    };
    stencil = maybeSource.mapNonEmpty(compile);
  } else {
    MOZ_ASSERT(aRequest->IsRetrievedAsSerializedStencil());
    JS::DecodeOptions decodeOptions(aOptions);
    if (!GetScriptLoader()->UsesMemoryCache()) {
      decodeOptions.borrowBuffer = true;
    }

    JS::TranscodeRange range = aRequest->SerializedStencil();
    JS::TranscodeResult tr =
        JS::DecodeStencil(aCx, decodeOptions, range, getter_AddRefs(stencil));
    if (tr != JS::TranscodeResult::Ok) {
      return NS_ERROR_DOM_JS_DECODING_ERROR;
    }
  }

  if (!stencil) {
    return NS_ERROR_FAILURE;
  }

  aRequest->SetStencil(stencil);

  JS::InstantiateOptions instantiateOptions(aOptions);
  aModuleOut.set(
      JS::InstantiateModuleStencil(aCx, instantiateOptions, stencil));
  if (!aModuleOut) {
    return NS_ERROR_FAILURE;
  }

  if (aRequest->PassedConditionForEitherCache()) {
    if (!GetScriptLoader()->StartCollectingDelazifications(aCx, aModuleOut,
                                                           stencil)) {
      return NS_ERROR_FAILURE;
    }
  }

  GetScriptLoader()->TryCacheRequest(aRequest);

  return NS_OK;
}

nsresult ModuleLoader::CompileJsonModule(
    JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
    JS::MutableHandle<JSObject*> aModuleOut) {
  MOZ_ASSERT(!aRequest->GetScriptLoadContext()->mWasCompiledOMT);

  MOZ_ASSERT(aRequest->IsFetchedAsTextSource());
  ModuleLoader::MaybeSourceText maybeSource;
  nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
                                          aRequest->mLoadContext.get());
  NS_ENSURE_SUCCESS(rv, rv);

  auto compile = [&](auto& source) {
    return JS::CompileJsonModule(aCx, aOptions, source);
  };

  auto* jsonModule = maybeSource.mapNonEmpty(compile);
  if (!jsonModule) {
    return NS_ERROR_FAILURE;
  }

  aModuleOut.set(jsonModule);
  return NS_OK;
}

nsresult CreateCssModule(JSContext* aCx, nsIGlobalObject* aGlobal,
                         const nsACString& aSource, nsIURI* aBaseURI,
                         JS::MutableHandle<JSObject*> aModuleOut) {
  // https://html.spec.whatwg.org/#creating-a-css-module-script
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
  if (!window) {
    JS_ReportErrorASCII(aCx,
                        "CSS module scripts not supported when there is no "
                        "window");
    return NS_ERROR_FAILURE;
  }

  Document* constructorDocument = window->GetExtantDoc();
  if (!constructorDocument) {
    JS_ReportErrorASCII(aCx,
                        "CSS module scripts not supported when there is no "
                        "document");
    return NS_ERROR_FAILURE;
  }

  // 5. Let sheet be the result of running the steps to create a constructed
  // CSSStyleSheet with an empty dictionary as the argument.
  // Note that according to the specification, the baseURL should be the
  // baseURL of the document, but that doesn't seem correct (see
  // https://github.com/whatwg/html/issues/11629).
  ErrorResult error;
  CSSStyleSheetInit options;
  RefPtr<StyleSheet> sheet = StyleSheet::CreateConstructedSheet(
      *constructorDocument, aBaseURI, options, error);
  if (error.Failed()) {
    MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(aCx));
    return NS_ERROR_FAILURE;
  }

  // 6. Run the steps to synchronously replace the rules of a CSSStyleSheet on
  // sheet given source. Ideally we wouldn't run this on the main thread for
  // large scripts, see https://bugzilla.mozilla.org/show_bug.cgi?id=1987143.
  sheet->ReplaceSync(aSource, error);
  if (error.Failed()) {
    MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(aCx));
    return NS_ERROR_FAILURE;
  }

  JS::Rooted<JS::Value> val(aCx, JS::NullValue());
  if (!GetOrCreateDOMReflector(aCx, sheet, &val) || !val.isObject()) {
    if (!JS_IsExceptionPending(aCx)) {
      JS_ReportErrorASCII(aCx, "Internal error");
    }
    return NS_ERROR_FAILURE;
  }

  // Steps 1 - 4 (re-ordered), 7, 8
  JSObject* cssModule = JS::CreateDefaultExportSyntheticModule(aCx, val);
  if (!cssModule) {
    return NS_ERROR_FAILURE;
  }

  aModuleOut.set(cssModule);
  return NS_OK;
}

nsresult ModuleLoader::CompileCssModule(
    JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
    JS::MutableHandle<JSObject*> aModuleOut) {
  MOZ_ASSERT(!aRequest->GetScriptLoadContext()->mWasCompiledOMT);
  MOZ_ASSERT(mozilla::StaticPrefs::layout_css_module_scripts_enabled());

  MOZ_ASSERT(aRequest->IsFetchedAsTextSource());
  ModuleLoader::MaybeSourceText maybeSource;
  nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
                                          aRequest->mLoadContext.get());
  NS_ENSURE_SUCCESS(rv, rv);

  JS::Rooted<JSObject*> cssModule(aCx, nullptr);
  ErrorResult error;
  auto compile = [&](auto& source) {
    using T = decltype(source);
    static_assert(std::is_same_v<T, JS::SourceText<char16_t>&> ||
                  std::is_same_v<T, JS::SourceText<Utf8Unit>&>);

    nsCString text;
    if constexpr (std::is_same_v<T, JS::SourceText<mozilla::Utf8Unit>&>) {
      text.Assign(source.get(), source.length());
    } else {
      CopyUTF16toUTF8(nsDependentSubstring(source.get(), source.length()),
                      text);
    }

    nsresult rv2 = CreateCssModule(aCx, aRequest->GetGlobalObject(), text,
                                   aRequest->BaseURL(), &cssModule);
    if (NS_FAILED(rv2) && !JS_IsExceptionPending(aCx)) {
      error.ThrowUnknownError("Internal error");
    }
  };

  maybeSource.mapNonEmpty(compile);
  if (!cssModule) {
    if (error.Failed()) {
      MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(aCx));
    }
    return NS_ERROR_FAILURE;
  }

  aModuleOut.set(cssModule);
  return NS_OK;
}

nsresult ModuleLoader::CreateTextModule(
    JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
    JS::MutableHandle<JSObject*> aModuleOut) {
  MOZ_ASSERT(!aRequest->GetScriptLoadContext()->mWasCompiledOMT);

  MOZ_ASSERT(aRequest->IsFetchedAsTextSource());
  ModuleLoader::MaybeSourceText maybeSource;
  nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
                                          aRequest->mLoadContext.get());
  NS_ENSURE_SUCCESS(rv, rv);

  auto compile = [&](auto& source) {
    using T = decltype(source);
    static_assert(std::is_same_v<T, JS::SourceText<char16_t>&> ||
                  std::is_same_v<T, JS::SourceText<Utf8Unit>&>);

    JSString* str;
    if constexpr (std::is_same_v<T, JS::SourceText<Utf8Unit>&>) {
      str = JS_NewStringCopyUTF8N(aCx,
                                  JS::UTF8Chars(source.get(), source.length()));
    } else {
      str = JS_NewUCStringCopyN(aCx, source.get(), source.length());
    }

    JS::Rooted<JS::Value> defaultExport(aCx, JS::StringValue(str));
    return JS::CreateDefaultExportSyntheticModule(aCx, defaultExport);
  };

  auto* textModule = maybeSource.mapNonEmpty(compile);
  if (!textModule) {
    return NS_ERROR_FAILURE;
  }

  aModuleOut.set(textModule);
  return NS_OK;
}

already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateTopLevel(
    nsIURI* aURI, nsIScriptElement* aElement, ReferrerPolicy aReferrerPolicy,
    ScriptFetchOptions* aFetchOptions, const SRIMetadata& aIntegrity,
    nsIURI* aReferrer, ScriptLoadContext* aContext,
    ScriptLoadRequestType aRequestType) {
  RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
      JS::ModuleType::JavaScript, aIntegrity, aReferrer, aContext,
      ModuleLoadRequest::Kind::TopLevel, this, nullptr);

  GetScriptLoader()->TryUseCache(aReferrerPolicy, aFetchOptions, aURI, request,
                                 aElement, aFetchOptions->mNonce, aRequestType);

  return request.forget();
}

already_AddRefed<ModuleLoadRequest> ModuleLoader::CreateRequest(
    JSContext* aCx, nsIURI* aURI, JS::Handle<JSObject*> aModuleRequest,
    JS::Handle<JS::Value> aHostDefined, JS::Handle<JS::Value> aPayload,
    bool aIsDynamicImport, ScriptFetchOptions* aOptions,
    ReferrerPolicy aReferrerPolicy, nsIURI* aBaseURL,
    const SRIMetadata& aSriMetadata) {
  RefPtr<ScriptLoadContext> context = new ScriptLoadContext();
  context->mIsInline = false;
  ModuleLoadRequest::Kind kind;
  ModuleLoadRequest* root = nullptr;
  if (aIsDynamicImport) {
    context->mScriptMode = ScriptLoadContext::ScriptMode::eAsync;
    kind = ModuleLoadRequest::Kind::DynamicImport;
  } else {
    MOZ_ASSERT(!aHostDefined.isUndefined());
    root = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate());
    MOZ_ASSERT(root);
    LoadContextBase* loadContext = root->mLoadContext;
    context->mScriptMode = loadContext->AsWindowContext()->mScriptMode;
    if (loadContext->AsWindowContext()->mIsPreload) {
      context->mIsPreload = true;
    }
    kind = ModuleLoadRequest::Kind::StaticImport;
  }

  JS::ModuleType moduleType = GetModuleRequestType(aCx, aModuleRequest);
  RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
      moduleType, aSriMetadata, aBaseURL, context, kind, this, root);

  GetScriptLoader()->TryUseCache(aReferrerPolicy, aOptions, aURI, request);

  return request.forget();
}

already_AddRefed<ScriptFetchOptions>
ModuleLoader::CreateDefaultScriptFetchOptions() {
  RefPtr<ScriptFetchOptions> options = ScriptFetchOptions::CreateDefault();
  nsCOMPtr<nsIPrincipal> principal = GetGlobalObject()->PrincipalOrNull();
  options->SetTriggeringPrincipal(principal);
  return options.forget();
}

nsIURI* ModuleLoader::GetClientReferrerURI() {
  Document* document = GetScriptLoader()->GetDocument();
#ifdef DEBUG
  nsCOMPtr<nsIPrincipal> principal = GetGlobalObject()->PrincipalOrNull();
#endif  // DEBUG
  MOZ_ASSERT_IF(GetKind() == WebExtension,
                BasePrincipal::Cast(principal)->ContentScriptAddonPolicy());
  MOZ_ASSERT_IF(GetKind() == Normal, principal == document->NodePrincipal());

  return document->GetDocBaseURI();
}

ModuleLoader::~ModuleLoader() {
  LOG(("ModuleLoader::~ModuleLoader %p", this));
  mLoader = nullptr;
}

#undef LOG
#undef LOG_ENABLED

}  // namespace mozilla::dom
