bug 440911: Add CF_HDROP support back into Windows drag and drop data object (dragging images to some applications, e.g. PhotoShop, fails); r=emaijala, approval1.9.1b2=beltzner

This commit is contained in:
Jim Mathies 2008-11-14 14:23:02 -08:00
Родитель 7494f0d268
Коммит 892d6df1f2
3 изменённых файлов: 207 добавлений и 19 удалений

Просмотреть файл

@ -62,6 +62,32 @@
#include "nsXPCOMStrings.h"
#include "nscore.h"
#include "prtypes.h"
#include "nsDirectoryServiceDefs.h"
// XXX Duped from profile/src/nsProfile.cpp.
#include <stdlib.h>
#define TABLE_SIZE 36
static const char table[] =
{ 'a','b','c','d','e','f','g','h','i','j',
'k','l','m','n','o','p','q','r','s','t',
'u','v','w','x','y','z','0','1','2','3',
'4','5','6','7','8','9' };
static void
MakeRandomString(char *buf, PRInt32 bufLen)
{
// turn PR_Now() into milliseconds since epoch
// and salt rand with that.
double fpTime;
LL_L2D(fpTime, PR_Now());
srand((uint)(fpTime * 1e-6 + 0.5)); // use 1e-6, granularity of PR_Now() on the mac is seconds
PRInt32 i;
for (i=0;i<bufLen;i++) {
*buf++ = table[rand()%TABLE_SIZE];
}
*buf = 0;
}
// XXX
// XXX for older version of PSDK where IAsyncOperation and related stuff is not available
// but this should be removed when parocles config is updated
@ -447,7 +473,7 @@ BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) c
STDMETHODIMP nsDataObj::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
{
PRNTDEBUG("nsDataObj::GetData\n");
PRNTDEBUG3(" format: %d Text: %d\n", pFE->cfFormat, CF_TEXT);
PRNTDEBUG3(" format: %d Text: %d\n", pFE->cfFormat, CF_HDROP);
if ( !mTransferable )
return ResultFromScode(DATA_E_FORMATETC);
@ -486,6 +512,11 @@ STDMETHODIMP nsDataObj::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
case CF_UNICODETEXT:
return GetText(*df, *pFE, *pSTM);
// Some 3rd party apps that receive drag and drop files from the browser
// window require support for this.
case CF_HDROP:
return GetFile(*pFE, *pSTM);
// Someone is asking for an image
case CF_DIB:
return GetDib(*df, *pFE, *pSTM);
@ -537,7 +568,7 @@ STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
{
PRNTDEBUG("nsDataObj::QueryGetData ");
PRNTDEBUG3("format: %d Text: %d\n", pFE->cfFormat, CF_TEXT);
PRNTDEBUG2("format: %d\n", pFE->cfFormat);
// Arbitrary system formats
LPDATAENTRY pde;
@ -553,7 +584,7 @@ STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
return S_OK;
}
}
PRNTDEBUG2("***** nsDataObj::QueryGetData - Unknown format %d\n", pFE->cfFormat);
return ResultFromScode(E_FAIL);
}
@ -1349,6 +1380,173 @@ HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGME
return ResultFromScode(S_OK);
}
//-----------------------------------------------------
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',
// if not fail.
PRUint32 dfInx = 0;
ULONG count;
FORMATETC fe;
m_enumFE->Reset();
PRBool found = PR_FALSE;
while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
nsCString * df = reinterpret_cast<nsCString*>(mDataFlavors.SafeElementAt(dfInx));
dfInx++;
if (df && df->EqualsLiteral(kNativeImageMime)) {
found = PR_TRUE;
break;
}
}
if (!found)
return E_FAIL;
nsresult rv;
PRUint32 len = 0;
nsCOMPtr<nsISupports> genericDataWrapper;
mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
nsCOMPtr<nsIImage> image ( do_QueryInterface(genericDataWrapper) );
if (!image) {
// In the 0.9.4 timeframe, I had some embedding clients put the nsIImage directly into the
// transferable. Newer code, however, wraps the nsIImage in a nsISupportsInterfacePointer.
// We should be backwards compatibile with code already out in the field. If we can't find
// the image directly out of the transferable, unwrap the image from its wrapper.
nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
if (ptr)
ptr->GetData(getter_AddRefs(image));
}
if (!image)
return E_FAIL;
// 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;
}
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;
rv = mCachedTempFile->GetPath(path);
if (NS_FAILED(rv))
return E_FAIL;
// Two null characters are needed to terminate the file name list.
HGLOBAL hGlobalMemory = NULL;
PRUint32 allocLen = path.Length() + 2;
aSTG.tymed = TYMED_HGLOBAL;
aSTG.pUnkForRelease = NULL;
hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(PRUnichar));
if (!hGlobalMemory)
return E_FAIL;
DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
// First, populate the drop file structure.
pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
pDropFile->fNC = 0;
pDropFile->pt.x = 0;
pDropFile->pt.y = 0;
pDropFile->fWide = TRUE;
// Copy the filename right after the DROPFILES structure.
PRUnichar* dest = (PRUnichar*)(((char*)pDropFile) + pDropFile->pFiles);
memcpy(dest, path.get(), (allocLen - 1) * sizeof(PRUnichar)); // Copies the null character in path as well.
// Two null characters are needed at the end of the file name.
// Lookup the CF_HDROP shell clipboard format for more info.
// Add the second null character right after the first one.
dest[allocLen - 1] = L'\0';
GlobalUnlock(hGlobalMemory);
aSTG.hGlobal = hGlobalMemory;
return S_OK;
}
//-----------------------------------------------------
HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
{
@ -1408,22 +1606,11 @@ CLSID nsDataObj::GetClassID() const
void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
{
// These two lists are the mapping to and from data flavors and FEs.
// Later, OLE will tell us it needs a certain type of FORMATETC (text,
// Later, OLE will tell us it needs a certain type of FORMATETC (text, unicode, etc)
// unicode, etc), so we will look up the data flavor that corresponds to
// the FE and then ask the transferable for that type of data.
#ifndef WINCE
// Just ignore the CF_HDROP here
// all file drags are now handled by CFSTR_FileContents format
if (aFE->cfFormat == CF_HDROP) {
return;
}
else
#endif
{
mDataFlavors.AppendElement(new nsCString(aDataFlavor));
m_enumFE->AddFE(aFE);
}
mDataFlavors.AppendElement(new nsCString(aDataFlavor));
m_enumFE->AddFE(aFE);
}
//-----------------------------------------------------

Просмотреть файл

@ -224,6 +224,7 @@ class nsDataObj : public IDataObject,
virtual HRESULT AddSetFormat(FORMATETC& FE);
virtual HRESULT AddGetFormat(FORMATETC& FE);
virtual HRESULT GetFile ( FORMATETC& aFE, STGMEDIUM& aSTG );
virtual HRESULT GetText ( const nsACString& aDF, FORMATETC& aFE, STGMEDIUM & aSTG );
virtual HRESULT GetBitmap ( const nsACString& inFlavor, FORMATETC& FE, STGMEDIUM& STM);
virtual HRESULT GetDib ( const nsACString& inFlavor, FORMATETC &, STGMEDIUM & aSTG );
@ -272,7 +273,7 @@ class nsDataObj : public IDataObject,
CEnumFormatEtc * m_enumFE; // Ownership Rules:
// nsDataObj owns and ref counts CEnumFormatEtc,
nsCOMPtr<nsILocalFile> mCachedTempFile;
nsCOMPtr<nsIFile> mCachedTempFile;
BOOL mIsAsyncMode;
BOOL mIsInOperation;

Просмотреть файл

@ -308,7 +308,7 @@ nsDragService::StartInvokingDragSession(IDataObject * aDataObj,
// do async drag
if (SUCCEEDED(aDataObj->QueryInterface(IID_IAsyncOperation,
(void**)&pAsyncOp)))
pAsyncOp->SetAsyncMode(TRUE);
pAsyncOp->SetAsyncMode(VARIANT_TRUE);
}
// Call the native D&D method