summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLorn Potter <lorn.potter@gmail.com>2021-05-14 18:37:12 +1000
committerLorn Potter <lorn.potter@gmail.com>2021-12-08 13:39:58 +1000
commitf0be152896471aa392bb1b2b649b66feb31480cc (patch)
treeaa8a3d1c6776416c45578d75177c8eba05ee0f35 /src
parent3b24713098abd34cf8652da815f4dcf3a22110d3 (diff)
wasm: improve clipboard support
Add support for Clipboard API Add clipboard manual test Also includes these fixes: - improve clipboard use for chrome browser - make QClipboard::setText work - html copy and paste - image copy/paste Chrome browser supports text, html and png To use the Clipboard API, apps need to be served from a secure context (https). There is a fallback in the case of non secure context (http) - Firefox requires dom.events.asyncClipboard.read, dom.events.asyncClipboard.clipboardItem and dom.events.asyncClipboard.dataTransfer to be set from about:config, in order to support the Clipboard API. Change-Id: Ie4cb1bbb1dfc77e9655090a30967632780d15dd9 Fixes: QTBUG-74504 Fixes: QTBUG-93619 Fixes: QTBUG-79365 Fixes: QTBUG-86169 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/platforms/wasm/qwasmclipboard.cpp392
-rw-r--r--src/plugins/platforms/wasm/qwasmclipboard.h10
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp11
3 files changed, 336 insertions, 77 deletions
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp
index 222dcff7fa..52471f7b41 100644
--- a/src/plugins/platforms/wasm/qwasmclipboard.cpp
+++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp
@@ -30,103 +30,263 @@
#include "qwasmclipboard.h"
#include "qwasmwindow.h"
#include "qwasmstring.h"
+#include <private/qstdweb_p.h>
#include <emscripten.h>
#include <emscripten/html5.h>
#include <emscripten/bind.h>
+#include <emscripten/val.h>
#include <QCoreApplication>
#include <qpa/qwindowsysteminterface.h>
+#include <QBuffer>
+#include <QString>
using namespace emscripten;
-// there has got to be a better way...
-static QString g_clipboardText;
-static QString g_clipboardFormat;
+static void pasteClipboardData(emscripten::val format, emscripten::val dataPtr)
+{
+ QString formatString = QWasmString::toQString(format);
+ QByteArray dataArray = QByteArray::fromStdString(dataPtr.as<std::string>());
+
+ QMimeData *mMimeData = new QMimeData;
+ mMimeData->setData(formatString, dataArray);
-static val getClipboardData()
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+// QWasmIntegration::get()->getWasmClipboard()->isPaste = false;
+}
+
+static void qClipboardPasteResolve(emscripten::val blob)
{
- return QWasmString::fromQString(g_clipboardText);
+ // read Blob here
+
+ auto fileReader = std::make_shared<qstdweb::FileReader>();
+ auto _blob = qstdweb::Blob(blob);
+ QString formatString = QString::fromStdString(_blob.type());
+
+ fileReader->readAsArrayBuffer(_blob);
+ char *chunkBuffer = nullptr;
+ qstdweb::ArrayBuffer result = fileReader->result();
+ qstdweb::Uint8Array(result).copyTo(chunkBuffer);
+ QMimeData *mMimeData = new QMimeData;
+ mMimeData->setData(formatString, chunkBuffer);
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
}
-static val getClipboardFormat()
+static void qClipboardPromiseResolve(emscripten::val clipboardItems)
{
- return QWasmString::fromQString(g_clipboardFormat);
+ int itemsCount = clipboardItems["length"].as<int>();
+
+ for (int i = 0; i < itemsCount; i++) {
+ int typesCount = clipboardItems[i]["types"]["length"].as<int>(); // ClipboardItem
+
+ std::string mimeFormat = clipboardItems[i]["types"][0].as<std::string>();
+
+ if (mimeFormat.find(std::string("text")) != std::string::npos) {
+ // simple val object, no further processing
+
+ val navigator = val::global("navigator");
+ val textPromise = navigator["clipboard"].call<val>("readText");
+ val readTextResolve = val::global("Module")["qtClipboardTextPromiseResolve"];
+ textPromise.call<val>("then", readTextResolve);
+
+ } else {
+ // binary types require additional processing
+ for (int j = 0; j < typesCount; j++) {
+ val pasteResolve = emscripten::val::module_property("qtClipboardPasteResolve");
+ val pasteException = emscripten::val::module_property("qtClipboardPromiseException");
+
+ // get the blob
+ clipboardItems[i]
+ .call<val>("getType", clipboardItems[i]["types"][j])
+ .call<val>("then", pasteResolve)
+ .call<val>("catch", pasteException);
+ }
+ }
+ }
}
-static void pasteClipboardData(emscripten::val format, emscripten::val dataPtr)
+static void qClipboardCopyPromiseResolve(emscripten::val something)
{
- QString formatString = QWasmString::toQString(format);
- QByteArray dataArray = QByteArray::fromStdString(dataPtr.as<std::string>());
- QMimeData *mMimeData = new QMimeData;
- mMimeData->setData(formatString, dataArray);
- QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ qWarning() << "copy succeeeded";
}
-static void qClipboardPromiseResolve(emscripten::val something)
+
+static emscripten::val qClipboardPromiseException(emscripten::val something)
{
- pasteClipboardData(emscripten::val("text/plain"), something);
+ qWarning() << "clipboard error"
+ << QString::fromStdString(something["name"].as<std::string>())
+ << QString::fromStdString(something["message"].as<std::string>());
+ return something;
+}
+
+static void commonCopyEvent(val event)
+{
+ QMimeData *_mimes = QWasmIntegration::get()->getWasmClipboard()->mimeData(QClipboard::Clipboard);
+ if (!_mimes)
+ return;
+
+ // doing it this way seems to sanitize the text better that calling data() like down below
+ if (_mimes->hasText()) {
+ event["clipboardData"].call<void>("setData", val("text/plain")
+ , QWasmString::fromQString(_mimes->text()));
+ }
+ if (_mimes->hasHtml()) {
+ event["clipboardData"].call<void>("setData", val("text/html")
+ , QWasmString::fromQString(_mimes->html()));
+ }
+
+ for (auto mimetype : _mimes->formats()) {
+ if (mimetype.contains("text/"))
+ continue;
+ QByteArray ba = _mimes->data(mimetype);
+ if (!ba.isEmpty())
+ event["clipboardData"].call<void>("setData", QWasmString::fromQString(mimetype)
+ , val(ba.constData()));
+ }
+
+ event.call<void>("preventDefault");
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = false;
}
static void qClipboardCutTo(val event)
{
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
// Send synthetic Ctrl+X to make the app cut data to Qt's clipboard
- QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
- 0, QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier, "X");
- }
- event["clipboardData"].call<void>("setData", getClipboardFormat(), getClipboardData());
- event.call<void>("preventDefault");
+ QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "X");
+ }
+
+ commonCopyEvent(event);
}
static void qClipboardCopyTo(val event)
{
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
+
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
// Send synthetic Ctrl+C to make the app copy data to Qt's clipboard
- QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
- 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C");
+ QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C");
}
- event["clipboardData"].call<void>("setData", getClipboardFormat(), getClipboardData());
- event.call<void>("preventDefault");
+ commonCopyEvent(event);
}
-static void qClipboardPasteTo(val event)
+static void qClipboardPasteTo(val dataTransfer)
{
- bool hasClipboardApi = QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi;
- val clipdata = hasClipboardApi ? getClipboardData() :
- event["clipboardData"].call<val>("getData", val("text"));
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
+ val clipboardData = dataTransfer["clipboardData"];
+ val types = clipboardData["types"];
+ int typesCount = types["length"].as<int>();
+ std::string stdMimeFormat;
+ QMimeData *mMimeData = new QMimeData;
+ for (int i = 0; i < typesCount; i++) {
+ stdMimeFormat = types[i].as<std::string>();
+ QString mimeFormat = QString::fromStdString(stdMimeFormat);
+ if (mimeFormat.contains("STRING", Qt::CaseSensitive) || mimeFormat.contains("TEXT", Qt::CaseSensitive))
+ continue;
- const QString qstr = QWasmString::toQString(clipdata);
- if (qstr.length() > 0) {
- QMimeData *mMimeData = new QMimeData;
- mMimeData->setText(qstr);
- QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ if (mimeFormat.contains("text")) {
+// also "text/plain;charset=utf-8"
+// "UTF8_STRING" "MULTIPLE"
+ val mimeData = clipboardData.call<val>("getData", val(stdMimeFormat)); // as DataTransfer
+
+ const QString qstr = QWasmString::toQString(mimeData);
+
+ if (qstr.length() > 0) {
+ if (mimeFormat.contains("text/html")) {
+ mMimeData->setHtml(qstr);
+ } else if (mimeFormat.isEmpty() || mimeFormat.contains("text/plain")) {
+ mMimeData->setText(qstr); // the type can be empty
+ } else {
+ mMimeData->setData(mimeFormat, qstr.toLocal8Bit());}
+ }
+ } else {
+ val items = clipboardData["items"];
+
+ int itemsCount = items["length"].as<int>();
+ // handle data
+ for (int i = 0; i < itemsCount; i++) {
+ val item = items[i];
+ val clipboardFile = item.call<emscripten::val>("getAsFile"); // string kind is handled above
+ if (clipboardFile.isUndefined() || item["kind"].as<std::string>() == "string" ) {
+ continue;
+ }
+ qstdweb::File file(clipboardFile);
+
+ mimeFormat = QString::fromStdString(file.type());
+ QByteArray fileContent;
+ fileContent.resize(file.size());
+
+ file.stream(fileContent.data(), [=]() {
+ if (!fileContent.isEmpty()) {
+
+ if (mimeFormat.contains("image")) {
+ QImage image;
+ image.loadFromData(fileContent, nullptr);
+ mMimeData->setImageData(image);
+ } else {
+ mMimeData->setData(mimeFormat,fileContent.data());
+ }
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ }
+ });
+ } // next item
+ }
}
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ QWasmIntegration::get()->getWasmClipboard()->m_isListener = false;
+}
+
+static void qClipboardTextPromiseResolve(emscripten::val clipdata)
+{
+ pasteClipboardData(emscripten::val("text/plain"), clipdata);
}
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
+ function("qtPasteClipboardData", &pasteClipboardData);
+
+ function("qtClipboardTextPromiseResolve", &qClipboardTextPromiseResolve);
function("qtClipboardPromiseResolve", &qClipboardPromiseResolve);
+
+ function("qtClipboardCopyPromiseResolve", &qClipboardCopyPromiseResolve);
+ function("qtClipboardPromiseException", &qClipboardPromiseException);
+
function("qtClipboardCutTo", &qClipboardCutTo);
function("qtClipboardCopyTo", &qClipboardCopyTo);
function("qtClipboardPasteTo", &qClipboardPasteTo);
+ function("qtClipboardPasteResolve", &qClipboardPasteResolve);
}
-QWasmClipboard::QWasmClipboard()
+QWasmClipboard::QWasmClipboard() :
+ isPaste(false),
+ m_isListener(false)
{
val clipboard = val::global("navigator")["clipboard"];
val permissions = val::global("navigator")["permissions"];
- hasClipboardApi = (!clipboard.isUndefined() && !permissions.isUndefined() && !clipboard["readText"].isUndefined());
- if (hasClipboardApi)
- initClipboardEvents();
+ val hasInstallTrigger = val::global("window")["InstallTrigger"];
+
+ hasPermissionsApi = !permissions.isUndefined();
+ hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined());
+ bool isFirefox = !hasInstallTrigger.isUndefined();
+ isSafari = !emscripten::val::global("window")["safari"].isUndefined();
+
+ // firefox has clipboard API if user sets these config tweaks:
+ // dom.events.asyncClipboard.clipboardItem true
+ // dom.events.asyncClipboard.read true
+ // dom.events.testing.asyncClipboard
+ // and permissions API, but does not currently support
+ // the clipboardRead and clipboardWrite permissions
+ if (hasClipboardApi && hasPermissionsApi && !isFirefox)
+ initClipboardPermissions();
}
QWasmClipboard::~QWasmClipboard()
{
- g_clipboardText.clear();
- g_clipboardFormat.clear();
}
-QMimeData* QWasmClipboard::mimeData(QClipboard::Mode mode)
+QMimeData *QWasmClipboard::mimeData(QClipboard::Mode mode)
{
if (mode != QClipboard::Clipboard)
return nullptr;
@@ -134,17 +294,18 @@ QMimeData* QWasmClipboard::mimeData(QClipboard::Mode mode)
return QPlatformClipboard::mimeData(mode);
}
-void QWasmClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
+void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
{
- if (mimeData->hasText()) {
- g_clipboardFormat = mimeData->formats().at(0);
- g_clipboardText = mimeData->text();
- } else if (mimeData->hasHtml()) {
- g_clipboardFormat = mimeData->formats().at(0);
- g_clipboardText = mimeData->html();
- }
-
QPlatformClipboard::setMimeData(mimeData, mode);
+ // handle setText/ setData programmatically
+ if (!isPaste) {
+ if (hasClipboardApi) {
+ writeToClipboardApi();
+ } else if (!m_isListener) {
+ writeToClipboard(mimeData);
+ }
+ }
+ isPaste = false;
}
bool QWasmClipboard::supportsMode(QClipboard::Mode mode) const
@@ -163,10 +324,10 @@ void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData)
QWasmIntegration::get()->clipboard()->setMimeData(mData, QClipboard::Clipboard);
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
- 0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V");
+ 0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V");
}
-void QWasmClipboard::initClipboardEvents()
+void QWasmClipboard::initClipboardPermissions()
{
if (!hasClipboardApi)
return;
@@ -183,32 +344,121 @@ void QWasmClipboard::initClipboardEvents()
void QWasmClipboard::installEventHandlers(const emscripten::val &canvas)
{
- if (hasClipboardApi)
- return;
-
+ emscripten::val cContext = val::undefined();
+ emscripten::val isChromium = val::global("window")["chrome"];
+ if (!isChromium.isUndefined()) {
+ cContext = val::global("document");
+ } else {
+ cContext = canvas;
+ }
// Fallback path for browsers which do not support direct clipboard access
- canvas.call<void>("addEventListener", val("cut"),
- val::module_property("qtClipboardCutTo"));
- canvas.call<void>("addEventListener", val("copy"),
- val::module_property("qtClipboardCopyTo"));
- canvas.call<void>("addEventListener", val("paste"),
- val::module_property("qtClipboardPasteTo"));
+ cContext.call<void>("addEventListener", val("cut"),
+ val::module_property("qtClipboardCutTo"), true);
+ cContext.call<void>("addEventListener", val("copy"),
+ val::module_property("qtClipboardCopyTo"), true);
+ cContext.call<void>("addEventListener", val("paste"),
+ val::module_property("qtClipboardPasteTo"), true);
}
-void QWasmClipboard::readTextFromClipboard()
+void QWasmClipboard::writeToClipboardApi()
{
- if (QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
- val navigator = val::global("navigator");
- val textPromise = navigator["clipboard"].call<val>("readText");
- val readTextResolve = val::module_property("qtClipboardPromiseResolve");
- textPromise.call<val>("then", readTextResolve);
+ if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi)
+ return;
+
+ // copy event
+ // browser event handler detected ctrl c if clipboard API
+ // or Qt call from keyboard event handler
+
+ QMimeData *_mimes = QWasmIntegration::get()->getWasmClipboard()->mimeData(QClipboard::Clipboard);
+ if (!_mimes)
+ return;
+
+ emscripten::val clipboardWriteArray = emscripten::val::array();
+ QByteArray ba;
+
+ for (auto mimetype : _mimes->formats()) {
+ // we need to treat binary and text differently, as the blob method below
+ // fails for text mimetypes
+ // ignore text types
+
+ if (mimetype.contains("STRING", Qt::CaseSensitive) || mimetype.contains("TEXT", Qt::CaseSensitive))
+ continue;
+
+ if (_mimes->hasHtml()) { // prefer html over text
+ ba = _mimes->html().toLocal8Bit();
+ // force this mime
+ mimetype = "text/html";
+ } else if (mimetype.contains("text/plain")) {
+ ba = _mimes->text().toLocal8Bit();
+ } else if (mimetype.contains("image")) {
+ QImage img = qvariant_cast<QImage>( _mimes->imageData());
+ QBuffer buffer(&ba);
+ buffer.open(QIODevice::WriteOnly);
+ img.save(&buffer, "PNG");
+ mimetype = "image/png"; // chrome only allows png
+ // clipboard error "NotAllowedError" "Type application/x-qt-image not supported on write."
+ // safari silently fails
+ // so we use png internally for now
+ } else {
+ // DATA
+ ba = _mimes->data(mimetype);
+ }
+ // Create file data Blob
+
+ const char *content = ba.data();
+ int dataLength = ba.length();
+ if (dataLength < 1) {
+ qDebug() << "no content found";
+ return;
+ }
+
+ emscripten::val document = emscripten::val::global("document");
+ emscripten::val window = emscripten::val::global("window");
+
+ emscripten::val fileContentView =
+ emscripten::val(emscripten::typed_memory_view(dataLength, content));
+ emscripten::val fileContentCopy = emscripten::val::global("ArrayBuffer").new_(dataLength);
+ emscripten::val fileContentCopyView =
+ emscripten::val::global("Uint8Array").new_(fileContentCopy);
+ fileContentCopyView.call<void>("set", fileContentView);
+
+ emscripten::val contentArray = emscripten::val::array();
+ contentArray.call<void>("push", fileContentCopyView);
+
+ // we have a blob, now create a ClipboardItem
+ emscripten::val type = emscripten::val::array();
+ type.set("type", val(QWasmString::fromQString(mimetype)));
+
+ emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type);
+
+ emscripten::val clipboardItemObject = emscripten::val::object();
+ clipboardItemObject.set(val(QWasmString::fromQString(mimetype)), contentBlob);
+
+ val clipboardItemData = val::global("ClipboardItem").new_(clipboardItemObject);
+
+ clipboardWriteArray.call<void>("push", clipboardItemData);
+
+ // Clipboard write is only supported with one ClipboardItem at the moment
+ // but somehow this still works?
+ // break;
}
+
+ val copyResolve = emscripten::val::module_property("qtClipboardCopyPromiseResolve");
+ val copyException = emscripten::val::module_property("qtClipboardPromiseException");
+
+ val navigator = val::global("navigator");
+ navigator["clipboard"]
+ .call<val>("write", clipboardWriteArray)
+ .call<val>("then", copyResolve)
+ .call<val>("catch", copyException);
}
-void QWasmClipboard::writeTextToClipboard()
+void QWasmClipboard::writeToClipboard(const QMimeData *data)
{
- if (QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
- val navigator = val::global("navigator");
- navigator["clipboard"].call<void>("writeText", getClipboardData());
- }
+ // this works for firefox, chrome by generating
+ // copy event, but not safari
+ // execCommand has been deemed deprecated in the docs, but browsers do not seem
+ // interested in removing it. There is no replacement, so we use it here.
+ val document = val::global("document");
+ document.call<val>("execCommand", val("copy"));
}
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h
index 3b28e2c381..9a33b79667 100644
--- a/src/plugins/platforms/wasm/qwasmclipboard.h
+++ b/src/plugins/platforms/wasm/qwasmclipboard.h
@@ -51,11 +51,15 @@ public:
bool ownsMode(QClipboard::Mode mode) const override;
static void qWasmClipboardPaste(QMimeData *mData);
- void initClipboardEvents();
+ void initClipboardPermissions();
void installEventHandlers(const emscripten::val &canvas);
bool hasClipboardApi;
- void readTextFromClipboard();
- void writeTextToClipboard();
+ bool hasPermissionsApi;
+ void writeToClipboardApi();
+ void writeToClipboard(const QMimeData *data);
+ bool isPaste;
+ bool m_isListener;
+ bool isSafari;
};
#endif // QWASMCLIPBOARD_H
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
index 5f809140f5..6d4ead60d5 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -845,7 +845,10 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
// handlers if direct clipboard access is not available.
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier &&
(qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) {
- return 0;
+ if (qtKey == Qt::Key_V) {
+ QWasmIntegration::get()->getWasmClipboard()->isPaste = true;
+ }
+ return false;
}
bool accepted = false;
@@ -853,7 +856,8 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
if (keyType == QEvent::KeyPress &&
mods.testFlag(Qt::ControlModifier)
&& qtKey == Qt::Key_V) {
- QWasmIntegration::get()->getWasmClipboard()->readTextFromClipboard();
+ QWasmIntegration::get()->getWasmClipboard()->isPaste = true;
+ accepted = false; // continue on to event
} else {
if (keyText.isEmpty())
keyText = QString(keyEvent->key);
@@ -865,7 +869,8 @@ bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboa
if (keyType == QEvent::KeyPress &&
mods.testFlag(Qt::ControlModifier)
&& qtKey == Qt::Key_C) {
- QWasmIntegration::get()->getWasmClipboard()->writeTextToClipboard();
+ QWasmIntegration::get()->getWasmClipboard()->isPaste = false;
+ accepted = false; // continue on to event
}
QWasmEventDispatcher::maintainTimers();