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