Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / widget / src / windows / nsDataObj.cpp
blob99f0d92f14a39abf10c67ccbda3d4192eaa750fe
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Sean Echevarria <Sean@Beatnik.com>
24 * Blake Ross <blaker@netscape.com>
25 * Brodie Thiesfield <brofield@jellycan.com>
26 * Masayuki Nakano <masayuki@d-toybox.com>
27 * David Gardiner <david.gardiner@unisa.edu.au>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either the GNU General Public License Version 2 or later (the "GPL"), or
31 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 #include <ole2.h>
44 #include <shlobj.h>
46 #include "nsDataObj.h"
47 #include "nsClipboard.h"
48 #include "nsReadableUtils.h"
49 #include "nsITransferable.h"
50 #include "nsISupportsPrimitives.h"
51 #include "IEnumFE.h"
52 #include "nsPrimitiveHelpers.h"
53 #include "nsXPIDLString.h"
54 #include "nsIImage.h"
55 #include "nsImageClipboard.h"
56 #include "nsCRT.h"
57 #include "nsPrintfCString.h"
58 #include "nsIStringBundle.h"
59 #include "nsEscape.h"
60 #include "nsIURL.h"
61 #include "nsNetUtil.h"
62 #include "nsXPCOMStrings.h"
63 #include "nscore.h"
64 #include "prtypes.h"
65 #include "nsDirectoryServiceDefs.h"
67 // XXX Duped from profile/src/nsProfile.cpp.
68 #include <stdlib.h>
69 #define TABLE_SIZE 36
70 static const char table[] =
71 { 'a','b','c','d','e','f','g','h','i','j',
72 'k','l','m','n','o','p','q','r','s','t',
73 'u','v','w','x','y','z','0','1','2','3',
74 '4','5','6','7','8','9' };
75 static void
76 MakeRandomString(char *buf, PRInt32 bufLen)
78 // turn PR_Now() into milliseconds since epoch
79 // and salt rand with that.
80 double fpTime;
81 LL_L2D(fpTime, PR_Now());
82 srand((uint)(fpTime * 1e-6 + 0.5)); // use 1e-6, granularity of PR_Now() on the mac is seconds
84 PRInt32 i;
85 for (i=0;i<bufLen;i++) {
86 *buf++ = table[rand()%TABLE_SIZE];
88 *buf = 0;
90 // XXX
92 // XXX for older version of PSDK where IAsyncOperation and related stuff is not available
93 // but this should be removed when parocles config is updated
94 // IAsyncOperation interface GUID
95 #ifndef __IAsyncOperation_INTERFACE_DEFINED__
96 const IID IID_IAsyncOperation = {0x3D8B0590, 0xF691, 0x11d2, {0x8E, 0xA9, 0x00, 0x60, 0x97, 0xDF, 0x5B, 0xD4}};
97 #endif
99 #if 0
100 #define PRNTDEBUG(_x) printf(_x);
101 #define PRNTDEBUG2(_x1, _x2) printf(_x1, _x2);
102 #define PRNTDEBUG3(_x1, _x2, _x3) printf(_x1, _x2, _x3);
103 #else
104 #define PRNTDEBUG(_x) // printf(_x);
105 #define PRNTDEBUG2(_x1, _x2) // printf(_x1, _x2);
106 #define PRNTDEBUG3(_x1, _x2, _x3) // printf(_x1, _x2, _x3);
107 #endif
109 //-----------------------------------------------------------------------------
110 // CStream implementation
111 nsDataObj::CStream::CStream() : mRefCount(1)
115 //-----------------------------------------------------------------------------
116 nsDataObj::CStream::~CStream()
120 //-----------------------------------------------------------------------------
121 // helper - initializes the stream
122 nsresult nsDataObj::CStream::Init(nsIURI *pSourceURI)
124 nsresult rv;
125 rv = NS_NewChannel(getter_AddRefs(mChannel), pSourceURI);
126 NS_ENSURE_SUCCESS(rv, rv);
128 rv = mChannel->Open(getter_AddRefs(mInputStream));
129 NS_ENSURE_SUCCESS(rv, rv);
131 return NS_OK;
134 //-----------------------------------------------------------------------------
135 // IUnknown
136 STDMETHODIMP nsDataObj::CStream::QueryInterface(REFIID refiid, void** ppvResult)
138 *ppvResult = NULL;
139 if (IID_IUnknown == refiid ||
140 refiid == IID_IStream)
143 *ppvResult = this;
146 if (NULL != *ppvResult)
148 ((LPUNKNOWN)*ppvResult)->AddRef();
149 return S_OK;
152 return ResultFromScode(E_NOINTERFACE);
155 //-----------------------------------------------------------------------------
156 STDMETHODIMP_(ULONG) nsDataObj::CStream::AddRef(void)
158 return ++mRefCount;
161 //-----------------------------------------------------------------------------
162 STDMETHODIMP_(ULONG) nsDataObj::CStream::Release(void)
164 ULONG nCount = --mRefCount;
165 if (nCount == 0)
167 delete this;
168 return (ULONG)0;
171 return mRefCount;
174 //-----------------------------------------------------------------------------
175 // IStream
176 STDMETHODIMP nsDataObj::CStream::Clone(IStream** ppStream)
178 return E_NOTIMPL;
181 //-----------------------------------------------------------------------------
182 STDMETHODIMP nsDataObj::CStream::Commit(DWORD dwFrags)
184 return E_NOTIMPL;
187 //-----------------------------------------------------------------------------
188 STDMETHODIMP nsDataObj::CStream::CopyTo(IStream* pDestStream,
189 ULARGE_INTEGER nBytesToCopy,
190 ULARGE_INTEGER* nBytesRead,
191 ULARGE_INTEGER* nBytesWritten)
193 return E_NOTIMPL;
196 //-----------------------------------------------------------------------------
197 STDMETHODIMP nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart,
198 ULARGE_INTEGER nBytes,
199 DWORD dwFlags)
201 return E_NOTIMPL;
204 //-----------------------------------------------------------------------------
205 STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer,
206 ULONG nBytesToRead,
207 ULONG* nBytesRead)
209 NS_ENSURE_TRUE(mInputStream, E_FAIL);
211 nsresult rv;
212 PRUint32 read;
213 *nBytesRead = 0;
215 do {
216 read = 0;
217 rv = mInputStream->Read((char*)pvBuffer + *nBytesRead, nBytesToRead - *nBytesRead, &read);
218 NS_ENSURE_SUCCESS(rv, S_FALSE);
220 *nBytesRead += read;
221 } while ((*nBytesRead < nBytesToRead) && read);
223 return S_OK;
226 //-----------------------------------------------------------------------------
227 STDMETHODIMP nsDataObj::CStream::Revert(void)
229 return E_NOTIMPL;
232 //-----------------------------------------------------------------------------
233 STDMETHODIMP nsDataObj::CStream::Seek(LARGE_INTEGER nMove,
234 DWORD dwOrigin,
235 ULARGE_INTEGER* nNewPos)
237 if (nNewPos == NULL)
238 return STG_E_INVALIDPOINTER;
240 if (nMove.LowPart == 0 && nMove.HighPart == 0 &&
241 (dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR)) {
242 nNewPos->LowPart = 0;
243 nNewPos->HighPart = 0;
244 return S_OK;
247 return E_NOTIMPL;
250 //-----------------------------------------------------------------------------
251 STDMETHODIMP nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize)
253 return E_NOTIMPL;
256 //-----------------------------------------------------------------------------
257 STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags)
259 if (statstg == NULL)
260 return STG_E_INVALIDPOINTER;
262 if (!mChannel)
263 return E_FAIL;
265 memset((void*)statstg, 0, sizeof(STATSTG));
267 if (dwFlags != STATFLAG_NONAME)
269 nsCOMPtr<nsIURI> sourceURI;
270 if (NS_FAILED(mChannel->GetURI(getter_AddRefs(sourceURI)))) {
271 return E_FAIL;
274 nsCAutoString strFileName;
275 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
276 sourceURL->GetFileName(strFileName);
278 if (strFileName.IsEmpty())
279 return E_FAIL;
281 NS_UnescapeURL(strFileName);
282 NS_ConvertUTF8toUTF16 wideFileName(strFileName);
284 PRUint32 nMaxNameLength = (wideFileName.Length()*2) + 2;
285 void * retBuf = CoTaskMemAlloc(nMaxNameLength); // freed by caller
286 if (!retBuf)
287 return STG_E_INSUFFICIENTMEMORY;
289 ZeroMemory(retBuf, nMaxNameLength);
290 memcpy(retBuf, wideFileName.get(), wideFileName.Length()*2);
291 statstg->pwcsName = (LPOLESTR)retBuf;
294 SYSTEMTIME st;
296 statstg->type = STGTY_STREAM;
298 GetSystemTime(&st);
299 SystemTimeToFileTime((const SYSTEMTIME*)&st, (LPFILETIME)&statstg->mtime);
300 statstg->ctime = statstg->atime = statstg->mtime;
302 PRInt32 nLength = 0;
303 if (mChannel)
304 mChannel->GetContentLength(&nLength);
306 if (nLength < 0)
307 nLength = 0;
309 statstg->cbSize.LowPart = (DWORD)nLength;
310 statstg->grfMode = STGM_READ;
311 statstg->grfLocksSupported = LOCK_ONLYONCE;
312 statstg->clsid = CLSID_NULL;
314 return S_OK;
317 //-----------------------------------------------------------------------------
318 STDMETHODIMP nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart,
319 ULARGE_INTEGER nBytes,
320 DWORD dwFlags)
322 return E_NOTIMPL;
325 //-----------------------------------------------------------------------------
326 STDMETHODIMP nsDataObj::CStream::Write(const void* pvBuffer,
327 ULONG nBytesToRead,
328 ULONG* nBytesRead)
330 return E_NOTIMPL;
333 //-----------------------------------------------------------------------------
334 HRESULT nsDataObj::CreateStream(IStream **outStream)
336 NS_ENSURE_TRUE(outStream, E_INVALIDARG);
338 nsresult rv = NS_ERROR_FAILURE;
339 nsAutoString wideFileName;
340 nsCOMPtr<nsIURI> sourceURI;
342 rv = GetDownloadDetails(getter_AddRefs(sourceURI),
343 wideFileName);
344 NS_ENSURE_SUCCESS(rv, rv);
346 nsDataObj::CStream *pStream = new nsDataObj::CStream();
347 NS_ENSURE_TRUE(pStream, E_OUTOFMEMORY);
349 rv = pStream->Init(sourceURI);
350 if (NS_FAILED(rv))
352 pStream->Release();
353 return E_FAIL;
355 *outStream = pStream;
357 return S_OK;
360 EXTERN_C GUID CDECL CLSID_nsDataObj =
361 { 0x1bba7640, 0xdf52, 0x11cf, { 0x82, 0x7b, 0, 0xa0, 0x24, 0x3a, 0xe5, 0x05 } };
364 * deliberately not using MAX_PATH. This is because on platforms < XP
365 * a file created with a long filename may be mishandled by the shell
366 * resulting in it not being able to be deleted or moved.
367 * See bug 250392 for more details.
369 #define NS_MAX_FILEDESCRIPTOR 128 + 1
372 * Class nsDataObj
375 //-----------------------------------------------------
376 // construction
377 //-----------------------------------------------------
378 nsDataObj::nsDataObj(nsIURI * uri)
379 : m_cRef(0), mTransferable(nsnull),
380 mIsAsyncMode(FALSE), mIsInOperation(FALSE)
382 m_enumFE = new CEnumFormatEtc(32);
383 m_enumFE->AddRef();
385 if (uri) {
386 // A URI was obtained, so pass this through to the DataObject
387 // so it can create a SourceURL for CF_HTML flavour
388 uri->GetSpec(mSourceURL);
391 //-----------------------------------------------------
392 // destruction
393 //-----------------------------------------------------
394 nsDataObj::~nsDataObj()
396 NS_IF_RELEASE(mTransferable);
398 for (PRInt32 i = 0; i < mDataFlavors.Count(); ++i) {
399 delete reinterpret_cast<nsCString *>(mDataFlavors.ElementAt(i));
402 m_enumFE->Release();
404 // Free arbitrary system formats
405 for (PRUint32 idx = 0; idx < mDataEntryList.Length(); idx++) {
406 CoTaskMemFree(mDataEntryList[idx]->fe.ptd);
407 ReleaseStgMedium(&mDataEntryList[idx]->stgm);
408 CoTaskMemFree(mDataEntryList[idx]);
413 //-----------------------------------------------------
414 // IUnknown interface methods - see inknown.h for documentation
415 //-----------------------------------------------------
416 STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv)
418 *ppv=NULL;
420 if ( (IID_IUnknown == riid) || (IID_IDataObject == riid) ) {
421 *ppv = this;
422 AddRef();
423 return S_OK;
424 } else if (IID_IAsyncOperation == riid) {
425 *ppv = static_cast<IAsyncOperation*>(this);
426 AddRef();
427 return S_OK;
430 return ResultFromScode(E_NOINTERFACE);
433 //-----------------------------------------------------
434 STDMETHODIMP_(ULONG) nsDataObj::AddRef()
436 ++m_cRef;
437 NS_LOG_ADDREF(this, m_cRef, "nsDataObj", sizeof(*this));
438 //PRNTDEBUG3("nsDataObj::AddRef >>>>>>>>>>>>>>>>>> %d on %p\n", (m_cRef+1), this);
439 return m_cRef;
443 //-----------------------------------------------------
444 STDMETHODIMP_(ULONG) nsDataObj::Release()
446 //PRNTDEBUG3("nsDataObj::Release >>>>>>>>>>>>>>>>>> %d on %p\n", (m_cRef-1), this);
448 --m_cRef;
449 NS_LOG_RELEASE(this, m_cRef, "nsDataObj");
450 if (0 != m_cRef)
451 return m_cRef;
453 delete this;
455 return 0;
458 //-----------------------------------------------------
459 BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) const
461 if ((source.cfFormat == target.cfFormat) &&
462 (source.dwAspect & target.dwAspect) &&
463 (source.tymed & target.tymed)) {
464 return TRUE;
465 } else {
466 return FALSE;
470 //-----------------------------------------------------
471 // IDataObject methods
472 //-----------------------------------------------------
473 STDMETHODIMP nsDataObj::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
475 PRNTDEBUG("nsDataObj::GetData\n");
476 PRNTDEBUG3(" format: %d Text: %d\n", pFE->cfFormat, CF_HDROP);
477 if ( !mTransferable )
478 return ResultFromScode(DATA_E_FORMATETC);
480 PRUint32 dfInx = 0;
482 static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA );
483 static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW );
484 static CLIPFORMAT uniformResourceLocatorA = ::RegisterClipboardFormat( CFSTR_INETURLA );
485 static CLIPFORMAT uniformResourceLocatorW = ::RegisterClipboardFormat( CFSTR_INETURLW );
486 #ifndef WINCE
487 static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS );
488 static CLIPFORMAT PreferredDropEffect = ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT );
489 #endif
491 // Arbitrary system formats
492 LPDATAENTRY pde;
493 HRESULT hres = FindFORMATETC(pFE, &pde, FALSE);
494 if (SUCCEEDED(hres)) {
495 return AddRefStgMedium(&pde->stgm, pSTM, FALSE);
498 // Firefox internal formats
499 ULONG count;
500 FORMATETC fe;
501 m_enumFE->Reset();
502 while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
503 nsCString * df = reinterpret_cast<nsCString*>(mDataFlavors.SafeElementAt(dfInx));
504 if ( df ) {
505 if (FormatsMatch(fe, *pFE)) {
506 pSTM->pUnkForRelease = NULL; // caller is responsible for deleting this data
507 CLIPFORMAT format = pFE->cfFormat;
508 switch(format) {
510 // Someone is asking for plain or unicode text
511 case CF_TEXT:
512 case CF_UNICODETEXT:
513 return GetText(*df, *pFE, *pSTM);
515 // Some 3rd party apps that receive drag and drop files from the browser
516 // window require support for this.
517 case CF_HDROP:
518 return GetFile(*pFE, *pSTM);
520 // Someone is asking for an image
521 case CF_DIB:
522 return GetDib(*df, *pFE, *pSTM);
524 // ... not yet implemented ...
525 //case CF_BITMAP:
526 // return GetBitmap(*pFE, *pSTM);
527 //case CF_METAFILEPICT:
528 // return GetMetafilePict(*pFE, *pSTM);
530 default:
531 if ( format == fileDescriptorFlavorA )
532 return GetFileDescriptor ( *pFE, *pSTM, PR_FALSE );
533 if ( format == fileDescriptorFlavorW )
534 return GetFileDescriptor ( *pFE, *pSTM, PR_TRUE);
535 if ( format == uniformResourceLocatorA )
536 return GetUniformResourceLocator( *pFE, *pSTM, PR_FALSE);
537 if ( format == uniformResourceLocatorW )
538 return GetUniformResourceLocator( *pFE, *pSTM, PR_TRUE);
539 #ifndef WINCE
540 if ( format == fileFlavor )
541 return GetFileContents ( *pFE, *pSTM );
542 if ( format == PreferredDropEffect )
543 return GetPreferredDropEffect( *pFE, *pSTM );
544 #endif
545 PRNTDEBUG2("***** nsDataObj::GetData - Unknown format %u\n", format);
546 return GetText(*df, *pFE, *pSTM);
547 } //switch
548 } // if
550 dfInx++;
551 } // while
553 return ResultFromScode(DATA_E_FORMATETC);
556 //-----------------------------------------------------
557 STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
559 PRNTDEBUG("nsDataObj::GetDataHere\n");
560 return ResultFromScode(E_FAIL);
564 //-----------------------------------------------------
565 // Other objects querying to see if we support a
566 // particular format
567 //-----------------------------------------------------
568 STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
570 PRNTDEBUG("nsDataObj::QueryGetData ");
571 PRNTDEBUG2("format: %d\n", pFE->cfFormat);
573 // Arbitrary system formats
574 LPDATAENTRY pde;
575 if (SUCCEEDED(FindFORMATETC(pFE, &pde, FALSE)))
576 return S_OK;
578 // Firefox internal formats
579 ULONG count;
580 FORMATETC fe;
581 m_enumFE->Reset();
582 while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
583 if (fe.cfFormat == pFE->cfFormat) {
584 return S_OK;
588 PRNTDEBUG2("***** nsDataObj::QueryGetData - Unknown format %d\n", pFE->cfFormat);
589 return ResultFromScode(E_FAIL);
592 //-----------------------------------------------------
593 STDMETHODIMP nsDataObj::GetCanonicalFormatEtc
594 (LPFORMATETC pFEIn, LPFORMATETC pFEOut)
596 PRNTDEBUG("nsDataObj::GetCanonicalFormatEtc\n");
597 return ResultFromScode(E_FAIL);
600 HGLOBAL nsDataObj::GlobalClone(HGLOBAL hglobIn)
602 HGLOBAL hglobOut = NULL;
604 LPVOID pvIn = GlobalLock(hglobIn);
605 if (pvIn) {
606 SIZE_T cb = GlobalSize(hglobIn);
607 HGLOBAL hglobOut = GlobalAlloc(GMEM_FIXED, cb);
608 if (hglobOut) {
609 CopyMemory(hglobOut, pvIn, cb);
611 GlobalUnlock(hglobIn);
613 return hglobOut;
616 IUnknown* nsDataObj::GetCanonicalIUnknown(IUnknown *punk)
618 IUnknown *punkCanonical;
619 if (punk && SUCCEEDED(punk->QueryInterface(IID_IUnknown,
620 (LPVOID*)&punkCanonical))) {
621 punkCanonical->Release();
622 } else {
623 punkCanonical = punk;
625 return punkCanonical;
628 //-----------------------------------------------------
629 STDMETHODIMP nsDataObj::SetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM, BOOL fRelease)
631 PRNTDEBUG("nsDataObj::SetData\n");
632 #ifndef WINCE
633 static CLIPFORMAT PerformedDropEffect = ::RegisterClipboardFormat( CFSTR_PERFORMEDDROPEFFECT );
635 if (pFE && pFE->cfFormat == PerformedDropEffect) {
636 // The drop operation has completed. Delete the temp file if it exists.
637 if (mCachedTempFile) {
638 mCachedTempFile->Remove(PR_FALSE);
639 mCachedTempFile = NULL;
642 #endif
644 // Store arbitrary system formats
645 LPDATAENTRY pde;
646 HRESULT hres = FindFORMATETC(pFE, &pde, TRUE); // add
647 if (SUCCEEDED(hres)) {
648 if (pde->stgm.tymed) {
649 ReleaseStgMedium(&pde->stgm);
650 ZeroMemory(&pde->stgm, sizeof(STGMEDIUM));
653 if (fRelease) {
654 pde->stgm = *pSTM;
655 hres = S_OK;
656 } else {
657 hres = AddRefStgMedium(pSTM, &pde->stgm, TRUE);
659 pde->fe.tymed = pde->stgm.tymed;
661 // Break circular reference loop (see msdn)
662 if (GetCanonicalIUnknown(pde->stgm.pUnkForRelease) ==
663 GetCanonicalIUnknown(static_cast<IDataObject*>(this))) {
664 pde->stgm.pUnkForRelease->Release();
665 pde->stgm.pUnkForRelease = NULL;
667 return hres;
670 if (fRelease)
671 ReleaseStgMedium(pSTM);
673 return ResultFromScode(S_OK);
676 HRESULT
677 nsDataObj::FindFORMATETC(FORMATETC *pfe, LPDATAENTRY *ppde, BOOL fAdd)
679 *ppde = NULL;
681 if (pfe->ptd != NULL) return DV_E_DVTARGETDEVICE;
683 // See if it's in our list
684 for (PRUint32 idx = 0; idx < mDataEntryList.Length(); idx++) {
685 if (mDataEntryList[idx]->fe.cfFormat == pfe->cfFormat &&
686 mDataEntryList[idx]->fe.dwAspect == pfe->dwAspect &&
687 mDataEntryList[idx]->fe.lindex == pfe->lindex) {
688 if (fAdd || (mDataEntryList[idx]->fe.tymed & pfe->tymed)) {
689 *ppde = mDataEntryList[idx];
690 return S_OK;
691 } else {
692 return DV_E_TYMED;
697 if (!fAdd)
698 return DV_E_FORMATETC;
700 LPDATAENTRY pde = (LPDATAENTRY)CoTaskMemAlloc(sizeof(DATAENTRY));
701 if (pde) {
702 pde->fe = *pfe;
703 *ppde = pde;
704 ZeroMemory(&pde->stgm, sizeof(STGMEDIUM));
706 m_enumFE->AddFE(pfe);
707 mDataEntryList.AppendElement(pde);
709 return S_OK;
710 } else {
711 return E_OUTOFMEMORY;
715 HRESULT
716 nsDataObj::AddRefStgMedium(STGMEDIUM *pstgmIn, STGMEDIUM *pstgmOut, BOOL fCopyIn)
718 HRESULT hres = S_OK;
719 STGMEDIUM stgmOut = *pstgmIn;
721 if (pstgmIn->pUnkForRelease == NULL &&
722 !(pstgmIn->tymed & (TYMED_ISTREAM | TYMED_ISTORAGE))) {
723 if (fCopyIn) {
724 // Object needs to be cloned
725 if (pstgmIn->tymed == TYMED_HGLOBAL) {
726 stgmOut.hGlobal = GlobalClone(pstgmIn->hGlobal);
727 if (!stgmOut.hGlobal) {
728 hres = E_OUTOFMEMORY;
730 } else {
731 hres = DV_E_TYMED;
733 } else {
734 stgmOut.pUnkForRelease = static_cast<IDataObject*>(this);
738 if (SUCCEEDED(hres)) {
739 switch (stgmOut.tymed) {
740 case TYMED_ISTREAM:
741 stgmOut.pstm->AddRef();
742 break;
743 case TYMED_ISTORAGE:
744 stgmOut.pstg->AddRef();
745 break;
747 if (stgmOut.pUnkForRelease) {
748 stgmOut.pUnkForRelease->AddRef();
750 *pstgmOut = stgmOut;
753 return hres;
756 //-----------------------------------------------------
757 STDMETHODIMP nsDataObj::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
759 PRNTDEBUG("nsDataObj::EnumFormatEtc\n");
761 switch (dwDir) {
762 case DATADIR_GET:
763 m_enumFE->Clone(ppEnum);
764 break;
765 case DATADIR_SET:
766 // fall through
767 default:
768 *ppEnum = NULL;
769 } // switch
771 if (NULL == *ppEnum)
772 return ResultFromScode(E_FAIL);
774 // Clone already AddRefed the result so don't addref it again.
775 return NOERROR;
778 //-----------------------------------------------------
779 STDMETHODIMP nsDataObj::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
780 LPADVISESINK pIAdviseSink, DWORD* pdwConn)
782 PRNTDEBUG("nsDataObj::DAdvise\n");
783 return ResultFromScode(E_FAIL);
787 //-----------------------------------------------------
788 STDMETHODIMP nsDataObj::DUnadvise(DWORD dwConn)
790 PRNTDEBUG("nsDataObj::DUnadvise\n");
791 return ResultFromScode(E_FAIL);
794 //-----------------------------------------------------
795 STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum)
797 PRNTDEBUG("nsDataObj::EnumDAdvise\n");
798 return ResultFromScode(E_FAIL);
801 // IAsyncOperation methods
802 STDMETHODIMP nsDataObj::EndOperation(HRESULT hResult,
803 IBindCtx *pbcReserved,
804 DWORD dwEffects)
806 mIsInOperation = FALSE;
807 Release();
808 return S_OK;
811 STDMETHODIMP nsDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
813 if (!pfIsOpAsync)
814 return E_FAIL;
816 *pfIsOpAsync = mIsAsyncMode;
818 return S_OK;
821 STDMETHODIMP nsDataObj::InOperation(BOOL *pfInAsyncOp)
823 if (!pfInAsyncOp)
824 return E_FAIL;
826 *pfInAsyncOp = mIsInOperation;
828 return S_OK;
831 STDMETHODIMP nsDataObj::SetAsyncMode(BOOL fDoOpAsync)
833 mIsAsyncMode = fDoOpAsync;
834 return S_OK;
837 STDMETHODIMP nsDataObj::StartOperation(IBindCtx *pbcReserved)
839 mIsInOperation = TRUE;
840 return S_OK;
843 //-----------------------------------------------------
844 // GetData and SetData helper functions
845 //-----------------------------------------------------
846 HRESULT nsDataObj::AddSetFormat(FORMATETC& aFE)
848 PRNTDEBUG("nsDataObj::AddSetFormat\n");
849 return ResultFromScode(S_OK);
852 //-----------------------------------------------------
853 HRESULT nsDataObj::AddGetFormat(FORMATETC& aFE)
855 PRNTDEBUG("nsDataObj::AddGetFormat\n");
856 return ResultFromScode(S_OK);
859 //-----------------------------------------------------
860 HRESULT
861 nsDataObj::GetBitmap ( const nsACString& , FORMATETC&, STGMEDIUM& )
863 PRNTDEBUG("nsDataObj::GetBitmap\n");
864 return ResultFromScode(E_NOTIMPL);
869 // GetDIB
871 // Someone is asking for a bitmap. The data in the transferable will be a straight
872 // nsIImage, so just QI it.
874 HRESULT
875 nsDataObj :: GetDib ( const nsACString& inFlavor, FORMATETC &, STGMEDIUM & aSTG )
877 PRNTDEBUG("nsDataObj::GetDib\n");
878 ULONG result = E_FAIL;
879 #ifndef WINCE
881 PRUint32 len = 0;
882 nsCOMPtr<nsISupports> genericDataWrapper;
883 mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(), getter_AddRefs(genericDataWrapper), &len);
884 nsCOMPtr<nsIImage> image ( do_QueryInterface(genericDataWrapper) );
885 if ( !image ) {
886 // In the 0.9.4 timeframe, I had some embedding clients put the nsIImage directly into the
887 // transferable. Newer code, however, wraps the nsIImage in a nsISupportsInterfacePointer.
888 // We should be backwards compatibile with code already out in the field. If we can't find
889 // the image directly out of the transferable, unwrap the image from its wrapper.
890 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
891 if ( ptr )
892 ptr->GetData(getter_AddRefs(image));
895 if ( image ) {
896 // use a the helper class to build up a bitmap. We now own the bits,
897 // and pass them back to the OS in |aSTG|.
898 nsImageToClipboard converter ( image );
899 HANDLE bits = nsnull;
900 nsresult rv = converter.GetPicture ( &bits );
901 if ( NS_SUCCEEDED(rv) && bits ) {
902 aSTG.hGlobal = bits;
903 aSTG.tymed = TYMED_HGLOBAL;
904 result = S_OK;
906 } // if we have an image
907 else
908 NS_WARNING ( "Definitely not an image on clipboard" );
910 #endif
911 return ResultFromScode(result);
917 // GetFileDescriptor
920 HRESULT
921 nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnicode )
923 HRESULT res = S_OK;
925 // How we handle this depends on if we're dealing with an internet
926 // shortcut, since those are done under the covers.
927 if (IsFlavourPresent(kFilePromiseMime) ||
928 IsFlavourPresent(kFileMime))
930 if (aIsUnicode)
931 return GetFileDescriptor_IStreamW(aFE, aSTG);
932 else
933 return GetFileDescriptor_IStreamA(aFE, aSTG);
935 else if (IsFlavourPresent(kURLMime))
937 if ( aIsUnicode )
938 res = GetFileDescriptorInternetShortcutW ( aFE, aSTG );
939 else
940 res = GetFileDescriptorInternetShortcutA ( aFE, aSTG );
942 else
943 NS_WARNING ( "Not yet implemented\n" );
945 return res;
947 } // GetFileDescriptor
951 HRESULT
952 nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG )
954 HRESULT res = S_OK;
956 // How we handle this depends on if we're dealing with an internet
957 // shortcut, since those are done under the covers.
958 if (IsFlavourPresent(kFilePromiseMime) ||
959 IsFlavourPresent(kFileMime))
960 return GetFileContents_IStream(aFE, aSTG);
961 else if (IsFlavourPresent(kURLMime))
962 return GetFileContentsInternetShortcut ( aFE, aSTG );
963 else
964 NS_WARNING ( "Not yet implemented\n" );
966 return res;
968 } // GetFileContents
971 // Given a unicode string, we ensure that it contains only characters which are valid within
972 // the file system. Remove all forbidden characters from the name, and completely disallow
973 // any title that starts with a forbidden name and extension (e.g. "nul" is invalid, but
974 // "nul." and "nul.txt" are also invalid and will cause problems).
976 // It would seem that this is more functionality suited to being in nsILocalFile.
978 static void
979 MangleTextToValidFilename(nsString & aText)
981 static const char* forbiddenNames[] = {
982 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
983 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
984 "CON", "PRN", "AUX", "NUL", "CLOCK$"
987 aText.StripChars(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS);
988 aText.CompressWhitespace(PR_TRUE, PR_TRUE);
989 PRUint32 nameLen;
990 for (size_t n = 0; n < NS_ARRAY_LENGTH(forbiddenNames); ++n) {
991 nameLen = (PRUint32) strlen(forbiddenNames[n]);
992 if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) {
993 // invalid name is either the entire string, or a prefix with a period
994 if (aText.Length() == nameLen || aText.CharAt(nameLen) == PRUnichar('.')) {
995 aText.Truncate();
996 break;
1003 // Given a unicode string, convert it down to a valid local charset filename
1004 // with the supplied extension. This ensures that we do not cut MBCS characters
1005 // in the middle.
1007 // It would seem that this is more functionality suited to being in nsILocalFile.
1009 static PRBool
1010 CreateFilenameFromTextA(nsString & aText, const char * aExtension,
1011 char * aFilename, PRUint32 aFilenameLen)
1013 // ensure that the supplied name doesn't have invalid characters. If
1014 // a valid mangled filename couldn't be created then it will leave the
1015 // text empty.
1016 MangleTextToValidFilename(aText);
1017 if (aText.IsEmpty())
1018 return PR_FALSE;
1020 // repeatably call WideCharToMultiByte as long as the title doesn't fit in the buffer
1021 // available to us. Continually reduce the length of the source title until the MBCS
1022 // version will fit in the buffer with room for the supplied extension. Doing it this
1023 // way ensures that even in MBCS environments there will be a valid MBCS filename of
1024 // the correct length.
1025 int maxUsableFilenameLen = aFilenameLen - strlen(aExtension) - 1; // space for ext + null byte
1026 int currLen, textLen = (int) NS_MIN(aText.Length(), aFilenameLen);
1027 char defaultChar = '_';
1028 do {
1029 currLen = WideCharToMultiByte(CP_ACP,
1030 WC_COMPOSITECHECK|WC_DEFAULTCHAR,
1031 aText.get(), textLen--, aFilename, maxUsableFilenameLen, &defaultChar, NULL);
1033 while (currLen == 0 && textLen > 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
1034 if (currLen > 0 && textLen > 0) {
1035 strcpy(&aFilename[currLen], aExtension);
1036 return PR_TRUE;
1038 else {
1039 // empty names aren't permitted
1040 return PR_FALSE;
1044 static PRBool
1045 CreateFilenameFromTextW(nsString & aText, const wchar_t * aExtension,
1046 wchar_t * aFilename, PRUint32 aFilenameLen)
1048 // ensure that the supplied name doesn't have invalid characters. If
1049 // a valid mangled filename couldn't be created then it will leave the
1050 // text empty.
1051 MangleTextToValidFilename(aText);
1052 if (aText.IsEmpty())
1053 return PR_FALSE;
1055 const int extensionLen = wcslen(aExtension);
1056 if (aText.Length() + extensionLen + 1 > aFilenameLen)
1057 aText.Truncate(aFilenameLen - extensionLen - 1);
1058 wcscpy(&aFilename[0], aText.get());
1059 wcscpy(&aFilename[aText.Length()], aExtension);
1060 return PR_TRUE;
1063 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
1065 static PRBool
1066 GetLocalizedString(const PRUnichar * aName, nsXPIDLString & aString)
1068 nsresult rv;
1069 nsCOMPtr<nsIStringBundleService> stringService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
1070 if (NS_FAILED(rv))
1071 return PR_FALSE;
1073 nsCOMPtr<nsIStringBundle> stringBundle;
1074 rv = stringService->CreateBundle(PAGEINFO_PROPERTIES, getter_AddRefs(stringBundle));
1075 if (NS_FAILED(rv))
1076 return PR_FALSE;
1078 rv = stringBundle->GetStringFromName(aName, getter_Copies(aString));
1079 return NS_SUCCEEDED(rv);
1083 // GetFileDescriptorInternetShortcut
1085 // Create the special format for an internet shortcut and build up the data
1086 // structures the shell is expecting.
1088 HRESULT
1089 nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aSTG )
1091 #ifdef WINCE
1092 return E_FAIL;
1093 #else
1094 // get the title of the shortcut
1095 nsAutoString title;
1096 if ( NS_FAILED(ExtractShortcutTitle(title)) )
1097 return E_OUTOFMEMORY;
1099 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORA));
1100 if (!fileGroupDescHandle)
1101 return E_OUTOFMEMORY;
1103 LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(::GlobalLock(fileGroupDescHandle));
1104 if (!fileGroupDescA) {
1105 ::GlobalFree(fileGroupDescHandle);
1106 return E_OUTOFMEMORY;
1109 // get a valid filename in the following order: 1) from the page title,
1110 // 2) localized string for an untitled page, 3) just use "Untitled.URL"
1111 if (!CreateFilenameFromTextA(title, ".URL",
1112 fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1113 nsXPIDLString untitled;
1114 if (!GetLocalizedString(NS_LITERAL_STRING("noPageTitle").get(), untitled) ||
1115 !CreateFilenameFromTextA(untitled, ".URL",
1116 fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1117 strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL");
1121 // one file in the file block
1122 fileGroupDescA->cItems = 1;
1123 fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;
1125 ::GlobalUnlock( fileGroupDescHandle );
1126 aSTG.hGlobal = fileGroupDescHandle;
1127 aSTG.tymed = TYMED_HGLOBAL;
1129 return S_OK;
1130 #endif
1131 } // GetFileDescriptorInternetShortcutA
1133 HRESULT
1134 nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG )
1136 #ifdef WINCE
1137 return E_FAIL;
1138 #else
1139 // get the title of the shortcut
1140 nsAutoString title;
1141 if ( NS_FAILED(ExtractShortcutTitle(title)) )
1142 return E_OUTOFMEMORY;
1144 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
1145 if (!fileGroupDescHandle)
1146 return E_OUTOFMEMORY;
1148 LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(::GlobalLock(fileGroupDescHandle));
1149 if (!fileGroupDescW) {
1150 ::GlobalFree(fileGroupDescHandle);
1151 return E_OUTOFMEMORY;
1154 // get a valid filename in the following order: 1) from the page title,
1155 // 2) localized string for an untitled page, 3) just use "Untitled.URL"
1156 if (!CreateFilenameFromTextW(title, NS_LITERAL_STRING(".URL").get(),
1157 fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1158 nsXPIDLString untitled;
1159 if (!GetLocalizedString(NS_LITERAL_STRING("noPageTitle").get(), untitled) ||
1160 !CreateFilenameFromTextW(untitled, NS_LITERAL_STRING(".URL").get(),
1161 fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1162 wcscpy(fileGroupDescW->fgd[0].cFileName, NS_LITERAL_STRING("Untitled.URL").get());
1166 // one file in the file block
1167 fileGroupDescW->cItems = 1;
1168 fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;
1170 ::GlobalUnlock( fileGroupDescHandle );
1171 aSTG.hGlobal = fileGroupDescHandle;
1172 aSTG.tymed = TYMED_HGLOBAL;
1174 return S_OK;
1175 #endif
1176 } // GetFileDescriptorInternetShortcutW
1180 // GetFileContentsInternetShortcut
1182 // Create the special format for an internet shortcut and build up the data
1183 // structures the shell is expecting.
1185 HRESULT
1186 nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
1188 #ifdef WINCE
1189 return E_FAIL;
1190 #else
1191 nsAutoString url;
1192 if ( NS_FAILED(ExtractShortcutURL(url)) )
1193 return E_OUTOFMEMORY;
1195 // will need to change if we ever support iDNS
1196 nsCAutoString asciiUrl;
1197 LossyCopyUTF16toASCII(url, asciiUrl);
1199 static const char* shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
1200 static const int formatLen = strlen(shortcutFormatStr) - 2; // don't include %s in the len
1201 const int totalLen = formatLen + asciiUrl.Length(); // we don't want a null character on the end
1203 // create a global memory area and build up the file contents w/in it
1204 HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen);
1205 if ( !hGlobalMemory )
1206 return E_OUTOFMEMORY;
1208 char* contents = reinterpret_cast<char*>(::GlobalLock(hGlobalMemory));
1209 if ( !contents ) {
1210 ::GlobalFree( hGlobalMemory );
1211 return E_OUTOFMEMORY;
1214 //NOTE: we intentionally use the Microsoft version of snprintf here because it does NOT null
1215 // terminate strings which reach the maximum size of the buffer. Since we know that the
1216 // formatted length here is totalLen, this call to _snprintf will format the string into
1217 // the buffer without appending the null character.
1218 _snprintf( contents, totalLen, shortcutFormatStr, asciiUrl.get() );
1220 ::GlobalUnlock(hGlobalMemory);
1221 aSTG.hGlobal = hGlobalMemory;
1222 aSTG.tymed = TYMED_HGLOBAL;
1224 return S_OK;
1225 #endif
1226 } // GetFileContentsInternetShortcut
1228 // check if specified flavour is present in the transferable
1229 PRBool nsDataObj :: IsFlavourPresent(const char *inFlavour)
1231 PRBool retval = PR_FALSE;
1232 NS_ENSURE_TRUE(mTransferable, PR_FALSE);
1234 // get the list of flavors available in the transferable
1235 nsCOMPtr<nsISupportsArray> flavorList;
1236 mTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
1237 NS_ENSURE_TRUE(flavorList, PR_FALSE);
1239 // try to find requested flavour
1240 PRUint32 cnt;
1241 flavorList->Count(&cnt);
1242 for (PRUint32 i = 0; i < cnt; ++i) {
1243 nsCOMPtr<nsISupports> genericFlavor;
1244 flavorList->GetElementAt (i, getter_AddRefs(genericFlavor));
1245 nsCOMPtr<nsISupportsCString> currentFlavor (do_QueryInterface(genericFlavor));
1246 if (currentFlavor) {
1247 nsCAutoString flavorStr;
1248 currentFlavor->GetData(flavorStr);
1249 if (flavorStr.Equals(inFlavour)) {
1250 retval = PR_TRUE; // found it!
1251 break;
1254 } // for each flavor
1256 return retval;
1259 HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG )
1261 HRESULT res = S_OK;
1262 aSTG.tymed = TYMED_HGLOBAL;
1263 aSTG.pUnkForRelease = NULL;
1264 HGLOBAL hGlobalMemory = NULL;
1265 hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
1266 if (hGlobalMemory) {
1267 DWORD* pdw = (DWORD*) GlobalLock(hGlobalMemory);
1268 // The PreferredDropEffect clipboard format is only registered if a drag/drop
1269 // of an image happens from Mozilla to the desktop. We want its value
1270 // to be DROPEFFECT_MOVE in that case so that the file is moved from the
1271 // temporary location, not copied.
1272 // This value should, ideally, be set on the data object via SetData() but
1273 // our IDataObject implementation doesn't implement SetData. It adds data
1274 // to the data object lazily only when the drop target asks for it.
1275 *pdw = (DWORD) DROPEFFECT_MOVE;
1276 GlobalUnlock(hGlobalMemory);
1278 else {
1279 res = E_OUTOFMEMORY;
1281 aSTG.hGlobal = hGlobalMemory;
1282 return res;
1285 //-----------------------------------------------------
1286 HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
1288 void* data = NULL;
1289 PRUint32 len;
1291 // if someone asks for text/plain, look up text/unicode instead in the transferable.
1292 const char* flavorStr;
1293 const nsPromiseFlatCString& flat = PromiseFlatCString(aDataFlavor);
1294 if ( aDataFlavor.Equals("text/plain") )
1295 flavorStr = kUnicodeMime;
1296 else
1297 flavorStr = flat.get();
1299 // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
1300 nsCOMPtr<nsISupports> genericDataWrapper;
1301 mTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &len);
1302 if ( !len )
1303 return ResultFromScode(E_FAIL);
1304 nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, len );
1305 if ( !data )
1306 return ResultFromScode(E_FAIL);
1308 HGLOBAL hGlobalMemory = NULL;
1310 aSTG.tymed = TYMED_HGLOBAL;
1311 aSTG.pUnkForRelease = NULL;
1313 // We play games under the hood and advertise flavors that we know we
1314 // can support, only they require a bit of conversion or munging of the data.
1315 // Do that here.
1317 // The transferable gives us data that is null-terminated, but this isn't reflected in
1318 // the |len| parameter. Windoze apps expect this null to be there so bump our data buffer
1319 // by the appropriate size to account for the null (one char for CF_TEXT, one PRUnichar for
1320 // CF_UNICODETEXT).
1321 DWORD allocLen = (DWORD)len;
1322 if ( aFE.cfFormat == CF_TEXT ) {
1323 // Someone is asking for text/plain; convert the unicode (assuming it's present)
1324 // to text with the correct platform encoding.
1325 char* plainTextData = nsnull;
1326 PRUnichar* castedUnicode = reinterpret_cast<PRUnichar*>(data);
1327 PRInt32 plainTextLen = 0;
1328 nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText ( castedUnicode, len / 2, &plainTextData, &plainTextLen );
1330 // replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include
1331 // the null in the length.
1332 nsMemory::Free(data);
1333 if ( plainTextData ) {
1334 data = plainTextData;
1335 allocLen = plainTextLen + sizeof(char);
1337 else {
1338 NS_WARNING ( "Oh no, couldn't convert unicode to plain text" );
1339 return ResultFromScode(S_OK);
1342 else if ( aFE.cfFormat == nsClipboard::CF_HTML ) {
1343 // Someone is asking for win32's HTML flavor. Convert our html fragment
1344 // from unicode to UTF-8 then put it into a format specified by msft.
1345 NS_ConvertUTF16toUTF8 converter ( reinterpret_cast<PRUnichar*>(data) );
1346 char* utf8HTML = nsnull;
1347 nsresult rv = BuildPlatformHTML ( converter.get(), &utf8HTML ); // null terminates
1349 nsMemory::Free(data);
1350 if ( NS_SUCCEEDED(rv) && utf8HTML ) {
1351 // replace the unicode data with our HTML data. Don't forget the null.
1352 data = utf8HTML;
1353 allocLen = strlen(utf8HTML) + sizeof(char);
1355 else {
1356 NS_WARNING ( "Oh no, couldn't convert to HTML" );
1357 return ResultFromScode(S_OK);
1360 else {
1361 // we assume that any data that isn't caught above is unicode. This may
1362 // be an erroneous assumption, but is true so far.
1363 allocLen += sizeof(PRUnichar);
1366 hGlobalMemory = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, allocLen);
1368 // Copy text to Global Memory Area
1369 if ( hGlobalMemory ) {
1370 char* dest = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
1371 char* source = reinterpret_cast<char*>(data);
1372 memcpy ( dest, source, allocLen ); // copies the null as well
1373 GlobalUnlock(hGlobalMemory);
1375 aSTG.hGlobal = hGlobalMemory;
1377 // Now, delete the memory that was created by CreateDataFromPrimitive (or our text/plain data)
1378 nsMemory::Free(data);
1380 return ResultFromScode(S_OK);
1383 //-----------------------------------------------------
1384 HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG)
1386 HRESULT res = S_OK;
1388 // We do not support 'application/x-moz-file-promise' since CF_HDROP does not
1389 // allow for delayed rendering of content. We'll need to write the content out emmediately
1390 // and return the path to it. Confirm we have support for 'application/x-moz-nativeimage',
1391 // if not fail.
1392 PRUint32 dfInx = 0;
1393 ULONG count;
1394 FORMATETC fe;
1395 m_enumFE->Reset();
1396 PRBool found = PR_FALSE;
1397 while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
1398 nsCString * df = reinterpret_cast<nsCString*>(mDataFlavors.SafeElementAt(dfInx));
1399 dfInx++;
1400 if (df && df->EqualsLiteral(kNativeImageMime)) {
1401 found = PR_TRUE;
1402 break;
1406 if (!found)
1407 return E_FAIL;
1409 nsresult rv;
1410 PRUint32 len = 0;
1411 nsCOMPtr<nsISupports> genericDataWrapper;
1413 mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
1414 nsCOMPtr<nsIImage> image ( do_QueryInterface(genericDataWrapper) );
1416 if (!image) {
1417 // In the 0.9.4 timeframe, I had some embedding clients put the nsIImage directly into the
1418 // transferable. Newer code, however, wraps the nsIImage in a nsISupportsInterfacePointer.
1419 // We should be backwards compatibile with code already out in the field. If we can't find
1420 // the image directly out of the transferable, unwrap the image from its wrapper.
1421 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
1422 if (ptr)
1423 ptr->GetData(getter_AddRefs(image));
1426 if (!image)
1427 return E_FAIL;
1429 // Use the clipboard helper class to build up a memory bitmap.
1430 nsImageToClipboard converter(image);
1431 HANDLE bits = nsnull;
1432 rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
1434 if (NS_FAILED(rv) || !bits)
1435 return E_FAIL;
1437 // We now own these bits!
1438 PRUint32 bitmapSize = GlobalSize(bits);
1439 if (!bitmapSize) {
1440 GlobalFree(bits);
1441 return E_FAIL;
1444 if (mCachedTempFile) {
1445 mCachedTempFile->Remove(PR_FALSE);
1446 mCachedTempFile = NULL;
1449 // Save the bitmap to a temporary location.
1450 nsCOMPtr<nsIFile> dropFile;
1451 rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
1452 if (!dropFile)
1453 return E_FAIL;
1455 // Filename must be random so as not to confuse apps like Photshop which handle
1456 // multiple drags into a single window.
1457 char buf[13];
1458 nsCString filename;
1459 MakeRandomString(buf, 8);
1460 memcpy(buf+8, ".bmp", 5);
1461 filename.Append(nsDependentCString(buf, 12));
1462 dropFile->AppendNative(filename);
1463 rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
1464 if (NS_FAILED(rv)) {
1465 GlobalFree(bits);
1466 return E_FAIL;
1469 // Cache the temp file so we can delete it later.
1470 dropFile->Clone(getter_AddRefs(mCachedTempFile));
1472 // Write the data to disk.
1473 nsCOMPtr<nsIOutputStream> outStream;
1474 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
1475 if (NS_FAILED(rv)) {
1476 GlobalFree(bits);
1477 return E_FAIL;
1480 char * bm = (char *)GlobalLock(bits);
1482 BITMAPFILEHEADER fileHdr;
1483 BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
1485 fileHdr.bfType = ((WORD) ('M' << 8) | 'B');
1486 fileHdr.bfSize = GlobalSize (bits) + sizeof(fileHdr);
1487 fileHdr.bfReserved1 = 0;
1488 fileHdr.bfReserved2 = 0;
1489 fileHdr.bfOffBits = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
1491 PRUint32 writeCount = 0;
1492 if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
1493 NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
1494 rv = NS_ERROR_FAILURE;
1496 outStream->Close();
1498 GlobalUnlock(bits);
1500 if (NS_FAILED(rv)) {
1501 GlobalFree(bits);
1502 return E_FAIL;
1505 GlobalFree(bits);
1507 // Pass the file name back to the drop target so that it can access the file.
1508 nsAutoString path;
1509 rv = mCachedTempFile->GetPath(path);
1510 if (NS_FAILED(rv))
1511 return E_FAIL;
1513 // Two null characters are needed to terminate the file name list.
1514 HGLOBAL hGlobalMemory = NULL;
1516 PRUint32 allocLen = path.Length() + 2;
1518 aSTG.tymed = TYMED_HGLOBAL;
1519 aSTG.pUnkForRelease = NULL;
1521 hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(PRUnichar));
1522 if (!hGlobalMemory)
1523 return E_FAIL;
1525 DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
1527 // First, populate the drop file structure.
1528 pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
1529 pDropFile->fNC = 0;
1530 pDropFile->pt.x = 0;
1531 pDropFile->pt.y = 0;
1532 pDropFile->fWide = TRUE;
1534 // Copy the filename right after the DROPFILES structure.
1535 PRUnichar* dest = (PRUnichar*)(((char*)pDropFile) + pDropFile->pFiles);
1536 memcpy(dest, path.get(), (allocLen - 1) * sizeof(PRUnichar)); // Copies the null character in path as well.
1538 // Two null characters are needed at the end of the file name.
1539 // Lookup the CF_HDROP shell clipboard format for more info.
1540 // Add the second null character right after the first one.
1541 dest[allocLen - 1] = L'\0';
1543 GlobalUnlock(hGlobalMemory);
1545 aSTG.hGlobal = hGlobalMemory;
1547 return S_OK;
1550 //-----------------------------------------------------
1551 HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
1553 return ResultFromScode(E_NOTIMPL);
1556 //-----------------------------------------------------
1557 HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&)
1559 return ResultFromScode(E_NOTIMPL);
1562 //-----------------------------------------------------
1563 HRESULT nsDataObj::SetDib(FORMATETC&, STGMEDIUM&)
1565 return ResultFromScode(E_FAIL);
1568 //-----------------------------------------------------
1569 HRESULT nsDataObj::SetText (FORMATETC& aFE, STGMEDIUM& aSTG)
1571 if (aFE.cfFormat == CF_TEXT && aFE.tymed == TYMED_HGLOBAL) {
1572 HGLOBAL hString = (HGLOBAL)aSTG.hGlobal;
1573 if(hString == NULL)
1574 return(FALSE);
1576 // get a pointer to the actual bytes
1577 char * pString = (char *) GlobalLock(hString);
1578 if(!pString)
1579 return(FALSE);
1581 GlobalUnlock(hString);
1582 nsAutoString str; str.AssignWithConversion(pString);
1585 return ResultFromScode(E_FAIL);
1588 //-----------------------------------------------------
1589 HRESULT nsDataObj::SetMetafilePict(FORMATETC&, STGMEDIUM&)
1591 return ResultFromScode(E_FAIL);
1596 //-----------------------------------------------------
1597 //-----------------------------------------------------
1598 CLSID nsDataObj::GetClassID() const
1600 return CLSID_nsDataObj;
1603 //-----------------------------------------------------
1604 // Registers the DataFlavor/FE pair.
1605 //-----------------------------------------------------
1606 void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
1608 // These two lists are the mapping to and from data flavors and FEs.
1609 // Later, OLE will tell us it needs a certain type of FORMATETC (text, unicode, etc)
1610 // unicode, etc), so we will look up the data flavor that corresponds to
1611 // the FE and then ask the transferable for that type of data.
1612 mDataFlavors.AppendElement(new nsCString(aDataFlavor));
1613 m_enumFE->AddFE(aFE);
1616 //-----------------------------------------------------
1617 // Sets the transferable object
1618 //-----------------------------------------------------
1619 void nsDataObj::SetTransferable(nsITransferable * aTransferable)
1621 NS_IF_RELEASE(mTransferable);
1623 mTransferable = aTransferable;
1624 if (nsnull == mTransferable) {
1625 return;
1628 NS_ADDREF(mTransferable);
1630 return;
1635 // ExtractURL
1637 // Roots around in the transferable for the appropriate flavor that indicates
1638 // a url and pulls out the url portion of the data. Used mostly for creating
1639 // internet shortcuts on the desktop. The url flavor is of the format:
1641 // <url> <linefeed> <page title>
1643 nsresult
1644 nsDataObj :: ExtractShortcutURL ( nsString & outURL )
1646 NS_ASSERTION ( mTransferable, "We don't have a good transferable" );
1647 nsresult rv = NS_ERROR_FAILURE;
1649 PRUint32 len = 0;
1650 nsCOMPtr<nsISupports> genericURL;
1651 if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
1652 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1653 if ( urlObject ) {
1654 nsAutoString url;
1655 urlObject->GetData ( url );
1656 outURL = url;
1658 // find the first linefeed in the data, that's where the url ends. trunc the
1659 // result string at that point.
1660 PRInt32 lineIndex = outURL.FindChar ( '\n' );
1661 NS_ASSERTION ( lineIndex > 0, "Format for url flavor is <url> <linefeed> <page title>" );
1662 if ( lineIndex > 0 ) {
1663 outURL.Truncate ( lineIndex );
1664 rv = NS_OK;
1667 } else if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLDataMime, getter_AddRefs(genericURL), &len)) ||
1668 NS_SUCCEEDED(mTransferable->GetTransferData(kURLPrivateMime, getter_AddRefs(genericURL), &len)) ) {
1669 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1670 if ( urlObject ) {
1671 nsAutoString url;
1672 urlObject->GetData ( url );
1673 outURL = url;
1675 rv = NS_OK;
1678 } // if found flavor
1680 return rv;
1682 } // ExtractShortcutURL
1686 // ExtractShortcutTitle
1688 // Roots around in the transferable for the appropriate flavor that indicates
1689 // a url and pulls out the title portion of the data. Used mostly for creating
1690 // internet shortcuts on the desktop. The url flavor is of the format:
1692 // <url> <linefeed> <page title>
1694 nsresult
1695 nsDataObj :: ExtractShortcutTitle ( nsString & outTitle )
1697 NS_ASSERTION ( mTransferable, "We'd don't have a good transferable" );
1698 nsresult rv = NS_ERROR_FAILURE;
1700 PRUint32 len = 0;
1701 nsCOMPtr<nsISupports> genericURL;
1702 if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
1703 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1704 if ( urlObject ) {
1705 nsAutoString url;
1706 urlObject->GetData ( url );
1708 // find the first linefeed in the data, that's where the url ends. we want
1709 // everything after that linefeed. FindChar() returns -1 if we can't find
1710 PRInt32 lineIndex = url.FindChar ( '\n' );
1711 NS_ASSERTION ( lineIndex != -1, "Format for url flavor is <url> <linefeed> <page title>" );
1712 if ( lineIndex != -1 ) {
1713 url.Mid ( outTitle, lineIndex + 1, (len/2) - (lineIndex + 1) );
1714 rv = NS_OK;
1717 } // if found flavor
1719 return rv;
1721 } // ExtractShortcutTitle
1725 // BuildPlatformHTML
1727 // Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
1728 // header information on it. This will null terminate |outPlatformHTML|. See
1729 // http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
1730 // for details.
1732 // We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
1733 // or <BODY> tags). We'll wrap the fragment with them to make other apps
1734 // happy.
1736 nsresult
1737 nsDataObj :: BuildPlatformHTML ( const char* inOurHTML, char** outPlatformHTML )
1739 *outPlatformHTML = nsnull;
1741 nsDependentCString inHTMLString(inOurHTML);
1742 const char* const numPlaceholder = "00000000";
1743 const char* const startHTMLPrefix = "Version:0.9\r\nStartHTML:";
1744 const char* const endHTMLPrefix = "\r\nEndHTML:";
1745 const char* const startFragPrefix = "\r\nStartFragment:";
1746 const char* const endFragPrefix = "\r\nEndFragment:";
1747 const char* const startSourceURLPrefix = "\r\nSourceURL:";
1748 const char* const endFragTrailer = "\r\n";
1750 // Do we already have mSourceURL from a drag?
1751 if (mSourceURL.IsEmpty()) {
1752 nsAutoString url;
1753 ExtractShortcutURL(url);
1755 AppendUTF16toUTF8(url, mSourceURL);
1758 const PRInt32 kSourceURLLength = mSourceURL.Length();
1759 const PRInt32 kNumberLength = strlen(numPlaceholder);
1761 const PRInt32 kTotalHeaderLen = strlen(startHTMLPrefix) +
1762 strlen(endHTMLPrefix) +
1763 strlen(startFragPrefix) +
1764 strlen(endFragPrefix) +
1765 strlen(endFragTrailer) +
1766 (kSourceURLLength > 0 ? strlen(startSourceURLPrefix) : 0) +
1767 kSourceURLLength +
1768 (4 * kNumberLength);
1770 NS_NAMED_LITERAL_CSTRING(htmlHeaderString, "<html><body>\r\n");
1772 NS_NAMED_LITERAL_CSTRING(fragmentHeaderString, "<!--StartFragment-->");
1774 nsDependentCString trailingString(
1775 "<!--EndFragment-->\r\n"
1776 "</body>\r\n"
1777 "</html>");
1779 // calculate the offsets
1780 PRInt32 startHTMLOffset = kTotalHeaderLen;
1781 PRInt32 startFragOffset = startHTMLOffset
1782 + htmlHeaderString.Length()
1783 + fragmentHeaderString.Length();
1785 PRInt32 endFragOffset = startFragOffset
1786 + inHTMLString.Length();
1788 PRInt32 endHTMLOffset = endFragOffset
1789 + trailingString.Length();
1791 // now build the final version
1792 nsCString clipboardString;
1793 clipboardString.SetCapacity(endHTMLOffset);
1795 clipboardString.Append(startHTMLPrefix);
1796 clipboardString.Append(nsPrintfCString("%08u", startHTMLOffset));
1798 clipboardString.Append(endHTMLPrefix);
1799 clipboardString.Append(nsPrintfCString("%08u", endHTMLOffset));
1801 clipboardString.Append(startFragPrefix);
1802 clipboardString.Append(nsPrintfCString("%08u", startFragOffset));
1804 clipboardString.Append(endFragPrefix);
1805 clipboardString.Append(nsPrintfCString("%08u", endFragOffset));
1807 if (kSourceURLLength > 0) {
1808 clipboardString.Append(startSourceURLPrefix);
1809 clipboardString.Append(mSourceURL);
1812 clipboardString.Append(endFragTrailer);
1814 clipboardString.Append(htmlHeaderString);
1815 clipboardString.Append(fragmentHeaderString);
1816 clipboardString.Append(inHTMLString);
1817 clipboardString.Append(trailingString);
1819 *outPlatformHTML = ToNewCString(clipboardString);
1820 if (!*outPlatformHTML)
1821 return NS_ERROR_OUT_OF_MEMORY;
1823 return NS_OK;
1826 HRESULT
1827 nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, PRBool aIsUnicode )
1829 HRESULT res = S_OK;
1830 if (IsFlavourPresent(kURLMime)) {
1831 if ( aIsUnicode )
1832 res = ExtractUniformResourceLocatorW( aFE, aSTG );
1833 else
1834 res = ExtractUniformResourceLocatorA( aFE, aSTG );
1836 else
1837 NS_WARNING ("Not yet implemented\n");
1838 return res;
1841 HRESULT
1842 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG )
1844 HRESULT result = S_OK;
1846 nsAutoString url;
1847 if (NS_FAILED(ExtractShortcutURL(url)))
1848 return E_OUTOFMEMORY;
1850 NS_LossyConvertUTF16toASCII asciiUrl(url);
1851 const int totalLen = asciiUrl.Length() + 1;
1852 HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
1853 if (!hGlobalMemory)
1854 return E_OUTOFMEMORY;
1856 char* contents = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
1857 if (!contents) {
1858 GlobalFree(hGlobalMemory);
1859 return E_OUTOFMEMORY;
1862 strcpy(contents, asciiUrl.get());
1863 GlobalUnlock(hGlobalMemory);
1864 aSTG.hGlobal = hGlobalMemory;
1865 aSTG.tymed = TYMED_HGLOBAL;
1867 return result;
1870 HRESULT
1871 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG )
1873 HRESULT result = S_OK;
1875 nsAutoString url;
1876 if (NS_FAILED(ExtractShortcutURL(url)))
1877 return E_OUTOFMEMORY;
1879 const int totalLen = (url.Length() + 1) * sizeof(PRUnichar);
1880 HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
1881 if (!hGlobalMemory)
1882 return E_OUTOFMEMORY;
1884 wchar_t* contents = reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory));
1885 if (!contents) {
1886 GlobalFree(hGlobalMemory);
1887 return E_OUTOFMEMORY;
1890 wcscpy(contents, url.get());
1891 GlobalUnlock(hGlobalMemory);
1892 aSTG.hGlobal = hGlobalMemory;
1893 aSTG.tymed = TYMED_HGLOBAL;
1895 return result;
1899 // Gets the filename from the kFilePromiseURLMime flavour
1900 nsresult nsDataObj::GetDownloadDetails(nsIURI **aSourceURI,
1901 nsAString &aFilename)
1903 *aSourceURI = nsnull;
1905 NS_ENSURE_TRUE(mTransferable, NS_ERROR_FAILURE);
1907 // get the URI from the kFilePromiseURLMime flavor
1908 nsCOMPtr<nsISupports> urlPrimitive;
1909 PRUint32 dataSize = 0;
1910 mTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize);
1911 nsCOMPtr<nsISupportsString> srcUrlPrimitive = do_QueryInterface(urlPrimitive);
1912 NS_ENSURE_TRUE(srcUrlPrimitive, NS_ERROR_FAILURE);
1914 nsAutoString srcUri;
1915 srcUrlPrimitive->GetData(srcUri);
1916 if (srcUri.IsEmpty())
1917 return NS_ERROR_FAILURE;
1918 nsCOMPtr<nsIURI> sourceURI;
1919 NS_NewURI(getter_AddRefs(sourceURI), srcUri);
1921 nsAutoString srcFileName;
1922 nsCOMPtr<nsISupports> fileNamePrimitive;
1923 mTransferable->GetTransferData(kFilePromiseDestFilename, getter_AddRefs(fileNamePrimitive), &dataSize);
1924 nsCOMPtr<nsISupportsString> srcFileNamePrimitive = do_QueryInterface(fileNamePrimitive);
1925 if (srcFileNamePrimitive) {
1926 srcFileNamePrimitive->GetData(srcFileName);
1927 } else {
1928 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
1929 if (!sourceURL)
1930 return NS_ERROR_FAILURE;
1932 nsCAutoString urlFileName;
1933 sourceURL->GetFileName(urlFileName);
1934 NS_UnescapeURL(urlFileName);
1935 CopyUTF8toUTF16(urlFileName, srcFileName);
1937 if (srcFileName.IsEmpty())
1938 return NS_ERROR_FAILURE;
1940 // make the name safe for the filesystem
1941 MangleTextToValidFilename(srcFileName);
1943 sourceURI.swap(*aSourceURI);
1944 aFilename = srcFileName;
1945 return NS_OK;
1948 HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG)
1950 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
1951 NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
1953 LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(GlobalLock(fileGroupDescHandle));
1954 if (!fileGroupDescA) {
1955 ::GlobalFree(fileGroupDescHandle);
1956 return E_OUTOFMEMORY;
1959 nsAutoString wideFileName;
1960 nsresult rv;
1961 nsCOMPtr<nsIURI> sourceURI;
1962 rv = GetDownloadDetails(getter_AddRefs(sourceURI),
1963 wideFileName);
1964 if (NS_FAILED(rv))
1966 ::GlobalFree(fileGroupDescHandle);
1967 return E_FAIL;
1970 nsCAutoString nativeFileName;
1971 NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, nativeFileName);
1973 strncpy(fileGroupDescA->fgd[0].cFileName, nativeFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
1974 fileGroupDescA->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
1976 // one file in the file block
1977 fileGroupDescA->cItems = 1;
1978 fileGroupDescA->fgd[0].dwFlags = FD_PROGRESSUI;
1980 GlobalUnlock( fileGroupDescHandle );
1981 aSTG.hGlobal = fileGroupDescHandle;
1982 aSTG.tymed = TYMED_HGLOBAL;
1984 return S_OK;
1987 HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG)
1989 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
1990 NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
1992 LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(GlobalLock(fileGroupDescHandle));
1993 if (!fileGroupDescW) {
1994 ::GlobalFree(fileGroupDescHandle);
1995 return E_OUTOFMEMORY;
1998 nsAutoString wideFileName;
1999 nsresult rv;
2000 nsCOMPtr<nsIURI> sourceURI;
2001 rv = GetDownloadDetails(getter_AddRefs(sourceURI),
2002 wideFileName);
2003 if (NS_FAILED(rv))
2005 ::GlobalFree(fileGroupDescHandle);
2006 return E_FAIL;
2009 wcsncpy(fileGroupDescW->fgd[0].cFileName, wideFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
2010 fileGroupDescW->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
2011 // one file in the file block
2012 fileGroupDescW->cItems = 1;
2013 fileGroupDescW->fgd[0].dwFlags = FD_PROGRESSUI;
2015 GlobalUnlock(fileGroupDescHandle);
2016 aSTG.hGlobal = fileGroupDescHandle;
2017 aSTG.tymed = TYMED_HGLOBAL;
2019 return S_OK;
2022 HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
2024 IStream *pStream = NULL;
2026 nsDataObj::CreateStream(&pStream);
2027 NS_ENSURE_TRUE(pStream, E_FAIL);
2029 aSTG.tymed = TYMED_ISTREAM;
2030 aSTG.pstm = pStream;
2031 aSTG.pUnkForRelease = pStream;
2033 return S_OK;