Backed out 7 changesets (bug 1881229) for causing linux drag and drop crashes. a=crash

Backed out changeset f98ce3bad10b (bug 1881229)
Backed out changeset 4d5da5f79ce6 (bug 1881229)
Backed out changeset d4dfae34380d (bug 1881229)
Backed out changeset 3f9f8f388f8d (bug 1881229)
Backed out changeset 4a58c9a0cbd6 (bug 1881229)
Backed out changeset e055b85d36fc (bug 1881229)
Backed out changeset 43ab3ccffa94 (bug 1881229)
This commit is contained in:
Tamas Szentpeteri 2024-04-17 11:56:57 +03:00
parent b4df363e19
commit c7df16ffad
2 changed files with 259 additions and 254 deletions

View file

@ -139,16 +139,6 @@ static const char gTabDropType[] = "application/x-moz-tabbrowser-tab";
static const char gPortalFile[] = "application/vnd.portal.files"; static const char gPortalFile[] = "application/vnd.portal.files";
static const char gPortalFileTransfer[] = "application/vnd.portal.filetransfer"; static const char gPortalFileTransfer[] = "application/vnd.portal.filetransfer";
GdkAtom nsDragService::sTextMimeAtom;
GdkAtom nsDragService::sMozUrlTypeAtom;
GdkAtom nsDragService::sMimeListTypeAtom;
GdkAtom nsDragService::sTextUriListTypeAtom;
GdkAtom nsDragService::sTextPlainUTF8TypeAtom;
GdkAtom nsDragService::sXdndDirectSaveTypeAtom;
GdkAtom nsDragService::sTabDropTypeAtom;
GdkAtom nsDragService::sPortalFileAtom;
GdkAtom nsDragService::sPortalFileTransferAtom;
// See https://docs.gtk.org/gtk3/enum.DragResult.html // See https://docs.gtk.org/gtk3/enum.DragResult.html
static const char kGtkDragResults[][100]{ static const char kGtkDragResults[][100]{
"GTK_DRAG_RESULT_SUCCESS", "GTK_DRAG_RESULT_NO_TARGET", "GTK_DRAG_RESULT_SUCCESS", "GTK_DRAG_RESULT_NO_TARGET",
@ -171,27 +161,6 @@ static void invisibleSourceDragDataGet(GtkWidget* aWidget,
guint aInfo, guint32 aTime, guint aInfo, guint32 aTime,
gpointer aData); gpointer aData);
void DragData::UpdateData(void* aData, uint32_t aDataLen, bool aCopyData) {
if (aData != mDragData) {
ReleaseData();
mDragData = aCopyData ? moz_xmemdup(aData, aDataLen) : aData;
} else {
// We changed the data in place so make sure we don't overflow.
MOZ_RELEASE_ASSERT(aDataLen <= mDragDataLen);
}
mDragDataLen = aDataLen;
}
void DragData::ReleaseData() {
if (mDragData) {
free(mDragData);
mDragData = nullptr;
}
mDragDataLen = 0;
}
DragData::~DragData() { ReleaseData(); }
nsDragService::nsDragService() nsDragService::nsDragService()
: mScheduledTask(eDragTaskNone), : mScheduledTask(eDragTaskNone),
mTaskSource(0), mTaskSource(0),
@ -229,22 +198,12 @@ nsDragService::nsDragService()
// set up our logging module // set up our logging module
mCanDrop = false; mCanDrop = false;
mTargetDragDataReceived = false;
mTargetDragUris = nullptr;
mTargetDragData = 0;
mTargetDragDataLen = 0;
mTempFileTimerID = 0; mTempFileTimerID = 0;
mEventLoopDepth = 0; mEventLoopDepth = 0;
static std::once_flag onceFlag;
std::call_once(onceFlag, [] {
sTextMimeAtom = gdk_atom_intern(kTextMime, FALSE);
sMozUrlTypeAtom = gdk_atom_intern(gMozUrlType, FALSE);
sMimeListTypeAtom = gdk_atom_intern(gMimeListType, FALSE);
sTextUriListTypeAtom = gdk_atom_intern(gTextUriListType, FALSE);
sTextPlainUTF8TypeAtom = gdk_atom_intern(gTextPlainUTF8Type, FALSE);
sXdndDirectSaveTypeAtom = gdk_atom_intern(gXdndDirectSaveType, FALSE);
sTabDropTypeAtom = gdk_atom_intern(gTabDropType, FALSE);
sPortalFileAtom = gdk_atom_intern(gPortalFile, FALSE);
sPortalFileTransferAtom = gdk_atom_intern(gPortalFileTransfer, FALSE);
});
LOGDRAGSERVICE("nsDragService::nsDragService"); LOGDRAGSERVICE("nsDragService::nsDragService");
} }
@ -735,7 +694,8 @@ nsDragService::GetNumDropItems(uint32_t* aNumItems) {
return NS_OK; return NS_OK;
} }
if (IsTargetContextList()) { bool isList = IsTargetContextList();
if (isList) {
if (!mSourceDataItems) { if (!mSourceDataItems) {
*aNumItems = 0; *aNumItems = 0;
return NS_OK; return NS_OK;
@ -743,25 +703,38 @@ nsDragService::GetNumDropItems(uint32_t* aNumItems) {
mSourceDataItems->GetLength(aNumItems); mSourceDataItems->GetLength(aNumItems);
} else { } else {
// text/uri-list // text/uri-list
nsTArray<GdkAtom> availableDragFlavors; GdkAtom gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
GetAvailableDragFlavors(availableDragFlavors); if (!gdkFlavor) {
*aNumItems = 0;
return NS_OK;
}
GetDragData(sTextUriListTypeAtom, availableDragFlavors); nsTArray<nsCString> dragFlavors;
GetDragFlavors(dragFlavors);
GetTargetDragData(gdkFlavor, dragFlavors);
// application/vnd.portal.files // application/vnd.portal.files
if (!mDragData->HasURIs()) { if (!mTargetDragUris) {
GetDragData(sPortalFileAtom, availableDragFlavors, gdkFlavor = gdk_atom_intern(gPortalFile, FALSE);
false /* resetTargetData */); if (!gdkFlavor) {
*aNumItems = 0;
return NS_OK;
}
GetTargetDragData(gdkFlavor, dragFlavors, false /* resetTargetData */);
} }
// application/vnd.portal.filetransfer // application/vnd.portal.filetransfer
if (!mDragData->HasURIs()) { if (!mTargetDragUris) {
GetDragData(sPortalFileTransferAtom, availableDragFlavors, gdkFlavor = gdk_atom_intern(gPortalFileTransfer, FALSE);
false /* resetTargetData */); if (!gdkFlavor) {
*aNumItems = 0;
return NS_OK;
}
GetTargetDragData(gdkFlavor, dragFlavors, false /* resetTargetData */);
} }
if (mDragData->HasURIs()) { if (mTargetDragUris) {
*aNumItems = g_strv_length(mDragData->GetURIs()); *aNumItems = g_strv_length(mTargetDragUris.get());
} else } else
*aNumItems = 1; *aNumItems = 1;
} }
@ -769,11 +742,15 @@ nsDragService::GetNumDropItems(uint32_t* aNumItems) {
return NS_OK; return NS_OK;
} }
void nsDragService::GetAvailableDragFlavors( void nsDragService::GetDragFlavors(nsTArray<nsCString>& aFlavors) {
nsTArray<GdkAtom>& aAvailableFlavors) {
for (GList* tmp = gdk_drag_context_list_targets(mTargetDragContext); tmp; for (GList* tmp = gdk_drag_context_list_targets(mTargetDragContext); tmp;
tmp = tmp->next) { tmp = tmp->next) {
aAvailableFlavors.AppendElement(GDK_POINTER_TO_ATOM(tmp->data)); GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
GUniquePtr<gchar> name(gdk_atom_name(atom));
if (!name) {
continue;
}
aFlavors.AppendElement(nsCString(name.get()));
} }
} }
@ -843,7 +820,9 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
} }
// check to see if this is an internal list // check to see if this is an internal list
if (IsTargetContextList()) { bool isList = IsTargetContextList();
if (isList) {
LOGDRAGSERVICE(" Process as a list..."); LOGDRAGSERVICE(" Process as a list...");
// find a matching flavor // find a matching flavor
for (uint32_t i = 0; i < flavors.Length(); ++i) { for (uint32_t i = 0; i < flavors.Length(); ++i) {
@ -875,8 +854,8 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
nsTArray<GdkAtom> availableDragFlavors; nsTArray<nsCString> dragFlavors;
GetAvailableDragFlavors(availableDragFlavors); GetDragFlavors(dragFlavors);
// Now walk down the list of flavors. When we find one that is // Now walk down the list of flavors. When we find one that is
// actually present, copy out the data into the transferable in that // actually present, copy out the data into the transferable in that
@ -884,57 +863,63 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
for (uint32_t i = 0; i < flavors.Length(); ++i) { for (uint32_t i = 0; i < flavors.Length(); ++i) {
nsCString& flavorStr = flavors[i]; nsCString& flavorStr = flavors[i];
GdkAtom requestedFlavor; GdkAtom gdkFlavor;
if (flavorStr.EqualsLiteral(kTextMime)) { if (flavorStr.EqualsLiteral(kTextMime)) {
requestedFlavor = sTextPlainUTF8TypeAtom; gdkFlavor = gdk_atom_intern(gTextPlainUTF8Type, FALSE);
} else { } else {
requestedFlavor = gdk_atom_intern(flavorStr.get(), FALSE); gdkFlavor = gdk_atom_intern(flavorStr.get(), FALSE);
} }
LOGDRAGSERVICE(" we're getting data %s (gdk flavor %p)\n", flavorStr.get(), LOGDRAGSERVICE(" we're getting data %s (gdk flavor %p)\n", flavorStr.get(),
requestedFlavor); gdkFlavor);
bool dataFound = false; bool dataFound = false;
nsCOMPtr<nsIFile> file; nsCOMPtr<nsIFile> file;
if (requestedFlavor) { if (gdkFlavor) {
GetDragData(requestedFlavor, availableDragFlavors); GetTargetDragData(gdkFlavor, dragFlavors);
GetReachableFileFromUriList(mDragData->GetURIs(), aItemIndex, file); GetReachableFileFromUriList(mTargetDragUris.get(), aItemIndex, file);
} }
// application/vnd.portal.files // application/vnd.portal.files
if (!file || !mDragData->HasURIs()) { if (!file || !mTargetDragUris) {
LOGDRAGSERVICE(" file not found, proceed with %s flavor\n", gPortalFile); LOGDRAGSERVICE(" file not found, proceed with %s flavor\n", gPortalFile);
GetDragData(sPortalFileAtom, availableDragFlavors, gdkFlavor = gdk_atom_intern(gPortalFile, FALSE);
false /* resetTargetData */); if (gdkFlavor) {
GetReachableFileFromUriList(mDragData->GetURIs(), aItemIndex, file); GetTargetDragData(gdkFlavor, dragFlavors, false /* resetTargetData */);
GetReachableFileFromUriList(mTargetDragUris.get(), aItemIndex, file);
}
} }
// application/vnd.portal.filetransfer // application/vnd.portal.filetransfer
if (!file || !mDragData->HasURIs()) { if (!file || !mTargetDragUris) {
LOGDRAGSERVICE(" file not found, proceed with %s flavor\n", LOGDRAGSERVICE(" file not found, proceed with %s flavor\n",
gPortalFileTransfer); gPortalFileTransfer);
GetDragData(sPortalFileTransferAtom, availableDragFlavors, gdkFlavor = gdk_atom_intern(gPortalFileTransfer, FALSE);
false /* resetTargetData */); if (gdkFlavor) {
GetReachableFileFromUriList(mDragData->GetURIs(), aItemIndex, file); GetTargetDragData(gdkFlavor, dragFlavors, false /* resetTargetData */);
GetReachableFileFromUriList(mTargetDragUris.get(), aItemIndex, file);
}
} }
// Conversion from application/x-moz-file to text/uri-list // Conversion from application/x-moz-file to text/uri-list
if ((!file || !mDragData->HasURIs()) && if ((!file || !mTargetDragUris) && (flavorStr.EqualsLiteral(kFileMime))) {
(flavorStr.EqualsLiteral(kFileMime))) {
LOGDRAGSERVICE( LOGDRAGSERVICE(
" file not found, proceed with conversion %s => %s flavor\n", " file not found, proceed with conversion %s => %s flavor\n",
kFileMime, gTextUriListType); kFileMime, gTextUriListType);
GetDragData(sTextUriListTypeAtom, availableDragFlavors,
false /* resetTargetData */); gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
GetReachableFileFromUriList(mDragData->GetURIs(), aItemIndex, file); if (gdkFlavor) {
GetTargetDragData(gdkFlavor, dragFlavors, false /* resetTargetData */);
GetReachableFileFromUriList(mTargetDragUris.get(), aItemIndex, file);
}
} }
if (file) { if (file) {
LOGDRAGSERVICE(" from drag uris set as file %s - flavor: %s", LOGDRAGSERVICE(" from drag uris set as file %s - flavor: %s",
mDragData->GetURIs()[aItemIndex], flavorStr.get()); mTargetDragUris.get()[aItemIndex], flavorStr.get());
aTransferable->SetTransferData(flavorStr.get(), file); aTransferable->SetTransferData(flavorStr.get(), file);
return NS_OK; return NS_OK;
} }
if (mDragData) { if (mTargetDragData) {
LOGDRAGSERVICE(" dataFound = true\n"); LOGDRAGSERVICE(" dataFound = true\n");
dataFound = true; dataFound = true;
} else { } else {
@ -942,8 +927,9 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
// If we are looking for text/plain, try again with non utf-8 text. // If we are looking for text/plain, try again with non utf-8 text.
if (flavorStr.EqualsLiteral(kTextMime)) { if (flavorStr.EqualsLiteral(kTextMime)) {
LOGDRAGSERVICE(" conversion %s => %s", kTextMime, kTextMime); LOGDRAGSERVICE(" conversion %s => %s", kTextMime, kTextMime);
GetDragData(sTextMimeAtom, availableDragFlavors); gdkFlavor = gdk_atom_intern(kTextMime, FALSE);
if (mDragData) { GetTargetDragData(gdkFlavor, dragFlavors);
if (mTargetDragData) {
dataFound = true; dataFound = true;
} // if plain text flavor present } // if plain text flavor present
} // if looking for text/plain } // if looking for text/plain
@ -953,32 +939,39 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
// _NETSCAPE_URL // _NETSCAPE_URL
if (flavorStr.EqualsLiteral(kURLMime)) { if (flavorStr.EqualsLiteral(kURLMime)) {
LOGDRAGSERVICE(" conversion %s => %s", kURLMime, gTextUriListType); LOGDRAGSERVICE(" conversion %s => %s", kURLMime, gTextUriListType);
GetDragData(sTextUriListTypeAtom, availableDragFlavors); gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
if (mDragData) { GetTargetDragData(gdkFlavor, dragFlavors);
if (mTargetDragData) {
const char* data = reinterpret_cast<char*>(mTargetDragData);
char16_t* convertedText = nullptr; char16_t* convertedText = nullptr;
uint32_t convertedTextLen = 0; uint32_t convertedTextLen = 0;
GetTextUriListItem(static_cast<const char*>(mDragData->GetData()), GetTextUriListItem(data, mTargetDragDataLen, aItemIndex,
mDragData->GetDataLen(), aItemIndex,
&convertedText, &convertedTextLen); &convertedText, &convertedTextLen);
if (convertedText) { if (convertedText) {
mDragData = new DragData(convertedText, convertedTextLen * 2, // out with the old, in with the new
/* aCopyData */ false); g_free(mTargetDragData);
mTargetDragData = convertedText;
mTargetDragDataLen = convertedTextLen * 2;
dataFound = true; dataFound = true;
} }
} }
if (!dataFound) { if (!dataFound) {
LOGDRAGSERVICE(" conversion %s => %s", kURLMime, gMozUrlType); LOGDRAGSERVICE(" conversion %s => %s", kURLMime, gMozUrlType);
GetDragData(sMozUrlTypeAtom, availableDragFlavors); gdkFlavor = gdk_atom_intern(gMozUrlType, FALSE);
if (mDragData) { GetTargetDragData(gdkFlavor, dragFlavors);
if (mTargetDragData) {
const char* castedText = reinterpret_cast<char*>(mTargetDragData);
char16_t* convertedText = nullptr; char16_t* convertedText = nullptr;
uint32_t convertedTextLen = 0; uint32_t convertedTextLen = 0;
UTF8ToNewUTF16(static_cast<const char*>(mDragData->GetData()), UTF8ToNewUTF16(castedText, mTargetDragDataLen, &convertedText,
mDragData->GetDataLen(), &convertedText,
&convertedTextLen); &convertedTextLen);
if (convertedText) { if (convertedText) {
mDragData = new DragData(convertedText, convertedTextLen * 2, // out with the old, in with the new
/* aCopyData */ false); g_free(mTargetDragData);
mTargetDragData = convertedText;
mTargetDragDataLen = convertedTextLen * 2;
dataFound = true; dataFound = true;
} }
} }
@ -989,17 +982,17 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
if (dataFound) { if (dataFound) {
LOGDRAGSERVICE(" actual data found %s\n", LOGDRAGSERVICE(" actual data found %s\n",
GUniquePtr<gchar>(gdk_atom_name(requestedFlavor)).get()); GUniquePtr<gchar>(gdk_atom_name(gdkFlavor)).get());
if (flavorStr.EqualsLiteral(kTextMime)) { if (flavorStr.EqualsLiteral(kTextMime)) {
// The text is in UTF-8, so convert the text into UTF-16 // The text is in UTF-8, so convert the text into UTF-16
NS_ConvertUTF8toUTF16 ucs2string( const char* text = static_cast<char*>(mTargetDragData);
static_cast<const char*>(mDragData->GetData()), NS_ConvertUTF8toUTF16 ucs2string(text, mTargetDragDataLen);
mDragData->GetDataLen());
char16_t* convertedText = ToNewUnicode(ucs2string, mozilla::fallible); char16_t* convertedText = ToNewUnicode(ucs2string, mozilla::fallible);
if (convertedText) { if (convertedText) {
mDragData = new DragData(convertedText, ucs2string.Length() * 2, g_free(mTargetDragData);
/* aCopyData */ false); mTargetDragData = convertedText;
mTargetDragDataLen = ucs2string.Length() * 2;
} }
} }
@ -1011,7 +1004,8 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
nsCOMPtr<nsIInputStream> byteStream; nsCOMPtr<nsIInputStream> byteStream;
NS_NewByteInputStream(getter_AddRefs(byteStream), NS_NewByteInputStream(getter_AddRefs(byteStream),
mDragData->GetDataSpan(), NS_ASSIGNMENT_COPY); Span((char*)mTargetDragData, mTargetDragDataLen),
NS_ASSIGNMENT_COPY);
aTransferable->SetTransferData(flavorStr.get(), byteStream); aTransferable->SetTransferData(flavorStr.get(), byteStream);
continue; continue;
} }
@ -1019,17 +1013,15 @@ nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) {
if (!flavorStr.EqualsLiteral(kCustomTypesMime)) { if (!flavorStr.EqualsLiteral(kCustomTypesMime)) {
// the DOM only wants LF, so convert from MacOS line endings // the DOM only wants LF, so convert from MacOS line endings
// to DOM line endings. // to DOM line endings.
void* data = mDragData->GetData();
int len = mDragData->GetDataLen();
nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks( nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(
flavorStr.EqualsLiteral(kRTFMime), &data, &len); flavorStr.EqualsLiteral(kRTFMime), &mTargetDragData,
mDragData->UpdateData(data, len); reinterpret_cast<int*>(&mTargetDragDataLen));
} }
// put it into the transferable. // put it into the transferable.
nsCOMPtr<nsISupports> genericDataWrapper; nsCOMPtr<nsISupports> genericDataWrapper;
nsPrimitiveHelpers::CreatePrimitiveForData( nsPrimitiveHelpers::CreatePrimitiveForData(
flavorStr, mDragData->GetData(), mDragData->GetDataLen(), flavorStr, mTargetDragData, mTargetDragDataLen,
getter_AddRefs(genericDataWrapper)); getter_AddRefs(genericDataWrapper));
aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper); aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper);
// we found one, get out of this loop! // we found one, get out of this loop!
@ -1060,9 +1052,10 @@ nsDragService::IsDataFlavorSupported(const char* aDataFlavor, bool* _retval) {
} }
// check to see if the target context is a list. // check to see if the target context is a list.
bool isList = IsTargetContextList();
// if it is, just look in the internal data since we are the source // if it is, just look in the internal data since we are the source
// for it. // for it.
if (IsTargetContextList()) { if (isList) {
LOGDRAGSERVICE(" It's a list"); LOGDRAGSERVICE(" It's a list");
uint32_t numDragItems = 0; uint32_t numDragItems = 0;
// if we don't have mDataItems we didn't start this drag so it's // if we don't have mDataItems we didn't start this drag so it's
@ -1202,7 +1195,8 @@ void nsDragService::ReplyToDragMotion(GdkDragContext* aDragContext,
void nsDragService::EnsureCachedDataValidForContext( void nsDragService::EnsureCachedDataValidForContext(
GdkDragContext* aDragContext) { GdkDragContext* aDragContext) {
if (mCachedDragContext != (uintptr_t)aDragContext) { if (mCachedDragContext != (uintptr_t)aDragContext) {
mCachedDragData.Clear(); mCachedUris.Clear();
mCachedData.Clear();
mCachedDragContext = (uintptr_t)aDragContext; mCachedDragContext = (uintptr_t)aDragContext;
} }
} }
@ -1217,66 +1211,94 @@ void nsDragService::TargetDataReceived(GtkWidget* aWidget,
EnsureCachedDataValidForContext(aContext); EnsureCachedDataValidForContext(aContext);
GdkAtom target = gtk_selection_data_get_target(aSelectionData); mTargetDragDataReceived = true;
if (gtk_targets_include_uri(&target, 1)) {
if (target == sPortalFileAtom || target == sPortalFileTransferAtom) {
const guchar* data = gtk_selection_data_get_data(aSelectionData);
if (!data || data[0] == '\0') {
LOGDRAGSERVICE(" Empty data!\n");
return;
}
// A workaround for https://gitlab.gnome.org/GNOME/gtk/-/issues/6563 GdkAtom target = gtk_selection_data_get_target(aSelectionData);
// GUniquePtr<gchar> name(gdk_atom_name(target));
nsDependentCString flavor(name.get());
if (gtk_targets_include_uri(&target, 1)) {
// For the vnd.portal.filetransfer and vnd.portal.files we receive numeric // For the vnd.portal.filetransfer and vnd.portal.files we receive numeric
// id when it's a local file. The numeric id is then used by // id when it's a local file. The numeric id is then used by
// gtk_selection_data_get_uris implementation to get the actual file // gtk_selection_data_get_uris implementation to get the actual file
// available in the flatpak environment. // available in the flatpak environment.
// //
// However due to GTK implementation also for example the uris like https // However due to GTK implementation also for example the uris like https
// are also provided by the vnd.portal.filetransfer target. In this case // are also provided by the vnd.portal.filetransfer target. In this case the
// the call gtk_selection_data_get_uris fails. This is a bug in the gtk. // call gtk_selection_data_get_uris fails. This is a bug in the gtk.
// To workaround it we try to create the valid uri and only if we fail // To workaround it we try to create the valid uri and only if we fail
// we try to use the gtk_selection_data_get_uris. We ignore the valid uris // we try to use the gtk_selection_data_get_uris. We ignore the valid uris
// for the vnd.portal.file* targets. // for the vnd.portal.file* targets.
// See: https://gitlab.gnome.org/GNOME/gtk/-/issues/6563
if (flavor.Equals(gPortalFile) || flavor.Equals(gPortalFileTransfer)) {
const guchar* data = gtk_selection_data_get_data(aSelectionData);
if (!data || data[0] == '\0') {
LOGDRAGSERVICE(" Empty data!\n");
return;
}
nsCOMPtr<nsIURI> sourceURI; nsCOMPtr<nsIURI> sourceURI;
nsresult rv = nsresult rv =
NS_NewURI(getter_AddRefs(sourceURI), (const gchar*)data, nullptr); NS_NewURI(getter_AddRefs(sourceURI), (const gchar*)data, nullptr);
if (NS_SUCCEEDED(rv)) { if (NS_FAILED(rv)) {
// We're unable to get the URI, we'll use the
// gtk_selection_data_get_uris to get the actual file location
// accessible from the Firefox runtime.
GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData));
uris.swap(mTargetDragUris);
} else {
LOGDRAGSERVICE( LOGDRAGSERVICE(
" got valid uri for MIME %s - this is bug in GTK - expected " " got valid uri for MIME %s - this is bug in GTK - expected "
"numeric value for portal, got %s\n", "numeric value for portal, got %s\n",
gdk_atom_name(target), data); flavor.get(), data);
return; return;
} }
}
mDragData = new DragData(gtk_selection_data_get_uris(aSelectionData)); } else {
GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData));
uris.swap(mTargetDragUris);
}
#ifdef MOZ_LOGGING #ifdef MOZ_LOGGING
if (MOZ_LOG_TEST(gWidgetDragLog, mozilla::LogLevel::Debug)) { if (MOZ_LOG_TEST(gWidgetDragLog, mozilla::LogLevel::Debug)) {
gchar** uri = mDragData->GetURIs(); gchar** uri = mTargetDragUris.get();
while (uri && *uri) { while (uri && *uri) {
LOGDRAGSERVICE(" got uri %s, MIME %s", *uri, gdk_atom_name(target)); LOGDRAGSERVICE(" got uri %s, MIME %s", *uri, flavor.get());
uri++; uri++;
} }
} }
#endif #endif
if (mTargetDragUris) {
mCachedUris.InsertOrUpdate(
flavor, GUniquePtr<gchar*>(g_strdupv(mTargetDragUris.get())));
} else { } else {
LOGDRAGSERVICE("Failed to get uri list\n");
mCachedUris.InsertOrUpdate(flavor, GUniquePtr<gchar*>(nullptr));
}
return;
}
const guchar* data = gtk_selection_data_get_data(aSelectionData); const guchar* data = gtk_selection_data_get_data(aSelectionData);
gint len = gtk_selection_data_get_length(aSelectionData); gint len = gtk_selection_data_get_length(aSelectionData);
if (len > 0 && data) { if (len > 0 && data) {
mDragData = new DragData(data, len); mTargetDragDataLen = len;
LOGDRAGSERVICE(" got data, MIME %s len = %d", gdk_atom_name(target), mTargetDragData = g_malloc(mTargetDragDataLen);
len); memcpy(mTargetDragData, data, mTargetDragDataLen);
}
}
if (mDragData) { LOGDRAGSERVICE(" got data, len = %d", mTargetDragDataLen);
mCachedDragData.InsertOrUpdate(target, mDragData);
} else { nsTArray<uint8_t> copy;
LOGDRAGSERVICE(" failed to get data, MIME %s", gdk_atom_name(target)); if (!copy.SetLength(len, fallible)) {
mCachedDragData.InsertOrUpdate(target, nullptr); return;
} }
memcpy(copy.Elements(), data, len);
mCachedData.InsertOrUpdate(flavor, std::move(copy));
} else {
LOGDRAGSERVICE("Failed to get data. selection data len was %d\n",
mTargetDragDataLen);
mCachedData.InsertOrUpdate(flavor, nsTArray<uint8_t>());
}
LOGDRAGSERVICE(" got data, MIME %s", flavor.get());
} }
bool nsDragService::IsTargetContextList(void) { bool nsDragService::IsTargetContextList(void) {
@ -1313,48 +1335,63 @@ bool nsDragService::IsTargetContextList(void) {
// Spins event loop, called from eDragTaskMotion handler by // Spins event loop, called from eDragTaskMotion handler by
// DispatchMotionEvents(). // DispatchMotionEvents().
// Can lead to another round of drag_motion events. // Can lead to another round of drag_motion events.
void nsDragService::GetDragData(GdkAtom aRequestedFlavor, void nsDragService::GetTargetDragData(GdkAtom aFlavor,
const nsTArray<GdkAtom>& aAvailableDragFlavors, nsTArray<nsCString>& aDropFlavors,
bool aResetDragData) { bool aResetTargetData) {
LOGDRAGSERVICE("nsDragService::GetDragData(%p) requested '%s'\n", LOGDRAGSERVICE("nsDragService::GetTargetDragData(%p) '%s'\n",
mTargetDragContext.get(), mTargetDragContext.get(),
GUniquePtr<gchar>(gdk_atom_name(aRequestedFlavor)).get()); GUniquePtr<gchar>(gdk_atom_name(aFlavor)).get());
// reset our target data areas // reset our target data areas
if (aResetDragData) { if (aResetTargetData) {
TargetResetData(); TargetResetData();
} }
GUniquePtr<gchar> name(gdk_atom_name(aFlavor));
nsDependentCString flavor(name.get());
// Return early when requested MIME is not offered by D&D. // Return early when requested MIME is not offered by D&D.
if (!aAvailableDragFlavors.Contains(aRequestedFlavor)) { if (!aDropFlavors.Contains(flavor)) {
LOGDRAGSERVICE(" %s is missing", gdk_atom_name(aRequestedFlavor)); LOGDRAGSERVICE(" %s is missing", flavor.get());
return;
}
if (!mTargetDragContext) {
LOGDRAGSERVICE(" failed, missing mTargetDragContext");
return; return;
} }
if (mTargetDragContext) {
// We keep a copy of the requested data with the same life-time // We keep a copy of the requested data with the same life-time
// as mTargetDragContext. // as mTargetDragContext.
// Especially with multiple items the same data is requested // Especially with multiple items the same data is requested
// very often. // very often.
EnsureCachedDataValidForContext(mTargetDragContext); EnsureCachedDataValidForContext(mTargetDragContext);
mDragData = mCachedDragData.Get(GDK_ATOM_TO_POINTER(aRequestedFlavor)); if (auto cached = mCachedUris.Lookup(flavor)) {
if (mDragData) { LOGDRAGSERVICE(" using cached uri list for %s", flavor.get());
LOGDRAGSERVICE(" %s found in cache", gdk_atom_name(aRequestedFlavor));
mTargetDragUris = GUniquePtr<gchar*>(g_strdupv(cached->get()));
mTargetDragDataReceived = true;
LOGDRAGSERVICE(" %s found in cache", flavor.get());
return;
}
if (auto cached = mCachedData.Lookup(flavor)) {
mTargetDragDataLen = cached->Length();
LOGDRAGSERVICE(" using cached data for %s, length is %d", flavor.get(),
mTargetDragDataLen);
if (mTargetDragDataLen) {
mTargetDragData = g_malloc(mTargetDragDataLen);
memcpy(mTargetDragData, cached->Elements(), mTargetDragDataLen);
}
mTargetDragDataReceived = true;
LOGDRAGSERVICE(" %s found in cache", flavor.get());
return; return;
} }
gtk_drag_get_data(mTargetWidget, mTargetDragContext, aRequestedFlavor, gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
mTargetTime);
LOGDRAGSERVICE(" about to start inner iteration."); LOGDRAGSERVICE(" about to start inner iteration.");
gtk_main_iteration(); gtk_main_iteration();
PRTime entryTime = PR_Now(); PRTime entryTime = PR_Now();
while (!mDragData && mDoingDrag) { while (!mTargetDragDataReceived && mDoingDrag) {
// check the number of iterations // check the number of iterations
LOGDRAGSERVICE(" doing iteration...\n"); LOGDRAGSERVICE(" doing iteration...\n");
PR_Sleep(PR_MillisecondsToInterval(10)); /* sleep for 10 ms/iteration */ PR_Sleep(PR_MillisecondsToInterval(10)); /* sleep for 10 ms/iteration */
@ -1364,18 +1401,25 @@ void nsDragService::GetDragData(GdkAtom aRequestedFlavor,
} }
gtk_main_iteration(); gtk_main_iteration();
} }
}
#ifdef MOZ_LOGGING #ifdef MOZ_LOGGING
if (mDragData) { if (mTargetDragUris || (mTargetDragDataLen && mTargetDragData)) {
LOGDRAGSERVICE(" %s got from system", gdk_atom_name(aRequestedFlavor)); LOGDRAGSERVICE(" %s got from system", flavor.get());
} else { } else {
LOGDRAGSERVICE(" %s failed to get from system", LOGDRAGSERVICE(" %s failed to get from system", flavor.get());
gdk_atom_name(aRequestedFlavor));
} }
#endif #endif
} }
void nsDragService::TargetResetData(void) { mDragData = nullptr; } void nsDragService::TargetResetData(void) {
mTargetDragDataReceived = false;
// make sure to free old data if we have to
mTargetDragUris = nullptr;
g_free(mTargetDragData);
mTargetDragData = 0;
mTargetDragDataLen = 0;
}
static void TargetArrayAddTarget(nsTArray<GtkTargetEntry*>& aTargetArray, static void TargetArrayAddTarget(nsTArray<GtkTargetEntry*>& aTargetArray,
const char* aTarget) { const char* aTarget) {
@ -1539,7 +1583,7 @@ void nsDragService::SourceEndDragSession(GdkDragContext* aContext,
mSourceDataItems = nullptr; mSourceDataItems = nullptr;
// Remove this property, if it exists, to satisfy the Direct Save Protocol. // Remove this property, if it exists, to satisfy the Direct Save Protocol.
GdkAtom property = sXdndDirectSaveTypeAtom; GdkAtom property = gdk_atom_intern(gXdndDirectSaveType, FALSE);
gdk_property_delete(gdk_drag_context_get_source_window(aContext), property); gdk_property_delete(gdk_drag_context_get_source_window(aContext), property);
if (!mDoingDrag || mScheduledTask == eDragTaskSourceEnd) if (!mDoingDrag || mScheduledTask == eDragTaskSourceEnd)
@ -1951,6 +1995,9 @@ void nsDragService::SourceDataGetXDND(nsITransferable* aItem,
GdkAtom target = gtk_selection_data_get_target(aSelectionData); GdkAtom target = gtk_selection_data_get_target(aSelectionData);
gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"E", 1); gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"E", 1);
GdkAtom property = gdk_atom_intern(gXdndDirectSaveType, FALSE);
GdkAtom type = gdk_atom_intern(kTextMime, FALSE);
GdkWindow* srcWindow = gdk_drag_context_get_source_window(aContext); GdkWindow* srcWindow = gdk_drag_context_get_source_window(aContext);
if (!srcWindow) { if (!srcWindow) {
LOGDRAGSERVICE(" failed to get source GdkWindow!"); LOGDRAGSERVICE(" failed to get source GdkWindow!");
@ -1962,9 +2009,8 @@ void nsDragService::SourceDataGetXDND(nsITransferable* aItem,
{ {
GUniquePtr<guchar> gdata; GUniquePtr<guchar> gdata;
gint length = 0; gint length = 0;
if (!gdk_property_get(srcWindow, sXdndDirectSaveTypeAtom, sTextMimeAtom, 0, if (!gdk_property_get(srcWindow, property, type, 0, INT32_MAX, FALSE,
INT32_MAX, FALSE, nullptr, nullptr, &length, nullptr, nullptr, &length, getter_Transfers(gdata))) {
getter_Transfers(gdata))) {
LOGDRAGSERVICE(" failed to get gXdndDirectSaveType GdkWindow property."); LOGDRAGSERVICE(" failed to get gXdndDirectSaveType GdkWindow property.");
return; return;
} }
@ -2197,10 +2243,13 @@ void nsDragService::SourceBeginDrag(GdkDragContext* aContext) {
nsCString fileNameCStr; nsCString fileNameCStr;
CopyUTF16toUTF8(fileNameStr, fileNameCStr); CopyUTF16toUTF8(fileNameStr, fileNameCStr);
gdk_property_change( GdkAtom property = gdk_atom_intern(gXdndDirectSaveType, FALSE);
gdk_drag_context_get_source_window(aContext), sXdndDirectSaveTypeAtom, GdkAtom type = gdk_atom_intern(kTextMime, FALSE);
sTextMimeAtom, 8, GDK_PROP_MODE_REPLACE,
(const guchar*)fileNameCStr.get(), fileNameCStr.Length()); gdk_property_change(gdk_drag_context_get_source_window(aContext),
property, type, 8, GDK_PROP_MODE_REPLACE,
(const guchar*)fileNameCStr.get(),
fileNameCStr.Length());
break; break;
} }
} }
@ -2319,7 +2368,8 @@ static gboolean invisibleSourceDragFailed(GtkWidget* aWidget,
for (GList* tmp = gdk_drag_context_list_targets(aContext); tmp; for (GList* tmp = gdk_drag_context_list_targets(aContext); tmp;
tmp = tmp->next) { tmp = tmp->next) {
GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data); GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
if (atom == nsDragService::sTabDropTypeAtom) { GUniquePtr<gchar> name(gdk_atom_name(atom));
if (name && !strcmp(name.get(), gTabDropType)) {
aResult = GTK_DRAG_RESULT_NO_TARGET; aResult = GTK_DRAG_RESULT_NO_TARGET;
LOGDRAGSERVICESTATIC("invisibleSourceDragFailed(%p): Wayland tab drop", LOGDRAGSERVICESTATIC("invisibleSourceDragFailed(%p): Wayland tab drop",
aContext); aContext);

View file

@ -24,42 +24,6 @@ class SourceSurface;
} }
} // namespace mozilla } // namespace mozilla
class DragData final {
public:
NS_INLINE_DECL_REFCOUNTING(DragData)
DragData(void* aData, uint32_t aDataLen, bool aCopyData) {
UpdateData(aData, aDataLen, aCopyData);
}
DragData(const void* aData, uint32_t aDataLen) {
// Always copy const data as we want to update it in-place by
// UTF8/UTF16 conversions.
UpdateData(const_cast<void*>(aData), aDataLen, /* aCopyData */ true);
}
explicit DragData(gchar** aDragUris) : mDragUris(aDragUris) {}
bool HasURIs() const { return !!mDragUris.get(); }
gchar** GetURIs() const { return mDragUris.get(); }
void UpdateData(void* aData, uint32_t aDataLen, bool aCopyData = false);
void* GetData() const { return mDragData; }
uint32_t GetDataLen() const { return mDragDataLen; }
mozilla::Span<char> GetDataSpan() const {
return mozilla::Span((char*)mDragData, mDragDataLen);
}
private:
void ReleaseData();
~DragData();
// We don't use any auto pointers here because we pass mDragData directly
// to various code which re-allocates it and releases it by malloc/free.
uint32_t mDragDataLen = 0;
void* mDragData = nullptr;
mozilla::GUniquePtr<gchar*> mDragUris;
};
/** /**
* Native GTK DragService wrapper * Native GTK DragService wrapper
*/ */
@ -197,15 +161,15 @@ class nsDragService final : public nsBaseDragService, public nsIObserver {
RefPtr<GdkDragContext> mPendingDragContext; RefPtr<GdkDragContext> mPendingDragContext;
// We cache all data for the current drag context, // We cache all data for the current drag context,
// because waiting for the data in GetDragData can be very slow. // because waiting for the data in GetTargetDragData can be very slow.
// nsTHashMap<nsCStringHashKey, nsTArray<uint8_t>> mCachedData;
// mCachedData are tied to mCachedDragContext. mCachedDragContext is not // mCachedData are tied to mCachedDragContext. mCachedDragContext is not
// ref counted and may be already deleted on Gtk side. // ref counted and may be already deleted on Gtk side.
//
// We used it for mCachedData invalidation only and can't be used for // We used it for mCachedData invalidation only and can't be used for
// any D&D operation. // any D&D operation.
uintptr_t mCachedDragContext; uintptr_t mCachedDragContext;
nsRefPtrHashtable<nsVoidPtrHashKey, DragData> mCachedDragData;
nsTHashMap<nsCStringHashKey, mozilla::GUniquePtr<gchar*>> mCachedUris;
guint mPendingTime; guint mPendingTime;
@ -233,16 +197,18 @@ class nsDragService final : public nsBaseDragService, public nsIObserver {
// is it OK to drop on us? // is it OK to drop on us?
bool mCanDrop; bool mCanDrop;
// Received drag data // have we received our drag data?
RefPtr<DragData> mDragData; bool mTargetDragDataReceived;
// last data received and its length
void* mTargetDragData;
uint32_t mTargetDragDataLen;
mozilla::GUniquePtr<gchar*> mTargetDragUris;
// is the current target drag context contain a list? // is the current target drag context contain a list?
bool IsTargetContextList(void); bool IsTargetContextList(void);
// this will get the native data from the last target given a // this will get the native data from the last target given a
// specific flavor // specific flavor
void GetDragData(GdkAtom aRequestedFlavor, void GetTargetDragData(GdkAtom aFlavor, nsTArray<nsCString>& aDropFlavors,
const nsTArray<GdkAtom>& aAvailableDragFlavors, bool aResetTargetData = true);
bool aResetDragData = true);
// this will reset all of the target vars // this will reset all of the target vars
void TargetResetData(void); void TargetResetData(void);
// Ensure our data cache belongs to aDragContext and clear the cache if // Ensure our data cache belongs to aDragContext and clear the cache if
@ -281,7 +247,7 @@ class nsDragService final : public nsBaseDragService, public nsIObserver {
#ifdef MOZ_LOGGING #ifdef MOZ_LOGGING
const char* GetDragServiceTaskName(nsDragService::DragTask aTask); const char* GetDragServiceTaskName(nsDragService::DragTask aTask);
#endif #endif
void GetAvailableDragFlavors(nsTArray<GdkAtom>& aAvailableFlavors); void GetDragFlavors(nsTArray<nsCString>& aFlavors);
gboolean DispatchDropEvent(); gboolean DispatchDropEvent();
static uint32_t GetCurrentModifiers(); static uint32_t GetCurrentModifiers();
@ -298,17 +264,6 @@ class nsDragService final : public nsBaseDragService, public nsIObserver {
guint mTempFileTimerID; guint mTempFileTimerID;
// How deep we're nested in event loops // How deep we're nested in event loops
int mEventLoopDepth; int mEventLoopDepth;
public:
static GdkAtom sTextMimeAtom;
static GdkAtom sMozUrlTypeAtom;
static GdkAtom sMimeListTypeAtom;
static GdkAtom sTextUriListTypeAtom;
static GdkAtom sTextPlainUTF8TypeAtom;
static GdkAtom sXdndDirectSaveTypeAtom;
static GdkAtom sTabDropTypeAtom;
static GdkAtom sPortalFileAtom;
static GdkAtom sPortalFileTransferAtom;
}; };
#endif // nsDragService_h__ #endif // nsDragService_h__