macOS: clean up clipboard code

Replace flavor with uti, remove unused parameters, remove unused
hasOSFlavor function. Hide QMacMimeData as an implementation detail, it
is generally undefined behavior to cast an object to a type it is not an
instance of (even if related and without new data members).

Apply const, fix coding style.

Task-number: QTBUG-93632
Change-Id: I3bc05a72bc47c78958f26211211e734387fbb024
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Volker Hilsheimer 2022-11-03 14:37:43 +01:00
parent 2c54086317
commit de0ff0d428
3 changed files with 61 additions and 108 deletions

View File

@ -309,7 +309,7 @@ QStringList QCocoaDropData::formats_sys() const
return formats; return formats;
} }
QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QMetaType type) const QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QMetaType) const
{ {
QVariant data; QVariant data;
PasteboardRef board; PasteboardRef board;
@ -317,7 +317,7 @@ QVariant QCocoaDropData::retrieveData_sys(const QString &mimeType, QMetaType typ
qDebug("DnD: Cannot get PasteBoard!"); qDebug("DnD: Cannot get PasteBoard!");
return data; return data;
} }
data = QMacPasteboard(board, QMacMime::HandlerScope::DnD).retrieveData(mimeType, type); data = QMacPasteboard(board, QMacMime::HandlerScope::DnD).retrieveData(mimeType);
CFRelease(board); CFRelease(board);
return data; return data;
} }

View File

