QWaylandMimeData: Check text/x-moz-urls for UTF-16

And fall back to UTF-8 if it's not.

When dragging a picture out of Chrome, it sends a simple URL as
UTF-8 under text/x-moz-urls. QXcbMime has this fall-back, too
but I originally didn't consider it necessary.

Pick-to: 6.6 6.5
Change-Id: I52378cfc354de342623e5dd3f7e1d028951e8dab
Reviewed-by: David Edmundson <davidedmundson@kde.org>
This commit is contained in:
Kai Uwe Broulik 2023-11-13 17:33:12 +01:00
parent d4220cf486
commit 1f060ee077
2 changed files with 65 additions and 10 deletions

View File

@ -42,20 +42,33 @@ static QByteArray convertData(const QString &originalMime, const QString &newMim
// see also qtbase/src/plugins/platforms/xcb/qxcbmime.cpp // see also qtbase/src/plugins/platforms/xcb/qxcbmime.cpp
if (originalMime == uriList() && newMime == mozUrl()) { if (originalMime == uriList() && newMime == mozUrl()) {
if (data.size() > 1) { if (data.size() > 1) {
const QString str = QString::fromUtf16( const quint8 byte0 = data.at(0);
reinterpret_cast<const char16_t *>(data.constData()), data.size() / 2); const quint8 byte1 = data.at(1);
if (!str.isNull()) {
if ((byte0 == 0xff && byte1 == 0xfe) || (byte0 == 0xfe && byte1 == 0xff)
|| (byte0 != 0 && byte1 == 0) || (byte0 == 0 && byte1 != 0)) {
QByteArray converted; QByteArray converted;
const auto urls = QStringView{str}.split(u'\n'); const QString str = QString::fromUtf16(
// Only the URL is interesting, skip the page title. reinterpret_cast<const char16_t *>(data.constData()), data.size() / 2);
for (int i = 0; i < urls.size(); i += 2) { if (!str.isNull()) {
const QUrl url(urls.at(i).trimmed().toString()); const auto urls = QStringView{str}.split(u'\n');
if (url.isValid()) { // Only the URL is interesting, skip the page title.
converted += url.toEncoded(); for (int i = 0; i < urls.size(); i += 2) {
converted += "\r\n"; const QUrl url(urls.at(i).trimmed().toString());
if (url.isValid()) {
converted += url.toEncoded();
converted += "\r\n";
}
} }
} }
return converted; return converted;
// 8 byte encoding, remove a possible 0 at the end.
} else {
QByteArray converted = data;
if (converted.endsWith('\0'))
converted.chop(1);
converted += "\r\n";
return converted;
} }
} }
} }

View File

@ -32,6 +32,7 @@ private slots:
void pasteAscii(); void pasteAscii();
void pasteUtf8(); void pasteUtf8();
void pasteMozUrl(); void pasteMozUrl();
void pasteSingleUtf8MozUrl();
void destroysPreviousSelection(); void destroysPreviousSelection();
void destroysSelectionWithSurface(); void destroysSelectionWithSurface();
void destroysSelectionOnLeave(); void destroysSelectionOnLeave();
@ -167,6 +168,47 @@ void tst_datadevicev1::pasteMozUrl()
QCOMPARE(window.m_urls.at(1), QUrl("https://www.example.com/")); QCOMPARE(window.m_urls.at(1), QUrl("https://www.example.com/"));
} }
void tst_datadevicev1::pasteSingleUtf8MozUrl()
{
class Window : public QRasterWindow {
public:
void mousePressEvent(QMouseEvent *) override { m_urls = QGuiApplication::clipboard()->mimeData()->urls(); }
QList<QUrl> m_urls;
};
Window window;
window.resize(64, 64);
window.show();
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
exec([&] {
auto *client = xdgSurface()->resource()->client();
auto *offer = dataDevice()->sendDataOffer(client, {"text/x-moz-url"});
connect(offer, &DataOffer::receive, [](QString mimeType, int fd) {
QFile file;
file.open(fd, QIODevice::WriteOnly, QFile::FileHandleFlag::AutoCloseHandle);
QCOMPARE(mimeType, "text/x-moz-url");
const QString content("https://www.qt.io/");
file.write(content.toUtf8());
file.close();
});
dataDevice()->sendSelection(offer);
auto *surface = xdgSurface()->m_surface;
keyboard()->sendEnter(surface); // Need to set keyboard focus according to protocol
pointer()->sendEnter(surface, {32, 32});
pointer()->sendFrame(client);
pointer()->sendButton(client, BTN_LEFT, 1);
pointer()->sendFrame(client);
pointer()->sendButton(client, BTN_LEFT, 0);
pointer()->sendFrame(client);
});
QTRY_COMPARE(window.m_urls.count(), 1);
QCOMPARE(window.m_urls.at(0), QUrl("https://www.qt.io/"));
}
void tst_datadevicev1::destroysPreviousSelection() void tst_datadevicev1::destroysPreviousSelection()
{ {
QRasterWindow window; QRasterWindow window;