diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/worklet/loader | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/worklet/loader')
-rw-r--r-- | dom/worklet/loader/WorkletModuleLoader.cpp | 297 | ||||
-rw-r--r-- | dom/worklet/loader/WorkletModuleLoader.h | 119 | ||||
-rw-r--r-- | dom/worklet/loader/moz.build | 20 |
3 files changed, 436 insertions, 0 deletions
diff --git a/dom/worklet/loader/WorkletModuleLoader.cpp b/dom/worklet/loader/WorkletModuleLoader.cpp new file mode 100644 index 0000000000..34353107f8 --- /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, + GetCurrentSerialEventTarget()) { + // 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->mFetchOptions, SRIMetadata(), referrer, loadContext, + false, /* is top level */ + false, /* is dynamic import */ + this, aParent->mVisitedSet, aParent->GetRootModule()); + + request->mURL = request->mURI->GetSpecOrDefault(); + return request.forget(); +} + +already_AddRefed<ModuleLoadRequest> WorkletModuleLoader::CreateDynamicImport( + JSContext* aCx, nsIURI* aURI, LoadedScript* aMaybeActiveScript, + JS::Handle<JS::Value> aReferencingPrivate, 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); + 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..818720ced8 --- /dev/null +++ b/dom/worklet/loader/WorkletModuleLoader.h @@ -0,0 +1,119 @@ +/* -*- 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<JS::Value> aReferencingPrivate, + 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" |