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