From c56bd31f23cdb67545775e7cd8be4264fa85d59a Mon Sep 17 00:00:00 2001 From: Mikolaj Boc Date: Mon, 14 Nov 2022 15:23:22 +0100 Subject: [PATCH] Always export and link in JSEvents and specialHTMLTargets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two symbols are linked in & exported by using a publicly visible symbol emscripten_set_wheel_callback. To actually link it, but avoid calling it, a volatile false boolean flag is used in a guarding if. This should ideally be done by setting DEFAULT_LIBRARY_FUNCS_TO_INCLUDE, but a cmake bug (see QTBUG-108444) is preventing this. Fixes: QTBUG-108423 Task-number: QTBUG-108444 Change-Id: I6b0406d1059dcec63b3942468e210c53292ffe90 Reviewed-by: Morten Johan Sørvig --- src/corelib/Qt6WasmMacros.cmake | 2 +- src/corelib/platform/wasm/qstdweb.cpp | 16 ++++ src/plugins/platforms/wasm/qwasmscreen.cpp | 88 ++-------------------- src/plugins/platforms/wasm/qwasmscreen.h | 3 - 4 files changed, 25 insertions(+), 84 deletions(-) diff --git a/src/corelib/Qt6WasmMacros.cmake b/src/corelib/Qt6WasmMacros.cmake index 5e9d817536..8274a4216e 100644 --- a/src/corelib/Qt6WasmMacros.cmake +++ b/src/corelib/Qt6WasmMacros.cmake @@ -86,7 +86,7 @@ endfunction() function(_qt_internal_add_wasm_extra_exported_methods target) get_target_property(wasm_extra_exported_methods "${target}" QT_WASM_EXTRA_EXPORTED_METHODS) - set(wasm_default_exported_methods "UTF16ToString,stringToUTF16,JSEvents") + set(wasm_default_exported_methods "UTF16ToString,stringToUTF16,JSEvents,specialHTMLTargets") if(NOT wasm_extra_exported_methods) set(wasm_extra_exported_methods ${QT_WASM_EXTRA_EXPORTED_METHODS}) diff --git a/src/corelib/platform/wasm/qstdweb.cpp b/src/corelib/platform/wasm/qstdweb.cpp index f95807ae03..59d8298bb6 100644 --- a/src/corelib/platform/wasm/qstdweb.cpp +++ b/src/corelib/platform/wasm/qstdweb.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -16,6 +17,21 @@ QT_BEGIN_NAMESPACE namespace qstdweb { +static void usePotentialyUnusedSymbols() +{ + // Using this adds a reference on JSEvents and specialHTMLTargets which are always exported. + // This hack is needed as it is currently impossible to specify a dollar sign in + // target_link_options. The following is impossible: + // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=$JSEvents + // TODO(mikolajboc): QTBUG-108444, review this when cmake gets fixed. + // Volatile is to make this unoptimizable, so that the function is referenced, but is not + // called at runtime. + volatile bool doIt = false; + if (doIt) + emscripten_set_wheel_callback(NULL, 0, 0, NULL); +} + +Q_CONSTRUCTOR_FUNCTION(usePotentialyUnusedSymbols) typedef double uint53_t; // see Number.MAX_SAFE_INTEGER namespace { enum class CallbackType { diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index 4aa14d820f..4a7cd988c3 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -77,20 +77,11 @@ QWasmScreen::QWasmScreen(const emscripten::val &containerOrCanvas) event.call("preventDefault"); }); - // Create "specialHTMLTargets" mapping for the canvas. Normally, Emscripten - // uses the html element id when targeting elements, for example when registering - // event callbacks. However, this approach is limited to supporting single-document - // 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. - // - // 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); + // Create "specialHTMLTargets" mapping for the canvas - the element might be unreachable based + // on its id only under some conditions, like the target being embedded in a shadow DOM or a + // subframe. + emscripten::val::module_property("specialHTMLTargets") + .set(canvasTargetId().toStdString(), m_canvas); // Install event handlers on the container/canvas. This must be // done after the canvas has been created above. @@ -104,9 +95,8 @@ QWasmScreen::~QWasmScreen() { Q_ASSERT(!m_compositor); // deleteScreen should have been called to remove this screen - if (hasSpecialHtmlTargets()) - emscripten::val::module_property("specialHTMLTargets") - .set(canvasSpecialHtmlTargetId(), emscripten::val::undefined()); + emscripten::val::module_property("specialHTMLTargets") + .set(canvasTargetId().toStdString(), emscripten::val::undefined()); m_canvas.set(m_canvasResizeObserverCallbackContextPropertyName, emscripten::val(intptr_t(0))); } @@ -158,73 +148,11 @@ QString QWasmScreen::canvasId() const return QWasmString::toQString(m_canvas["id"]); } -// 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 -{ - 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(uintptr_t(this)); -} - -namespace { - -// Compare Emscripten versions, returns > 0 if a is greater than b. - -int compareVersionComponents(int a, int b) -{ - return a >= 0 && b >= 0 ? a - b : 0; -} - -int compareEmscriptenVersions(std::tuple a, std::tuple b) -{ - if (std::get<0>(a) == std::get<0>(b)) { - if (std::get<1>(a) == std::get<1>(b)) { - return compareVersionComponents(std::get<2>(a), std::get<2>(b)); - } - return compareVersionComponents(std::get<1>(a), std::get<1>(b)); - } - return compareVersionComponents(std::get<0>(a), std::get<0>(b)); -} - -bool isEmsdkVersionGreaterThan(std::tuple test) -{ - return compareEmscriptenVersions( - std::make_tuple(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__), test) > 0; -} - -} // namespace - -bool QWasmScreen::hasSpecialHtmlTargets() const -{ - static bool gotIt = []{ - // Enable use of specialHTMLTargets, if available - - // On Emscripten > 3.1.14 (exact version not known), emscripten::val::module_property() - // aborts instead of returning undefined when attempting to resolve the specialHTMLTargets - // property, in the case where it is not defined. Disable the availability test in this case. - // FIXME: Add alternative way to enable. - if (isEmsdkVersionGreaterThan(std::make_tuple(3, 1, 14))) - return false; - - emscripten::val htmlTargets = emscripten::val::module_property("specialHTMLTargets"); - if (htmlTargets.isUndefined()) - return false; - - // Check that the object has the expected type - it can also be - // defined as an abort() function which prints an error on usage. - return htmlTargets["constructor"]["name"].as() == std::string("Array"); - }(); - return gotIt; + return QString("!qtcanvas_%1").arg(uintptr_t(this)); } QRect QWasmScreen::geometry() const diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index 49e539be82..72f727da3a 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -64,9 +64,6 @@ 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 m_compositor;