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