Bug 494989 - Windows drag and drop does not use temporary file when needed--fixes dragging image to apps that can handle images. r=roc, r=jmathies.

This commit is contained in:
Phil Lacy 2009-12-17 22:06:19 -06:00
parent f8ae6ca13f
commit 8170eb63e3
3 changed files with 118 additions and 124 deletions

View File

@ -63,6 +63,7 @@
#include "nscore.h"
#include "prtypes.h"
#include "nsDirectoryServiceDefs.h"
#include "nsITimer.h"
// XXX Duped from profile/src/nsProfile.cpp.
#include <stdlib.h>
@ -448,6 +449,22 @@ STDMETHODIMP_(ULONG) nsDataObj::Release()
if (0 != m_cRef)
return m_cRef;
// We have released our last ref on this object and need to delete the
// temp file. External app acting as drop target may still need to open the
// temp file. Addref a timer so it can delay deleting file and destroying
// this object. Delete file anyway and destroy this obj if there's a problem.
if (mCachedTempFile) {
nsresult rv;
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
mTimer->InitWithFuncCallback(nsDataObj::RemoveTempFile, this,
500, nsITimer::TYPE_ONE_SHOT);
return AddRef();
}
mCachedTempFile->Remove(PR_FALSE);
mCachedTempFile = NULL;
}
delete this;
return 0;
@ -622,15 +639,6 @@ IUnknown* nsDataObj::GetCanonicalIUnknown(IUnknown *punk)
STDMETHODIMP nsDataObj::SetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM, BOOL fRelease)
{
PRNTDEBUG("nsDataObj::SetData\n");
static CLIPFORMAT PerformedDropEffect = ::RegisterClipboardFormat( CFSTR_PERFORMEDDROPEFFECT );
if (pFE && pFE->cfFormat == PerformedDropEffect) {
// The drop operation has completed. Delete the temp file if it exists.
if (mCachedTempFile) {
mCachedTempFile->Remove(PR_FALSE);
mCachedTempFile = NULL;
}
}
// Store arbitrary system formats
LPDATAENTRY pde;
HRESULT hres = FindFORMATETC(pFE, &pde, TRUE); // add
@ -1355,8 +1363,6 @@ HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGME
//-----------------------------------------------------
HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG)
{
HRESULT res = S_OK;
// We do not support 'application/x-moz-file-promise' since CF_HDROP does not
// allow for delayed rendering of content. We'll need to write the content out emmediately
// and return the path to it. Confirm we have support for 'application/x-moz-nativeimage',
@ -1449,101 +1455,99 @@ HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG)
HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
{
nsresult rv;
PRUint32 len = 0;
nsCOMPtr<nsISupports> genericDataWrapper;
if (!mCachedTempFile) {
PRUint32 len = 0;
nsCOMPtr<nsISupports> genericDataWrapper;
mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
nsCOMPtr<imgIContainer> image ( do_QueryInterface(genericDataWrapper) );
if (!image) {
// Check if the image was put in an nsISupportsInterfacePointer wrapper.
// This might not be necessary any more, but could be useful for backwards
// compatibility.
nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
if (ptr)
ptr->GetData(getter_AddRefs(image));
}
mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
nsCOMPtr<imgIContainer> image(do_QueryInterface(genericDataWrapper));
if (!image)
return E_FAIL;
if (!image) {
// Check if the image was put in an nsISupportsInterfacePointer wrapper.
// This might not be necessary any more, but could be useful for backwards
// compatibility.
nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
if (ptr)
ptr->GetData(getter_AddRefs(image));
}
// Use the clipboard helper class to build up a memory bitmap.
nsImageToClipboard converter(image);
HANDLE bits = nsnull;
rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
if (NS_FAILED(rv) || !bits)
return E_FAIL;
if (!image)
return E_FAIL;
// We now own these bits!
PRUint32 bitmapSize = GlobalSize(bits);
if (!bitmapSize) {
// Use the clipboard helper class to build up a memory bitmap.
nsImageToClipboard converter(image);
HANDLE bits = nsnull;
rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
if (NS_FAILED(rv) || !bits)
return E_FAIL;
// We now own these bits!
PRUint32 bitmapSize = GlobalSize(bits);
if (!bitmapSize) {
GlobalFree(bits);
return E_FAIL;
}
// Save the bitmap to a temporary location.
nsCOMPtr<nsIFile> dropFile;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
if (!dropFile) {
GlobalFree(bits);
return E_FAIL;
}
// Filename must be random so as not to confuse apps like
// Photoshop which handle multiple drags into a single window.
char buf[13];
nsCString filename;
MakeRandomString(buf, 8);
memcpy(buf+8, ".bmp", 5);
filename.Append(nsDependentCString(buf, 12));
dropFile->AppendNative(filename);
rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
// Cache the temp file so we can delete it later and so
// it doesn't get recreated over and over on multiple calls
// which does occur from windows shell.
dropFile->Clone(getter_AddRefs(mCachedTempFile));
// Write the data to disk.
nsCOMPtr<nsIOutputStream> outStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
char * bm = (char *)GlobalLock(bits);
BITMAPFILEHEADER fileHdr;
BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
fileHdr.bfType = ((WORD) ('M' << 8) | 'B');
fileHdr.bfSize = GlobalSize (bits) + sizeof(fileHdr);
fileHdr.bfReserved1 = 0;
fileHdr.bfReserved2 = 0;
fileHdr.bfOffBits = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
PRUint32 writeCount = 0;
if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
rv = NS_ERROR_FAILURE;
outStream->Close();
GlobalUnlock(bits);
GlobalFree(bits);
return E_FAIL;
if (NS_FAILED(rv))
return E_FAIL;
}
if (mCachedTempFile) {
mCachedTempFile->Remove(PR_FALSE);
mCachedTempFile = NULL;
}
// Save the bitmap to a temporary location.
nsCOMPtr<nsIFile> dropFile;
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
if (!dropFile)
return E_FAIL;
// Filename must be random so as not to confuse apps like Photshop which handle
// multiple drags into a single window.
char buf[13];
nsCString filename;
MakeRandomString(buf, 8);
memcpy(buf+8, ".bmp", 5);
filename.Append(nsDependentCString(buf, 12));
dropFile->AppendNative(filename);
rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
// Cache the temp file so we can delete it later.
dropFile->Clone(getter_AddRefs(mCachedTempFile));
// Write the data to disk.
nsCOMPtr<nsIOutputStream> outStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
char * bm = (char *)GlobalLock(bits);
BITMAPFILEHEADER fileHdr;
BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
fileHdr.bfType = ((WORD) ('M' << 8) | 'B');
fileHdr.bfSize = GlobalSize (bits) + sizeof(fileHdr);
fileHdr.bfReserved1 = 0;
fileHdr.bfReserved2 = 0;
fileHdr.bfOffBits = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
PRUint32 writeCount = 0;
if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
rv = NS_ERROR_FAILURE;
outStream->Close();
GlobalUnlock(bits);
if (NS_FAILED(rv)) {
GlobalFree(bits);
return E_FAIL;
}
GlobalFree(bits);
// Pass the file name back to the drop target so that it can access the file.
nsAutoString path;
@ -2073,3 +2077,13 @@ HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
return S_OK;
}
void nsDataObj::RemoveTempFile(nsITimer* aTimer, void* aClosure)
{
nsDataObj *timedDataObj = static_cast<nsDataObj *>(aClosure);
if (timedDataObj->mCachedTempFile) {
timedDataObj->mCachedTempFile->Remove(PR_FALSE);
timedDataObj->mCachedTempFile = NULL;
}
timedDataObj->Release();
}

View File

@ -52,6 +52,8 @@
#include "nsIInputStream.h"
#include "nsIChannel.h"
#include "nsTPtrArray.h"
#include "nsCOMArray.h"
#include "nsITimer.h"
// XXX for older version of PSDK where IAsyncOperation and related stuff is not available
// but thisdefine should be removed when parocles config is updated
@ -333,6 +335,8 @@ class nsDataObj : public IDataObject,
BOOL fCopyIn);
IUnknown* GetCanonicalIUnknown(IUnknown *punk);
HGLOBAL GlobalClone(HGLOBAL hglobIn);
static void RemoveTempFile(nsITimer* aTimer, void* aClosure);
nsCOMPtr<nsITimer> mTimer;
};

View File

@ -365,30 +365,6 @@ nsDragService::StartInvokingDragSession(IDataObject * aDataObj,
SetDragEndPoint(nsIntPoint(cpos.x, cpos.y));
EndDragSession(PR_TRUE);
// For some drag/drop interactions, IDataObject::SetData doesn't get
// called with a CFSTR_PERFORMEDDROPEFFECT format and the
// intermediate file (if it was created) isn't deleted. See
// http://bugzilla.mozilla.org/show_bug.cgi?id=203847#c4 for a
// detailed description of the different cases. Now that we know
// that the drag/drop operation has ended, call SetData() so that
// the intermediate file is deleted.
static CLIPFORMAT PerformedDropEffect =
::RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
FORMATETC fmte =
{
(CLIPFORMAT)PerformedDropEffect,
NULL,
DVASPECT_CONTENT,
-1,
TYMED_NULL
};
STGMEDIUM medium;
medium.tymed = TYMED_NULL;
medium.pUnkForRelease = NULL;
aDataObj->SetData(&fmte, &medium, FALSE);
mDoingDrag = PR_FALSE;
return DRAGDROP_S_DROP == res ? NS_OK : NS_ERROR_FAILURE;