1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "mozilla/ArrayUtils.h"
7 
8 #include <ole2.h>
9 #include <shlobj.h>
10 
11 #include "nsDataObj.h"
12 #include "nsArrayUtils.h"
13 #include "nsClipboard.h"
14 #include "nsReadableUtils.h"
15 #include "nsITransferable.h"
16 #include "nsISupportsPrimitives.h"
17 #include "IEnumFE.h"
18 #include "nsPrimitiveHelpers.h"
19 #include "nsXPIDLString.h"
20 #include "nsImageClipboard.h"
21 #include "nsCRT.h"
22 #include "nsPrintfCString.h"
23 #include "nsIStringBundle.h"
24 #include "nsEscape.h"
25 #include "nsIURL.h"
26 #include "nsNetUtil.h"
27 #include "mozilla/Services.h"
28 #include "nsIOutputStream.h"
29 #include "nsXPCOMStrings.h"
30 #include "nscore.h"
31 #include "nsDirectoryServiceDefs.h"
32 #include "nsITimer.h"
33 #include "nsThreadUtils.h"
34 #include "mozilla/Preferences.h"
35 #include "nsIContentPolicy.h"
36 #include "nsContentUtils.h"
37 #include "nsIPrincipal.h"
38 
39 #include "WinUtils.h"
40 #include "mozilla/LazyIdleThread.h"
41 #include "mozilla/WindowsVersion.h"
42 #include <algorithm>
43 
44 
45 using namespace mozilla;
46 using namespace mozilla::widget;
47 
48 #define DEFAULT_THREAD_TIMEOUT_MS 30000
49 
NS_IMPL_ISUPPORTS(nsDataObj::CStream,nsIStreamListener)50 NS_IMPL_ISUPPORTS(nsDataObj::CStream, nsIStreamListener)
51 
52 //-----------------------------------------------------------------------------
53 // CStream implementation
54 nsDataObj::CStream::CStream() :
55   mChannelRead(false),
56   mStreamRead(0)
57 {
58 }
59 
60 //-----------------------------------------------------------------------------
~CStream()61 nsDataObj::CStream::~CStream()
62 {
63 }
64 
65 //-----------------------------------------------------------------------------
66 // helper - initializes the stream
Init(nsIURI * pSourceURI,uint32_t aContentPolicyType,nsIPrincipal * aRequestingPrincipal)67 nsresult nsDataObj::CStream::Init(nsIURI *pSourceURI,
68                                   uint32_t aContentPolicyType,
69                                   nsIPrincipal* aRequestingPrincipal)
70 {
71   // we can not create a channel without a requestingPrincipal
72   if (!aRequestingPrincipal) {
73     return NS_ERROR_FAILURE;
74   }
75   nsresult rv;
76   rv = NS_NewChannel(getter_AddRefs(mChannel),
77                      pSourceURI,
78                      aRequestingPrincipal,
79                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS,
80                      aContentPolicyType,
81                      nullptr,   // loadGroup
82                      nullptr,   // aCallbacks
83                      nsIRequest::LOAD_FROM_CACHE);
84 
85   NS_ENSURE_SUCCESS(rv, rv);
86   rv = mChannel->AsyncOpen2(this);
87   NS_ENSURE_SUCCESS(rv, rv);
88   return NS_OK;
89 }
90 
91 //-----------------------------------------------------------------------------
92 // IUnknown's QueryInterface, nsISupport's AddRef and Release are shared by
93 // IUnknown and nsIStreamListener.
QueryInterface(REFIID refiid,void ** ppvResult)94 STDMETHODIMP nsDataObj::CStream::QueryInterface(REFIID refiid, void** ppvResult)
95 {
96   *ppvResult = nullptr;
97   if (IID_IUnknown == refiid ||
98       refiid == IID_IStream)
99 
100   {
101     *ppvResult = this;
102   }
103 
104   if (nullptr != *ppvResult)
105   {
106     ((LPUNKNOWN)*ppvResult)->AddRef();
107     return S_OK;
108   }
109 
110   return E_NOINTERFACE;
111 }
112 
113 // nsIStreamListener implementation
114 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsISupports * aContext,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)115 nsDataObj::CStream::OnDataAvailable(nsIRequest *aRequest,
116                                     nsISupports *aContext,
117                                     nsIInputStream *aInputStream,
118                                     uint64_t aOffset, // offset within the stream
119                                     uint32_t aCount) // bytes available on this call
120 {
121     // Extend the write buffer for the incoming data.
122     uint8_t* buffer = mChannelData.AppendElements(aCount, fallible);
123     if (!buffer) {
124       return NS_ERROR_OUT_OF_MEMORY;
125     }
126     NS_ASSERTION((mChannelData.Length() == (aOffset + aCount)),
127       "stream length mismatch w/write buffer");
128 
129     // Read() may not return aCount on a single call, so loop until we've
130     // accumulated all the data OnDataAvailable has promised.
131     nsresult rv;
132     uint32_t odaBytesReadTotal = 0;
133     do {
134       uint32_t bytesReadByCall = 0;
135       rv = aInputStream->Read((char*)(buffer + odaBytesReadTotal),
136                               aCount, &bytesReadByCall);
137       odaBytesReadTotal += bytesReadByCall;
138     } while (aCount < odaBytesReadTotal && NS_SUCCEEDED(rv));
139     return rv;
140 }
141 
OnStartRequest(nsIRequest * aRequest,nsISupports * aContext)142 NS_IMETHODIMP nsDataObj::CStream::OnStartRequest(nsIRequest *aRequest,
143                                                  nsISupports *aContext)
144 {
145     mChannelResult = NS_OK;
146     return NS_OK;
147 }
148 
OnStopRequest(nsIRequest * aRequest,nsISupports * aContext,nsresult aStatusCode)149 NS_IMETHODIMP nsDataObj::CStream::OnStopRequest(nsIRequest *aRequest,
150                                                 nsISupports *aContext,
151                                                 nsresult aStatusCode)
152 {
153     mChannelRead = true;
154     mChannelResult = aStatusCode;
155     return NS_OK;
156 }
157 
158 // Pumps thread messages while waiting for the async listener operation to
159 // complete. Failing this call will fail the stream incall from Windows
160 // and cancel the operation.
WaitForCompletion()161 nsresult nsDataObj::CStream::WaitForCompletion()
162 {
163   // We are guaranteed OnStopRequest will get called, so this should be ok.
164   while (!mChannelRead) {
165     // Pump messages
166     NS_ProcessNextEvent(nullptr, true);
167   }
168 
169   if (!mChannelData.Length())
170     mChannelResult = NS_ERROR_FAILURE;
171 
172   return mChannelResult;
173 }
174 
175 //-----------------------------------------------------------------------------
176 // IStream
Clone(IStream ** ppStream)177 STDMETHODIMP nsDataObj::CStream::Clone(IStream** ppStream)
178 {
179   return E_NOTIMPL;
180 }
181 
182 //-----------------------------------------------------------------------------
Commit(DWORD dwFrags)183 STDMETHODIMP nsDataObj::CStream::Commit(DWORD dwFrags)
184 {
185   return E_NOTIMPL;
186 }
187 
188 //-----------------------------------------------------------------------------
CopyTo(IStream * pDestStream,ULARGE_INTEGER nBytesToCopy,ULARGE_INTEGER * nBytesRead,ULARGE_INTEGER * nBytesWritten)189 STDMETHODIMP nsDataObj::CStream::CopyTo(IStream* pDestStream,
190                                         ULARGE_INTEGER nBytesToCopy,
191                                         ULARGE_INTEGER* nBytesRead,
192                                         ULARGE_INTEGER* nBytesWritten)
193 {
194   return E_NOTIMPL;
195 }
196 
197 //-----------------------------------------------------------------------------
LockRegion(ULARGE_INTEGER nStart,ULARGE_INTEGER nBytes,DWORD dwFlags)198 STDMETHODIMP nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart,
199                                             ULARGE_INTEGER nBytes,
200                                             DWORD dwFlags)
201 {
202   return E_NOTIMPL;
203 }
204 
205 //-----------------------------------------------------------------------------
Read(void * pvBuffer,ULONG nBytesToRead,ULONG * nBytesRead)206 STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer,
207                                       ULONG nBytesToRead,
208                                       ULONG* nBytesRead)
209 {
210   // Wait for the write into our buffer to complete via the stream listener.
211   // We can't respond to this by saying "call us back later".
212   if (NS_FAILED(WaitForCompletion()))
213     return E_FAIL;
214 
215   // Bytes left for Windows to read out of our buffer
216   ULONG bytesLeft = mChannelData.Length() - mStreamRead;
217   // Let Windows know what we will hand back, usually this is the entire buffer
218   *nBytesRead = std::min(bytesLeft, nBytesToRead);
219   // Copy the buffer data over
220   memcpy(pvBuffer, ((char*)mChannelData.Elements() + mStreamRead), *nBytesRead);
221   // Update our bytes read tracking
222   mStreamRead += *nBytesRead;
223   return S_OK;
224 }
225 
226 //-----------------------------------------------------------------------------
Revert(void)227 STDMETHODIMP nsDataObj::CStream::Revert(void)
228 {
229   return E_NOTIMPL;
230 }
231 
232 //-----------------------------------------------------------------------------
Seek(LARGE_INTEGER nMove,DWORD dwOrigin,ULARGE_INTEGER * nNewPos)233 STDMETHODIMP nsDataObj::CStream::Seek(LARGE_INTEGER nMove,
234                                       DWORD dwOrigin,
235                                       ULARGE_INTEGER* nNewPos)
236 {
237   if (nNewPos == nullptr)
238     return STG_E_INVALIDPOINTER;
239 
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;
245   }
246 
247   return E_NOTIMPL;
248 }
249 
250 //-----------------------------------------------------------------------------
SetSize(ULARGE_INTEGER nNewSize)251 STDMETHODIMP nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize)
252 {
253   return E_NOTIMPL;
254 }
255 
256 //-----------------------------------------------------------------------------
Stat(STATSTG * statstg,DWORD dwFlags)257 STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags)
258 {
259   if (statstg == nullptr)
260     return STG_E_INVALIDPOINTER;
261 
262   if (!mChannel || NS_FAILED(WaitForCompletion()))
263     return E_FAIL;
264 
265   memset((void*)statstg, 0, sizeof(STATSTG));
266 
267   if (dwFlags != STATFLAG_NONAME)
268   {
269     nsCOMPtr<nsIURI> sourceURI;
270     if (NS_FAILED(mChannel->GetURI(getter_AddRefs(sourceURI)))) {
271       return E_FAIL;
272     }
273 
274     nsAutoCString strFileName;
275     nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
276     sourceURL->GetFileName(strFileName);
277 
278     if (strFileName.IsEmpty())
279       return E_FAIL;
280 
281     NS_UnescapeURL(strFileName);
282     NS_ConvertUTF8toUTF16 wideFileName(strFileName);
283 
284     uint32_t nMaxNameLength = (wideFileName.Length()*2) + 2;
285     void * retBuf = CoTaskMemAlloc(nMaxNameLength); // freed by caller
286     if (!retBuf)
287       return STG_E_INSUFFICIENTMEMORY;
288 
289     ZeroMemory(retBuf, nMaxNameLength);
290     memcpy(retBuf, wideFileName.get(), wideFileName.Length()*2);
291     statstg->pwcsName = (LPOLESTR)retBuf;
292   }
293 
294   SYSTEMTIME st;
295 
296   statstg->type = STGTY_STREAM;
297 
298   GetSystemTime(&st);
299   SystemTimeToFileTime((const SYSTEMTIME*)&st, (LPFILETIME)&statstg->mtime);
300   statstg->ctime = statstg->atime = statstg->mtime;
301 
302   statstg->cbSize.LowPart = (DWORD)mChannelData.Length();
303   statstg->grfMode = STGM_READ;
304   statstg->grfLocksSupported = LOCK_ONLYONCE;
305   statstg->clsid = CLSID_NULL;
306 
307   return S_OK;
308 }
309 
310 //-----------------------------------------------------------------------------
UnlockRegion(ULARGE_INTEGER nStart,ULARGE_INTEGER nBytes,DWORD dwFlags)311 STDMETHODIMP nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart,
312                                               ULARGE_INTEGER nBytes,
313                                               DWORD dwFlags)
314 {
315   return E_NOTIMPL;
316 }
317 
318 //-----------------------------------------------------------------------------
Write(const void * pvBuffer,ULONG nBytesToRead,ULONG * nBytesRead)319 STDMETHODIMP nsDataObj::CStream::Write(const void* pvBuffer,
320                                        ULONG nBytesToRead,
321                                        ULONG* nBytesRead)
322 {
323   return E_NOTIMPL;
324 }
325 
326 //-----------------------------------------------------------------------------
CreateStream(IStream ** outStream)327 HRESULT nsDataObj::CreateStream(IStream **outStream)
328 {
329   NS_ENSURE_TRUE(outStream, E_INVALIDARG);
330 
331   nsresult rv = NS_ERROR_FAILURE;
332   nsAutoString wideFileName;
333   nsCOMPtr<nsIURI> sourceURI;
334   HRESULT res;
335 
336   res = GetDownloadDetails(getter_AddRefs(sourceURI),
337                            wideFileName);
338   if(FAILED(res))
339     return res;
340 
341   nsDataObj::CStream *pStream = new nsDataObj::CStream();
342   NS_ENSURE_TRUE(pStream, E_OUTOFMEMORY);
343 
344   pStream->AddRef();
345 
346   // query the requestingPrincipal from the transferable and add it to the new channel
347   nsCOMPtr<nsIPrincipal> requestingPrincipal;
348   mTransferable->GetRequestingPrincipal(getter_AddRefs(requestingPrincipal));
349   MOZ_ASSERT(requestingPrincipal, "can not create channel without a principal");
350   // default transferable content policy is nsIContentPolicy::TYPE_OTHER
351   uint32_t contentPolicyType = nsIContentPolicy::TYPE_OTHER;
352   mTransferable->GetContentPolicyType(&contentPolicyType);
353   rv = pStream->Init(sourceURI, contentPolicyType, requestingPrincipal);
354   if (NS_FAILED(rv))
355   {
356     pStream->Release();
357     return E_FAIL;
358   }
359   *outStream = pStream;
360 
361   return S_OK;
362 }
363 
364 static GUID CLSID_nsDataObj =
365 	{ 0x1bba7640, 0xdf52, 0x11cf, { 0x82, 0x7b, 0, 0xa0, 0x24, 0x3a, 0xe5, 0x05 } };
366 
367 /*
368  * deliberately not using MAX_PATH. This is because on platforms < XP
369  * a file created with a long filename may be mishandled by the shell
370  * resulting in it not being able to be deleted or moved.
371  * See bug 250392 for more details.
372  */
373 #define NS_MAX_FILEDESCRIPTOR 128 + 1
374 
375 /*
376  * Class nsDataObj
377  */
378 
379 //-----------------------------------------------------
380 // construction
381 //-----------------------------------------------------
nsDataObj(nsIURI * uri)382 nsDataObj::nsDataObj(nsIURI * uri)
383   : m_cRef(0), mTransferable(nullptr),
384     mIsAsyncMode(FALSE), mIsInOperation(FALSE)
385 {
386   mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
387                                  NS_LITERAL_CSTRING("nsDataObj"),
388                                  LazyIdleThread::ManualShutdown);
389   m_enumFE = new CEnumFormatEtc();
390   m_enumFE->AddRef();
391 
392   if (uri) {
393     // A URI was obtained, so pass this through to the DataObject
394     // so it can create a SourceURL for CF_HTML flavour
395     uri->GetSpec(mSourceURL);
396   }
397 }
398 //-----------------------------------------------------
399 // destruction
400 //-----------------------------------------------------
~nsDataObj()401 nsDataObj::~nsDataObj()
402 {
403   NS_IF_RELEASE(mTransferable);
404 
405   mDataFlavors.Clear();
406 
407   m_enumFE->Release();
408 
409   // Free arbitrary system formats
410   for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
411       CoTaskMemFree(mDataEntryList[idx]->fe.ptd);
412       ReleaseStgMedium(&mDataEntryList[idx]->stgm);
413       CoTaskMemFree(mDataEntryList[idx]);
414   }
415 }
416 
417 
418 //-----------------------------------------------------
419 // IUnknown interface methods - see inknown.h for documentation
420 //-----------------------------------------------------
QueryInterface(REFIID riid,void ** ppv)421 STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv)
422 {
423 	*ppv=nullptr;
424 
425 	if ( (IID_IUnknown == riid) || (IID_IDataObject	== riid) ) {
426 		*ppv = this;
427 		AddRef();
428 		return S_OK;
429   } else if (IID_IAsyncOperation == riid) {
430     *ppv = static_cast<IAsyncOperation*>(this);
431     AddRef();
432     return S_OK;
433   }
434 
435 	return E_NOINTERFACE;
436 }
437 
438 //-----------------------------------------------------
STDMETHODIMP_(ULONG)439 STDMETHODIMP_(ULONG) nsDataObj::AddRef()
440 {
441 	++m_cRef;
442 	NS_LOG_ADDREF(this, m_cRef, "nsDataObj", sizeof(*this));
443 	return m_cRef;
444 }
445 
446 
447 //-----------------------------------------------------
STDMETHODIMP_(ULONG)448 STDMETHODIMP_(ULONG) nsDataObj::Release()
449 {
450 	--m_cRef;
451 
452 	NS_LOG_RELEASE(this, m_cRef, "nsDataObj");
453 	if (0 != m_cRef)
454 		return m_cRef;
455 
456   // We have released our last ref on this object and need to delete the
457   // temp file. External app acting as drop target may still need to open the
458   // temp file. Addref a timer so it can delay deleting file and destroying
459   // this object. Delete file anyway and destroy this obj if there's a problem.
460   if (mCachedTempFile) {
461     nsresult rv;
462     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
463     if (NS_SUCCEEDED(rv)) {
464       mTimer->InitWithFuncCallback(nsDataObj::RemoveTempFile, this,
465                                    500, nsITimer::TYPE_ONE_SHOT);
466       return AddRef();
467     }
468     mCachedTempFile->Remove(false);
469     mCachedTempFile = nullptr;
470   }
471 
472 	delete this;
473 
474 	return 0;
475 }
476 
477 //-----------------------------------------------------
FormatsMatch(const FORMATETC & source,const FORMATETC & target) const478 BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) const
479 {
480   if ((source.cfFormat == target.cfFormat) &&
481       (source.dwAspect & target.dwAspect) &&
482       (source.tymed & target.tymed)) {
483     return TRUE;
484   } else {
485     return FALSE;
486   }
487 }
488 
489 //-----------------------------------------------------
490 // IDataObject methods
491 //-----------------------------------------------------
GetData(LPFORMATETC aFormat,LPSTGMEDIUM pSTM)492 STDMETHODIMP nsDataObj::GetData(LPFORMATETC aFormat, LPSTGMEDIUM pSTM)
493 {
494   if (!mTransferable)
495     return DV_E_FORMATETC;
496 
497   uint32_t dfInx = 0;
498 
499   static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA );
500   static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW );
501   static CLIPFORMAT uniformResourceLocatorA = ::RegisterClipboardFormat( CFSTR_INETURLA );
502   static CLIPFORMAT uniformResourceLocatorW = ::RegisterClipboardFormat( CFSTR_INETURLW );
503   static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS );
504   static CLIPFORMAT PreferredDropEffect = ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT );
505 
506   // Arbitrary system formats are used for image feedback during drag
507   // and drop. We are responsible for storing these internally during
508   // drag operations.
509   LPDATAENTRY pde;
510   if (LookupArbitraryFormat(aFormat, &pde, FALSE)) {
511     return CopyMediumData(pSTM, &pde->stgm, aFormat, FALSE)
512            ? S_OK : E_UNEXPECTED;
513   }
514 
515   // Firefox internal formats
516   ULONG count;
517   FORMATETC fe;
518   m_enumFE->Reset();
519   while (NOERROR == m_enumFE->Next(1, &fe, &count)
520          && dfInx < mDataFlavors.Length()) {
521     nsCString& df = mDataFlavors.ElementAt(dfInx);
522     if (FormatsMatch(fe, *aFormat)) {
523       pSTM->pUnkForRelease = nullptr;     // caller is responsible for deleting this data
524       CLIPFORMAT format = aFormat->cfFormat;
525       switch(format) {
526 
527       // Someone is asking for plain or unicode text
528       case CF_TEXT:
529       case CF_UNICODETEXT:
530       return GetText(df, *aFormat, *pSTM);
531 
532       // Some 3rd party apps that receive drag and drop files from the browser
533       // window require support for this.
534       case CF_HDROP:
535         return GetFile(*aFormat, *pSTM);
536 
537       // Someone is asking for an image
538       case CF_DIBV5:
539       case CF_DIB:
540         return GetDib(df, *aFormat, *pSTM);
541 
542       default:
543         if ( format == fileDescriptorFlavorA )
544           return GetFileDescriptor ( *aFormat, *pSTM, false );
545         if ( format == fileDescriptorFlavorW )
546           return GetFileDescriptor ( *aFormat, *pSTM, true);
547         if ( format == uniformResourceLocatorA )
548           return GetUniformResourceLocator( *aFormat, *pSTM, false);
549         if ( format == uniformResourceLocatorW )
550           return GetUniformResourceLocator( *aFormat, *pSTM, true);
551         if ( format == fileFlavor )
552           return GetFileContents ( *aFormat, *pSTM );
553         if ( format == PreferredDropEffect )
554           return GetPreferredDropEffect( *aFormat, *pSTM );
555         //MOZ_LOG(gWindowsLog, LogLevel::Info,
556         //       ("***** nsDataObj::GetData - Unknown format %u\n", format));
557         return GetText(df, *aFormat, *pSTM);
558       } //switch
559     } // if
560     dfInx++;
561   } // while
562 
563   return DATA_E_FORMATETC;
564 }
565 
566 //-----------------------------------------------------
GetDataHere(LPFORMATETC pFE,LPSTGMEDIUM pSTM)567 STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
568 {
569   return E_FAIL;
570 }
571 
572 
573 //-----------------------------------------------------
574 // Other objects querying to see if we support a
575 // particular format
576 //-----------------------------------------------------
QueryGetData(LPFORMATETC pFE)577 STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
578 {
579   // Arbitrary system formats are used for image feedback during drag
580   // and drop. We are responsible for storing these internally during
581   // drag operations.
582   LPDATAENTRY pde;
583   if (LookupArbitraryFormat(pFE, &pde, FALSE))
584     return S_OK;
585 
586   // Firefox internal formats
587   ULONG count;
588   FORMATETC fe;
589   m_enumFE->Reset();
590   while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
591     if (fe.cfFormat == pFE->cfFormat) {
592       return S_OK;
593     }
594   }
595   return E_FAIL;
596 }
597 
598 //-----------------------------------------------------
GetCanonicalFormatEtc(LPFORMATETC pFEIn,LPFORMATETC pFEOut)599 STDMETHODIMP nsDataObj::GetCanonicalFormatEtc
600 	 (LPFORMATETC pFEIn, LPFORMATETC pFEOut)
601 {
602   return E_NOTIMPL;
603 }
604 
605 //-----------------------------------------------------
SetData(LPFORMATETC aFormat,LPSTGMEDIUM aMedium,BOOL shouldRel)606 STDMETHODIMP nsDataObj::SetData(LPFORMATETC aFormat, LPSTGMEDIUM aMedium, BOOL shouldRel)
607 {
608   // Arbitrary system formats are used for image feedback during drag
609   // and drop. We are responsible for storing these internally during
610   // drag operations.
611   LPDATAENTRY pde;
612   if (LookupArbitraryFormat(aFormat, &pde, TRUE)) {
613     // Release the old data the lookup handed us for this format. This
614     // may have been set in CopyMediumData when we originally stored the
615     // data.
616     if (pde->stgm.tymed) {
617       ReleaseStgMedium(&pde->stgm);
618       memset(&pde->stgm, 0, sizeof(STGMEDIUM));
619     }
620 
621     bool result = true;
622     if (shouldRel) {
623       // If shouldRel is TRUE, the data object called owns the storage medium
624       // after the call returns. Store the incoming data in our data array for
625       // release when we are destroyed. This is the common case with arbitrary
626       // data from explorer.
627       pde->stgm = *aMedium;
628     } else {
629       // Copy the incoming data into our data array. (AFAICT, this never gets
630       // called with arbitrary formats for drag images.)
631       result = CopyMediumData(&pde->stgm, aMedium, aFormat, TRUE);
632     }
633     pde->fe.tymed = pde->stgm.tymed;
634 
635     return result ? S_OK : DV_E_TYMED;
636   }
637 
638   if (shouldRel)
639     ReleaseStgMedium(aMedium);
640 
641   return S_OK;
642 }
643 
644 bool
LookupArbitraryFormat(FORMATETC * aFormat,LPDATAENTRY * aDataEntry,BOOL aAddorUpdate)645 nsDataObj::LookupArbitraryFormat(FORMATETC *aFormat, LPDATAENTRY *aDataEntry, BOOL aAddorUpdate)
646 {
647   *aDataEntry = nullptr;
648 
649   if (aFormat->ptd != nullptr)
650     return false;
651 
652   // See if it's already in our list. If so return the data entry.
653   for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
654     if (mDataEntryList[idx]->fe.cfFormat == aFormat->cfFormat &&
655         mDataEntryList[idx]->fe.dwAspect == aFormat->dwAspect &&
656         mDataEntryList[idx]->fe.lindex == aFormat->lindex) {
657       if (aAddorUpdate || (mDataEntryList[idx]->fe.tymed & aFormat->tymed)) {
658         // If the caller requests we update, or if the
659         // medium type matches, return the entry.
660         *aDataEntry = mDataEntryList[idx];
661         return true;
662       } else {
663         // Medium does not match, not found.
664         return false;
665       }
666     }
667   }
668 
669   if (!aAddorUpdate)
670     return false;
671 
672   // Add another entry to mDataEntryList
673   LPDATAENTRY dataEntry = (LPDATAENTRY)CoTaskMemAlloc(sizeof(DATAENTRY));
674   if (!dataEntry)
675     return false;
676 
677   dataEntry->fe = *aFormat;
678   *aDataEntry = dataEntry;
679   memset(&dataEntry->stgm, 0, sizeof(STGMEDIUM));
680 
681   // Add this to our IEnumFORMATETC impl. so we can return it when
682   // it's requested.
683   m_enumFE->AddFormatEtc(aFormat);
684 
685   // Store a copy internally in the arbitrary formats array.
686   mDataEntryList.AppendElement(dataEntry);
687 
688   return true;
689 }
690 
691 bool
CopyMediumData(STGMEDIUM * aMediumDst,STGMEDIUM * aMediumSrc,LPFORMATETC aFormat,BOOL aSetData)692 nsDataObj::CopyMediumData(STGMEDIUM *aMediumDst, STGMEDIUM *aMediumSrc, LPFORMATETC aFormat, BOOL aSetData)
693 {
694   STGMEDIUM stgmOut = *aMediumSrc;
695 
696   switch (stgmOut.tymed) {
697     case TYMED_ISTREAM:
698       stgmOut.pstm->AddRef();
699     break;
700     case TYMED_ISTORAGE:
701       stgmOut.pstg->AddRef();
702     break;
703     case TYMED_HGLOBAL:
704       if (!aMediumSrc->pUnkForRelease) {
705         if (aSetData) {
706           if (aMediumSrc->tymed != TYMED_HGLOBAL)
707             return false;
708           stgmOut.hGlobal = OleDuplicateData(aMediumSrc->hGlobal, aFormat->cfFormat, 0);
709           if (!stgmOut.hGlobal)
710             return false;
711         } else {
712           // We are returning this data from LookupArbitraryFormat, indicate to the
713           // shell we hold it and will free it.
714           stgmOut.pUnkForRelease = static_cast<IDataObject*>(this);
715         }
716       }
717     break;
718     default:
719       return false;
720   }
721 
722   if (stgmOut.pUnkForRelease)
723     stgmOut.pUnkForRelease->AddRef();
724 
725   *aMediumDst = stgmOut;
726 
727   return true;
728 }
729 
730 //-----------------------------------------------------
EnumFormatEtc(DWORD dwDir,LPENUMFORMATETC * ppEnum)731 STDMETHODIMP nsDataObj::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
732 {
733   switch (dwDir) {
734     case DATADIR_GET:
735       m_enumFE->Clone(ppEnum);
736       break;
737     case DATADIR_SET:
738       // fall through
739     default:
740       *ppEnum = nullptr;
741   } // switch
742 
743   if (nullptr == *ppEnum)
744     return E_FAIL;
745 
746   (*ppEnum)->Reset();
747   // Clone already AddRefed the result so don't addref it again.
748   return NOERROR;
749 }
750 
751 //-----------------------------------------------------
DAdvise(LPFORMATETC pFE,DWORD dwFlags,LPADVISESINK pIAdviseSink,DWORD * pdwConn)752 STDMETHODIMP nsDataObj::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
753 										            LPADVISESINK pIAdviseSink, DWORD* pdwConn)
754 {
755   return OLE_E_ADVISENOTSUPPORTED;
756 }
757 
758 
759 //-----------------------------------------------------
DUnadvise(DWORD dwConn)760 STDMETHODIMP nsDataObj::DUnadvise(DWORD dwConn)
761 {
762   return OLE_E_ADVISENOTSUPPORTED;
763 }
764 
765 //-----------------------------------------------------
EnumDAdvise(LPENUMSTATDATA * ppEnum)766 STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum)
767 {
768   return OLE_E_ADVISENOTSUPPORTED;
769 }
770 
771 // IAsyncOperation methods
EndOperation(HRESULT hResult,IBindCtx * pbcReserved,DWORD dwEffects)772 STDMETHODIMP nsDataObj::EndOperation(HRESULT hResult,
773                                      IBindCtx *pbcReserved,
774                                      DWORD dwEffects)
775 {
776   mIsInOperation = FALSE;
777   return S_OK;
778 }
779 
GetAsyncMode(BOOL * pfIsOpAsync)780 STDMETHODIMP nsDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
781 {
782   *pfIsOpAsync = mIsAsyncMode;
783 
784   return S_OK;
785 }
786 
InOperation(BOOL * pfInAsyncOp)787 STDMETHODIMP nsDataObj::InOperation(BOOL *pfInAsyncOp)
788 {
789   *pfInAsyncOp = mIsInOperation;
790 
791   return S_OK;
792 }
793 
SetAsyncMode(BOOL fDoOpAsync)794 STDMETHODIMP nsDataObj::SetAsyncMode(BOOL fDoOpAsync)
795 {
796   mIsAsyncMode = fDoOpAsync;
797   return S_OK;
798 }
799 
StartOperation(IBindCtx * pbcReserved)800 STDMETHODIMP nsDataObj::StartOperation(IBindCtx *pbcReserved)
801 {
802   mIsInOperation = TRUE;
803   return S_OK;
804 }
805 
806 //-----------------------------------------------------
807 // GetData and SetData helper functions
808 //-----------------------------------------------------
AddSetFormat(FORMATETC & aFE)809 HRESULT nsDataObj::AddSetFormat(FORMATETC& aFE)
810 {
811   return S_OK;
812 }
813 
814 //-----------------------------------------------------
AddGetFormat(FORMATETC & aFE)815 HRESULT nsDataObj::AddGetFormat(FORMATETC& aFE)
816 {
817   return S_OK;
818 }
819 
820 //
821 // GetDIB
822 //
823 // Someone is asking for a bitmap. The data in the transferable will be a straight
824 // imgIContainer, so just QI it.
825 //
826 HRESULT
GetDib(const nsACString & inFlavor,FORMATETC & aFormat,STGMEDIUM & aSTG)827 nsDataObj::GetDib(const nsACString& inFlavor,
828                   FORMATETC &aFormat,
829                   STGMEDIUM & aSTG)
830 {
831   ULONG result = E_FAIL;
832   uint32_t len = 0;
833   nsCOMPtr<nsISupports> genericDataWrapper;
834   mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(), getter_AddRefs(genericDataWrapper), &len);
835   nsCOMPtr<imgIContainer> image ( do_QueryInterface(genericDataWrapper) );
836   if ( !image ) {
837     // Check if the image was put in an nsISupportsInterfacePointer wrapper.
838     // This might not be necessary any more, but could be useful for backwards
839     // compatibility.
840     nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
841     if ( ptr ) {
842       nsCOMPtr<nsISupports> supports;
843       ptr->GetData(getter_AddRefs(supports));
844       image = do_QueryInterface(supports);
845     }
846   }
847 
848   if ( image ) {
849     // use the |nsImageToClipboard| helper class to build up a bitmap. We now own
850     // the bits, and pass them back to the OS in |aSTG|.
851     nsImageToClipboard converter(image, aFormat.cfFormat == CF_DIBV5);
852     HANDLE bits = nullptr;
853     nsresult rv = converter.GetPicture ( &bits );
854     if ( NS_SUCCEEDED(rv) && bits ) {
855       aSTG.hGlobal = bits;
856       aSTG.tymed = TYMED_HGLOBAL;
857       result = S_OK;
858     }
859   } // if we have an image
860   else
861     NS_WARNING ( "Definitely not an image on clipboard" );
862 	return result;
863 }
864 
865 
866 
867 //
868 // GetFileDescriptor
869 //
870 
871 HRESULT
GetFileDescriptor(FORMATETC & aFE,STGMEDIUM & aSTG,bool aIsUnicode)872 nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
873 {
874   HRESULT res = S_OK;
875 
876   // How we handle this depends on if we're dealing with an internet
877   // shortcut, since those are done under the covers.
878   if (IsFlavourPresent(kFilePromiseMime) ||
879       IsFlavourPresent(kFileMime))
880   {
881     if (aIsUnicode)
882       return GetFileDescriptor_IStreamW(aFE, aSTG);
883     else
884       return GetFileDescriptor_IStreamA(aFE, aSTG);
885   }
886   else if (IsFlavourPresent(kURLMime))
887   {
888     if ( aIsUnicode )
889       res = GetFileDescriptorInternetShortcutW ( aFE, aSTG );
890     else
891       res = GetFileDescriptorInternetShortcutA ( aFE, aSTG );
892   }
893   else
894     NS_WARNING ( "Not yet implemented\n" );
895 
896 	return res;
897 } // GetFileDescriptor
898 
899 
900 //
901 HRESULT
GetFileContents(FORMATETC & aFE,STGMEDIUM & aSTG)902 nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG )
903 {
904   HRESULT res = S_OK;
905 
906   // How we handle this depends on if we're dealing with an internet
907   // shortcut, since those are done under the covers.
908   if (IsFlavourPresent(kFilePromiseMime) ||
909       IsFlavourPresent(kFileMime))
910     return GetFileContents_IStream(aFE, aSTG);
911   else if (IsFlavourPresent(kURLMime))
912     return GetFileContentsInternetShortcut ( aFE, aSTG );
913   else
914     NS_WARNING ( "Not yet implemented\n" );
915 
916 	return res;
917 
918 } // GetFileContents
919 
920 //
921 // Given a unicode string, we ensure that it contains only characters which are valid within
922 // the file system. Remove all forbidden characters from the name, and completely disallow
923 // any title that starts with a forbidden name and extension (e.g. "nul" is invalid, but
924 // "nul." and "nul.txt" are also invalid and will cause problems).
925 //
926 // It would seem that this is more functionality suited to being in nsIFile.
927 //
928 static void
MangleTextToValidFilename(nsString & aText)929 MangleTextToValidFilename(nsString & aText)
930 {
931   static const char* forbiddenNames[] = {
932     "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
933     "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
934     "CON", "PRN", "AUX", "NUL", "CLOCK$"
935   };
936 
937   aText.StripChars(FILE_PATH_SEPARATOR  FILE_ILLEGAL_CHARACTERS);
938   aText.CompressWhitespace(true, true);
939   uint32_t nameLen;
940   for (size_t n = 0; n < ArrayLength(forbiddenNames); ++n) {
941     nameLen = (uint32_t) strlen(forbiddenNames[n]);
942     if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) {
943       // invalid name is either the entire string, or a prefix with a period
944       if (aText.Length() == nameLen || aText.CharAt(nameLen) == char16_t('.')) {
945         aText.Truncate();
946         break;
947       }
948     }
949   }
950 }
951 
952 //
953 // Given a unicode string, convert it down to a valid local charset filename
954 // with the supplied extension. This ensures that we do not cut MBCS characters
955 // in the middle.
956 //
957 // It would seem that this is more functionality suited to being in nsIFile.
958 //
959 static bool
CreateFilenameFromTextA(nsString & aText,const char * aExtension,char * aFilename,uint32_t aFilenameLen)960 CreateFilenameFromTextA(nsString & aText, const char * aExtension,
961                          char * aFilename, uint32_t aFilenameLen)
962 {
963   // ensure that the supplied name doesn't have invalid characters. If
964   // a valid mangled filename couldn't be created then it will leave the
965   // text empty.
966   MangleTextToValidFilename(aText);
967   if (aText.IsEmpty())
968     return false;
969 
970   // repeatably call WideCharToMultiByte as long as the title doesn't fit in the buffer
971   // available to us. Continually reduce the length of the source title until the MBCS
972   // version will fit in the buffer with room for the supplied extension. Doing it this
973   // way ensures that even in MBCS environments there will be a valid MBCS filename of
974   // the correct length.
975   int maxUsableFilenameLen = aFilenameLen - strlen(aExtension) - 1; // space for ext + null byte
976   int currLen, textLen = (int) std::min(aText.Length(), aFilenameLen);
977   char defaultChar = '_';
978   do {
979     currLen = WideCharToMultiByte(CP_ACP,
980       WC_COMPOSITECHECK|WC_DEFAULTCHAR,
981       aText.get(), textLen--, aFilename, maxUsableFilenameLen, &defaultChar, nullptr);
982   }
983   while (currLen == 0 && textLen > 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
984   if (currLen > 0 && textLen > 0) {
985     strcpy(&aFilename[currLen], aExtension);
986     return true;
987   }
988   else {
989     // empty names aren't permitted
990     return false;
991   }
992 }
993 
994 static bool
CreateFilenameFromTextW(nsString & aText,const wchar_t * aExtension,wchar_t * aFilename,uint32_t aFilenameLen)995 CreateFilenameFromTextW(nsString & aText, const wchar_t * aExtension,
996                          wchar_t * aFilename, uint32_t aFilenameLen)
997 {
998   // ensure that the supplied name doesn't have invalid characters. If
999   // a valid mangled filename couldn't be created then it will leave the
1000   // text empty.
1001   MangleTextToValidFilename(aText);
1002   if (aText.IsEmpty())
1003     return false;
1004 
1005   const int extensionLen = wcslen(aExtension);
1006   if (aText.Length() + extensionLen + 1 > aFilenameLen)
1007     aText.Truncate(aFilenameLen - extensionLen - 1);
1008   wcscpy(&aFilename[0], aText.get());
1009   wcscpy(&aFilename[aText.Length()], aExtension);
1010   return true;
1011 }
1012 
1013 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
1014 
1015 static bool
GetLocalizedString(const char16_t * aName,nsXPIDLString & aString)1016 GetLocalizedString(const char16_t * aName, nsXPIDLString & aString)
1017 {
1018   nsCOMPtr<nsIStringBundleService> stringService =
1019     mozilla::services::GetStringBundleService();
1020   if (!stringService)
1021     return false;
1022 
1023   nsCOMPtr<nsIStringBundle> stringBundle;
1024   nsresult rv = stringService->CreateBundle(PAGEINFO_PROPERTIES,
1025                                             getter_AddRefs(stringBundle));
1026   if (NS_FAILED(rv))
1027     return false;
1028 
1029   rv = stringBundle->GetStringFromName(aName, getter_Copies(aString));
1030   return NS_SUCCEEDED(rv);
1031 }
1032 
1033 //
1034 // GetFileDescriptorInternetShortcut
1035 //
1036 // Create the special format for an internet shortcut and build up the data
1037 // structures the shell is expecting.
1038 //
1039 HRESULT
GetFileDescriptorInternetShortcutA(FORMATETC & aFE,STGMEDIUM & aSTG)1040 nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aSTG )
1041 {
1042   // get the title of the shortcut
1043   nsAutoString title;
1044   if ( NS_FAILED(ExtractShortcutTitle(title)) )
1045     return E_OUTOFMEMORY;
1046 
1047   HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORA));
1048   if (!fileGroupDescHandle)
1049     return E_OUTOFMEMORY;
1050 
1051   LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(::GlobalLock(fileGroupDescHandle));
1052   if (!fileGroupDescA) {
1053     ::GlobalFree(fileGroupDescHandle);
1054     return E_OUTOFMEMORY;
1055   }
1056 
1057   // get a valid filename in the following order: 1) from the page title,
1058   // 2) localized string for an untitled page, 3) just use "Untitled.URL"
1059   if (!CreateFilenameFromTextA(title, ".URL",
1060                                fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1061     nsXPIDLString untitled;
1062     if (!GetLocalizedString(u"noPageTitle", untitled) ||
1063         !CreateFilenameFromTextA(untitled, ".URL",
1064                                  fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1065       strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL");
1066     }
1067   }
1068 
1069   // one file in the file block
1070   fileGroupDescA->cItems = 1;
1071   fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;
1072 
1073   ::GlobalUnlock( fileGroupDescHandle );
1074   aSTG.hGlobal = fileGroupDescHandle;
1075   aSTG.tymed = TYMED_HGLOBAL;
1076 
1077   return S_OK;
1078 } // GetFileDescriptorInternetShortcutA
1079 
1080 HRESULT
GetFileDescriptorInternetShortcutW(FORMATETC & aFE,STGMEDIUM & aSTG)1081 nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG )
1082 {
1083   // get the title of the shortcut
1084   nsAutoString title;
1085   if ( NS_FAILED(ExtractShortcutTitle(title)) )
1086     return E_OUTOFMEMORY;
1087 
1088   HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
1089   if (!fileGroupDescHandle)
1090     return E_OUTOFMEMORY;
1091 
1092   LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(::GlobalLock(fileGroupDescHandle));
1093   if (!fileGroupDescW) {
1094     ::GlobalFree(fileGroupDescHandle);
1095     return E_OUTOFMEMORY;
1096   }
1097 
1098   // get a valid filename in the following order: 1) from the page title,
1099   // 2) localized string for an untitled page, 3) just use "Untitled.URL"
1100   if (!CreateFilenameFromTextW(title, L".URL",
1101                                fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1102     nsXPIDLString untitled;
1103     if (!GetLocalizedString(u"noPageTitle", untitled) ||
1104         !CreateFilenameFromTextW(untitled, L".URL",
1105                                  fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1106       wcscpy(fileGroupDescW->fgd[0].cFileName, L"Untitled.URL");
1107     }
1108   }
1109 
1110   // one file in the file block
1111   fileGroupDescW->cItems = 1;
1112   fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;
1113 
1114   ::GlobalUnlock( fileGroupDescHandle );
1115   aSTG.hGlobal = fileGroupDescHandle;
1116   aSTG.tymed = TYMED_HGLOBAL;
1117 
1118   return S_OK;
1119 } // GetFileDescriptorInternetShortcutW
1120 
1121 
1122 //
1123 // GetFileContentsInternetShortcut
1124 //
1125 // Create the special format for an internet shortcut and build up the data
1126 // structures the shell is expecting.
1127 //
1128 HRESULT
GetFileContentsInternetShortcut(FORMATETC & aFE,STGMEDIUM & aSTG)1129 nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
1130 {
1131   static const char * kShellIconPref = "browser.shell.shortcutFavicons";
1132   nsAutoString url;
1133   if ( NS_FAILED(ExtractShortcutURL(url)) )
1134     return E_OUTOFMEMORY;
1135 
1136   nsCOMPtr<nsIURI> aUri;
1137   nsresult rv = NS_NewURI(getter_AddRefs(aUri), url);
1138   if (NS_FAILED(rv)) {
1139     return E_FAIL;
1140   }
1141 
1142   nsAutoCString asciiUrl;
1143   rv = aUri->GetAsciiSpec(asciiUrl);
1144   if (NS_FAILED(rv)) {
1145     return E_FAIL;
1146   }
1147 
1148   const char *shortcutFormatStr;
1149   int totalLen;
1150   nsCString path;
1151   if (!Preferences::GetBool(kShellIconPref, true) ||
1152       !IsVistaOrLater()) {
1153     shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
1154     const int formatLen = strlen(shortcutFormatStr) - 2;  // don't include %s
1155     totalLen = formatLen + asciiUrl.Length();  // don't include null character
1156   } else {
1157     nsCOMPtr<nsIFile> icoFile;
1158 
1159     nsAutoString aUriHash;
1160 
1161     mozilla::widget::FaviconHelper::ObtainCachedIconFile(aUri, aUriHash, mIOThread, true);
1162 
1163     rv = mozilla::widget::FaviconHelper::GetOutputIconPath(aUri, icoFile, true);
1164     NS_ENSURE_SUCCESS(rv, E_FAIL);
1165     rv = icoFile->GetNativePath(path);
1166     NS_ENSURE_SUCCESS(rv, E_FAIL);
1167 
1168     shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n"
1169                         "IDList=\r\nHotKey=0\r\nIconFile=%s\r\n"
1170                         "IconIndex=0\r\n";
1171     const int formatLen = strlen(shortcutFormatStr) - 2 * 2; // no %s twice
1172     totalLen = formatLen + asciiUrl.Length() +
1173                path.Length(); // we don't want a null character on the end
1174   }
1175 
1176   // create a global memory area and build up the file contents w/in it
1177   HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen);
1178   if ( !hGlobalMemory )
1179     return E_OUTOFMEMORY;
1180 
1181   char* contents = reinterpret_cast<char*>(::GlobalLock(hGlobalMemory));
1182   if ( !contents ) {
1183     ::GlobalFree( hGlobalMemory );
1184     return E_OUTOFMEMORY;
1185   }
1186 
1187   //NOTE: we intentionally use the Microsoft version of snprintf here because it does NOT null
1188   // terminate strings which reach the maximum size of the buffer. Since we know that the
1189   // formatted length here is totalLen, this call to _snprintf will format the string into
1190   // the buffer without appending the null character.
1191 
1192   if (!Preferences::GetBool(kShellIconPref, true)) {
1193     _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get());
1194   } else {
1195     _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get(), path.get());
1196   }
1197 
1198   ::GlobalUnlock(hGlobalMemory);
1199   aSTG.hGlobal = hGlobalMemory;
1200   aSTG.tymed = TYMED_HGLOBAL;
1201 
1202   return S_OK;
1203 } // GetFileContentsInternetShortcut
1204 
1205 // check if specified flavour is present in the transferable
IsFlavourPresent(const char * inFlavour)1206 bool nsDataObj :: IsFlavourPresent(const char *inFlavour)
1207 {
1208   bool retval = false;
1209   NS_ENSURE_TRUE(mTransferable, false);
1210 
1211   // get the list of flavors available in the transferable
1212   nsCOMPtr<nsIArray> flavorList;
1213   mTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
1214   NS_ENSURE_TRUE(flavorList, false);
1215 
1216   // try to find requested flavour
1217   uint32_t cnt;
1218   flavorList->GetLength(&cnt);
1219   for (uint32_t i = 0; i < cnt; ++i) {
1220     nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i);
1221     if (currentFlavor) {
1222       nsAutoCString flavorStr;
1223       currentFlavor->GetData(flavorStr);
1224       if (flavorStr.Equals(inFlavour)) {
1225         retval = true;         // found it!
1226         break;
1227       }
1228     }
1229   } // for each flavor
1230 
1231   return retval;
1232 }
1233 
GetPreferredDropEffect(FORMATETC & aFE,STGMEDIUM & aSTG)1234 HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG )
1235 {
1236   HRESULT res = S_OK;
1237   aSTG.tymed = TYMED_HGLOBAL;
1238   aSTG.pUnkForRelease = nullptr;
1239   HGLOBAL hGlobalMemory = nullptr;
1240   hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
1241   if (hGlobalMemory) {
1242     DWORD* pdw = (DWORD*) GlobalLock(hGlobalMemory);
1243     // The PreferredDropEffect clipboard format is only registered if a drag/drop
1244     // of an image happens from Mozilla to the desktop.  We want its value
1245     // to be DROPEFFECT_MOVE in that case so that the file is moved from the
1246     // temporary location, not copied.
1247     // This value should, ideally, be set on the data object via SetData() but
1248     // our IDataObject implementation doesn't implement SetData.  It adds data
1249     // to the data object lazily only when the drop target asks for it.
1250     *pdw = (DWORD) DROPEFFECT_MOVE;
1251     GlobalUnlock(hGlobalMemory);
1252   }
1253   else {
1254     res = E_OUTOFMEMORY;
1255   }
1256   aSTG.hGlobal = hGlobalMemory;
1257   return res;
1258 }
1259 
1260 //-----------------------------------------------------
GetText(const nsACString & aDataFlavor,FORMATETC & aFE,STGMEDIUM & aSTG)1261 HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
1262 {
1263   void* data = nullptr;
1264   uint32_t   len;
1265 
1266   // if someone asks for text/plain, look up text/unicode instead in the transferable.
1267   const char* flavorStr;
1268   const nsPromiseFlatCString& flat = PromiseFlatCString(aDataFlavor);
1269   if (aDataFlavor.EqualsLiteral("text/plain"))
1270     flavorStr = kUnicodeMime;
1271   else
1272     flavorStr = flat.get();
1273 
1274   // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
1275   nsCOMPtr<nsISupports> genericDataWrapper;
1276   mTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &len);
1277   if ( !len )
1278     return E_FAIL;
1279   nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, len );
1280   if ( !data )
1281     return E_FAIL;
1282 
1283   HGLOBAL     hGlobalMemory = nullptr;
1284 
1285   aSTG.tymed          = TYMED_HGLOBAL;
1286   aSTG.pUnkForRelease = nullptr;
1287 
1288   // We play games under the hood and advertise flavors that we know we
1289   // can support, only they require a bit of conversion or munging of the data.
1290   // Do that here.
1291   //
1292   // The transferable gives us data that is null-terminated, but this isn't reflected in
1293   // the |len| parameter. Windoze apps expect this null to be there so bump our data buffer
1294   // by the appropriate size to account for the null (one char for CF_TEXT, one char16_t for
1295   // CF_UNICODETEXT).
1296   DWORD allocLen = (DWORD)len;
1297   if ( aFE.cfFormat == CF_TEXT ) {
1298     // Someone is asking for text/plain; convert the unicode (assuming it's present)
1299     // to text with the correct platform encoding.
1300     size_t bufferSize = sizeof(char)*(len + 2);
1301     char* plainTextData = static_cast<char*>(moz_xmalloc(bufferSize));
1302     char16_t* castedUnicode = reinterpret_cast<char16_t*>(data);
1303     int32_t plainTextLen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)castedUnicode, len / 2 + 1, plainTextData, bufferSize, NULL, NULL);
1304     // replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include
1305     // the null in the length.
1306     free(data);
1307     if ( plainTextLen ) {
1308       data = plainTextData;
1309       allocLen = plainTextLen;
1310     }
1311     else {
1312       free(plainTextData);
1313       NS_WARNING ( "Oh no, couldn't convert unicode to plain text" );
1314       return S_OK;
1315     }
1316   }
1317   else if ( aFE.cfFormat == nsClipboard::CF_HTML ) {
1318     // Someone is asking for win32's HTML flavor. Convert our html fragment
1319     // from unicode to UTF-8 then put it into a format specified by msft.
1320     NS_ConvertUTF16toUTF8 converter ( reinterpret_cast<char16_t*>(data) );
1321     char* utf8HTML = nullptr;
1322     nsresult rv = BuildPlatformHTML ( converter.get(), &utf8HTML );      // null terminates
1323 
1324     free(data);
1325     if ( NS_SUCCEEDED(rv) && utf8HTML ) {
1326       // replace the unicode data with our HTML data. Don't forget the null.
1327       data = utf8HTML;
1328       allocLen = strlen(utf8HTML) + sizeof(char);
1329     }
1330     else {
1331       NS_WARNING ( "Oh no, couldn't convert to HTML" );
1332       return S_OK;
1333     }
1334   }
1335   else if ( aFE.cfFormat != nsClipboard::CF_CUSTOMTYPES ) {
1336     // we assume that any data that isn't caught above is unicode. This may
1337     // be an erroneous assumption, but is true so far.
1338     allocLen += sizeof(char16_t);
1339   }
1340 
1341   hGlobalMemory = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, allocLen);
1342 
1343   // Copy text to Global Memory Area
1344   if ( hGlobalMemory ) {
1345     char* dest = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
1346     char* source = reinterpret_cast<char*>(data);
1347     memcpy ( dest, source, allocLen );                         // copies the null as well
1348     GlobalUnlock(hGlobalMemory);
1349   }
1350   aSTG.hGlobal = hGlobalMemory;
1351 
1352   // Now, delete the memory that was created by CreateDataFromPrimitive (or our text/plain data)
1353   free(data);
1354 
1355   return S_OK;
1356 }
1357 
1358 //-----------------------------------------------------
GetFile(FORMATETC & aFE,STGMEDIUM & aSTG)1359 HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG)
1360 {
1361   uint32_t dfInx = 0;
1362   ULONG count;
1363   FORMATETC fe;
1364   m_enumFE->Reset();
1365   while (NOERROR == m_enumFE->Next(1, &fe, &count)
1366     && dfInx < mDataFlavors.Length()) {
1367       if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime))
1368         return DropImage(aFE, aSTG);
1369       if (mDataFlavors[dfInx].EqualsLiteral(kFileMime))
1370         return DropFile(aFE, aSTG);
1371       if (mDataFlavors[dfInx].EqualsLiteral(kFilePromiseMime))
1372         return DropTempFile(aFE, aSTG);
1373       dfInx++;
1374   }
1375   return E_FAIL;
1376 }
1377 
DropFile(FORMATETC & aFE,STGMEDIUM & aSTG)1378 HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG)
1379 {
1380   nsresult rv;
1381   uint32_t len = 0;
1382   nsCOMPtr<nsISupports> genericDataWrapper;
1383 
1384   mTransferable->GetTransferData(kFileMime, getter_AddRefs(genericDataWrapper),
1385                                  &len);
1386   nsCOMPtr<nsIFile> file ( do_QueryInterface(genericDataWrapper) );
1387 
1388   if (!file)
1389   {
1390     nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
1391     if (ptr) {
1392       nsCOMPtr<nsISupports> supports;
1393       ptr->GetData(getter_AddRefs(supports));
1394       file = do_QueryInterface(supports);
1395     }
1396   }
1397 
1398   if (!file)
1399     return E_FAIL;
1400 
1401   aSTG.tymed = TYMED_HGLOBAL;
1402   aSTG.pUnkForRelease = nullptr;
1403 
1404   nsAutoString path;
1405   rv = file->GetPath(path);
1406   if (NS_FAILED(rv))
1407     return E_FAIL;
1408 
1409   uint32_t allocLen = path.Length() + 2;
1410   HGLOBAL hGlobalMemory = nullptr;
1411   char16_t *dest;
1412 
1413   hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) +
1414                                              allocLen * sizeof(char16_t));
1415   if (!hGlobalMemory)
1416     return E_FAIL;
1417 
1418   DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
1419 
1420   // First, populate the drop file structure
1421   pDropFile->pFiles = sizeof(DROPFILES); //Offset to start of file name string
1422   pDropFile->fNC    = 0;
1423   pDropFile->pt.x   = 0;
1424   pDropFile->pt.y   = 0;
1425   pDropFile->fWide  = TRUE;
1426 
1427   // Copy the filename right after the DROPFILES structure
1428   dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
1429   memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t));
1430 
1431   // Two null characters are needed at the end of the file name.
1432   // Lookup the CF_HDROP shell clipboard format for more info.
1433   // Add the second null character right after the first one.
1434   dest[allocLen - 1] = L'\0';
1435 
1436   GlobalUnlock(hGlobalMemory);
1437 
1438   aSTG.hGlobal = hGlobalMemory;
1439 
1440   return S_OK;
1441 }
1442 
DropImage(FORMATETC & aFE,STGMEDIUM & aSTG)1443 HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
1444 {
1445   nsresult rv;
1446   if (!mCachedTempFile) {
1447     uint32_t len = 0;
1448     nsCOMPtr<nsISupports> genericDataWrapper;
1449 
1450     mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
1451     nsCOMPtr<imgIContainer> image(do_QueryInterface(genericDataWrapper));
1452 
1453     if (!image) {
1454       // Check if the image was put in an nsISupportsInterfacePointer wrapper.
1455       // This might not be necessary any more, but could be useful for backwards
1456       // compatibility.
1457       nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
1458       if (ptr) {
1459         nsCOMPtr<nsISupports> supports;
1460         ptr->GetData(getter_AddRefs(supports));
1461         image = do_QueryInterface(supports);
1462       }
1463     }
1464 
1465     if (!image)
1466       return E_FAIL;
1467 
1468     // Use the clipboard helper class to build up a memory bitmap.
1469     nsImageToClipboard converter(image);
1470     HANDLE bits = nullptr;
1471     rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
1472 
1473     if (NS_FAILED(rv) || !bits)
1474       return E_FAIL;
1475 
1476     // We now own these bits!
1477     uint32_t bitmapSize = GlobalSize(bits);
1478     if (!bitmapSize) {
1479       GlobalFree(bits);
1480       return E_FAIL;
1481     }
1482 
1483     // Save the bitmap to a temporary location.
1484     nsCOMPtr<nsIFile> dropFile;
1485     rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
1486     if (!dropFile) {
1487       GlobalFree(bits);
1488       return E_FAIL;
1489     }
1490 
1491     // Filename must be random so as not to confuse apps like
1492     // Photoshop which handle multiple drags into a single window.
1493     char buf[13];
1494     nsCString filename;
1495     NS_MakeRandomString(buf, 8);
1496     memcpy(buf+8, ".bmp", 5);
1497     filename.Append(nsDependentCString(buf, 12));
1498     dropFile->AppendNative(filename);
1499     rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
1500     if (NS_FAILED(rv)) {
1501       GlobalFree(bits);
1502       return E_FAIL;
1503     }
1504 
1505     // Cache the temp file so we can delete it later and so
1506     // it doesn't get recreated over and over on multiple calls
1507     // which does occur from windows shell.
1508     dropFile->Clone(getter_AddRefs(mCachedTempFile));
1509 
1510     // Write the data to disk.
1511     nsCOMPtr<nsIOutputStream> outStream;
1512     rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
1513     if (NS_FAILED(rv)) {
1514       GlobalFree(bits);
1515       return E_FAIL;
1516     }
1517 
1518     char * bm = (char *)GlobalLock(bits);
1519 
1520     BITMAPFILEHEADER	fileHdr;
1521     BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
1522 
1523     fileHdr.bfType        = ((WORD) ('M' << 8) | 'B');
1524     fileHdr.bfSize        = GlobalSize (bits) + sizeof(fileHdr);
1525     fileHdr.bfReserved1   = 0;
1526     fileHdr.bfReserved2   = 0;
1527     fileHdr.bfOffBits     = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
1528 
1529     uint32_t writeCount = 0;
1530     if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
1531         NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
1532       rv = NS_ERROR_FAILURE;
1533 
1534     outStream->Close();
1535 
1536     GlobalUnlock(bits);
1537     GlobalFree(bits);
1538 
1539     if (NS_FAILED(rv))
1540       return E_FAIL;
1541   }
1542 
1543   // Pass the file name back to the drop target so that it can access the file.
1544   nsAutoString path;
1545   rv = mCachedTempFile->GetPath(path);
1546   if (NS_FAILED(rv))
1547     return E_FAIL;
1548 
1549   // Two null characters are needed to terminate the file name list.
1550   HGLOBAL hGlobalMemory = nullptr;
1551 
1552   uint32_t allocLen = path.Length() + 2;
1553 
1554   aSTG.tymed = TYMED_HGLOBAL;
1555   aSTG.pUnkForRelease = nullptr;
1556 
1557   hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
1558   if (!hGlobalMemory)
1559     return E_FAIL;
1560 
1561   DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
1562 
1563   // First, populate the drop file structure.
1564   pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
1565   pDropFile->fNC    = 0;
1566   pDropFile->pt.x   = 0;
1567   pDropFile->pt.y   = 0;
1568   pDropFile->fWide  = TRUE;
1569 
1570   // Copy the filename right after the DROPFILES structure.
1571   char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
1572   memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
1573 
1574   // Two null characters are needed at the end of the file name.
1575   // Lookup the CF_HDROP shell clipboard format for more info.
1576   // Add the second null character right after the first one.
1577   dest[allocLen - 1] = L'\0';
1578 
1579   GlobalUnlock(hGlobalMemory);
1580 
1581   aSTG.hGlobal = hGlobalMemory;
1582 
1583   return S_OK;
1584 }
1585 
DropTempFile(FORMATETC & aFE,STGMEDIUM & aSTG)1586 HRESULT nsDataObj::DropTempFile(FORMATETC& aFE, STGMEDIUM& aSTG)
1587 {
1588   nsresult rv;
1589   if (!mCachedTempFile) {
1590     // Tempfile will need a temporary location.
1591     nsCOMPtr<nsIFile> dropFile;
1592     rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
1593     if (!dropFile)
1594       return E_FAIL;
1595 
1596     // Filename must be random
1597     nsCString filename;
1598     nsAutoString wideFileName;
1599     nsCOMPtr<nsIURI> sourceURI;
1600     HRESULT res;
1601     res = GetDownloadDetails(getter_AddRefs(sourceURI),
1602       wideFileName);
1603     if (FAILED(res))
1604       return res;
1605     NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, filename);
1606 
1607     dropFile->AppendNative(filename);
1608     rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
1609     if (NS_FAILED(rv))
1610       return E_FAIL;
1611 
1612     // Cache the temp file so we can delete it later and so
1613     // it doesn't get recreated over and over on multiple calls
1614     // which does occur from windows shell.
1615     dropFile->Clone(getter_AddRefs(mCachedTempFile));
1616 
1617     // Write the data to disk.
1618     nsCOMPtr<nsIOutputStream> outStream;
1619     rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
1620     if (NS_FAILED(rv))
1621       return E_FAIL;
1622 
1623     IStream *pStream = nullptr;
1624     nsDataObj::CreateStream(&pStream);
1625     NS_ENSURE_TRUE(pStream, E_FAIL);
1626 
1627     char buffer[512];
1628     ULONG readCount = 0;
1629     uint32_t writeCount = 0;
1630     while (1) {
1631       HRESULT hres = pStream->Read(buffer, sizeof(buffer), &readCount);
1632       if (FAILED(hres))
1633         return E_FAIL;
1634       if (readCount == 0)
1635         break;
1636       rv = outStream->Write(buffer, readCount, &writeCount);
1637       if (NS_FAILED(rv))
1638         return E_FAIL;
1639     }
1640     outStream->Close();
1641     pStream->Release();
1642   }
1643 
1644   // Pass the file name back to the drop target so that it can access the file.
1645   nsAutoString path;
1646   rv = mCachedTempFile->GetPath(path);
1647   if (NS_FAILED(rv))
1648     return E_FAIL;
1649 
1650   uint32_t allocLen = path.Length() + 2;
1651 
1652   // Two null characters are needed to terminate the file name list.
1653   HGLOBAL hGlobalMemory = nullptr;
1654 
1655   aSTG.tymed = TYMED_HGLOBAL;
1656   aSTG.pUnkForRelease = nullptr;
1657 
1658   hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
1659   if (!hGlobalMemory)
1660     return E_FAIL;
1661 
1662   DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
1663 
1664   // First, populate the drop file structure.
1665   pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
1666   pDropFile->fNC    = 0;
1667   pDropFile->pt.x   = 0;
1668   pDropFile->pt.y   = 0;
1669   pDropFile->fWide  = TRUE;
1670 
1671   // Copy the filename right after the DROPFILES structure.
1672   char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
1673   memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
1674 
1675   // Two null characters are needed at the end of the file name.
1676   // Lookup the CF_HDROP shell clipboard format for more info.
1677   // Add the second null character right after the first one.
1678   dest[allocLen - 1] = L'\0';
1679 
1680   GlobalUnlock(hGlobalMemory);
1681 
1682   aSTG.hGlobal = hGlobalMemory;
1683 
1684   return S_OK;
1685 }
1686 
1687 //-----------------------------------------------------
GetMetafilePict(FORMATETC &,STGMEDIUM &)1688 HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
1689 {
1690 	return E_NOTIMPL;
1691 }
1692 
1693 //-----------------------------------------------------
SetBitmap(FORMATETC &,STGMEDIUM &)1694 HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&)
1695 {
1696 	return E_NOTIMPL;
1697 }
1698 
1699 //-----------------------------------------------------
SetDib(FORMATETC &,STGMEDIUM &)1700 HRESULT nsDataObj::SetDib(FORMATETC&, STGMEDIUM&)
1701 {
1702 	return E_FAIL;
1703 }
1704 
1705 //-----------------------------------------------------
SetText(FORMATETC & aFE,STGMEDIUM & aSTG)1706 HRESULT nsDataObj::SetText  (FORMATETC& aFE, STGMEDIUM& aSTG)
1707 {
1708 	return E_FAIL;
1709 }
1710 
1711 //-----------------------------------------------------
SetMetafilePict(FORMATETC &,STGMEDIUM &)1712 HRESULT nsDataObj::SetMetafilePict(FORMATETC&, STGMEDIUM&)
1713 {
1714 	return E_FAIL;
1715 }
1716 
1717 
1718 
1719 //-----------------------------------------------------
1720 //-----------------------------------------------------
GetClassID() const1721 CLSID nsDataObj::GetClassID() const
1722 {
1723 	return CLSID_nsDataObj;
1724 }
1725 
1726 //-----------------------------------------------------
1727 // Registers the DataFlavor/FE pair.
1728 //-----------------------------------------------------
AddDataFlavor(const char * aDataFlavor,LPFORMATETC aFE)1729 void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
1730 {
1731   // These two lists are the mapping to and from data flavors and FEs.
1732   // Later, OLE will tell us it needs a certain type of FORMATETC (text, unicode, etc)
1733   // unicode, etc), so we will look up the data flavor that corresponds to
1734   // the FE and then ask the transferable for that type of data.
1735   mDataFlavors.AppendElement(aDataFlavor);
1736   m_enumFE->AddFormatEtc(aFE);
1737 }
1738 
1739 //-----------------------------------------------------
1740 // Sets the transferable object
1741 //-----------------------------------------------------
SetTransferable(nsITransferable * aTransferable)1742 void nsDataObj::SetTransferable(nsITransferable * aTransferable)
1743 {
1744     NS_IF_RELEASE(mTransferable);
1745 
1746   mTransferable = aTransferable;
1747   if (nullptr == mTransferable) {
1748     return;
1749   }
1750 
1751   NS_ADDREF(mTransferable);
1752 
1753   return;
1754 }
1755 
1756 
1757 //
1758 // ExtractURL
1759 //
1760 // Roots around in the transferable for the appropriate flavor that indicates
1761 // a url and pulls out the url portion of the data. Used mostly for creating
1762 // internet shortcuts on the desktop. The url flavor is of the format:
1763 //
1764 //   <url> <linefeed> <page title>
1765 //
1766 nsresult
ExtractShortcutURL(nsString & outURL)1767 nsDataObj :: ExtractShortcutURL ( nsString & outURL )
1768 {
1769   NS_ASSERTION ( mTransferable, "We don't have a good transferable" );
1770   nsresult rv = NS_ERROR_FAILURE;
1771 
1772   uint32_t len = 0;
1773   nsCOMPtr<nsISupports> genericURL;
1774   if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
1775     nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1776     if ( urlObject ) {
1777       nsAutoString url;
1778       urlObject->GetData ( url );
1779       outURL = url;
1780 
1781       // find the first linefeed in the data, that's where the url ends. trunc the
1782       // result string at that point.
1783       int32_t lineIndex = outURL.FindChar ( '\n' );
1784       NS_ASSERTION ( lineIndex > 0, "Format for url flavor is <url> <linefeed> <page title>" );
1785       if ( lineIndex > 0 ) {
1786         outURL.Truncate ( lineIndex );
1787         rv = NS_OK;
1788       }
1789     }
1790   } else if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLDataMime, getter_AddRefs(genericURL), &len)) ||
1791               NS_SUCCEEDED(mTransferable->GetTransferData(kURLPrivateMime, getter_AddRefs(genericURL), &len)) ) {
1792     nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1793     if ( urlObject ) {
1794       nsAutoString url;
1795       urlObject->GetData ( url );
1796       outURL = url;
1797 
1798       rv = NS_OK;
1799     }
1800 
1801   }  // if found flavor
1802 
1803   return rv;
1804 
1805 } // ExtractShortcutURL
1806 
1807 
1808 //
1809 // ExtractShortcutTitle
1810 //
1811 // Roots around in the transferable for the appropriate flavor that indicates
1812 // a url and pulls out the title portion of the data. Used mostly for creating
1813 // internet shortcuts on the desktop. The url flavor is of the format:
1814 //
1815 //   <url> <linefeed> <page title>
1816 //
1817 nsresult
ExtractShortcutTitle(nsString & outTitle)1818 nsDataObj :: ExtractShortcutTitle ( nsString & outTitle )
1819 {
1820   NS_ASSERTION ( mTransferable, "We'd don't have a good transferable" );
1821   nsresult rv = NS_ERROR_FAILURE;
1822 
1823   uint32_t len = 0;
1824   nsCOMPtr<nsISupports> genericURL;
1825   if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
1826     nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1827     if ( urlObject ) {
1828       nsAutoString url;
1829       urlObject->GetData ( url );
1830 
1831       // find the first linefeed in the data, that's where the url ends. we want
1832       // everything after that linefeed. FindChar() returns -1 if we can't find
1833       int32_t lineIndex = url.FindChar ( '\n' );
1834       NS_ASSERTION ( lineIndex != -1, "Format for url flavor is <url> <linefeed> <page title>" );
1835       if ( lineIndex != -1 ) {
1836         url.Mid ( outTitle, lineIndex + 1, (len/2) - (lineIndex + 1) );
1837         rv = NS_OK;
1838       }
1839     }
1840   } // if found flavor
1841 
1842   return rv;
1843 
1844 } // ExtractShortcutTitle
1845 
1846 
1847 //
1848 // BuildPlatformHTML
1849 //
1850 // Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
1851 // header information on it. This will null terminate |outPlatformHTML|. See
1852 //  http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
1853 // for details.
1854 //
1855 // We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
1856 // or <BODY> tags). We'll wrap the fragment with them to make other apps
1857 // happy.
1858 //
1859 nsresult
BuildPlatformHTML(const char * inOurHTML,char ** outPlatformHTML)1860 nsDataObj :: BuildPlatformHTML ( const char* inOurHTML, char** outPlatformHTML )
1861 {
1862   *outPlatformHTML = nullptr;
1863 
1864   nsDependentCString inHTMLString(inOurHTML);
1865   const char* const numPlaceholder  = "00000000";
1866   const char* const startHTMLPrefix = "Version:0.9\r\nStartHTML:";
1867   const char* const endHTMLPrefix   = "\r\nEndHTML:";
1868   const char* const startFragPrefix = "\r\nStartFragment:";
1869   const char* const endFragPrefix   = "\r\nEndFragment:";
1870   const char* const startSourceURLPrefix = "\r\nSourceURL:";
1871   const char* const endFragTrailer  = "\r\n";
1872 
1873   // Do we already have mSourceURL from a drag?
1874   if (mSourceURL.IsEmpty()) {
1875     nsAutoString url;
1876     ExtractShortcutURL(url);
1877 
1878     AppendUTF16toUTF8(url, mSourceURL);
1879   }
1880 
1881   const int32_t kSourceURLLength    = mSourceURL.Length();
1882   const int32_t kNumberLength       = strlen(numPlaceholder);
1883 
1884   const int32_t kTotalHeaderLen     = strlen(startHTMLPrefix) +
1885                                       strlen(endHTMLPrefix) +
1886                                       strlen(startFragPrefix) +
1887                                       strlen(endFragPrefix) +
1888                                       strlen(endFragTrailer) +
1889                                       (kSourceURLLength > 0 ? strlen(startSourceURLPrefix) : 0) +
1890                                       kSourceURLLength +
1891                                       (4 * kNumberLength);
1892 
1893   NS_NAMED_LITERAL_CSTRING(htmlHeaderString, "<html><body>\r\n");
1894 
1895   NS_NAMED_LITERAL_CSTRING(fragmentHeaderString, "<!--StartFragment-->");
1896 
1897   nsDependentCString trailingString(
1898       "<!--EndFragment-->\r\n"
1899       "</body>\r\n"
1900       "</html>");
1901 
1902   // calculate the offsets
1903   int32_t startHTMLOffset = kTotalHeaderLen;
1904   int32_t startFragOffset = startHTMLOffset
1905                               + htmlHeaderString.Length()
1906 			      + fragmentHeaderString.Length();
1907 
1908   int32_t endFragOffset   = startFragOffset
1909                               + inHTMLString.Length();
1910 
1911   int32_t endHTMLOffset   = endFragOffset
1912                               + trailingString.Length();
1913 
1914   // now build the final version
1915   nsCString clipboardString;
1916   clipboardString.SetCapacity(endHTMLOffset);
1917 
1918   clipboardString.Append(startHTMLPrefix);
1919   clipboardString.Append(nsPrintfCString("%08u", startHTMLOffset));
1920 
1921   clipboardString.Append(endHTMLPrefix);
1922   clipboardString.Append(nsPrintfCString("%08u", endHTMLOffset));
1923 
1924   clipboardString.Append(startFragPrefix);
1925   clipboardString.Append(nsPrintfCString("%08u", startFragOffset));
1926 
1927   clipboardString.Append(endFragPrefix);
1928   clipboardString.Append(nsPrintfCString("%08u", endFragOffset));
1929 
1930   if (kSourceURLLength > 0) {
1931     clipboardString.Append(startSourceURLPrefix);
1932     clipboardString.Append(mSourceURL);
1933   }
1934 
1935   clipboardString.Append(endFragTrailer);
1936 
1937   clipboardString.Append(htmlHeaderString);
1938   clipboardString.Append(fragmentHeaderString);
1939   clipboardString.Append(inHTMLString);
1940   clipboardString.Append(trailingString);
1941 
1942   *outPlatformHTML = ToNewCString(clipboardString);
1943   if (!*outPlatformHTML)
1944     return NS_ERROR_OUT_OF_MEMORY;
1945 
1946   return NS_OK;
1947 }
1948 
1949 HRESULT
GetUniformResourceLocator(FORMATETC & aFE,STGMEDIUM & aSTG,bool aIsUnicode)1950 nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
1951 {
1952   HRESULT res = S_OK;
1953   if (IsFlavourPresent(kURLMime)) {
1954     if ( aIsUnicode )
1955       res = ExtractUniformResourceLocatorW( aFE, aSTG );
1956     else
1957       res = ExtractUniformResourceLocatorA( aFE, aSTG );
1958   }
1959   else
1960     NS_WARNING ("Not yet implemented\n");
1961   return res;
1962 }
1963 
1964 HRESULT
ExtractUniformResourceLocatorA(FORMATETC & aFE,STGMEDIUM & aSTG)1965 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG )
1966 {
1967   HRESULT result = S_OK;
1968 
1969   nsAutoString url;
1970   if (NS_FAILED(ExtractShortcutURL(url)))
1971     return E_OUTOFMEMORY;
1972 
1973   NS_LossyConvertUTF16toASCII asciiUrl(url);
1974   const int totalLen = asciiUrl.Length() + 1;
1975   HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
1976   if (!hGlobalMemory)
1977     return E_OUTOFMEMORY;
1978 
1979   char* contents = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
1980   if (!contents) {
1981     GlobalFree(hGlobalMemory);
1982     return E_OUTOFMEMORY;
1983   }
1984 
1985   strcpy(contents, asciiUrl.get());
1986   GlobalUnlock(hGlobalMemory);
1987   aSTG.hGlobal = hGlobalMemory;
1988   aSTG.tymed = TYMED_HGLOBAL;
1989 
1990   return result;
1991 }
1992 
1993 HRESULT
ExtractUniformResourceLocatorW(FORMATETC & aFE,STGMEDIUM & aSTG)1994 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG )
1995 {
1996   HRESULT result = S_OK;
1997 
1998   nsAutoString url;
1999   if (NS_FAILED(ExtractShortcutURL(url)))
2000     return E_OUTOFMEMORY;
2001 
2002   const int totalLen = (url.Length() + 1) * sizeof(char16_t);
2003   HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
2004   if (!hGlobalMemory)
2005     return E_OUTOFMEMORY;
2006 
2007   wchar_t* contents = reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory));
2008   if (!contents) {
2009     GlobalFree(hGlobalMemory);
2010     return E_OUTOFMEMORY;
2011   }
2012 
2013   wcscpy(contents, url.get());
2014   GlobalUnlock(hGlobalMemory);
2015   aSTG.hGlobal = hGlobalMemory;
2016   aSTG.tymed = TYMED_HGLOBAL;
2017 
2018   return result;
2019 }
2020 
2021 
2022 // Gets the filename from the kFilePromiseURLMime flavour
GetDownloadDetails(nsIURI ** aSourceURI,nsAString & aFilename)2023 HRESULT nsDataObj::GetDownloadDetails(nsIURI **aSourceURI,
2024                                       nsAString &aFilename)
2025 {
2026   *aSourceURI = nullptr;
2027 
2028   NS_ENSURE_TRUE(mTransferable, E_FAIL);
2029 
2030   // get the URI from the kFilePromiseURLMime flavor
2031   nsCOMPtr<nsISupports> urlPrimitive;
2032   uint32_t dataSize = 0;
2033   mTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize);
2034   nsCOMPtr<nsISupportsString> srcUrlPrimitive = do_QueryInterface(urlPrimitive);
2035   NS_ENSURE_TRUE(srcUrlPrimitive, E_FAIL);
2036 
2037   nsAutoString srcUri;
2038   srcUrlPrimitive->GetData(srcUri);
2039   if (srcUri.IsEmpty())
2040     return E_FAIL;
2041   nsCOMPtr<nsIURI> sourceURI;
2042   NS_NewURI(getter_AddRefs(sourceURI), srcUri);
2043 
2044   nsAutoString srcFileName;
2045   nsCOMPtr<nsISupports> fileNamePrimitive;
2046   mTransferable->GetTransferData(kFilePromiseDestFilename, getter_AddRefs(fileNamePrimitive), &dataSize);
2047   nsCOMPtr<nsISupportsString> srcFileNamePrimitive = do_QueryInterface(fileNamePrimitive);
2048   if (srcFileNamePrimitive) {
2049     srcFileNamePrimitive->GetData(srcFileName);
2050   } else {
2051     nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
2052     if (!sourceURL)
2053       return E_FAIL;
2054 
2055     nsAutoCString urlFileName;
2056     sourceURL->GetFileName(urlFileName);
2057     NS_UnescapeURL(urlFileName);
2058     CopyUTF8toUTF16(urlFileName, srcFileName);
2059   }
2060   if (srcFileName.IsEmpty())
2061     return E_FAIL;
2062 
2063   // make the name safe for the filesystem
2064   MangleTextToValidFilename(srcFileName);
2065 
2066   sourceURI.swap(*aSourceURI);
2067   aFilename = srcFileName;
2068   return S_OK;
2069 }
2070 
GetFileDescriptor_IStreamA(FORMATETC & aFE,STGMEDIUM & aSTG)2071 HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG)
2072 {
2073   HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
2074   NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
2075 
2076   LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(GlobalLock(fileGroupDescHandle));
2077   if (!fileGroupDescA) {
2078     ::GlobalFree(fileGroupDescHandle);
2079     return E_OUTOFMEMORY;
2080   }
2081 
2082   nsAutoString wideFileName;
2083   HRESULT res;
2084   nsCOMPtr<nsIURI> sourceURI;
2085   res = GetDownloadDetails(getter_AddRefs(sourceURI), wideFileName);
2086   if (FAILED(res))
2087   {
2088     ::GlobalFree(fileGroupDescHandle);
2089     return res;
2090   }
2091 
2092   nsAutoCString nativeFileName;
2093   NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, nativeFileName);
2094 
2095   strncpy(fileGroupDescA->fgd[0].cFileName, nativeFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
2096   fileGroupDescA->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
2097 
2098   // one file in the file block
2099   fileGroupDescA->cItems = 1;
2100   fileGroupDescA->fgd[0].dwFlags = FD_PROGRESSUI;
2101 
2102   GlobalUnlock( fileGroupDescHandle );
2103   aSTG.hGlobal = fileGroupDescHandle;
2104   aSTG.tymed = TYMED_HGLOBAL;
2105 
2106   return S_OK;
2107 }
2108 
GetFileDescriptor_IStreamW(FORMATETC & aFE,STGMEDIUM & aSTG)2109 HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG)
2110 {
2111   HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
2112   NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
2113 
2114   LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(GlobalLock(fileGroupDescHandle));
2115   if (!fileGroupDescW) {
2116     ::GlobalFree(fileGroupDescHandle);
2117     return E_OUTOFMEMORY;
2118   }
2119 
2120   nsAutoString wideFileName;
2121   HRESULT res;
2122   nsCOMPtr<nsIURI> sourceURI;
2123   res = GetDownloadDetails(getter_AddRefs(sourceURI),
2124                            wideFileName);
2125   if (FAILED(res))
2126   {
2127     ::GlobalFree(fileGroupDescHandle);
2128     return res;
2129   }
2130 
2131   wcsncpy(fileGroupDescW->fgd[0].cFileName, wideFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
2132   fileGroupDescW->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
2133   // one file in the file block
2134   fileGroupDescW->cItems = 1;
2135   fileGroupDescW->fgd[0].dwFlags = FD_PROGRESSUI;
2136 
2137   GlobalUnlock(fileGroupDescHandle);
2138   aSTG.hGlobal = fileGroupDescHandle;
2139   aSTG.tymed = TYMED_HGLOBAL;
2140 
2141   return S_OK;
2142 }
2143 
GetFileContents_IStream(FORMATETC & aFE,STGMEDIUM & aSTG)2144 HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
2145 {
2146   IStream *pStream = nullptr;
2147 
2148   nsDataObj::CreateStream(&pStream);
2149   NS_ENSURE_TRUE(pStream, E_FAIL);
2150 
2151   aSTG.tymed = TYMED_ISTREAM;
2152   aSTG.pstm = pStream;
2153   aSTG.pUnkForRelease = nullptr;
2154 
2155   return S_OK;
2156 }
2157 
RemoveTempFile(nsITimer * aTimer,void * aClosure)2158 void nsDataObj::RemoveTempFile(nsITimer* aTimer, void* aClosure)
2159 {
2160   nsDataObj *timedDataObj = static_cast<nsDataObj *>(aClosure);
2161   if (timedDataObj->mCachedTempFile) {
2162     timedDataObj->mCachedTempFile->Remove(false);
2163     timedDataObj->mCachedTempFile = nullptr;
2164   }
2165   timedDataObj->Release();
2166 }
2167