summaryrefslogtreecommitdiffstats
path: root/dom/worklet/WorkletFetchHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/worklet/WorkletFetchHandler.cpp')
-rw-r--r--dom/worklet/WorkletFetchHandler.cpp642
1 files changed, 642 insertions, 0 deletions
diff --git a/dom/worklet/WorkletFetchHandler.cpp b/dom/worklet/WorkletFetchHandler.cpp
new file mode 100644
index 0000000000..bc764c5316
--- /dev/null
+++ b/dom/worklet/WorkletFetchHandler.cpp
@@ -0,0 +1,642 @@
+/* -*- 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 "WorkletFetchHandler.h"
+
+#include "js/loader/ModuleLoadRequest.h"
+#include "js/ContextOptions.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/Fetch.h"
+#include "mozilla/dom/Request.h"
+#include "mozilla/dom/Response.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/dom/ScriptLoader.h"
+#include "mozilla/dom/ScriptLoadHandler.h" // ScriptDecoder
+#include "mozilla/dom/Worklet.h"
+#include "mozilla/dom/WorkletBinding.h"
+#include "mozilla/dom/WorkletGlobalScope.h"
+#include "mozilla/dom/WorkletImpl.h"
+#include "mozilla/dom/WorkletThread.h"
+#include "mozilla/dom/worklet/WorkletModuleLoader.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/TaskQueue.h"
+#include "nsIInputStreamPump.h"
+#include "nsIThreadRetargetableRequest.h"
+#include "xpcpublic.h"
+
+using JS::loader::ModuleLoadRequest;
+using JS::loader::ScriptFetchOptions;
+using mozilla::dom::loader::WorkletModuleLoader;
+
+namespace mozilla::dom {
+
+// A Runnable to call ModuleLoadRequest::StartModuleLoad on a worklet thread.
+class StartModuleLoadRunnable final : public Runnable {
+ public:
+ StartModuleLoadRunnable(
+ WorkletImpl* aWorkletImpl,
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef,
+ nsCOMPtr<nsIURI> aURI, nsIURI* aReferrer,
+ const nsTArray<nsString>& aLocalizedStrs)
+ : Runnable("Worklet::StartModuleLoadRunnable"),
+ mWorkletImpl(aWorkletImpl),
+ mHandlerRef(aHandlerRef),
+ mURI(std::move(aURI)),
+ mReferrer(aReferrer),
+ mLocalizedStrs(aLocalizedStrs),
+ mParentRuntime(
+ JS_GetParentRuntime(CycleCollectedJSContext::Get()->Context())) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mParentRuntime);
+ xpc::SetPrefableContextOptions(mContextOptions);
+ }
+
+ ~StartModuleLoadRunnable() = default;
+
+ NS_IMETHOD Run() override;
+
+ private:
+ NS_IMETHOD RunOnWorkletThread();
+
+ RefPtr<WorkletImpl> mWorkletImpl;
+ nsMainThreadPtrHandle<WorkletFetchHandler> mHandlerRef;
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIURI> mReferrer;
+ const nsTArray<nsString>& mLocalizedStrs;
+ JSRuntime* mParentRuntime;
+ JS::ContextOptions mContextOptions;
+};
+
+NS_IMETHODIMP
+StartModuleLoadRunnable::Run() {
+ // WorkletThread::IsOnWorkletThread() cannot be used here because it depends
+ // on a WorkletJSContext having been created for this thread. That does not
+ // happen until the global scope is created the first time
+ // RunOnWorkletThread() is called.
+ MOZ_ASSERT(!NS_IsMainThread());
+ return RunOnWorkletThread();
+}
+
+NS_IMETHODIMP StartModuleLoadRunnable::RunOnWorkletThread() {
+ // This can be called on a GraphRunner thread or a DOM Worklet thread.
+ WorkletThread::EnsureCycleCollectedJSContext(mParentRuntime, mContextOptions);
+
+ WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope();
+ if (!globalScope) {
+ return NS_ERROR_DOM_UNKNOWN_ERR;
+ }
+
+ // To fetch a worklet/module worker script graph:
+ // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-worklet/module-worker-script-graph
+ // Step 1. Let options be a script fetch options. And referrer policy is the
+ // empty string.
+ ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
+ RefPtr<ScriptFetchOptions> fetchOptions = new ScriptFetchOptions(
+ CORSMode::CORS_NONE, referrerPolicy, /*triggeringPrincipal*/ nullptr);
+
+ WorkletModuleLoader* moduleLoader =
+ static_cast<WorkletModuleLoader*>(globalScope->GetModuleLoader());
+ MOZ_ASSERT(moduleLoader);
+
+ if (!moduleLoader->HasSetLocalizedStrings()) {
+ moduleLoader->SetLocalizedStrings(&mLocalizedStrs);
+ }
+
+ RefPtr<WorkletLoadContext> loadContext = new WorkletLoadContext(mHandlerRef);
+
+ // Part of Step 2. This sets the Top-level flag to true
+ RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
+ mURI, fetchOptions, SRIMetadata(), mReferrer, loadContext,
+ true, /* is top level */
+ false, /* is dynamic import */
+ moduleLoader, ModuleLoadRequest::NewVisitedSetForTopLevelImport(mURI),
+ nullptr);
+
+ request->mURL = request->mURI->GetSpecOrDefault();
+
+ return request->StartModuleLoad();
+}
+
+StartFetchRunnable::StartFetchRunnable(
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef, nsIURI* aURI,
+ nsIURI* aReferrer)
+ : Runnable("Worklet::StartFetchRunnable"),
+ mHandlerRef(aHandlerRef),
+ mURI(aURI),
+ mReferrer(aReferrer) {
+ MOZ_ASSERT(!NS_IsMainThread());
+}
+
+NS_IMETHODIMP
+StartFetchRunnable::Run() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIGlobalObject> global =
+ do_QueryInterface(mHandlerRef->mWorklet->GetParentObject());
+ MOZ_ASSERT(global);
+
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(global))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ JSContext* cx = jsapi.cx();
+ nsresult rv = mHandlerRef->StartFetch(cx, mURI, mReferrer);
+ if (NS_FAILED(rv)) {
+ mHandlerRef->HandleFetchFailed(mURI);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// A Runnable to call ModuleLoadRequest::OnFetchComplete on a worklet thread.
+class FetchCompleteRunnable final : public Runnable {
+ public:
+ FetchCompleteRunnable(WorkletImpl* aWorkletImpl, nsIURI* aURI,
+ nsresult aResult,
+ UniquePtr<uint8_t[]> aScriptBuffer = nullptr,
+ size_t aScriptLength = 0)
+ : Runnable("Worklet::FetchCompleteRunnable"),
+ mWorkletImpl(aWorkletImpl),
+ mURI(aURI),
+ mResult(aResult),
+ mScriptBuffer(std::move(aScriptBuffer)),
+ mScriptLength(aScriptLength) {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ ~FetchCompleteRunnable() = default;
+
+ NS_IMETHOD Run() override;
+
+ private:
+ NS_IMETHOD RunOnWorkletThread();
+
+ RefPtr<WorkletImpl> mWorkletImpl;
+ nsCOMPtr<nsIURI> mURI;
+ nsresult mResult;
+ UniquePtr<uint8_t[]> mScriptBuffer;
+ size_t mScriptLength;
+};
+
+NS_IMETHODIMP
+FetchCompleteRunnable::Run() {
+ MOZ_ASSERT(WorkletThread::IsOnWorkletThread());
+ return RunOnWorkletThread();
+}
+
+NS_IMETHODIMP FetchCompleteRunnable::RunOnWorkletThread() {
+ WorkletGlobalScope* globalScope = mWorkletImpl->GetGlobalScope();
+ if (!globalScope) {
+ return NS_ERROR_DOM_UNKNOWN_ERR;
+ }
+
+ WorkletModuleLoader* moduleLoader =
+ static_cast<WorkletModuleLoader*>(globalScope->GetModuleLoader());
+ MOZ_ASSERT(moduleLoader);
+ MOZ_ASSERT(mURI);
+ ModuleLoadRequest* request = moduleLoader->GetRequest(mURI);
+ MOZ_ASSERT(request);
+
+ // Set the Source type to "text" for decoding.
+ request->SetTextSource();
+
+ nsresult rv;
+ if (mScriptBuffer) {
+ UniquePtr<ScriptDecoder> decoder = MakeUnique<ScriptDecoder>(
+ UTF_8_ENCODING, ScriptDecoder::BOMHandling::Remove);
+ rv = decoder->DecodeRawData(request, mScriptBuffer.get(), mScriptLength,
+ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ request->mBaseURL = mURI;
+ request->OnFetchComplete(mResult);
+
+ if (NS_FAILED(mResult)) {
+ if (request->IsTopLevel()) {
+ request->LoadFailed();
+ } else {
+ request->Cancel();
+ }
+ }
+
+ moduleLoader->RemoveRequest(mURI);
+ return NS_OK;
+}
+
+//////////////////////////////////////////////////////////////
+// WorkletFetchHandler
+//////////////////////////////////////////////////////////////
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkletFetchHandler)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkletFetchHandler)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletFetchHandler)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(WorkletFetchHandler)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkletFetchHandler)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWorklet, mPromises)
+ tmp->mErrorToRethrow.setUndefined();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkletFetchHandler)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWorklet, mPromises)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WorkletFetchHandler)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+// static
+already_AddRefed<Promise> WorkletFetchHandler::AddModule(
+ Worklet* aWorklet, JSContext* aCx, const nsAString& aModuleURL,
+ const WorkletOptions& aOptions, ErrorResult& aRv) {
+ MOZ_ASSERT(aWorklet);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ aWorklet->Impl()->OnAddModuleStarted();
+
+ auto promiseSettledGuard =
+ MakeScopeExit([&] { aWorklet->Impl()->OnAddModulePromiseSettled(); });
+
+ nsCOMPtr<nsIGlobalObject> global =
+ do_QueryInterface(aWorklet->GetParentObject());
+ MOZ_ASSERT(global);
+
+ RefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> window = aWorklet->GetParentObject();
+ MOZ_ASSERT(window);
+
+ nsCOMPtr<Document> doc;
+ doc = window->GetExtantDoc();
+ if (!doc) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ return promise.forget();
+ }
+
+ nsCOMPtr<nsIURI> resolvedURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr,
+ doc->GetBaseURI());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule
+ // Step 3. If this fails, then return a promise rejected with a
+ // "SyntaxError" DOMException.
+ rv = NS_ERROR_DOM_SYNTAX_ERR;
+
+ promise->MaybeReject(rv);
+ return promise.forget();
+ }
+
+ nsAutoCString spec;
+ rv = resolvedURI->GetSpec(spec);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ rv = NS_ERROR_DOM_SYNTAX_ERR;
+
+ promise->MaybeReject(rv);
+ return promise.forget();
+ }
+
+ // Maybe we already have an handler for this URI
+ {
+ WorkletFetchHandler* handler = aWorklet->GetImportFetchHandler(spec);
+ if (handler) {
+ handler->AddPromise(aCx, promise);
+ return promise.forget();
+ }
+ }
+
+ RefPtr<WorkletFetchHandler> handler =
+ new WorkletFetchHandler(aWorklet, promise, aOptions.mCredentials);
+
+ nsMainThreadPtrHandle<WorkletFetchHandler> handlerRef{
+ new nsMainThreadPtrHolder<WorkletFetchHandler>("FetchHandler", handler)};
+
+ // Determine request's Referrer
+ // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
+ // Step 3. Switch on request’s referrer:
+ // "client"
+ // Step 1.4. Let referrerSource be document’s URL.
+ nsIURI* referrer = doc->GetDocumentURIAsReferrer();
+ nsCOMPtr<nsIRunnable> runnable = new StartModuleLoadRunnable(
+ aWorklet->mImpl, handlerRef, std::move(resolvedURI), referrer,
+ aWorklet->GetLocalizedStrings());
+
+ if (NS_FAILED(aWorklet->mImpl->SendControlMessage(runnable.forget()))) {
+ return nullptr;
+ }
+
+ promiseSettledGuard.release();
+
+ aWorklet->AddImportFetchHandler(spec, handler);
+ return promise.forget();
+}
+
+WorkletFetchHandler::WorkletFetchHandler(Worklet* aWorklet, Promise* aPromise,
+ RequestCredentials aCredentials)
+ : mWorklet(aWorklet), mStatus(ePending), mCredentials(aCredentials) {
+ MOZ_ASSERT(aWorklet);
+ MOZ_ASSERT(aPromise);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mPromises.AppendElement(aPromise);
+}
+
+WorkletFetchHandler::~WorkletFetchHandler() { mozilla::DropJSObjects(this); }
+
+void WorkletFetchHandler::ExecutionFailed() {
+ MOZ_ASSERT(NS_IsMainThread());
+ RejectPromises(NS_ERROR_DOM_ABORT_ERR);
+}
+
+void WorkletFetchHandler::ExecutionFailed(JS::Handle<JS::Value> aError) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RejectPromises(aError);
+}
+
+void WorkletFetchHandler::ExecutionSucceeded() {
+ MOZ_ASSERT(NS_IsMainThread());
+ ResolvePromises();
+}
+
+void WorkletFetchHandler::AddPromise(JSContext* aCx, Promise* aPromise) {
+ MOZ_ASSERT(aPromise);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ switch (mStatus) {
+ case ePending:
+ mPromises.AppendElement(aPromise);
+ return;
+
+ case eRejected:
+ if (mHasError) {
+ JS::Rooted<JS::Value> error(aCx, mErrorToRethrow);
+ aPromise->MaybeReject(error);
+ } else {
+ aPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+ }
+ return;
+
+ case eResolved:
+ aPromise->MaybeResolveWithUndefined();
+ return;
+ }
+}
+
+void WorkletFetchHandler::RejectPromises(nsresult aResult) {
+ MOZ_ASSERT(mStatus == ePending);
+ MOZ_ASSERT(NS_FAILED(aResult));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mWorklet->Impl()->OnAddModulePromiseSettled();
+
+ for (uint32_t i = 0; i < mPromises.Length(); ++i) {
+ mPromises[i]->MaybeReject(aResult);
+ }
+ mPromises.Clear();
+
+ mStatus = eRejected;
+ mWorklet = nullptr;
+}
+
+void WorkletFetchHandler::RejectPromises(JS::Handle<JS::Value> aValue) {
+ MOZ_ASSERT(mStatus == ePending);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mWorklet->Impl()->OnAddModulePromiseSettled();
+
+ for (uint32_t i = 0; i < mPromises.Length(); ++i) {
+ mPromises[i]->MaybeReject(aValue);
+ }
+ mPromises.Clear();
+
+ mHasError = true;
+ mErrorToRethrow = aValue;
+
+ mozilla::HoldJSObjects(this);
+
+ mStatus = eRejected;
+ mWorklet = nullptr;
+}
+
+void WorkletFetchHandler::ResolvePromises() {
+ MOZ_ASSERT(mStatus == ePending);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mWorklet->Impl()->OnAddModulePromiseSettled();
+
+ for (uint32_t i = 0; i < mPromises.Length(); ++i) {
+ mPromises[i]->MaybeResolveWithUndefined();
+ }
+ mPromises.Clear();
+
+ mStatus = eResolved;
+ mWorklet = nullptr;
+}
+
+nsresult WorkletFetchHandler::StartFetch(JSContext* aCx, nsIURI* aURI,
+ nsIURI* aReferrer) {
+ nsAutoCString spec;
+ nsresult res = aURI->GetSpec(spec);
+ if (NS_WARN_IF(NS_FAILED(res))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RequestOrUSVString requestInput;
+
+ nsAutoString url;
+ CopyUTF8toUTF16(spec, url);
+ requestInput.SetAsUSVString().ShareOrDependUpon(url);
+
+ RootedDictionary<RequestInit> requestInit(aCx);
+ requestInit.mCredentials.Construct(mCredentials);
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-single-module-script
+ // Step 8. mode is "cors"
+ requestInit.mMode.Construct(RequestMode::Cors);
+
+ if (aReferrer) {
+ nsAutoString referrer;
+ res = aReferrer->GetSpec(spec);
+ if (NS_WARN_IF(NS_FAILED(res))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CopyUTF8toUTF16(spec, referrer);
+ requestInit.mReferrer.Construct(referrer);
+ }
+
+ nsCOMPtr<nsIGlobalObject> global =
+ do_QueryInterface(mWorklet->GetParentObject());
+ MOZ_ASSERT(global);
+
+ IgnoredErrorResult rv;
+ SafeRefPtr<Request> request =
+ Request::Constructor(global, aCx, requestInput, requestInit, rv);
+ if (rv.Failed()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ request->OverrideContentPolicyType(mWorklet->Impl()->ContentPolicyType());
+
+ RequestOrUSVString finalRequestInput;
+ finalRequestInput.SetAsRequest() = request.unsafeGetRawPtr();
+
+ RefPtr<Promise> fetchPromise = FetchRequest(
+ global, finalRequestInput, requestInit, CallerType::System, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<WorkletScriptHandler> scriptHandler =
+ new WorkletScriptHandler(mWorklet, aURI);
+ fetchPromise->AppendNativeHandler(scriptHandler);
+ return NS_OK;
+}
+
+void WorkletFetchHandler::HandleFetchFailed(nsIURI* aURI) {
+ nsCOMPtr<nsIRunnable> runnable = new FetchCompleteRunnable(
+ mWorklet->mImpl, aURI, NS_ERROR_FAILURE, nullptr, 0);
+
+ if (NS_WARN_IF(
+ NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget())))) {
+ NS_WARNING("Failed to dispatch FetchCompleteRunnable to a worklet thread.");
+ }
+}
+
+//////////////////////////////////////////////////////////////
+// WorkletScriptHandler
+//////////////////////////////////////////////////////////////
+NS_IMPL_ISUPPORTS(WorkletScriptHandler, nsIStreamLoaderObserver)
+
+WorkletScriptHandler::WorkletScriptHandler(Worklet* aWorklet, nsIURI* aURI)
+ : mWorklet(aWorklet), mURI(aURI) {}
+
+void WorkletScriptHandler::ResolvedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aValue.isObject()) {
+ HandleFailure(NS_ERROR_FAILURE);
+ return;
+ }
+
+ RefPtr<Response> response;
+ nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ HandleFailure(NS_ERROR_FAILURE);
+ return;
+ }
+
+ // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule
+ // Step 6.4.1. If script is null, then:
+ // Step 1.1.2. Reject promise with an "AbortError" DOMException.
+ if (!response->Ok()) {
+ HandleFailure(NS_ERROR_DOM_ABORT_ERR);
+ return;
+ }
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ response->GetBody(getter_AddRefs(inputStream));
+ if (!inputStream) {
+ HandleFailure(NS_ERROR_DOM_NETWORK_ERR);
+ return;
+ }
+
+ nsCOMPtr<nsIInputStreamPump> pump;
+ rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream.forget());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ HandleFailure(rv);
+ return;
+ }
+
+ nsCOMPtr<nsIStreamLoader> loader;
+ rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ HandleFailure(rv);
+ return;
+ }
+
+ rv = pump->AsyncRead(loader);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ HandleFailure(rv);
+ return;
+ }
+
+ nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
+ if (rr) {
+ nsCOMPtr<nsIEventTarget> sts =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+ RefPtr<TaskQueue> queue = TaskQueue::Create(
+ sts.forget(), "WorkletScriptHandler STS Delivery Queue");
+ rv = rr->RetargetDeliveryTo(queue);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
+ }
+ }
+}
+
+NS_IMETHODIMP WorkletScriptHandler::OnStreamComplete(nsIStreamLoader* aLoader,
+ nsISupports* aContext,
+ nsresult aStatus,
+ uint32_t aStringLen,
+ const uint8_t* aString) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (NS_FAILED(aStatus)) {
+ HandleFailure(aStatus);
+ return NS_OK;
+ }
+
+ // Copy the buffer and decode it on worklet thread, as we can only access
+ // ModuleLoadRequest on worklet thread.
+ UniquePtr<uint8_t[]> scriptTextBuf = MakeUnique<uint8_t[]>(aStringLen);
+ memcpy(scriptTextBuf.get(), aString, aStringLen);
+
+ nsCOMPtr<nsIRunnable> runnable = new FetchCompleteRunnable(
+ mWorklet->mImpl, mURI, NS_OK, std::move(scriptTextBuf), aStringLen);
+
+ if (NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget()))) {
+ HandleFailure(NS_ERROR_FAILURE);
+ }
+
+ return NS_OK;
+}
+
+void WorkletScriptHandler::RejectedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // https://html.spec.whatwg.org/multipage/worklets.html#dom-worklet-addmodule
+ // Step 6.4.1. If script is null, then:
+ // Step 1.1.2. Reject promise with an "AbortError" DOMException.
+ HandleFailure(NS_ERROR_DOM_ABORT_ERR);
+}
+
+void WorkletScriptHandler::HandleFailure(nsresult aResult) {
+ DispatchFetchCompleteToWorklet(aResult);
+}
+
+void WorkletScriptHandler::DispatchFetchCompleteToWorklet(nsresult aRv) {
+ nsCOMPtr<nsIRunnable> runnable =
+ new FetchCompleteRunnable(mWorklet->mImpl, mURI, aRv, nullptr, 0);
+
+ if (NS_WARN_IF(
+ NS_FAILED(mWorklet->mImpl->SendControlMessage(runnable.forget())))) {
+ NS_WARNING("Failed to dispatch FetchCompleteRunnable to a worklet thread.");
+ }
+}
+
+} // namespace mozilla::dom