diff --git a/Repository.mk b/Repository.mk
index 97e3e5eb25f8..3fc103ba0757 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -443,6 +443,7 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo, \
localebe1 \
log \
lng \
+ $(call gb_Helper_optional,SCRIPTING,$(if $(ENABLE_QUICKJS),jsprov)) \
$(if $(filter $(OS),MACOSX),macbe1) \
$(if $(MERGELIBS),merged) \
migrationoo2 \
diff --git a/scripting/Library_jsprov.mk b/scripting/Library_jsprov.mk
new file mode 100644
index 000000000000..6a49315972c2
--- /dev/null
+++ b/scripting/Library_jsprov.mk
@@ -0,0 +1,34 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,jsprov))
+
+$(eval $(call gb_Library_set_componentfile,jsprov,scripting/source/jsprov/jsprov,services))
+
+$(eval $(call gb_Library_use_sdk_api,jsprov))
+
+$(eval $(call gb_Library_set_include,jsprov,\
+ $$(INCLUDE) \
+ -I$(SRCDIR)/scripting/source/inc \
+))
+
+$(eval $(call gb_Library_use_libraries,jsprov,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ jsuno \
+ sal \
+ singleprov \
+))
+
+$(eval $(call gb_Library_add_exception_objects,jsprov,\
+ scripting/source/jsprov/loader \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/scripting/Module_scripting.mk b/scripting/Module_scripting.mk
index 2c52f6656b03..aad81a0e2fdc 100644
--- a/scripting/Module_scripting.mk
+++ b/scripting/Module_scripting.mk
@@ -37,6 +37,7 @@ $(eval $(call gb_Module_add_targets,scripting,\
Package_ScriptsPython \
Pyuno_mailmerge \
) \
+ $(if $(ENABLE_QUICKJS),Library_jsprov) \
Library_basprov \
Library_dlgprov \
Library_protocolhandler \
diff --git a/scripting/README.md b/scripting/README.md
index 0bfe4d184572..988d1484c3ac 100644
--- a/scripting/README.md
+++ b/scripting/README.md
@@ -41,6 +41,10 @@ C++ implementation of the `LanguageScriptProvider` UNO service for Basic
C++ implementation of the `DialogProvider` UNO service used for loading
UNO dialogs from various languages
+- `source/jsprov`
+
+C++ implementation of the `LanguageScriptProvider` UNO service for JavaScript using QuickJS.
+
- `source/pyprov`
`LanguageScriptProvider` for Python
diff --git a/scripting/source/jsprov/jsprov.component b/scripting/source/jsprov/jsprov.component
new file mode 100644
index 000000000000..893b5cf55e31
--- /dev/null
+++ b/scripting/source/jsprov/jsprov.component
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
diff --git a/scripting/source/jsprov/loader.cxx b/scripting/source/jsprov/loader.cxx
new file mode 100644
index 000000000000..e012351cc1f6
--- /dev/null
+++ b/scripting/source/jsprov/loader.cxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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
+#include
+#include
+
+#include
+
+#include
+#include
+
+namespace
+{
+class ScriptWrapper : public cppu::WeakImplHelper
+{
+public:
+ ScriptWrapper(const OUString& sScript)
+ : m_sScript(sScript)
+ {
+ }
+
+ // XScript
+ css::uno::Any SAL_CALL invoke(const css::uno::Sequence& aParams,
+ css::uno::Sequence& aOutParamIndex,
+ css::uno::Sequence& aOutParam) override;
+
+private:
+ OUString m_sScript;
+};
+
+class JsProvScriptFactory : public singleprovider::SingleScriptFactory
+{
+ OUString getLanguageName() const override;
+ OUString getExtension() const override;
+ css::uno::Reference
+ getScript(css::uno::Reference xContext, const OUString& sName,
+ std::string_view sSource) const override;
+ bool appMightExecute(const OUString& sAppName) const override;
+ OUString getExampleMacro() const override;
+};
+
+OUString JsProvScriptFactory::getLanguageName() const { return "JavaScript"; }
+
+OUString JsProvScriptFactory::getExtension() const { return ".js"; }
+
+css::uno::Reference
+JsProvScriptFactory::getScript(css::uno::Reference, const OUString&,
+ std::string_view sSource) const
+{
+ return new ScriptWrapper(OStringToOUString(sSource, RTL_TEXTENCODING_UTF8));
+}
+
+bool JsProvScriptFactory::appMightExecute(const OUString& sAppName) const
+{
+ return sAppName.startsWithIgnoreAsciiCase("js.") || sAppName.startsWithIgnoreAsciiCase("node.");
+}
+
+OUString JsProvScriptFactory::getExampleMacro() const
+{
+ return u"let context = uno.componentContext;\n"
+ "let desktop = uno.idl.com.sun.star.frame.theDesktop(context);\n"
+ "let frame = desktop.getCurrentFrame();\n"
+ "let controller = frame.getController();\n"
+ "let model = controller.getModel();\n"
+ "let text = model.getText();\n"
+ "\n"
+ "text.getEnd().setString(\"Hello, world!\");\n"_ustr;
+}
+
+css::uno::Any SAL_CALL ScriptWrapper::invoke(const css::uno::Sequence&,
+ css::uno::Sequence&,
+ css::uno::Sequence&)
+{
+ jsuno::execute(m_sScript);
+
+ return css::uno::Any();
+}
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+scripting_JavaScriptProviderImpl_get_implementation(css::uno::XComponentContext* pContext,
+ css::uno::Sequence const&)
+{
+ css::uno::Reference xProvider
+ = singleprovider::ScriptProvider::create(pContext, std::make_shared());
+ xProvider->acquire();
+ return xProvider.get();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */