(From update of attachment 403730) >diff --git a/widget/src/gtk2/nsDragService.cpp b/widget/src/gtk2/nsDragService.cpp >--- a/widget/src/gtk2/nsDragService.cpp >+++ b/widget/src/gtk2/nsDragService.cpp >@@ -53,26 +53,31 @@ > #include "nsNetUtil.h" > #include "prlog.h" > #include "nsVoidArray.h" > #include "nsPrimitiveHelpers.h" > #include "prtime.h" > #include "prthread.h" > #include > #include >+#include > #include "nsCRT.h" > > #include "gfxASurface.h" > #include "gfxXlibSurface.h" > #include "gfxContext.h" > #include "nsImageToPixbuf.h" > #include "nsIPresShell.h" > #include "nsPresContext.h" > #include "nsIDocument.h" > #include "nsISelection.h" >+#include "nsDirectoryService.h" >+#include "nsDirectoryServiceDefs.h" >+#include "nsEscape.h" >+#include "nsString.h" > > // This sets how opaque the drag image is > #define DRAG_IMAGE_ALPHA_LEVEL 0.5 > > // These values are copied from GtkDragResult (rather than using GtkDragResult > // directly) so that this code can be compiled against versions of GTK+ that > // do not have GtkDragResult. > // GtkDragResult is available from GTK+ version 2.12. >@@ -141,16 +146,19 @@ nsDragService::nsDragService() > PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::nsDragService")); > mTargetWidget = 0; > mTargetDragContext = 0; > mTargetTime = 0; > mCanDrop = PR_FALSE; > mTargetDragDataReceived = PR_FALSE; > mTargetDragData = 0; > mTargetDragDataLen = 0; >+ mTempFileCreated = PR_FALSE; >+ mTimer = do_CreateInstance("@mozilla.org/timer;1"); >+ > } > > nsDragService::~nsDragService() > { > PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::~nsDragService")); > } > > NS_IMPL_ISUPPORTS_INHERITED2(nsDragService, nsBaseDragService, >@@ -173,16 +181,36 @@ nsDragService::Observe(nsISupports *aSub > } else { > NS_NOTREACHED("unexpected topic"); > return NS_ERROR_UNEXPECTED; > } > > return NS_OK; > } > >+// nsITimer >+ >+NS_IMETHODIMP >+nsDragService::Notify(nsITimer *timer) >+{ >+ // 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. Instead we collect all temporary >+ // files in mTemporaryFiles array and remove them here in the timer event. >+ PRUint32 count = mTemporaryFiles.Count(); >+ >+ while (count > 0) { >+ --count; >+ mTemporaryFiles[count]->Remove(PR_TRUE); >+ } >+ mTemporaryFiles.Clear(); >+ >+ return NS_OK; >+} >+ > // nsIDragService > > NS_IMETHODIMP > nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, > nsISupportsArray * aArrayTransferables, > nsIScriptableRegion * aRegion, > PRUint32 aActionType) > { >@@ -321,16 +349,18 @@ nsDragService::SetAlphaPixmap(gfxASurfac > gdk_pixmap_unref(pixmap); > return PR_TRUE; > } > > NS_IMETHODIMP > nsDragService::StartDragSession() > { > PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::StartDragSession")); >+ mTempFileCreated = false; >+ > return nsBaseDragService::StartDragSession(); > } > > NS_IMETHODIMP > nsDragService::EndDragSession(PRBool aDoneDrag) > { > PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::EndDragSession %d", > aDoneDrag)); >@@ -439,16 +469,59 @@ nsDragService::GetNumDropItems(PRUint32 > *aNumItems = CountTextUriListItems(data, mTargetDragDataLen); > } else > *aNumItems = 1; > } > PR_LOG(sDragLm, PR_LOG_DEBUG, ("%d items", *aNumItems)); > return NS_OK; > } > >+nsresult GetDownloadDetails(nsITransferable * aTransferable, nsIURI **aSourceURI, nsAString &aFilename) >+{ >+ *aSourceURI = nsnull; >+ >+ NS_ENSURE_TRUE(aTransferable, NS_ERROR_FAILURE); >+ >+ // get the URI from the kFilePromiseURLMime flavor >+ nsCOMPtr urlPrimitive; >+ PRUint32 dataSize = 0; >+ aTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize); >+ nsCOMPtr srcUrlPrimitive = do_QueryInterface(urlPrimitive); >+ NS_ENSURE_TRUE(srcUrlPrimitive, 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; >+ aTransferable->GetTransferData(kFilePromiseDestFilename, getter_AddRefs(fileNamePrimitive), &dataSize); >+ nsCOMPtr srcFileNamePrimitive = do_QueryInterface(fileNamePrimitive); >+ if (srcFileNamePrimitive) { >+ srcFileNamePrimitive->GetData(srcFileName); >+ } else { >+ nsCOMPtr sourceURL = do_QueryInterface(sourceURI); >+ if (!sourceURL) >+ return NS_ERROR_FAILURE; >+ >+ nsCAutoString 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; >+} > > NS_IMETHODIMP > nsDragService::GetData(nsITransferable * aTransferable, > PRUint32 aItemIndex) > { > PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::GetData %d", aItemIndex)); > > // make sure that we have a transferable >@@ -1063,16 +1136,17 @@ nsDragService::GetSourceList(void) > listTarget->target = g_strdup(gTextUriListType); > listTarget->flags = 0; > /* Bug 331198 */ > listTarget->info = NS_PTR_TO_UINT32(listAtom); > PR_LOG(sDragLm, PR_LOG_DEBUG, > ("automatically adding target %s with \ > id %ld\n", listTarget->target, listAtom)); > targetArray.AppendElement(listTarget); >+ break; > } > } > } // foreach flavor in item > } // if valid flavor list > } // if item is a transferable > } else if (numDragItems == 1) { > nsCOMPtr genericItem; > mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem)); >@@ -1138,16 +1212,28 @@ nsDragService::GetSourceList(void) > urlTarget->flags = 0; > /* Bug 331198 */ > urlTarget->info = NS_PTR_TO_UINT32(urlAtom); > PR_LOG(sDragLm, PR_LOG_DEBUG, > ("automatically adding target %s with \ > id %ld\n", urlTarget->target, urlAtom)); > targetArray.AppendElement(urlTarget); > } >+ // check if application/x-moz-file-promise url is supported. >+ // If so, advertise text/uri-list. >+ if (strcmp(flavorStr, kFilePromiseURLMime) == 0) { >+ GdkAtom urlAtom = gdk_atom_intern(gTextUriListType, FALSE); >+ GtkTargetEntry *urlTarget = >+ (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry)); >+ urlTarget->target = g_strdup(gTextUriListType); >+ urlTarget->flags = 0; >+ /* Bug 331198 */ >+ urlTarget->info = NS_PTR_TO_UINT32(urlAtom); >+ targetArray.AppendElement(urlTarget); >+ } > } > } // foreach flavor in item > } // if valid flavor list > } // if item is a transferable > } // if it is a single item drag > > // get all the elements that we created. > targetCount = targetArray.Count(); >@@ -1235,16 +1321,18 @@ nsDragService::SourceEndDragSession(GdkD > do_QueryInterface(mDataTransfer); > > if (dataTransfer) { > dataTransfer->SetDropEffectInt(dropEffect); > } > > // Inform the drag session that we're ending the drag. > EndDragSession(PR_TRUE); >+ >+ mTempFileCreated = PR_FALSE; > } > > static void > CreateUriList(nsISupportsArray *items, gchar **text, gint *length) > { > PRUint32 i, count; > GString *uriList = g_string_new(NULL); > >@@ -1301,16 +1389,128 @@ CreateUriList(nsISupportsArray *items, g > } > } > } > *text = uriList->str; > *length = uriList->len + 1; > g_string_free(uriList, FALSE); // don't free the data > } > >+PRBool >+nsDragService::CreateTempFile(nsITransferable *aItem, GtkSelectionData *aSelectionData) >+{ >+ nsCOMPtr tmpDir; >+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir)); >+ >+ if (NS_FAILED(rv)) { >+ PR_LOG(sDragLm, PR_LOG_DEBUG, ("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)) { >+ PR_LOG(sDragLm, PR_LOG_DEBUG, ("Failed to extract file name and source uri from download url\n")); >+ return rv; >+ } >+ >+ // create and open channel for source uri >+ rv = NS_NewChannel(getter_AddRefs(channel), sourceURI); >+ if (NS_FAILED(rv)) { >+ PR_LOG(sDragLm, PR_LOG_DEBUG, ("Failed to create new channel for source uri\n")); >+ return rv; >+ } >+ >+ rv = channel->Open(getter_AddRefs(inputStream)); >+ if (NS_FAILED(rv)) { >+ PR_LOG(sDragLm, PR_LOG_DEBUG, ("Failed to open channel for source uri\n")); >+ return rv; >+ } >+ >+ // build the file:///tmp/dnd_file URL >+ tmpDir->Append(NS_LITERAL_STRING("dnd_file")); >+ tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700); >+ >+ // 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); >+ >+ mTimer->Cancel(); >+ mTimer->InitWithCallback(static_cast(this), >+ NS_DND_TIMEOUT, >+ nsITimer::TYPE_ONE_SHOT); >+ >+ // extend file:///tmp/dnd_file/ URL >+ tmpDir->Append(wideFileName); >+ >+ nsCOMPtr outputStream; >+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), tmpDir); >+ if (NS_FAILED(rv)) { >+ PR_LOG(sDragLm, PR_LOG_DEBUG, ("Failed to open output stream for temporary file\n")); >+ return rv; >+ } >+ mTempFileCreated = PR_TRUE; >+ >+ char buffer[512]; >+ PRUint32 readCount = 0; >+ PRUint32 writeCount = 0; >+ while (1) { >+ rv = inputStream->Read(buffer, sizeof(buffer), &readCount); >+ if (NS_FAILED(rv)) { >+ PR_LOG(sDragLm, PR_LOG_DEBUG, ("Failed to read data from source uri\n")); >+ return rv; >+ } >+ >+ if (readCount == 0) >+ break; >+ >+ rv = outputStream->Write(buffer, readCount, &writeCount); >+ if (NS_FAILED(rv)) { >+ PR_LOG(sDragLm, PR_LOG_DEBUG, ("Failed to write data to temporary file\n")); >+ return rv; >+ } >+ } >+ >+ outputStream->Close(); >+ inputStream->Close(); >+ >+ nsCOMPtr uri; >+ rv = NS_NewFileURI(getter_AddRefs(uri), tmpDir); >+ if (NS_SUCCEEDED(rv)) { >+ nsCOMPtr fileURL(do_QueryInterface(uri)); >+ if (fileURL) { >+ // according to XDG spec, text/uri-list urls need a hostname set >+ char hostname[255]; >+ gethostname(hostname, sizeof(hostname)); >+ >+ nsCAutoString hostnameStr; >+ hostnameStr.AssignASCII(hostname); >+ >+ fileURL->SetHost(hostnameStr); >+ >+ nsCAutoString urltext; >+ rv = fileURL->GetSpec(urltext); >+ if (NS_SUCCEEDED(rv)) { >+ // store url of temporary file >+ mTempFileUrl.AssignASCII(urltext.get()); >+ return NS_OK; >+ } >+ } >+ } >+ >+ return NS_ERROR_FAILURE; >+} > > void > nsDragService::SourceDataGet(GtkWidget *aWidget, > GdkDragContext *aContext, > GtkSelectionData *aSelectionData, > guint aInfo, > guint32 aTime) > { >@@ -1357,16 +1557,63 @@ nsDragService::SourceDataGet(GtkWidget > // plain text. > else if (strcmp(mimeFlavor, gTextUriListType) == 0) { > actualFlavor = gTextUriListType; > needToDoConversionToPlainText = PR_TRUE; > } > else > actualFlavor = mimeFlavor; > >+ // 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. >+ if (strcmp(mimeFlavor, gTextUriListType) == 0) { >+ PRUint32 i, count; >+ mSourceDataItems->Count(&count); >+ >+ // check whether transferable contains FilePromiseUrl flavor... >+ PRBool foundFilePromiseFlavor = PR_FALSE; >+ for (i = 0; i < count; i++) { >+ nsCOMPtr genericItem; >+ mSourceDataItems->GetElementAt(i, getter_AddRefs(genericItem)); >+ nsCOMPtr item; >+ item = do_QueryInterface(genericItem); >+ >+ PRUint32 tmpDataLen = 0; >+ nsresult rv; >+ nsCOMPtr data; >+ rv = item->GetTransferData(kFilePromiseURLMime, >+ getter_AddRefs(data), >+ &tmpDataLen); >+ if (NS_SUCCEEDED(rv)) { >+ foundFilePromiseFlavor = PR_TRUE; >+ break; >+ } >+ } >+ >+ // ... if so, create a temporary file and pass its url as text/uri-list flavor >+ if (foundFilePromiseFlavor == PR_TRUE) { >+ if (!mTempFileCreated) { >+ nsresult rv; >+ rv = CreateTempFile(item, aSelectionData); >+ if (NS_FAILED(rv)) >+ return; >+ } >+ >+ NS_ConvertUTF16toUTF8 tempFileUrl(mTempFileUrl); >+ gtk_selection_data_set(aSelectionData, >+ aSelectionData->target, >+ 8, (guchar*)tempFileUrl.get(), tempFileUrl.Length()); >+ return; >+ } >+ } >+ > PRUint32 tmpDataLen = 0; > void *tmpData = NULL; > nsresult rv; > nsCOMPtr data; > rv = item->GetTransferData(actualFlavor, > getter_AddRefs(data), > &tmpDataLen); > if (NS_SUCCEEDED(rv)) { >diff --git a/widget/src/gtk2/nsDragService.h b/widget/src/gtk2/nsDragService.h >--- a/widget/src/gtk2/nsDragService.h >+++ b/widget/src/gtk2/nsDragService.h >@@ -37,36 +37,40 @@ > * the terms of any one of the MPL, the GPL or the LGPL. > * > * ***** END LICENSE BLOCK ***** */ > > #ifndef nsDragService_h__ > #define nsDragService_h__ > > #include "nsBaseDragService.h" >+#include "nsCOMArray.h" > #include "nsIDragSessionGTK.h" > #include "nsIObserver.h" >+#include "nsITimer.h" > #include > > > /** > * Native GTK DragService wrapper > */ > > class nsDragService : public nsBaseDragService, > public nsIDragSessionGTK, >+ public nsITimerCallback, > public nsIObserver > { > public: > nsDragService(); > virtual ~nsDragService(); > > NS_DECL_ISUPPORTS_INHERITED > > NS_DECL_NSIOBSERVER >+ NS_DECL_NSITIMERCALLBACK > > // nsIDragService > NS_IMETHOD InvokeDragSession (nsIDOMNode *aDOMNode, > nsISupportsArray * anArrayTransferables, > nsIScriptableRegion * aRegion, > PRUint32 aActionType); > NS_IMETHOD StartDragSession(); > NS_IMETHOD EndDragSession(PRBool aDoneDrag); >@@ -146,12 +150,25 @@ private: > // attempts to create a semi-transparent drag image. Returns TRUE if > // successful, FALSE if not > PRBool SetAlphaPixmap(gfxASurface *aPixbuf, > GdkDragContext *aContext, > PRInt32 aXOffset, > PRInt32 aYOffset, > const nsRect& dragRect); > >+ PRBool CreateTempFile(nsITransferable *aItem, GtkSelectionData *aSelectionData); >+ >+ // stores whether a temporary file has been created for the >+ // current drag session >+ PRBool mTempFileCreated; >+ >+ nsString mTempFileUrl; >+ >+ // stores all temporary files >+ nsCOMArray mTemporaryFiles; >+ >+ // timer to trigger deletion of temporary files >+ nsCOMPtr mTimer; > }; > > #endif // nsDragService_h__ >