summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/wasm/qwasmclipboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmclipboard.cpp')
-rw-r--r--src/plugins/platforms/wasm/qwasmclipboard.cpp227
1 files changed, 88 insertions, 139 deletions
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp
index 99f3e61155..1aa3ffa5b3 100644
--- a/src/plugins/platforms/wasm/qwasmclipboard.cpp
+++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp
@@ -2,20 +2,19 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmclipboard.h"
+#include "qwasmdom.h"
+#include "qwasmevent.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 <private/qstdweb_p.h>
#include <QCoreApplication>
#include <qpa/qwindowsysteminterface.h>
#include <QBuffer>
#include <QString>
+#include <emscripten/val.h>
+
QT_BEGIN_NAMESPACE
using namespace emscripten;
@@ -27,12 +26,11 @@ static void commonCopyEvent(val event)
// 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()));
+ event["clipboardData"].call<void>("setData", val("text/plain"),
+ _mimes->text().toEcmaString());
}
if (_mimes->hasHtml()) {
- event["clipboardData"].call<void>("setData", val("text/html")
- , QWasmString::fromQString(_mimes->html()));
+ event["clipboardData"].call<void>("setData", val("text/html"), _mimes->html().toEcmaString());
}
for (auto mimetype : _mimes->formats()) {
@@ -40,21 +38,19 @@ static void commonCopyEvent(val event)
continue;
QByteArray ba = _mimes->data(mimetype);
if (!ba.isEmpty())
- event["clipboardData"].call<void>("setData", QWasmString::fromQString(mimetype)
- , val(ba.constData()));
+ event["clipboardData"].call<void>("setData", mimetype.toEcmaString(),
+ 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) {
+ 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_C, Qt::ControlModifier, "X");
+ QWindowSystemInterface::handleKeyEvent(
+ 0, QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier, "X");
}
commonCopyEvent(event);
@@ -62,80 +58,19 @@ static void qClipboardCutTo(val event)
static void qClipboardCopyTo(val event)
{
- QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
-
- if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
+ if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi()) {
// Send synthetic Ctrl+C to make the app copy data to Qt's clipboard
- QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ QWindowSystemInterface::handleKeyEvent(
0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C");
}
commonCopyEvent(event);
}
-static void qClipboardPasteTo(val dataTransfer)
+static void qClipboardPasteTo(val event)
{
- 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;
-
- 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);
+ event.call<void>("preventDefault"); // prevent browser from handling drop event
- 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;
+ QWasmIntegration::get()->getWasmClipboard()->sendClipboardData(event);
}
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
@@ -144,26 +79,14 @@ EMSCRIPTEN_BINDINGS(qtClipboardModule) {
function("qtClipboardPasteTo", &qClipboardPasteTo);
}
-QWasmClipboard::QWasmClipboard() :
- isPaste(false),
- m_isListener(false)
+QWasmClipboard::QWasmClipboard()
{
val clipboard = val::global("navigator")["clipboard"];
- val permissions = val::global("navigator")["permissions"];
- 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)
+
+ const bool hasPermissionsApi = !val::global("navigator")["permissions"].isUndefined();
+ m_hasClipboardApi = !clipboard.isUndefined() && !clipboard["readText"].isUndefined();
+
+ if (m_hasClipboardApi && hasPermissionsApi)
initClipboardPermissions();
}
@@ -181,16 +104,27 @@ QMimeData *QWasmClipboard::mimeData(QClipboard::Mode mode)
void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
{
- QPlatformClipboard::setMimeData(mimeData, mode);
// handle setText/ setData programmatically
- if (!isPaste) {
- if (hasClipboardApi) {
- writeToClipboardApi();
- } else if (!m_isListener) {
- writeToClipboard(mimeData);
- }
- }
- isPaste = false;
+ QPlatformClipboard::setMimeData(mimeData, mode);
+ if (m_hasClipboardApi)
+ writeToClipboardApi();
+ else
+ writeToClipboard();
+}
+
+QWasmClipboard::ProcessKeyboardResult QWasmClipboard::processKeyboard(const KeyEvent &event)
+{
+ if (event.type != EventType::KeyDown || !event.modifiers.testFlag(Qt::ControlModifier))
+ return ProcessKeyboardResult::Ignored;
+
+ if (event.key != Qt::Key_C && event.key != Qt::Key_V && event.key != Qt::Key_X)
+ return ProcessKeyboardResult::Ignored;
+
+ const bool isPaste = event.key == Qt::Key_V;
+
+ return m_hasClipboardApi && !isPaste
+ ? ProcessKeyboardResult::NativeClipboardEventAndCopiedDataNeeded
+ : ProcessKeyboardResult::NativeClipboardEventNeeded;
}
bool QWasmClipboard::supportsMode(QClipboard::Mode mode) const
@@ -204,38 +138,31 @@ bool QWasmClipboard::ownsMode(QClipboard::Mode mode) const
return false;
}
-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");
-}
-
void QWasmClipboard::initClipboardPermissions()
{
- if (!hasClipboardApi)
- return;
-
val permissions = val::global("navigator")["permissions"];
- val readPermissionsMap = val::object();
- readPermissionsMap.set("name", val("clipboard-read"));
- permissions.call<val>("query", readPermissionsMap);
- val writePermissionsMap = val::object();
- writePermissionsMap.set("name", val("clipboard-write"));
- permissions.call<val>("query", writePermissionsMap);
+ qstdweb::Promise::make(permissions, "query", { .catchFunc = [](emscripten::val) {} }, ([]() {
+ val readPermissionsMap = val::object();
+ readPermissionsMap.set("name", val("clipboard-read"));
+ return readPermissionsMap;
+ })());
+ qstdweb::Promise::make(permissions, "query", { .catchFunc = [](emscripten::val) {} }, ([]() {
+ val readPermissionsMap = val::object();
+ readPermissionsMap.set("name", val("clipboard-write"));
+ return readPermissionsMap;
+ })());
}
-void QWasmClipboard::installEventHandlers(const emscripten::val &canvas)
+void QWasmClipboard::installEventHandlers(const emscripten::val &target)
{
emscripten::val cContext = val::undefined();
emscripten::val isChromium = val::global("window")["chrome"];
- if (!isChromium.isUndefined()) {
+ if (!isChromium.isUndefined()) {
cContext = val::global("document");
- } else {
- cContext = canvas;
- }
+ } else {
+ cContext = target;
+ }
// Fallback path for browsers which do not support direct clipboard access
cContext.call<void>("addEventListener", val("cut"),
val::module_property("qtClipboardCutTo"), true);
@@ -245,16 +172,20 @@ void QWasmClipboard::installEventHandlers(const emscripten::val &canvas)
val::module_property("qtClipboardPasteTo"), true);
}
+bool QWasmClipboard::hasClipboardApi()
+{
+ return m_hasClipboardApi;
+}
+
void QWasmClipboard::writeToClipboardApi()
{
- if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi)
- return;
+ Q_ASSERT(m_hasClipboardApi);
// 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);
+ QMimeData *_mimes = mimeData(QClipboard::Clipboard);
if (!_mimes)
return;
@@ -312,12 +243,12 @@ void QWasmClipboard::writeToClipboardApi()
// we have a blob, now create a ClipboardItem
emscripten::val type = emscripten::val::array();
- type.set("type", val(QWasmString::fromQString(mimetype)));
+ type.set("type", mimetype.toEcmaString());
emscripten::val contentBlob = emscripten::val::global("Blob").new_(contentArray, type);
emscripten::val clipboardItemObject = emscripten::val::object();
- clipboardItemObject.set(val(QWasmString::fromQString(mimetype)), contentBlob);
+ clipboardItemObject.set(mimetype.toEcmaString(), contentBlob);
val clipboardItemData = val::global("ClipboardItem").new_(clipboardItemObject);
@@ -342,9 +273,8 @@ void QWasmClipboard::writeToClipboardApi()
clipboardWriteArray);
}
-void QWasmClipboard::writeToClipboard(const QMimeData *data)
+void QWasmClipboard::writeToClipboard()
{
- Q_UNUSED(data)
// 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
@@ -352,4 +282,23 @@ void QWasmClipboard::writeToClipboard(const QMimeData *data)
val document = val::global("document");
document.call<val>("execCommand", val("copy"));
}
+
+void QWasmClipboard::sendClipboardData(emscripten::val event)
+{
+ qDebug() << "sendClipboardData";
+
+ dom::DataTransfer *transfer = new dom::DataTransfer(event["clipboardData"]);
+ const auto mimeCallback = std::function([transfer](QMimeData *data) {
+
+ // Persist clipboard data so that the app can read it when handling the CTRL+V
+ QWasmIntegration::get()->clipboard()->QPlatformClipboard::setMimeData(data, QClipboard::Clipboard);
+ QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, Qt::Key_V,
+ Qt::ControlModifier, "V");
+ QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, Qt::Key_V,
+ Qt::ControlModifier, "V");
+ delete transfer;
+ });
+
+ transfer->toMimeDataWithFile(mimeCallback);
+}
QT_END_NAMESPACE