wasm: don't enable specialHTMLTargets by default

Adding specialHTMLTargets to EXPORTED_RUNTIME_METHODS carries the
obligation to actually use it as well; failing to do so makes Emscripten
stop with a reference error on startup.

However, we can't guarantee that Qt will use it in all cases. The
current usage depends on QGuiApplication being used. Application code
could be using QCoreApplication, or no application object at all.

Detect if specialHTMLTargets is present instead, and then enable the code
paths which uses it if that's the case. This means that apps which want
to use e.g. multiple browser windows can opt into support by making sure
EXPORTED_RUNTIME_METHODS contains specialHTMLTargets.

Change-Id: I81105aa01946602fcf593f170e305d7dc9bad3be
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
Morten Sørvig 2022-05-10 09:36:59 +02:00
parent cfa44787de
commit 86103f3af5
5 changed files with 42 additions and 13 deletions

View File

@ -4,7 +4,7 @@ function (qt_internal_setup_wasm_target_properties wasmTarget)
target_link_options("${wasmTarget}" INTERFACE
"SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=1"
"SHELL:-s EXPORTED_RUNTIME_METHODS=[UTF16ToString,stringToUTF16,specialHTMLTargets]"
"SHELL:-s EXPORTED_RUNTIME_METHODS=[UTF16ToString,stringToUTF16]"
"SHELL:-s USE_WEBGL2=1"
"--bind"
"SHELL:-s FETCH=1"

View File

@ -34,7 +34,7 @@ EMCC_COMMON_LFLAGS += \
-s FULL_ES3=1 \
-s USE_WEBGL2=1 \
-s ERROR_ON_UNDEFINED_SYMBOLS=1 \
-s EXPORTED_RUNTIME_METHODS=[UTF16ToString,stringToUTF16,specialHTMLTargets] \
-s EXPORTED_RUNTIME_METHODS=[UTF16ToString,stringToUTF16] \
--bind \
-s FETCH=1 \
-s MODULARIZE=1 \

View File

@ -11,6 +11,7 @@
#include "emscripten.h"
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
QT_BEGIN_NAMESPACE

View File

@ -78,9 +78,14 @@ QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas)
// apps/ages only, since Emscripten uses the main document to look up the element.
// As a workaround for this, Emscripten supports registering custom mappings in the
// "specialHTMLTargets" object. Add a mapping for the canvas for this screen.
emscripten::val specialHtmlTargets = emscripten::val::module_property("specialHTMLTargets");
std::string id = std::string("!qtcanvas_") + std::to_string(uint32_t(this));
specialHtmlTargets.set(id, m_canvas);
//
// This functionality is gated on "specialHTMLTargets" being available as a module
// property. One way to ensure this is the case is to add it to EXPORTED_RUNTIME_METHODS.
// Qt does not currently do this by default since if added it _must_ be used in order
// to avoid an undefined reference error at startup, and there are cases when Qt won't use
// it, for example if QGuiApplication is not usded.
if (hasSpecialHtmlTargets())
emscripten::val::module_property("specialHTMLTargets").set(canvasSpecialHtmlTargetId(), m_canvas);
// Install event handlers on the container/canvas. This must be
// done after the canvas has been created above.
@ -97,9 +102,9 @@ QWasmScreen::~QWasmScreen()
// event handlers.
m_compositor = nullptr;
emscripten::val specialHtmlTargets = emscripten::val::module_property("specialHTMLTargets");
std::string id = std::string("!qtcanvas_") + std::to_string(uint32_t(this));
specialHtmlTargets.set(id, emscripten::val::undefined());
if (hasSpecialHtmlTargets())
emscripten::val::module_property("specialHTMLTargets")
.set(canvasSpecialHtmlTargetId(), emscripten::val::undefined());
m_canvas.set(m_canvasResizeObserverCallbackContextPropertyName, emscripten::val(intptr_t(0)));
}
@ -146,13 +151,33 @@ QString QWasmScreen::canvasId() const
return QWasmString::toQString(m_canvas["id"]);
}
// Returns the canvas _target_ id, for use with Emscripten's
// event registration functions. The target id is a globally
// unique id, unlike the html element id which is only unique
// within one html document. See specialHtmlTargets.
// Returns the canvas _target_ id, for use with Emscripten's event registration
// functions. This either based on the id registered in specialHtmlTargets, or
// on the canvas id.
QString QWasmScreen::canvasTargetId() const
{
return QStringLiteral("!qtcanvas_") + QString::number(int32_t(this));
if (hasSpecialHtmlTargets())
return QString::fromStdString(canvasSpecialHtmlTargetId());
else
return QStringLiteral("#") + canvasId();
}
std::string QWasmScreen::canvasSpecialHtmlTargetId() const
{
// Return a globally unique id for the canvas. We can choose any string,
// as long as it starts with a "!".
return std::string("!qtcanvas_") + std::to_string(uint32_t(this));
}
bool QWasmScreen::hasSpecialHtmlTargets() const
{
static bool gotIt = []{
// specialHTMLTargets is a JavaScript Array if available. Note that it is
// an abort() function if not, so a simple isUndefined() test wont't work here.
return emscripten::val::module_property("specialHTMLTargets")
["constructor"]["name"].as<std::string>() == std::string("Array");
}();
return gotIt;
}
QRect QWasmScreen::geometry() const

View File

@ -62,6 +62,9 @@ public slots:
void setGeometry(const QRect &rect);
private:
std::string canvasSpecialHtmlTargetId() const;
bool hasSpecialHtmlTargets() const;
emscripten::val m_container;
emscripten::val m_canvas;
std::unique_ptr<QWasmCompositor> m_compositor;