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