Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwasmdom.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qwasmdom.h"
5
6#include <QtCore/qdir.h>
7#include <QtCore/qfile.h>
8#include <QtCore/qpoint.h>
9#include <QtCore/qrect.h>
10#include <QtGui/qimage.h>
11#include <private/qstdweb_p.h>
12#include <QtCore/qurl.h>
13
14#include <utility>
15#include <emscripten/wire.h>
16
18
19namespace dom {
20namespace {
21std::string dropActionToDropEffect(Qt::DropAction action)
22{
23 switch (action) {
25 return "copy";
27 return "none";
29 return "link";
32 return "move";
34 Q_ASSERT(false);
35 return "";
36 }
37}
38} // namespace
39
40DataTransfer::DataTransfer(emscripten::val webDataTransfer)
41 : webDataTransfer(webDataTransfer) {
42}
43
45
47
49
51
53
54void DataTransfer::setDragImage(emscripten::val element, const QPoint &hotspot)
55{
56 webDataTransfer.call<void>("setDragImage", element, emscripten::val(hotspot.x()),
57 emscripten::val(hotspot.y()));
58}
59
60void DataTransfer::setData(std::string format, std::string data)
61{
62 webDataTransfer.call<void>("setData", emscripten::val(std::move(format)),
63 emscripten::val(std::move(data)));
64}
65
67{
68 webDataTransfer.set("dropEffect", emscripten::val(dropActionToDropEffect(action)));
69}
70
72{
73 for (const auto &format : mimeData.formats()) {
74 auto data = mimeData.data(format);
75
76 auto encoded = format.startsWith("text/")
77 ? QString::fromLocal8Bit(data).toStdString()
78 : "QB64" + QString::fromLocal8Bit(data.toBase64()).toStdString();
79
80 setData(format.toStdString(), std::move(encoded));
81 }
82}
83
84// Converts a DataTransfer instance to a QMimeData instance. Invokes the
85// given callback when the conversion is complete. The callback takes ownership
86// of the QMimeData.
87void DataTransfer::toMimeDataWithFile(std::function<void(QMimeData *)> callback)
88{
89 enum class ItemKind {
90 File,
91 String,
92 };
93
94 class MimeContext {
95
96 public:
97 MimeContext(int itemCount, std::function<void(QMimeData *)> callback)
98 :m_remainingItemCount(itemCount), m_callback(callback)
99 {
100
101 }
102
103 void deref() {
104 if (--m_remainingItemCount > 0)
105 return;
106
107 mimeData->setUrls(fileUrls);
108
109 m_callback(mimeData);
110
111 // Delete files; we expect that the user callback reads/copies
112 // file content before returning.
113 // Fixme: tie file lifetime to lifetime of the QMimeData?
114 for (QUrl fileUrl: fileUrls)
115 QFile(fileUrl.toLocalFile()).remove();
116
117 delete this;
118 }
119
121 QList<QUrl> fileUrls;
122
123 private:
124 int m_remainingItemCount;
125 std::function<void(QMimeData *)> m_callback;
126 };
127
128 const auto items = webDataTransfer["items"];
129 const int itemCount = items["length"].as<int>();
130 const int fileCount = webDataTransfer["files"]["length"].as<int>();
131 MimeContext *mimeContext = new MimeContext(itemCount, callback);
132
133 for (int i = 0; i < itemCount; ++i) {
134 const auto item = items[i];
135 const auto itemKind =
136 item["kind"].as<std::string>() == "string" ? ItemKind::String : ItemKind::File;
137 const auto itemMimeType = QString::fromStdString(item["type"].as<std::string>());
138
139 switch (itemKind) {
140 case ItemKind::File: {
141 qstdweb::File webfile(item.call<emscripten::val>("getAsFile"));
142
143 if (webfile.size() > 1e+9) { // limit file size to 1 GB
144 qWarning() << "File is too large (> 1GB) and will be skipped. File size is" << webfile.size();
145 mimeContext->deref();
146 continue;
147 }
148
149 QString mimeFormat = QString::fromStdString(webfile.type());
150 QString fileName = QString::fromStdString(webfile.name());
151
152 // there's a file, now read it
153 QByteArray fileContent(webfile.size(), Qt::Uninitialized);
154 webfile.stream(fileContent.data(), [=]() {
155
156 // If we get a single file, and that file is an image, then
157 // try to decode the image data. This handles the case where
158 // image data (i.e. not an image file) is pasted. The browsers
159 // will then create a fake "image.png" file which has the image
160 // data. As a side effect Qt will also decode the image for
161 // single-image-file drops, since there is no way to differentiate
162 // the fake "image.png" from a real one.
163 if (fileCount == 1 && mimeFormat.contains("image/")) {
164 QImage image;
165 if (image.loadFromData(fileContent))
166 mimeContext->mimeData->setImageData(image);
167 }
168
169 QDir qtTmpDir("/qt/tmp/"); // "tmp": indicate that these files won't stay around
170 qtTmpDir.mkpath(qtTmpDir.path());
171
172 QUrl fileUrl = QUrl::fromLocalFile(qtTmpDir.filePath(QString::fromStdString(webfile.name())));
173 mimeContext->fileUrls.append(fileUrl);
174
175 QFile file(fileUrl.toLocalFile());
176 if (!file.open(QFile::WriteOnly)) {
177 qWarning() << "File was not opened";
178 mimeContext->deref();
179 return;
180 }
181 if (file.write(fileContent) < 0) {
182 qWarning() << "Write failed";
183 file.close();
184 }
185 mimeContext->deref();
186 });
187 break;
188 }
189 case ItemKind::String:
190 if (itemMimeType.contains("STRING", Qt::CaseSensitive)
191 || itemMimeType.contains("TEXT", Qt::CaseSensitive)) {
192 mimeContext->deref();
193 break;
194 }
195 QString a;
196 QString data = QString::fromEcmaString(webDataTransfer.call<emscripten::val>(
197 "getData", emscripten::val(itemMimeType.toStdString())));
198
199 if (!data.isEmpty()) {
200 if (itemMimeType == "text/html")
201 mimeContext->mimeData->setHtml(data);
202 else if (itemMimeType.isEmpty() || itemMimeType == "text/plain")
203 mimeContext->mimeData->setText(data); // the type can be empty
204 else {
205 // TODO improve encoding
206 if (data.startsWith("QB64")) {
207 data.remove(0, 4);
208 mimeContext->mimeData->setData(itemMimeType,
210 data.toStdString())));
211 } else {
212 mimeContext->mimeData->setData(itemMimeType, data.toLocal8Bit());
213 }
214 }
215 }
216 mimeContext->deref();
217 break;
218 }
219 } // for items
220}
221
222QMimeData *DataTransfer::toMimeDataPreview()
223{
224 auto data = new QMimeData();
225
226 QList<QUrl> uriList;
227 for (int i = 0; i < webDataTransfer["items"]["length"].as<int>(); ++i) {
228 const auto item = webDataTransfer["items"][i];
229 if (item["kind"].as<std::string>() == "file") {
230 uriList.append(QUrl("blob://placeholder"));
231 } else {
232 const auto itemMimeType = QString::fromStdString(item["type"].as<std::string>());
233 data->setData(itemMimeType, QByteArray());
234 }
235 }
236 data->setUrls(uriList);
237 return data;
238}
239
240void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
241{
242 if (flag) {
243 element["classList"].call<void>("add", emscripten::val(std::move(cssClassName)));
244 return;
245 }
246
247 element["classList"].call<void>("remove", emscripten::val(std::move(cssClassName)));
248}
249
250QPointF mapPoint(emscripten::val source, emscripten::val target, const QPointF &point)
251{
252 const auto sourceBoundingRect =
253 QRectF::fromDOMRect(source.call<emscripten::val>("getBoundingClientRect"));
254 const auto targetBoundingRect =
255 QRectF::fromDOMRect(target.call<emscripten::val>("getBoundingClientRect"));
256
257 const auto offset = sourceBoundingRect.topLeft() - targetBoundingRect.topLeft();
258 return point + offset;
259}
260
261void drawImageToWebImageDataArray(const QImage &sourceImage, emscripten::val destinationImageData,
262 const QRect &sourceRect)
263{
264 Q_ASSERT_X(destinationImageData["constructor"]["name"].as<std::string>() == "ImageData",
265 Q_FUNC_INFO, "The destination should be an ImageData instance");
266
267 constexpr int BytesPerColor = 4;
268 if (sourceRect.width() == sourceImage.width()) {
269 // Copy a contiguous chunk of memory
270 // ...............
271 // OOOOOOOOOOOOOOO
272 // OOOOOOOOOOOOOOO -> image data
273 // OOOOOOOOOOOOOOO
274 // ...............
275 auto imageMemory = emscripten::typed_memory_view(sourceRect.width() * sourceRect.height()
276 * BytesPerColor,
277 sourceImage.constScanLine(sourceRect.y()));
278 destinationImageData["data"].call<void>(
279 "set", imageMemory, sourceRect.y() * sourceImage.width() * BytesPerColor);
280 } else {
281 // Go through the scanlines manually to set the individual lines in bulk. This is
282 // marginally less performant than the above.
283 // ...............
284 // ...OOOOOOOOO... r = 0 -> image data
285 // ...OOOOOOOOO... r = 1 -> image data
286 // ...OOOOOOOOO... r = 2 -> image data
287 // ...............
288 for (int row = 0; row < sourceRect.height(); ++row) {
289 auto scanlineMemory =
290 emscripten::typed_memory_view(sourceRect.width() * BytesPerColor,
291 sourceImage.constScanLine(row + sourceRect.y())
292 + BytesPerColor * sourceRect.x());
293 destinationImageData["data"].call<void>("set", scanlineMemory,
294 (sourceRect.y() + row) * sourceImage.width()
295 * BytesPerColor
296 + sourceRect.x() * BytesPerColor);
297 }
298 }
299}
300
301} // namespace dom
302
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray fromStdString(const std::string &s)
static QByteArray fromBase64(const QByteArray &base64, Base64Options options=Base64Encoding)
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:223
\inmodule QtCore
Definition qdir.h:20
bool mkpath(const QString &dirPath) const
Creates the directory path dirPath.
Definition qdir.cpp:1578
void close() override
Calls QFileDevice::flush() and closes the file.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
\inmodule QtGui
Definition qimage.h:37
\inmodule QtCore
Definition qmimedata.h:16
QByteArray data(const QString &mimetype) const
Returns the data stored in the object in the format described by the MIME type specified by mimeType.
virtual QStringList formats() const
Returns a list of formats supported by the object.
void setUrls(const QList< QUrl > &urls)
Sets the URLs stored in the MIME data object to those specified by urls.
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:185
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:188
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromStdString(const std::string &s)
Definition qstring.h:1447
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3368
QString toLocalFile() const
Returns the path of this URL formatted as a local file path.
Definition qurl.cpp:3425
Combined button and popup list for selecting options.
@ CaseSensitive
DropAction
@ CopyAction
@ ActionMask
@ IgnoreAction
@ MoveAction
@ TargetMoveAction
@ LinkAction
constexpr Initialization Uninitialized
void syncCSSClassWith(emscripten::val element, std::string cssClassName, bool flag)
Definition qwasmdom.cpp:240
QPointF mapPoint(emscripten::val source, emscripten::val target, const QPointF &point)
Definition qwasmdom.cpp:250
void drawImageToWebImageDataArray(const QImage &sourceImage, emscripten::val destinationImageData, const QRect &sourceRect)
Definition qwasmdom.cpp:261
#define Q_FUNC_INFO
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define qWarning
Definition qlogging.h:166
GLboolean GLboolean GLboolean GLboolean a
[7]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum target
GLenum GLuint GLintptr offset
GLint GLsizei GLsizei GLenum format
GLsizei GLsizei GLchar * source
GLenum GLenum GLsizei void * row
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
decltype(openFileForWriting({})) File
Definition main.cpp:76
QFile file
[0]
settings remove("monkey")
QMimeData * mimeData
QSharedPointer< T > other(t)
[5]
QGraphicsItem * item
QList< QTreeWidgetItem * > items
DataTransfer(emscripten::val webDataTransfer)
Definition qwasmdom.cpp:40
void toMimeDataWithFile(std::function< void(QMimeData *)> callback)
Definition qwasmdom.cpp:87
void setDragImage(emscripten::val element, const QPoint &hotspot)
Definition qwasmdom.cpp:54
void setDataFromMimeData(const QMimeData &mimeData)
Definition qwasmdom.cpp:71
DataTransfer & operator=(const DataTransfer &other)
emscripten::val webDataTransfer
Definition qwasmdom.h:46
void setData(std::string format, std::string data)
Definition qwasmdom.cpp:60
void setDropAction(Qt::DropAction dropAction)
Definition qwasmdom.cpp:66