summaryrefslogtreecommitdiffstats
path: root/dom/worklet/loader
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/worklet/loader
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/worklet/loader')
-rw-r--r--dom/worklet/loader/WorkletModuleLoader.cpp297
-rw-r--r--dom/worklet/loader/WorkletModuleLoader.h118
-rw-r--r--dom/worklet/loader/moz.build20
3 files changed, 435 insertions, 0 deletions
diff --git a/dom/worklet/loader/WorkletModuleLoader.cpp b/dom/worklet/loader/WorkletModuleLoader.cpp
new file mode 100644
index 0000000000..59d2405398
--- /dev/null
+++ b/dom/worklet/loader/WorkletModuleLoader.cpp
@@ -0,0 +1,297 @@
+/* -*- 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 "WorkletModuleLoader.h"
+
+#include "js/CompileOptions.h" // JS::InstantiateOptions
+#include "js/experimental/JSStencil.h" // JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil
+#include "js/loader/ModuleLoadRequest.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/StructuredCloneHolder.h"
+#include "mozilla/dom/Worklet.h"
+#include "mozilla/dom/WorkletFetchHandler.h"
+#include "nsStringBundle.h"
+
+using JS::loader::ModuleLoadRequest;
+using JS::loader::ResolveError;
+
+namespace mozilla::dom::loader {
+
+//////////////////////////////////////////////////////////////
+// WorkletScriptLoader
+//////////////////////////////////////////////////////////////
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletScriptLoader)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(WorkletScriptLoader)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkletScriptLoader)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkletScriptLoader)
+
+//////////////////////////////////////////////////////////////
+// WorkletModuleLoader
+//////////////////////////////////////////////////////////////
+
+NS_IMPL_ADDREF_INHERITED(WorkletModuleLoader, ModuleLoaderBase)
+NS_IMPL_RELEASE_INHERITED(WorkletModuleLoader, ModuleLoaderBase)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(WorkletModuleLoader, ModuleLoaderBase,
+ mFetchingRequests)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletModuleLoader)
+NS_INTERFACE_MAP_END_INHERITING(ModuleLoaderBase)
+
+WorkletModuleLoader::WorkletModuleLoader(WorkletScriptLoader* aScriptLoader,
+ nsIGlobalObject* aGlobalObject)
+ : ModuleLoaderBase(aScriptLoader, aGlobalObject) {
+ // This should be constructed on a worklet thread.
+ MOZ_ASSERT(!NS_IsMainThread());
+}
+
+already_AddRefed<ModuleLoadRequest> WorkletModuleLoader::CreateStaticImport(
+ nsIURI* aURI, ModuleLoadRequest* aParent) {
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& handlerRef =
+ aParent->GetWorkletLoadContext()->GetHandlerRef();
+ RefPtr<WorkletLoadContext> loadContext = new WorkletLoadContext(handlerRef);
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-the-descendants-of-a-module-script
+ // Step 11. Perform the internal module script graph fetching procedure
+ //
+ // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
+ // Step 5. Fetch a single module script with referrer is referringScript's
+ // base URL,
+ nsIURI* referrer = aParent->mURI;
+ RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
+ aURI, aParent->ReferrerPolicy(), aParent->mFetchOptions, SRIMetadata(),
+ referrer, loadContext, false, /* is top level */
+ false, /* is dynamic import */
+ this, aParent->mVisitedSet, aParent->GetRootModule());
+
+ request->mURL = request->mURI->GetSpecOrDefault();
+ request->NoCacheEntryFound();
+ return request.forget();
+}
+
+already_AddRefed<ModuleLoadRequest> WorkletModuleLoader::CreateDynamicImport(
+ JSContext* aCx, nsIURI* aURI, LoadedScript* aMaybeActiveScript,
+ JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise) {
+ return nullptr;
+}
+
+bool WorkletModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest,
+ nsresult* aRvOut) {
+ return true;
+}
+
+nsresult WorkletModuleLoader::StartFetch(ModuleLoadRequest* aRequest) {
+ InsertRequest(aRequest->mURI, aRequest);
+
+ RefPtr<StartFetchRunnable> runnable =
+ new StartFetchRunnable(aRequest->GetWorkletLoadContext()->GetHandlerRef(),
+ aRequest->mURI, aRequest->mReferrer);
+ NS_DispatchToMainThread(runnable.forget());
+ return NS_OK;
+}
+
+nsresult WorkletModuleLoader::CompileFetchedModule(
+ JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::CompileOptions& aOptions,
+ ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleScript) {
+ RefPtr<JS::Stencil> stencil;
+ 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::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));
+ return aModuleScript ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// AddModuleResultRunnable is a Runnable which will notify the result of
+// Worklet::AddModule on the main thread.
+class AddModuleResultRunnable final : public Runnable {
+ public:
+ explicit AddModuleResultRunnable(
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef,
+ bool aSucceeded)
+ : Runnable("Worklet::AddModuleResultRunnable"),
+ mHandlerRef(aHandlerRef),
+ mSucceeded(aSucceeded) {
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+ ~AddModuleResultRunnable() = default;
+
+ NS_IMETHOD
+ Run() override;
+
+ private:
+ nsMainThreadPtrHandle<WorkletFetchHandler> mHandlerRef;
+ bool mSucceeded;
+};
+
+NS_IMETHODIMP
+AddModuleResultRunnable::Run() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mSucceeded) {
+ mHandlerRef->ExecutionSucceeded();
+ } else {
+ mHandlerRef->ExecutionFailed();
+ }
+
+ return NS_OK;
+}
+
+class AddModuleThrowErrorRunnable final : public Runnable,
+ public StructuredCloneHolder {
+ public:
+ explicit AddModuleThrowErrorRunnable(
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef)
+ : Runnable("Worklet::AddModuleThrowErrorRunnable"),
+ StructuredCloneHolder(CloningSupported, TransferringNotSupported,
+ StructuredCloneScope::SameProcess),
+ mHandlerRef(aHandlerRef) {
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+ ~AddModuleThrowErrorRunnable() = default;
+
+ NS_IMETHOD
+ Run() override;
+
+ private:
+ nsMainThreadPtrHandle<WorkletFetchHandler> mHandlerRef;
+};
+
+NS_IMETHODIMP
+AddModuleThrowErrorRunnable::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))) {
+ mHandlerRef->ExecutionFailed();
+ return NS_ERROR_FAILURE;
+ }
+
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JS::Value> error(cx);
+ ErrorResult result;
+ Read(global, cx, &error, result);
+ Unused << NS_WARN_IF(result.Failed());
+ mHandlerRef->ExecutionFailed(error);
+
+ return NS_OK;
+}
+
+void WorkletModuleLoader::OnModuleLoadComplete(ModuleLoadRequest* aRequest) {
+ if (!aRequest->IsTopLevel()) {
+ return;
+ }
+
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& handlerRef =
+ aRequest->GetWorkletLoadContext()->GetHandlerRef();
+
+ auto addModuleFailed = MakeScopeExit([&] {
+ RefPtr<AddModuleResultRunnable> runnable =
+ new AddModuleResultRunnable(handlerRef, false);
+ NS_DispatchToMainThread(runnable.forget());
+ });
+
+ if (!aRequest->mModuleScript) {
+ return;
+ }
+
+ if (!aRequest->InstantiateModuleGraph()) {
+ return;
+ }
+
+ nsresult rv = aRequest->EvaluateModule();
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ bool hasError = aRequest->mModuleScript->HasErrorToRethrow();
+ if (hasError) {
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
+ return;
+ }
+
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JS::Value> error(cx, aRequest->mModuleScript->ErrorToRethrow());
+ RefPtr<AddModuleThrowErrorRunnable> runnable =
+ new AddModuleThrowErrorRunnable(handlerRef);
+ ErrorResult result;
+ runnable->Write(cx, error, result);
+ if (NS_WARN_IF(result.Failed())) {
+ return;
+ }
+
+ addModuleFailed.release();
+ NS_DispatchToMainThread(runnable.forget());
+ return;
+ }
+
+ addModuleFailed.release();
+ RefPtr<AddModuleResultRunnable> runnable =
+ new AddModuleResultRunnable(handlerRef, true);
+ NS_DispatchToMainThread(runnable.forget());
+}
+
+// TODO: Bug 1808301: Call FormatLocalizedString from a worklet thread.
+nsresult WorkletModuleLoader::GetResolveFailureMessage(
+ ResolveError aError, const nsAString& aSpecifier, nsAString& aResult) {
+ uint8_t index = static_cast<uint8_t>(aError);
+ MOZ_ASSERT(index < static_cast<uint8_t>(ResolveError::Length));
+ MOZ_ASSERT(mLocalizedStrs);
+ MOZ_ASSERT(!mLocalizedStrs->IsEmpty());
+ if (!mLocalizedStrs || NS_WARN_IF(mLocalizedStrs->IsEmpty())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const nsString& localizedStr = mLocalizedStrs->ElementAt(index);
+
+ AutoTArray<nsString, 1> params;
+ params.AppendElement(aSpecifier);
+
+ nsStringBundleBase::FormatString(localizedStr.get(), params, aResult);
+ return NS_OK;
+}
+
+void WorkletModuleLoader::InsertRequest(nsIURI* aURI,
+ ModuleLoadRequest* aRequest) {
+ mFetchingRequests.InsertOrUpdate(aURI, aRequest);
+}
+
+void WorkletModuleLoader::RemoveRequest(nsIURI* aURI) {
+ MOZ_ASSERT(mFetchingRequests.Remove(aURI));
+}
+
+ModuleLoadRequest* WorkletModuleLoader::GetRequest(nsIURI* aURI) const {
+ RefPtr<ModuleLoadRequest> req;
+ MOZ_ALWAYS_TRUE(mFetchingRequests.Get(aURI, getter_AddRefs(req)));
+ return req;
+}
+
+} // namespace mozilla::dom::loader
diff --git a/dom/worklet/loader/WorkletModuleLoader.h b/dom/worklet/loader/WorkletModuleLoader.h
new file mode 100644
index 0000000000..3fb2a59231
--- /dev/null
+++ b/dom/worklet/loader/WorkletModuleLoader.h
@@ -0,0 +1,118 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_worklet_WorkletModuleLoader_h
+#define mozilla_dom_worklet_WorkletModuleLoader_h
+
+#include "js/loader/LoadContextBase.h"
+#include "js/loader/ModuleLoaderBase.h"
+#include "js/loader/ResolveResult.h" // For ResolveError
+#include "mozilla/dom/WorkletFetchHandler.h"
+
+namespace mozilla::dom {
+namespace loader {
+class WorkletScriptLoader : public JS::loader::ScriptLoaderInterface {
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(WorkletScriptLoader)
+
+ nsIURI* GetBaseURI() const override { return nullptr; }
+
+ void ReportErrorToConsole(ScriptLoadRequest* aRequest,
+ nsresult aResult) const override {}
+
+ void ReportWarningToConsole(
+ ScriptLoadRequest* aRequest, const char* aMessageName,
+ const nsTArray<nsString>& aParams) const override {}
+
+ nsresult FillCompileOptionsForRequest(
+ JSContext* cx, ScriptLoadRequest* aRequest, JS::CompileOptions* aOptions,
+ JS::MutableHandle<JSScript*> aIntroductionScript) override {
+ aOptions->setIntroductionType("Worklet");
+ aOptions->setFileAndLine(aRequest->mURL.get(), 1);
+ aOptions->setIsRunOnce(true);
+ aOptions->setNoScriptRval(true);
+ return NS_OK;
+ }
+
+ private:
+ ~WorkletScriptLoader() = default;
+};
+
+class WorkletModuleLoader : public JS::loader::ModuleLoaderBase {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WorkletModuleLoader,
+ JS::loader::ModuleLoaderBase)
+
+ WorkletModuleLoader(WorkletScriptLoader* aScriptLoader,
+ nsIGlobalObject* aGlobalObject);
+
+ void InsertRequest(nsIURI* aURI, JS::loader::ModuleLoadRequest* aRequest);
+ void RemoveRequest(nsIURI* aURI);
+ JS::loader::ModuleLoadRequest* GetRequest(nsIURI* aURI) const;
+
+ bool HasSetLocalizedStrings() const { return (bool)mLocalizedStrs; }
+ void SetLocalizedStrings(const nsTArray<nsString>* aStrings) {
+ mLocalizedStrs = aStrings;
+ }
+
+ private:
+ ~WorkletModuleLoader() = default;
+
+ already_AddRefed<JS::loader::ModuleLoadRequest> CreateStaticImport(
+ nsIURI* aURI, JS::loader::ModuleLoadRequest* aParent) override;
+
+ already_AddRefed<JS::loader::ModuleLoadRequest> CreateDynamicImport(
+ JSContext* aCx, nsIURI* aURI, LoadedScript* aMaybeActiveScript,
+ JS::Handle<JSString*> aSpecifier,
+ JS::Handle<JSObject*> aPromise) override;
+
+ bool CanStartLoad(JS::loader::ModuleLoadRequest* aRequest,
+ nsresult* aRvOut) override;
+
+ nsresult StartFetch(JS::loader::ModuleLoadRequest* aRequest) override;
+
+ nsresult CompileFetchedModule(
+ JSContext* aCx, JS::Handle<JSObject*> aGlobal,
+ JS::CompileOptions& aOptions, JS::loader::ModuleLoadRequest* aRequest,
+ JS::MutableHandle<JSObject*> aModuleScript) override;
+
+ void OnModuleLoadComplete(JS::loader::ModuleLoadRequest* aRequest) override;
+
+ nsresult GetResolveFailureMessage(JS::loader::ResolveError aError,
+ const nsAString& aSpecifier,
+ nsAString& aResult) override;
+
+ // A hashtable to map a nsIURI(from main thread) to a ModuleLoadRequest(in
+ // worklet thread).
+ nsRefPtrHashtable<nsURIHashKey, JS::loader::ModuleLoadRequest>
+ mFetchingRequests;
+
+ // We get the localized strings on the main thread, and pass it to
+ // WorkletModuleLoader.
+ const nsTArray<nsString>* mLocalizedStrs = nullptr;
+};
+} // namespace loader
+
+class WorkletLoadContext : public JS::loader::LoadContextBase {
+ public:
+ explicit WorkletLoadContext(
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& aHandlerRef)
+ : JS::loader::LoadContextBase(JS::loader::ContextKind::Worklet),
+ mHandlerRef(aHandlerRef) {}
+
+ const nsMainThreadPtrHandle<WorkletFetchHandler>& GetHandlerRef() const {
+ return mHandlerRef;
+ }
+
+ private:
+ ~WorkletLoadContext() = default;
+
+ nsMainThreadPtrHandle<WorkletFetchHandler> mHandlerRef;
+};
+} // namespace mozilla::dom
+#endif // mozilla_dom_worklet_WorkletModuleLoader_h
diff --git a/dom/worklet/loader/moz.build b/dom/worklet/loader/moz.build
new file mode 100644
index 0000000000..38707a81cc
--- /dev/null
+++ b/dom/worklet/loader/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+EXPORTS.mozilla.dom.worklet += [
+ "WorkletModuleLoader.h",
+]
+
+UNIFIED_SOURCES += [
+ "WorkletModuleLoader.cpp",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"