From 0c0955bd663390a972f42f73873deeace3eaec02 Mon Sep 17 00:00:00 2001 From: "gavin%gavinsharp.com" Date: Fri, 14 Jul 2006 01:48:51 +0000 Subject: [PATCH] Back out the patch from bug 267426 to fix Thunderbird (prometheus) build bustage. --- widget/src/windows/nsDataObj.cpp | 573 ++++++++------------------- widget/src/windows/nsDataObj.h | 69 +--- widget/src/windows/nsDragService.cpp | 343 +++++++++++++--- widget/src/windows/nsDragService.h | 39 +- 4 files changed, 510 insertions(+), 514 deletions(-) diff --git a/widget/src/windows/nsDataObj.cpp b/widget/src/windows/nsDataObj.cpp index 6065445b26b..55154fe5294 100644 --- a/widget/src/windows/nsDataObj.cpp +++ b/widget/src/windows/nsDataObj.cpp @@ -68,7 +68,6 @@ #include "nsEscape.h" #include "nsIURL.h" #include "nsNetUtil.h" -#include "nsXPCOMStrings.h" #if 0 #define PRNTDEBUG(_x) printf(_x); @@ -80,186 +79,6 @@ #define PRNTDEBUG3(_x1, _x2, _x3) // printf(_x1, _x2, _x3); #endif -//----------------------------------------------------------------------------- -// CStream implementation -nsDataObj::CStream::CStream() : mRefCount(1) -{ -} - -//----------------------------------------------------------------------------- -nsDataObj::CStream::~CStream() -{ -} - -//----------------------------------------------------------------------------- -// helper - initializes the stream -nsresult nsDataObj::CStream::Init(nsIURI *pSourceURI) -{ - nsresult rv; - rv = NS_NewChannel(getter_AddRefs(mChannel), pSourceURI); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mChannel->Open(getter_AddRefs(mInputStream)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -//----------------------------------------------------------------------------- -// IUnknown -STDMETHODIMP nsDataObj::CStream::QueryInterface(REFIID refiid, void** ppvResult) -{ - *ppvResult = NULL; - if (IID_IUnknown == refiid || - refiid == IID_IStream) - - { - *ppvResult = this; - } - - if (NULL != *ppvResult) - { - ((LPUNKNOWN)*ppvResult)->AddRef(); - return S_OK; - } - - return ResultFromScode(E_NOINTERFACE); -} - -//----------------------------------------------------------------------------- -STDMETHODIMP_(ULONG) nsDataObj::CStream::AddRef(void) -{ - return ++mRefCount; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP_(ULONG) nsDataObj::CStream::Release(void) -{ - ULONG nCount = --mRefCount; - if (nCount == 0) - { - delete this; - return (ULONG)0; - } - - return mRefCount; -} - -//----------------------------------------------------------------------------- -// IStream -STDMETHODIMP nsDataObj::CStream::Clone(IStream** ppStream) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::Commit(DWORD dwFrags) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::CopyTo(IStream* pDestStream, - ULARGE_INTEGER nBytesToCopy, - ULARGE_INTEGER* nBytesRead, - ULARGE_INTEGER* nBytesWritten) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart, - ULARGE_INTEGER nBytes, - DWORD dwFlags) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer, - ULONG nBytesToRead, - ULONG* nBytesRead) -{ - NS_ENSURE_TRUE(mInputStream, E_FAIL); - - nsresult rv; - PRUint32 read = 0; - rv = mInputStream->Read((char*)pvBuffer, nBytesToRead, &read); - *nBytesRead = read; - NS_ENSURE_SUCCESS(rv, S_FALSE); - - return S_OK; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::Revert(void) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::Seek(LARGE_INTEGER nMove, - DWORD dwOrigin, - ULARGE_INTEGER* nNewPos) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart, - ULARGE_INTEGER nBytes, - DWORD dwFlags) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -STDMETHODIMP nsDataObj::CStream::Write(const void* pvBuffer, - ULONG nBytesToRead, - ULONG* nBytesRead) -{ - return E_NOTIMPL; -} - -//----------------------------------------------------------------------------- -HRESULT nsDataObj::CreateStream(IStream **outStream) -{ - NS_ENSURE_TRUE(outStream, E_INVALIDARG); - - nsresult rv = NS_ERROR_FAILURE; - nsAutoString wideFileName; - nsCOMPtr sourceURI; - - rv = GetDownloadDetails(getter_AddRefs(sourceURI), - wideFileName); - NS_ENSURE_SUCCESS(rv, rv); - - nsDataObj::CStream *pStream = new nsDataObj::CStream(); - NS_ENSURE_TRUE(pStream, E_OUTOFMEMORY); - - rv = pStream->Init(sourceURI); - if (NS_FAILED(rv)) - { - pStream->Release(); - return E_FAIL; - } - *outStream = pStream; - - return S_OK; -} - ULONG nsDataObj::g_cRef = 0; EXTERN_C GUID CDECL CLSID_nsDataObj = @@ -294,9 +113,6 @@ nsDataObj::nsDataObj(nsIURI * uri) // so it can create a SourceURL for CF_HTML flavour uri->GetSpec(mSourceURL); } - - mIsAsyncMode = FALSE; - mIsInOperation = FALSE; } //----------------------------------------------------- // destruction @@ -328,12 +144,8 @@ STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv) if ( (IID_IUnknown == riid) || (IID_IDataObject == riid) ) { *ppv = this; AddRef(); - return S_OK; - } else if (IID_IAsyncOperation == riid) { - *ppv = static_cast(this); - AddRef(); - return S_OK; - } + return NOERROR; + } return ResultFromScode(E_NOINTERFACE); } @@ -413,7 +225,11 @@ STDMETHODIMP nsDataObj::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM) case CF_TEXT: case CF_UNICODETEXT: return GetText(*df, *pFE, *pSTM); - + +#ifndef WINCE + case CF_HDROP: + return GetFile(*df, *pFE, *pSTM); +#endif // Someone is asking for an image case CF_DIB: return GetDib(*df, *pFE, *pSTM); @@ -566,48 +382,6 @@ STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum) return ResultFromScode(E_FAIL); } -// IAsyncOperation methods -STDMETHODIMP nsDataObj::EndOperation(HRESULT hResult, - IBindCtx *pbcReserved, - DWORD dwEffects) -{ - mIsInOperation = FALSE; - Release(); - return S_OK; -} - -STDMETHODIMP nsDataObj::GetAsyncMode(BOOL *pfIsOpAsync) -{ - if (!pfIsOpAsync) - return E_FAIL; - - *pfIsOpAsync = mIsAsyncMode; - - return S_OK; -} - -STDMETHODIMP nsDataObj::InOperation(BOOL *pfInAsyncOp) -{ - if (!pfInAsyncOp) - return E_FAIL; - - *pfInAsyncOp = mIsInOperation; - - return S_OK; -} - -STDMETHODIMP nsDataObj::SetAsyncMode(BOOL fDoOpAsync) -{ - mIsAsyncMode = fDoOpAsync; - return S_OK; -} - -STDMETHODIMP nsDataObj::StartOperation(IBindCtx *pbcReserved) -{ - mIsInOperation = TRUE; - return S_OK; -} - //----------------------------------------------------- // other methods //----------------------------------------------------- @@ -706,21 +480,12 @@ nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnic // How we handle this depends on if we're dealing with an internet // shortcut, since those are done under the covers. - if (IsFlavourPresent(kFilePromiseMime) || - IsFlavourPresent(kFileMime)) - { - if (aIsUnicode) - return GetFileDescriptor_IStreamW(aFE, aSTG); - else - return GetFileDescriptor_IStreamA(aFE, aSTG); - } - else if (IsFlavourPresent(kURLMime)) - { + if ( IsInternetShortcut() ) { if ( aIsUnicode ) res = GetFileDescriptorInternetShortcutW ( aFE, aSTG ); else res = GetFileDescriptorInternetShortcutA ( aFE, aSTG ); - } + } else NS_WARNING ( "Not yet implemented\n" ); @@ -737,11 +502,8 @@ nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG ) // How we handle this depends on if we're dealing with an internet // shortcut, since those are done under the covers. - if (IsFlavourPresent(kFilePromiseMime) || - IsFlavourPresent(kFileMime)) - return GetFileContents_IStream(aFE, aSTG); - else if (IsFlavourPresent(kURLMime)) - return GetFileContentsInternetShortcut ( aFE, aSTG ); + if ( IsInternetShortcut() ) + res = GetFileContentsInternetShortcut ( aFE, aSTG ); else NS_WARNING ( "Not yet implemented\n" ); @@ -892,11 +654,11 @@ nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aST // get a valid filename in the following order: 1) from the page title, // 2) localized string for an untitled page, 3) just use "Untitled.URL" if (!CreateFilenameFromTextA(title, ".URL", - fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { + fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { nsXPIDLString untitled; if (!GetLocalizedString(NS_LITERAL_STRING("noPageTitle").get(), untitled) || !CreateFilenameFromTextA(untitled, ".URL", - fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { + fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL"); } } @@ -938,11 +700,11 @@ nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aST // get a valid filename in the following order: 1) from the page title, // 2) localized string for an untitled page, 3) just use "Untitled.URL" if (!CreateFilenameFromTextW(title, NS_LITERAL_STRING(".URL").get(), - fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { + fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { nsXPIDLString untitled; if (!GetLocalizedString(NS_LITERAL_STRING("noPageTitle").get(), untitled) || !CreateFilenameFromTextW(untitled, NS_LITERAL_STRING(".URL").get(), - fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { + fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) { wcscpy(fileGroupDescW->fgd[0].cFileName, NS_LITERAL_STRING("Untitled.URL").get()); } } @@ -1009,28 +771,39 @@ nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG ) #endif } // GetFileContentsInternetShortcut -// check if specified flavour is present in the transferable -PRBool nsDataObj :: IsFlavourPresent(const char *inFlavour) + +// +// IsInternetShortcut +// +// Is the data that we're handling destined for an internet shortcut if +// dragged to the desktop? We can tell because there will be a mime type +// of |kURLMime| present in the transferable +// +PRBool +nsDataObj :: IsInternetShortcut ( ) { PRBool retval = PR_FALSE; - NS_ENSURE_TRUE(mTransferable, PR_FALSE); + + if ( !mTransferable ) + return PR_FALSE; // get the list of flavors available in the transferable nsCOMPtr flavorList; - mTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList)); - NS_ENSURE_TRUE(flavorList, PR_FALSE); - - // try to find requested flavour + mTransferable->FlavorsTransferableCanExport ( getter_AddRefs(flavorList) ); + if ( !flavorList ) + return PR_FALSE; + + // go spelunking for the url flavor PRUint32 cnt; flavorList->Count(&cnt); - for (PRUint32 i = 0; i < cnt; ++i) { + for ( PRUint32 i = 0;i < cnt; ++i ) { nsCOMPtr genericFlavor; flavorList->GetElementAt (i, getter_AddRefs(genericFlavor)); nsCOMPtr currentFlavor (do_QueryInterface(genericFlavor)); if (currentFlavor) { nsCAutoString flavorStr; currentFlavor->GetData(flavorStr); - if (flavorStr.Equals(inFlavour)) { + if ( flavorStr.Equals(kURLMime) ) { retval = PR_TRUE; // found it! break; } @@ -1038,7 +811,8 @@ PRBool nsDataObj :: IsFlavourPresent(const char *inFlavour) } // for each flavor return retval; -} + +} // IsInternetShortcut HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG ) { @@ -1161,6 +935,93 @@ HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGME return ResultFromScode(S_OK); } +//----------------------------------------------------- +HRESULT nsDataObj::GetFile(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG) +{ + HRESULT res = S_OK; + if (strcmp(PromiseFlatCString(aDataFlavor).get(), kFilePromiseMime) == 0) { + // If we have a cached file just pass it to HGLOBAL + // otherwise create an empty file in the temp location. + // The download will start after the user drops the file. + nsresult rv; + if (!mCachedTempFile) { + // Get system temp directory + nsCOMPtr dropDirectory; + nsCOMPtr directoryService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); + if (!directoryService || NS_FAILED(rv)) + return ResultFromScode(E_FAIL); + directoryService->Get(NS_OS_TEMP_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(dropDirectory)); + + nsCOMPtr destFile = do_QueryInterface(dropDirectory); + if (!destFile) + return E_FAIL; + + // Get the filename form the kFilePromiseURLFlavour + nsAutoString wideFileName; + nsCOMPtr sourceURI; + rv = nsDataObj::GetDownloadDetails(mTransferable, + getter_AddRefs(sourceURI), + wideFileName); + if (NS_FAILED(rv)) + return rv; + + rv = destFile->Append(wideFileName); + if (NS_FAILED(rv)) + return rv; + + // Try to create the file. + rv = destFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600); + // Bail out if it fails for a reason other than the existence of the file. + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) + return rv; + + mCachedTempFile = do_QueryInterface(destFile); + if (!mCachedTempFile) + return ResultFromScode(E_FAIL); + } + + // Pass the file name back to the drop target so that it can copy to the drop location + nsAutoString path; + rv = mCachedTempFile->GetPath(path); + if (NS_FAILED(rv)) return ResultFromScode(E_FAIL); + // Two null characters are needed to terminate the file name list + PRUint32 allocLen = path.Length() + 2; + HGLOBAL hGlobalMemory = NULL; + aSTG.tymed = TYMED_HGLOBAL; + aSTG.pUnkForRelease = NULL; + hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(PRUnichar)); + if (hGlobalMemory) { + DROPFILES* pDropFile = NS_REINTERPRET_CAST(DROPFILES*, GlobalLock(hGlobalMemory)); + + // First, populate 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 = NS_REINTERPRET_CAST(PRUnichar*, NS_REINTERPRET_CAST(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); + } + else { + res = E_OUTOFMEMORY; + } + aSTG.hGlobal = hGlobalMemory; + } + return res; +} + //----------------------------------------------------- HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&) { @@ -1224,11 +1085,14 @@ void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE) // so we will look up data flavor that corresponds to the FE // and then ask the transferable for that type of data - // Just ignore the CF_HDROP here - // all file drags are now hangled by CFSTR_FileContents format + // Add CF_HDROP to the head of the list so that it is returned first + // when the drop target calls EnumFormatEtc to enumerate through the FE list + // We use the CF_HDROP format to copy file contents when an image is dragged + // from Mozilla to a drop target. #ifndef WINCE if (aFE->cfFormat == CF_HDROP) { - return; + mDataFlavors->InsertElementAt(new nsCAutoString(aDataFlavor), 0); + m_enumFE->InsertFEAt(aFE, 0); } else #endif @@ -1452,7 +1316,7 @@ HRESULT nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnicode ) { HRESULT res = S_OK; - if (IsFlavourPresent(kURLMime)) { + if ( IsInternetShortcut() ) { if ( aIsUnicode ) res = ExtractUniformResourceLocatorW( aFE, aSTG ); else @@ -1520,145 +1384,58 @@ nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG ) return result; } - // Gets the filename from the kFilePromiseURLMime flavour -nsresult nsDataObj::GetDownloadDetails(nsIURI **aSourceURI, +nsresult nsDataObj::GetDownloadDetails(nsITransferable *aTransferable, + nsIURI **aSourceURI, nsAString &aFilename) { - NS_ENSURE_TRUE(mTransferable, NS_ERROR_FAILURE); + if (!aTransferable) + return NS_ERROR_FAILURE; - // get the URI from the kFilePromiseURLMime flavor - nsCOMPtr urlPrimitive; - PRUint32 dataSize = 0; - mTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize); - nsCOMPtr srcUrlPrimitive = do_QueryInterface(urlPrimitive); - NS_ENSURE_TRUE(srcUrlPrimitive, NS_ERROR_FAILURE); - - // Get data for flavor - // The format of the data is (URLSTRING\nFILENAME) - nsAutoString strData; - srcUrlPrimitive->GetData(strData); - if (strData.IsEmpty()) - return 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); + if (!srcUrlPrimitive) + return NS_ERROR_FAILURE; - // Now figure if there is a "\n" delimiter in the data string. - // If there is, the string after the "\n" is a filename. - // If there is no delimiter then just get the filename from the url. - nsCAutoString strFileName; - nsCOMPtr sourceURI; - // New line char is used as a delimiter (hardcoded) - PRInt32 nPos = strData.FindChar('\n'); - // Store source uri - NS_NewURI(aSourceURI, Substring(strData, 0, nPos)); - if (nPos != -1) { - // if there is delimiter - CopyUTF16toUTF8(Substring(strData, nPos + 1, strData.Length()), strFileName); - } else { - // no filename was supplied - try to get it from a URL - nsCOMPtr sourceURL = do_QueryInterface(*aSourceURI); - sourceURL->GetFileName(strFileName); - } - // check for an error; the URL must point to a file - if (strFileName.IsEmpty()) - return NS_ERROR_FAILURE; + // Get data for flavor + // The format of the data is (URLSTRING\nFILENAME) + nsAutoString strData; + srcUrlPrimitive->GetData(strData); + if (strData.IsEmpty()) + return NS_ERROR_FAILURE; - NS_UnescapeURL(strFileName); - NS_ConvertUTF8toUTF16 wideFileName(strFileName); + // Now figure if there is a "\n" delimiter in the data string. + // If there is, the string after the "\n" is a filename. + // If there is no delimiter then just get the filename from the url. + nsCAutoString strFileName; + nsCOMPtr sourceURI; + // New line char is used as a delimiter (hardcoded) + PRInt32 nPos = strData.FindChar('\n'); + // Store source uri + NS_NewURI(aSourceURI, Substring(strData, 0, nPos)); + if (nPos != -1) { + // if there is delimiter + CopyUTF16toUTF8(Substring(strData, nPos + 1, strData.Length()), strFileName); + } else { + // no filename was supplied - try to get it from a URL + nsCOMPtr sourceURL = do_QueryInterface(*aSourceURI); + sourceURL->GetFileName(strFileName); + } + // check for an error; the URL must point to a file + if (strFileName.IsEmpty()) + return NS_ERROR_FAILURE; - // make the name safe for the filesystem - MangleTextToValidFilename(wideFileName); + NS_UnescapeURL(strFileName); + NS_ConvertUTF8toUTF16 wideFileName(strFileName); - aFilename = wideFileName; + // make the name safe for the filesystem + MangleTextToValidFilename(wideFileName); - return NS_OK; + aFilename = wideFileName; + + return NS_OK; } -HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG) -{ - HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW)); - NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY); - - LPFILEGROUPDESCRIPTORA fileGroupDescA = NS_REINTERPRET_CAST(LPFILEGROUPDESCRIPTORA, - ::GlobalLock(fileGroupDescHandle)); - if (!fileGroupDescA) { - ::GlobalFree(fileGroupDescHandle); - return E_OUTOFMEMORY; - } - - nsAutoString wideFileName; - nsresult rv; - nsCOMPtr sourceURI; - rv = GetDownloadDetails(getter_AddRefs(sourceURI), - wideFileName); - if (NS_FAILED(rv)) - { - ::GlobalFree(fileGroupDescHandle); - return E_FAIL; - } - - nsCAutoString nativeFileName; - NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, nativeFileName); - - strncpy(fileGroupDescA->fgd[0].cFileName, nativeFileName.get(), NS_MAX_FILEDESCRIPTOR - 1); - fileGroupDescA->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0'; - - // one file in the file block - fileGroupDescA->cItems = 1; - fileGroupDescA->fgd[0].dwFlags = FD_PROGRESSUI; - - ::GlobalUnlock( fileGroupDescHandle ); - aSTG.hGlobal = fileGroupDescHandle; - aSTG.tymed = TYMED_HGLOBAL; - - return S_OK; -} - -HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG) -{ - HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW)); - NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY); - - LPFILEGROUPDESCRIPTORW fileGroupDescW = NS_REINTERPRET_CAST(LPFILEGROUPDESCRIPTORW, - ::GlobalLock(fileGroupDescHandle)); - if (!fileGroupDescW) { - ::GlobalFree(fileGroupDescHandle); - return E_OUTOFMEMORY; - } - - nsAutoString wideFileName; - nsresult rv; - nsCOMPtr sourceURI; - rv = GetDownloadDetails(getter_AddRefs(sourceURI), - wideFileName); - if (NS_FAILED(rv)) - { - ::GlobalFree(fileGroupDescHandle); - return E_FAIL; - } - - wcsncpy(fileGroupDescW->fgd[0].cFileName, wideFileName.get(), NS_MAX_FILEDESCRIPTOR - 1); - fileGroupDescW->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0'; - // one file in the file block - fileGroupDescW->cItems = 1; - fileGroupDescW->fgd[0].dwFlags = FD_PROGRESSUI; - - ::GlobalUnlock(fileGroupDescHandle); - aSTG.hGlobal = fileGroupDescHandle; - aSTG.tymed = TYMED_HGLOBAL; - - return S_OK; -} - -HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG) -{ - IStream *pStream = NULL; - - nsDataObj::CreateStream(&pStream); - NS_ENSURE_TRUE(pStream, E_FAIL); - - aSTG.tymed = TYMED_ISTREAM; - aSTG.pstm = pStream; - aSTG.pUnkForRelease = pStream; - - return S_OK; -} diff --git a/widget/src/windows/nsDataObj.h b/widget/src/windows/nsDataObj.h index e012c08fc1d..79c10ffd857 100644 --- a/widget/src/windows/nsDataObj.h +++ b/widget/src/windows/nsDataObj.h @@ -48,8 +48,6 @@ #include "nsString.h" #include "nsILocalFile.h" #include "nsIURI.h" -#include "nsIInputStream.h" -#include "nsIChannel.h" #define MAX_FORMATS 32 @@ -114,8 +112,7 @@ class nsITransferable; * can be adapted by an object derived from CfDragDrop. The CfDragDrop is * associated with instances via SetDragDrop(). */ -class nsDataObj : public IDataObject, - public IAsyncOperation +class nsDataObj : public IDataObject { public: // construction, destruction nsDataObj(nsIURI *uri = nsnull); @@ -180,13 +177,6 @@ class nsDataObj : public IDataObject, // object. STDMETHODIMP EnumDAdvise (LPENUMSTATDATA *ppEnum); - // IAsyncOperation methods - STDMETHOD(EndOperation)(HRESULT hResult, IBindCtx *pbcReserved, DWORD dwEffects); - STDMETHOD(GetAsyncMode)(BOOL *pfIsOpAsync); - STDMETHOD(InOperation)(BOOL *pfInAsyncOp); - STDMETHOD(SetAsyncMode)(BOOL fDoOpAsync); - STDMETHOD(StartOperation)(IBindCtx *pbcReserved); - public: // other methods // Return the total reference counts of all instances of this class. @@ -197,17 +187,20 @@ class nsDataObj : public IDataObject, ULONG GetRefCount() const; // Gets the filename from the kFilePromiseURLMime flavour - nsresult GetDownloadDetails(nsIURI **aSourceURI, - nsAString &aFilename); + static nsresult GetDownloadDetails(nsITransferable *aTransferable, + nsIURI **aSourceURI, + nsAString &aFilename); protected: - // help determine the kind of drag - PRBool IsFlavourPresent(const char *inFlavour); + + // Help determine if the drag should create an internet shortcut + PRBool IsInternetShortcut ( ) ; virtual HRESULT AddSetFormat(FORMATETC& FE); virtual HRESULT AddGetFormat(FORMATETC& FE); virtual HRESULT GetText ( const nsACString& aDF, FORMATETC& aFE, STGMEDIUM & aSTG ); + virtual HRESULT GetFile ( 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 ); virtual HRESULT GetMetafilePict(FORMATETC& FE, STGMEDIUM& STM); @@ -229,11 +222,6 @@ class nsDataObj : public IDataObject, virtual HRESULT GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG ) ; virtual HRESULT GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG ) ; - // IStream implementation - virtual HRESULT GetFileDescriptor_IStreamA ( FORMATETC& aFE, STGMEDIUM& aSTG ) ; - virtual HRESULT GetFileDescriptor_IStreamW ( FORMATETC& aFE, STGMEDIUM& aSTG ) ; - virtual HRESULT GetFileContents_IStream ( FORMATETC& aFE, STGMEDIUM& aSTG ) ; - nsresult ExtractShortcutURL ( nsString & outURL ) ; nsresult ExtractShortcutTitle ( nsString & outTitle ) ; @@ -259,47 +247,6 @@ class nsDataObj : public IDataObject, // nsDataObj owns and ref counts CEnumFormatEtc, nsCOMPtr mCachedTempFile; - - BOOL mIsAsyncMode; - BOOL mIsInOperation; - /////////////////////////////////////////////////////////////////////////////// - // CStream class implementation - // this class is used in Drag and drop with download sample - // called from IDataObject::GetData - class CStream : public IStream - { - ULONG mRefCount; // reference counting - nsCOMPtr mInputStream; - nsCOMPtr mChannel; - - protected: - virtual ~CStream(); - // TODO: forbid copying and assignment - - public: - CStream(); - nsresult Init(nsIURI *pSourceURI); - - // IUnknown - STDMETHOD(QueryInterface)(REFIID refiid, void** ppvResult); - STDMETHOD_(ULONG, AddRef)(void); - STDMETHOD_(ULONG, Release)(void); - - // IStream - STDMETHOD(Clone)(IStream** ppStream); - STDMETHOD(Commit)(DWORD dwFrags); - STDMETHOD(CopyTo)(IStream* pDestStream, ULARGE_INTEGER nBytesToCopy, ULARGE_INTEGER* nBytesRead, ULARGE_INTEGER* nBytesWritten); - STDMETHOD(LockRegion)(ULARGE_INTEGER nStart, ULARGE_INTEGER nBytes, DWORD dwFlags); - STDMETHOD(Read)(void* pvBuffer, ULONG nBytesToRead, ULONG* nBytesRead); - STDMETHOD(Revert)(void); - STDMETHOD(Seek)(LARGE_INTEGER nMove, DWORD dwOrigin, ULARGE_INTEGER* nNewPos); - STDMETHOD(SetSize)(ULARGE_INTEGER nNewSize); - STDMETHOD(Stat)(STATSTG* statstg, DWORD dwFlags); - STDMETHOD(UnlockRegion)(ULARGE_INTEGER nStart, ULARGE_INTEGER nBytes, DWORD dwFlags); - STDMETHOD(Write)(const void* pvBuffer, ULONG nBytesToRead, ULONG* nBytesRead); - }; - - HRESULT CreateStream(IStream **outStream); }; diff --git a/widget/src/windows/nsDragService.cpp b/widget/src/windows/nsDragService.cpp index 0f3787daaba..c08a8617e9a 100644 --- a/widget/src/windows/nsDragService.cpp +++ b/widget/src/windows/nsDragService.cpp @@ -41,7 +41,6 @@ #include #include #include -#include // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN #include @@ -72,6 +71,10 @@ #include "nsDirectoryServiceDefs.h" #include "nsUnicharUtils.h" +// Static member declaration +PRUnichar *nsDragService::mDropPath; +PRUnichar *nsDragService::mFileName; + //------------------------------------------------------------------------- // // DragService constructor @@ -89,6 +92,11 @@ nsDragService::nsDragService() //------------------------------------------------------------------------- nsDragService::~nsDragService() { + if (mFileName) + NS_Free(mFileName); + if (mDropPath) + NS_Free(mDropPath); + NS_IF_RELEASE(mNativeDragSrc); NS_IF_RELEASE(mNativeDragTarget); NS_IF_RELEASE(mDataObject); @@ -158,8 +166,110 @@ nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode, } } // else dragging a single object + // Before starting get the file name to monitor if there is any + nsAutoString strData; // holds kFilePromiseURLMime flavour data + PRBool bDownload = PR_FALSE; // Used after drag ends to fugure if the download should start + + nsCOMPtr sourceURI; + nsCOMPtr urlPrimitive; + nsCOMPtr transSupports; + anArrayTransferables->GetElementAt(0, getter_AddRefs(transSupports)); + nsCOMPtr trans = (do_QueryInterface(transSupports)); + if (trans) { + // Get the filename form the kFilePromiseURLFlavour + nsAutoString wideFileName; + // if this fails there is no kFilePromiseMime flavour or no filename + rv = nsDataObj::GetDownloadDetails(trans, + getter_AddRefs(sourceURI), + wideFileName); + if (SUCCEEDED(rv)) + { + // Start listening to the shell notifications + if (StartWatchingShell(wideFileName)) { + // Set download flag if all went ok so far + bDownload = PR_TRUE; + } + } + } + // Kick off the native drag session - return StartInvokingDragSession(itemToDrag, aActionType); + rv = StartInvokingDragSession(itemToDrag, aActionType); + + // Check if the download flag was set + // if not then we are done + if (!bDownload) + return rv; + + if (NS_FAILED(rv)) // See if dragsession failed + { + ::DestroyWindow(mHiddenWnd); + return rv; + } + + // Construct target URI from nsILocalFile + // Init file for the download + nsCOMPtr dropLocalFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); + if (!dropLocalFile) + { + ::DestroyWindow(mHiddenWnd); + return NS_ERROR_FAILURE; + } + + // init with path we get from GetDropPath + // if it fails then there is no target path. + // This could happen if the drag operation was cancelled. + nsAutoString fileName; + if (!GetDropPath(fileName)) + return NS_OK; + + // hidden window is destroyed by now + rv = dropLocalFile->InitWithPath(fileName); + if (NS_FAILED(rv)) + return rv; + + // set the directory promise flavour + // if this fails it won't affect the drag so we can just continue + trans->SetTransferData(kFilePromiseDirectoryMime, + dropLocalFile, + sizeof(nsILocalFile*)); + + // Create a new FileURI to pass to the nsIDownload + nsCOMPtr targetURI; + + nsCOMPtr file = do_QueryInterface(dropLocalFile); + if (!file) + return NS_ERROR_FAILURE; + + rv = NS_NewFileURI(getter_AddRefs(targetURI), file); + if (NS_FAILED(rv)) + return rv; + + // Start download + nsCOMPtr fileAsSupports = do_QueryInterface(dropLocalFile); + + nsCOMPtr persist = do_CreateInstance(NS_WEBBROWSERPERSIST_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr transfer = do_CreateInstance("@mozilla.org/transfer;1", &rv); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr listener = do_QueryInterface(transfer); + rv = persist->SetProgressListener(listener); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr cancelable = do_QueryInterface(persist); + rv = transfer->Init(sourceURI, targetURI, NS_LITERAL_STRING(""), nsnull, PR_Now(), nsnull, cancelable); + if (NS_FAILED(rv)) + return rv; + + rv = persist->SaveURI(sourceURI, nsnull, nsnull, nsnull, nsnull, fileAsSupports); + if (NS_FAILED(rv)) + return rv; + + return rv; } //------------------------------------------------------------------------- @@ -197,34 +307,9 @@ nsDragService::StartInvokingDragSession(IDataObject * aDataObj, // Start dragging StartDragSession(); - // check shell32.dll version and do async drag if it is >= 5.0 - PRUint64 lShellVersion = GetShellVersion(); - IAsyncOperation *pAsyncOp = NULL; - PRBool isAsyncAvailable = LL_UCMP(lShellVersion, >=, LL_INIT(5, 0)); - if (isAsyncAvailable) - { - // do async drag - if (SUCCEEDED(aDataObj->QueryInterface(IID_IAsyncOperation, - (void**)&pAsyncOp))) - pAsyncOp->SetAsyncMode(TRUE); - } - // Call the native D&D method HRESULT res = ::DoDragDrop(aDataObj, mNativeDragSrc, effects, &dropRes); - if (isAsyncAvailable) - { - // if dragging async - // check for async operation - BOOL isAsync = FALSE; - if (pAsyncOp) - { - pAsyncOp->InOperation(&isAsync); - if (!isAsync) - aDataObj->Release(); - } - } - // We're done dragging EndDragSession(); @@ -490,37 +575,189 @@ nsDragService::EndDragSession() return NS_OK; } -// Gets shell version as packed 64 bit int -PRUint64 nsDragService::GetShellVersion() +// Start monitoring shell events - when user drops we'll get a notification from shell +// containing drop location +// aFileName is a filename to monitor +// returns true if all went ok +// if hidden window was created and shell is watching out for files being created +PRBool nsDragService::StartWatchingShell(const nsAString& aFileName) { - PRUint64 lVersion = LL_INIT(0, 0); + // init member varable with the file name to watch + if (mFileName) + NS_Free(mFileName); - // shell32.dll should be loaded already, so we ae not actually loading the library here - PRLibrary *libShell = PR_LoadLibrary("shell32.dll"); - if (libShell == NULL) - return lVersion; + mFileName = ToNewUnicode(aFileName); - do - { - DLLGETVERSIONPROC versionProc = NULL; - versionProc = (DLLGETVERSIONPROC)PR_FindFunctionSymbol(libShell, "DllGetVersion"); - if (versionProc == NULL) - break; + // Create hidden window to process shell notifications + WNDCLASS wc; - DLLVERSIONINFO versionInfo; - ::ZeroMemory(&versionInfo, sizeof(DLLVERSIONINFO)); - versionInfo.cbSize = sizeof(DLLVERSIONINFO); - if (FAILED(versionProc(&versionInfo))) - break; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = (WNDPROC)HiddenWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = NULL; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; + wc.lpszClassName = TEXT("DHHidden"); - // why is this? - PRUint32 maji64 = versionInfo.dwMajorVersion; - PRUint32 mini64 = versionInfo.dwMinorVersion; - lVersion = LL_INIT(maj, min); - } while (false); + unsigned short res = RegisterClass(&wc); - PR_UnloadLibrary(libShell); - libShell = NULL; + mHiddenWnd = CreateWindowEx(WS_EX_TOOLWINDOW, + TEXT("DHHidden"), + TEXT("DHHidden"), + WS_POPUP, + 0, + 0, + 100, + 100, + NULL, + NULL, + NULL, + NULL); + if (mHiddenWnd == NULL) + return PR_FALSE; - return lVersion; + // Now let the explorer know what we want + // Get My Computer PIDL + LPITEMIDLIST pidl; + HRESULT hr = ::SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl); + + if (SUCCEEDED(hr)) { + SHChangeNotifyStruct notify; + notify.pidl = pidl; + notify.fRecursive = TRUE; + + HMODULE hShell32 = ::GetModuleHandleA("SHELL32.DLL"); + + if (hShell32 == NULL) { + ::DestroyWindow(mHiddenWnd); + return PR_FALSE; + } + + // Have to use ordinals in the call to GetProcAddress instead of function names. + // The reason for this is because on some older systems (prior to WinXP) + // this function has no name in shell32.dll. + DWORD dwOrdinal = 2; + // get reg func + SHCNRegPtr reg; + reg = (SHCNRegPtr)::GetProcAddress(hShell32, (LPCSTR)dwOrdinal); + if (reg == NULL) { + ::DestroyWindow(mHiddenWnd); + return PR_FALSE; + } + + mNotifyHandle = (reg)(mHiddenWnd, + 0x2, + 0x1, + WM_USER, + 1, + ¬ify); + } + + // destroy hidden window if failed to register notification handle + if (mNotifyHandle == 0) { + ::DestroyWindow(mHiddenWnd); + } + + return (mNotifyHandle != 0); +} + +// Get the drop path if there is one:) +// returns true if there is a drop path +PRBool nsDragService::GetDropPath(nsAString& aDropPath) const +{ + // we failed to create window so there is no drop path + if (mHiddenWnd == NULL) + return PR_FALSE; + + // If we get 0 for this that means we failed to register notification callback + if (mNotifyHandle == 0) + return PR_FALSE; + + // Do clean up stuff (reverses all that's been done in Start) + HMODULE hShell32 = ::GetModuleHandleA("SHELL32.DLL"); + + if (hShell32 == NULL) + return PR_FALSE; + + // Have to use ordinals in the call to GetProcAddress instead of function names. + // The reason for this is because on some older systems (prior to WinXP) + // this function has no name in shell32.dll. + DWORD dwOrdinal = 4; + SHCNDeregPtr dereg; + dereg = (SHCNDeregPtr)::GetProcAddress(hShell32, (LPCSTR)dwOrdinal); + if (dereg == NULL) + return PR_FALSE; + (dereg)(mNotifyHandle); + + ::DestroyWindow(mHiddenWnd); + + // if drop path is too short then there is no drop location + if (!mDropPath) + return PR_FALSE; + + aDropPath.Assign(mDropPath); + + return PR_TRUE; +} + +// Hidden window procedure - this is where shell sends notifications +// When notification is received, perform some checking and copy path to the member variable +LRESULT WINAPI nsDragService::HiddenWndProc(HWND aWnd, UINT aMsg, WPARAM awParam, LPARAM alParam) +{ + switch (aMsg) { + case WM_USER: + if (alParam == SHCNE_RENAMEITEM) { + // we got notification from shell + // as wParam we get 2 PIDL + LPCITEMIDLIST* ppidl = (LPCITEMIDLIST*)awParam; + // Get From path (where the file is comming from) + WCHAR szPathFrom[MAX_PATH + 1]; + WCHAR szPathTo[MAX_PATH + 1]; + ::SHGetPathFromIDListW(ppidl[0], szPathFrom); + // Get To path (where the file is going to) + ::SHGetPathFromIDListW(ppidl[1], szPathTo); + + // first is from where the file is coming + // and the second is where the file is going + nsAutoString pathFrom(szPathFrom); // where the file is comming from + nsAutoString pathTo(szPathTo); // where the file is going to + // Get OS Temp directory + // Get system temp directory + nsresult rv; + nsCOMPtr tempDir; + nsCOMPtr directoryService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); + if (!directoryService || NS_FAILED(rv)) + return ResultFromScode(E_FAIL); + directoryService->Get(NS_OS_TEMP_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(tempDir)); + + // append file name to Temp directory string + nsAutoString tempPath; + tempDir->Append(nsDependentString(mFileName)); + tempDir->GetPath(tempPath); + + // Now check if there is our filename in the path + // and also check for the source directory - it should be OS Temp dir + // this way we can ensure that this is the file that we need + PRInt32 pathToLength = pathTo.Length(); + if (Substring(pathTo, pathToLength - NS_strlen(mFileName), pathToLength).Equals(mFileName) && + tempPath.Equals(pathFrom, nsCaseInsensitiveStringComparator())) + { + // This is what we wanted to get + if (mDropPath) + NS_Free(mDropPath); + + mDropPath = ToNewUnicode(pathTo); + } + return 0; + } + break; + } + + return ::DefWindowProc(aWnd, aMsg, awParam, alParam); } diff --git a/widget/src/windows/nsDragService.h b/widget/src/windows/nsDragService.h index 3458d758bac..e44c6e18469 100644 --- a/widget/src/windows/nsDragService.h +++ b/widget/src/windows/nsDragService.h @@ -48,6 +48,30 @@ class nsNativeDragTarget; class nsDataObjCollection; class nsString; +// some older versions of MS PSDK do not have definitions for these, even though +// the functions are there and exported from the shell32.dll, +// so I had to add them manually. For example if one tries to build without those +// using VC6 standard libs one will get an error. +#ifndef SHCNE_RENAMEITEM_DEF +#define SHCNE_RENAMEITEM_DEF 0x00000001 + +// Structure +typedef struct { + LPCITEMIDLIST pidl; + BOOL fRecursive; +} SHChangeNotifyStruct; +#endif // SHCNE_RENAMEITEM_DEF + +// Register for shell notifications func ptr +typedef WINSHELLAPI ULONG (WINAPI *SHCNRegPtr)(HWND hWnd, // Ordinal of 2 + int fSources, + LONG fEvents, + UINT wMsg, + int cEntries, + SHChangeNotifyStruct *pschn); +// Unregister form from the shell notifications func ptr +typedef WINSHELLAPI BOOL (WINAPI *SHCNDeregPtr)(ULONG ulID); // Ordinal of 4 + /** * Native Win32 DragService wrapper */ @@ -82,12 +106,23 @@ protected: // collections PRBool IsCollectionObject(IDataObject* inDataObj); - // gets shell version - PRUint64 GetShellVersion(); + // Begin monitoring the shell for events (this would allow to figure drop location) + PRBool StartWatchingShell(const nsAString& aFileName); + + // Should be called after drag is over to get the drop path (if any) + PRBool GetDropPath(nsAString& aDropPath) const; + + // Hidden window procedure - here we shall receive notification from the shell + static LRESULT WINAPI HiddenWndProc(HWND aWnd, UINT aMsg, WPARAM awParam, LPARAM alParam); IDropSource * mNativeDragSrc; nsNativeDragTarget * mNativeDragTarget; IDataObject * mDataObject; + + static PRUnichar *mFileName; // File name to look for + static PRUnichar *mDropPath; // Drop path + HWND mHiddenWnd; // Handle to a hidden window for notifications + PRInt32 mNotifyHandle; // Handle to installed hook (SHChangeNotify) }; #endif // nsDragService_h__