-
Notifications
You must be signed in to change notification settings - Fork 462
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This does not seem safe.
- Loading branch information
Showing
9 changed files
with
253 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
#include "common/clipboarddataguard.h" | ||
#include "common/common.h" | ||
#include "common/log.h" | ||
#include "common/mimetypes.h" | ||
#include "common/textdata.h" | ||
|
||
#include <QImage> | ||
#include <QList> | ||
#include <QMimeData> | ||
#include <QObject> | ||
#include <QUrl> | ||
#include <QCoreApplication> | ||
|
||
namespace { | ||
|
||
int clipboardCopyTimeoutMs() | ||
{ | ||
static bool ok = false; | ||
static int ms = qgetenv("COPYQ_CLIPBOARD_COPY_TIMEOUT_MS").toInt(&ok); | ||
return ok ? ms : 5000; | ||
} | ||
|
||
const QMimeData *dummyMimeData() | ||
{ | ||
static QMimeData mimeData; | ||
return &mimeData; | ||
} | ||
|
||
class ElapsedGuard { | ||
public: | ||
explicit ElapsedGuard(const QString &type, const QString &format) | ||
: m_type(type) | ||
, m_format(format) | ||
{ | ||
COPYQ_LOG_VERBOSE( QStringLiteral("Accessing [%1:%2]").arg(type, format) ); | ||
m_elapsed.start(); | ||
} | ||
|
||
~ElapsedGuard() | ||
{ | ||
const auto t = m_elapsed.elapsed(); | ||
if (t > 500) | ||
log( QStringLiteral("ELAPSED %1 ms accessing [%2:%3]").arg(t).arg(m_type, m_format), LogWarning ); | ||
} | ||
private: | ||
QString m_type; | ||
QString m_format; | ||
QElapsedTimer m_elapsed; | ||
}; | ||
|
||
} //namespace | ||
|
||
ClipboardDataGuard::ClipboardDataGuard(const QMimeData *data, const long int *clipboardSequenceNumber) | ||
: m_data(data) | ||
, m_clipboardSequenceNumber(clipboardSequenceNumber) | ||
, m_clipboardSequenceNumberOriginal(clipboardSequenceNumber ? *clipboardSequenceNumber : 0) | ||
{ | ||
// This uses simple connection to ensure pointer is not destroyed | ||
// instead of QPointer to work around a possible Qt bug: | ||
// - https://bugzilla.redhat.com/show_bug.cgi?id=2320093 | ||
// - https://bugzilla.redhat.com/show_bug.cgi?id=2326881 | ||
m_connection = QObject::connect(m_data, &QObject::destroyed, [this](){ | ||
m_data = nullptr; | ||
log( QByteArrayLiteral("Aborting clipboard cloning: Data deleted"), LogWarning ); | ||
}); | ||
m_timerExpire.start(); | ||
} | ||
|
||
ClipboardDataGuard::~ClipboardDataGuard() | ||
{ | ||
QObject::disconnect(m_connection); | ||
} | ||
|
||
QStringList ClipboardDataGuard::formats() | ||
{ | ||
ElapsedGuard _(QStringLiteral(), QStringLiteral("formats")); | ||
return mimeData()->formats(); | ||
} | ||
|
||
bool ClipboardDataGuard::hasFormat(const QString &mime) | ||
{ | ||
ElapsedGuard _(QStringLiteral("hasFormat"), mime); | ||
return mimeData()->hasFormat(mime); | ||
} | ||
|
||
QByteArray ClipboardDataGuard::data(const QString &mime) | ||
{ | ||
ElapsedGuard _(QStringLiteral("data"), mime); | ||
return mimeData()->data(mime); | ||
} | ||
|
||
QList<QUrl> ClipboardDataGuard::urls() | ||
{ | ||
ElapsedGuard _(QStringLiteral(), QStringLiteral("urls")); | ||
return mimeData()->urls(); | ||
} | ||
|
||
QString ClipboardDataGuard::text() | ||
{ | ||
ElapsedGuard _(QStringLiteral(), QStringLiteral("text")); | ||
return mimeData()->text(); | ||
} | ||
|
||
bool ClipboardDataGuard::hasText() | ||
{ | ||
ElapsedGuard _(QStringLiteral(), QStringLiteral("hasText")); | ||
return mimeData()->hasText(); | ||
} | ||
|
||
QImage ClipboardDataGuard::getImageData() | ||
{ | ||
ElapsedGuard _(QStringLiteral(), QStringLiteral("imageData")); | ||
|
||
// NOTE: Application hangs if using multiple sessions and | ||
// calling QMimeData::hasImage() on X11 clipboard. | ||
QImage image = mimeData()->imageData().value<QImage>(); | ||
if ( image.isNull() ) { | ||
image.loadFromData( data(QStringLiteral("image/png")), "png" ); | ||
if ( image.isNull() ) { | ||
image.loadFromData( data(QStringLiteral("image/bmp")), "bmp" ); | ||
} | ||
} | ||
COPYQ_LOG_VERBOSE( | ||
QStringLiteral("Image is %1") | ||
.arg(image.isNull() ? QStringLiteral("invalid") : QStringLiteral("valid")) ); | ||
return image; | ||
} | ||
|
||
QByteArray ClipboardDataGuard::getUtf8Data(const QString &format) | ||
{ | ||
ElapsedGuard _(QStringLiteral("UTF8"), format); | ||
|
||
if (format == mimeUriList) { | ||
QByteArray bytes; | ||
for ( const auto &url : urls() ) { | ||
if ( !bytes.isEmpty() ) | ||
bytes += '\n'; | ||
bytes += url.toString().toUtf8(); | ||
} | ||
return bytes; | ||
} | ||
|
||
if ( format == mimeText && !hasFormat(mimeText) ) | ||
return text().toUtf8(); | ||
|
||
if ( format.startsWith(QLatin1String("text/")) ) | ||
return dataToText( data(format), format ).toUtf8(); | ||
|
||
return data(format); | ||
} | ||
|
||
bool ClipboardDataGuard::isExpired() { | ||
if (!m_data) | ||
return true; | ||
|
||
if (m_clipboardSequenceNumber && *m_clipboardSequenceNumber != m_clipboardSequenceNumberOriginal) { | ||
m_data = nullptr; | ||
log( QByteArrayLiteral("Aborting clipboard cloning: Clipboard changed again"), LogWarning ); | ||
return true; | ||
} | ||
|
||
if (m_timerExpire.elapsed() > clipboardCopyTimeoutMs()) { | ||
m_data = nullptr; | ||
log( QByteArrayLiteral("Aborting clipboard cloning: Data access took too long"), LogWarning ); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
const QMimeData *ClipboardDataGuard::mimeData() | ||
{ | ||
if (isExpired()) | ||
return dummyMimeData(); | ||
|
||
if (m_timerExpire.elapsed() > 100) { | ||
QCoreApplication::processEvents(); | ||
if (isExpired()) | ||
return dummyMimeData(); | ||
} | ||
|
||
return m_data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
#pragma once | ||
|
||
#include <QElapsedTimer> | ||
#include <QtContainerFwd> | ||
#include <QMetaType> | ||
|
||
class QMimeData; | ||
class QImage; | ||
class QUrl; | ||
|
||
// Avoids accessing old clipboard/drag'n'drop data. | ||
class ClipboardDataGuard final { | ||
public: | ||
explicit ClipboardDataGuard( | ||
const QMimeData *data, | ||
const long int *clipboardSequenceNumber = nullptr); | ||
|
||
~ClipboardDataGuard(); | ||
|
||
QStringList formats(); | ||
bool hasFormat(const QString &mime); | ||
|
||
QByteArray data(const QString &mime); | ||
|
||
QList<QUrl> urls(); | ||
QString text(); | ||
bool hasText(); | ||
QImage getImageData(); | ||
QByteArray getUtf8Data(const QString &format); | ||
bool isExpired(); | ||
const QMimeData *mimeData(); | ||
|
||
private: | ||
const QMimeData *m_data; | ||
QElapsedTimer m_timerExpire; | ||
const long int *m_clipboardSequenceNumber; | ||
long int m_clipboardSequenceNumberOriginal; | ||
QMetaObject::Connection m_connection; | ||
}; |
Oops, something went wrong.