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.cpp368
1 files changed, 103 insertions, 265 deletions
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp
index 17ba1f4db6..1aa3ffa5b3 100644
--- a/src/plugins/platforms/wasm/qwasmclipboard.cpp
+++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp
@@ -1,125 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 or (at your option) any later version
-** approved by the KDE Free Qt Foundation. The licenses are as published by
-** the Free Software Foundation and appearing in the file LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// 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>
-using namespace emscripten;
-
-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);
-
- QWasmClipboard::qWasmClipboardPaste(mMimeData);
-// QWasmIntegration::get()->getWasmClipboard()->isPaste = false;
-}
-
-static void qClipboardPasteResolve(emscripten::val blob)
-{
- // 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 void qClipboardPromiseResolve(emscripten::val clipboardItems)
-{
- 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 qClipboardCopyPromiseResolve(emscripten::val something)
-{
- Q_UNUSED(something)
- qWarning() << "copy succeeeded";
-}
-
+#include <emscripten/val.h>
-static emscripten::val qClipboardPromiseException(emscripten::val something)
-{
- qWarning() << "clipboard error"
- << QString::fromStdString(something["name"].as<std::string>())
- << QString::fromStdString(something["message"].as<std::string>());
- return something;
-}
+QT_BEGIN_NAMESPACE
+using namespace emscripten;
static void commonCopyEvent(val event)
{
@@ -129,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()) {
@@ -142,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);
@@ -164,122 +58,35 @@ 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;
-}
-
-static void qClipboardTextPromiseResolve(emscripten::val clipdata)
-{
- pasteClipboardData(emscripten::val("text/plain"), clipdata);
+ QWasmIntegration::get()->getWasmClipboard()->sendClipboardData(event);
}
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() :
- 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();
}
@@ -297,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
@@ -320,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);
@@ -361,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;
@@ -428,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);
@@ -444,19 +259,22 @@ void QWasmClipboard::writeToClipboardApi()
// 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);
+
+ qstdweb::Promise::make(
+ navigator["clipboard"], "write",
+ {
+ .catchFunc = [](emscripten::val error) {
+ qWarning() << "clipboard error"
+ << QString::fromStdString(error["name"].as<std::string>())
+ << QString::fromStdString(error["message"].as<std::string>());
+ }
+ },
+ 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
@@ -464,3 +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