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
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.
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 ***** */
46 #include "nsDataObj.h"
47 #include "nsClipboard.h"
48 #include "nsReadableUtils.h"
49 #include "nsITransferable.h"
50 #include "nsISupportsPrimitives.h"
52 #include "nsPrimitiveHelpers.h"
53 #include "nsXPIDLString.h"
55 #include "nsImageClipboard.h"
57 #include "nsPrintfCString.h"
58 #include "nsIStringBundle.h"
61 #include "nsNetUtil.h"
62 #include "nsXPCOMStrings.h"
65 #include "nsDirectoryServiceDefs.h"
67 // XXX Duped from profile/src/nsProfile.cpp.
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' };
76 MakeRandomString(char *buf
, PRInt32 bufLen
)
78 // turn PR_Now() into milliseconds since epoch
79 // and salt rand with that.
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
85 for (i
=0;i
<bufLen
;i
++) {
86 *buf
++ = table
[rand()%TABLE_SIZE
];
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}};
100 #define PRNTDEBUG(_x) printf(_x);
101 #define PRNTDEBUG2(_x1, _x2) printf(_x1, _x2);
102 #define PRNTDEBUG3(_x1, _x2, _x3) printf(_x1, _x2, _x3);
104 #define PRNTDEBUG(_x) // printf(_x);
105 #define PRNTDEBUG2(_x1, _x2) // printf(_x1, _x2);
106 #define PRNTDEBUG3(_x1, _x2, _x3) // printf(_x1, _x2, _x3);
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
)
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
);
134 //-----------------------------------------------------------------------------
136 STDMETHODIMP
nsDataObj::CStream::QueryInterface(REFIID refiid
, void** ppvResult
)
139 if (IID_IUnknown
== refiid
||
140 refiid
== IID_IStream
)
146 if (NULL
!= *ppvResult
)
148 ((LPUNKNOWN
)*ppvResult
)->AddRef();
152 return ResultFromScode(E_NOINTERFACE
);
155 //-----------------------------------------------------------------------------
156 STDMETHODIMP_(ULONG
) nsDataObj::CStream::AddRef(void)
161 //-----------------------------------------------------------------------------
162 STDMETHODIMP_(ULONG
) nsDataObj::CStream::Release(void)
164 ULONG nCount
= --mRefCount
;
174 //-----------------------------------------------------------------------------
176 STDMETHODIMP
nsDataObj::CStream::Clone(IStream
** ppStream
)
181 //-----------------------------------------------------------------------------
182 STDMETHODIMP
nsDataObj::CStream::Commit(DWORD dwFrags
)
187 //-----------------------------------------------------------------------------
188 STDMETHODIMP
nsDataObj::CStream::CopyTo(IStream
* pDestStream
,
189 ULARGE_INTEGER nBytesToCopy
,
190 ULARGE_INTEGER
* nBytesRead
,
191 ULARGE_INTEGER
* nBytesWritten
)
196 //-----------------------------------------------------------------------------
197 STDMETHODIMP
nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart
,
198 ULARGE_INTEGER nBytes
,
204 //-----------------------------------------------------------------------------
205 STDMETHODIMP
nsDataObj::CStream::Read(void* pvBuffer
,
209 NS_ENSURE_TRUE(mInputStream
, E_FAIL
);
217 rv
= mInputStream
->Read((char*)pvBuffer
+ *nBytesRead
, nBytesToRead
- *nBytesRead
, &read
);
218 NS_ENSURE_SUCCESS(rv
, S_FALSE
);
221 } while ((*nBytesRead
< nBytesToRead
) && read
);
226 //-----------------------------------------------------------------------------
227 STDMETHODIMP
nsDataObj::CStream::Revert(void)
232 //-----------------------------------------------------------------------------
233 STDMETHODIMP
nsDataObj::CStream::Seek(LARGE_INTEGER nMove
,
235 ULARGE_INTEGER
* nNewPos
)
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;
250 //-----------------------------------------------------------------------------
251 STDMETHODIMP
nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize
)
256 //-----------------------------------------------------------------------------
257 STDMETHODIMP
nsDataObj::CStream::Stat(STATSTG
* statstg
, DWORD dwFlags
)
260 return STG_E_INVALIDPOINTER
;
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
)))) {
274 nsCAutoString strFileName
;
275 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(sourceURI
);
276 sourceURL
->GetFileName(strFileName
);
278 if (strFileName
.IsEmpty())
281 NS_UnescapeURL(strFileName
);
282 NS_ConvertUTF8toUTF16
wideFileName(strFileName
);
284 PRUint32 nMaxNameLength
= (wideFileName
.Length()*2) + 2;
285 void * retBuf
= CoTaskMemAlloc(nMaxNameLength
); // freed by caller
287 return STG_E_INSUFFICIENTMEMORY
;
289 ZeroMemory(retBuf
, nMaxNameLength
);
290 memcpy(retBuf
, wideFileName
.get(), wideFileName
.Length()*2);
291 statstg
->pwcsName
= (LPOLESTR
)retBuf
;
296 statstg
->type
= STGTY_STREAM
;
299 SystemTimeToFileTime((const SYSTEMTIME
*)&st
, (LPFILETIME
)&statstg
->mtime
);
300 statstg
->ctime
= statstg
->atime
= statstg
->mtime
;
304 mChannel
->GetContentLength(&nLength
);
309 statstg
->cbSize
.LowPart
= (DWORD
)nLength
;
310 statstg
->grfMode
= STGM_READ
;
311 statstg
->grfLocksSupported
= LOCK_ONLYONCE
;
312 statstg
->clsid
= CLSID_NULL
;
317 //-----------------------------------------------------------------------------
318 STDMETHODIMP
nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart
,
319 ULARGE_INTEGER nBytes
,
325 //-----------------------------------------------------------------------------
326 STDMETHODIMP
nsDataObj::CStream::Write(const void* pvBuffer
,
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
),
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
);
355 *outStream
= pStream
;
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
375 //-----------------------------------------------------
377 //-----------------------------------------------------
378 nsDataObj::nsDataObj(nsIURI
* uri
)
379 : m_cRef(0), mTransferable(nsnull
),
380 mIsAsyncMode(FALSE
), mIsInOperation(FALSE
)
382 m_enumFE
= new CEnumFormatEtc(32);
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 //-----------------------------------------------------
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
));
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
)
420 if ( (IID_IUnknown
== riid
) || (IID_IDataObject
== riid
) ) {
424 } else if (IID_IAsyncOperation
== riid
) {
425 *ppv
= static_cast<IAsyncOperation
*>(this);
430 return ResultFromScode(E_NOINTERFACE
);
433 //-----------------------------------------------------
434 STDMETHODIMP_(ULONG
) nsDataObj::AddRef()
437 NS_LOG_ADDREF(this, m_cRef
, "nsDataObj", sizeof(*this));
438 //PRNTDEBUG3("nsDataObj::AddRef >>>>>>>>>>>>>>>>>> %d on %p\n", (m_cRef+1), this);
443 //-----------------------------------------------------
444 STDMETHODIMP_(ULONG
) nsDataObj::Release()
446 //PRNTDEBUG3("nsDataObj::Release >>>>>>>>>>>>>>>>>> %d on %p\n", (m_cRef-1), this);
449 NS_LOG_RELEASE(this, m_cRef
, "nsDataObj");
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
)) {
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
);
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
);
487 static CLIPFORMAT fileFlavor
= ::RegisterClipboardFormat( CFSTR_FILECONTENTS
);
488 static CLIPFORMAT PreferredDropEffect
= ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT
);
491 // Arbitrary system formats
493 HRESULT hres
= FindFORMATETC(pFE
, &pde
, FALSE
);
494 if (SUCCEEDED(hres
)) {
495 return AddRefStgMedium(&pde
->stgm
, pSTM
, FALSE
);
498 // Firefox internal formats
502 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
)) {
503 nsCString
* df
= reinterpret_cast<nsCString
*>(mDataFlavors
.SafeElementAt(dfInx
));
505 if (FormatsMatch(fe
, *pFE
)) {
506 pSTM
->pUnkForRelease
= NULL
; // caller is responsible for deleting this data
507 CLIPFORMAT format
= pFE
->cfFormat
;
510 // Someone is asking for plain or unicode text
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.
518 return GetFile(*pFE
, *pSTM
);
520 // Someone is asking for an image
522 return GetDib(*df
, *pFE
, *pSTM
);
524 // ... not yet implemented ...
526 // return GetBitmap(*pFE, *pSTM);
527 //case CF_METAFILEPICT:
528 // return GetMetafilePict(*pFE, *pSTM);
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
);
540 if ( format
== fileFlavor
)
541 return GetFileContents ( *pFE
, *pSTM
);
542 if ( format
== PreferredDropEffect
)
543 return GetPreferredDropEffect( *pFE
, *pSTM
);
545 PRNTDEBUG2("***** nsDataObj::GetData - Unknown format %u\n", format
);
546 return GetText(*df
, *pFE
, *pSTM
);
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
567 //-----------------------------------------------------
568 STDMETHODIMP
nsDataObj::QueryGetData(LPFORMATETC pFE
)
570 PRNTDEBUG("nsDataObj::QueryGetData ");
571 PRNTDEBUG2("format: %d\n", pFE
->cfFormat
);
573 // Arbitrary system formats
575 if (SUCCEEDED(FindFORMATETC(pFE
, &pde
, FALSE
)))
578 // Firefox internal formats
582 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
)) {
583 if (fe
.cfFormat
== pFE
->cfFormat
) {
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
);
606 SIZE_T cb
= GlobalSize(hglobIn
);
607 HGLOBAL hglobOut
= GlobalAlloc(GMEM_FIXED
, cb
);
609 CopyMemory(hglobOut
, pvIn
, cb
);
611 GlobalUnlock(hglobIn
);
616 IUnknown
* nsDataObj::GetCanonicalIUnknown(IUnknown
*punk
)
618 IUnknown
*punkCanonical
;
619 if (punk
&& SUCCEEDED(punk
->QueryInterface(IID_IUnknown
,
620 (LPVOID
*)&punkCanonical
))) {
621 punkCanonical
->Release();
623 punkCanonical
= punk
;
625 return punkCanonical
;
628 //-----------------------------------------------------
629 STDMETHODIMP
nsDataObj::SetData(LPFORMATETC pFE
, LPSTGMEDIUM pSTM
, BOOL fRelease
)
631 PRNTDEBUG("nsDataObj::SetData\n");
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
;
644 // Store arbitrary system formats
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
));
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
;
671 ReleaseStgMedium(pSTM
);
673 return ResultFromScode(S_OK
);
677 nsDataObj::FindFORMATETC(FORMATETC
*pfe
, LPDATAENTRY
*ppde
, BOOL fAdd
)
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
];
698 return DV_E_FORMATETC
;
700 LPDATAENTRY pde
= (LPDATAENTRY
)CoTaskMemAlloc(sizeof(DATAENTRY
));
704 ZeroMemory(&pde
->stgm
, sizeof(STGMEDIUM
));
706 m_enumFE
->AddFE(pfe
);
707 mDataEntryList
.AppendElement(pde
);
711 return E_OUTOFMEMORY
;
716 nsDataObj::AddRefStgMedium(STGMEDIUM
*pstgmIn
, STGMEDIUM
*pstgmOut
, BOOL fCopyIn
)
719 STGMEDIUM stgmOut
= *pstgmIn
;
721 if (pstgmIn
->pUnkForRelease
== NULL
&&
722 !(pstgmIn
->tymed
& (TYMED_ISTREAM
| TYMED_ISTORAGE
))) {
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
;
734 stgmOut
.pUnkForRelease
= static_cast<IDataObject
*>(this);
738 if (SUCCEEDED(hres
)) {
739 switch (stgmOut
.tymed
) {
741 stgmOut
.pstm
->AddRef();
744 stgmOut
.pstg
->AddRef();
747 if (stgmOut
.pUnkForRelease
) {
748 stgmOut
.pUnkForRelease
->AddRef();
756 //-----------------------------------------------------
757 STDMETHODIMP
nsDataObj::EnumFormatEtc(DWORD dwDir
, LPENUMFORMATETC
*ppEnum
)
759 PRNTDEBUG("nsDataObj::EnumFormatEtc\n");
763 m_enumFE
->Clone(ppEnum
);
772 return ResultFromScode(E_FAIL
);
774 // Clone already AddRefed the result so don't addref it again.
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
,
806 mIsInOperation
= FALSE
;
811 STDMETHODIMP
nsDataObj::GetAsyncMode(BOOL
*pfIsOpAsync
)
816 *pfIsOpAsync
= mIsAsyncMode
;
821 STDMETHODIMP
nsDataObj::InOperation(BOOL
*pfInAsyncOp
)
826 *pfInAsyncOp
= mIsInOperation
;
831 STDMETHODIMP
nsDataObj::SetAsyncMode(BOOL fDoOpAsync
)
833 mIsAsyncMode
= fDoOpAsync
;
837 STDMETHODIMP
nsDataObj::StartOperation(IBindCtx
*pbcReserved
)
839 mIsInOperation
= TRUE
;
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 //-----------------------------------------------------
861 nsDataObj::GetBitmap ( const nsACString
& , FORMATETC
&, STGMEDIUM
& )
863 PRNTDEBUG("nsDataObj::GetBitmap\n");
864 return ResultFromScode(E_NOTIMPL
);
871 // Someone is asking for a bitmap. The data in the transferable will be a straight
872 // nsIImage, so just QI it.
875 nsDataObj :: GetDib ( const nsACString
& inFlavor
, FORMATETC
&, STGMEDIUM
& aSTG
)
877 PRNTDEBUG("nsDataObj::GetDib\n");
878 ULONG result
= E_FAIL
;
882 nsCOMPtr
<nsISupports
> genericDataWrapper
;
883 mTransferable
->GetTransferData(PromiseFlatCString(inFlavor
).get(), getter_AddRefs(genericDataWrapper
), &len
);
884 nsCOMPtr
<nsIImage
> image ( do_QueryInterface(genericDataWrapper
) );
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
));
892 ptr
->GetData(getter_AddRefs(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
) {
903 aSTG
.tymed
= TYMED_HGLOBAL
;
906 } // if we have an image
908 NS_WARNING ( "Definitely not an image on clipboard" );
911 return ResultFromScode(result
);
921 nsDataObj :: GetFileDescriptor ( FORMATETC
& aFE
, STGMEDIUM
& aSTG
, PRBool aIsUnicode
)
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
))
931 return GetFileDescriptor_IStreamW(aFE
, aSTG
);
933 return GetFileDescriptor_IStreamA(aFE
, aSTG
);
935 else if (IsFlavourPresent(kURLMime
))
938 res
= GetFileDescriptorInternetShortcutW ( aFE
, aSTG
);
940 res
= GetFileDescriptorInternetShortcutA ( aFE
, aSTG
);
943 NS_WARNING ( "Not yet implemented\n" );
947 } // GetFileDescriptor
952 nsDataObj :: GetFileContents ( FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
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
);
964 NS_WARNING ( "Not yet implemented\n" );
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.
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
);
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('.')) {
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
1007 // It would seem that this is more functionality suited to being in nsILocalFile.
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
1016 MangleTextToValidFilename(aText
);
1017 if (aText
.IsEmpty())
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
= '_';
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
);
1039 // empty names aren't permitted
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
1051 MangleTextToValidFilename(aText
);
1052 if (aText
.IsEmpty())
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
);
1063 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
1066 GetLocalizedString(const PRUnichar
* aName
, nsXPIDLString
& aString
)
1069 nsCOMPtr
<nsIStringBundleService
> stringService
= do_GetService(NS_STRINGBUNDLE_CONTRACTID
, &rv
);
1073 nsCOMPtr
<nsIStringBundle
> stringBundle
;
1074 rv
= stringService
->CreateBundle(PAGEINFO_PROPERTIES
, getter_AddRefs(stringBundle
));
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.
1089 nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
1094 // get the title of the shortcut
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
;
1131 } // GetFileDescriptorInternetShortcutA
1134 nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
1139 // get the title of the shortcut
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
;
1176 } // GetFileDescriptorInternetShortcutW
1180 // GetFileContentsInternetShortcut
1182 // Create the special format for an internet shortcut and build up the data
1183 // structures the shell is expecting.
1186 nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
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
));
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
;
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
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!
1254 } // for each flavor
1259 HRESULT
nsDataObj::GetPreferredDropEffect ( FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
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
);
1279 res
= E_OUTOFMEMORY
;
1281 aSTG
.hGlobal
= hGlobalMemory
;
1285 //-----------------------------------------------------
1286 HRESULT
nsDataObj::GetText(const nsACString
& aDataFlavor
, FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
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
;
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
);
1303 return ResultFromScode(E_FAIL
);
1304 nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr
, genericDataWrapper
, &data
, len
);
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.
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
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);
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.
1353 allocLen
= strlen(utf8HTML
) + sizeof(char);
1356 NS_WARNING ( "Oh no, couldn't convert to HTML" );
1357 return ResultFromScode(S_OK
);
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
)
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',
1396 PRBool found
= PR_FALSE
;
1397 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
)) {
1398 nsCString
* df
= reinterpret_cast<nsCString
*>(mDataFlavors
.SafeElementAt(dfInx
));
1400 if (df
&& df
->EqualsLiteral(kNativeImageMime
)) {
1411 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1413 mTransferable
->GetTransferData(kNativeImageMime
, getter_AddRefs(genericDataWrapper
), &len
);
1414 nsCOMPtr
<nsIImage
> image ( do_QueryInterface(genericDataWrapper
) );
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
));
1423 ptr
->GetData(getter_AddRefs(image
));
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
)
1437 // We now own these bits!
1438 PRUint32 bitmapSize
= GlobalSize(bits
);
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
));
1455 // Filename must be random so as not to confuse apps like Photshop which handle
1456 // multiple drags into a single window.
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
)) {
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
)) {
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
;
1500 if (NS_FAILED(rv
)) {
1507 // Pass the file name back to the drop target so that it can access the file.
1509 rv
= mCachedTempFile
->GetPath(path
);
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
));
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.
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
;
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
;
1576 // get a pointer to the actual bytes
1577 char * pString
= (char *) GlobalLock(hString
);
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
) {
1628 NS_ADDREF(mTransferable
);
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>
1644 nsDataObj :: ExtractShortcutURL ( nsString
& outURL
)
1646 NS_ASSERTION ( mTransferable
, "We don't have a good transferable" );
1647 nsresult rv
= NS_ERROR_FAILURE
;
1650 nsCOMPtr
<nsISupports
> genericURL
;
1651 if ( NS_SUCCEEDED(mTransferable
->GetTransferData(kURLMime
, getter_AddRefs(genericURL
), &len
)) ) {
1652 nsCOMPtr
<nsISupportsString
> urlObject ( do_QueryInterface(genericURL
) );
1655 urlObject
->GetData ( 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
);
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
) );
1672 urlObject
->GetData ( url
);
1678 } // if found flavor
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>
1695 nsDataObj :: ExtractShortcutTitle ( nsString
& outTitle
)
1697 NS_ASSERTION ( mTransferable
, "We'd don't have a good transferable" );
1698 nsresult rv
= NS_ERROR_FAILURE
;
1701 nsCOMPtr
<nsISupports
> genericURL
;
1702 if ( NS_SUCCEEDED(mTransferable
->GetTransferData(kURLMime
, getter_AddRefs(genericURL
), &len
)) ) {
1703 nsCOMPtr
<nsISupportsString
> urlObject ( do_QueryInterface(genericURL
) );
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) );
1717 } // if found flavor
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
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
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()) {
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) +
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"
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
;
1827 nsDataObj :: GetUniformResourceLocator( FORMATETC
& aFE
, STGMEDIUM
& aSTG
, PRBool aIsUnicode
)
1830 if (IsFlavourPresent(kURLMime
)) {
1832 res
= ExtractUniformResourceLocatorW( aFE
, aSTG
);
1834 res
= ExtractUniformResourceLocatorA( aFE
, aSTG
);
1837 NS_WARNING ("Not yet implemented\n");
1842 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
1844 HRESULT result
= S_OK
;
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
);
1854 return E_OUTOFMEMORY
;
1856 char* contents
= reinterpret_cast<char*>(GlobalLock(hGlobalMemory
));
1858 GlobalFree(hGlobalMemory
);
1859 return E_OUTOFMEMORY
;
1862 strcpy(contents
, asciiUrl
.get());
1863 GlobalUnlock(hGlobalMemory
);
1864 aSTG
.hGlobal
= hGlobalMemory
;
1865 aSTG
.tymed
= TYMED_HGLOBAL
;
1871 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC
& aFE
, STGMEDIUM
& aSTG
)
1873 HRESULT result
= S_OK
;
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
);
1882 return E_OUTOFMEMORY
;
1884 wchar_t* contents
= reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory
));
1886 GlobalFree(hGlobalMemory
);
1887 return E_OUTOFMEMORY
;
1890 wcscpy(contents
, url
.get());
1891 GlobalUnlock(hGlobalMemory
);
1892 aSTG
.hGlobal
= hGlobalMemory
;
1893 aSTG
.tymed
= TYMED_HGLOBAL
;
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
);
1928 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(sourceURI
);
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
;
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
;
1961 nsCOMPtr
<nsIURI
> sourceURI
;
1962 rv
= GetDownloadDetails(getter_AddRefs(sourceURI
),
1966 ::GlobalFree(fileGroupDescHandle
);
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
;
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
;
2000 nsCOMPtr
<nsIURI
> sourceURI
;
2001 rv
= GetDownloadDetails(getter_AddRefs(sourceURI
),
2005 ::GlobalFree(fileGroupDescHandle
);
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
;
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
;