diff options
author | Stephan Bergmann <stephan.bergmann@allotropia.de> | 2024-08-14 16:54:49 +0200 |
---|---|---|
committer | Stephan Bergmann <stephan.bergmann@allotropia.de> | 2024-08-14 21:57:00 +0200 |
commit | cccc983eb3f0532dbf7e3edf4477ce63ec3d2795 (patch) | |
tree | cba9fe3b1bd9277fba60173199ffb43cdaf5a365 | |
parent | d193bf5cec65ab55d5a76b3a4f68eb52e5a747f3 (diff) |
Emscripten: Run external code on LO's main thread
...and not the browser's main thread. Those are different threads now since
6e6451ce96f47e0ef5e8ecf1750f394ff3fb48e4 "Emscripten: Move the Qt event loop off
the JS main thread". Running `Module.uno_init.then` on the browser's main
thread would never trigger, as that promise is only resolved on LO's main
thread.
When external code was included in soffice.js via
EMSCRIPTEN_EXTRA_SOFFICE_POST_JS, that didn't make a difference: Emscripten
effectively replicates that code to all the worker threads, so whatever worker
thread resolved the Module.uno_init promise (i.e., the LO main thread) had the
external code available and ran it. But when external code was included
directly in some HTML file (which "manually" provides a canvas and loads
soffice.js, not relying on the qt_soffice.html convenience functionality), it
was available in the browser's main thread but not in LO's main thread. For
that use case, introduce a new Module.uno_scripts array that can be set up in
the HTML file to contain an array of URLs (represented as strings) of scripts to
load into LO's main thread.
(Alternatively, running external code on the browser's main thread rather than
on LO's main thread could be more ideal in the sense that the external code
would then have access to the browser's document object. But, for one, it would
be less ideal in the sense that we would then potentially again execute LO code
(which we call into from the external code via UNO) on the browser's main
thread, which would bring back the issues that
6e6451ce96f47e0ef5e8ecf1750f394ff3fb48e4 "Emscripten: Move the Qt event loop off
the JS main thread" solved. And, for another, when I experimentally tried it
out, it caused massive Qt threading issues, so I quickly gave up again.)
Change-Id: If01a7859751706f168e93ccac75758ae5ce17cd2
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171870
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
-rw-r--r-- | desktop/source/app/appinit.cxx | 51 | ||||
-rw-r--r-- | static/README.wasm.md | 14 |
2 files changed, 62 insertions, 3 deletions
diff --git a/desktop/source/app/appinit.cxx b/desktop/source/app/appinit.cxx index f2b7edba1c8e..6f8bffa1146a 100644 --- a/desktop/source/app/appinit.cxx +++ b/desktop/source/app/appinit.cxx @@ -19,6 +19,10 @@ #include <algorithm> +#include <cassert> +#include <cstdint> +#include <string> +#include <vector> #include <app.hxx> #include <dp_shared.hxx> @@ -46,6 +50,8 @@ #if defined EMSCRIPTEN #include <emscripten.h> +#include <emscripten/threading.h> +#include <emscripten/val.h> #include <bindings_uno.hxx> #endif @@ -53,6 +59,48 @@ using namespace ::com::sun::star::uno; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::ucb; +#if defined EMSCRIPTEN + +namespace { + +extern "C" void getUnoScriptUrls(std::vector<std::u16string> * urls) { + assert(urls != nullptr); + auto const val = emscripten::val::module_property("uno_scripts"); + if (!val.isUndefined()) { + auto const len = val["length"].as<std::uint32_t>(); + for (std::uint32_t i = 0; i != len; ++i) { + urls->push_back(val[i].as<std::u16string>()); + } + } +} + +EM_JS(void, runUnoScriptUrl, (char16_t const * url), { + fetch(UTF16ToString(url)).then(res => { + if (!res.ok) { + throw Error( + "Loading <" + res.url + "> failed with " + res.status + " " + res.statusText); + } + return res.blob(); + }).then(blob => blob.text()).then(text => eval(text)); +}); + +void initUno() { + init_unoembind_uno(); + EM_ASM(Module.uno_init$resolve();); + std::vector<std::u16string> urls; + emscripten_sync_run_in_main_runtime_thread(EM_FUNC_SIG_VI, getUnoScriptUrls, &urls); + for (auto const & url: urls) { + if (url.find('\0') != std::u16string::npos) { + throw std::invalid_argument("Module.uno_scripts element contains embedded NUL"); + } + runUnoScriptUrl(url.c_str()); + } +} + +} + +#endif + namespace desktop { @@ -86,8 +134,7 @@ void Desktop::InitApplicationServiceManager() #endif comphelper::setProcessServiceFactory(sm); #if defined EMSCRIPTEN - init_unoembind_uno(); - EM_ASM(Module.uno_init$resolve();); + initUno(); #endif } diff --git a/static/README.wasm.md b/static/README.wasm.md index 84635d09390d..c69129303aa5 100644 --- a/static/README.wasm.md +++ b/static/README.wasm.md @@ -241,7 +241,19 @@ Module.uno_init.then(function() { }); ``` - +If you enter the above examples into the browser console, you need to enter them into the console of +the first soffice.worker.js thread, which is the LO main thread since we use -sPROXY_TO_PTHREAD, not +into the console of the browser's main thread. + +Alternatively, you can do the following: Put an example into some file like `example.js` that you +put next to the `qt_soffice.html` that you serve to the browser (i.e., in +`workdir/installation/LibreOffice/emscripten/`). Create another small JS snippet file like +`include.js` (which is only needed during the build) containing +``` +Module.uno_scripts = ['./example.js']; +``` +And rebuild LO configured with an additional +`EMSCRIPTEN_EXTRA_SOFFICE_POST_JS=/...path-to.../include.js`. ## Tools for problem diagnosis |