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/. */
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/TextUtils.h"
12 #include "nsComponentManagerUtils.h"
13 #include "nsDataObj.h"
14 #include "nsArrayUtils.h"
15 #include "nsClipboard.h"
16 #include "nsReadableUtils.h"
17 #include "nsICookieJarSettings.h"
18 #include "nsIHttpChannel.h"
19 #include "nsISupportsPrimitives.h"
20 #include "nsITransferable.h"
22 #include "nsPrimitiveHelpers.h"
25 #include "nsPrintfCString.h"
26 #include "nsIStringBundle.h"
29 #include "nsNetUtil.h"
30 #include "mozilla/Components.h"
31 #include "mozilla/SpinEventLoopUntil.h"
32 #include "mozilla/Unused.h"
33 #include "nsProxyRelease.h"
34 #include "nsIObserverService.h"
35 #include "nsIOutputStream.h"
37 #include "nsDirectoryServiceDefs.h"
39 #include "nsThreadUtils.h"
40 #include "mozilla/Preferences.h"
41 #include "nsContentUtils.h"
42 #include "nsIPrincipal.h"
43 #include "nsNativeCharsetUtils.h"
44 #include "nsMimeTypes.h"
45 #include "nsIMIMEService.h"
46 #include "imgIEncoder.h"
47 #include "imgITools.h"
49 #include "nsLocalFile.h"
51 #include "mozilla/LazyIdleThread.h"
54 using namespace mozilla
;
55 using namespace mozilla::glue
;
56 using namespace mozilla::widget
;
59 #define DEFAULT_THREAD_TIMEOUT_MS 30000
61 //-----------------------------------------------------------------------------
62 // CStreamBase implementation
63 nsDataObj::CStreamBase::CStreamBase() : mStreamRead(0) {}
65 //-----------------------------------------------------------------------------
66 nsDataObj::CStreamBase::~CStreamBase() {}
68 NS_IMPL_ISUPPORTS(nsDataObj::CStream
, nsIStreamListener
)
70 //-----------------------------------------------------------------------------
71 // CStream implementation
72 nsDataObj::CStream::CStream() : mChannelRead(false) {}
74 //-----------------------------------------------------------------------------
75 nsDataObj::CStream::~CStream() {}
77 //-----------------------------------------------------------------------------
78 // helper - initializes the stream
79 nsresult
nsDataObj::CStream::Init(nsIURI
* pSourceURI
,
80 nsContentPolicyType aContentPolicyType
,
81 nsIPrincipal
* aRequestingPrincipal
,
82 nsICookieJarSettings
* aCookieJarSettings
,
83 nsIReferrerInfo
* aReferrerInfo
) {
84 // we can not create a channel without a requestingPrincipal
85 if (!aRequestingPrincipal
) {
86 return NS_ERROR_FAILURE
;
89 rv
= NS_NewChannel(getter_AddRefs(mChannel
), pSourceURI
, aRequestingPrincipal
,
90 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
,
91 aContentPolicyType
, aCookieJarSettings
,
92 nullptr, // PerformanceStorage
94 nullptr, // aCallbacks
95 nsIRequest::LOAD_FROM_CACHE
);
96 NS_ENSURE_SUCCESS(rv
, rv
);
98 if (nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
)) {
99 rv
= httpChannel
->SetReferrerInfo(aReferrerInfo
);
100 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
103 rv
= mChannel
->AsyncOpen(this);
104 NS_ENSURE_SUCCESS(rv
, rv
);
108 //-----------------------------------------------------------------------------
109 // IUnknown's QueryInterface, nsISupport's AddRef and Release are shared by
110 // IUnknown and nsIStreamListener.
111 STDMETHODIMP
nsDataObj::CStream::QueryInterface(REFIID refiid
,
113 *ppvResult
= nullptr;
114 if (IID_IUnknown
== refiid
|| refiid
== IID_IStream
)
120 if (nullptr != *ppvResult
) {
121 ((LPUNKNOWN
)*ppvResult
)->AddRef();
125 return E_NOINTERFACE
;
128 // nsIStreamListener implementation
130 nsDataObj::CStream::OnDataAvailable(
131 nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
132 uint64_t aOffset
, // offset within the stream
133 uint32_t aCount
) // bytes available on this call
135 // If we've been asked to read zero bytes, call `Read` once, just to ensure
136 // any side-effects take place, and return immediately.
138 char buffer
[1] = {0};
139 uint32_t bytesReadByCall
= 0;
140 nsresult rv
= aInputStream
->Read(buffer
, 0, &bytesReadByCall
);
141 MOZ_ASSERT(bytesReadByCall
== 0);
145 // Extend the write buffer for the incoming data.
146 size_t oldLength
= mChannelData
.Length();
148 reinterpret_cast<char*>(mChannelData
.AppendElements(aCount
, fallible
));
150 return NS_ERROR_OUT_OF_MEMORY
;
152 MOZ_ASSERT(mChannelData
.Length() == (aOffset
+ aCount
),
153 "stream length mismatch w/write buffer");
155 // Read() may not return aCount on a single call, so loop until we've
156 // accumulated all the data OnDataAvailable has promised.
157 uint32_t bytesRead
= 0;
158 while (bytesRead
< aCount
) {
159 uint32_t bytesReadByCall
= 0;
160 nsresult rv
= aInputStream
->Read(buffer
+ bytesRead
, aCount
- bytesRead
,
162 bytesRead
+= bytesReadByCall
;
164 if (bytesReadByCall
== 0) {
165 // A `bytesReadByCall` of zero indicates EOF without failure... but we
166 // were promised `aCount` elements and haven't gotten them. Return a
168 rv
= NS_ERROR_FAILURE
;
172 // Drop any trailing uninitialized elements before erroring out.
173 mChannelData
.RemoveElementsAt(oldLength
+ bytesRead
, aCount
- bytesRead
);
180 NS_IMETHODIMP
nsDataObj::CStream::OnStartRequest(nsIRequest
* aRequest
) {
181 mChannelResult
= NS_OK
;
185 NS_IMETHODIMP
nsDataObj::CStream::OnStopRequest(nsIRequest
* aRequest
,
186 nsresult aStatusCode
) {
188 mChannelResult
= aStatusCode
;
192 // Pumps thread messages while waiting for the async listener operation to
193 // complete. Failing this call will fail the stream incall from Windows
194 // and cancel the operation.
195 nsresult
nsDataObj::CStream::WaitForCompletion() {
196 // We are guaranteed OnStopRequest will get called, so this should be ok.
197 SpinEventLoopUntil("widget:nsDataObj::CStream::WaitForCompletion"_ns
,
198 [&]() { return mChannelRead
; });
200 if (!mChannelData
.Length()) mChannelResult
= NS_ERROR_FAILURE
;
202 return mChannelResult
;
205 //-----------------------------------------------------------------------------
207 STDMETHODIMP
nsDataObj::CStreamBase::Clone(IStream
** ppStream
) {
211 //-----------------------------------------------------------------------------
212 STDMETHODIMP
nsDataObj::CStreamBase::Commit(DWORD dwFrags
) { return E_NOTIMPL
; }
214 //-----------------------------------------------------------------------------
215 STDMETHODIMP
nsDataObj::CStreamBase::CopyTo(IStream
* pDestStream
,
216 ULARGE_INTEGER nBytesToCopy
,
217 ULARGE_INTEGER
* nBytesRead
,
218 ULARGE_INTEGER
* nBytesWritten
) {
222 //-----------------------------------------------------------------------------
223 STDMETHODIMP
nsDataObj::CStreamBase::LockRegion(ULARGE_INTEGER nStart
,
224 ULARGE_INTEGER nBytes
,
229 //-----------------------------------------------------------------------------
230 STDMETHODIMP
nsDataObj::CStream::Read(void* pvBuffer
, ULONG nBytesToRead
,
232 // Wait for the write into our buffer to complete via the stream listener.
233 // We can't respond to this by saying "call us back later".
234 if (NS_FAILED(WaitForCompletion())) return E_FAIL
;
236 // Bytes left for Windows to read out of our buffer
237 ULONG bytesLeft
= mChannelData
.Length() - mStreamRead
;
238 // Let Windows know what we will hand back, usually this is the entire buffer
239 *nBytesRead
= std::min(bytesLeft
, nBytesToRead
);
240 // Copy the buffer data over
241 memcpy(pvBuffer
, ((char*)mChannelData
.Elements() + mStreamRead
), *nBytesRead
);
242 // Update our bytes read tracking
243 mStreamRead
+= *nBytesRead
;
247 //-----------------------------------------------------------------------------
248 STDMETHODIMP
nsDataObj::CStreamBase::Revert(void) { return E_NOTIMPL
; }
250 //-----------------------------------------------------------------------------
251 STDMETHODIMP
nsDataObj::CStreamBase::Seek(LARGE_INTEGER nMove
, DWORD dwOrigin
,
252 ULARGE_INTEGER
* nNewPos
) {
253 if (nNewPos
== nullptr) return STG_E_INVALIDPOINTER
;
255 if (nMove
.LowPart
== 0 && nMove
.HighPart
== 0 &&
256 (dwOrigin
== STREAM_SEEK_SET
|| dwOrigin
== STREAM_SEEK_CUR
)) {
257 nNewPos
->LowPart
= 0;
258 nNewPos
->HighPart
= 0;
265 //-----------------------------------------------------------------------------
266 STDMETHODIMP
nsDataObj::CStreamBase::SetSize(ULARGE_INTEGER nNewSize
) {
270 //-----------------------------------------------------------------------------
271 STDMETHODIMP
nsDataObj::CStream::Stat(STATSTG
* statstg
, DWORD dwFlags
) {
272 if (statstg
== nullptr) return STG_E_INVALIDPOINTER
;
274 if (!mChannel
|| NS_FAILED(WaitForCompletion())) return E_FAIL
;
276 memset((void*)statstg
, 0, sizeof(STATSTG
));
278 if (dwFlags
!= STATFLAG_NONAME
) {
279 nsCOMPtr
<nsIURI
> sourceURI
;
280 if (NS_FAILED(mChannel
->GetURI(getter_AddRefs(sourceURI
)))) {
284 nsAutoCString strFileName
;
285 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(sourceURI
);
286 sourceURL
->GetFileName(strFileName
);
288 if (strFileName
.IsEmpty()) return E_FAIL
;
290 NS_UnescapeURL(strFileName
);
291 NS_ConvertUTF8toUTF16
wideFileName(strFileName
);
293 uint32_t nMaxNameLength
= (wideFileName
.Length() * 2) + 2;
294 void* retBuf
= CoTaskMemAlloc(nMaxNameLength
); // freed by caller
295 if (!retBuf
) return STG_E_INSUFFICIENTMEMORY
;
297 ZeroMemory(retBuf
, nMaxNameLength
);
298 memcpy(retBuf
, wideFileName
.get(), wideFileName
.Length() * 2);
299 statstg
->pwcsName
= (LPOLESTR
)retBuf
;
304 statstg
->type
= STGTY_STREAM
;
307 SystemTimeToFileTime((const SYSTEMTIME
*)&st
, (LPFILETIME
)&statstg
->mtime
);
308 statstg
->ctime
= statstg
->atime
= statstg
->mtime
;
310 statstg
->cbSize
.QuadPart
= mChannelData
.Length();
311 statstg
->grfMode
= STGM_READ
;
312 statstg
->grfLocksSupported
= LOCK_ONLYONCE
;
313 statstg
->clsid
= CLSID_NULL
;
318 //-----------------------------------------------------------------------------
319 STDMETHODIMP
nsDataObj::CStreamBase::UnlockRegion(ULARGE_INTEGER nStart
,
320 ULARGE_INTEGER nBytes
,
325 //-----------------------------------------------------------------------------
326 STDMETHODIMP
nsDataObj::CStreamBase::Write(const void* pvBuffer
,
332 //-----------------------------------------------------------------------------
333 HRESULT
nsDataObj::CreateStream(IStream
** outStream
) {
334 NS_ENSURE_TRUE(outStream
, E_INVALIDARG
);
336 nsresult rv
= NS_ERROR_FAILURE
;
337 nsAutoString wideFileName
;
338 nsCOMPtr
<nsIURI
> sourceURI
;
341 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
342 if (FAILED(res
)) return res
;
344 nsDataObj::CStream
* pStream
= new nsDataObj::CStream();
345 NS_ENSURE_TRUE(pStream
, E_OUTOFMEMORY
);
349 // query the requestingPrincipal from the transferable and add it to the new
351 nsCOMPtr
<nsIPrincipal
> requestingPrincipal
=
352 mTransferable
->GetRequestingPrincipal();
353 MOZ_ASSERT(requestingPrincipal
, "can not create channel without a principal");
355 // Note that the cookieJarSettings could be null if the data object is for the
356 // image copy. We will fix this in Bug 1690532.
357 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
=
358 mTransferable
->GetCookieJarSettings();
360 // The referrer is optional.
361 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= mTransferable
->GetReferrerInfo();
363 nsContentPolicyType contentPolicyType
= mTransferable
->GetContentPolicyType();
364 rv
= pStream
->Init(sourceURI
, contentPolicyType
, requestingPrincipal
,
365 cookieJarSettings
, referrerInfo
);
370 *outStream
= pStream
;
375 //-----------------------------------------------------------------------------
376 // AutoCloseEvent implementation
377 nsDataObj::AutoCloseEvent::AutoCloseEvent()
378 : mEvent(::CreateEventW(nullptr, TRUE
, FALSE
, nullptr)) {}
380 bool nsDataObj::AutoCloseEvent::IsInited() const { return !!mEvent
; }
382 void nsDataObj::AutoCloseEvent::Signal() const { ::SetEvent(mEvent
); }
384 DWORD
nsDataObj::AutoCloseEvent::Wait(DWORD aMillisec
) const {
385 return ::WaitForSingleObject(mEvent
, aMillisec
);
388 //-----------------------------------------------------------------------------
389 // AutoSetEvent implementation
390 nsDataObj::AutoSetEvent::AutoSetEvent(NotNull
<AutoCloseEvent
*> aEvent
)
393 nsDataObj::AutoSetEvent::~AutoSetEvent() { Signal(); }
395 void nsDataObj::AutoSetEvent::Signal() const { mEvent
->Signal(); }
397 bool nsDataObj::AutoSetEvent::IsWaiting() const {
398 return mEvent
->Wait(0) == WAIT_TIMEOUT
;
401 //-----------------------------------------------------------------------------
402 // CMemStream implementation
403 Win32SRWLock
nsDataObj::CMemStream::mLock
;
405 //-----------------------------------------------------------------------------
406 nsDataObj::CMemStream::CMemStream(nsHGLOBAL aGlobalMem
, uint32_t aTotalLength
,
407 already_AddRefed
<AutoCloseEvent
> aEvent
)
408 : mGlobalMem(aGlobalMem
), mEvent(aEvent
), mTotalLength(aTotalLength
) {
409 ::CoCreateFreeThreadedMarshaler(this, getter_AddRefs(mMarshaler
));
412 //-----------------------------------------------------------------------------
413 nsDataObj::CMemStream::~CMemStream() {}
415 //-----------------------------------------------------------------------------
417 STDMETHODIMP
nsDataObj::CMemStream::QueryInterface(REFIID refiid
,
419 *ppvResult
= nullptr;
420 if (refiid
== IID_IUnknown
|| refiid
== IID_IStream
||
421 refiid
== IID_IAgileObject
) {
423 } else if (refiid
== IID_IMarshal
&& mMarshaler
) {
424 return mMarshaler
->QueryInterface(refiid
, ppvResult
);
427 if (nullptr != *ppvResult
) {
428 ((LPUNKNOWN
)*ppvResult
)->AddRef();
432 return E_NOINTERFACE
;
435 void nsDataObj::CMemStream::WaitForCompletion() {
437 // We are not waiting for obtaining the icon cache.
440 if (!NS_IsMainThread()) {
441 mEvent
->Wait(INFINITE
);
443 // We should not block the main thread.
446 // mEvent will always be in the signaled state here.
449 //-----------------------------------------------------------------------------
451 STDMETHODIMP
nsDataObj::CMemStream::Read(void* pvBuffer
, ULONG nBytesToRead
,
453 // Wait until the event is signaled.
456 AutoExclusiveLock
lock(mLock
);
457 char* contents
= reinterpret_cast<char*>(GlobalLock(mGlobalMem
.get()));
459 return E_OUTOFMEMORY
;
462 // Bytes left for Windows to read out of our buffer
463 ULONG bytesLeft
= mTotalLength
- mStreamRead
;
464 // Let Windows know what we will hand back, usually this is the entire buffer
465 *nBytesRead
= std::min(bytesLeft
, nBytesToRead
);
466 // Copy the buffer data over
467 memcpy(pvBuffer
, contents
+ mStreamRead
, *nBytesRead
);
468 // Update our bytes read tracking
469 mStreamRead
+= *nBytesRead
;
471 GlobalUnlock(mGlobalMem
.get());
475 //-----------------------------------------------------------------------------
476 STDMETHODIMP
nsDataObj::CMemStream::Stat(STATSTG
* statstg
, DWORD dwFlags
) {
477 if (statstg
== nullptr) return STG_E_INVALIDPOINTER
;
479 memset((void*)statstg
, 0, sizeof(STATSTG
));
481 if (dwFlags
!= STATFLAG_NONAME
) {
482 constexpr size_t kMaxNameLength
= sizeof(wchar_t);
483 void* retBuf
= CoTaskMemAlloc(kMaxNameLength
); // freed by caller
484 if (!retBuf
) return STG_E_INSUFFICIENTMEMORY
;
486 ZeroMemory(retBuf
, kMaxNameLength
);
487 statstg
->pwcsName
= (LPOLESTR
)retBuf
;
492 statstg
->type
= STGTY_STREAM
;
495 SystemTimeToFileTime((const SYSTEMTIME
*)&st
, (LPFILETIME
)&statstg
->mtime
);
496 statstg
->ctime
= statstg
->atime
= statstg
->mtime
;
498 statstg
->cbSize
.QuadPart
= mTotalLength
;
499 statstg
->grfMode
= STGM_READ
;
500 statstg
->grfLocksSupported
= LOCK_ONLYONCE
;
501 statstg
->clsid
= CLSID_NULL
;
510 //-----------------------------------------------------
512 //-----------------------------------------------------
513 nsDataObj::nsDataObj(nsIURI
* uri
)
515 mTransferable(nullptr),
517 mIsInOperation(FALSE
) {
518 mIOThread
= new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS
, "nsDataObj",
519 LazyIdleThread::ManualShutdown
);
520 m_enumFE
= new CEnumFormatEtc();
524 // A URI was obtained, so pass this through to the DataObject
525 // so it can create a SourceURL for CF_HTML flavour
526 uri
->GetSpec(mSourceURL
);
529 //-----------------------------------------------------
531 //-----------------------------------------------------
532 nsDataObj::~nsDataObj() {
533 NS_IF_RELEASE(mTransferable
);
535 mDataFlavors
.Clear();
539 // Free arbitrary system formats
540 for (uint32_t idx
= 0; idx
< mDataEntryList
.Length(); idx
++) {
541 CoTaskMemFree(mDataEntryList
[idx
]->fe
.ptd
);
542 ReleaseStgMedium(&mDataEntryList
[idx
]->stgm
);
543 CoTaskMemFree(mDataEntryList
[idx
]);
547 //-----------------------------------------------------
548 // IUnknown interface methods - see inknown.h for documentation
549 //-----------------------------------------------------
550 STDMETHODIMP
nsDataObj::QueryInterface(REFIID riid
, void** ppv
) {
553 if ((IID_IUnknown
== riid
) || (IID_IDataObject
== riid
)) {
557 } else if (IID_IDataObjectAsyncCapability
== riid
) {
558 *ppv
= static_cast<IDataObjectAsyncCapability
*>(this);
563 return E_NOINTERFACE
;
566 //-----------------------------------------------------
567 STDMETHODIMP_(ULONG
) nsDataObj::AddRef() {
569 NS_LOG_ADDREF(this, m_cRef
, "nsDataObj", sizeof(*this));
571 // When the first reference is taken, hold our own internal reference.
580 class RemoveTempFileHelper final
: public nsIObserver
, public nsINamed
{
582 explicit RemoveTempFileHelper(nsIFile
* aTempFile
) : mTempFile(aTempFile
) {
583 MOZ_ASSERT(mTempFile
);
586 // The attach method is seperate from the constructor as we may be addref-ing
587 // ourself, and we want to be sure someone has a strong reference to us.
589 // We need to listen to both the xpcom shutdown message and our timer, and
590 // fire when the first of either of these two messages is received.
592 rv
= NS_NewTimerWithObserver(getter_AddRefs(mTimer
), this, 500,
593 nsITimer::TYPE_ONE_SHOT
);
594 if (NS_WARN_IF(NS_FAILED(rv
))) {
598 nsCOMPtr
<nsIObserverService
> observerService
=
599 do_GetService("@mozilla.org/observer-service;1");
600 if (NS_WARN_IF(!observerService
)) {
605 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
613 ~RemoveTempFileHelper() {
615 mTempFile
->Remove(false);
619 nsCOMPtr
<nsIFile
> mTempFile
;
620 nsCOMPtr
<nsITimer
> mTimer
;
623 NS_IMPL_ISUPPORTS(RemoveTempFileHelper
, nsIObserver
, nsINamed
);
626 RemoveTempFileHelper::Observe(nsISupports
* aSubject
, const char* aTopic
,
627 const char16_t
* aData
) {
628 // Let's be careful and make sure that we don't die immediately
629 RefPtr
<RemoveTempFileHelper
> grip
= this;
631 // Make sure that we aren't called again by destroying references to ourself.
632 nsCOMPtr
<nsIObserverService
> observerService
=
633 do_GetService("@mozilla.org/observer-service;1");
634 if (observerService
) {
635 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
643 // Remove the tempfile
645 mTempFile
->Remove(false);
652 RemoveTempFileHelper::GetName(nsACString
& aName
) {
653 aName
.AssignLiteral("RemoveTempFileHelper");
658 //-----------------------------------------------------
659 STDMETHODIMP_(ULONG
) nsDataObj::Release() {
662 NS_LOG_RELEASE(this, m_cRef
, "nsDataObj");
664 // If we hold the last reference, submit release of it to the main thread.
665 if (m_cRef
== 1 && mKeepAlive
) {
666 NS_ReleaseOnMainThread("nsDataObj release", mKeepAlive
.forget(), true);
669 if (0 != m_cRef
) return m_cRef
;
671 // We have released our last ref on this object and need to delete the
672 // temp file. External app acting as drop target may still need to open the
673 // temp file. Addref a timer so it can delay deleting file and destroying
675 if (mCachedTempFile
) {
676 RefPtr
<RemoveTempFileHelper
> helper
=
677 new RemoveTempFileHelper(mCachedTempFile
);
678 mCachedTempFile
= nullptr;
682 // In case the destructor ever AddRef/Releases, ensure we don't delete twice
683 // or take mKeepAlive as another reference.
691 //-----------------------------------------------------
692 BOOL
nsDataObj::FormatsMatch(const FORMATETC
& source
,
693 const FORMATETC
& target
) const {
694 if ((source
.cfFormat
== target
.cfFormat
) &&
695 (source
.dwAspect
& target
.dwAspect
) && (source
.tymed
& target
.tymed
)) {
702 //-----------------------------------------------------
703 // IDataObject methods
704 //-----------------------------------------------------
705 STDMETHODIMP
nsDataObj::GetData(LPFORMATETC aFormat
, LPSTGMEDIUM pSTM
) {
706 if (!mTransferable
) return DV_E_FORMATETC
;
708 // Hold an extra reference in case we end up spinning the event loop.
709 RefPtr
<nsDataObj
> keepAliveDuringGetData(this);
713 static CLIPFORMAT fileDescriptorFlavorA
=
714 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
);
715 static CLIPFORMAT fileDescriptorFlavorW
=
716 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
);
717 static CLIPFORMAT uniformResourceLocatorA
=
718 ::RegisterClipboardFormat(CFSTR_INETURLA
);
719 static CLIPFORMAT uniformResourceLocatorW
=
720 ::RegisterClipboardFormat(CFSTR_INETURLW
);
721 static CLIPFORMAT fileFlavor
= ::RegisterClipboardFormat(CFSTR_FILECONTENTS
);
722 static CLIPFORMAT PreferredDropEffect
=
723 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
);
725 // Arbitrary system formats are used for image feedback during drag
726 // and drop. We are responsible for storing these internally during
729 if (LookupArbitraryFormat(aFormat
, &pde
, FALSE
)) {
730 return CopyMediumData(pSTM
, &pde
->stgm
, aFormat
, FALSE
) ? S_OK
734 // Firefox internal formats
738 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
) &&
739 dfInx
< mDataFlavors
.Length()) {
740 nsCString
& df
= mDataFlavors
.ElementAt(dfInx
);
741 if (FormatsMatch(fe
, *aFormat
)) {
742 pSTM
->pUnkForRelease
=
743 nullptr; // caller is responsible for deleting this data
744 CLIPFORMAT format
= aFormat
->cfFormat
;
746 // Someone is asking for plain or unicode text
749 return GetText(df
, *aFormat
, *pSTM
);
751 // Some 3rd party apps that receive drag and drop files from the browser
752 // window require support for this.
754 return GetFile(*aFormat
, *pSTM
);
756 // Someone is asking for an image
759 return GetDib(df
, *aFormat
, *pSTM
);
762 if (format
== fileDescriptorFlavorA
)
763 return GetFileDescriptor(*aFormat
, *pSTM
, false);
764 if (format
== fileDescriptorFlavorW
)
765 return GetFileDescriptor(*aFormat
, *pSTM
, true);
766 if (format
== uniformResourceLocatorA
)
767 return GetUniformResourceLocator(*aFormat
, *pSTM
, false);
768 if (format
== uniformResourceLocatorW
)
769 return GetUniformResourceLocator(*aFormat
, *pSTM
, true);
770 if (format
== fileFlavor
) return GetFileContents(*aFormat
, *pSTM
);
771 if (format
== PreferredDropEffect
)
772 return GetPreferredDropEffect(*aFormat
, *pSTM
);
773 // MOZ_LOG(gWindowsLog, LogLevel::Info,
774 // ("***** nsDataObj::GetData - Unknown format %u\n", format));
775 return GetText(df
, *aFormat
, *pSTM
);
781 return DATA_E_FORMATETC
;
784 //-----------------------------------------------------
785 STDMETHODIMP
nsDataObj::GetDataHere(LPFORMATETC pFE
, LPSTGMEDIUM pSTM
) {
789 //-----------------------------------------------------
790 // Other objects querying to see if we support a
792 //-----------------------------------------------------
793 STDMETHODIMP
nsDataObj::QueryGetData(LPFORMATETC pFE
) {
794 // Arbitrary system formats are used for image feedback during drag
795 // and drop. We are responsible for storing these internally during
798 if (LookupArbitraryFormat(pFE
, &pde
, FALSE
)) return S_OK
;
800 // Firefox internal formats
804 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
)) {
805 if (fe
.cfFormat
== pFE
->cfFormat
) {
812 //-----------------------------------------------------
813 STDMETHODIMP
nsDataObj::GetCanonicalFormatEtc(LPFORMATETC pFEIn
,
814 LPFORMATETC pFEOut
) {
818 //-----------------------------------------------------
819 STDMETHODIMP
nsDataObj::SetData(LPFORMATETC aFormat
, LPSTGMEDIUM aMedium
,
821 // Arbitrary system formats are used for image feedback during drag
822 // and drop. We are responsible for storing these internally during
825 if (LookupArbitraryFormat(aFormat
, &pde
, TRUE
)) {
826 // Release the old data the lookup handed us for this format. This
827 // may have been set in CopyMediumData when we originally stored the
829 if (pde
->stgm
.tymed
) {
830 ReleaseStgMedium(&pde
->stgm
);
831 memset(&pde
->stgm
, 0, sizeof(STGMEDIUM
));
836 // If shouldRel is TRUE, the data object called owns the storage medium
837 // after the call returns. Store the incoming data in our data array for
838 // release when we are destroyed. This is the common case with arbitrary
839 // data from explorer.
840 pde
->stgm
= *aMedium
;
842 // Copy the incoming data into our data array. (AFAICT, this never gets
843 // called with arbitrary formats for drag images.)
844 result
= CopyMediumData(&pde
->stgm
, aMedium
, aFormat
, TRUE
);
846 pde
->fe
.tymed
= pde
->stgm
.tymed
;
848 return result
? S_OK
: DV_E_TYMED
;
851 if (shouldRel
) ReleaseStgMedium(aMedium
);
856 bool nsDataObj::LookupArbitraryFormat(FORMATETC
* aFormat
,
857 LPDATAENTRY
* aDataEntry
,
859 *aDataEntry
= nullptr;
861 if (aFormat
->ptd
!= nullptr) return false;
863 // See if it's already in our list. If so return the data entry.
864 for (uint32_t idx
= 0; idx
< mDataEntryList
.Length(); idx
++) {
865 if (mDataEntryList
[idx
]->fe
.cfFormat
== aFormat
->cfFormat
&&
866 mDataEntryList
[idx
]->fe
.dwAspect
== aFormat
->dwAspect
&&
867 mDataEntryList
[idx
]->fe
.lindex
== aFormat
->lindex
) {
868 if (aAddorUpdate
|| (mDataEntryList
[idx
]->fe
.tymed
& aFormat
->tymed
)) {
869 // If the caller requests we update, or if the
870 // medium type matches, return the entry.
871 *aDataEntry
= mDataEntryList
[idx
];
874 // Medium does not match, not found.
880 if (!aAddorUpdate
) return false;
882 // Add another entry to mDataEntryList
883 LPDATAENTRY dataEntry
= (LPDATAENTRY
)CoTaskMemAlloc(sizeof(DATAENTRY
));
884 if (!dataEntry
) return false;
886 dataEntry
->fe
= *aFormat
;
887 *aDataEntry
= dataEntry
;
888 memset(&dataEntry
->stgm
, 0, sizeof(STGMEDIUM
));
890 // Add this to our IEnumFORMATETC impl. so we can return it when
892 m_enumFE
->AddFormatEtc(aFormat
);
894 // Store a copy internally in the arbitrary formats array.
895 mDataEntryList
.AppendElement(dataEntry
);
900 bool nsDataObj::CopyMediumData(STGMEDIUM
* aMediumDst
, STGMEDIUM
* aMediumSrc
,
901 LPFORMATETC aFormat
, BOOL aSetData
) {
902 STGMEDIUM stgmOut
= *aMediumSrc
;
904 switch (stgmOut
.tymed
) {
906 stgmOut
.pstm
->AddRef();
909 stgmOut
.pstg
->AddRef();
912 if (!aMediumSrc
->pUnkForRelease
) {
914 if (aMediumSrc
->tymed
!= TYMED_HGLOBAL
) return false;
916 OleDuplicateData(aMediumSrc
->hGlobal
, aFormat
->cfFormat
, 0);
917 if (!stgmOut
.hGlobal
) return false;
919 // We are returning this data from LookupArbitraryFormat, indicate to
920 // the shell we hold it and will free it.
921 stgmOut
.pUnkForRelease
= static_cast<IDataObject
*>(this);
929 if (stgmOut
.pUnkForRelease
) stgmOut
.pUnkForRelease
->AddRef();
931 *aMediumDst
= stgmOut
;
936 //-----------------------------------------------------
937 STDMETHODIMP
nsDataObj::EnumFormatEtc(DWORD dwDir
, LPENUMFORMATETC
* ppEnum
) {
940 m_enumFE
->Clone(ppEnum
);
948 if (nullptr == *ppEnum
) return E_FAIL
;
951 // Clone already AddRefed the result so don't addref it again.
955 //-----------------------------------------------------
956 STDMETHODIMP
nsDataObj::DAdvise(LPFORMATETC pFE
, DWORD dwFlags
,
957 LPADVISESINK pIAdviseSink
, DWORD
* pdwConn
) {
958 return OLE_E_ADVISENOTSUPPORTED
;
961 //-----------------------------------------------------
962 STDMETHODIMP
nsDataObj::DUnadvise(DWORD dwConn
) {
963 return OLE_E_ADVISENOTSUPPORTED
;
966 //-----------------------------------------------------
967 STDMETHODIMP
nsDataObj::EnumDAdvise(LPENUMSTATDATA
* ppEnum
) {
968 return OLE_E_ADVISENOTSUPPORTED
;
971 // IDataObjectAsyncCapability methods
972 STDMETHODIMP
nsDataObj::EndOperation(HRESULT hResult
, IBindCtx
* pbcReserved
,
974 mIsInOperation
= FALSE
;
978 STDMETHODIMP
nsDataObj::GetAsyncMode(BOOL
* pfIsOpAsync
) {
979 *pfIsOpAsync
= mIsAsyncMode
;
984 STDMETHODIMP
nsDataObj::InOperation(BOOL
* pfInAsyncOp
) {
985 *pfInAsyncOp
= mIsInOperation
;
990 STDMETHODIMP
nsDataObj::SetAsyncMode(BOOL fDoOpAsync
) {
991 mIsAsyncMode
= fDoOpAsync
;
995 STDMETHODIMP
nsDataObj::StartOperation(IBindCtx
* pbcReserved
) {
996 mIsInOperation
= TRUE
;
1003 // Someone is asking for a bitmap. The data in the transferable will be a
1004 // straight imgIContainer, so just QI it.
1007 nsDataObj::GetDib(const nsACString
& inFlavor
, FORMATETC
& aFormat
,
1009 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1011 mTransferable
->GetTransferData(PromiseFlatCString(inFlavor
).get(),
1012 getter_AddRefs(genericDataWrapper
)))) {
1016 nsCOMPtr
<imgIContainer
> image
= do_QueryInterface(genericDataWrapper
);
1021 nsCOMPtr
<imgITools
> imgTools
=
1022 do_CreateInstance("@mozilla.org/image/tools;1");
1024 nsAutoString
options(u
"bpp=32;"_ns
);
1025 if (aFormat
.cfFormat
== CF_DIBV5
) {
1026 options
.AppendLiteral("version=5");
1028 options
.AppendLiteral("version=3");
1031 nsCOMPtr
<nsIInputStream
> inputStream
;
1032 nsresult rv
= imgTools
->EncodeImage(image
, nsLiteralCString(IMAGE_BMP
),
1033 options
, getter_AddRefs(inputStream
));
1034 if (NS_FAILED(rv
) || !inputStream
) {
1038 nsCOMPtr
<imgIEncoder
> encoder
= do_QueryInterface(inputStream
);
1044 rv
= encoder
->GetImageBufferUsed(&size
);
1045 if (NS_FAILED(rv
) || size
<= BFH_LENGTH
) {
1049 char* src
= nullptr;
1050 rv
= encoder
->GetImageBuffer(&src
);
1051 if (NS_FAILED(rv
) || !src
) {
1055 // We don't want the file header.
1059 HGLOBAL glob
= ::GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, size
);
1064 char* dst
= (char*)::GlobalLock(glob
);
1065 ::CopyMemory(dst
, src
, size
);
1066 ::GlobalUnlock(glob
);
1068 aSTG
.hGlobal
= glob
;
1069 aSTG
.tymed
= TYMED_HGLOBAL
;
1074 // GetFileDescriptor
1078 nsDataObj ::GetFileDescriptor(FORMATETC
& aFE
, STGMEDIUM
& aSTG
,
1082 // How we handle this depends on if we're dealing with an internet
1083 // shortcut, since those are done under the covers.
1084 if (IsFlavourPresent(kFilePromiseMime
) || IsFlavourPresent(kFileMime
)) {
1086 return GetFileDescriptor_IStreamW(aFE
, aSTG
);
1088 return GetFileDescriptor_IStreamA(aFE
, aSTG
);
1089 } else if (IsFlavourPresent(kURLMime
)) {
1091 res
= GetFileDescriptorInternetShortcutW(aFE
, aSTG
);
1093 res
= GetFileDescriptorInternetShortcutA(aFE
, aSTG
);
1095 NS_WARNING("Not yet implemented\n");
1098 } // GetFileDescriptor
1102 nsDataObj ::GetFileContents(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1105 // How we handle this depends on if we're dealing with an internet
1106 // shortcut, since those are done under the covers.
1107 if (IsFlavourPresent(kFilePromiseMime
) || IsFlavourPresent(kFileMime
))
1108 return GetFileContents_IStream(aFE
, aSTG
);
1109 else if (IsFlavourPresent(kURLMime
))
1110 return GetFileContentsInternetShortcut(aFE
, aSTG
);
1112 NS_WARNING("Not yet implemented\n");
1116 } // GetFileContents
1118 // Ensure that the supplied name doesn't have invalid characters.
1119 static void ValidateFilename(nsString
& aFilename
, bool isShortcut
) {
1120 nsCOMPtr
<nsIMIMEService
> mimeService
= do_GetService("@mozilla.org/mime;1");
1121 if (NS_WARN_IF(!mimeService
)) {
1122 aFilename
.Truncate();
1126 uint32_t flags
= nsIMIMEService::VALIDATE_SANITIZE_ONLY
;
1128 flags
|= nsIMIMEService::VALIDATE_ALLOW_INVALID_FILENAMES
;
1131 nsAutoString outFilename
;
1132 mimeService
->ValidateFileNameForSaving(aFilename
, EmptyCString(), flags
,
1134 aFilename
= outFilename
;
1138 // Given a unicode string, convert it to a valid local charset filename
1139 // and append the .url extension to be used for a shortcut file.
1140 // This ensures that we do not cut MBCS characters in the middle.
1142 // It would seem that this is more functionality suited to being in nsIFile.
1144 static bool CreateURLFilenameFromTextA(nsAutoString
& aText
, char* aFilename
) {
1145 if (aText
.IsEmpty()) {
1148 aText
.AppendLiteral(".url");
1149 ValidateFilename(aText
, true);
1150 if (aText
.IsEmpty()) {
1154 // ValidateFilename should already be checking the filename length, but do
1155 // an extra check to verify for the local code page that the converted text
1156 // doesn't go over MAX_PATH and just return false if it does.
1157 char defaultChar
= '_';
1158 int currLen
= WideCharToMultiByte(CP_ACP
, WC_COMPOSITECHECK
| WC_DEFAULTCHAR
,
1159 aText
.get(), -1, aFilename
, MAX_PATH
,
1160 &defaultChar
, nullptr);
1161 return currLen
!= 0;
1164 // Wide character version of CreateURLFilenameFromTextA
1165 static bool CreateURLFilenameFromTextW(nsAutoString
& aText
,
1166 wchar_t* aFilename
) {
1167 if (aText
.IsEmpty()) {
1170 aText
.AppendLiteral(".url");
1171 ValidateFilename(aText
, true);
1172 if (aText
.IsEmpty() || aText
.Length() >= MAX_PATH
) {
1176 wcscpy(&aFilename
[0], aText
.get());
1180 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
1182 static bool GetLocalizedString(const char* aName
, nsAString
& aString
) {
1183 nsCOMPtr
<nsIStringBundleService
> stringService
=
1184 mozilla::components::StringBundle::Service();
1185 if (!stringService
) return false;
1187 nsCOMPtr
<nsIStringBundle
> stringBundle
;
1188 nsresult rv
= stringService
->CreateBundle(PAGEINFO_PROPERTIES
,
1189 getter_AddRefs(stringBundle
));
1190 if (NS_FAILED(rv
)) return false;
1192 rv
= stringBundle
->GetStringFromName(aName
, aString
);
1193 return NS_SUCCEEDED(rv
);
1197 // GetFileDescriptorInternetShortcut
1199 // Create the special format for an internet shortcut and build up the data
1200 // structures the shell is expecting.
1203 nsDataObj ::GetFileDescriptorInternetShortcutA(FORMATETC
& aFE
,
1205 // get the title of the shortcut
1207 if (NS_FAILED(ExtractShortcutTitle(title
))) return E_OUTOFMEMORY
;
1209 HGLOBAL fileGroupDescHandle
=
1210 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORA
));
1211 if (!fileGroupDescHandle
) return E_OUTOFMEMORY
;
1213 LPFILEGROUPDESCRIPTORA fileGroupDescA
=
1214 reinterpret_cast<LPFILEGROUPDESCRIPTORA
>(
1215 ::GlobalLock(fileGroupDescHandle
));
1216 if (!fileGroupDescA
) {
1217 ::GlobalFree(fileGroupDescHandle
);
1218 return E_OUTOFMEMORY
;
1221 // get a valid filename in the following order: 1) from the page title,
1222 // 2) localized string for an untitled page, 3) just use "Untitled.url"
1223 if (!CreateURLFilenameFromTextA(title
, fileGroupDescA
->fgd
[0].cFileName
)) {
1224 nsAutoString untitled
;
1225 if (!GetLocalizedString("noPageTitle", untitled
) ||
1226 !CreateURLFilenameFromTextA(untitled
,
1227 fileGroupDescA
->fgd
[0].cFileName
)) {
1228 strcpy(fileGroupDescA
->fgd
[0].cFileName
, "Untitled.url");
1232 // one file in the file block
1233 fileGroupDescA
->cItems
= 1;
1234 fileGroupDescA
->fgd
[0].dwFlags
= FD_LINKUI
;
1236 ::GlobalUnlock(fileGroupDescHandle
);
1237 aSTG
.hGlobal
= fileGroupDescHandle
;
1238 aSTG
.tymed
= TYMED_HGLOBAL
;
1241 } // GetFileDescriptorInternetShortcutA
1244 nsDataObj ::GetFileDescriptorInternetShortcutW(FORMATETC
& aFE
,
1246 // get the title of the shortcut
1248 if (NS_FAILED(ExtractShortcutTitle(title
))) return E_OUTOFMEMORY
;
1250 HGLOBAL fileGroupDescHandle
=
1251 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORW
));
1252 if (!fileGroupDescHandle
) return E_OUTOFMEMORY
;
1254 LPFILEGROUPDESCRIPTORW fileGroupDescW
=
1255 reinterpret_cast<LPFILEGROUPDESCRIPTORW
>(
1256 ::GlobalLock(fileGroupDescHandle
));
1257 if (!fileGroupDescW
) {
1258 ::GlobalFree(fileGroupDescHandle
);
1259 return E_OUTOFMEMORY
;
1262 // get a valid filename in the following order: 1) from the page title,
1263 // 2) localized string for an untitled page, 3) just use "Untitled.url"
1264 if (!CreateURLFilenameFromTextW(title
, fileGroupDescW
->fgd
[0].cFileName
)) {
1265 nsAutoString untitled
;
1266 if (!GetLocalizedString("noPageTitle", untitled
) ||
1267 !CreateURLFilenameFromTextW(untitled
,
1268 fileGroupDescW
->fgd
[0].cFileName
)) {
1269 wcscpy(fileGroupDescW
->fgd
[0].cFileName
, L
"Untitled.url");
1273 // one file in the file block
1274 fileGroupDescW
->cItems
= 1;
1275 fileGroupDescW
->fgd
[0].dwFlags
= FD_LINKUI
;
1277 ::GlobalUnlock(fileGroupDescHandle
);
1278 aSTG
.hGlobal
= fileGroupDescHandle
;
1279 aSTG
.tymed
= TYMED_HGLOBAL
;
1282 } // GetFileDescriptorInternetShortcutW
1285 // GetFileContentsInternetShortcut
1287 // Create the special format for an internet shortcut and build up the data
1288 // structures the shell is expecting.
1291 nsDataObj ::GetFileContentsInternetShortcut(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1292 static const char* kShellIconPref
= "browser.shell.shortcutFavicons";
1294 if (NS_FAILED(ExtractShortcutURL(url
))) return E_OUTOFMEMORY
;
1296 nsCOMPtr
<nsIURI
> aUri
;
1297 nsresult rv
= NS_NewURI(getter_AddRefs(aUri
), url
);
1298 if (NS_FAILED(rv
)) {
1302 nsAutoCString asciiUrl
;
1303 rv
= aUri
->GetAsciiSpec(asciiUrl
);
1304 if (NS_FAILED(rv
)) {
1308 RefPtr
<AutoCloseEvent
> event
;
1310 const char* shortcutFormatStr
;
1312 nsCString asciiPath
;
1313 if (!Preferences::GetBool(kShellIconPref
, true)) {
1314 shortcutFormatStr
= "[InternetShortcut]\r\nURL=%s\r\n";
1315 const int formatLen
= strlen(shortcutFormatStr
) - 2; // don't include %s
1316 totalLen
= formatLen
+ asciiUrl
.Length(); // don't include null character
1318 nsCOMPtr
<nsIFile
> icoFile
;
1320 nsAutoString aUriHash
;
1322 event
= new AutoCloseEvent();
1323 if (!event
->IsInited()) {
1327 RefPtr
<AutoSetEvent
> e
= new AutoSetEvent(WrapNotNull(event
));
1328 mozilla::widget::FaviconHelper::ObtainCachedIconFile(
1329 aUri
, aUriHash
, mIOThread
, true,
1330 NS_NewRunnableFunction(
1331 "FaviconHelper::RefreshDesktop", [e
= std::move(e
)] {
1332 if (e
->IsWaiting()) {
1333 // Unblock IStream:::Read.
1336 // We could not wait until the favicon was available. We have
1337 // to refresh to refect the favicon.
1338 SendNotifyMessage(HWND_BROADCAST
, WM_SETTINGCHANGE
,
1339 SPI_SETNONCLIENTMETRICS
, 0);
1343 rv
= mozilla::widget::FaviconHelper::GetOutputIconPath(aUri
, icoFile
, true);
1344 NS_ENSURE_SUCCESS(rv
, E_FAIL
);
1346 rv
= icoFile
->GetPath(path
);
1347 NS_ENSURE_SUCCESS(rv
, E_FAIL
);
1349 if (IsAsciiNullTerminated(static_cast<const char16_t
*>(path
.get()))) {
1350 LossyCopyUTF16toASCII(path
, asciiPath
);
1352 "[InternetShortcut]\r\nURL=%s\r\n"
1353 "IDList=\r\nHotKey=0\r\nIconFile=%s\r\n"
1357 WideCharToMultiByte(CP_UTF7
, 0, char16ptr_t(path
.BeginReading()),
1358 path
.Length(), nullptr, 0, nullptr, nullptr);
1359 NS_ENSURE_TRUE(len
> 0, E_FAIL
);
1360 asciiPath
.SetLength(len
);
1361 WideCharToMultiByte(CP_UTF7
, 0, char16ptr_t(path
.BeginReading()),
1362 path
.Length(), asciiPath
.BeginWriting(), len
, nullptr,
1365 "[InternetShortcut]\r\nURL=%s\r\n"
1366 "IDList=\r\nHotKey=0\r\nIconIndex=0\r\n"
1367 "[InternetShortcut.W]\r\nIconFile=%s\r\n";
1369 const int formatLen
= strlen(shortcutFormatStr
) - 2 * 2; // no %s twice
1370 totalLen
= formatLen
+ asciiUrl
.Length() +
1371 asciiPath
.Length(); // we don't want a null character on the end
1374 // create a global memory area and build up the file contents w/in it
1375 nsAutoGlobalMem
globalMem(nsHGLOBAL(::GlobalAlloc(GMEM_SHARE
, totalLen
)));
1376 if (!globalMem
) return E_OUTOFMEMORY
;
1378 char* contents
= reinterpret_cast<char*>(::GlobalLock(globalMem
.get()));
1380 return E_OUTOFMEMORY
;
1383 // NOTE: we intentionally use the Microsoft version of snprintf here because
1385 // terminate strings which reach the maximum size of the buffer. Since we know
1386 // that the formatted length here is totalLen, this call to _snprintf will
1387 // format the string into the buffer without appending the null character.
1389 if (!Preferences::GetBool(kShellIconPref
, true)) {
1390 _snprintf(contents
, totalLen
, shortcutFormatStr
, asciiUrl
.get());
1392 _snprintf(contents
, totalLen
, shortcutFormatStr
, asciiUrl
.get(),
1396 ::GlobalUnlock(globalMem
.get());
1398 if (aFE
.tymed
& TYMED_ISTREAM
) {
1399 if (!mIsInOperation
) {
1400 // The drop target didn't initiate an async operation.
1401 // We can't block CMemStream::Read.
1404 RefPtr
<IStream
> stream
=
1405 new CMemStream(globalMem
.disown(), totalLen
, event
.forget());
1406 stream
.forget(&aSTG
.pstm
);
1407 aSTG
.tymed
= TYMED_ISTREAM
;
1409 if (event
&& event
->IsInited()) {
1410 event
->Signal(); // We can't block reading the global memory
1412 aSTG
.hGlobal
= globalMem
.disown();
1413 aSTG
.tymed
= TYMED_HGLOBAL
;
1417 } // GetFileContentsInternetShortcut
1419 // check if specified flavour is present in the transferable
1420 bool nsDataObj ::IsFlavourPresent(const char* inFlavour
) {
1421 bool retval
= false;
1422 NS_ENSURE_TRUE(mTransferable
, false);
1424 // get the list of flavors available in the transferable
1425 nsTArray
<nsCString
> flavors
;
1426 nsresult rv
= mTransferable
->FlavorsTransferableCanExport(flavors
);
1427 NS_ENSURE_SUCCESS(rv
, false);
1429 // try to find requested flavour
1430 for (uint32_t i
= 0; i
< flavors
.Length(); ++i
) {
1431 if (flavors
[i
].Equals(inFlavour
)) {
1432 retval
= true; // found it!
1435 } // for each flavor
1440 HRESULT
nsDataObj::GetPreferredDropEffect(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1442 aSTG
.tymed
= TYMED_HGLOBAL
;
1443 aSTG
.pUnkForRelease
= nullptr;
1444 HGLOBAL hGlobalMemory
= nullptr;
1445 hGlobalMemory
= ::GlobalAlloc(GMEM_MOVEABLE
, sizeof(DWORD
));
1446 if (hGlobalMemory
) {
1447 DWORD
* pdw
= (DWORD
*)GlobalLock(hGlobalMemory
);
1448 // The PreferredDropEffect clipboard format is only registered if a
1449 // drag/drop of an image happens from Mozilla to the desktop. We want its
1450 // value to be DROPEFFECT_MOVE in that case so that the file is moved from
1451 // the temporary location, not copied. This value should, ideally, be set on
1452 // the data object via SetData() but our IDataObject implementation doesn't
1453 // implement SetData. It adds data to the data object lazily only when the
1454 // drop target asks for it.
1455 *pdw
= (DWORD
)DROPEFFECT_MOVE
;
1456 GlobalUnlock(hGlobalMemory
);
1458 res
= E_OUTOFMEMORY
;
1460 aSTG
.hGlobal
= hGlobalMemory
;
1464 //-----------------------------------------------------
1465 HRESULT
nsDataObj::GetText(const nsACString
& aDataFlavor
, FORMATETC
& aFE
,
1467 void* data
= nullptr;
1469 const nsPromiseFlatCString
& flavorStr
= PromiseFlatCString(aDataFlavor
);
1471 // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
1472 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1473 nsresult rv
= mTransferable
->GetTransferData(
1474 flavorStr
.get(), getter_AddRefs(genericDataWrapper
));
1475 if (NS_FAILED(rv
) || !genericDataWrapper
) {
1480 nsPrimitiveHelpers::CreateDataFromPrimitive(
1481 nsDependentCString(flavorStr
.get()), genericDataWrapper
, &data
, &len
);
1482 if (!data
) return E_FAIL
;
1484 HGLOBAL hGlobalMemory
= nullptr;
1486 aSTG
.tymed
= TYMED_HGLOBAL
;
1487 aSTG
.pUnkForRelease
= nullptr;
1489 // We play games under the hood and advertise flavors that we know we
1490 // can support, only they require a bit of conversion or munging of the data.
1493 // The transferable gives us data that is null-terminated, but this isn't
1494 // reflected in the |len| parameter. Windoze apps expect this null to be there
1495 // so bump our data buffer by the appropriate size to account for the null
1496 // (one char for CF_TEXT, one char16_t for CF_UNICODETEXT).
1497 DWORD allocLen
= (DWORD
)len
;
1498 if (aFE
.cfFormat
== CF_TEXT
) {
1499 // Someone is asking for text/plain; convert the unicode (assuming it's
1500 // present) to text with the correct platform encoding.
1501 size_t bufferSize
= sizeof(char) * (len
+ 2);
1502 char* plainTextData
= static_cast<char*>(moz_xmalloc(bufferSize
));
1503 char16_t
* castedUnicode
= reinterpret_cast<char16_t
*>(data
);
1504 int32_t plainTextLen
=
1505 WideCharToMultiByte(CP_ACP
, 0, (LPCWSTR
)castedUnicode
, len
/ 2 + 1,
1506 plainTextData
, bufferSize
, NULL
, NULL
);
1507 // replace the unicode data with our plaintext data. Recall that
1508 // |plainTextLen| doesn't include the null in the length.
1511 data
= plainTextData
;
1512 allocLen
= plainTextLen
;
1514 free(plainTextData
);
1515 NS_WARNING("Oh no, couldn't convert unicode to plain text");
1518 } else if (aFE
.cfFormat
== nsClipboard::GetHtmlClipboardFormat()) {
1519 // Someone is asking for win32's HTML flavor. Convert our html fragment
1520 // from unicode to UTF-8 then put it into a format specified by msft.
1521 NS_ConvertUTF16toUTF8
converter(reinterpret_cast<char16_t
*>(data
));
1522 char* utf8HTML
= nullptr;
1524 BuildPlatformHTML(converter
.get(), &utf8HTML
); // null terminates
1527 if (NS_SUCCEEDED(rv
) && utf8HTML
) {
1528 // replace the unicode data with our HTML data. Don't forget the null.
1530 allocLen
= strlen(utf8HTML
) + sizeof(char);
1532 NS_WARNING("Oh no, couldn't convert to HTML");
1535 } else if (aFE
.cfFormat
!= nsClipboard::GetCustomClipboardFormat()) {
1536 // we assume that any data that isn't caught above is unicode. This may
1537 // be an erroneous assumption, but is true so far.
1538 allocLen
+= sizeof(char16_t
);
1541 hGlobalMemory
= (HGLOBAL
)GlobalAlloc(GMEM_MOVEABLE
, allocLen
);
1543 // Copy text to Global Memory Area
1544 if (hGlobalMemory
) {
1545 char* dest
= reinterpret_cast<char*>(GlobalLock(hGlobalMemory
));
1546 char* source
= reinterpret_cast<char*>(data
);
1547 memcpy(dest
, source
, allocLen
); // copies the null as well
1548 GlobalUnlock(hGlobalMemory
);
1550 aSTG
.hGlobal
= hGlobalMemory
;
1552 // Now, delete the memory that was created by CreateDataFromPrimitive (or our
1559 //-----------------------------------------------------
1560 HRESULT
nsDataObj::GetFile(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1565 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
) &&
1566 dfInx
< mDataFlavors
.Length()) {
1567 if (mDataFlavors
[dfInx
].EqualsLiteral(kNativeImageMime
))
1568 return DropImage(aFE
, aSTG
);
1569 if (mDataFlavors
[dfInx
].EqualsLiteral(kFileMime
))
1570 return DropFile(aFE
, aSTG
);
1571 if (mDataFlavors
[dfInx
].EqualsLiteral(kFilePromiseMime
))
1572 return DropTempFile(aFE
, aSTG
);
1578 HRESULT
nsDataObj::DropFile(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1580 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1582 if (NS_FAILED(mTransferable
->GetTransferData(
1583 kFileMime
, getter_AddRefs(genericDataWrapper
)))) {
1586 nsCOMPtr
<nsIFile
> file(do_QueryInterface(genericDataWrapper
));
1587 if (!file
) return E_FAIL
;
1589 aSTG
.tymed
= TYMED_HGLOBAL
;
1590 aSTG
.pUnkForRelease
= nullptr;
1593 rv
= file
->GetPath(path
);
1594 if (NS_FAILED(rv
)) return E_FAIL
;
1596 uint32_t allocLen
= path
.Length() + 2;
1597 HGLOBAL hGlobalMemory
= nullptr;
1600 hGlobalMemory
= GlobalAlloc(GMEM_MOVEABLE
,
1601 sizeof(DROPFILES
) + allocLen
* sizeof(char16_t
));
1602 if (!hGlobalMemory
) return E_FAIL
;
1604 DROPFILES
* pDropFile
= (DROPFILES
*)GlobalLock(hGlobalMemory
);
1606 // First, populate the drop file structure
1607 pDropFile
->pFiles
= sizeof(DROPFILES
); // Offset to start of file name string
1609 pDropFile
->pt
.x
= 0;
1610 pDropFile
->pt
.y
= 0;
1611 pDropFile
->fWide
= TRUE
;
1613 // Copy the filename right after the DROPFILES structure
1614 dest
= (char16_t
*)(((char*)pDropFile
) + pDropFile
->pFiles
);
1615 memcpy(dest
, path
.get(), (allocLen
- 1) * sizeof(char16_t
));
1617 // Two null characters are needed at the end of the file name.
1618 // Lookup the CF_HDROP shell clipboard format for more info.
1619 // Add the second null character right after the first one.
1620 dest
[allocLen
- 1] = L
'\0';
1622 GlobalUnlock(hGlobalMemory
);
1624 aSTG
.hGlobal
= hGlobalMemory
;
1629 HRESULT
nsDataObj::DropImage(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1631 if (!mCachedTempFile
) {
1632 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1634 if (NS_FAILED(mTransferable
->GetTransferData(
1635 kNativeImageMime
, getter_AddRefs(genericDataWrapper
)))) {
1638 nsCOMPtr
<imgIContainer
> image(do_QueryInterface(genericDataWrapper
));
1639 if (!image
) return E_FAIL
;
1641 nsCOMPtr
<imgITools
> imgTools
=
1642 do_CreateInstance("@mozilla.org/image/tools;1");
1643 nsCOMPtr
<nsIInputStream
> inputStream
;
1644 rv
= imgTools
->EncodeImage(image
, nsLiteralCString(IMAGE_BMP
),
1645 u
"bpp=32;version=3"_ns
,
1646 getter_AddRefs(inputStream
));
1647 if (NS_FAILED(rv
) || !inputStream
) {
1651 nsCOMPtr
<imgIEncoder
> encoder
= do_QueryInterface(inputStream
);
1657 rv
= encoder
->GetImageBufferUsed(&size
);
1658 if (NS_FAILED(rv
)) {
1662 char* src
= nullptr;
1663 rv
= encoder
->GetImageBuffer(&src
);
1664 if (NS_FAILED(rv
) || !src
) {
1668 // Save the bitmap to a temporary location.
1669 nsCOMPtr
<nsIFile
> dropFile
;
1670 rv
= NS_GetSpecialDirectory(NS_OS_TEMP_DIR
, getter_AddRefs(dropFile
));
1675 // Filename must be random so as not to confuse apps like
1676 // Photoshop which handle multiple drags into a single window.
1679 NS_MakeRandomString(buf
, 8);
1680 memcpy(buf
+ 8, ".bmp", 5);
1681 filename
.Append(nsDependentCString(buf
, 12));
1682 dropFile
->AppendNative(filename
);
1683 rv
= dropFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0660);
1684 if (NS_FAILED(rv
)) {
1688 // Cache the temp file so we can delete it later and so
1689 // it doesn't get recreated over and over on multiple calls
1690 // which does occur from windows shell.
1691 dropFile
->Clone(getter_AddRefs(mCachedTempFile
));
1693 // Write the data to disk.
1694 nsCOMPtr
<nsIOutputStream
> outStream
;
1695 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outStream
), dropFile
);
1696 if (NS_FAILED(rv
)) {
1700 uint32_t written
= 0;
1701 rv
= outStream
->Write(src
, size
, &written
);
1702 if (NS_FAILED(rv
) || written
!= size
) {
1709 // Pass the file name back to the drop target so that it can access the file.
1711 rv
= mCachedTempFile
->GetPath(path
);
1712 if (NS_FAILED(rv
)) return E_FAIL
;
1714 // Two null characters are needed to terminate the file name list.
1715 HGLOBAL hGlobalMemory
= nullptr;
1717 uint32_t allocLen
= path
.Length() + 2;
1719 aSTG
.tymed
= TYMED_HGLOBAL
;
1720 aSTG
.pUnkForRelease
= nullptr;
1722 hGlobalMemory
= GlobalAlloc(GMEM_MOVEABLE
,
1723 sizeof(DROPFILES
) + allocLen
* sizeof(char16_t
));
1724 if (!hGlobalMemory
) return E_FAIL
;
1726 DROPFILES
* pDropFile
= (DROPFILES
*)GlobalLock(hGlobalMemory
);
1728 // First, populate the drop file structure.
1730 sizeof(DROPFILES
); // Offset to start of file name char array.
1732 pDropFile
->pt
.x
= 0;
1733 pDropFile
->pt
.y
= 0;
1734 pDropFile
->fWide
= TRUE
;
1736 // Copy the filename right after the DROPFILES structure.
1737 char16_t
* dest
= (char16_t
*)(((char*)pDropFile
) + pDropFile
->pFiles
);
1738 memcpy(dest
, path
.get(),
1740 sizeof(char16_t
)); // Copies the null character in path as well.
1742 // Two null characters are needed at the end of the file name.
1743 // Lookup the CF_HDROP shell clipboard format for more info.
1744 // Add the second null character right after the first one.
1745 dest
[allocLen
- 1] = L
'\0';
1747 GlobalUnlock(hGlobalMemory
);
1749 aSTG
.hGlobal
= hGlobalMemory
;
1754 HRESULT
nsDataObj::DropTempFile(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1756 if (!mCachedTempFile
) {
1757 // Tempfile will need a temporary location.
1758 nsCOMPtr
<nsIFile
> dropFile
;
1759 rv
= NS_GetSpecialDirectory(NS_OS_TEMP_DIR
, getter_AddRefs(dropFile
));
1760 if (!dropFile
) return E_FAIL
;
1762 // Filename must be random
1764 nsAutoString wideFileName
;
1765 nsCOMPtr
<nsIURI
> sourceURI
;
1767 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
1768 if (FAILED(res
)) return res
;
1769 NS_CopyUnicodeToNative(wideFileName
, filename
);
1771 dropFile
->AppendNative(filename
);
1772 rv
= dropFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0660);
1773 if (NS_FAILED(rv
)) return E_FAIL
;
1775 // Cache the temp file so we can delete it later and so
1776 // it doesn't get recreated over and over on multiple calls
1777 // which does occur from windows shell.
1778 dropFile
->Clone(getter_AddRefs(mCachedTempFile
));
1780 // Write the data to disk.
1781 nsCOMPtr
<nsIOutputStream
> outStream
;
1782 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outStream
), dropFile
);
1783 if (NS_FAILED(rv
)) return E_FAIL
;
1785 IStream
* pStream
= nullptr;
1786 nsDataObj::CreateStream(&pStream
);
1787 NS_ENSURE_TRUE(pStream
, E_FAIL
);
1790 ULONG readCount
= 0;
1791 uint32_t writeCount
= 0;
1793 HRESULT hres
= pStream
->Read(buffer
, sizeof(buffer
), &readCount
);
1794 if (FAILED(hres
)) return E_FAIL
;
1795 if (readCount
== 0) break;
1796 rv
= outStream
->Write(buffer
, readCount
, &writeCount
);
1797 if (NS_FAILED(rv
)) return E_FAIL
;
1803 // Pass the file name back to the drop target so that it can access the file.
1805 rv
= mCachedTempFile
->GetPath(path
);
1806 if (NS_FAILED(rv
)) return E_FAIL
;
1808 uint32_t allocLen
= path
.Length() + 2;
1810 // Two null characters are needed to terminate the file name list.
1811 HGLOBAL hGlobalMemory
= nullptr;
1813 aSTG
.tymed
= TYMED_HGLOBAL
;
1814 aSTG
.pUnkForRelease
= nullptr;
1816 hGlobalMemory
= GlobalAlloc(GMEM_MOVEABLE
,
1817 sizeof(DROPFILES
) + allocLen
* sizeof(char16_t
));
1818 if (!hGlobalMemory
) return E_FAIL
;
1820 DROPFILES
* pDropFile
= (DROPFILES
*)GlobalLock(hGlobalMemory
);
1822 // First, populate the drop file structure.
1824 sizeof(DROPFILES
); // Offset to start of file name char array.
1826 pDropFile
->pt
.x
= 0;
1827 pDropFile
->pt
.y
= 0;
1828 pDropFile
->fWide
= TRUE
;
1830 // Copy the filename right after the DROPFILES structure.
1831 char16_t
* dest
= (char16_t
*)(((char*)pDropFile
) + pDropFile
->pFiles
);
1832 memcpy(dest
, path
.get(),
1834 sizeof(char16_t
)); // Copies the null character in path as well.
1836 // Two null characters are needed at the end of the file name.
1837 // Lookup the CF_HDROP shell clipboard format for more info.
1838 // Add the second null character right after the first one.
1839 dest
[allocLen
- 1] = L
'\0';
1841 GlobalUnlock(hGlobalMemory
);
1843 aSTG
.hGlobal
= hGlobalMemory
;
1848 //-----------------------------------------------------
1849 // Registers the DataFlavor/FE pair.
1850 //-----------------------------------------------------
1851 void nsDataObj::AddDataFlavor(const char* aDataFlavor
, LPFORMATETC aFE
) {
1852 // These two lists are the mapping to and from data flavors and FEs.
1853 // Later, OLE will tell us it needs a certain type of FORMATETC (text,
1854 // unicode, etc) unicode, etc), so we will look up the data flavor that
1855 // corresponds to the FE and then ask the transferable for that type of data.
1856 mDataFlavors
.AppendElement(aDataFlavor
);
1857 m_enumFE
->AddFormatEtc(aFE
);
1860 //-----------------------------------------------------
1861 // Sets the transferable object
1862 //-----------------------------------------------------
1863 void nsDataObj::SetTransferable(nsITransferable
* aTransferable
) {
1864 NS_IF_RELEASE(mTransferable
);
1866 mTransferable
= aTransferable
;
1867 if (nullptr == mTransferable
) {
1871 NS_ADDREF(mTransferable
);
1879 // Roots around in the transferable for the appropriate flavor that indicates
1880 // a url and pulls out the url portion of the data. Used mostly for creating
1881 // internet shortcuts on the desktop. The url flavor is of the format:
1883 // <url> <linefeed> <page title>
1885 nsresult
nsDataObj ::ExtractShortcutURL(nsString
& outURL
) {
1886 NS_ASSERTION(mTransferable
, "We don't have a good transferable");
1887 nsresult rv
= NS_ERROR_FAILURE
;
1889 nsCOMPtr
<nsISupports
> genericURL
;
1890 if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1891 kURLMime
, getter_AddRefs(genericURL
)))) {
1892 nsCOMPtr
<nsISupportsString
> urlObject(do_QueryInterface(genericURL
));
1895 urlObject
->GetData(url
);
1898 // find the first linefeed in the data, that's where the url ends. trunc
1899 // the result string at that point.
1900 int32_t lineIndex
= outURL
.FindChar('\n');
1901 NS_ASSERTION(lineIndex
> 0,
1902 "Format for url flavor is <url> <linefeed> <page title>");
1903 if (lineIndex
> 0) {
1904 outURL
.Truncate(lineIndex
);
1908 } else if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1909 kURLDataMime
, getter_AddRefs(genericURL
))) ||
1910 NS_SUCCEEDED(mTransferable
->GetTransferData(
1911 kURLPrivateMime
, getter_AddRefs(genericURL
)))) {
1912 nsCOMPtr
<nsISupportsString
> urlObject(do_QueryInterface(genericURL
));
1915 urlObject
->GetData(url
);
1921 } // if found flavor
1925 } // ExtractShortcutURL
1928 // ExtractShortcutTitle
1930 // Roots around in the transferable for the appropriate flavor that indicates
1931 // a url and pulls out the title portion of the data. Used mostly for creating
1932 // internet shortcuts on the desktop. The url flavor is of the format:
1934 // <url> <linefeed> <page title>
1936 nsresult
nsDataObj ::ExtractShortcutTitle(nsString
& outTitle
) {
1937 NS_ASSERTION(mTransferable
, "We'd don't have a good transferable");
1938 nsresult rv
= NS_ERROR_FAILURE
;
1940 nsCOMPtr
<nsISupports
> genericURL
;
1941 if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1942 kURLMime
, getter_AddRefs(genericURL
)))) {
1943 nsCOMPtr
<nsISupportsString
> urlObject(do_QueryInterface(genericURL
));
1946 urlObject
->GetData(url
);
1948 // find the first linefeed in the data, that's where the url ends. we want
1949 // everything after that linefeed. FindChar() returns -1 if we can't find
1950 int32_t lineIndex
= url
.FindChar('\n');
1951 NS_ASSERTION(lineIndex
!= -1,
1952 "Format for url flavor is <url> <linefeed> <page title>");
1953 if (lineIndex
!= -1) {
1954 url
.Mid(outTitle
, lineIndex
+ 1, url
.Length() - (lineIndex
+ 1));
1958 } // if found flavor
1962 } // ExtractShortcutTitle
1965 // BuildPlatformHTML
1967 // Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
1968 // header information on it. This will null-terminate |outPlatformHTML|. See
1969 // https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
1972 // We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
1973 // or <BODY> tags). We'll wrap the fragment with them to make other apps
1976 nsresult
nsDataObj ::BuildPlatformHTML(const char* inOurHTML
,
1977 char** outPlatformHTML
) {
1978 *outPlatformHTML
= nullptr;
1979 nsDependentCString
inHTMLString(inOurHTML
);
1981 // Do we already have mSourceURL from a drag?
1982 if (mSourceURL
.IsEmpty()) {
1984 ExtractShortcutURL(url
);
1986 AppendUTF16toUTF8(url
, mSourceURL
);
1989 constexpr auto kStartHTMLPrefix
= "Version:0.9\r\nStartHTML:"_ns
;
1990 constexpr auto kEndHTMLPrefix
= "\r\nEndHTML:"_ns
;
1991 constexpr auto kStartFragPrefix
= "\r\nStartFragment:"_ns
;
1992 constexpr auto kEndFragPrefix
= "\r\nEndFragment:"_ns
;
1993 constexpr auto kStartSourceURLPrefix
= "\r\nSourceURL:"_ns
;
1994 constexpr auto kEndFragTrailer
= "\r\n"_ns
;
1996 // The CF_HTML's size is embedded in the fragment, in such a way that the
1997 // number of digits in the size is part of the size itself. While it _is_
1998 // technically possible to compute the necessary size of the size-field
1999 // precisely -- by trial and error, if nothing else -- it's simpler just to
2000 // pick a rough but generous estimate and zero-pad it. (Zero-padding is
2001 // explicitly permitted by the format definition.)
2003 // Originally, in 2001, the "rough but generous estimate" was 8 digits. While
2004 // a maximum size of (10**9 - 1) bytes probably would have covered all
2005 // possible use-cases at the time, it's somewhat more likely to overflow
2006 // nowadays. Nonetheless, for the sake of backwards compatibility with any
2007 // misbehaving consumers of our existing CF_HTML output, we retain exactly
2008 // that padding for (most) fragments where it suffices. (No such misbehaving
2009 // consumers are actually known, so this is arguably paranoia.)
2011 // It is now 2022. A padding size of 16 will cover up to about 8.8 petabytes,
2012 // which should be enough for at least the next few years or so.
2013 const size_t numberLength
= inHTMLString
.Length() < 9999'0000 ? 8 : 16;
2015 const size_t sourceURLLength
= mSourceURL
.Length();
2017 const size_t fixedHeaderLen
=
2018 kStartHTMLPrefix
.Length() + kEndHTMLPrefix
.Length() +
2019 kStartFragPrefix
.Length() + kEndFragPrefix
.Length() +
2020 kEndFragTrailer
.Length() + (4 * numberLength
);
2022 const size_t totalHeaderLen
=
2023 fixedHeaderLen
+ (sourceURLLength
> 0
2024 ? kStartSourceURLPrefix
.Length() + sourceURLLength
2027 constexpr auto kHeaderString
= "<html><body>\r\n<!--StartFragment-->"_ns
;
2028 constexpr auto kTrailingString
=
2029 "<!--EndFragment-->\r\n"
2033 // calculate the offsets
2034 size_t startHTMLOffset
= totalHeaderLen
;
2035 size_t startFragOffset
= startHTMLOffset
+ kHeaderString
.Length();
2037 size_t endFragOffset
= startFragOffset
+ inHTMLString
.Length();
2038 size_t endHTMLOffset
= endFragOffset
+ kTrailingString
.Length();
2040 // now build the final version
2041 nsCString clipboardString
;
2042 clipboardString
.SetCapacity(endHTMLOffset
);
2044 const int numberLengthInt
= static_cast<int>(numberLength
);
2045 clipboardString
.Append(kStartHTMLPrefix
);
2046 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, startHTMLOffset
);
2048 clipboardString
.Append(kEndHTMLPrefix
);
2049 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, endHTMLOffset
);
2051 clipboardString
.Append(kStartFragPrefix
);
2052 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, startFragOffset
);
2054 clipboardString
.Append(kEndFragPrefix
);
2055 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, endFragOffset
);
2057 if (sourceURLLength
> 0) {
2058 clipboardString
.Append(kStartSourceURLPrefix
);
2059 clipboardString
.Append(mSourceURL
);
2062 clipboardString
.Append(kEndFragTrailer
);
2064 // Assert that the positional values were correct as we pass by their
2065 // corresponding positions.
2066 MOZ_ASSERT(clipboardString
.Length() == startHTMLOffset
);
2067 clipboardString
.Append(kHeaderString
);
2068 MOZ_ASSERT(clipboardString
.Length() == startFragOffset
);
2069 clipboardString
.Append(inHTMLString
);
2070 MOZ_ASSERT(clipboardString
.Length() == endFragOffset
);
2071 clipboardString
.Append(kTrailingString
);
2072 MOZ_ASSERT(clipboardString
.Length() == endHTMLOffset
);
2074 *outPlatformHTML
= ToNewCString(clipboardString
, mozilla::fallible
);
2075 if (!*outPlatformHTML
) return NS_ERROR_OUT_OF_MEMORY
;
2081 nsDataObj ::GetUniformResourceLocator(FORMATETC
& aFE
, STGMEDIUM
& aSTG
,
2084 if (IsFlavourPresent(kURLMime
)) {
2086 res
= ExtractUniformResourceLocatorW(aFE
, aSTG
);
2088 res
= ExtractUniformResourceLocatorA(aFE
, aSTG
);
2090 NS_WARNING("Not yet implemented\n");
2095 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2096 HRESULT result
= S_OK
;
2099 if (NS_FAILED(ExtractShortcutURL(url
))) return E_OUTOFMEMORY
;
2101 NS_LossyConvertUTF16toASCII
asciiUrl(url
);
2102 const int totalLen
= asciiUrl
.Length() + 1;
2103 HGLOBAL hGlobalMemory
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, totalLen
);
2104 if (!hGlobalMemory
) return E_OUTOFMEMORY
;
2106 char* contents
= reinterpret_cast<char*>(GlobalLock(hGlobalMemory
));
2108 GlobalFree(hGlobalMemory
);
2109 return E_OUTOFMEMORY
;
2112 strcpy(contents
, asciiUrl
.get());
2113 GlobalUnlock(hGlobalMemory
);
2114 aSTG
.hGlobal
= hGlobalMemory
;
2115 aSTG
.tymed
= TYMED_HGLOBAL
;
2121 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2122 HRESULT result
= S_OK
;
2125 if (NS_FAILED(ExtractShortcutURL(url
))) return E_OUTOFMEMORY
;
2127 const int totalLen
= (url
.Length() + 1) * sizeof(char16_t
);
2128 HGLOBAL hGlobalMemory
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, totalLen
);
2129 if (!hGlobalMemory
) return E_OUTOFMEMORY
;
2131 wchar_t* contents
= reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory
));
2133 GlobalFree(hGlobalMemory
);
2134 return E_OUTOFMEMORY
;
2137 wcscpy(contents
, url
.get());
2138 GlobalUnlock(hGlobalMemory
);
2139 aSTG
.hGlobal
= hGlobalMemory
;
2140 aSTG
.tymed
= TYMED_HGLOBAL
;
2145 // Gets the filename from the kFilePromiseURLMime flavour
2146 HRESULT
nsDataObj::GetDownloadDetails(nsIURI
** aSourceURI
,
2147 nsAString
& aFilename
) {
2148 *aSourceURI
= nullptr;
2150 NS_ENSURE_TRUE(mTransferable
, E_FAIL
);
2152 // get the URI from the kFilePromiseURLMime flavor
2153 nsCOMPtr
<nsISupports
> urlPrimitive
;
2154 nsresult rv
= mTransferable
->GetTransferData(kFilePromiseURLMime
,
2155 getter_AddRefs(urlPrimitive
));
2156 NS_ENSURE_SUCCESS(rv
, E_FAIL
);
2157 nsCOMPtr
<nsISupportsString
> srcUrlPrimitive
= do_QueryInterface(urlPrimitive
);
2158 NS_ENSURE_TRUE(srcUrlPrimitive
, E_FAIL
);
2160 nsAutoString srcUri
;
2161 srcUrlPrimitive
->GetData(srcUri
);
2162 if (srcUri
.IsEmpty()) return E_FAIL
;
2163 nsCOMPtr
<nsIURI
> sourceURI
;
2164 NS_NewURI(getter_AddRefs(sourceURI
), srcUri
);
2166 nsAutoString srcFileName
;
2167 nsCOMPtr
<nsISupports
> fileNamePrimitive
;
2168 Unused
<< mTransferable
->GetTransferData(kFilePromiseDestFilename
,
2169 getter_AddRefs(fileNamePrimitive
));
2170 nsCOMPtr
<nsISupportsString
> srcFileNamePrimitive
=
2171 do_QueryInterface(fileNamePrimitive
);
2172 if (srcFileNamePrimitive
) {
2173 srcFileNamePrimitive
->GetData(srcFileName
);
2175 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(sourceURI
);
2176 if (!sourceURL
) return E_FAIL
;
2178 nsAutoCString urlFileName
;
2179 sourceURL
->GetFileName(urlFileName
);
2180 NS_UnescapeURL(urlFileName
);
2181 CopyUTF8toUTF16(urlFileName
, srcFileName
);
2184 // make the name safe for the filesystem
2185 ValidateFilename(srcFileName
, false);
2186 if (srcFileName
.IsEmpty()) return E_FAIL
;
2188 sourceURI
.swap(*aSourceURI
);
2189 aFilename
= srcFileName
;
2193 HRESULT
nsDataObj::GetFileDescriptor_IStreamA(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2194 HGLOBAL fileGroupDescHandle
=
2195 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORW
));
2196 NS_ENSURE_TRUE(fileGroupDescHandle
, E_OUTOFMEMORY
);
2198 LPFILEGROUPDESCRIPTORA fileGroupDescA
=
2199 reinterpret_cast<LPFILEGROUPDESCRIPTORA
>(GlobalLock(fileGroupDescHandle
));
2200 if (!fileGroupDescA
) {
2201 ::GlobalFree(fileGroupDescHandle
);
2202 return E_OUTOFMEMORY
;
2205 nsAutoString wideFileName
;
2207 nsCOMPtr
<nsIURI
> sourceURI
;
2208 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
2210 ::GlobalFree(fileGroupDescHandle
);
2214 nsAutoCString nativeFileName
;
2215 NS_CopyUnicodeToNative(wideFileName
, nativeFileName
);
2217 strncpy(fileGroupDescA
->fgd
[0].cFileName
, nativeFileName
.get(), MAX_PATH
- 1);
2218 fileGroupDescA
->fgd
[0].cFileName
[MAX_PATH
- 1] = '\0';
2220 // one file in the file block
2221 fileGroupDescA
->cItems
= 1;
2222 fileGroupDescA
->fgd
[0].dwFlags
= FD_PROGRESSUI
;
2224 GlobalUnlock(fileGroupDescHandle
);
2225 aSTG
.hGlobal
= fileGroupDescHandle
;
2226 aSTG
.tymed
= TYMED_HGLOBAL
;
2231 HRESULT
nsDataObj::GetFileDescriptor_IStreamW(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2232 HGLOBAL fileGroupDescHandle
=
2233 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORW
));
2234 NS_ENSURE_TRUE(fileGroupDescHandle
, E_OUTOFMEMORY
);
2236 LPFILEGROUPDESCRIPTORW fileGroupDescW
=
2237 reinterpret_cast<LPFILEGROUPDESCRIPTORW
>(GlobalLock(fileGroupDescHandle
));
2238 if (!fileGroupDescW
) {
2239 ::GlobalFree(fileGroupDescHandle
);
2240 return E_OUTOFMEMORY
;
2243 nsAutoString wideFileName
;
2245 nsCOMPtr
<nsIURI
> sourceURI
;
2246 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
2248 ::GlobalFree(fileGroupDescHandle
);
2252 wcsncpy(fileGroupDescW
->fgd
[0].cFileName
, wideFileName
.get(), MAX_PATH
- 1);
2253 fileGroupDescW
->fgd
[0].cFileName
[MAX_PATH
- 1] = '\0';
2254 // one file in the file block
2255 fileGroupDescW
->cItems
= 1;
2256 fileGroupDescW
->fgd
[0].dwFlags
= FD_PROGRESSUI
;
2258 GlobalUnlock(fileGroupDescHandle
);
2259 aSTG
.hGlobal
= fileGroupDescHandle
;
2260 aSTG
.tymed
= TYMED_HGLOBAL
;
2265 HRESULT
nsDataObj::GetFileContents_IStream(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2266 IStream
* pStream
= nullptr;
2268 nsDataObj::CreateStream(&pStream
);
2269 NS_ENSURE_TRUE(pStream
, E_FAIL
);
2271 aSTG
.tymed
= TYMED_ISTREAM
;
2272 aSTG
.pstm
= pStream
;
2273 aSTG
.pUnkForRelease
= nullptr;