From f4287c4531d4bede3347df37168c3e19f3d25e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20S=C3=B8rvig?= Date: Fri, 28 Feb 2025 20:09:00 +0100 Subject: [PATCH] wasm: fix clipboard event handler leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QWasmEventHandler instead of calling addEventListener() directly (using QWasmEventHandler also allows supporting JSPI). The QWasmEventHandler destructor calls removeEventListener(), which should make sure everything gets cleaned up. Keep the Chrome-specific global (document) event handler code path, but register once at startup instead of once per window. Change-Id: If4314df738afc0dcfdb0f6f1ab9e1f176e1812ac Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/wasm/qwasmclipboard.cpp | 43 +++++++------------ src/plugins/platforms/wasm/qwasmclipboard.h | 9 +++- src/plugins/platforms/wasm/qwasmwindow.cpp | 6 ++- src/plugins/platforms/wasm/qwasmwindow.h | 4 ++ 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp index b96262319d8..7851162494d 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.cpp +++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp @@ -46,7 +46,7 @@ static void commonCopyEvent(val event) event.call("preventDefault"); } -static void qClipboardCutTo(val event) +void QWasmClipboard::cut(val event) { QWasmInputContext *wasmInput = QWasmIntegration::get()->wasmInputContext(); if (wasmInput && wasmInput->usingTextInput()) @@ -61,7 +61,7 @@ static void qClipboardCutTo(val event) commonCopyEvent(event); } -static void qClipboardCopyTo(val event) +void QWasmClipboard::copy(val event) { QWasmInputContext *wasmInput = QWasmIntegration::get()->wasmInputContext(); if (wasmInput && wasmInput->usingTextInput()) @@ -75,7 +75,7 @@ static void qClipboardCopyTo(val event) commonCopyEvent(event); } -static void qClipboardPasteTo(val event) +void QWasmClipboard::paste(val event) { QWasmInputContext *wasmInput = QWasmIntegration::get()->wasmInputContext(); if (wasmInput && wasmInput->usingTextInput()) @@ -86,12 +86,6 @@ static void qClipboardPasteTo(val event) QWasmIntegration::get()->getWasmClipboard()->sendClipboardData(event); } -EMSCRIPTEN_BINDINGS(qtClipboardModule) { - function("qtClipboardCutTo", &qClipboardCutTo); - function("qtClipboardCopyTo", &qClipboardCopyTo); - function("qtClipboardPasteTo", &qClipboardPasteTo); -} - QWasmClipboard::QWasmClipboard() { val clipboard = val::global("navigator")["clipboard"]; @@ -101,6 +95,13 @@ QWasmClipboard::QWasmClipboard() if (m_hasClipboardApi && hasPermissionsApi) initClipboardPermissions(); + + if (!shouldInstallWindowEventHandlers()) { + val document = val::global("document"); + m_documentCut = QWasmEventHandler(document, "cut", QWasmClipboard::cut); + m_documentCopy = QWasmEventHandler(document, "copy", QWasmClipboard::copy); + m_documentPaste = QWasmEventHandler(document, "paste", QWasmClipboard::paste); + } } QWasmClipboard::~QWasmClipboard() @@ -167,29 +168,17 @@ void QWasmClipboard::initClipboardPermissions() })()); } -void QWasmClipboard::installEventHandlers(const emscripten::val &target) -{ - emscripten::val cContext = val::undefined(); - emscripten::val isChromium = val::global("window")["chrome"]; - if (!isChromium.isUndefined()) { - cContext = val::global("document"); - } else { - cContext = target; - } - // Fallback path for browsers which do not support direct clipboard access - cContext.call("addEventListener", val("cut"), - val::module_property("qtClipboardCutTo"), true); - cContext.call("addEventListener", val("copy"), - val::module_property("qtClipboardCopyTo"), true); - cContext.call("addEventListener", val("paste"), - val::module_property("qtClipboardPasteTo"), true); -} - bool QWasmClipboard::hasClipboardApi() { return m_hasClipboardApi; } +bool QWasmClipboard::shouldInstallWindowEventHandlers() +{ + // Chrome uses global handlers + return val::global("window")["chrome"].isUndefined() == false; +} + void QWasmClipboard::writeToClipboardApi() { Q_ASSERT(m_hasClipboardApi); diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h index 86618dd5604..3f24e64ceb5 100644 --- a/src/plugins/platforms/wasm/qwasmclipboard.h +++ b/src/plugins/platforms/wasm/qwasmclipboard.h @@ -36,16 +36,23 @@ public: bool ownsMode(QClipboard::Mode mode) const override; ProcessKeyboardResult processKeyboard(const KeyEvent &event); - static void installEventHandlers(const emscripten::val &target); bool hasClipboardApi(); + static bool shouldInstallWindowEventHandlers(); void sendClipboardData(emscripten::val event); + static void cut(emscripten::val event); + static void copy(emscripten::val event); + static void paste(emscripten::val event); + private: void initClipboardPermissions(); void writeToClipboardApi(); void writeToClipboard(); bool m_hasClipboardApi = false; + QWasmEventHandler m_documentCut; + QWasmEventHandler m_documentCopy; + QWasmEventHandler m_documentPaste; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index e3e5f064811..eaf9a67e152 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -95,7 +95,11 @@ QWasmWindow::QWasmWindow(QWindow *w, QWasmDeadKeySupport *deadKeySupport, m_window.set("contentEditable", std::string("true")); m_window["style"].set("outline", std::string("none")); - QWasmClipboard::installEventHandlers(m_window); + if (QWasmClipboard::shouldInstallWindowEventHandlers()) { + m_cutCallback = QWasmEventHandler(m_window, "cut", QWasmClipboard::cut); + m_copyCallback = QWasmEventHandler(m_window, "copy", QWasmClipboard::copy); + m_pasteCallback = QWasmEventHandler(m_window, "paste", QWasmClipboard::paste); + } #endif // Set inputMode to none to stop the mobile keyboard from opening diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h index c77bfae3cb2..67a3e8ea293 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.h +++ b/src/plugins/platforms/wasm/qwasmwindow.h @@ -173,6 +173,10 @@ private: QMap m_pointerIdToTouchPoints; + QWasmEventHandler m_cutCallback; + QWasmEventHandler m_copyCallback; + QWasmEventHandler m_pasteCallback; + Qt::WindowStates m_state = Qt::WindowNoState; Qt::WindowStates m_previousWindowState = Qt::WindowNoState;