2018-02-21 18:35:25 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** 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$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qwasmclipboard.h"
|
|
|
|
#include "qwasmwindow.h"
|
2020-01-17 09:32:27 +00:00
|
|
|
#include "qwasmstring.h"
|
2021-05-14 08:37:12 +00:00
|
|
|
#include <private/qstdweb_p.h>
|
2018-02-21 18:35:25 +00:00
|
|
|
|
|
|
|
#include <emscripten.h>
|
|
|
|
#include <emscripten/html5.h>
|
|
|
|
#include <emscripten/bind.h>
|
2021-05-14 08:37:12 +00:00
|
|
|
#include <emscripten/val.h>
|
2018-02-21 18:35:25 +00:00
|
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <qpa/qwindowsysteminterface.h>
|
2021-05-14 08:37:12 +00:00
|
|
|
#include <QBuffer>
|
|
|
|
#include <QString>
|
2018-02-21 18:35:25 +00:00
|
|
|
|
|
|
|
using namespace emscripten;
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
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);
|
2018-02-21 18:35:25 +00:00
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
QWasmClipboard::qWasmClipboardPaste(mMimeData);
|
|
|
|
// QWasmIntegration::get()->getWasmClipboard()->isPaste = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qClipboardPasteResolve(emscripten::val blob)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
// 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);
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
static void qClipboardPromiseResolve(emscripten::val clipboardItems)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
static void qClipboardCopyPromiseResolve(emscripten::val something)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
qWarning() << "copy succeeeded";
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
|
|
|
|
static emscripten::val qClipboardPromiseException(emscripten::val something)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
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;
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 08:40:30 +00:00
|
|
|
static void qClipboardCutTo(val event)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
|
2019-02-23 08:40:30 +00:00
|
|
|
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
|
|
|
|
// Send synthetic Ctrl+X to make the app cut data to Qt's clipboard
|
2021-05-14 08:37:12 +00:00
|
|
|
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
|
|
|
|
0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "X");
|
|
|
|
}
|
|
|
|
|
|
|
|
commonCopyEvent(event);
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 08:40:30 +00:00
|
|
|
static void qClipboardCopyTo(val event)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
QWasmIntegration::get()->getWasmClipboard()->m_isListener = true;
|
|
|
|
|
2019-02-23 08:40:30 +00:00
|
|
|
if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
|
|
|
|
// Send synthetic Ctrl+C to make the app copy data to Qt's clipboard
|
2021-05-14 08:37:12 +00:00
|
|
|
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
|
|
|
|
0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C");
|
2019-02-23 08:40:30 +00:00
|
|
|
}
|
2021-05-14 08:37:12 +00:00
|
|
|
commonCopyEvent(event);
|
2019-02-23 08:40:30 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
static void qClipboardPasteTo(val dataTransfer)
|
2019-02-23 08:40:30 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
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;
|
2018-02-21 18:35:25 +00:00
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
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
|
|
|
|
}
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
2021-05-14 08:37:12 +00:00
|
|
|
QWasmClipboard::qWasmClipboardPaste(mMimeData);
|
|
|
|
QWasmIntegration::get()->getWasmClipboard()->m_isListener = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qClipboardTextPromiseResolve(emscripten::val clipdata)
|
|
|
|
{
|
|
|
|
pasteClipboardData(emscripten::val("text/plain"), clipdata);
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2019-03-15 14:04:59 +00:00
|
|
|
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
|
2021-05-14 08:37:12 +00:00
|
|
|
function("qtPasteClipboardData", &pasteClipboardData);
|
|
|
|
|
|
|
|
function("qtClipboardTextPromiseResolve", &qClipboardTextPromiseResolve);
|
2019-03-15 14:04:59 +00:00
|
|
|
function("qtClipboardPromiseResolve", &qClipboardPromiseResolve);
|
2021-05-14 08:37:12 +00:00
|
|
|
|
|
|
|
function("qtClipboardCopyPromiseResolve", &qClipboardCopyPromiseResolve);
|
|
|
|
function("qtClipboardPromiseException", &qClipboardPromiseException);
|
|
|
|
|
2019-03-15 14:04:59 +00:00
|
|
|
function("qtClipboardCutTo", &qClipboardCutTo);
|
|
|
|
function("qtClipboardCopyTo", &qClipboardCopyTo);
|
|
|
|
function("qtClipboardPasteTo", &qClipboardPasteTo);
|
2021-05-14 08:37:12 +00:00
|
|
|
function("qtClipboardPasteResolve", &qClipboardPasteResolve);
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
QWasmClipboard::QWasmClipboard() :
|
|
|
|
isPaste(false),
|
|
|
|
m_isListener(false)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2019-02-15 21:04:09 +00:00
|
|
|
val clipboard = val::global("navigator")["clipboard"];
|
2020-06-05 16:07:15 +00:00
|
|
|
val permissions = val::global("navigator")["permissions"];
|
2021-05-14 08:37:12 +00:00
|
|
|
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();
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QWasmClipboard::~QWasmClipboard()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
QMimeData *QWasmClipboard::mimeData(QClipboard::Mode mode)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
|
|
|
if (mode != QClipboard::Clipboard)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return QPlatformClipboard::mimeData(mode);
|
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
void QWasmClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
|
|
|
QPlatformClipboard::setMimeData(mimeData, mode);
|
2021-05-14 08:37:12 +00:00
|
|
|
// handle setText/ setData programmatically
|
|
|
|
if (!isPaste) {
|
|
|
|
if (hasClipboardApi) {
|
|
|
|
writeToClipboardApi();
|
|
|
|
} else if (!m_isListener) {
|
|
|
|
writeToClipboard(mimeData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isPaste = false;
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QWasmClipboard::supportsMode(QClipboard::Mode mode) const
|
|
|
|
{
|
|
|
|
return mode == QClipboard::Clipboard;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QWasmClipboard::ownsMode(QClipboard::Mode mode) const
|
|
|
|
{
|
|
|
|
Q_UNUSED(mode);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData)
|
|
|
|
{
|
|
|
|
QWasmIntegration::get()->clipboard()->setMimeData(mData, QClipboard::Clipboard);
|
|
|
|
|
|
|
|
QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
|
2021-05-14 08:37:12 +00:00
|
|
|
0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V");
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
void QWasmClipboard::initClipboardPermissions()
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2019-02-15 21:04:09 +00:00
|
|
|
if (!hasClipboardApi)
|
|
|
|
return;
|
2018-02-21 18:35:25 +00:00
|
|
|
|
2019-02-15 21:04:09 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-02-02 00:22:24 +00:00
|
|
|
void QWasmClipboard::installEventHandlers(const emscripten::val &canvas)
|
2019-02-15 21:04:09 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
emscripten::val cContext = val::undefined();
|
|
|
|
emscripten::val isChromium = val::global("window")["chrome"];
|
|
|
|
if (!isChromium.isUndefined()) {
|
|
|
|
cContext = val::global("document");
|
|
|
|
} else {
|
|
|
|
cContext = canvas;
|
|
|
|
}
|
2019-02-15 21:04:09 +00:00
|
|
|
// Fallback path for browsers which do not support direct clipboard access
|
2021-05-14 08:37:12 +00:00
|
|
|
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);
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
void QWasmClipboard::writeToClipboardApi()
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
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;
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
2021-05-14 08:37:12 +00:00
|
|
|
|
|
|
|
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);
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 08:37:12 +00:00
|
|
|
void QWasmClipboard::writeToClipboard(const QMimeData *data)
|
2018-02-21 18:35:25 +00:00
|
|
|
{
|
2021-05-14 08:37:12 +00:00
|
|
|
// 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"));
|
2018-02-21 18:35:25 +00:00
|
|
|
}
|