tor-browser

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

WorkerModuleLoader.cpp (9625B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "WorkerModuleLoader.h"
      8 
      9 #include "js/experimental/JSStencil.h"  // JS::Stencil, JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil
     10 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     11 #include "js/loader/ModuleLoadRequest.h"
     12 #include "mozilla/dom/RequestBinding.h"
     13 #include "mozilla/dom/WorkerLoadContext.h"
     14 #include "mozilla/dom/WorkerPrivate.h"
     15 #include "mozilla/dom/WorkerScope.h"
     16 #include "mozilla/dom/workerinternals/ScriptLoader.h"
     17 #include "nsISupportsImpl.h"
     18 
     19 namespace mozilla::dom::workerinternals::loader {
     20 
     21 //////////////////////////////////////////////////////////////
     22 // WorkerModuleLoader
     23 //////////////////////////////////////////////////////////////
     24 
     25 NS_IMPL_ADDREF_INHERITED(WorkerModuleLoader, JS::loader::ModuleLoaderBase)
     26 NS_IMPL_RELEASE_INHERITED(WorkerModuleLoader, JS::loader::ModuleLoaderBase)
     27 
     28 NS_IMPL_CYCLE_COLLECTION_INHERITED(WorkerModuleLoader,
     29                                   JS::loader::ModuleLoaderBase)
     30 
     31 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerModuleLoader)
     32 NS_INTERFACE_MAP_END_INHERITING(JS::loader::ModuleLoaderBase)
     33 
     34 WorkerModuleLoader::WorkerModuleLoader(WorkerScriptLoader* aScriptLoader,
     35                                       nsIGlobalObject* aGlobalObject)
     36    : ModuleLoaderBase(aScriptLoader, aGlobalObject) {}
     37 
     38 nsIURI* WorkerModuleLoader::GetBaseURI() const {
     39  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     40  return workerPrivate->GetBaseURI();
     41 }
     42 
     43 nsIURI* WorkerModuleLoader::GetClientReferrerURI() {
     44  // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
     45  // Step 3. "client":
     46  //   2. let referrerSource be environment’s creation URL.
     47  //
     48  // https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-creation-url
     49  // https://html.spec.whatwg.org/multipage/workers.html#set-up-a-worker-environment-settings-object
     50  return GetBaseURI();
     51 }
     52 
     53 already_AddRefed<JS::loader::ScriptFetchOptions>
     54 WorkerModuleLoader::CreateDefaultScriptFetchOptions() {
     55  RefPtr<ScriptFetchOptions> options = ScriptFetchOptions::CreateDefault();
     56  return options.forget();
     57 }
     58 
     59 already_AddRefed<ModuleLoadRequest> WorkerModuleLoader::CreateRequest(
     60    JSContext* aCx, nsIURI* aURI, JS::Handle<JSObject*> aModuleRequest,
     61    JS::Handle<JS::Value> aHostDefined, JS::Handle<JS::Value> aPayload,
     62    bool aIsDynamicImport, ScriptFetchOptions* aOptions,
     63    mozilla::dom::ReferrerPolicy aReferrerPolicy, nsIURI* aBaseURL,
     64    const mozilla::dom::SRIMetadata& aSriMetadata) {
     65  Maybe<ClientInfo> clientInfo = GetGlobalObject()->GetClientInfo();
     66 
     67  ModuleLoadRequest::Kind kind;
     68  RefPtr<WorkerLoadContext> loadContext;
     69  ModuleLoadRequest* root = nullptr;
     70  if (aIsDynamicImport) {
     71    if (!CreateDynamicImportLoader()) {
     72      return nullptr;
     73    }
     74 
     75    loadContext = new WorkerLoadContext(
     76        WorkerLoadContext::Kind::DynamicImport, clientInfo,
     77        GetCurrentScriptLoader(),
     78        // When dynamic import is supported in ServiceWorkers,
     79        // the current plan in onlyExistingCachedResourcesAllowed
     80        // is that only existing cached resources will be
     81        // allowed.  (`import()` will not be used for caching
     82        // side effects, but instead a specific method will be
     83        // used during installation.)
     84        true);
     85 
     86    kind = ModuleLoadRequest::Kind::DynamicImport;
     87  } else {
     88    MOZ_ASSERT(!aHostDefined.isUndefined());
     89    root = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate());
     90    MOZ_ASSERT(root);
     91    WorkerLoadContext* context = root->mLoadContext->AsWorkerContext();
     92    loadContext = new WorkerLoadContext(
     93        WorkerLoadContext::Kind::StaticImport, clientInfo,
     94        context->mScriptLoader, context->mOnlyExistingCachedResourcesAllowed);
     95    kind = ModuleLoadRequest::Kind::StaticImport;
     96  }
     97 
     98  JS::ModuleType moduleType = JS::GetModuleRequestType(aCx, aModuleRequest);
     99  RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
    100      moduleType, SRIMetadata(), aBaseURL, loadContext, kind, this, root);
    101 
    102  request->mURL = aURI->GetSpecOrDefault();
    103  request->NoCacheEntryFound(aReferrerPolicy, aOptions, aURI);
    104  return request.forget();
    105 }
    106 
    107 bool WorkerModuleLoader::CreateDynamicImportLoader() {
    108  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    109  workerPrivate->AssertIsOnWorkerThread();
    110 
    111  IgnoredErrorResult rv;
    112  RefPtr<WorkerScriptLoader> loader = loader::WorkerScriptLoader::Create(
    113      workerPrivate, nullptr, nullptr,
    114      GetCurrentScriptLoader()->GetWorkerScriptType(), rv);
    115  if (NS_WARN_IF(rv.Failed())) {
    116    return false;
    117  }
    118 
    119  SetScriptLoader(loader);
    120  return true;
    121 }
    122 
    123 bool WorkerModuleLoader::IsDynamicImportSupported() {
    124  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    125  // Not supported for Service Workers.
    126  // https://github.com/w3c/ServiceWorker/issues/1585 covers existing discussion
    127  // about potentially supporting use of import().
    128  return !workerPrivate->IsServiceWorker();
    129 }
    130 
    131 bool WorkerModuleLoader::IsForServiceWorker() const {
    132  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    133  return workerPrivate && workerPrivate->IsServiceWorker();
    134 }
    135 
    136 bool WorkerModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest,
    137                                      nsresult* aRvOut) {
    138  return true;
    139 }
    140 
    141 nsresult WorkerModuleLoader::StartFetch(ModuleLoadRequest* aRequest) {
    142  if (!GetScriptLoaderFor(aRequest)->DispatchLoadScript(aRequest)) {
    143    return NS_ERROR_FAILURE;
    144  }
    145  return NS_OK;
    146 }
    147 
    148 nsresult WorkerModuleLoader::CompileFetchedModule(
    149    JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::CompileOptions& aOptions,
    150    ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleScript) {
    151  switch (aRequest->mModuleType) {
    152    case JS::ModuleType::Unknown:
    153    case JS::ModuleType::Bytes:
    154      MOZ_CRASH("Unexpected module type");
    155    case JS::ModuleType::JavaScriptOrWasm:
    156      return CompileJavaScriptOrWasmModule(aCx, aOptions, aRequest,
    157                                           aModuleScript);
    158    case JS::ModuleType::JSON:
    159      return CompileJsonModule(aCx, aOptions, aRequest, aModuleScript);
    160    case JS::ModuleType::CSS:
    161      MOZ_CRASH("CSS modules are not supported in workers");
    162  }
    163 
    164  MOZ_CRASH("Unhandled module type");
    165 }
    166 
    167 nsresult WorkerModuleLoader::CompileJavaScriptOrWasmModule(
    168    JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
    169    JS::MutableHandle<JSObject*> aModuleScript) {
    170  MOZ_ASSERT(aRequest->IsTextSource());
    171  MaybeSourceText maybeSource;
    172  nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
    173                                          aRequest->mLoadContext.get());
    174  NS_ENSURE_SUCCESS(rv, rv);
    175 
    176 #ifdef NIGHTLY_BUILD
    177  if (aRequest->HasWasmMimeTypeEssence()) {
    178    auto compile = [&](auto& source) {
    179      return JS::CompileWasmModule(aCx, aOptions, source);
    180    };
    181 
    182    auto* wasmModule = maybeSource.mapNonEmpty(compile);
    183    if (!wasmModule) {
    184      return NS_ERROR_FAILURE;
    185    }
    186 
    187    aModuleScript.set(wasmModule);
    188    return NS_OK;
    189  }
    190 #endif
    191 
    192  RefPtr<JS::Stencil> stencil;
    193 
    194  auto compile = [&](auto& source) {
    195    return JS::CompileModuleScriptToStencil(aCx, aOptions, source);
    196  };
    197  stencil = maybeSource.mapNonEmpty(compile);
    198 
    199  if (!stencil) {
    200    return NS_ERROR_FAILURE;
    201  }
    202 
    203  JS::InstantiateOptions instantiateOptions(aOptions);
    204  aModuleScript.set(
    205      JS::InstantiateModuleStencil(aCx, instantiateOptions, stencil));
    206  if (!aModuleScript) {
    207    return NS_ERROR_FAILURE;
    208  }
    209 
    210  return NS_OK;
    211 }
    212 
    213 nsresult WorkerModuleLoader::CompileJsonModule(
    214    JSContext* aCx, JS::CompileOptions& aOptions, ModuleLoadRequest* aRequest,
    215    JS::MutableHandle<JSObject*> aModuleScript) {
    216  MOZ_ASSERT(aRequest->IsTextSource());
    217  MaybeSourceText maybeSource;
    218  nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
    219                                          aRequest->mLoadContext.get());
    220  NS_ENSURE_SUCCESS(rv, rv);
    221 
    222  auto compile = [&](auto& source) {
    223    return JS::CompileJsonModule(aCx, aOptions, source);
    224  };
    225 
    226  auto* jsonModule = maybeSource.mapNonEmpty(compile);
    227  if (!jsonModule) {
    228    return NS_ERROR_FAILURE;
    229  }
    230 
    231  aModuleScript.set(jsonModule);
    232  return NS_OK;
    233 }
    234 
    235 WorkerScriptLoader* WorkerModuleLoader::GetCurrentScriptLoader() {
    236  return static_cast<WorkerScriptLoader*>(mLoader.get());
    237 }
    238 
    239 WorkerScriptLoader* WorkerModuleLoader::GetScriptLoaderFor(
    240    ModuleLoadRequest* aRequest) {
    241  return aRequest->GetWorkerLoadContext()->mScriptLoader;
    242 }
    243 
    244 void WorkerModuleLoader::OnModuleLoadComplete(ModuleLoadRequest* aRequest) {
    245  if (aRequest->IsStaticImport()) {
    246    return;
    247  }
    248 
    249  AutoJSAPI jsapi;
    250  if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
    251    return;
    252  }
    253  RefPtr<WorkerScriptLoader> requestScriptLoader = GetScriptLoaderFor(aRequest);
    254  if (aRequest->IsDynamicImport()) {
    255    aRequest->ProcessDynamicImport();
    256    requestScriptLoader->TryShutdown();
    257  } else {
    258    requestScriptLoader->MaybeMoveToLoadedList(aRequest);
    259    requestScriptLoader->ProcessPendingRequests(jsapi.cx());
    260  }
    261 }
    262 
    263 bool WorkerModuleLoader::IsModuleEvaluationAborted(
    264    ModuleLoadRequest* aRequest) {
    265  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    266  return !workerPrivate || !workerPrivate->GlobalScope() ||
    267         workerPrivate->GlobalScope()->IsDying();
    268 }
    269 
    270 }  // namespace mozilla::dom::workerinternals::loader