Resolve the ownership problems in qClipboardPasteTo
A deleted mMimeData instance might be referenced if more that one file item is present in the clipboadData. This fix uses a continuation mechanism to only run the actual qWasmClipboardPaste when all the data has been collected. The ownership of QMimeData is then transferred to the global QClipboardData q_clipboardData. Fixes: QTBUG-108841 Change-Id: I16def48d70ebbffc68462ed74ccd9ee8ee8053de Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
effd3b4d83
commit
4673f00277
@ -80,67 +80,81 @@ static void qWasmClipboardPaste(QMimeData *mData)
|
|||||||
|
|
||||||
static void qClipboardPasteTo(val dataTransfer)
|
static void qClipboardPasteTo(val dataTransfer)
|
||||||
{
|
{
|
||||||
val clipboardData = dataTransfer["clipboardData"];
|
enum class ItemKind {
|
||||||
val types = clipboardData["types"];
|
File,
|
||||||
int typesCount = types["length"].as<int>();
|
String,
|
||||||
std::string stdMimeFormat;
|
};
|
||||||
QMimeData *mMimeData = new QMimeData;
|
|
||||||
for (int i = 0; i < typesCount; i++) {
|
|
||||||
stdMimeFormat = types[i].as<std::string>();
|
|
||||||
QString mimeFormat = QString::fromStdString(stdMimeFormat);
|
|
||||||
if (mimeFormat.contains("STRING", Qt::CaseSensitive) || mimeFormat.contains("TEXT", Qt::CaseSensitive))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mimeFormat.contains("text")) {
|
struct Data
|
||||||
// also "text/plain;charset=utf-8"
|
{
|
||||||
// "UTF8_STRING" "MULTIPLE"
|
std::unique_ptr<QMimeData> data;
|
||||||
val mimeData = clipboardData.call<val>("getData", val(stdMimeFormat)); // as DataTransfer
|
int fileCount = 0;
|
||||||
|
int doneCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
const QString qstr = QWasmString::toQString(mimeData);
|
auto sharedData = std::make_shared<Data>();
|
||||||
|
sharedData->data = std::make_unique<QMimeData>();
|
||||||
|
|
||||||
if (qstr.length() > 0) {
|
auto continuation = [sharedData]() {
|
||||||
if (mimeFormat.contains("text/html")) {
|
Q_ASSERT(sharedData->doneCount <= sharedData->fileCount);
|
||||||
mMimeData->setHtml(qstr);
|
if (sharedData->doneCount < sharedData->fileCount)
|
||||||
} else if (mimeFormat.isEmpty() || mimeFormat.contains("text/plain")) {
|
return;
|
||||||
mMimeData->setText(qstr); // the type can be empty
|
|
||||||
} else {
|
if (!sharedData->data->formats().isEmpty())
|
||||||
mMimeData->setData(mimeFormat, qstr.toLocal8Bit());}
|
qWasmClipboardPaste(sharedData->data.release());
|
||||||
|
};
|
||||||
|
|
||||||
|
const val clipboardData = dataTransfer["clipboardData"];
|
||||||
|
const val items = clipboardData["items"];
|
||||||
|
for (int i = 0; i < items["length"].as<int>(); ++i) {
|
||||||
|
const val item = items[i];
|
||||||
|
const auto itemKind =
|
||||||
|
item["kind"].as<std::string>() == "string" ? ItemKind::String : ItemKind::File;
|
||||||
|
const auto itemMimeType = QString::fromStdString(item["type"].as<std::string>());
|
||||||
|
|
||||||
|
switch (itemKind) {
|
||||||
|
case ItemKind::File: {
|
||||||
|
++sharedData->fileCount;
|
||||||
|
|
||||||
|
qstdweb::File file(item.call<emscripten::val>("getAsFile"));
|
||||||
|
|
||||||
|
QByteArray fileContent(file.size(), Qt::Uninitialized);
|
||||||
|
file.stream(fileContent.data(),
|
||||||
|
[continuation, itemMimeType, fileContent, sharedData]() {
|
||||||
|
if (!fileContent.isEmpty()) {
|
||||||
|
if (itemMimeType.startsWith("image/")) {
|
||||||
|
QImage image;
|
||||||
|
image.loadFromData(fileContent, nullptr);
|
||||||
|
sharedData->data->setImageData(image);
|
||||||
|
} else {
|
||||||
|
sharedData->data->setData(itemMimeType, fileContent.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++sharedData->doneCount;
|
||||||
|
continuation();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ItemKind::String:
|
||||||
|
if (itemMimeType.contains("STRING", Qt::CaseSensitive)
|
||||||
|
|| itemMimeType.contains("TEXT", Qt::CaseSensitive)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
const QString data = QWasmString::toQString(
|
||||||
val items = clipboardData["items"];
|
clipboardData.call<val>("getData", val(itemMimeType.toStdString())));
|
||||||
|
|
||||||
int itemsCount = items["length"].as<int>();
|
if (!data.isEmpty()) {
|
||||||
// handle data
|
if (itemMimeType == "text/html")
|
||||||
for (int i = 0; i < itemsCount; i++) {
|
sharedData->data->setHtml(data);
|
||||||
val item = items[i];
|
else if (itemMimeType.isEmpty() || itemMimeType == "text/plain")
|
||||||
val clipboardFile = item.call<emscripten::val>("getAsFile"); // string kind is handled above
|
sharedData->data->setText(data); // the type can be empty
|
||||||
if (clipboardFile.isUndefined() || item["kind"].as<std::string>() == "string" ) {
|
else
|
||||||
continue;
|
sharedData->data->setData(itemMimeType, data.toLocal8Bit());
|
||||||
}
|
}
|
||||||
qstdweb::File file(clipboardFile);
|
break;
|
||||||
|
|
||||||
mimeFormat = QString::fromStdString(file.type());
|
|
||||||
QByteArray fileContent;
|
|
||||||
fileContent.resize(file.size());
|
|
||||||
|
|
||||||
file.stream(fileContent.data(), [=]() {
|
|
||||||
if (!fileContent.isEmpty()) {
|
|
||||||
|
|
||||||
if (mimeFormat.contains("image")) {
|
|
||||||
QImage image;
|
|
||||||
image.loadFromData(fileContent, nullptr);
|
|
||||||
mMimeData->setImageData(image);
|
|
||||||
} else {
|
|
||||||
mMimeData->setData(mimeFormat,fileContent.data());
|
|
||||||
}
|
|
||||||
qWasmClipboardPaste(mMimeData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} // next item
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!mMimeData->formats().isEmpty())
|
continuation();
|
||||||
qWasmClipboardPaste(mMimeData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
|
EMSCRIPTEN_BINDINGS(qtClipboardModule) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user