From d9a9cfc939acbfa76f3c8f5e0e5889fc78ef59d7 Mon Sep 17 00:00:00 2001 From: stransky Date: Mon, 15 Nov 2021 15:25:36 +0000 Subject: [PATCH] Bug 377621 [Linux] Support application/x-moz-file-promise mime type by text/uri-list, r=karlt When application/x-moz-file-promise MIME content is adverised, save the file to /tmp/dnd_file-*/ directory and offer it as text/uri-list MIME type. Based on patch by Tobias Koenig. Differential Revision: https://phabricator.services.mozilla.com/D130767 --- widget/gtk/nsDragService.cpp | 601 +++++++++++++++++++++++++---------- widget/gtk/nsDragService.h | 16 +- 2 files changed, 448 insertions(+), 169 deletions(-) diff --git a/widget/gtk/nsDragService.cpp b/widget/gtk/nsDragService.cpp index 9ee0c4989216..2788237995b6 100644 --- a/widget/gtk/nsDragService.cpp +++ b/widget/gtk/nsDragService.cpp @@ -53,10 +53,19 @@ # include "nsClipboardWayland.h" # include "gfxPlatformGtk.h" #endif +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#include "nsEscape.h" +#include "nsString.h" using namespace mozilla; using namespace mozilla::gfx; +// 1) The maximum time to wait for a "drag_received" arrived in microseconds. +// 2) The maximum time to wait before temporary files resulting +// from drag'n'drop events will be removed +#define NS_DND_TIMEOUT 1000000 + #define NS_SYSTEMINFO_CONTRACTID "@mozilla.org/system-info;1" // This sets how opaque the drag image is @@ -161,11 +170,13 @@ nsDragService::nsDragService() mTargetDragDataReceived = false; mTargetDragData = 0; mTargetDragDataLen = 0; + mTempFileTimerID = 0; } nsDragService::~nsDragService() { LOGDRAGSERVICE(("nsDragService::~nsDragService")); if (mTaskSource) g_source_remove(mTaskSource); + if (mTempFileTimerID) g_source_remove(mTempFileTimerID); } NS_IMPL_ISUPPORTS_INHERITED(nsDragService, nsBaseDragService, nsIObserver) @@ -455,9 +466,37 @@ bool nsDragService::SetAlphaPixmap(SourceSurface* aSurface, NS_IMETHODIMP nsDragService::StartDragSession() { LOGDRAGSERVICE(("nsDragService::StartDragSession")); + mTempFileUrl.Truncate(); return nsBaseDragService::StartDragSession(); } +bool nsDragService::RemoveTempFiles() { + // We can not delete the temporary files immediately after the + // drag has finished, because the target application might have not + // copied the temporary file yet. The Qt toolkit does not provide a + // way to mark a drop as finished in an asynchronous way, so most + // Qt based applications do send the dnd_finished signal before they + // have actually accessed the data from the temporary file. + // (https://bugreports.qt.io/browse/QTBUG-5441) + // + // To work also with these applications we collect all temporary + // files in mTemporaryFiles array and remove them here in the timer event. + int32_t count = mTemporaryFiles.Count(); + for (int32_t pos = 0; pos < count; pos++) { + mTemporaryFiles[pos]->Remove(/* recursive = */ true); + } + mTemporaryFiles.Clear(); + + mTempFileTimerID = 0; + // Return false to remove the timer added by g_timeout_add_full(). + return false; +} + +gboolean nsDragService::TaskRemoveTempFiles(gpointer data) { + RefPtr dragService = static_cast(data); + return dragService->RemoveTempFiles(); +} + NS_IMETHODIMP nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) { LOGDRAGSERVICE(("nsDragService::EndDragSession %d", aDoneDrag)); @@ -481,6 +520,15 @@ nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) { // unset our drag action SetDragAction(DRAGDROP_ACTION_NONE); + // start timer to remove temporary files + if (mTemporaryFiles.Count() > 0 && !mTempFileTimerID) { + LOGDRAGSERVICE((" removing temporary files")); + // |this| won't be used after nsDragService delete because the timer is + // removed in the nsDragService destructor. + mTempFileTimerID = g_timeout_add(NS_DND_TIMEOUT, TaskRemoveTempFiles, this); + mTempFileUrl.Truncate(); + } + // We're done with the drag context. mTargetDragContextForRemote = nullptr; #ifdef MOZ_WAYLAND @@ -1151,9 +1199,6 @@ bool nsDragService::IsTargetContextList(void) { return retval; } -// Maximum time to wait for a "drag_received" arrived, in microseconds -#define NS_DND_TIMEOUT 1000000 - void nsDragService::GetTargetDragData(GdkAtom aFlavor, nsTArray& aDropFlavors) { LOGDRAGSERVICE( @@ -1268,6 +1313,7 @@ GtkTargetList* nsDragService::GetSourceList(void) { // text/uri-list. if (flavors[i].EqualsLiteral(kURLMime)) { TargetArrayAddTarget(targetArray, gTextUriListType); + break; } } } // if item is a transferable @@ -1299,6 +1345,11 @@ GtkTargetList* nsDragService::GetSourceList(void) { else if (flavorStr.EqualsLiteral(kURLMime)) { TargetArrayAddTarget(targetArray, gMozUrlType); } + // check if application/x-moz-file-promise url is supported. + // If so, advertise text/uri-list. + else if (flavorStr.EqualsLiteral(kFilePromiseURLMime)) { + TargetArrayAddTarget(targetArray, gTextUriListType); + } // XdndDirectSave else if (widget::GdkIsX11Display() && flavorStr.EqualsLiteral(kFilePromiseMime)) { @@ -1468,6 +1519,185 @@ static void CreateURIList(nsIArray* aItems, nsACString& aURIList) { } } +static nsresult GetDownloadDetails(nsITransferable* aTransferable, + nsIURI** aSourceURI, nsAString& aFilename) { + *aSourceURI = nullptr; + MOZ_ASSERT(aTransferable != nullptr, "aTransferable must not be null"); + + // get the URI from the kFilePromiseURLMime flavor + nsCOMPtr urlPrimitive; + nsresult rv = aTransferable->GetTransferData(kFilePromiseURLMime, + getter_AddRefs(urlPrimitive)); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + nsCOMPtr srcUrlPrimitive = do_QueryInterface(urlPrimitive); + if (!srcUrlPrimitive) { + return NS_ERROR_FAILURE; + } + + nsAutoString srcUri; + srcUrlPrimitive->GetData(srcUri); + if (srcUri.IsEmpty()) { + return NS_ERROR_FAILURE; + } + nsCOMPtr sourceURI; + NS_NewURI(getter_AddRefs(sourceURI), srcUri); + + nsAutoString srcFileName; + nsCOMPtr fileNamePrimitive; + rv = aTransferable->GetTransferData(kFilePromiseDestFilename, + getter_AddRefs(fileNamePrimitive)); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + nsCOMPtr srcFileNamePrimitive = + do_QueryInterface(fileNamePrimitive); + if (srcFileNamePrimitive) { + srcFileNamePrimitive->GetData(srcFileName); + } else { + nsCOMPtr sourceURL = do_QueryInterface(sourceURI); + if (!sourceURL) { + return NS_ERROR_FAILURE; + } + nsAutoCString urlFileName; + sourceURL->GetFileName(urlFileName); + NS_UnescapeURL(urlFileName); + CopyUTF8toUTF16(urlFileName, srcFileName); + } + if (srcFileName.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + sourceURI.swap(*aSourceURI); + aFilename = srcFileName; + return NS_OK; +} + +// See nsContentAreaDragDropDataProvider::GetFlavorData() for reference. +nsresult nsDragService::CreateTempFile(nsITransferable* aItem, + GtkSelectionData* aSelectionData) { + LOGDRAGSERVICE(("nsDragService::CreateTempFile()")); + + nsCOMPtr tmpDir; + nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir)); + + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed to get temp directory\n")); + return rv; + } + + nsCOMPtr inputStream; + nsCOMPtr channel; + + // extract the file name and source uri of the promise-file data + nsAutoString wideFileName; + nsCOMPtr sourceURI; + rv = GetDownloadDetails(aItem, getter_AddRefs(sourceURI), wideFileName); + if (NS_FAILED(rv)) { + LOGDRAGSERVICE( + (" Failed to extract file name and source uri from download url")); + return rv; + } + + // create and open channel for source uri + nsCOMPtr principal = aItem->GetRequestingPrincipal(); + nsContentPolicyType contentPolicyType = aItem->GetContentPolicyType(); + nsCOMPtr cookieJarSettings = + aItem->GetCookieJarSettings(); + rv = NS_NewChannel(getter_AddRefs(channel), sourceURI, principal, + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + contentPolicyType, cookieJarSettings); + /* + contentPolicyType, nsIContentPolicy::TYPE_OTHER, + cookieJarSettings); + */ + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed to create new channel for source uri")); + return rv; + } + + rv = channel->Open(getter_AddRefs(inputStream)); + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed to open channel for source uri")); + return rv; + } + + // build the file:///tmp/dnd_file URL + tmpDir->Append(NS_LITERAL_STRING_FROM_CSTRING("dnd_file")); + rv = tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700); + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed create tmp dir")); + return rv; + } + + // store a copy of that temporary directory so we can + // clean them up when nsDragService is destructed + nsCOMPtr tempFile; + tmpDir->Clone(getter_AddRefs(tempFile)); + + mTemporaryFiles.AppendObject(tempFile); + + if (mTempFileTimerID) { + g_source_remove(mTempFileTimerID); + mTempFileTimerID = 0; + } + + // extend file:///tmp/dnd_file/ URL + tmpDir->Append(wideFileName); + + nsCOMPtr outputStream; + rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), tmpDir); + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed to open output stream for temporary file")); + return rv; + } + + char buffer[8192]; + uint32_t readCount = 0; + uint32_t writeCount = 0; + while (1) { + rv = inputStream->Read(buffer, sizeof(buffer), &readCount); + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed to read data from source uri")); + return rv; + } + + if (readCount == 0) break; + + rv = outputStream->Write(buffer, readCount, &writeCount); + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed to write data to temporary file")); + return rv; + } + } + + inputStream->Close(); + rv = outputStream->Close(); + if (NS_FAILED(rv)) { + LOGDRAGSERVICE((" Failed to write data to temporary file")); + return rv; + } + + nsCOMPtr uri; + rv = NS_NewFileURI(getter_AddRefs(uri), tmpDir); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr fileURL(do_QueryInterface(uri)); + if (fileURL) { + nsAutoCString urltext; + rv = fileURL->GetSpec(urltext); + if (NS_SUCCEEDED(rv)) { + // store url of temporary file + LOGDRAGSERVICE((" storing tmp file as %s", urltext.get())); + mTempFileUrl = urltext; + return NS_OK; + } + } + } + + return NS_ERROR_FAILURE; +} + void nsDragService::SourceDataGet(GtkWidget* aWidget, GdkDragContext* aContext, GtkSelectionData* aSelectionData, guint32 aTime) { @@ -1487,190 +1717,225 @@ void nsDragService::SourceDataGet(GtkWidget* aWidget, GdkDragContext* aContext, return; } - nsDependentCSubstring mimeFlavor(typeName, strlen(typeName)); nsCOMPtr item; item = do_QueryElementAt(mSourceDataItems, 0); - if (item) { - // if someone was asking for text/plain, lookup unicode instead so - // we can convert it. - bool needToDoConversionToPlainText = false; - bool needToDoConversionToImage = false; - const char* actualFlavor; - if (mimeFlavor.EqualsLiteral(kTextMime) || - mimeFlavor.EqualsLiteral(gTextPlainUTF8Type)) { - actualFlavor = kUnicodeMime; - needToDoConversionToPlainText = true; - } - // if someone was asking for _NETSCAPE_URL we need to convert to - // plain text but we also need to look for x-moz-url - else if (mimeFlavor.EqualsLiteral(gMozUrlType)) { - actualFlavor = kURLMime; - needToDoConversionToPlainText = true; - } - // if someone was asking for text/uri-list we need to convert to - // plain text. - else if (mimeFlavor.EqualsLiteral(gTextUriListType)) { - actualFlavor = gTextUriListType; - needToDoConversionToPlainText = true; - } - // Someone is asking for the special Direct Save Protocol type. - else if (mimeFlavor.EqualsLiteral(gXdndDirectSaveType)) { - // Indicate failure by default. - gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"E", 1); + if (!item) { + return; + } - GdkAtom property = gdk_atom_intern(gXdndDirectSaveType, FALSE); - GdkAtom type = gdk_atom_intern(kTextMime, FALSE); +#ifdef MOZ_LOGGING + PRUint32 dragItems; + mSourceDataItems->GetLength(&dragItems); + LOGDRAGSERVICE(("source data items %d", dragItems)); +#endif - guchar* data; - gint length; - if (!gdk_property_get(gdk_drag_context_get_source_window(aContext), - property, type, 0, INT32_MAX, FALSE, nullptr, - nullptr, &length, &data)) { - return; - } + // if someone was asking for text/plain, lookup unicode instead so + // we can convert it. + bool needToDoConversionToPlainText = false; + bool needToDoConversionToImage = false; - // Zero-terminate the string. - data = (guchar*)g_realloc(data, length + 1); - if (!data) return; - data[length] = '\0'; + nsDependentCSubstring mimeFlavor(typeName, strlen(typeName)); + const char* actualFlavor; + if (mimeFlavor.EqualsLiteral(kTextMime) || + mimeFlavor.EqualsLiteral(gTextPlainUTF8Type)) { + actualFlavor = kUnicodeMime; + needToDoConversionToPlainText = true; + } + // if someone was asking for _NETSCAPE_URL we need to convert to + // plain text but we also need to look for x-moz-url + else if (mimeFlavor.EqualsLiteral(gMozUrlType)) { + actualFlavor = kURLMime; + needToDoConversionToPlainText = true; + } + // if someone was asking for text/uri-list we need to convert to + // plain text. + else if (mimeFlavor.EqualsLiteral(gTextUriListType)) { + actualFlavor = gTextUriListType; + needToDoConversionToPlainText = true; - gchar* hostname; - char* gfullpath = - g_filename_from_uri((const gchar*)data, &hostname, nullptr); - g_free(data); - if (!gfullpath) return; + // The desktop or file manager expects for drags of promise-file data + // the text/uri-list flavor set to a temporary file that contains the + // promise-file data. + // We open a stream on the :// url here and save the content + // to file:///tmp/dnd_file/ and pass this url + // as text/uri-list flavor. - nsCString fullpath(gfullpath); - g_free(gfullpath); - - LOGDRAGSERVICE(("XdndDirectSave filepath is %s\n", fullpath.get())); - - // If there is no hostname in the URI, NULL will be stored. - // We should not accept uris with from a different host. - if (hostname) { - nsCOMPtr infoService = - do_GetService(NS_SYSTEMINFO_CONTRACTID); - if (!infoService) return; - - nsAutoCString host; - if (NS_SUCCEEDED( - infoService->GetPropertyAsACString(u"host"_ns, host))) { - if (!host.Equals(hostname)) { - LOGDRAGSERVICE(("ignored drag because of different host.\n")); - - // Special error code "F" for this case. - gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"F", 1); - g_free(hostname); - return; - } - } - - g_free(hostname); - } - - nsCOMPtr file; - if (NS_FAILED( - NS_NewNativeLocalFile(fullpath, false, getter_AddRefs(file)))) { - return; - } - - // We have to split the path into a directory and filename, - // because our internal file-promise API is based on these. - - nsCOMPtr directory; - file->GetParent(getter_AddRefs(directory)); - - item->SetTransferData(kFilePromiseDirectoryMime, directory); - - nsCOMPtr filenamePrimitive = - do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); - if (!filenamePrimitive) return; - - nsAutoString leafName; - file->GetLeafName(leafName); - filenamePrimitive->SetData(leafName); - - item->SetTransferData(kFilePromiseDestFilename, filenamePrimitive); - - // Request a different type in GetTransferData. - actualFlavor = kFilePromiseMime; - // if someone was asking for image we need to convert it - // from kNativeImageMime - } else if (mimeFlavor.EqualsLiteral(kPNGImageMime) || - mimeFlavor.EqualsLiteral(kJPEGImageMime) || - mimeFlavor.EqualsLiteral(kJPGImageMime) || - mimeFlavor.EqualsLiteral(kGIFImageMime)) { - actualFlavor = kNativeImageMime; - needToDoConversionToImage = true; - } else { - actualFlavor = typeName; - } + // check whether transferable contains FilePromiseUrl flavor... nsresult rv; nsCOMPtr data; - rv = item->GetTransferData(actualFlavor, getter_AddRefs(data)); + rv = item->GetTransferData(kFilePromiseURLMime, getter_AddRefs(data)); - if (strcmp(actualFlavor, kFilePromiseMime) == 0) { - if (NS_SUCCEEDED(rv)) { - // Indicate success. - gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"S", 1); + // ... if so, create a temporary file and pass its url as + // text/uri-list flavor + if (NS_SUCCEEDED(rv)) { + if (mTempFileUrl.IsEmpty()) { + rv = CreateTempFile(item, aSelectionData); } + if (NS_SUCCEEDED(rv)) { + gtk_selection_data_set(aSelectionData, target, 8, + (guchar*)mTempFileUrl.get(), + mTempFileUrl.Length()); + // We're done here, data is set. + return; + } + } + } + // Someone is asking for the special Direct Save Protocol type. + else if (mimeFlavor.EqualsLiteral(gXdndDirectSaveType)) { + // Indicate failure by default. + gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"E", 1); + + GdkAtom property = gdk_atom_intern(gXdndDirectSaveType, FALSE); + GdkAtom type = gdk_atom_intern(kTextMime, FALSE); + + guchar* data; + gint length; + if (!gdk_property_get(gdk_drag_context_get_source_window(aContext), + property, type, 0, INT32_MAX, FALSE, nullptr, nullptr, + &length, &data)) { return; } - if (NS_SUCCEEDED(rv)) { - if (needToDoConversionToImage) { - LOGDRAGSERVICE((" posting image\n")); - nsCOMPtr image = do_QueryInterface(data); - if (!image) { - LOGDRAGSERVICE((" do_QueryInterface failed\n")); - return; - } - GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image); - if (!pixbuf) { - LOGDRAGSERVICE((" ImageToPixbuf failed\n")); - return; - } - gtk_selection_data_set_pixbuf(aSelectionData, pixbuf); - LOGDRAGSERVICE((" image data set\n")); - g_object_unref(pixbuf); - } else { - void* tmpData = nullptr; - uint32_t tmpDataLen = 0; + // Zero-terminate the string. + data = (guchar*)g_realloc(data, length + 1); + if (!data) return; + data[length] = '\0'; - nsPrimitiveHelpers::CreateDataFromPrimitive( - nsDependentCString(actualFlavor), data, &tmpData, &tmpDataLen); - // if required, do the extra work to convert unicode to plain - // text and replace the output values with the plain text. - if (needToDoConversionToPlainText) { - char* plainTextData = nullptr; - char16_t* castedUnicode = reinterpret_cast(tmpData); - uint32_t plainTextLen = 0; - UTF16ToNewUTF8(castedUnicode, tmpDataLen / 2, &plainTextData, - &plainTextLen); - if (tmpData) { - // this was not allocated using glib - free(tmpData); - tmpData = plainTextData; - tmpDataLen = plainTextLen; - } - } - if (tmpData) { - // this copies the data - gtk_selection_data_set(aSelectionData, target, 8, (guchar*)tmpData, - tmpDataLen); - // this wasn't allocated with glib - free(tmpData); + gchar* hostname; + char* gfullpath = + g_filename_from_uri((const gchar*)data, &hostname, nullptr); + g_free(data); + if (!gfullpath) return; + + nsCString fullpath(gfullpath); + g_free(gfullpath); + + LOGDRAGSERVICE(("XdndDirectSave filepath is %s\n", fullpath.get())); + + // If there is no hostname in the URI, NULL will be stored. + // We should not accept uris with from a different host. + if (hostname) { + nsCOMPtr infoService = + do_GetService(NS_SYSTEMINFO_CONTRACTID); + if (!infoService) return; + + nsAutoCString host; + if (NS_SUCCEEDED(infoService->GetPropertyAsACString(u"host"_ns, host))) { + if (!host.Equals(hostname)) { + LOGDRAGSERVICE(("ignored drag because of different host.\n")); + + // Special error code "F" for this case. + gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"F", 1); + g_free(hostname); + return; } } - } else { - if (mimeFlavor.EqualsLiteral(gTextUriListType)) { - // fall back for text/uri-list - nsAutoCString list; - CreateURIList(mSourceDataItems, list); - gtk_selection_data_set(aSelectionData, target, 8, (guchar*)list.get(), - list.Length()); + + g_free(hostname); + } + + nsCOMPtr file; + if (NS_FAILED( + NS_NewNativeLocalFile(fullpath, false, getter_AddRefs(file)))) { + return; + } + + // We have to split the path into a directory and filename, + // because our internal file-promise API is based on these. + + nsCOMPtr directory; + file->GetParent(getter_AddRefs(directory)); + + item->SetTransferData(kFilePromiseDirectoryMime, directory); + + nsCOMPtr filenamePrimitive = + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID); + if (!filenamePrimitive) return; + + nsAutoString leafName; + file->GetLeafName(leafName); + filenamePrimitive->SetData(leafName); + + item->SetTransferData(kFilePromiseDestFilename, filenamePrimitive); + + // Request a different type in GetTransferData. + actualFlavor = kFilePromiseMime; + // if someone was asking for image we need to convert it + // from kNativeImageMime + } else if (mimeFlavor.EqualsLiteral(kPNGImageMime) || + mimeFlavor.EqualsLiteral(kJPEGImageMime) || + mimeFlavor.EqualsLiteral(kJPGImageMime) || + mimeFlavor.EqualsLiteral(kGIFImageMime)) { + actualFlavor = kNativeImageMime; + needToDoConversionToImage = true; + } else { + actualFlavor = typeName; + } + nsresult rv; + nsCOMPtr data; + rv = item->GetTransferData(actualFlavor, getter_AddRefs(data)); + + if (strcmp(actualFlavor, kFilePromiseMime) == 0) { + if (NS_SUCCEEDED(rv)) { + // Indicate success. + gtk_selection_data_set(aSelectionData, target, 8, (guchar*)"S", 1); + } + return; + } + + if (NS_SUCCEEDED(rv)) { + if (needToDoConversionToImage) { + LOGDRAGSERVICE((" posting image\n")); + nsCOMPtr image = do_QueryInterface(data); + if (!image) { + LOGDRAGSERVICE((" do_QueryInterface failed\n")); return; } + GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image); + if (!pixbuf) { + LOGDRAGSERVICE((" ImageToPixbuf failed\n")); + return; + } + gtk_selection_data_set_pixbuf(aSelectionData, pixbuf); + LOGDRAGSERVICE((" image data set\n")); + g_object_unref(pixbuf); + } else { + void* tmpData = nullptr; + uint32_t tmpDataLen = 0; + + nsPrimitiveHelpers::CreateDataFromPrimitive( + nsDependentCString(actualFlavor), data, &tmpData, &tmpDataLen); + // if required, do the extra work to convert unicode to plain + // text and replace the output values with the plain text. + if (needToDoConversionToPlainText) { + char* plainTextData = nullptr; + char16_t* castedUnicode = reinterpret_cast(tmpData); + uint32_t plainTextLen = 0; + UTF16ToNewUTF8(castedUnicode, tmpDataLen / 2, &plainTextData, + &plainTextLen); + if (tmpData) { + // this was not allocated using glib + free(tmpData); + tmpData = plainTextData; + tmpDataLen = plainTextLen; + } + } + if (tmpData) { + // this copies the data + gtk_selection_data_set(aSelectionData, target, 8, (guchar*)tmpData, + tmpDataLen); + // this wasn't allocated with glib + free(tmpData); + } + } + } else { + if (mimeFlavor.EqualsLiteral(gTextUriListType)) { + // fall back for text/uri-list + nsAutoCString list; + CreateURIList(mSourceDataItems, list); + gtk_selection_data_set(aSelectionData, target, 8, (guchar*)list.get(), + list.Length()); + return; } } } diff --git a/widget/gtk/nsDragService.h b/widget/gtk/nsDragService.h index 35a49bd94bd3..40471f126aaa 100644 --- a/widget/gtk/nsDragService.h +++ b/widget/gtk/nsDragService.h @@ -9,8 +9,10 @@ #include "mozilla/RefPtr.h" #include "nsBaseDragService.h" +#include "nsCOMArray.h" #include "nsIObserver.h" #include +#include "nsITimer.h" class nsICookieJarSettings; class nsWindow; @@ -36,7 +38,6 @@ class nsDragService final : public nsBaseDragService, public nsIObserver { nsDragService(); NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIOBSERVER // nsBaseDragService @@ -239,6 +240,19 @@ class nsDragService final : public nsBaseDragService, public nsIObserver { void GetDragFlavors(nsTArray& aFlavors); gboolean DispatchDropEvent(); static uint32_t GetCurrentModifiers(); + + nsresult CreateTempFile(nsITransferable* aItem, + GtkSelectionData* aSelectionData); + bool RemoveTempFiles(); + static gboolean TaskRemoveTempFiles(gpointer data); + + // the url of the temporary file that has been created in the current drag + // session + nsCString mTempFileUrl; + // stores all temporary files + nsCOMArray mTemporaryFiles; + // timer to trigger deletion of temporary files + guint mTempFileTimerID; }; #endif // nsDragService_h__