Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylanddataoffer.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
8#if QT_CONFIG(xdg_desktop_portal_file_transfer)
9#include <QtGui/private/qxdgdesktopportalfiletransfer_p.h>
10#endif
11
12#include <QtCore/private/qcore_unix_p.h>
13#include <QtGui/private/qguiapplication_p.h>
14#include <qpa/qplatformclipboard.h>
15
16#include <QtCore/QDebug>
17
18using namespace std::chrono;
19using namespace Qt::StringLiterals;
20QT_BEGIN_NAMESPACE
21
22namespace QtWaylandClient {
23
25{
26 return QStringLiteral("text/plain");
27}
28
30{
31 return QStringLiteral("text/plain;charset=utf-8");
32}
33
35{
36 return QStringLiteral("text/uri-list");
37}
38
40{
41 return QStringLiteral("text/x-moz-url");
42}
43
45{
46 return QStringLiteral("application/vnd.portal.filetransfer");
47}
48
49static QByteArray convertData(const QString &originalMime, const QString &newMime, const QByteArray &data)
50{
51 if (originalMime == newMime)
52 return data;
53
54 // Convert text/x-moz-url, which is an UTF-16 string of
55 // URL and page title pairs, all separated by line breaks, to text/uri-list.
56 // see also qtbase/src/plugins/platforms/xcb/qxcbmime.cpp
57 if (originalMime == uriList() && newMime == mozUrl()) {
58 if (data.size() > 1) {
59 const quint8 byte0 = data.at(0);
60 const quint8 byte1 = data.at(1);
61
62 if ((byte0 == 0xff && byte1 == 0xfe) || (byte0 == 0xfe && byte1 == 0xff)
63 || (byte0 != 0 && byte1 == 0) || (byte0 == 0 && byte1 != 0)) {
64 QByteArray converted;
65 const QString str = QString::fromUtf16(
66 reinterpret_cast<const char16_t *>(data.constData()), data.size() / 2);
67 if (!str.isNull()) {
68 const auto urls = QStringView{str}.split(u'\n');
69 // Only the URL is interesting, skip the page title.
70 for (int i = 0; i < urls.size(); i += 2) {
71 const QUrl url(urls.at(i).trimmed().toString());
72 if (url.isValid()) {
73 converted += url.toEncoded();
74 converted += "\r\n";
75 }
76 }
77 }
78 return converted;
79 // 8 byte encoding, remove a possible 0 at the end.
80 } else {
81 QByteArray converted = data;
82 if (converted.endsWith('\0'))
83 converted.chop(1);
84 converted += "\r\n";
85 return converted;
86 }
87 }
88 }
89 return data;
90}
91
98
103
104
106{
107 if (m_mimeData->formats().isEmpty())
108 return QString();
109
110 return m_mimeData->formats().first();
111}
112
117
119{
120 if (version() < 3) {
121 return Qt::MoveAction | Qt::CopyAction;
122 }
123
124 return m_supportedActions;
125}
126
132
137
139{
141 // This is the compositor telling the drag target what action it should perform
142 // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action?
143}
144
153
155 : m_dataOffer(dataOffer)
156{
157}
158
162
163void QWaylandMimeData::appendFormat(const QString &mimeType)
164{
165 // "DELETE" is a potential leftover from XdndActionMode sent by e.g. Firefox, ignore it.
166 if (mimeType != QLatin1String("DELETE")) {
167 m_types << mimeType;
168 m_data.remove(mimeType); // Clear previous contents
169 }
170}
171
172bool QWaylandMimeData::hasFormat_sys(const QString &mimeType) const
173{
174 return formats().contains(mimeType);
175}
176
178{
179 QStringList types;
180 types.reserve(m_types.size());
181
182 for (const QString &type : m_types) {
183 QString mime = type;
184
185 if (mime == utf8Text()) {
186 mime = plainText();
187 } else if (mime == mozUrl()) {
188 mime = uriList();
189 }
190
191 if (!types.contains(mime)) {
192 types << mime;
193 }
194 }
195
196 return types;
197}
198
199QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QMetaType type) const
200{
201 Q_UNUSED(type);
202 auto it = m_data.constFind(mimeType);
203 if (it != m_data.constEnd())
204 return *it;
205
206 QString mime = mimeType;
207
208 if (!m_types.contains(mimeType)) {
209 if (mimeType == plainText() && m_types.contains(utf8Text()))
210 mime = utf8Text();
211 else if (mimeType == uriList() && m_types.contains(mozUrl()))
212 mime = mozUrl();
213 else
214 return QVariant();
215 }
216
217#if QT_CONFIG(xdg_desktop_portal_file_transfer)
218 const bool retrieveFilesFromPortal = mimeType == uriList() && m_types.contains(QXdgDesktopPortalFileTransfer::fileTransferMimeType());
219 if (retrieveFilesFromPortal) {
220 mime = QXdgDesktopPortalFileTransfer::fileTransferMimeType();
221 }
222#endif
223
224 QByteArray content = readData(mime).value_or(QByteArray());
225
226 content = convertData(mimeType, mime, content);
227
228#if QT_CONFIG(xdg_desktop_portal_file_transfer)
229 if (retrieveFilesFromPortal && !content.isEmpty()) {
230 const auto paths = QXdgDesktopPortalFileTransfer::retrieveFiles(QString::fromUtf8(content));
231 if (!paths.empty()) {
232 content.clear();
233 for (const auto &path : paths) {
234 content += QUrl::fromLocalFile(path).toEncoded();
235 content += "\r\n";
236 }
237 } else {
238 qCInfo(lcQpaWayland) << "Failed retrieving files, falling back to uris";
239 content = readData(mime).value_or(QByteArray());
240 }
241 }
242#endif
243
244 if (mimeType != portalFileTransfer())
245 m_data.insert(mimeType, content);
246
247 return content;
248}
249
250std::optional<QByteArray> QWaylandMimeData::readData(const QString &mimeType) const
251{
252 int pipefd[2];
253 if (qt_safe_pipe(pipefd) == -1) {
254 qWarning("QWaylandMimeData: pipe2() failed");
255 return std::nullopt;
256 }
257
258 m_dataOffer->startReceiving(mimeType, pipefd[1]);
259
260 close(pipefd[1]);
261 const QScopeGuard closeGuard([fd = pipefd[0]] { close(fd); });
262
263 QByteArray data;
264
265 struct pollfd readset;
266 readset.fd = pipefd[0];
267 readset.events = POLLIN;
268
269 Q_FOREVER {
270 int ready = qt_safe_poll(&readset, 1, QDeadlineTimer(1s));
271 if (ready < 0) {
272 qCWarning(lcQpaWayland) << "QWaylandDataOffer: qt_safe_poll() failed while reading data for mimeType %s", qPrintable(mimeType);
273 return std::nullopt;
274 } else if (ready == 0) {
275 qCWarning(lcQpaWayland, "QWaylandDataOffer: timeout reading from pipe while reading data for mimeType %s", qPrintable(mimeType));
276 return std::nullopt;
277 } else {
278 char buf[4096];
279 int n = QT_READ(pipefd[0], buf, sizeof buf);
280
281 if (n < 0) {
282 qWarning(lcQpaWayland, "QWaylandDataOffer: read() failed");
283 return std::nullopt;
284 } else if (n == 0) {
285 return data;
286 } else if (n > 0) {
287 data.append(buf, n);
288 }
289 }
290 }
291}
292
293}
294
295QT_END_NAMESPACE
QVariant retrieveData_sys(const QString &mimeType, QMetaType type) const override
void appendFormat(const QString &mimeType)
QStringList formats_sys() const override
QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer)
bool hasFormat_sys(const QString &mimeType) const override
static QString portalFileTransfer()
static QByteArray convertData(const QString &originalMime, const QString &newMime, const QByteArray &data)
static QString utf8Text()
static QString mozUrl()
static QString uriList()
static QString plainText()