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