@ -11,7 +11,6 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QMacMimeData;
class QMacMime; class QMacMime;
class QMacPasteboard class QMacPasteboard
@ -22,14 +21,14 @@ private:
struct Promise { struct Promise {
Promise() : itemId(0), converter(nullptr) { } Promise() : itemId(0), converter(nullptr) { }
static Promise eagerPromise(int itemId, QMacMime *c, QString m, QMacMimeData *d, int o = 0); static Promise eagerPromise(int itemId, const QMacMime *c, const QString &m, QMimeData *d, int o = 0);
static Promise lazyPromise(int itemId, QMacMime *c, QString m, QMacMimeData *d, int o = 0); static Promise lazyPromise(int itemId, const QMacMime *c, const QString &m, QMimeData *d, int o = 0);
Promise(int itemId, QMacMime *c, QString m, QMacMimeData *md, int o, DataRequestType drt); Promise(int itemId, const QMacMime *c, const QString &m, QMimeData *md, int o, DataRequestType drt);
int itemId, offset; int itemId, offset;
QMacMime *converter; const QMacMime *converter;
QString mime; QString mime;
QPointer<QMacMimeData> mimeData; QPointer<QMimeData> mimeData;
QVariant variantData; QVariant variantData;
DataRequestType dataRequestType; DataRequestType dataRequestType;
// QMimeData can be set from QVariant, holding // QMimeData can be set from QVariant, holding
@ -54,8 +53,7 @@ public:
QMacPasteboard(CFStringRef name=nullptr, QMacMime::HandlerScope scope = QMacMime::HandlerScope::All); QMacPasteboard(CFStringRef name=nullptr, QMacMime::HandlerScope scope = QMacMime::HandlerScope::All);
~QMacPasteboard(); ~QMacPasteboard();
bool hasFlavor(QString flavor) const; bool hasUti(const QString &uti) const;
bool hasOSType(int c_flavor) const;
PasteboardRef pasteBoard() const; PasteboardRef pasteBoard() const;
QMimeData *mimeData() const; QMimeData *mimeData() const;
@ -64,7 +62,7 @@ public:
QStringList formats() const; QStringList formats() const;
bool hasFormat(const QString &format) const; bool hasFormat(const QString &format) const;
QVariant retrieveData(const QString &format, QMetaType) const; QVariant retrieveData(const QString &format) const;
void clear(); void clear();
bool sync() const; bool sync() const;

View File

@ -54,12 +54,12 @@ private:
QMacMimeData(); QMacMimeData();
}; };
QMacPasteboard::Promise::Promise(int itemId, QMacMime *c, QString m, QMacMimeData *md, int o, DataRequestType drt) QMacPasteboard::Promise::Promise(int itemId, const QMacMime *c, const QString &m, QMimeData *md, int o, DataRequestType drt)
: itemId(itemId), offset(o), converter(c), mime(m), dataRequestType(drt) : itemId(itemId), offset(o), converter(c), mime(m), dataRequestType(drt)
{ {
// Request the data from the application immediately for eager requests. // Request the data from the application immediately for eager requests.
if (dataRequestType == QMacPasteboard::EagerRequest) { if (dataRequestType == QMacPasteboard::EagerRequest) {
variantData = md->variantData(m); variantData = static_cast<QMacMimeData *>(md)->variantData(m);
isPixmap = variantData.metaType().id() == QMetaType::QPixmap; isPixmap = variantData.metaType().id() == QMetaType::QPixmap;
mimeData = nullptr; mimeData = nullptr;
} else { } else {
@ -84,11 +84,10 @@ QMacPasteboard::QMacPasteboard(QMacMime::HandlerScope scope)
mac_mime_source = false; mac_mime_source = false;
paste = nullptr; paste = nullptr;
OSStatus err = PasteboardCreate(nullptr, &paste); OSStatus err = PasteboardCreate(nullptr, &paste);
if (err == noErr) { if (err == noErr)
PasteboardSetPromiseKeeper(paste, promiseKeeper, this); PasteboardSetPromiseKeeper(paste, promiseKeeper, this);
} else { else
qDebug("PasteBoard: Error creating pasteboard: [%d]", (int)err); qDebug("PasteBoard: Error creating pasteboard: [%d]", (int)err);
}
resolvingBeforeDestruction = false; resolvingBeforeDestruction = false;
} }
@ -119,13 +118,13 @@ QMacPasteboard::~QMacPasteboard()
CFRelease(paste); CFRelease(paste);
} }
PasteboardRef PasteboardRef QMacPasteboard::pasteBoard() const
QMacPasteboard::pasteBoard() const
{ {
return paste; return paste;
} }
OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id, CFStringRef uti, void *_qpaste) OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id,
CFStringRef uti, void *_qpaste)
{ {
QMacPasteboard *qpaste = (QMacPasteboard*)_qpaste; QMacPasteboard *qpaste = (QMacPasteboard*)_qpaste;
const long promise_id = (long)id; const long promise_id = (long)id;
@ -136,7 +135,7 @@ OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id,
const QString flavorAsQString = QString::fromCFString(uti); const QString flavorAsQString = QString::fromCFString(uti);
QMacPasteboard::Promise promise; QMacPasteboard::Promise promise;
for (int i = 0; i < qpaste->promises.size(); i++){ for (int i = 0; i < qpaste->promises.size(); i++){
QMacPasteboard::Promise tmp = qpaste->promises[i]; const QMacPasteboard::Promise tmp = qpaste->promises[i];
if (!availableConverters.contains(tmp.converter)) { if (!availableConverters.contains(tmp.converter)) {
// promise.converter is a pointer initialized by the value found // promise.converter is a pointer initialized by the value found
// in QMacMime's global list of QMacMimes. // in QMacMime's global list of QMacMimes.
@ -157,7 +156,7 @@ OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id,
// we have promised this data, but won't be able to convert, so return null data. // we have promised this data, but won't be able to convert, so return null data.
// This helps in making the application/x-qt-mime-type-name hidden from normal use. // This helps in making the application/x-qt-mime-type-name hidden from normal use.
QByteArray ba; QByteArray ba;
QCFType<CFDataRef> data = CFDataCreate(nullptr, (UInt8*)ba.constData(), ba.size()); const QCFType<CFDataRef> data = CFDataCreate(nullptr, (UInt8*)ba.constData(), ba.size());
PasteboardPutItemFlavor(paste, id, uti, data, kPasteboardFlavorNoFlags); PasteboardPutItemFlavor(paste, id, uti, data, kPasteboardFlavorNoFlags);
return noErr; return noErr;
} }
@ -182,23 +181,22 @@ OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id,
"Cannot keep promise, data contains QPixmap and requires livining QGuiApplication"); "Cannot keep promise, data contains QPixmap and requires livining QGuiApplication");
return cantGetFlavorErr; return cantGetFlavorErr;
} }
promiseData = promise.mimeData->variantData(promise.mime); promiseData = static_cast<QMacMimeData *>(promise.mimeData.data())->variantData(promise.mime);
} }
} else { } else {
promiseData = promise.variantData; promiseData = promise.variantData;
} }
QList<QByteArray> md = promise.converter->convertFromMime(promise.mime, promiseData, flavorAsQString); const QList<QByteArray> md = promise.converter->convertFromMime(promise.mime, promiseData, flavorAsQString);
if (md.size() <= promise.offset) if (md.size() <= promise.offset)
return cantGetFlavorErr; return cantGetFlavorErr;
const QByteArray &ba = md[promise.offset]; const QByteArray &ba = md[promise.offset];
QCFType<CFDataRef> data = CFDataCreate(nullptr, (UInt8*)ba.constData(), ba.size()); const QCFType<CFDataRef> data = CFDataCreate(nullptr, (UInt8*)ba.constData(), ba.size());
PasteboardPutItemFlavor(paste, id, uti, data, kPasteboardFlavorNoFlags); PasteboardPutItemFlavor(paste, id, uti, data, kPasteboardFlavorNoFlags);
return noErr; return noErr;
} }
bool bool QMacPasteboard::hasUti(const QString &uti) const
QMacPasteboard::hasOSType(int c_flavor) const
{ {
if (!paste) if (!paste)
return false; return false;
@ -209,48 +207,8 @@ QMacPasteboard::hasOSType(int c_flavor) const
if (PasteboardGetItemCountSafe(paste, &cnt) || !cnt) if (PasteboardGetItemCountSafe(paste, &cnt) || !cnt)
return false; return false;
qCDebug(lcQpaClipboard, "PasteBoard: hasOSType [%c%c%c%c]", (c_flavor>>24)&0xFF, (c_flavor>>16)&0xFF, qCDebug(lcQpaClipboard, "PasteBoard: hasUti [%s]", qPrintable(uti));
(c_flavor>>8)&0xFF, (c_flavor>>0)&0xFF); const QCFString c_uti(uti);
for (uint index = 1; index <= cnt; ++index) {
PasteboardItemID id;
if (PasteboardGetItemIdentifier(paste, index, &id) != noErr)
return false;
QCFType<CFArrayRef> types;
if (PasteboardCopyItemFlavors(paste, id, &types ) != noErr)
return false;
const int type_count = CFArrayGetCount(types);
for (int i = 0; i < type_count; ++i) {
CFStringRef uti = (CFStringRef)CFArrayGetValueAtIndex(types, i);
CFStringRef preferredTag = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassOSType);
const int os_flavor = UTGetOSTypeFromString(preferredTag);
if (preferredTag)
CFRelease(preferredTag);
if (os_flavor == c_flavor) {
qCDebug(lcQpaClipboard, " - Found!");
return true;
}
}
}
qCDebug(lcQpaClipboard, " - NotFound!");
return false;
}
bool
QMacPasteboard::hasFlavor(QString c_flavor) const
{
if (!paste)
return false;
sync();
ItemCount cnt = 0;
if (PasteboardGetItemCountSafe(paste, &cnt) || !cnt)
return false;
qCDebug(lcQpaClipboard, "PasteBoard: hasFlavor [%s]", qPrintable(c_flavor));
for (uint index = 1; index <= cnt; ++index) { for (uint index = 1; index <= cnt; ++index) {
PasteboardItemID id; PasteboardItemID id;
@ -258,7 +216,7 @@ QMacPasteboard::hasFlavor(QString c_flavor) const
return false; return false;
PasteboardFlavorFlags flags; PasteboardFlavorFlags flags;
if (PasteboardGetItemFlavorFlags(paste, id, QCFString(c_flavor), &flags) == noErr) { if (PasteboardGetItemFlavorFlags(paste, id, c_uti, &flags) == noErr) {
qCDebug(lcQpaClipboard, " - Found!"); qCDebug(lcQpaClipboard, " - Found!");
return true; return true;
} }
@ -267,17 +225,20 @@ QMacPasteboard::hasFlavor(QString c_flavor) const
return false; return false;
} }
class QMacPasteboardMimeSource : public QMimeData { class QMacPasteboardMimeSource : public QMimeData
{
const QMacPasteboard *paste; const QMacPasteboard *paste;
public: public:
QMacPasteboardMimeSource(const QMacPasteboard *p) : QMimeData(), paste(p) { } QMacPasteboardMimeSource(const QMacPasteboard *p) : QMimeData(), paste(p) { }
~QMacPasteboardMimeSource() { } ~QMacPasteboardMimeSource() { }
virtual QStringList formats() const { return paste->formats(); } QStringList formats() const override { return paste->formats(); }
virtual QVariant retrieveData(const QString &format, QMetaType type) const { return paste->retrieveData(format, type); } QVariant retrieveData(const QString &format, QMetaType) const override
{
return paste->retrieveData(format);
}
}; };
QMimeData QMimeData *QMacPasteboard::mimeData() const
*QMacPasteboard::mimeData() const
{ {
if (!mime) { if (!mime) {
mac_mime_source = true; mac_mime_source = true;
@ -287,8 +248,7 @@ QMimeData
return mime; return mime;
} }
void void QMacPasteboard::setMimeData(QMimeData *mime_src, DataRequestType dataRequestType)
QMacPasteboard::setMimeData(QMimeData *mime_src, DataRequestType dataRequestType)
{ {
if (!paste) if (!paste)
return; return;
@ -310,30 +270,27 @@ QMacPasteboard::setMimeData(QMimeData *mime_src, DataRequestType dataRequestType
QString dummyMimeType("application/x-qt-mime-type-name"_L1); QString dummyMimeType("application/x-qt-mime-type-name"_L1);
if (!formats.contains(dummyMimeType)) { if (!formats.contains(dummyMimeType)) {
QByteArray dummyType = mime_src->data(dummyMimeType); QByteArray dummyType = mime_src->data(dummyMimeType);
if (!dummyType.isEmpty()) { if (!dummyType.isEmpty())
formats.append(dummyMimeType); formats.append(dummyMimeType);
}
} }
for (int f = 0; f < formats.size(); ++f) { for (const auto &mimeType : formats) {
QString mimeType = formats.at(f); for (const auto *c : availableConverters) {
for (auto *c : availableConverters) {
// Hack: The Rtf handler converts incoming Rtf to Html. We do // Hack: The Rtf handler converts incoming Rtf to Html. We do
// not want to convert outgoing Html to Rtf but instead keep // not want to convert outgoing Html to Rtf but instead keep
// posting it as Html. Skip the Rtf handler here. // posting it as Html. Skip the Rtf handler here.
if (c->utiForMime("text/html"_L1) == "public.rtf"_L1) if (c->utiForMime("text/html"_L1) == "public.rtf"_L1)
continue; continue;
QString uti(c->utiForMime(mimeType)); const QString uti(c->utiForMime(mimeType));
if (!uti.isEmpty()) { if (!uti.isEmpty()) {
QMacMimeData *mimeData = static_cast<QMacMimeData*>(mime_src);
int numItems = c->count(mime_src); const int numItems = c->count(mime_src);
for (int item = 0; item < numItems; ++item) { for (int item = 0; item < numItems; ++item) {
const NSInteger itemID = item+1; //id starts at 1 const NSInteger itemID = item + 1; //id starts at 1
//QMacPasteboard::Promise promise = (dataRequestType == QMacPasteboard::EagerRequest) ? //QMacPasteboard::Promise promise = (dataRequestType == QMacPasteboard::EagerRequest) ?
// QMacPasteboard::Promise::eagerPromise(itemID, c, mimeType, mimeData, item) : // QMacPasteboard::Promise::eagerPromise(itemID, c, mimeType, mimeData, item) :
// QMacPasteboard::Promise::lazyPromise(itemID, c, mimeType, mimeData, item); // QMacPasteboard::Promise::lazyPromise(itemID, c, mimeType, mimeData, item);
QMacPasteboard::Promise promise(itemID, c, mimeType, mimeData, item, dataRequestType); const QMacPasteboard::Promise promise(itemID, c, mimeType, mime_src, item, dataRequestType);
promises.append(promise); promises.append(promise);
PasteboardPutItemFlavor(paste, reinterpret_cast<PasteboardItemID>(itemID), QCFString(uti), 0, kPasteboardFlavorNoFlags); PasteboardPutItemFlavor(paste, reinterpret_cast<PasteboardItemID>(itemID), QCFString(uti), 0, kPasteboardFlavorNoFlags);
qCDebug(lcQpaClipboard, " - adding %ld %s [%s] [%d]", qCDebug(lcQpaClipboard, " - adding %ld %s [%s] [%d]",
@ -345,8 +302,7 @@ QMacPasteboard::setMimeData(QMimeData *mime_src, DataRequestType dataRequestType
} }
} }
QStringList QStringList QMacPasteboard::formats() const
QMacPasteboard::formats() const
{ {
if (!paste) if (!paste)
return QStringList(); return QStringList();
@ -383,8 +339,7 @@ QMacPasteboard::formats() const
return ret; return ret;
} }
bool bool QMacPasteboard::hasFormat(const QString &format) const
QMacPasteboard::hasFormat(const QString &format) const
{ {
if (!paste) if (!paste)
return false; return false;
@ -420,8 +375,7 @@ QMacPasteboard::hasFormat(const QString &format) const
return false; return false;
} }
QVariant QVariant QMacPasteboard::retrieveData(const QString &format) const
QMacPasteboard::retrieveData(const QString &format, QMetaType) const
{ {
if (!paste) if (!paste)
return QVariant(); return QVariant();
@ -435,15 +389,15 @@ QMacPasteboard::retrieveData(const QString &format, QMetaType) const
qCDebug(lcQpaClipboard, "Pasteboard: retrieveData [%s]", qPrintable(format)); qCDebug(lcQpaClipboard, "Pasteboard: retrieveData [%s]", qPrintable(format));
const QList<QMacMime *> availableConverters = QMacMimeRegistry::all(scope); const QList<QMacMime *> availableConverters = QMacMimeRegistry::all(scope);
for (const auto *c : availableConverters) { for (const auto *c : availableConverters) {
QString c_flavor = c->utiForMime(format); const QString c_uti = c->utiForMime(format);
if (!c_flavor.isEmpty()) { if (!c_uti.isEmpty()) {
// Converting via PasteboardCopyItemFlavorData below will for some UITs result // Converting via PasteboardCopyItemFlavorData below will for some UITs result
// in newlines mapping to '\r' instead of '\n'. To work around this we shortcut // in newlines mapping to '\r' instead of '\n'. To work around this we shortcut
// the conversion via NSPasteboard's NSStringPboardType if possible. // the conversion via NSPasteboard's NSStringPboardType if possible.
if (c_flavor == "com.apple.traditional-mac-plain-text"_L1 if (c_uti == "com.apple.traditional-mac-plain-text"_L1
|| c_flavor == "public.utf8-plain-text"_L1 || c_uti == "public.utf8-plain-text"_L1
|| c_flavor == "public.utf16-plain-text"_L1) { || c_uti == "public.utf16-plain-text"_L1) {
QString str = qt_mac_get_pasteboardString(paste); const QString str = qt_mac_get_pasteboardString(paste);
if (!str.isEmpty()) if (!str.isEmpty())
return str; return str;
} }
@ -461,26 +415,29 @@ QMacPasteboard::retrieveData(const QString &format, QMetaType) const
const int type_count = CFArrayGetCount(types); const int type_count = CFArrayGetCount(types);
for (int i = 0; i < type_count; ++i) { for (int i = 0; i < type_count; ++i) {
CFStringRef uti = static_cast<CFStringRef>(CFArrayGetValueAtIndex(types, i)); const CFStringRef uti = static_cast<CFStringRef>(CFArrayGetValueAtIndex(types, i));
if (c_flavor == QString::fromCFString(uti)) { if (c_uti == QString::fromCFString(uti)) {
QCFType<CFDataRef> macBuffer; QCFType<CFDataRef> macBuffer;
if (PasteboardCopyItemFlavorData(paste, id, uti, &macBuffer) == noErr) { if (PasteboardCopyItemFlavorData(paste, id, uti, &macBuffer) == noErr) {
QByteArray buffer((const char *)CFDataGetBytePtr(macBuffer), CFDataGetLength(macBuffer)); QByteArray buffer((const char *)CFDataGetBytePtr(macBuffer),
CFDataGetLength(macBuffer));
if (!buffer.isEmpty()) { if (!buffer.isEmpty()) {
qCDebug(lcQpaClipboard, " - %s [%s]", qPrintable(format), qPrintable(c_flavor)); qCDebug(lcQpaClipboard, " - %s [%s]", qPrintable(format),
qPrintable(c_uti));
buffer.detach(); //detach since we release the macBuffer buffer.detach(); //detach since we release the macBuffer
retList.append(buffer); retList.append(buffer);
break; //skip to next element break; //skip to next element
} }
} }
} else { } else {
qCDebug(lcQpaClipboard, " - NoMatch %s [%s]", qPrintable(c_flavor), qPrintable(QString::fromCFString(uti))); qCDebug(lcQpaClipboard, " - NoMatch %s [%s]", qPrintable(c_uti),
qPrintable(QString::fromCFString(uti)));
} }
} }
} }
if (!retList.isEmpty()) { if (!retList.isEmpty()) {
ret = c->convertToMime(format, retList, c_flavor); ret = c->convertToMime(format, retList, c_uti);
return ret; return ret;
} }
} }
@ -495,15 +452,13 @@ void QMacPasteboard::clear_helper()
promises.clear(); promises.clear();
} }
void void QMacPasteboard::clear()
QMacPasteboard::clear()
{ {
qCDebug(lcQpaClipboard, "PasteBoard: clear!"); qCDebug(lcQpaClipboard, "PasteBoard: clear!");
clear_helper(); clear_helper();
} }
bool bool QMacPasteboard::sync() const
QMacPasteboard::sync() const
{ {
if (!paste) if (!paste)
return false; return false;