tor-browser

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

WorkletFetchHandler.cpp (21641B)


      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 #include "WorkletFetchHandler.h"
      7 
      8 #include "js/ContextOptions.h"
      9 #include "js/loader/ModuleLoadRequest.h"
     10 #include "mozilla/CycleCollectedJSContext.h"
     11 #include "mozilla/ScopeExit.h"
     12 #include "mozilla/StaticPrefs_javascript.h"
     13 #include "mozilla/TaskQueue.h"
     14 #include "mozilla/dom/Document.h"
     15 #include "mozilla/dom/Fetch.h"
     16 #include "mozilla/dom/Request.h"
     17 #include "mozilla/dom/RequestBinding.h"
     18 #include "mozilla/dom/Response.h"
     19 #include "mozilla/dom/RootedDictionary.h"
     20 #include "mozilla/dom/ScriptLoadHandler.h"  // ScriptDecoder
     21 #include "mozilla/dom/ScriptLoader.h"
     22 #include "mozilla/dom/Worklet.h"
     23 #include "mozilla/dom/WorkletBinding.h"
     24 #include "mozilla/dom/WorkletGlobalScope.h"
     25 #include "mozilla/dom/WorkletImpl.h"
     26 #include "mozilla/dom/WorkletThread.h"
     27 #include "mozilla/dom/worklet/WorkletModuleLoader.h"
     28 #include "nsIInputStreamPump.h"
     29 #include "nsIThreadRetargetableRequest.h"
     30 #include "xpcpublic.h"
     31 
     32 using JS::loader::ModuleLoadRequest;
     33 using JS::loader::ParserMetadata;
     34 using JS::loader::ScriptFetchOptions;
     35 using mozilla::dom::loader::WorkletModuleLoader;
     36 
     37 namespace mozilla::dom {
     38 
     39 // A Runnable to call ModuleLoadRequest::StartModuleLoad on a worklet thread.
     40 class StartModuleLoadRunnable final : public Runnable {
     41 public:
     42  StartModuleLoadRunnable(
     43      WorkletImpl* aWorkletImpl,
     44      const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef,
     45      nsCOMPtr<nsIURI> aURI, nsIURI* aReferrer,
     46      const nsTArray<nsString>& aLocalizedStrs)
     47      : Runnable("Worklet::StartModuleLoadRunnable"),
     48        mWorkletImpl(aWorkletImpl),
     49        mHandlerRef(aHandlerRef),
     50        mURI(std::move(aURI)),
     51        mReferrer(aReferrer),
     52        mLocalizedStrs(aLocalizedStrs),
     53        mParentRuntime(
     54            JS_GetParentRuntime(CycleCollectedJSContext::Get()->Context())) {
     55    MOZ_ASSERT(NS_IsMainThread());
     56    MOZ_ASSERT(mParentRuntime);
     57    xpc::SetPrefableContextOptions(mContextOptions);
     58  }
     59 
     60  ~StartModuleLoadRunnable() = default;
     61 
     62  NS_IMETHOD Run() override;
     63 
     64 private:
     65  NS_IMETHOD RunOnWorkletThread();
     66 
     67  RefPtr<WorkletImpl> mWorkletImpl;
     68  nsMainThreadPtrHandle<WorkletFetchHandler> mHandlerRef;
     69  nsCOMPtr<nsIURI> mURI;
     70  nsCOMPtr<nsIURI> mReferrer;
     71  const nsTArray<nsString>& mLocalizedStrs;
     72  JSRuntime* mParentRuntime;
     73  JS::ContextOptions mContextOptions;
     74 };
     75 
     76 NS_IMETHODIMP
     77 StartModuleLoadRunnable::Run() {
     78  // WorkletThread::IsOnWorkletThread() cannot be used here because it depends
     79  // on a WorkletJSContext having been created for this thread.  That does not
     80  // happen until the global scope is created the first time
     81  // RunOnWorkletThread() is called.
     82  MOZ_ASSERT(!NS_IsMainThread());
     83  return RunOnWorkletThread();
     84 }
     85 
     86 NS_IMETHODIMP StartModuleLoadRunnable::RunOnWorkletThread() {
     87  // This can be called on a GraphRunner thread or a DOM Worklet thread.
     88  WorkletThread::EnsureCycleCollectedJSContext(mParentRuntime, mContextOptions);
     89 
     90  WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope();
     91  if (!globalScope) {
     92    return NS_ERROR_DOM_UNKNOWN_ERR;
     93  }
     94 
     95  // To fetch a worklet/module worker script graph:
     96  // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph
     97  // Step 1. Let options be a script fetch options whose cryptographic nonce is
     98  // the empty string, integrity metadata is the empty string, parser metadata
     99  // is "not-parser-inserted", credentials mode is credentials mode, referrer
    100  // policy is the empty string, and fetch priority is "auto".
    101  RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
    102      CORSMode::CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto,
    103      ParserMetadata::NotParserInserted,
    104      /*triggeringPrincipal*/ nullptr);
    105 
    106  WorkletModuleLoader* moduleLoader =
    107      static_cast<WorkletModuleLoader*>(globalScope->GetModuleLoader());
    108  MOZ_ASSERT(moduleLoader);
    109 
    110  if (!moduleLoader->HasSetLocalizedStrings()) {
    111    moduleLoader->SetLocalizedStrings(&mLocalizedStrs);
    112  }
    113 
    114  RefPtr<WorkletLoadContext> loadContext = new WorkletLoadContext(mHandlerRef);
    115 
    116  // Part of Step 2. This sets the Top-level flag to true
    117  RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
    118      JS::ModuleType::JavaScript, SRIMetadata(), mReferrer, loadContext,
    119      ModuleLoadRequest::Kind::TopLevel, moduleLoader, nullptr);
    120 
    121  request->mURL = mURI->GetSpecOrDefault();
    122  request->NoCacheEntryFound(ReferrerPolicy::_empty, fetchOptions, mURI);
    123 
    124  return request->StartModuleLoad();
    125 }
    126 
    127 StartFetchRunnable::StartFetchRunnable(
    128    const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef, nsIURI* aURI,
    129    nsIURI* aReferrer)
    130    : Runnable("Worklet::StartFetchRunnable"),
    131      mHandlerRef(aHandlerRef),
    132      mURI(aURI),
    133      mReferrer(aReferrer) {
    134  MOZ_ASSERT(!NS_IsMainThread());
    135 }
    136 
    137 NS_IMETHODIMP
    138 StartFetchRunnable::Run() {
    139  MOZ_ASSERT(NS_IsMainThread());
    140 
    141  nsCOMPtr<nsIGlobalObject> global =
    142      do_QueryInterface(mHandlerRef->mWorklet->GetParentObject());
    143  MOZ_ASSERT(global);
    144 
    145  AutoJSAPI jsapi;
    146  if (NS_WARN_IF(!jsapi.Init(global))) {
    147    return NS_ERROR_FAILURE;
    148  }
    149 
    150  JSContext* cx = jsapi.cx();
    151  nsresult rv = mHandlerRef->StartFetch(cx, mURI, mReferrer);
    152  if (NS_FAILED(rv)) {
    153    mHandlerRef->HandleFetchFailed(mURI);
    154    return NS_ERROR_FAILURE;
    155  }
    156 
    157  return NS_OK;
    158 }
    159 
    160 // A Runnable to call ModuleLoadRequest::OnFetchComplete on a worklet thread.
    161 class FetchCompleteRunnable final : public Runnable {
    162 public:
    163  FetchCompleteRunnable(WorkletImpl* aWorkletImpl, nsIURI* aURI,
    164                        nsresult aResult,
    165 #ifdef NIGHTLY_BUILD
    166                        bool aHasWasmMimeTypeEssence,
    167 #endif
    168                        UniquePtr<uint8_t[]> aScriptBuffer = nullptr,
    169                        size_t aScriptLength = 0)
    170      : Runnable("Worklet::FetchCompleteRunnable"),
    171        mWorkletImpl(aWorkletImpl),
    172        mURI(aURI),
    173        mResult(aResult),
    174 #ifdef NIGHTLY_BUILD
    175        mHasWasmMimeTypeEssence(aHasWasmMimeTypeEssence),
    176 #endif
    177        mScriptBuffer(std::move(aScriptBuffer)),
    178        mScriptLength(aScriptLength) {
    179    MOZ_ASSERT(NS_IsMainThread());
    180  }
    181 
    182  ~FetchCompleteRunnable() = default;
    183 
    184  NS_IMETHOD Run() override;
    185 
    186 private:
    187  NS_IMETHOD RunOnWorkletThread();
    188 
    189  RefPtr<WorkletImpl> mWorkletImpl;
    190  nsCOMPtr<nsIURI> mURI;
    191  nsresult mResult;
    192 #ifdef NIGHTLY_BUILD
    193  bool mHasWasmMimeTypeEssence;
    194 #endif
    195  UniquePtr<uint8_t[]> mScriptBuffer;
    196  size_t mScriptLength;
    197 };
    198 
    199 NS_IMETHODIMP
    200 FetchCompleteRunnable::Run() {
    201  MOZ_ASSERT(WorkletThread::IsOnWorkletThread());
    202  return RunOnWorkletThread();
    203 }
    204 
    205 NS_IMETHODIMP FetchCompleteRunnable::RunOnWorkletThread() {
    206  WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope();
    207  if (!globalScope) {
    208    return NS_ERROR_DOM_UNKNOWN_ERR;
    209  }
    210 
    211  WorkletModuleLoader* moduleLoader =
    212      static_cast<WorkletModuleLoader*>(globalScope->GetModuleLoader());
    213  MOZ_ASSERT(moduleLoader);
    214  MOZ_ASSERT(mURI);
    215  ModuleLoadRequest* request = moduleLoader->GetRequest(mURI);
    216  MOZ_ASSERT(request);
    217 
    218  // Set the Source type to "text" for decoding.
    219  request->SetTextSource(request->mLoadContext.get());
    220 
    221  nsresult rv;
    222  if (mScriptBuffer) {
    223    UniquePtr<ScriptDecoder> decoder = MakeUnique<ScriptDecoder>(
    224        UTF_8_ENCODING, ScriptDecoder::BOMHandling::Remove);
    225    rv = decoder->DecodeRawData(request, mScriptBuffer.get(), mScriptLength,
    226                                true);
    227    NS_ENSURE_SUCCESS(rv, rv);
    228  }
    229 
    230 #ifdef NIGHTLY_BUILD
    231  // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script
    232  // Extract the content-type. If its essence is wasm, we'll attempt to
    233  // compile this module as a wasm module. (Steps 13.2, 13.6)
    234  if (mHasWasmMimeTypeEssence) {
    235    request->SetHasWasmMimeTypeEssence();
    236  }
    237 #endif
    238 
    239  request->SetBaseURL(mURI);
    240  request->OnFetchComplete(mResult);
    241  moduleLoader->RemoveRequest(mURI);
    242  return NS_OK;
    243 }
    244 
    245 //////////////////////////////////////////////////////////////
    246 // WorkletFetchHandler
    247 //////////////////////////////////////////////////////////////
    248 NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkletFetchHandler)
    249 NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkletFetchHandler)
    250 
    251 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletFetchHandler)
    252  NS_INTERFACE_MAP_ENTRY(nsISupports)
    253 NS_INTERFACE_MAP_END
    254 
    255 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkletFetchHandler)
    256 
    257 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkletFetchHandler)
    258  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWorklet, mPromises)
    259  tmp->mErrorToRethrow.setUndefined();
    260 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    261 
    262 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkletFetchHandler)
    263  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWorklet, mPromises)
    264 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    265 
    266 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WorkletFetchHandler)
    267  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
    268 NS_IMPL_CYCLE_COLLECTION_TRACE_END
    269 
    270 // static
    271 already_AddRefed<Promise> WorkletFetchHandler::AddModule(
    272    Worklet* aWorklet, JSContext* aCx, const nsAString& aModuleURL,
    273    const WorkletOptions& aOptions, ErrorResult& aRv) {
    274  MOZ_ASSERT(aWorklet);
    275  MOZ_ASSERT(NS_IsMainThread());
    276 
    277  aWorklet->Impl()->OnAddModuleStarted();
    278 
    279  auto promiseSettledGuard =
    280      MakeScopeExit([&] { aWorklet->Impl()->OnAddModulePromiseSettled(); });
    281 
    282  nsCOMPtr<nsIGlobalObject> global =
    283      do_QueryInterface(aWorklet->GetParentObject());
    284  MOZ_ASSERT(global);
    285 
    286  RefPtr<Promise> promise = Promise::Create(global, aRv);
    287  if (NS_WARN_IF(aRv.Failed())) {
    288    return nullptr;
    289  }
    290 
    291  nsCOMPtr<nsPIDOMWindowInner> window = aWorklet->GetParentObject();
    292  MOZ_ASSERT(window);
    293 
    294  nsCOMPtr<Document> doc;
    295  doc = window->GetExtantDoc();
    296  if (!doc) {
    297    promise->MaybeReject(NS_ERROR_FAILURE);
    298    return promise.forget();
    299  }
    300 
    301  nsCOMPtr<nsIURI> resolvedURI;
    302  nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr,
    303                          doc->GetBaseURI());
    304  if (NS_WARN_IF(NS_FAILED(rv))) {
    305    // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule
    306    // Step 3. If this fails, then return a promise rejected with a
    307    // "SyntaxError" DOMException.
    308    rv = NS_ERROR_DOM_SYNTAX_ERR;
    309 
    310    promise->MaybeReject(rv);
    311    return promise.forget();
    312  }
    313 
    314  nsAutoCString spec;
    315  rv = resolvedURI->GetSpec(spec);
    316  if (NS_WARN_IF(NS_FAILED(rv))) {
    317    rv = NS_ERROR_DOM_SYNTAX_ERR;
    318 
    319    promise->MaybeReject(rv);
    320    return promise.forget();
    321  }
    322 
    323  // Maybe we already have an handler for this URI
    324  {
    325    WorkletFetchHandler* handler = aWorklet->GetImportFetchHandler(spec);
    326    if (handler) {
    327      handler->AddPromise(aCx, promise);
    328      return promise.forget();
    329    }
    330  }
    331 
    332  RefPtr<WorkletFetchHandler> handler =
    333      new WorkletFetchHandler(aWorklet, promise, aOptions.mCredentials);
    334 
    335  nsMainThreadPtrHandle<WorkletFetchHandler> handlerRef{
    336      new nsMainThreadPtrHolder<WorkletFetchHandler>("FetchHandler", handler)};
    337 
    338  // Determine request's Referrer
    339  // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
    340  // Step 3.  Switch on request’s referrer:
    341  //    "client"
    342  //    Step 1.4. Let referrerSource be document’s URL.
    343  nsIURI* referrer = doc->GetDocumentURIAsReferrer();
    344  nsCOMPtr<nsIRunnable> runnable = new StartModuleLoadRunnable(
    345      aWorklet->mImpl, handlerRef, std::move(resolvedURI), referrer,
    346      aWorklet->GetLocalizedStrings());
    347 
    348  if (NS_FAILED(aWorklet->mImpl->SendControlMessage(runnable.forget()))) {
    349    return nullptr;
    350  }
    351 
    352  promiseSettledGuard.release();
    353 
    354  aWorklet->AddImportFetchHandler(spec, handler);
    355  return promise.forget();
    356 }
    357 
    358 WorkletFetchHandler::WorkletFetchHandler(Worklet* aWorklet, Promise* aPromise,
    359                                         RequestCredentials aCredentials)
    360    : mWorklet(aWorklet), mStatus(ePending), mCredentials(aCredentials) {
    361  MOZ_ASSERT(aWorklet);
    362  MOZ_ASSERT(aPromise);
    363  MOZ_ASSERT(NS_IsMainThread());
    364 
    365  mPromises.AppendElement(aPromise);
    366 }
    367 
    368 WorkletFetchHandler::~WorkletFetchHandler() { mozilla::DropJSObjects(this); }
    369 
    370 void WorkletFetchHandler::ExecutionFailed() {
    371  MOZ_ASSERT(NS_IsMainThread());
    372  RejectPromises(NS_ERROR_DOM_ABORT_ERR);
    373 }
    374 
    375 void WorkletFetchHandler::ExecutionFailed(JS::Handle<JS::Value> aError) {
    376  MOZ_ASSERT(NS_IsMainThread());
    377  RejectPromises(aError);
    378 }
    379 
    380 void WorkletFetchHandler::ExecutionSucceeded() {
    381  MOZ_ASSERT(NS_IsMainThread());
    382  ResolvePromises();
    383 }
    384 
    385 void WorkletFetchHandler::AddPromise(JSContext* aCx, Promise* aPromise) {
    386  MOZ_ASSERT(aPromise);
    387  MOZ_ASSERT(NS_IsMainThread());
    388 
    389  switch (mStatus) {
    390    case ePending:
    391      mPromises.AppendElement(aPromise);
    392      return;
    393 
    394    case eRejected:
    395      if (mHasError) {
    396        JS::Rooted<JS::Value> error(aCx, mErrorToRethrow);
    397        aPromise->MaybeReject(error);
    398      } else {
    399        aPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
    400      }
    401      return;
    402 
    403    case eResolved:
    404      aPromise->MaybeResolveWithUndefined();
    405      return;
    406  }
    407 }
    408 
    409 void WorkletFetchHandler::RejectPromises(nsresult aResult) {
    410  MOZ_ASSERT(mStatus == ePending);
    411  MOZ_ASSERT(NS_FAILED(aResult));
    412  MOZ_ASSERT(NS_IsMainThread());
    413 
    414  mWorklet->Impl()->OnAddModulePromiseSettled();
    415 
    416  for (uint32_t i = 0; i < mPromises.Length(); ++i) {
    417    mPromises[i]->MaybeReject(aResult);
    418  }
    419  mPromises.Clear();
    420 
    421  mStatus = eRejected;
    422  mWorklet = nullptr;
    423 }
    424 
    425 void WorkletFetchHandler::RejectPromises(JS::Handle<JS::Value> aValue) {
    426  MOZ_ASSERT(mStatus == ePending);
    427  MOZ_ASSERT(NS_IsMainThread());
    428 
    429  mWorklet->Impl()->OnAddModulePromiseSettled();
    430 
    431  for (uint32_t i = 0; i < mPromises.Length(); ++i) {
    432    mPromises[i]->MaybeReject(aValue);
    433  }
    434  mPromises.Clear();
    435 
    436  mHasError = true;
    437  mErrorToRethrow = aValue;
    438 
    439  mozilla::HoldJSObjects(this);
    440 
    441  mStatus = eRejected;
    442  mWorklet = nullptr;
    443 }
    444 
    445 void WorkletFetchHandler::ResolvePromises() {
    446  MOZ_ASSERT(mStatus == ePending);
    447  MOZ_ASSERT(NS_IsMainThread());
    448 
    449  mWorklet->Impl()->OnAddModulePromiseSettled();
    450 
    451  for (uint32_t i = 0; i < mPromises.Length(); ++i) {
    452    mPromises[i]->MaybeResolveWithUndefined();
    453  }
    454  mPromises.Clear();
    455 
    456  mStatus = eResolved;
    457  mWorklet = nullptr;
    458 }
    459 
    460 nsresult WorkletFetchHandler::StartFetch(JSContext* aCx, nsIURI* aURI,
    461                                         nsIURI* aReferrer) {
    462  nsAutoCString spec;
    463  nsresult res = aURI->GetSpec(spec);
    464  if (NS_WARN_IF(NS_FAILED(res))) {
    465    return NS_ERROR_FAILURE;
    466  }
    467 
    468  RequestOrUTF8String requestInput;
    469  requestInput.SetAsUTF8String().ShareOrDependUpon(spec);
    470 
    471  RootedDictionary<RequestInit> requestInit(aCx);
    472  requestInit.mCredentials.Construct(mCredentials);
    473 
    474  // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script
    475  // Step 8. mode is "cors"
    476  requestInit.mMode.Construct(RequestMode::Cors);
    477 
    478  if (aReferrer) {
    479    res = aReferrer->GetSpec(requestInit.mReferrer.Construct());
    480    if (NS_WARN_IF(NS_FAILED(res))) {
    481      return NS_ERROR_FAILURE;
    482    }
    483  }
    484 
    485  nsCOMPtr<nsIGlobalObject> global =
    486      do_QueryInterface(mWorklet->GetParentObject());
    487  MOZ_ASSERT(global);
    488 
    489  // Note: added to infer a default credentials mode in the Request setup,
    490  // but we always pass an explicit credentials value in requestInit, so
    491  // this has no effect right now. Bug 1887862 covers fixing worklets to behave
    492  // the same as "normal" fetch calls.
    493  nsIPrincipal* p = global->PrincipalOrNull();
    494  CallerType callerType = (p && p->IsSystemPrincipal() ? CallerType::System
    495                                                       : CallerType::NonSystem);
    496  IgnoredErrorResult rv;
    497  SafeRefPtr<Request> request = Request::Constructor(
    498      global, aCx, requestInput, requestInit, callerType, rv);
    499  if (rv.Failed()) {
    500    return NS_ERROR_FAILURE;
    501  }
    502 
    503  request->OverrideContentPolicyType(mWorklet->Impl()->ContentPolicyType());
    504 
    505  RequestOrUTF8String finalRequestInput;
    506  finalRequestInput.SetAsRequest() = request.unsafeGetRawPtr();
    507 
    508  RefPtr<Promise> fetchPromise = FetchRequest(
    509      global, finalRequestInput, requestInit, CallerType::System, rv);
    510  if (NS_WARN_IF(rv.Failed())) {
    511    return NS_ERROR_FAILURE;
    512  }
    513 
    514  RefPtr<WorkletScriptHandler> scriptHandler =
    515      new WorkletScriptHandler(mWorklet, aURI);
    516  fetchPromise->AppendNativeHandler(scriptHandler);
    517  return NS_OK;
    518 }
    519 
    520 void WorkletFetchHandler::HandleFetchFailed(nsIURI* aURI) {
    521  nsCOMPtr<nsIRunnable> runnable =
    522      new FetchCompleteRunnable(mWorklet->mImpl, aURI, NS_ERROR_FAILURE,
    523 #ifdef NIGHTLY_BUILD
    524                                false,
    525 #endif
    526                                nullptr, 0);
    527 
    528  if (NS_WARN_IF(
    529          NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget())))) {
    530    NS_WARNING("Failed to dispatch FetchCompleteRunnable to a worklet thread.");
    531  }
    532 }
    533 
    534 //////////////////////////////////////////////////////////////
    535 // WorkletScriptHandler
    536 //////////////////////////////////////////////////////////////
    537 NS_IMPL_ISUPPORTS(WorkletScriptHandler, nsIStreamLoaderObserver)
    538 
    539 WorkletScriptHandler::WorkletScriptHandler(Worklet* aWorklet, nsIURI* aURI)
    540    : mWorklet(aWorklet), mURI(aURI) {}
    541 
    542 void WorkletScriptHandler::ResolvedCallback(JSContext* aCx,
    543                                            JS::Handle<JS::Value> aValue,
    544                                            ErrorResult& aRv) {
    545  MOZ_ASSERT(NS_IsMainThread());
    546 
    547  if (!aValue.isObject()) {
    548    HandleFailure(NS_ERROR_FAILURE);
    549    return;
    550  }
    551 
    552  RefPtr<Response> response;
    553  nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
    554  if (NS_WARN_IF(NS_FAILED(rv))) {
    555    HandleFailure(NS_ERROR_FAILURE);
    556    return;
    557  }
    558 
    559  // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule
    560  // Step 6.4.1. If script is null, then:
    561  //   Step 1.1.2. Reject promise with an "AbortError" DOMException.
    562  if (!response->Ok()) {
    563    HandleFailure(NS_ERROR_DOM_ABORT_ERR);
    564    return;
    565  }
    566 
    567 #ifdef NIGHTLY_BUILD
    568  nsAutoCString contentType;
    569  ErrorResult result;
    570  if (response->GetInternalHeaders()) {
    571    response->GetInternalHeaders()->Get("Content-Type"_ns, contentType, result);
    572    if (!result.Failed()) {
    573      mHasWasmMimeTypeEssence = nsContentUtils::HasWasmMimeTypeEssence(
    574          NS_ConvertUTF8toUTF16(contentType));
    575    }
    576  }
    577 #endif
    578 
    579  nsCOMPtr<nsIInputStream> inputStream;
    580  response->GetBody(getter_AddRefs(inputStream));
    581  if (!inputStream) {
    582    HandleFailure(NS_ERROR_DOM_NETWORK_ERR);
    583    return;
    584  }
    585 
    586  nsCOMPtr<nsIInputStreamPump> pump;
    587  rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream.forget());
    588  if (NS_WARN_IF(NS_FAILED(rv))) {
    589    HandleFailure(rv);
    590    return;
    591  }
    592 
    593  nsCOMPtr<nsIStreamLoader> loader;
    594  rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
    595  if (NS_WARN_IF(NS_FAILED(rv))) {
    596    HandleFailure(rv);
    597    return;
    598  }
    599 
    600  rv = pump->AsyncRead(loader);
    601  if (NS_WARN_IF(NS_FAILED(rv))) {
    602    HandleFailure(rv);
    603    return;
    604  }
    605 
    606  nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
    607  if (rr) {
    608    nsCOMPtr<nsIEventTarget> sts =
    609        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
    610    RefPtr<TaskQueue> queue = TaskQueue::Create(
    611        sts.forget(), "WorkletScriptHandler STS Delivery Queue");
    612    rv = rr->RetargetDeliveryTo(queue);
    613    if (NS_FAILED(rv)) {
    614      NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
    615    }
    616  }
    617 }
    618 
    619 NS_IMETHODIMP WorkletScriptHandler::OnStreamComplete(nsIStreamLoader* aLoader,
    620                                                     nsISupports* aContext,
    621                                                     nsresult aStatus,
    622                                                     uint32_t aStringLen,
    623                                                     const uint8_t* aString) {
    624  MOZ_ASSERT(NS_IsMainThread());
    625 
    626  if (NS_FAILED(aStatus)) {
    627    HandleFailure(aStatus);
    628    return NS_OK;
    629  }
    630 
    631  // Copy the buffer and decode it on worklet thread, as we can only access
    632  // ModuleLoadRequest on worklet thread.
    633  UniquePtr<uint8_t[]> scriptTextBuf = MakeUnique<uint8_t[]>(aStringLen);
    634  memcpy(scriptTextBuf.get(), aString, aStringLen);
    635 
    636  nsCOMPtr<nsIRunnable> runnable =
    637      new FetchCompleteRunnable(mWorklet->mImpl, mURI, NS_OK,
    638 #ifdef NIGHTLY_BUILD
    639                                mHasWasmMimeTypeEssence,
    640 #endif
    641                                std::move(scriptTextBuf), aStringLen);
    642 
    643  if (NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget()))) {
    644    HandleFailure(NS_ERROR_FAILURE);
    645  }
    646 
    647  return NS_OK;
    648 }
    649 
    650 void WorkletScriptHandler::RejectedCallback(JSContext* aCx,
    651                                            JS::Handle<JS::Value> aValue,
    652                                            ErrorResult& aRv) {
    653  MOZ_ASSERT(NS_IsMainThread());
    654 
    655  // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule
    656  // Step 6.4.1. If script is null, then:
    657  //   Step 1.1.2. Reject promise with an "AbortError" DOMException.
    658  HandleFailure(NS_ERROR_DOM_ABORT_ERR);
    659 }
    660 
    661 void WorkletScriptHandler::HandleFailure(nsresult aResult) {
    662  DispatchFetchCompleteToWorklet(aResult);
    663 }
    664 
    665 void WorkletScriptHandler::DispatchFetchCompleteToWorklet(nsresult aRv) {
    666  nsCOMPtr<nsIRunnable> runnable =
    667      new FetchCompleteRunnable(mWorklet->mImpl, mURI, aRv,
    668 #ifdef NIGHTLY_BUILD
    669                                mHasWasmMimeTypeEssence,
    670 #endif
    671                                nullptr, 0);
    672 
    673  if (NS_WARN_IF(
    674          NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget())))) {
    675    NS_WARNING("Failed to dispatch FetchCompleteRunnable to a worklet thread.");
    676  }
    677 }
    678 
    679 }  // namespace mozilla::dom