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 "nsITransferable.h"
19 #include "nsISupportsPrimitives.h"
21 #include "nsPrimitiveHelpers.h"
24 #include "nsPrintfCString.h"
25 #include "nsIStringBundle.h"
28 #include "nsNetUtil.h"
29 #include "mozilla/Components.h"
30 #include "mozilla/SpinEventLoopUntil.h"
31 #include "mozilla/Unused.h"
32 #include "nsProxyRelease.h"
33 #include "nsIObserverService.h"
34 #include "nsIOutputStream.h"
36 #include "nsDirectoryServiceDefs.h"
38 #include "nsThreadUtils.h"
39 #include "mozilla/Preferences.h"
40 #include "nsContentUtils.h"
41 #include "nsIPrincipal.h"
42 #include "nsNativeCharsetUtils.h"
43 #include "nsMimeTypes.h"
44 #include "nsIMIMEService.h"
45 #include "imgIEncoder.h"
46 #include "imgITools.h"
48 #include "nsLocalFile.h"
50 #include "mozilla/LazyIdleThread.h"
53 using namespace mozilla
;
54 using namespace mozilla::glue
;
55 using namespace mozilla::widget
;
58 #define DEFAULT_THREAD_TIMEOUT_MS 30000
60 //-----------------------------------------------------------------------------
61 // CStreamBase implementation
62 nsDataObj::CStreamBase::CStreamBase() : mStreamRead(0) {}
64 //-----------------------------------------------------------------------------
65 nsDataObj::CStreamBase::~CStreamBase() {}
67 NS_IMPL_ISUPPORTS(nsDataObj::CStream
, nsIStreamListener
)
69 //-----------------------------------------------------------------------------
70 // CStream implementation
71 nsDataObj::CStream::CStream() : mChannelRead(false) {}
73 //-----------------------------------------------------------------------------
74 nsDataObj::CStream::~CStream() {}
76 //-----------------------------------------------------------------------------
77 // helper - initializes the stream
78 nsresult
nsDataObj::CStream::Init(nsIURI
* pSourceURI
,
79 nsContentPolicyType aContentPolicyType
,
80 nsIPrincipal
* aRequestingPrincipal
,
81 nsICookieJarSettings
* aCookieJarSettings
) {
82 // we can not create a channel without a requestingPrincipal
83 if (!aRequestingPrincipal
) {
84 return NS_ERROR_FAILURE
;
87 rv
= NS_NewChannel(getter_AddRefs(mChannel
), pSourceURI
, aRequestingPrincipal
,
88 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
,
89 aContentPolicyType
, aCookieJarSettings
,
90 nullptr, // PerformanceStorage
92 nullptr, // aCallbacks
93 nsIRequest::LOAD_FROM_CACHE
);
95 NS_ENSURE_SUCCESS(rv
, rv
);
96 rv
= mChannel
->AsyncOpen(this);
97 NS_ENSURE_SUCCESS(rv
, rv
);
101 //-----------------------------------------------------------------------------
102 // IUnknown's QueryInterface, nsISupport's AddRef and Release are shared by
103 // IUnknown and nsIStreamListener.
104 STDMETHODIMP
nsDataObj::CStream::QueryInterface(REFIID refiid
,
106 *ppvResult
= nullptr;
107 if (IID_IUnknown
== refiid
|| refiid
== IID_IStream
)
113 if (nullptr != *ppvResult
) {
114 ((LPUNKNOWN
)*ppvResult
)->AddRef();
118 return E_NOINTERFACE
;
121 // nsIStreamListener implementation
123 nsDataObj::CStream::OnDataAvailable(
124 nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
125 uint64_t aOffset
, // offset within the stream
126 uint32_t aCount
) // bytes available on this call
128 // Extend the write buffer for the incoming data.
129 uint8_t* buffer
= mChannelData
.AppendElements(aCount
, fallible
);
131 return NS_ERROR_OUT_OF_MEMORY
;
133 NS_ASSERTION((mChannelData
.Length() == (aOffset
+ aCount
)),
134 "stream length mismatch w/write buffer");
136 // Read() may not return aCount on a single call, so loop until we've
137 // accumulated all the data OnDataAvailable has promised.
139 uint32_t odaBytesReadTotal
= 0;
141 uint32_t bytesReadByCall
= 0;
142 rv
= aInputStream
->Read((char*)(buffer
+ odaBytesReadTotal
), aCount
,
144 odaBytesReadTotal
+= bytesReadByCall
;
145 } while (aCount
< odaBytesReadTotal
&& NS_SUCCEEDED(rv
));
149 NS_IMETHODIMP
nsDataObj::CStream::OnStartRequest(nsIRequest
* aRequest
) {
150 mChannelResult
= NS_OK
;
154 NS_IMETHODIMP
nsDataObj::CStream::OnStopRequest(nsIRequest
* aRequest
,
155 nsresult aStatusCode
) {
157 mChannelResult
= aStatusCode
;
161 // Pumps thread messages while waiting for the async listener operation to
162 // complete. Failing this call will fail the stream incall from Windows
163 // and cancel the operation.
164 nsresult
nsDataObj::CStream::WaitForCompletion() {
165 // We are guaranteed OnStopRequest will get called, so this should be ok.
166 SpinEventLoopUntil("widget:nsDataObj::CStream::WaitForCompletion"_ns
,
167 [&]() { return mChannelRead
; });
169 if (!mChannelData
.Length()) mChannelResult
= NS_ERROR_FAILURE
;
171 return mChannelResult
;
174 //-----------------------------------------------------------------------------
176 STDMETHODIMP
nsDataObj::CStreamBase::Clone(IStream
** ppStream
) {
180 //-----------------------------------------------------------------------------
181 STDMETHODIMP
nsDataObj::CStreamBase::Commit(DWORD dwFrags
) { return E_NOTIMPL
; }
183 //-----------------------------------------------------------------------------
184 STDMETHODIMP
nsDataObj::CStreamBase::CopyTo(IStream
* pDestStream
,
185 ULARGE_INTEGER nBytesToCopy
,
186 ULARGE_INTEGER
* nBytesRead
,
187 ULARGE_INTEGER
* nBytesWritten
) {
191 //-----------------------------------------------------------------------------
192 STDMETHODIMP
nsDataObj::CStreamBase::LockRegion(ULARGE_INTEGER nStart
,
193 ULARGE_INTEGER nBytes
,
198 //-----------------------------------------------------------------------------
199 STDMETHODIMP
nsDataObj::CStream::Read(void* pvBuffer
, ULONG nBytesToRead
,
201 // Wait for the write into our buffer to complete via the stream listener.
202 // We can't respond to this by saying "call us back later".
203 if (NS_FAILED(WaitForCompletion())) return E_FAIL
;
205 // Bytes left for Windows to read out of our buffer
206 ULONG bytesLeft
= mChannelData
.Length() - mStreamRead
;
207 // Let Windows know what we will hand back, usually this is the entire buffer
208 *nBytesRead
= std::min(bytesLeft
, nBytesToRead
);
209 // Copy the buffer data over
210 memcpy(pvBuffer
, ((char*)mChannelData
.Elements() + mStreamRead
), *nBytesRead
);
211 // Update our bytes read tracking
212 mStreamRead
+= *nBytesRead
;
216 //-----------------------------------------------------------------------------
217 STDMETHODIMP
nsDataObj::CStreamBase::Revert(void) { return E_NOTIMPL
; }
219 //-----------------------------------------------------------------------------
220 STDMETHODIMP
nsDataObj::CStreamBase::Seek(LARGE_INTEGER nMove
, DWORD dwOrigin
,
221 ULARGE_INTEGER
* nNewPos
) {
222 if (nNewPos
== nullptr) return STG_E_INVALIDPOINTER
;
224 if (nMove
.LowPart
== 0 && nMove
.HighPart
== 0 &&
225 (dwOrigin
== STREAM_SEEK_SET
|| dwOrigin
== STREAM_SEEK_CUR
)) {
226 nNewPos
->LowPart
= 0;
227 nNewPos
->HighPart
= 0;
234 //-----------------------------------------------------------------------------
235 STDMETHODIMP
nsDataObj::CStreamBase::SetSize(ULARGE_INTEGER nNewSize
) {
239 //-----------------------------------------------------------------------------
240 STDMETHODIMP
nsDataObj::CStream::Stat(STATSTG
* statstg
, DWORD dwFlags
) {
241 if (statstg
== nullptr) return STG_E_INVALIDPOINTER
;
243 if (!mChannel
|| NS_FAILED(WaitForCompletion())) return E_FAIL
;
245 memset((void*)statstg
, 0, sizeof(STATSTG
));
247 if (dwFlags
!= STATFLAG_NONAME
) {
248 nsCOMPtr
<nsIURI
> sourceURI
;
249 if (NS_FAILED(mChannel
->GetURI(getter_AddRefs(sourceURI
)))) {
253 nsAutoCString strFileName
;
254 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(sourceURI
);
255 sourceURL
->GetFileName(strFileName
);
257 if (strFileName
.IsEmpty()) return E_FAIL
;
259 NS_UnescapeURL(strFileName
);
260 NS_ConvertUTF8toUTF16
wideFileName(strFileName
);
262 uint32_t nMaxNameLength
= (wideFileName
.Length() * 2) + 2;
263 void* retBuf
= CoTaskMemAlloc(nMaxNameLength
); // freed by caller
264 if (!retBuf
) return STG_E_INSUFFICIENTMEMORY
;
266 ZeroMemory(retBuf
, nMaxNameLength
);
267 memcpy(retBuf
, wideFileName
.get(), wideFileName
.Length() * 2);
268 statstg
->pwcsName
= (LPOLESTR
)retBuf
;
273 statstg
->type
= STGTY_STREAM
;
276 SystemTimeToFileTime((const SYSTEMTIME
*)&st
, (LPFILETIME
)&statstg
->mtime
);
277 statstg
->ctime
= statstg
->atime
= statstg
->mtime
;
279 statstg
->cbSize
.QuadPart
= mChannelData
.Length();
280 statstg
->grfMode
= STGM_READ
;
281 statstg
->grfLocksSupported
= LOCK_ONLYONCE
;
282 statstg
->clsid
= CLSID_NULL
;
287 //-----------------------------------------------------------------------------
288 STDMETHODIMP
nsDataObj::CStreamBase::UnlockRegion(ULARGE_INTEGER nStart
,
289 ULARGE_INTEGER nBytes
,
294 //-----------------------------------------------------------------------------
295 STDMETHODIMP
nsDataObj::CStreamBase::Write(const void* pvBuffer
,
301 //-----------------------------------------------------------------------------
302 HRESULT
nsDataObj::CreateStream(IStream
** outStream
) {
303 NS_ENSURE_TRUE(outStream
, E_INVALIDARG
);
305 nsresult rv
= NS_ERROR_FAILURE
;
306 nsAutoString wideFileName
;
307 nsCOMPtr
<nsIURI
> sourceURI
;
310 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
311 if (FAILED(res
)) return res
;
313 nsDataObj::CStream
* pStream
= new nsDataObj::CStream();
314 NS_ENSURE_TRUE(pStream
, E_OUTOFMEMORY
);
318 // query the requestingPrincipal from the transferable and add it to the new
320 nsCOMPtr
<nsIPrincipal
> requestingPrincipal
=
321 mTransferable
->GetRequestingPrincipal();
322 MOZ_ASSERT(requestingPrincipal
, "can not create channel without a principal");
324 // Note that the cookieJarSettings could be null if the data object is for the
325 // image copy. We will fix this in Bug 1690532.
326 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
=
327 mTransferable
->GetCookieJarSettings();
329 nsContentPolicyType contentPolicyType
= mTransferable
->GetContentPolicyType();
330 rv
= pStream
->Init(sourceURI
, contentPolicyType
, requestingPrincipal
,
336 *outStream
= pStream
;
341 //-----------------------------------------------------------------------------
342 // AutoCloseEvent implementation
343 nsDataObj::AutoCloseEvent::AutoCloseEvent()
344 : mEvent(::CreateEventW(nullptr, TRUE
, FALSE
, nullptr)) {}
346 bool nsDataObj::AutoCloseEvent::IsInited() const { return !!mEvent
; }
348 void nsDataObj::AutoCloseEvent::Signal() const { ::SetEvent(mEvent
); }
350 DWORD
nsDataObj::AutoCloseEvent::Wait(DWORD aMillisec
) const {
351 return ::WaitForSingleObject(mEvent
, aMillisec
);
354 //-----------------------------------------------------------------------------
355 // AutoSetEvent implementation
356 nsDataObj::AutoSetEvent::AutoSetEvent(NotNull
<AutoCloseEvent
*> aEvent
)
359 nsDataObj::AutoSetEvent::~AutoSetEvent() { Signal(); }
361 void nsDataObj::AutoSetEvent::Signal() const { mEvent
->Signal(); }
363 bool nsDataObj::AutoSetEvent::IsWaiting() const {
364 return mEvent
->Wait(0) == WAIT_TIMEOUT
;
367 //-----------------------------------------------------------------------------
368 // CMemStream implementation
369 Win32SRWLock
nsDataObj::CMemStream::mLock
;
371 //-----------------------------------------------------------------------------
372 nsDataObj::CMemStream::CMemStream(nsHGLOBAL aGlobalMem
, uint32_t aTotalLength
,
373 already_AddRefed
<AutoCloseEvent
> aEvent
)
374 : mGlobalMem(aGlobalMem
), mEvent(aEvent
), mTotalLength(aTotalLength
) {
375 ::CoCreateFreeThreadedMarshaler(this, getter_AddRefs(mMarshaler
));
378 //-----------------------------------------------------------------------------
379 nsDataObj::CMemStream::~CMemStream() {}
381 //-----------------------------------------------------------------------------
383 STDMETHODIMP
nsDataObj::CMemStream::QueryInterface(REFIID refiid
,
385 *ppvResult
= nullptr;
386 if (refiid
== IID_IUnknown
|| refiid
== IID_IStream
||
387 refiid
== IID_IAgileObject
) {
389 } else if (refiid
== IID_IMarshal
&& mMarshaler
) {
390 return mMarshaler
->QueryInterface(refiid
, ppvResult
);
393 if (nullptr != *ppvResult
) {
394 ((LPUNKNOWN
)*ppvResult
)->AddRef();
398 return E_NOINTERFACE
;
401 void nsDataObj::CMemStream::WaitForCompletion() {
403 // We are not waiting for obtaining the icon cache.
406 if (!NS_IsMainThread()) {
407 mEvent
->Wait(INFINITE
);
409 // We should not block the main thread.
412 // mEvent will always be in the signaled state here.
415 //-----------------------------------------------------------------------------
417 STDMETHODIMP
nsDataObj::CMemStream::Read(void* pvBuffer
, ULONG nBytesToRead
,
419 // Wait until the event is signaled.
422 AutoExclusiveLock
lock(mLock
);
423 char* contents
= reinterpret_cast<char*>(GlobalLock(mGlobalMem
.get()));
425 return E_OUTOFMEMORY
;
428 // Bytes left for Windows to read out of our buffer
429 ULONG bytesLeft
= mTotalLength
- mStreamRead
;
430 // Let Windows know what we will hand back, usually this is the entire buffer
431 *nBytesRead
= std::min(bytesLeft
, nBytesToRead
);
432 // Copy the buffer data over
433 memcpy(pvBuffer
, contents
+ mStreamRead
, *nBytesRead
);
434 // Update our bytes read tracking
435 mStreamRead
+= *nBytesRead
;
437 GlobalUnlock(mGlobalMem
.get());
441 //-----------------------------------------------------------------------------
442 STDMETHODIMP
nsDataObj::CMemStream::Stat(STATSTG
* statstg
, DWORD dwFlags
) {
443 if (statstg
== nullptr) return STG_E_INVALIDPOINTER
;
445 memset((void*)statstg
, 0, sizeof(STATSTG
));
447 if (dwFlags
!= STATFLAG_NONAME
) {
448 constexpr size_t kMaxNameLength
= sizeof(wchar_t);
449 void* retBuf
= CoTaskMemAlloc(kMaxNameLength
); // freed by caller
450 if (!retBuf
) return STG_E_INSUFFICIENTMEMORY
;
452 ZeroMemory(retBuf
, kMaxNameLength
);
453 statstg
->pwcsName
= (LPOLESTR
)retBuf
;
458 statstg
->type
= STGTY_STREAM
;
461 SystemTimeToFileTime((const SYSTEMTIME
*)&st
, (LPFILETIME
)&statstg
->mtime
);
462 statstg
->ctime
= statstg
->atime
= statstg
->mtime
;
464 statstg
->cbSize
.QuadPart
= mTotalLength
;
465 statstg
->grfMode
= STGM_READ
;
466 statstg
->grfLocksSupported
= LOCK_ONLYONCE
;
467 statstg
->clsid
= CLSID_NULL
;
476 //-----------------------------------------------------
478 //-----------------------------------------------------
479 nsDataObj::nsDataObj(nsIURI
* uri
)
481 mTransferable(nullptr),
483 mIsInOperation(FALSE
) {
484 mIOThread
= new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS
, "nsDataObj",
485 LazyIdleThread::ManualShutdown
);
486 m_enumFE
= new CEnumFormatEtc();
490 // A URI was obtained, so pass this through to the DataObject
491 // so it can create a SourceURL for CF_HTML flavour
492 uri
->GetSpec(mSourceURL
);
495 //-----------------------------------------------------
497 //-----------------------------------------------------
498 nsDataObj::~nsDataObj() {
499 NS_IF_RELEASE(mTransferable
);
501 mDataFlavors
.Clear();
505 // Free arbitrary system formats
506 for (uint32_t idx
= 0; idx
< mDataEntryList
.Length(); idx
++) {
507 CoTaskMemFree(mDataEntryList
[idx
]->fe
.ptd
);
508 ReleaseStgMedium(&mDataEntryList
[idx
]->stgm
);
509 CoTaskMemFree(mDataEntryList
[idx
]);
513 //-----------------------------------------------------
514 // IUnknown interface methods - see inknown.h for documentation
515 //-----------------------------------------------------
516 STDMETHODIMP
nsDataObj::QueryInterface(REFIID riid
, void** ppv
) {
519 if ((IID_IUnknown
== riid
) || (IID_IDataObject
== riid
)) {
523 } else if (IID_IDataObjectAsyncCapability
== riid
) {
524 *ppv
= static_cast<IDataObjectAsyncCapability
*>(this);
529 return E_NOINTERFACE
;
532 //-----------------------------------------------------
533 STDMETHODIMP_(ULONG
) nsDataObj::AddRef() {
535 NS_LOG_ADDREF(this, m_cRef
, "nsDataObj", sizeof(*this));
537 // When the first reference is taken, hold our own internal reference.
546 class RemoveTempFileHelper final
: public nsIObserver
, public nsINamed
{
548 explicit RemoveTempFileHelper(nsIFile
* aTempFile
) : mTempFile(aTempFile
) {
549 MOZ_ASSERT(mTempFile
);
552 // The attach method is seperate from the constructor as we may be addref-ing
553 // ourself, and we want to be sure someone has a strong reference to us.
555 // We need to listen to both the xpcom shutdown message and our timer, and
556 // fire when the first of either of these two messages is received.
558 rv
= NS_NewTimerWithObserver(getter_AddRefs(mTimer
), this, 500,
559 nsITimer::TYPE_ONE_SHOT
);
560 if (NS_WARN_IF(NS_FAILED(rv
))) {
564 nsCOMPtr
<nsIObserverService
> observerService
=
565 do_GetService("@mozilla.org/observer-service;1");
566 if (NS_WARN_IF(!observerService
)) {
571 observerService
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, false);
579 ~RemoveTempFileHelper() {
581 mTempFile
->Remove(false);
585 nsCOMPtr
<nsIFile
> mTempFile
;
586 nsCOMPtr
<nsITimer
> mTimer
;
589 NS_IMPL_ISUPPORTS(RemoveTempFileHelper
, nsIObserver
, nsINamed
);
592 RemoveTempFileHelper::Observe(nsISupports
* aSubject
, const char* aTopic
,
593 const char16_t
* aData
) {
594 // Let's be careful and make sure that we don't die immediately
595 RefPtr
<RemoveTempFileHelper
> grip
= this;
597 // Make sure that we aren't called again by destroying references to ourself.
598 nsCOMPtr
<nsIObserverService
> observerService
=
599 do_GetService("@mozilla.org/observer-service;1");
600 if (observerService
) {
601 observerService
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
609 // Remove the tempfile
611 mTempFile
->Remove(false);
618 RemoveTempFileHelper::GetName(nsACString
& aName
) {
619 aName
.AssignLiteral("RemoveTempFileHelper");
624 //-----------------------------------------------------
625 STDMETHODIMP_(ULONG
) nsDataObj::Release() {
628 NS_LOG_RELEASE(this, m_cRef
, "nsDataObj");
630 // If we hold the last reference, submit release of it to the main thread.
631 if (m_cRef
== 1 && mKeepAlive
) {
632 NS_ReleaseOnMainThread("nsDataObj release", mKeepAlive
.forget(), true);
635 if (0 != m_cRef
) return m_cRef
;
637 // We have released our last ref on this object and need to delete the
638 // temp file. External app acting as drop target may still need to open the
639 // temp file. Addref a timer so it can delay deleting file and destroying
641 if (mCachedTempFile
) {
642 RefPtr
<RemoveTempFileHelper
> helper
=
643 new RemoveTempFileHelper(mCachedTempFile
);
644 mCachedTempFile
= nullptr;
648 // In case the destructor ever AddRef/Releases, ensure we don't delete twice
649 // or take mKeepAlive as another reference.
657 //-----------------------------------------------------
658 BOOL
nsDataObj::FormatsMatch(const FORMATETC
& source
,
659 const FORMATETC
& target
) const {
660 if ((source
.cfFormat
== target
.cfFormat
) &&
661 (source
.dwAspect
& target
.dwAspect
) && (source
.tymed
& target
.tymed
)) {
668 //-----------------------------------------------------
669 // IDataObject methods
670 //-----------------------------------------------------
671 STDMETHODIMP
nsDataObj::GetData(LPFORMATETC aFormat
, LPSTGMEDIUM pSTM
) {
672 if (!mTransferable
) return DV_E_FORMATETC
;
674 // Hold an extra reference in case we end up spinning the event loop.
675 RefPtr
<nsDataObj
> keepAliveDuringGetData(this);
679 static CLIPFORMAT fileDescriptorFlavorA
=
680 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA
);
681 static CLIPFORMAT fileDescriptorFlavorW
=
682 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW
);
683 static CLIPFORMAT uniformResourceLocatorA
=
684 ::RegisterClipboardFormat(CFSTR_INETURLA
);
685 static CLIPFORMAT uniformResourceLocatorW
=
686 ::RegisterClipboardFormat(CFSTR_INETURLW
);
687 static CLIPFORMAT fileFlavor
= ::RegisterClipboardFormat(CFSTR_FILECONTENTS
);
688 static CLIPFORMAT PreferredDropEffect
=
689 ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT
);
691 // Arbitrary system formats are used for image feedback during drag
692 // and drop. We are responsible for storing these internally during
695 if (LookupArbitraryFormat(aFormat
, &pde
, FALSE
)) {
696 return CopyMediumData(pSTM
, &pde
->stgm
, aFormat
, FALSE
) ? S_OK
700 // Firefox internal formats
704 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
) &&
705 dfInx
< mDataFlavors
.Length()) {
706 nsCString
& df
= mDataFlavors
.ElementAt(dfInx
);
707 if (FormatsMatch(fe
, *aFormat
)) {
708 pSTM
->pUnkForRelease
=
709 nullptr; // caller is responsible for deleting this data
710 CLIPFORMAT format
= aFormat
->cfFormat
;
712 // Someone is asking for plain or unicode text
715 return GetText(df
, *aFormat
, *pSTM
);
717 // Some 3rd party apps that receive drag and drop files from the browser
718 // window require support for this.
720 return GetFile(*aFormat
, *pSTM
);
722 // Someone is asking for an image
725 return GetDib(df
, *aFormat
, *pSTM
);
728 if (format
== fileDescriptorFlavorA
)
729 return GetFileDescriptor(*aFormat
, *pSTM
, false);
730 if (format
== fileDescriptorFlavorW
)
731 return GetFileDescriptor(*aFormat
, *pSTM
, true);
732 if (format
== uniformResourceLocatorA
)
733 return GetUniformResourceLocator(*aFormat
, *pSTM
, false);
734 if (format
== uniformResourceLocatorW
)
735 return GetUniformResourceLocator(*aFormat
, *pSTM
, true);
736 if (format
== fileFlavor
) return GetFileContents(*aFormat
, *pSTM
);
737 if (format
== PreferredDropEffect
)
738 return GetPreferredDropEffect(*aFormat
, *pSTM
);
739 // MOZ_LOG(gWindowsLog, LogLevel::Info,
740 // ("***** nsDataObj::GetData - Unknown format %u\n", format));
741 return GetText(df
, *aFormat
, *pSTM
);
747 return DATA_E_FORMATETC
;
750 //-----------------------------------------------------
751 STDMETHODIMP
nsDataObj::GetDataHere(LPFORMATETC pFE
, LPSTGMEDIUM pSTM
) {
755 //-----------------------------------------------------
756 // Other objects querying to see if we support a
758 //-----------------------------------------------------
759 STDMETHODIMP
nsDataObj::QueryGetData(LPFORMATETC pFE
) {
760 // Arbitrary system formats are used for image feedback during drag
761 // and drop. We are responsible for storing these internally during
764 if (LookupArbitraryFormat(pFE
, &pde
, FALSE
)) return S_OK
;
766 // Firefox internal formats
770 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
)) {
771 if (fe
.cfFormat
== pFE
->cfFormat
) {
778 //-----------------------------------------------------
779 STDMETHODIMP
nsDataObj::GetCanonicalFormatEtc(LPFORMATETC pFEIn
,
780 LPFORMATETC pFEOut
) {
784 //-----------------------------------------------------
785 STDMETHODIMP
nsDataObj::SetData(LPFORMATETC aFormat
, LPSTGMEDIUM aMedium
,
787 // Arbitrary system formats are used for image feedback during drag
788 // and drop. We are responsible for storing these internally during
791 if (LookupArbitraryFormat(aFormat
, &pde
, TRUE
)) {
792 // Release the old data the lookup handed us for this format. This
793 // may have been set in CopyMediumData when we originally stored the
795 if (pde
->stgm
.tymed
) {
796 ReleaseStgMedium(&pde
->stgm
);
797 memset(&pde
->stgm
, 0, sizeof(STGMEDIUM
));
802 // If shouldRel is TRUE, the data object called owns the storage medium
803 // after the call returns. Store the incoming data in our data array for
804 // release when we are destroyed. This is the common case with arbitrary
805 // data from explorer.
806 pde
->stgm
= *aMedium
;
808 // Copy the incoming data into our data array. (AFAICT, this never gets
809 // called with arbitrary formats for drag images.)
810 result
= CopyMediumData(&pde
->stgm
, aMedium
, aFormat
, TRUE
);
812 pde
->fe
.tymed
= pde
->stgm
.tymed
;
814 return result
? S_OK
: DV_E_TYMED
;
817 if (shouldRel
) ReleaseStgMedium(aMedium
);
822 bool nsDataObj::LookupArbitraryFormat(FORMATETC
* aFormat
,
823 LPDATAENTRY
* aDataEntry
,
825 *aDataEntry
= nullptr;
827 if (aFormat
->ptd
!= nullptr) return false;
829 // See if it's already in our list. If so return the data entry.
830 for (uint32_t idx
= 0; idx
< mDataEntryList
.Length(); idx
++) {
831 if (mDataEntryList
[idx
]->fe
.cfFormat
== aFormat
->cfFormat
&&
832 mDataEntryList
[idx
]->fe
.dwAspect
== aFormat
->dwAspect
&&
833 mDataEntryList
[idx
]->fe
.lindex
== aFormat
->lindex
) {
834 if (aAddorUpdate
|| (mDataEntryList
[idx
]->fe
.tymed
& aFormat
->tymed
)) {
835 // If the caller requests we update, or if the
836 // medium type matches, return the entry.
837 *aDataEntry
= mDataEntryList
[idx
];
840 // Medium does not match, not found.
846 if (!aAddorUpdate
) return false;
848 // Add another entry to mDataEntryList
849 LPDATAENTRY dataEntry
= (LPDATAENTRY
)CoTaskMemAlloc(sizeof(DATAENTRY
));
850 if (!dataEntry
) return false;
852 dataEntry
->fe
= *aFormat
;
853 *aDataEntry
= dataEntry
;
854 memset(&dataEntry
->stgm
, 0, sizeof(STGMEDIUM
));
856 // Add this to our IEnumFORMATETC impl. so we can return it when
858 m_enumFE
->AddFormatEtc(aFormat
);
860 // Store a copy internally in the arbitrary formats array.
861 mDataEntryList
.AppendElement(dataEntry
);
866 bool nsDataObj::CopyMediumData(STGMEDIUM
* aMediumDst
, STGMEDIUM
* aMediumSrc
,
867 LPFORMATETC aFormat
, BOOL aSetData
) {
868 STGMEDIUM stgmOut
= *aMediumSrc
;
870 switch (stgmOut
.tymed
) {
872 stgmOut
.pstm
->AddRef();
875 stgmOut
.pstg
->AddRef();
878 if (!aMediumSrc
->pUnkForRelease
) {
880 if (aMediumSrc
->tymed
!= TYMED_HGLOBAL
) return false;
882 OleDuplicateData(aMediumSrc
->hGlobal
, aFormat
->cfFormat
, 0);
883 if (!stgmOut
.hGlobal
) return false;
885 // We are returning this data from LookupArbitraryFormat, indicate to
886 // the shell we hold it and will free it.
887 stgmOut
.pUnkForRelease
= static_cast<IDataObject
*>(this);
895 if (stgmOut
.pUnkForRelease
) stgmOut
.pUnkForRelease
->AddRef();
897 *aMediumDst
= stgmOut
;
902 //-----------------------------------------------------
903 STDMETHODIMP
nsDataObj::EnumFormatEtc(DWORD dwDir
, LPENUMFORMATETC
* ppEnum
) {
906 m_enumFE
->Clone(ppEnum
);
914 if (nullptr == *ppEnum
) return E_FAIL
;
917 // Clone already AddRefed the result so don't addref it again.
921 //-----------------------------------------------------
922 STDMETHODIMP
nsDataObj::DAdvise(LPFORMATETC pFE
, DWORD dwFlags
,
923 LPADVISESINK pIAdviseSink
, DWORD
* pdwConn
) {
924 return OLE_E_ADVISENOTSUPPORTED
;
927 //-----------------------------------------------------
928 STDMETHODIMP
nsDataObj::DUnadvise(DWORD dwConn
) {
929 return OLE_E_ADVISENOTSUPPORTED
;
932 //-----------------------------------------------------
933 STDMETHODIMP
nsDataObj::EnumDAdvise(LPENUMSTATDATA
* ppEnum
) {
934 return OLE_E_ADVISENOTSUPPORTED
;
937 // IDataObjectAsyncCapability methods
938 STDMETHODIMP
nsDataObj::EndOperation(HRESULT hResult
, IBindCtx
* pbcReserved
,
940 mIsInOperation
= FALSE
;
944 STDMETHODIMP
nsDataObj::GetAsyncMode(BOOL
* pfIsOpAsync
) {
945 *pfIsOpAsync
= mIsAsyncMode
;
950 STDMETHODIMP
nsDataObj::InOperation(BOOL
* pfInAsyncOp
) {
951 *pfInAsyncOp
= mIsInOperation
;
956 STDMETHODIMP
nsDataObj::SetAsyncMode(BOOL fDoOpAsync
) {
957 mIsAsyncMode
= fDoOpAsync
;
961 STDMETHODIMP
nsDataObj::StartOperation(IBindCtx
* pbcReserved
) {
962 mIsInOperation
= TRUE
;
969 // Someone is asking for a bitmap. The data in the transferable will be a
970 // straight imgIContainer, so just QI it.
973 nsDataObj::GetDib(const nsACString
& inFlavor
, FORMATETC
& aFormat
,
975 nsCOMPtr
<nsISupports
> genericDataWrapper
;
977 mTransferable
->GetTransferData(PromiseFlatCString(inFlavor
).get(),
978 getter_AddRefs(genericDataWrapper
)))) {
982 nsCOMPtr
<imgIContainer
> image
= do_QueryInterface(genericDataWrapper
);
987 nsCOMPtr
<imgITools
> imgTools
=
988 do_CreateInstance("@mozilla.org/image/tools;1");
990 nsAutoString
options(u
"bpp=32;"_ns
);
991 if (aFormat
.cfFormat
== CF_DIBV5
) {
992 options
.AppendLiteral("version=5");
994 options
.AppendLiteral("version=3");
997 nsCOMPtr
<nsIInputStream
> inputStream
;
998 nsresult rv
= imgTools
->EncodeImage(image
, nsLiteralCString(IMAGE_BMP
),
999 options
, getter_AddRefs(inputStream
));
1000 if (NS_FAILED(rv
) || !inputStream
) {
1004 nsCOMPtr
<imgIEncoder
> encoder
= do_QueryInterface(inputStream
);
1010 rv
= encoder
->GetImageBufferUsed(&size
);
1011 if (NS_FAILED(rv
) || size
<= BFH_LENGTH
) {
1015 char* src
= nullptr;
1016 rv
= encoder
->GetImageBuffer(&src
);
1017 if (NS_FAILED(rv
) || !src
) {
1021 // We don't want the file header.
1025 HGLOBAL glob
= ::GlobalAlloc(GMEM_MOVEABLE
| GMEM_ZEROINIT
, size
);
1030 char* dst
= (char*)::GlobalLock(glob
);
1031 ::CopyMemory(dst
, src
, size
);
1032 ::GlobalUnlock(glob
);
1034 aSTG
.hGlobal
= glob
;
1035 aSTG
.tymed
= TYMED_HGLOBAL
;
1040 // GetFileDescriptor
1044 nsDataObj ::GetFileDescriptor(FORMATETC
& aFE
, STGMEDIUM
& aSTG
,
1048 // How we handle this depends on if we're dealing with an internet
1049 // shortcut, since those are done under the covers.
1050 if (IsFlavourPresent(kFilePromiseMime
) || IsFlavourPresent(kFileMime
)) {
1052 return GetFileDescriptor_IStreamW(aFE
, aSTG
);
1054 return GetFileDescriptor_IStreamA(aFE
, aSTG
);
1055 } else if (IsFlavourPresent(kURLMime
)) {
1057 res
= GetFileDescriptorInternetShortcutW(aFE
, aSTG
);
1059 res
= GetFileDescriptorInternetShortcutA(aFE
, aSTG
);
1061 NS_WARNING("Not yet implemented\n");
1064 } // GetFileDescriptor
1068 nsDataObj ::GetFileContents(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1071 // How we handle this depends on if we're dealing with an internet
1072 // shortcut, since those are done under the covers.
1073 if (IsFlavourPresent(kFilePromiseMime
) || IsFlavourPresent(kFileMime
))
1074 return GetFileContents_IStream(aFE
, aSTG
);
1075 else if (IsFlavourPresent(kURLMime
))
1076 return GetFileContentsInternetShortcut(aFE
, aSTG
);
1078 NS_WARNING("Not yet implemented\n");
1082 } // GetFileContents
1084 // Ensure that the supplied name doesn't have invalid characters.
1085 static void ValidateFilename(nsString
& aFilename
, bool isShortcut
) {
1086 nsCOMPtr
<nsIMIMEService
> mimeService
= do_GetService("@mozilla.org/mime;1");
1087 if (NS_WARN_IF(!mimeService
)) {
1088 aFilename
.Truncate();
1092 uint32_t flags
= nsIMIMEService::VALIDATE_SANITIZE_ONLY
;
1094 flags
|= nsIMIMEService::VALIDATE_ALLOW_INVALID_FILENAMES
;
1097 nsAutoString outFilename
;
1098 mimeService
->ValidateFileNameForSaving(aFilename
, EmptyCString(), flags
,
1100 aFilename
= outFilename
;
1104 // Given a unicode string, convert it to a valid local charset filename
1105 // and append the .url extension to be used for a shortcut file.
1106 // This ensures that we do not cut MBCS characters in the middle.
1108 // It would seem that this is more functionality suited to being in nsIFile.
1110 static bool CreateURLFilenameFromTextA(nsAutoString
& aText
, char* aFilename
) {
1111 if (aText
.IsEmpty()) {
1114 aText
.AppendLiteral(".url");
1115 ValidateFilename(aText
, true);
1116 if (aText
.IsEmpty()) {
1120 // ValidateFilename should already be checking the filename length, but do
1121 // an extra check to verify for the local code page that the converted text
1122 // doesn't go over MAX_PATH and just return false if it does.
1123 char defaultChar
= '_';
1124 int currLen
= WideCharToMultiByte(CP_ACP
, WC_COMPOSITECHECK
| WC_DEFAULTCHAR
,
1125 aText
.get(), -1, aFilename
, MAX_PATH
,
1126 &defaultChar
, nullptr);
1127 return currLen
!= 0;
1130 // Wide character version of CreateURLFilenameFromTextA
1131 static bool CreateURLFilenameFromTextW(nsAutoString
& aText
,
1132 wchar_t* aFilename
) {
1133 if (aText
.IsEmpty()) {
1136 aText
.AppendLiteral(".url");
1137 ValidateFilename(aText
, true);
1138 if (aText
.IsEmpty() || aText
.Length() >= MAX_PATH
) {
1142 wcscpy(&aFilename
[0], aText
.get());
1146 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
1148 static bool GetLocalizedString(const char* aName
, nsAString
& aString
) {
1149 nsCOMPtr
<nsIStringBundleService
> stringService
=
1150 mozilla::components::StringBundle::Service();
1151 if (!stringService
) return false;
1153 nsCOMPtr
<nsIStringBundle
> stringBundle
;
1154 nsresult rv
= stringService
->CreateBundle(PAGEINFO_PROPERTIES
,
1155 getter_AddRefs(stringBundle
));
1156 if (NS_FAILED(rv
)) return false;
1158 rv
= stringBundle
->GetStringFromName(aName
, aString
);
1159 return NS_SUCCEEDED(rv
);
1163 // GetFileDescriptorInternetShortcut
1165 // Create the special format for an internet shortcut and build up the data
1166 // structures the shell is expecting.
1169 nsDataObj ::GetFileDescriptorInternetShortcutA(FORMATETC
& aFE
,
1171 // get the title of the shortcut
1173 if (NS_FAILED(ExtractShortcutTitle(title
))) return E_OUTOFMEMORY
;
1175 HGLOBAL fileGroupDescHandle
=
1176 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORA
));
1177 if (!fileGroupDescHandle
) return E_OUTOFMEMORY
;
1179 LPFILEGROUPDESCRIPTORA fileGroupDescA
=
1180 reinterpret_cast<LPFILEGROUPDESCRIPTORA
>(
1181 ::GlobalLock(fileGroupDescHandle
));
1182 if (!fileGroupDescA
) {
1183 ::GlobalFree(fileGroupDescHandle
);
1184 return E_OUTOFMEMORY
;
1187 // get a valid filename in the following order: 1) from the page title,
1188 // 2) localized string for an untitled page, 3) just use "Untitled.url"
1189 if (!CreateURLFilenameFromTextA(title
, fileGroupDescA
->fgd
[0].cFileName
)) {
1190 nsAutoString untitled
;
1191 if (!GetLocalizedString("noPageTitle", untitled
) ||
1192 !CreateURLFilenameFromTextA(untitled
,
1193 fileGroupDescA
->fgd
[0].cFileName
)) {
1194 strcpy(fileGroupDescA
->fgd
[0].cFileName
, "Untitled.url");
1198 // one file in the file block
1199 fileGroupDescA
->cItems
= 1;
1200 fileGroupDescA
->fgd
[0].dwFlags
= FD_LINKUI
;
1202 ::GlobalUnlock(fileGroupDescHandle
);
1203 aSTG
.hGlobal
= fileGroupDescHandle
;
1204 aSTG
.tymed
= TYMED_HGLOBAL
;
1207 } // GetFileDescriptorInternetShortcutA
1210 nsDataObj ::GetFileDescriptorInternetShortcutW(FORMATETC
& aFE
,
1212 // get the title of the shortcut
1214 if (NS_FAILED(ExtractShortcutTitle(title
))) return E_OUTOFMEMORY
;
1216 HGLOBAL fileGroupDescHandle
=
1217 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORW
));
1218 if (!fileGroupDescHandle
) return E_OUTOFMEMORY
;
1220 LPFILEGROUPDESCRIPTORW fileGroupDescW
=
1221 reinterpret_cast<LPFILEGROUPDESCRIPTORW
>(
1222 ::GlobalLock(fileGroupDescHandle
));
1223 if (!fileGroupDescW
) {
1224 ::GlobalFree(fileGroupDescHandle
);
1225 return E_OUTOFMEMORY
;
1228 // get a valid filename in the following order: 1) from the page title,
1229 // 2) localized string for an untitled page, 3) just use "Untitled.url"
1230 if (!CreateURLFilenameFromTextW(title
, fileGroupDescW
->fgd
[0].cFileName
)) {
1231 nsAutoString untitled
;
1232 if (!GetLocalizedString("noPageTitle", untitled
) ||
1233 !CreateURLFilenameFromTextW(untitled
,
1234 fileGroupDescW
->fgd
[0].cFileName
)) {
1235 wcscpy(fileGroupDescW
->fgd
[0].cFileName
, L
"Untitled.url");
1239 // one file in the file block
1240 fileGroupDescW
->cItems
= 1;
1241 fileGroupDescW
->fgd
[0].dwFlags
= FD_LINKUI
;
1243 ::GlobalUnlock(fileGroupDescHandle
);
1244 aSTG
.hGlobal
= fileGroupDescHandle
;
1245 aSTG
.tymed
= TYMED_HGLOBAL
;
1248 } // GetFileDescriptorInternetShortcutW
1251 // GetFileContentsInternetShortcut
1253 // Create the special format for an internet shortcut and build up the data
1254 // structures the shell is expecting.
1257 nsDataObj ::GetFileContentsInternetShortcut(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1258 static const char* kShellIconPref
= "browser.shell.shortcutFavicons";
1260 if (NS_FAILED(ExtractShortcutURL(url
))) return E_OUTOFMEMORY
;
1262 nsCOMPtr
<nsIURI
> aUri
;
1263 nsresult rv
= NS_NewURI(getter_AddRefs(aUri
), url
);
1264 if (NS_FAILED(rv
)) {
1268 nsAutoCString asciiUrl
;
1269 rv
= aUri
->GetAsciiSpec(asciiUrl
);
1270 if (NS_FAILED(rv
)) {
1274 RefPtr
<AutoCloseEvent
> event
;
1276 const char* shortcutFormatStr
;
1278 nsCString asciiPath
;
1279 if (!Preferences::GetBool(kShellIconPref
, true)) {
1280 shortcutFormatStr
= "[InternetShortcut]\r\nURL=%s\r\n";
1281 const int formatLen
= strlen(shortcutFormatStr
) - 2; // don't include %s
1282 totalLen
= formatLen
+ asciiUrl
.Length(); // don't include null character
1284 nsCOMPtr
<nsIFile
> icoFile
;
1286 nsAutoString aUriHash
;
1288 event
= new AutoCloseEvent();
1289 if (!event
->IsInited()) {
1293 RefPtr
<AutoSetEvent
> e
= new AutoSetEvent(WrapNotNull(event
));
1294 mozilla::widget::FaviconHelper::ObtainCachedIconFile(
1295 aUri
, aUriHash
, mIOThread
, true,
1296 NS_NewRunnableFunction(
1297 "FaviconHelper::RefreshDesktop", [e
= std::move(e
)] {
1298 if (e
->IsWaiting()) {
1299 // Unblock IStream:::Read.
1302 // We could not wait until the favicon was available. We have
1303 // to refresh to refect the favicon.
1304 SendNotifyMessage(HWND_BROADCAST
, WM_SETTINGCHANGE
,
1305 SPI_SETNONCLIENTMETRICS
, 0);
1309 rv
= mozilla::widget::FaviconHelper::GetOutputIconPath(aUri
, icoFile
, true);
1310 NS_ENSURE_SUCCESS(rv
, E_FAIL
);
1312 rv
= icoFile
->GetPath(path
);
1313 NS_ENSURE_SUCCESS(rv
, E_FAIL
);
1315 if (IsAsciiNullTerminated(static_cast<const char16_t
*>(path
.get()))) {
1316 LossyCopyUTF16toASCII(path
, asciiPath
);
1318 "[InternetShortcut]\r\nURL=%s\r\n"
1319 "IDList=\r\nHotKey=0\r\nIconFile=%s\r\n"
1323 WideCharToMultiByte(CP_UTF7
, 0, char16ptr_t(path
.BeginReading()),
1324 path
.Length(), nullptr, 0, nullptr, nullptr);
1325 NS_ENSURE_TRUE(len
> 0, E_FAIL
);
1326 asciiPath
.SetLength(len
);
1327 WideCharToMultiByte(CP_UTF7
, 0, char16ptr_t(path
.BeginReading()),
1328 path
.Length(), asciiPath
.BeginWriting(), len
, nullptr,
1331 "[InternetShortcut]\r\nURL=%s\r\n"
1332 "IDList=\r\nHotKey=0\r\nIconIndex=0\r\n"
1333 "[InternetShortcut.W]\r\nIconFile=%s\r\n";
1335 const int formatLen
= strlen(shortcutFormatStr
) - 2 * 2; // no %s twice
1336 totalLen
= formatLen
+ asciiUrl
.Length() +
1337 asciiPath
.Length(); // we don't want a null character on the end
1340 // create a global memory area and build up the file contents w/in it
1341 nsAutoGlobalMem
globalMem(nsHGLOBAL(::GlobalAlloc(GMEM_SHARE
, totalLen
)));
1342 if (!globalMem
) return E_OUTOFMEMORY
;
1344 char* contents
= reinterpret_cast<char*>(::GlobalLock(globalMem
.get()));
1346 return E_OUTOFMEMORY
;
1349 // NOTE: we intentionally use the Microsoft version of snprintf here because
1351 // terminate strings which reach the maximum size of the buffer. Since we know
1352 // that the formatted length here is totalLen, this call to _snprintf will
1353 // format the string into the buffer without appending the null character.
1355 if (!Preferences::GetBool(kShellIconPref
, true)) {
1356 _snprintf(contents
, totalLen
, shortcutFormatStr
, asciiUrl
.get());
1358 _snprintf(contents
, totalLen
, shortcutFormatStr
, asciiUrl
.get(),
1362 ::GlobalUnlock(globalMem
.get());
1364 if (aFE
.tymed
& TYMED_ISTREAM
) {
1365 if (!mIsInOperation
) {
1366 // The drop target didn't initiate an async operation.
1367 // We can't block CMemStream::Read.
1370 RefPtr
<IStream
> stream
=
1371 new CMemStream(globalMem
.disown(), totalLen
, event
.forget());
1372 stream
.forget(&aSTG
.pstm
);
1373 aSTG
.tymed
= TYMED_ISTREAM
;
1375 if (event
&& event
->IsInited()) {
1376 event
->Signal(); // We can't block reading the global memory
1378 aSTG
.hGlobal
= globalMem
.disown();
1379 aSTG
.tymed
= TYMED_HGLOBAL
;
1383 } // GetFileContentsInternetShortcut
1385 // check if specified flavour is present in the transferable
1386 bool nsDataObj ::IsFlavourPresent(const char* inFlavour
) {
1387 bool retval
= false;
1388 NS_ENSURE_TRUE(mTransferable
, false);
1390 // get the list of flavors available in the transferable
1391 nsTArray
<nsCString
> flavors
;
1392 nsresult rv
= mTransferable
->FlavorsTransferableCanExport(flavors
);
1393 NS_ENSURE_SUCCESS(rv
, false);
1395 // try to find requested flavour
1396 for (uint32_t i
= 0; i
< flavors
.Length(); ++i
) {
1397 if (flavors
[i
].Equals(inFlavour
)) {
1398 retval
= true; // found it!
1401 } // for each flavor
1406 HRESULT
nsDataObj::GetPreferredDropEffect(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1408 aSTG
.tymed
= TYMED_HGLOBAL
;
1409 aSTG
.pUnkForRelease
= nullptr;
1410 HGLOBAL hGlobalMemory
= nullptr;
1411 hGlobalMemory
= ::GlobalAlloc(GMEM_MOVEABLE
, sizeof(DWORD
));
1412 if (hGlobalMemory
) {
1413 DWORD
* pdw
= (DWORD
*)GlobalLock(hGlobalMemory
);
1414 // The PreferredDropEffect clipboard format is only registered if a
1415 // drag/drop of an image happens from Mozilla to the desktop. We want its
1416 // value to be DROPEFFECT_MOVE in that case so that the file is moved from
1417 // the temporary location, not copied. This value should, ideally, be set on
1418 // the data object via SetData() but our IDataObject implementation doesn't
1419 // implement SetData. It adds data to the data object lazily only when the
1420 // drop target asks for it.
1421 *pdw
= (DWORD
)DROPEFFECT_MOVE
;
1422 GlobalUnlock(hGlobalMemory
);
1424 res
= E_OUTOFMEMORY
;
1426 aSTG
.hGlobal
= hGlobalMemory
;
1430 //-----------------------------------------------------
1431 HRESULT
nsDataObj::GetText(const nsACString
& aDataFlavor
, FORMATETC
& aFE
,
1433 void* data
= nullptr;
1435 const nsPromiseFlatCString
& flavorStr
= PromiseFlatCString(aDataFlavor
);
1437 // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
1438 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1439 nsresult rv
= mTransferable
->GetTransferData(
1440 flavorStr
.get(), getter_AddRefs(genericDataWrapper
));
1441 if (NS_FAILED(rv
) || !genericDataWrapper
) {
1446 nsPrimitiveHelpers::CreateDataFromPrimitive(
1447 nsDependentCString(flavorStr
.get()), genericDataWrapper
, &data
, &len
);
1448 if (!data
) return E_FAIL
;
1450 HGLOBAL hGlobalMemory
= nullptr;
1452 aSTG
.tymed
= TYMED_HGLOBAL
;
1453 aSTG
.pUnkForRelease
= nullptr;
1455 // We play games under the hood and advertise flavors that we know we
1456 // can support, only they require a bit of conversion or munging of the data.
1459 // The transferable gives us data that is null-terminated, but this isn't
1460 // reflected in the |len| parameter. Windoze apps expect this null to be there
1461 // so bump our data buffer by the appropriate size to account for the null
1462 // (one char for CF_TEXT, one char16_t for CF_UNICODETEXT).
1463 DWORD allocLen
= (DWORD
)len
;
1464 if (aFE
.cfFormat
== CF_TEXT
) {
1465 // Someone is asking for text/plain; convert the unicode (assuming it's
1466 // present) to text with the correct platform encoding.
1467 size_t bufferSize
= sizeof(char) * (len
+ 2);
1468 char* plainTextData
= static_cast<char*>(moz_xmalloc(bufferSize
));
1469 char16_t
* castedUnicode
= reinterpret_cast<char16_t
*>(data
);
1470 int32_t plainTextLen
=
1471 WideCharToMultiByte(CP_ACP
, 0, (LPCWSTR
)castedUnicode
, len
/ 2 + 1,
1472 plainTextData
, bufferSize
, NULL
, NULL
);
1473 // replace the unicode data with our plaintext data. Recall that
1474 // |plainTextLen| doesn't include the null in the length.
1477 data
= plainTextData
;
1478 allocLen
= plainTextLen
;
1480 free(plainTextData
);
1481 NS_WARNING("Oh no, couldn't convert unicode to plain text");
1484 } else if (aFE
.cfFormat
== nsClipboard::GetHtmlClipboardFormat()) {
1485 // Someone is asking for win32's HTML flavor. Convert our html fragment
1486 // from unicode to UTF-8 then put it into a format specified by msft.
1487 NS_ConvertUTF16toUTF8
converter(reinterpret_cast<char16_t
*>(data
));
1488 char* utf8HTML
= nullptr;
1490 BuildPlatformHTML(converter
.get(), &utf8HTML
); // null terminates
1493 if (NS_SUCCEEDED(rv
) && utf8HTML
) {
1494 // replace the unicode data with our HTML data. Don't forget the null.
1496 allocLen
= strlen(utf8HTML
) + sizeof(char);
1498 NS_WARNING("Oh no, couldn't convert to HTML");
1501 } else if (aFE
.cfFormat
!= nsClipboard::GetCustomClipboardFormat()) {
1502 // we assume that any data that isn't caught above is unicode. This may
1503 // be an erroneous assumption, but is true so far.
1504 allocLen
+= sizeof(char16_t
);
1507 hGlobalMemory
= (HGLOBAL
)GlobalAlloc(GMEM_MOVEABLE
, allocLen
);
1509 // Copy text to Global Memory Area
1510 if (hGlobalMemory
) {
1511 char* dest
= reinterpret_cast<char*>(GlobalLock(hGlobalMemory
));
1512 char* source
= reinterpret_cast<char*>(data
);
1513 memcpy(dest
, source
, allocLen
); // copies the null as well
1514 GlobalUnlock(hGlobalMemory
);
1516 aSTG
.hGlobal
= hGlobalMemory
;
1518 // Now, delete the memory that was created by CreateDataFromPrimitive (or our
1525 //-----------------------------------------------------
1526 HRESULT
nsDataObj::GetFile(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1531 while (NOERROR
== m_enumFE
->Next(1, &fe
, &count
) &&
1532 dfInx
< mDataFlavors
.Length()) {
1533 if (mDataFlavors
[dfInx
].EqualsLiteral(kNativeImageMime
))
1534 return DropImage(aFE
, aSTG
);
1535 if (mDataFlavors
[dfInx
].EqualsLiteral(kFileMime
))
1536 return DropFile(aFE
, aSTG
);
1537 if (mDataFlavors
[dfInx
].EqualsLiteral(kFilePromiseMime
))
1538 return DropTempFile(aFE
, aSTG
);
1544 HRESULT
nsDataObj::DropFile(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1546 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1548 if (NS_FAILED(mTransferable
->GetTransferData(
1549 kFileMime
, getter_AddRefs(genericDataWrapper
)))) {
1552 nsCOMPtr
<nsIFile
> file(do_QueryInterface(genericDataWrapper
));
1553 if (!file
) return E_FAIL
;
1555 aSTG
.tymed
= TYMED_HGLOBAL
;
1556 aSTG
.pUnkForRelease
= nullptr;
1559 rv
= file
->GetPath(path
);
1560 if (NS_FAILED(rv
)) return E_FAIL
;
1562 uint32_t allocLen
= path
.Length() + 2;
1563 HGLOBAL hGlobalMemory
= nullptr;
1566 hGlobalMemory
= GlobalAlloc(GMEM_MOVEABLE
,
1567 sizeof(DROPFILES
) + allocLen
* sizeof(char16_t
));
1568 if (!hGlobalMemory
) return E_FAIL
;
1570 DROPFILES
* pDropFile
= (DROPFILES
*)GlobalLock(hGlobalMemory
);
1572 // First, populate the drop file structure
1573 pDropFile
->pFiles
= sizeof(DROPFILES
); // Offset to start of file name string
1575 pDropFile
->pt
.x
= 0;
1576 pDropFile
->pt
.y
= 0;
1577 pDropFile
->fWide
= TRUE
;
1579 // Copy the filename right after the DROPFILES structure
1580 dest
= (char16_t
*)(((char*)pDropFile
) + pDropFile
->pFiles
);
1581 memcpy(dest
, path
.get(), (allocLen
- 1) * sizeof(char16_t
));
1583 // Two null characters are needed at the end of the file name.
1584 // Lookup the CF_HDROP shell clipboard format for more info.
1585 // Add the second null character right after the first one.
1586 dest
[allocLen
- 1] = L
'\0';
1588 GlobalUnlock(hGlobalMemory
);
1590 aSTG
.hGlobal
= hGlobalMemory
;
1595 HRESULT
nsDataObj::DropImage(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1597 if (!mCachedTempFile
) {
1598 nsCOMPtr
<nsISupports
> genericDataWrapper
;
1600 if (NS_FAILED(mTransferable
->GetTransferData(
1601 kNativeImageMime
, getter_AddRefs(genericDataWrapper
)))) {
1604 nsCOMPtr
<imgIContainer
> image(do_QueryInterface(genericDataWrapper
));
1605 if (!image
) return E_FAIL
;
1607 nsCOMPtr
<imgITools
> imgTools
=
1608 do_CreateInstance("@mozilla.org/image/tools;1");
1609 nsCOMPtr
<nsIInputStream
> inputStream
;
1610 rv
= imgTools
->EncodeImage(image
, nsLiteralCString(IMAGE_BMP
),
1611 u
"bpp=32;version=3"_ns
,
1612 getter_AddRefs(inputStream
));
1613 if (NS_FAILED(rv
) || !inputStream
) {
1617 nsCOMPtr
<imgIEncoder
> encoder
= do_QueryInterface(inputStream
);
1623 rv
= encoder
->GetImageBufferUsed(&size
);
1624 if (NS_FAILED(rv
)) {
1628 char* src
= nullptr;
1629 rv
= encoder
->GetImageBuffer(&src
);
1630 if (NS_FAILED(rv
) || !src
) {
1634 // Save the bitmap to a temporary location.
1635 nsCOMPtr
<nsIFile
> dropFile
;
1636 rv
= NS_GetSpecialDirectory(NS_OS_TEMP_DIR
, getter_AddRefs(dropFile
));
1641 // Filename must be random so as not to confuse apps like
1642 // Photoshop which handle multiple drags into a single window.
1645 NS_MakeRandomString(buf
, 8);
1646 memcpy(buf
+ 8, ".bmp", 5);
1647 filename
.Append(nsDependentCString(buf
, 12));
1648 dropFile
->AppendNative(filename
);
1649 rv
= dropFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0660);
1650 if (NS_FAILED(rv
)) {
1654 // Cache the temp file so we can delete it later and so
1655 // it doesn't get recreated over and over on multiple calls
1656 // which does occur from windows shell.
1657 dropFile
->Clone(getter_AddRefs(mCachedTempFile
));
1659 // Write the data to disk.
1660 nsCOMPtr
<nsIOutputStream
> outStream
;
1661 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outStream
), dropFile
);
1662 if (NS_FAILED(rv
)) {
1666 uint32_t written
= 0;
1667 rv
= outStream
->Write(src
, size
, &written
);
1668 if (NS_FAILED(rv
) || written
!= size
) {
1675 // Pass the file name back to the drop target so that it can access the file.
1677 rv
= mCachedTempFile
->GetPath(path
);
1678 if (NS_FAILED(rv
)) return E_FAIL
;
1680 // Two null characters are needed to terminate the file name list.
1681 HGLOBAL hGlobalMemory
= nullptr;
1683 uint32_t allocLen
= path
.Length() + 2;
1685 aSTG
.tymed
= TYMED_HGLOBAL
;
1686 aSTG
.pUnkForRelease
= nullptr;
1688 hGlobalMemory
= GlobalAlloc(GMEM_MOVEABLE
,
1689 sizeof(DROPFILES
) + allocLen
* sizeof(char16_t
));
1690 if (!hGlobalMemory
) return E_FAIL
;
1692 DROPFILES
* pDropFile
= (DROPFILES
*)GlobalLock(hGlobalMemory
);
1694 // First, populate the drop file structure.
1696 sizeof(DROPFILES
); // Offset to start of file name char array.
1698 pDropFile
->pt
.x
= 0;
1699 pDropFile
->pt
.y
= 0;
1700 pDropFile
->fWide
= TRUE
;
1702 // Copy the filename right after the DROPFILES structure.
1703 char16_t
* dest
= (char16_t
*)(((char*)pDropFile
) + pDropFile
->pFiles
);
1704 memcpy(dest
, path
.get(),
1706 sizeof(char16_t
)); // Copies the null character in path as well.
1708 // Two null characters are needed at the end of the file name.
1709 // Lookup the CF_HDROP shell clipboard format for more info.
1710 // Add the second null character right after the first one.
1711 dest
[allocLen
- 1] = L
'\0';
1713 GlobalUnlock(hGlobalMemory
);
1715 aSTG
.hGlobal
= hGlobalMemory
;
1720 HRESULT
nsDataObj::DropTempFile(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
1722 if (!mCachedTempFile
) {
1723 // Tempfile will need a temporary location.
1724 nsCOMPtr
<nsIFile
> dropFile
;
1725 rv
= NS_GetSpecialDirectory(NS_OS_TEMP_DIR
, getter_AddRefs(dropFile
));
1726 if (!dropFile
) return E_FAIL
;
1728 // Filename must be random
1730 nsAutoString wideFileName
;
1731 nsCOMPtr
<nsIURI
> sourceURI
;
1733 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
1734 if (FAILED(res
)) return res
;
1735 NS_CopyUnicodeToNative(wideFileName
, filename
);
1737 dropFile
->AppendNative(filename
);
1738 rv
= dropFile
->CreateUnique(nsIFile::NORMAL_FILE_TYPE
, 0660);
1739 if (NS_FAILED(rv
)) return E_FAIL
;
1741 // Cache the temp file so we can delete it later and so
1742 // it doesn't get recreated over and over on multiple calls
1743 // which does occur from windows shell.
1744 dropFile
->Clone(getter_AddRefs(mCachedTempFile
));
1746 // Write the data to disk.
1747 nsCOMPtr
<nsIOutputStream
> outStream
;
1748 rv
= NS_NewLocalFileOutputStream(getter_AddRefs(outStream
), dropFile
);
1749 if (NS_FAILED(rv
)) return E_FAIL
;
1751 IStream
* pStream
= nullptr;
1752 nsDataObj::CreateStream(&pStream
);
1753 NS_ENSURE_TRUE(pStream
, E_FAIL
);
1756 ULONG readCount
= 0;
1757 uint32_t writeCount
= 0;
1759 HRESULT hres
= pStream
->Read(buffer
, sizeof(buffer
), &readCount
);
1760 if (FAILED(hres
)) return E_FAIL
;
1761 if (readCount
== 0) break;
1762 rv
= outStream
->Write(buffer
, readCount
, &writeCount
);
1763 if (NS_FAILED(rv
)) return E_FAIL
;
1769 // Pass the file name back to the drop target so that it can access the file.
1771 rv
= mCachedTempFile
->GetPath(path
);
1772 if (NS_FAILED(rv
)) return E_FAIL
;
1774 uint32_t allocLen
= path
.Length() + 2;
1776 // Two null characters are needed to terminate the file name list.
1777 HGLOBAL hGlobalMemory
= nullptr;
1779 aSTG
.tymed
= TYMED_HGLOBAL
;
1780 aSTG
.pUnkForRelease
= nullptr;
1782 hGlobalMemory
= GlobalAlloc(GMEM_MOVEABLE
,
1783 sizeof(DROPFILES
) + allocLen
* sizeof(char16_t
));
1784 if (!hGlobalMemory
) return E_FAIL
;
1786 DROPFILES
* pDropFile
= (DROPFILES
*)GlobalLock(hGlobalMemory
);
1788 // First, populate the drop file structure.
1790 sizeof(DROPFILES
); // Offset to start of file name char array.
1792 pDropFile
->pt
.x
= 0;
1793 pDropFile
->pt
.y
= 0;
1794 pDropFile
->fWide
= TRUE
;
1796 // Copy the filename right after the DROPFILES structure.
1797 char16_t
* dest
= (char16_t
*)(((char*)pDropFile
) + pDropFile
->pFiles
);
1798 memcpy(dest
, path
.get(),
1800 sizeof(char16_t
)); // Copies the null character in path as well.
1802 // Two null characters are needed at the end of the file name.
1803 // Lookup the CF_HDROP shell clipboard format for more info.
1804 // Add the second null character right after the first one.
1805 dest
[allocLen
- 1] = L
'\0';
1807 GlobalUnlock(hGlobalMemory
);
1809 aSTG
.hGlobal
= hGlobalMemory
;
1814 //-----------------------------------------------------
1815 // Registers the DataFlavor/FE pair.
1816 //-----------------------------------------------------
1817 void nsDataObj::AddDataFlavor(const char* aDataFlavor
, LPFORMATETC aFE
) {
1818 // These two lists are the mapping to and from data flavors and FEs.
1819 // Later, OLE will tell us it needs a certain type of FORMATETC (text,
1820 // unicode, etc) unicode, etc), so we will look up the data flavor that
1821 // corresponds to the FE and then ask the transferable for that type of data.
1822 mDataFlavors
.AppendElement(aDataFlavor
);
1823 m_enumFE
->AddFormatEtc(aFE
);
1826 //-----------------------------------------------------
1827 // Sets the transferable object
1828 //-----------------------------------------------------
1829 void nsDataObj::SetTransferable(nsITransferable
* aTransferable
) {
1830 NS_IF_RELEASE(mTransferable
);
1832 mTransferable
= aTransferable
;
1833 if (nullptr == mTransferable
) {
1837 NS_ADDREF(mTransferable
);
1845 // Roots around in the transferable for the appropriate flavor that indicates
1846 // a url and pulls out the url portion of the data. Used mostly for creating
1847 // internet shortcuts on the desktop. The url flavor is of the format:
1849 // <url> <linefeed> <page title>
1851 nsresult
nsDataObj ::ExtractShortcutURL(nsString
& outURL
) {
1852 NS_ASSERTION(mTransferable
, "We don't have a good transferable");
1853 nsresult rv
= NS_ERROR_FAILURE
;
1855 nsCOMPtr
<nsISupports
> genericURL
;
1856 if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1857 kURLMime
, getter_AddRefs(genericURL
)))) {
1858 nsCOMPtr
<nsISupportsString
> urlObject(do_QueryInterface(genericURL
));
1861 urlObject
->GetData(url
);
1864 // find the first linefeed in the data, that's where the url ends. trunc
1865 // the result string at that point.
1866 int32_t lineIndex
= outURL
.FindChar('\n');
1867 NS_ASSERTION(lineIndex
> 0,
1868 "Format for url flavor is <url> <linefeed> <page title>");
1869 if (lineIndex
> 0) {
1870 outURL
.Truncate(lineIndex
);
1874 } else if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1875 kURLDataMime
, getter_AddRefs(genericURL
))) ||
1876 NS_SUCCEEDED(mTransferable
->GetTransferData(
1877 kURLPrivateMime
, getter_AddRefs(genericURL
)))) {
1878 nsCOMPtr
<nsISupportsString
> urlObject(do_QueryInterface(genericURL
));
1881 urlObject
->GetData(url
);
1887 } // if found flavor
1891 } // ExtractShortcutURL
1894 // ExtractShortcutTitle
1896 // Roots around in the transferable for the appropriate flavor that indicates
1897 // a url and pulls out the title portion of the data. Used mostly for creating
1898 // internet shortcuts on the desktop. The url flavor is of the format:
1900 // <url> <linefeed> <page title>
1902 nsresult
nsDataObj ::ExtractShortcutTitle(nsString
& outTitle
) {
1903 NS_ASSERTION(mTransferable
, "We'd don't have a good transferable");
1904 nsresult rv
= NS_ERROR_FAILURE
;
1906 nsCOMPtr
<nsISupports
> genericURL
;
1907 if (NS_SUCCEEDED(mTransferable
->GetTransferData(
1908 kURLMime
, getter_AddRefs(genericURL
)))) {
1909 nsCOMPtr
<nsISupportsString
> urlObject(do_QueryInterface(genericURL
));
1912 urlObject
->GetData(url
);
1914 // find the first linefeed in the data, that's where the url ends. we want
1915 // everything after that linefeed. FindChar() returns -1 if we can't find
1916 int32_t lineIndex
= url
.FindChar('\n');
1917 NS_ASSERTION(lineIndex
!= -1,
1918 "Format for url flavor is <url> <linefeed> <page title>");
1919 if (lineIndex
!= -1) {
1920 url
.Mid(outTitle
, lineIndex
+ 1, url
.Length() - (lineIndex
+ 1));
1924 } // if found flavor
1928 } // ExtractShortcutTitle
1931 // BuildPlatformHTML
1933 // Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
1934 // header information on it. This will null-terminate |outPlatformHTML|. See
1935 // https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
1938 // We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
1939 // or <BODY> tags). We'll wrap the fragment with them to make other apps
1942 nsresult
nsDataObj ::BuildPlatformHTML(const char* inOurHTML
,
1943 char** outPlatformHTML
) {
1944 *outPlatformHTML
= nullptr;
1945 nsDependentCString
inHTMLString(inOurHTML
);
1947 // Do we already have mSourceURL from a drag?
1948 if (mSourceURL
.IsEmpty()) {
1950 ExtractShortcutURL(url
);
1952 AppendUTF16toUTF8(url
, mSourceURL
);
1955 constexpr auto kStartHTMLPrefix
= "Version:0.9\r\nStartHTML:"_ns
;
1956 constexpr auto kEndHTMLPrefix
= "\r\nEndHTML:"_ns
;
1957 constexpr auto kStartFragPrefix
= "\r\nStartFragment:"_ns
;
1958 constexpr auto kEndFragPrefix
= "\r\nEndFragment:"_ns
;
1959 constexpr auto kStartSourceURLPrefix
= "\r\nSourceURL:"_ns
;
1960 constexpr auto kEndFragTrailer
= "\r\n"_ns
;
1962 // The CF_HTML's size is embedded in the fragment, in such a way that the
1963 // number of digits in the size is part of the size itself. While it _is_
1964 // technically possible to compute the necessary size of the size-field
1965 // precisely -- by trial and error, if nothing else -- it's simpler just to
1966 // pick a rough but generous estimate and zero-pad it. (Zero-padding is
1967 // explicitly permitted by the format definition.)
1969 // Originally, in 2001, the "rough but generous estimate" was 8 digits. While
1970 // a maximum size of (10**9 - 1) bytes probably would have covered all
1971 // possible use-cases at the time, it's somewhat more likely to overflow
1972 // nowadays. Nonetheless, for the sake of backwards compatibility with any
1973 // misbehaving consumers of our existing CF_HTML output, we retain exactly
1974 // that padding for (most) fragments where it suffices. (No such misbehaving
1975 // consumers are actually known, so this is arguably paranoia.)
1977 // It is now 2022. A padding size of 16 will cover up to about 8.8 petabytes,
1978 // which should be enough for at least the next few years or so.
1979 const size_t numberLength
= inHTMLString
.Length() < 9999'0000 ? 8 : 16;
1981 const size_t sourceURLLength
= mSourceURL
.Length();
1983 const size_t fixedHeaderLen
=
1984 kStartHTMLPrefix
.Length() + kEndHTMLPrefix
.Length() +
1985 kStartFragPrefix
.Length() + kEndFragPrefix
.Length() +
1986 kEndFragTrailer
.Length() + (4 * numberLength
);
1988 const size_t totalHeaderLen
=
1989 fixedHeaderLen
+ (sourceURLLength
> 0
1990 ? kStartSourceURLPrefix
.Length() + sourceURLLength
1993 constexpr auto kHeaderString
= "<html><body>\r\n<!--StartFragment-->"_ns
;
1994 constexpr auto kTrailingString
=
1995 "<!--EndFragment-->\r\n"
1999 // calculate the offsets
2000 size_t startHTMLOffset
= totalHeaderLen
;
2001 size_t startFragOffset
= startHTMLOffset
+ kHeaderString
.Length();
2003 size_t endFragOffset
= startFragOffset
+ inHTMLString
.Length();
2004 size_t endHTMLOffset
= endFragOffset
+ kTrailingString
.Length();
2006 // now build the final version
2007 nsCString clipboardString
;
2008 clipboardString
.SetCapacity(endHTMLOffset
);
2010 const int numberLengthInt
= static_cast<int>(numberLength
);
2011 clipboardString
.Append(kStartHTMLPrefix
);
2012 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, startHTMLOffset
);
2014 clipboardString
.Append(kEndHTMLPrefix
);
2015 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, endHTMLOffset
);
2017 clipboardString
.Append(kStartFragPrefix
);
2018 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, startFragOffset
);
2020 clipboardString
.Append(kEndFragPrefix
);
2021 clipboardString
.AppendPrintf("%0*zu", numberLengthInt
, endFragOffset
);
2023 if (sourceURLLength
> 0) {
2024 clipboardString
.Append(kStartSourceURLPrefix
);
2025 clipboardString
.Append(mSourceURL
);
2028 clipboardString
.Append(kEndFragTrailer
);
2030 // Assert that the positional values were correct as we pass by their
2031 // corresponding positions.
2032 MOZ_ASSERT(clipboardString
.Length() == startHTMLOffset
);
2033 clipboardString
.Append(kHeaderString
);
2034 MOZ_ASSERT(clipboardString
.Length() == startFragOffset
);
2035 clipboardString
.Append(inHTMLString
);
2036 MOZ_ASSERT(clipboardString
.Length() == endFragOffset
);
2037 clipboardString
.Append(kTrailingString
);
2038 MOZ_ASSERT(clipboardString
.Length() == endHTMLOffset
);
2040 *outPlatformHTML
= ToNewCString(clipboardString
, mozilla::fallible
);
2041 if (!*outPlatformHTML
) return NS_ERROR_OUT_OF_MEMORY
;
2047 nsDataObj ::GetUniformResourceLocator(FORMATETC
& aFE
, STGMEDIUM
& aSTG
,
2050 if (IsFlavourPresent(kURLMime
)) {
2052 res
= ExtractUniformResourceLocatorW(aFE
, aSTG
);
2054 res
= ExtractUniformResourceLocatorA(aFE
, aSTG
);
2056 NS_WARNING("Not yet implemented\n");
2061 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2062 HRESULT result
= S_OK
;
2065 if (NS_FAILED(ExtractShortcutURL(url
))) return E_OUTOFMEMORY
;
2067 NS_LossyConvertUTF16toASCII
asciiUrl(url
);
2068 const int totalLen
= asciiUrl
.Length() + 1;
2069 HGLOBAL hGlobalMemory
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, totalLen
);
2070 if (!hGlobalMemory
) return E_OUTOFMEMORY
;
2072 char* contents
= reinterpret_cast<char*>(GlobalLock(hGlobalMemory
));
2074 GlobalFree(hGlobalMemory
);
2075 return E_OUTOFMEMORY
;
2078 strcpy(contents
, asciiUrl
.get());
2079 GlobalUnlock(hGlobalMemory
);
2080 aSTG
.hGlobal
= hGlobalMemory
;
2081 aSTG
.tymed
= TYMED_HGLOBAL
;
2087 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2088 HRESULT result
= S_OK
;
2091 if (NS_FAILED(ExtractShortcutURL(url
))) return E_OUTOFMEMORY
;
2093 const int totalLen
= (url
.Length() + 1) * sizeof(char16_t
);
2094 HGLOBAL hGlobalMemory
= GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, totalLen
);
2095 if (!hGlobalMemory
) return E_OUTOFMEMORY
;
2097 wchar_t* contents
= reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory
));
2099 GlobalFree(hGlobalMemory
);
2100 return E_OUTOFMEMORY
;
2103 wcscpy(contents
, url
.get());
2104 GlobalUnlock(hGlobalMemory
);
2105 aSTG
.hGlobal
= hGlobalMemory
;
2106 aSTG
.tymed
= TYMED_HGLOBAL
;
2111 // Gets the filename from the kFilePromiseURLMime flavour
2112 HRESULT
nsDataObj::GetDownloadDetails(nsIURI
** aSourceURI
,
2113 nsAString
& aFilename
) {
2114 *aSourceURI
= nullptr;
2116 NS_ENSURE_TRUE(mTransferable
, E_FAIL
);
2118 // get the URI from the kFilePromiseURLMime flavor
2119 nsCOMPtr
<nsISupports
> urlPrimitive
;
2120 nsresult rv
= mTransferable
->GetTransferData(kFilePromiseURLMime
,
2121 getter_AddRefs(urlPrimitive
));
2122 NS_ENSURE_SUCCESS(rv
, E_FAIL
);
2123 nsCOMPtr
<nsISupportsString
> srcUrlPrimitive
= do_QueryInterface(urlPrimitive
);
2124 NS_ENSURE_TRUE(srcUrlPrimitive
, E_FAIL
);
2126 nsAutoString srcUri
;
2127 srcUrlPrimitive
->GetData(srcUri
);
2128 if (srcUri
.IsEmpty()) return E_FAIL
;
2129 nsCOMPtr
<nsIURI
> sourceURI
;
2130 NS_NewURI(getter_AddRefs(sourceURI
), srcUri
);
2132 nsAutoString srcFileName
;
2133 nsCOMPtr
<nsISupports
> fileNamePrimitive
;
2134 Unused
<< mTransferable
->GetTransferData(kFilePromiseDestFilename
,
2135 getter_AddRefs(fileNamePrimitive
));
2136 nsCOMPtr
<nsISupportsString
> srcFileNamePrimitive
=
2137 do_QueryInterface(fileNamePrimitive
);
2138 if (srcFileNamePrimitive
) {
2139 srcFileNamePrimitive
->GetData(srcFileName
);
2141 nsCOMPtr
<nsIURL
> sourceURL
= do_QueryInterface(sourceURI
);
2142 if (!sourceURL
) return E_FAIL
;
2144 nsAutoCString urlFileName
;
2145 sourceURL
->GetFileName(urlFileName
);
2146 NS_UnescapeURL(urlFileName
);
2147 CopyUTF8toUTF16(urlFileName
, srcFileName
);
2150 // make the name safe for the filesystem
2151 ValidateFilename(srcFileName
, false);
2152 if (srcFileName
.IsEmpty()) return E_FAIL
;
2154 sourceURI
.swap(*aSourceURI
);
2155 aFilename
= srcFileName
;
2159 HRESULT
nsDataObj::GetFileDescriptor_IStreamA(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2160 HGLOBAL fileGroupDescHandle
=
2161 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORW
));
2162 NS_ENSURE_TRUE(fileGroupDescHandle
, E_OUTOFMEMORY
);
2164 LPFILEGROUPDESCRIPTORA fileGroupDescA
=
2165 reinterpret_cast<LPFILEGROUPDESCRIPTORA
>(GlobalLock(fileGroupDescHandle
));
2166 if (!fileGroupDescA
) {
2167 ::GlobalFree(fileGroupDescHandle
);
2168 return E_OUTOFMEMORY
;
2171 nsAutoString wideFileName
;
2173 nsCOMPtr
<nsIURI
> sourceURI
;
2174 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
2176 ::GlobalFree(fileGroupDescHandle
);
2180 nsAutoCString nativeFileName
;
2181 NS_CopyUnicodeToNative(wideFileName
, nativeFileName
);
2183 strncpy(fileGroupDescA
->fgd
[0].cFileName
, nativeFileName
.get(), MAX_PATH
- 1);
2184 fileGroupDescA
->fgd
[0].cFileName
[MAX_PATH
- 1] = '\0';
2186 // one file in the file block
2187 fileGroupDescA
->cItems
= 1;
2188 fileGroupDescA
->fgd
[0].dwFlags
= FD_PROGRESSUI
;
2190 GlobalUnlock(fileGroupDescHandle
);
2191 aSTG
.hGlobal
= fileGroupDescHandle
;
2192 aSTG
.tymed
= TYMED_HGLOBAL
;
2197 HRESULT
nsDataObj::GetFileDescriptor_IStreamW(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2198 HGLOBAL fileGroupDescHandle
=
2199 ::GlobalAlloc(GMEM_ZEROINIT
| GMEM_SHARE
, sizeof(FILEGROUPDESCRIPTORW
));
2200 NS_ENSURE_TRUE(fileGroupDescHandle
, E_OUTOFMEMORY
);
2202 LPFILEGROUPDESCRIPTORW fileGroupDescW
=
2203 reinterpret_cast<LPFILEGROUPDESCRIPTORW
>(GlobalLock(fileGroupDescHandle
));
2204 if (!fileGroupDescW
) {
2205 ::GlobalFree(fileGroupDescHandle
);
2206 return E_OUTOFMEMORY
;
2209 nsAutoString wideFileName
;
2211 nsCOMPtr
<nsIURI
> sourceURI
;
2212 res
= GetDownloadDetails(getter_AddRefs(sourceURI
), wideFileName
);
2214 ::GlobalFree(fileGroupDescHandle
);
2218 wcsncpy(fileGroupDescW
->fgd
[0].cFileName
, wideFileName
.get(), MAX_PATH
- 1);
2219 fileGroupDescW
->fgd
[0].cFileName
[MAX_PATH
- 1] = '\0';
2220 // one file in the file block
2221 fileGroupDescW
->cItems
= 1;
2222 fileGroupDescW
->fgd
[0].dwFlags
= FD_PROGRESSUI
;
2224 GlobalUnlock(fileGroupDescHandle
);
2225 aSTG
.hGlobal
= fileGroupDescHandle
;
2226 aSTG
.tymed
= TYMED_HGLOBAL
;
2231 HRESULT
nsDataObj::GetFileContents_IStream(FORMATETC
& aFE
, STGMEDIUM
& aSTG
) {
2232 IStream
* pStream
= nullptr;
2234 nsDataObj::CreateStream(&pStream
);
2235 NS_ENSURE_TRUE(pStream
, E_FAIL
);
2237 aSTG
.tymed
= TYMED_ISTREAM
;
2238 aSTG
.pstm
= pStream
;
2239 aSTG
.pUnkForRelease
= nullptr;