Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "js/experimental/JSStencil.h" // JS::Stencil, JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/loader/ModuleLoadRequest.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/WorkerLoadContext.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/workerinternals/ScriptLoader.h"
#include "mozilla/dom/WorkerScope.h"
#include "WorkerModuleLoader.h"
#include "nsISupportsImpl.h"
namespace mozilla::dom::workerinternals::loader {
//////////////////////////////////////////////////////////////
// WorkerModuleLoader
//////////////////////////////////////////////////////////////
NS_IMPL_ADDREF_INHERITED(WorkerModuleLoader, JS::loader::ModuleLoaderBase)
NS_IMPL_RELEASE_INHERITED(WorkerModuleLoader, JS::loader::ModuleLoaderBase)
NS_IMPL_CYCLE_COLLECTION_INHERITED(WorkerModuleLoader,
JS::loader::ModuleLoaderBase)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerModuleLoader)
NS_INTERFACE_MAP_END_INHERITING(JS::loader::ModuleLoaderBase)
WorkerModuleLoader::WorkerModuleLoader(WorkerScriptLoader* aScriptLoader,
nsIGlobalObject* aGlobalObject)
: ModuleLoaderBase(aScriptLoader, aGlobalObject) {}
nsIURI* WorkerModuleLoader::GetBaseURI() const {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
return workerPrivate->GetBaseURI();
}
already_AddRefed<ModuleLoadRequest> WorkerModuleLoader::CreateStaticImport(
nsIURI* aURI, JS::ModuleType aModuleType, ModuleLoadRequest* aParent) {
// We are intentionally deviating from the specification here and using the
// worker's CSP rather than the document CSP. The spec otherwise requires our
// service worker integration to be changed, and additionally the decision
// here did not make sense as we are treating static imports as different from
// other kinds of subresources.
Maybe<ClientInfo> clientInfo = GetGlobalObject()->GetClientInfo();
RefPtr<WorkerLoadContext> loadContext = new WorkerLoadContext(
WorkerLoadContext::Kind::StaticImport, clientInfo,
aParent->GetWorkerLoadContext()->mScriptLoader,
aParent->GetWorkerLoadContext()->mOnlyExistingCachedResourcesAllowed);
RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
aURI, aModuleType, aParent->ReferrerPolicy(), aParent->mFetchOptions,
SRIMetadata(), aParent->mURI, loadContext, false, /* is top level */
false, /* is dynamic import */
this, aParent->mVisitedSet, aParent->GetRootModule());
request->mURL = request->mURI->GetSpecOrDefault();
request->NoCacheEntryFound();
return request.forget();
}
bool WorkerModuleLoader::CreateDynamicImportLoader() {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
workerPrivate->AssertIsOnWorkerThread();
IgnoredErrorResult rv;
RefPtr<WorkerScriptLoader> loader = loader::WorkerScriptLoader::Create(
workerPrivate, nullptr, nullptr,
GetCurrentScriptLoader()->GetWorkerScriptType(), rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
SetScriptLoader(loader);
return true;
}
already_AddRefed<ModuleLoadRequest> WorkerModuleLoader::CreateDynamicImport(
JSContext* aCx, nsIURI* aURI, JS::ModuleType aModuleType,
LoadedScript* aMaybeActiveScript, JS::Handle<JSString*> aSpecifier,
JS::Handle<JSObject*> aPromise) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
if (!CreateDynamicImportLoader()) {
return nullptr;
}
// Not supported for Service Workers.
// https://github.com/w3c/ServiceWorker/issues/1585 covers existing discussion
// about potentially supporting use of import().
if (workerPrivate->IsServiceWorker()) {
return nullptr;
}
MOZ_ASSERT(aSpecifier);
MOZ_ASSERT(aPromise);
RefPtr<ScriptFetchOptions> options;
nsIURI* baseURL = nullptr;
if (aMaybeActiveScript) {
// Step 6.3. Set fetchOptions to the new descendant script fetch options for
// referencingScript's fetch options.
options = aMaybeActiveScript->GetFetchOptions();
baseURL = aMaybeActiveScript->BaseURL();
} else {
// Step 4. Let fetchOptions be the default classic script fetch options.
//
// The default classic script fetch options are a script fetch options whose
// cryptographic nonce is the empty string, integrity metadata is the empty
// string, parser metadata is "not-parser-inserted", credentials mode is
// "same-origin", referrer policy is the empty string, and fetch priority is
// "auto".
options = new ScriptFetchOptions(
CORSMode::CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto,
JS::loader::ParserMetadata::NotParserInserted, nullptr);
baseURL = GetBaseURI();
}
Maybe<ClientInfo> clientInfo = GetGlobalObject()->GetClientInfo();
RefPtr<WorkerLoadContext> context = new WorkerLoadContext(
WorkerLoadContext::Kind::DynamicImport, clientInfo,
GetCurrentScriptLoader(),
// When dynamic import is supported in ServiceWorkers,
// the current plan in onlyExistingCachedResourcesAllowed
// is that only existing cached resources will be
// allowed. (`import()` will not be used for caching
// side effects, but instead a specific method will be
// used during installation.)
true);
RefPtr<JS::loader::VisitedURLSet> visitedSet =
ModuleLoadRequest::NewVisitedSetForTopLevelImport(aURI, aModuleType);
ReferrerPolicy referrerPolicy = workerPrivate->GetReferrerPolicy();
RefPtr<ModuleLoadRequest> request =
new ModuleLoadRequest(aURI, aModuleType, referrerPolicy, options,
SRIMetadata(), baseURL, context, true,
/* is top level */ true, /* is dynamic import */
this, visitedSet, nullptr);
request->SetDynamicImport(aMaybeActiveScript, aSpecifier, aPromise);
request->NoCacheEntryFound();
return request.forget();
}
bool WorkerModuleLoader::IsDynamicImportSupported() {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
if (workerPrivate->IsServiceWorker()) {
return false;
}
return true;
}
bool WorkerModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest,
nsresult* aRvOut) {
return true;
}
nsresult WorkerModuleLoader::StartFetch(ModuleLoadRequest* aRequest) {
if (!GetScriptLoaderFor(aRequest)->DispatchLoadScript(aRequest)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult WorkerModuleLoader::CompileFetchedModule(
JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::CompileOptions& aOptions,
ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleScript) {
switch (aRequest->mModuleType) {
case JS::ModuleType::Unknown:
MOZ_CRASH("Unexpected module type");
case JS::ModuleType::JavaScript:
return CompileJavaScriptModule(aCx, aOptions, aRequest, aModuleScript);
case JS::ModuleType::JSON:
return CompileJsonModule(aCx, aOptions, aRequest, aModuleScript);
}
MOZ_CRASH("Unhandled module type");
}
nsresult WorkerModuleLoader::CompileJavaScriptModule(
JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
JS::MutableHandle<JSObject*> aModuleScript) {
MOZ_ASSERT(aRequest->IsTextSource());
MaybeSourceText maybeSource;
nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
aRequest->mLoadContext.get());
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<JS::Stencil> stencil;
auto compile = [&](auto& source) {
return JS::CompileModuleScriptToStencil(aCx, aOptions, source);
};
stencil = maybeSource.mapNonEmpty(compile);
if (!stencil) {
return NS_ERROR_FAILURE;
}
JS::InstantiateOptions instantiateOptions(aOptions);
aModuleScript.set(
JS::InstantiateModuleStencil(aCx, instantiateOptions, stencil));
if (!aModuleScript) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult WorkerModuleLoader::CompileJsonModule(
JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
JS::MutableHandle<JSObject*> aModuleScript) {
MOZ_ASSERT(aRequest->IsTextSource());
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;
}
aModuleScript.set(jsonModule);
return NS_OK;
}
WorkerScriptLoader* WorkerModuleLoader::GetCurrentScriptLoader() {
return static_cast<WorkerScriptLoader*>(mLoader.get());
}
WorkerScriptLoader* WorkerModuleLoader::GetScriptLoaderFor(
ModuleLoadRequest* aRequest) {
return aRequest->GetWorkerLoadContext()->mScriptLoader;
}
void WorkerModuleLoader::OnModuleLoadComplete(ModuleLoadRequest* aRequest) {
if (aRequest->IsTopLevel()) {
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
return;
}
RefPtr<WorkerScriptLoader> requestScriptLoader =
GetScriptLoaderFor(aRequest);
if (aRequest->IsDynamicImport()) {
aRequest->ProcessDynamicImport();
requestScriptLoader->TryShutdown();
} else {
requestScriptLoader->MaybeMoveToLoadedList(aRequest);
requestScriptLoader->ProcessPendingRequests(jsapi.cx());
}
}
}
bool WorkerModuleLoader::IsModuleEvaluationAborted(
ModuleLoadRequest* aRequest) {
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
return !workerPrivate || !workerPrivate->GlobalScope() ||
workerPrivate->GlobalScope()->IsDying();
}
} // namespace mozilla::dom::workerinternals::loader