Bumping manifests a=b2g-bump
[gecko.git] / widget / windows / nsDataObj.cpp
blob44c4865f806d8e8169c5b9b127579f89a7c31611
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"
32 #include "nsIContentPolicy.h"
33 #include "nsContentUtils.h"
34 #include "nsINode.h"
36 #include "WinUtils.h"
37 #include "mozilla/LazyIdleThread.h"
38 #include "mozilla/WindowsVersion.h"
39 #include <algorithm>
42 using namespace mozilla;
43 using namespace mozilla::widget;
45 #define DEFAULT_THREAD_TIMEOUT_MS 30000
47 NS_IMPL_ISUPPORTS(nsDataObj::CStream, nsIStreamListener)
49 //-----------------------------------------------------------------------------
50 // CStream implementation
51 nsDataObj::CStream::CStream() :
52 mChannelRead(false),
53 mStreamRead(0)
57 //-----------------------------------------------------------------------------
58 nsDataObj::CStream::~CStream()
62 //-----------------------------------------------------------------------------
63 // helper - initializes the stream
64 nsresult nsDataObj::CStream::Init(nsIURI *pSourceURI,
65 nsINode* aRequestingNode)
67 // we can not create a channel without a requestingNode
68 if (!aRequestingNode) {
69 return NS_ERROR_FAILURE;
71 nsresult rv;
72 rv = NS_NewChannel(getter_AddRefs(mChannel),
73 pSourceURI,
74 aRequestingNode,
75 nsILoadInfo::SEC_NORMAL,
76 nsIContentPolicy::TYPE_OTHER,
77 nullptr, // loadGroup
78 nullptr, // aCallbacks
79 nsIRequest::LOAD_FROM_CACHE);
81 NS_ENSURE_SUCCESS(rv, rv);
82 rv = mChannel->AsyncOpen(this, nullptr);
83 NS_ENSURE_SUCCESS(rv, rv);
84 return NS_OK;
87 //-----------------------------------------------------------------------------
88 // IUnknown's QueryInterface, nsISupport's AddRef and Release are shared by
89 // IUnknown and nsIStreamListener.
90 STDMETHODIMP nsDataObj::CStream::QueryInterface(REFIID refiid, void** ppvResult)
92 *ppvResult = nullptr;
93 if (IID_IUnknown == refiid ||
94 refiid == IID_IStream)
97 *ppvResult = this;
100 if (nullptr != *ppvResult)
102 ((LPUNKNOWN)*ppvResult)->AddRef();
103 return S_OK;
106 return E_NOINTERFACE;
109 // nsIStreamListener implementation
110 NS_IMETHODIMP
111 nsDataObj::CStream::OnDataAvailable(nsIRequest *aRequest,
112 nsISupports *aContext,
113 nsIInputStream *aInputStream,
114 uint64_t aOffset, // offset within the stream
115 uint32_t aCount) // bytes available on this call
117 // Extend the write buffer for the incoming data.
118 uint8_t* buffer = mChannelData.AppendElements(aCount);
119 if (buffer == nullptr)
120 return NS_ERROR_OUT_OF_MEMORY;
121 NS_ASSERTION((mChannelData.Length() == (aOffset + aCount)),
122 "stream length mismatch w/write buffer");
124 // Read() may not return aCount on a single call, so loop until we've
125 // accumulated all the data OnDataAvailable has promised.
126 nsresult rv;
127 uint32_t odaBytesReadTotal = 0;
128 do {
129 uint32_t bytesReadByCall = 0;
130 rv = aInputStream->Read((char*)(buffer + odaBytesReadTotal),
131 aCount, &bytesReadByCall);
132 odaBytesReadTotal += bytesReadByCall;
133 } while (aCount < odaBytesReadTotal && NS_SUCCEEDED(rv));
134 return rv;
137 NS_IMETHODIMP nsDataObj::CStream::OnStartRequest(nsIRequest *aRequest,
138 nsISupports *aContext)
140 mChannelResult = NS_OK;
141 return NS_OK;
144 NS_IMETHODIMP nsDataObj::CStream::OnStopRequest(nsIRequest *aRequest,
145 nsISupports *aContext,
146 nsresult aStatusCode)
148 mChannelRead = true;
149 mChannelResult = aStatusCode;
150 return NS_OK;
153 // Pumps thread messages while waiting for the async listener operation to
154 // complete. Failing this call will fail the stream incall from Windows
155 // and cancel the operation.
156 nsresult nsDataObj::CStream::WaitForCompletion()
158 // We are guaranteed OnStopRequest will get called, so this should be ok.
159 while (!mChannelRead) {
160 // Pump messages
161 NS_ProcessNextEvent(nullptr, true);
164 if (!mChannelData.Length())
165 mChannelResult = NS_ERROR_FAILURE;
167 return mChannelResult;
170 //-----------------------------------------------------------------------------
171 // IStream
172 STDMETHODIMP nsDataObj::CStream::Clone(IStream** ppStream)
174 return E_NOTIMPL;
177 //-----------------------------------------------------------------------------
178 STDMETHODIMP nsDataObj::CStream::Commit(DWORD dwFrags)
180 return E_NOTIMPL;
183 //-----------------------------------------------------------------------------
184 STDMETHODIMP nsDataObj::CStream::CopyTo(IStream* pDestStream,
185 ULARGE_INTEGER nBytesToCopy,
186 ULARGE_INTEGER* nBytesRead,
187 ULARGE_INTEGER* nBytesWritten)
189 return E_NOTIMPL;
192 //-----------------------------------------------------------------------------
193 STDMETHODIMP nsDataObj::CStream::LockRegion(ULARGE_INTEGER nStart,
194 ULARGE_INTEGER nBytes,
195 DWORD dwFlags)
197 return E_NOTIMPL;
200 //-----------------------------------------------------------------------------
201 STDMETHODIMP nsDataObj::CStream::Read(void* pvBuffer,
202 ULONG nBytesToRead,
203 ULONG* nBytesRead)
205 // Wait for the write into our buffer to complete via the stream listener.
206 // We can't respond to this by saying "call us back later".
207 if (NS_FAILED(WaitForCompletion()))
208 return E_FAIL;
210 // Bytes left for Windows to read out of our buffer
211 ULONG bytesLeft = mChannelData.Length() - mStreamRead;
212 // Let Windows know what we will hand back, usually this is the entire buffer
213 *nBytesRead = std::min(bytesLeft, nBytesToRead);
214 // Copy the buffer data over
215 memcpy(pvBuffer, ((char*)mChannelData.Elements() + mStreamRead), *nBytesRead);
216 // Update our bytes read tracking
217 mStreamRead += *nBytesRead;
218 return S_OK;
221 //-----------------------------------------------------------------------------
222 STDMETHODIMP nsDataObj::CStream::Revert(void)
224 return E_NOTIMPL;
227 //-----------------------------------------------------------------------------
228 STDMETHODIMP nsDataObj::CStream::Seek(LARGE_INTEGER nMove,
229 DWORD dwOrigin,
230 ULARGE_INTEGER* nNewPos)
232 if (nNewPos == nullptr)
233 return STG_E_INVALIDPOINTER;
235 if (nMove.LowPart == 0 && nMove.HighPart == 0 &&
236 (dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR)) {
237 nNewPos->LowPart = 0;
238 nNewPos->HighPart = 0;
239 return S_OK;
242 return E_NOTIMPL;
245 //-----------------------------------------------------------------------------
246 STDMETHODIMP nsDataObj::CStream::SetSize(ULARGE_INTEGER nNewSize)
248 return E_NOTIMPL;
251 //-----------------------------------------------------------------------------
252 STDMETHODIMP nsDataObj::CStream::Stat(STATSTG* statstg, DWORD dwFlags)
254 if (statstg == nullptr)
255 return STG_E_INVALIDPOINTER;
257 if (!mChannel || NS_FAILED(WaitForCompletion()))
258 return E_FAIL;
260 memset((void*)statstg, 0, sizeof(STATSTG));
262 if (dwFlags != STATFLAG_NONAME)
264 nsCOMPtr<nsIURI> sourceURI;
265 if (NS_FAILED(mChannel->GetURI(getter_AddRefs(sourceURI)))) {
266 return E_FAIL;
269 nsAutoCString strFileName;
270 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
271 sourceURL->GetFileName(strFileName);
273 if (strFileName.IsEmpty())
274 return E_FAIL;
276 NS_UnescapeURL(strFileName);
277 NS_ConvertUTF8toUTF16 wideFileName(strFileName);
279 uint32_t nMaxNameLength = (wideFileName.Length()*2) + 2;
280 void * retBuf = CoTaskMemAlloc(nMaxNameLength); // freed by caller
281 if (!retBuf)
282 return STG_E_INSUFFICIENTMEMORY;
284 ZeroMemory(retBuf, nMaxNameLength);
285 memcpy(retBuf, wideFileName.get(), wideFileName.Length()*2);
286 statstg->pwcsName = (LPOLESTR)retBuf;
289 SYSTEMTIME st;
291 statstg->type = STGTY_STREAM;
293 GetSystemTime(&st);
294 SystemTimeToFileTime((const SYSTEMTIME*)&st, (LPFILETIME)&statstg->mtime);
295 statstg->ctime = statstg->atime = statstg->mtime;
297 statstg->cbSize.LowPart = (DWORD)mChannelData.Length();
298 statstg->grfMode = STGM_READ;
299 statstg->grfLocksSupported = LOCK_ONLYONCE;
300 statstg->clsid = CLSID_NULL;
302 return S_OK;
305 //-----------------------------------------------------------------------------
306 STDMETHODIMP nsDataObj::CStream::UnlockRegion(ULARGE_INTEGER nStart,
307 ULARGE_INTEGER nBytes,
308 DWORD dwFlags)
310 return E_NOTIMPL;
313 //-----------------------------------------------------------------------------
314 STDMETHODIMP nsDataObj::CStream::Write(const void* pvBuffer,
315 ULONG nBytesToRead,
316 ULONG* nBytesRead)
318 return E_NOTIMPL;
321 //-----------------------------------------------------------------------------
322 HRESULT nsDataObj::CreateStream(IStream **outStream)
324 NS_ENSURE_TRUE(outStream, E_INVALIDARG);
326 nsresult rv = NS_ERROR_FAILURE;
327 nsAutoString wideFileName;
328 nsCOMPtr<nsIURI> sourceURI;
329 HRESULT res;
331 res = GetDownloadDetails(getter_AddRefs(sourceURI),
332 wideFileName);
333 if(FAILED(res))
334 return res;
336 nsDataObj::CStream *pStream = new nsDataObj::CStream();
337 NS_ENSURE_TRUE(pStream, E_OUTOFMEMORY);
339 pStream->AddRef();
341 // query the requestingNode from the transferable and add it to the new channel
342 nsCOMPtr<nsIDOMNode> requestingDomNode;
343 mTransferable->GetRequestingNode(getter_AddRefs(requestingDomNode));
344 nsCOMPtr<nsINode> requestingNode = do_QueryInterface(requestingDomNode);
345 MOZ_ASSERT(requestingNode, "can not create channel without a node");
347 rv = pStream->Init(sourceURI, requestingNode);
348 if (NS_FAILED(rv))
350 pStream->Release();
351 return E_FAIL;
353 *outStream = pStream;
355 return S_OK;
358 static GUID CLSID_nsDataObj =
359 { 0x1bba7640, 0xdf52, 0x11cf, { 0x82, 0x7b, 0, 0xa0, 0x24, 0x3a, 0xe5, 0x05 } };
362 * deliberately not using MAX_PATH. This is because on platforms < XP
363 * a file created with a long filename may be mishandled by the shell
364 * resulting in it not being able to be deleted or moved.
365 * See bug 250392 for more details.
367 #define NS_MAX_FILEDESCRIPTOR 128 + 1
370 * Class nsDataObj
373 //-----------------------------------------------------
374 // construction
375 //-----------------------------------------------------
376 nsDataObj::nsDataObj(nsIURI * uri)
377 : m_cRef(0), mTransferable(nullptr),
378 mIsAsyncMode(FALSE), mIsInOperation(FALSE)
380 mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
381 NS_LITERAL_CSTRING("nsDataObj"),
382 LazyIdleThread::ManualShutdown);
383 m_enumFE = new CEnumFormatEtc();
384 m_enumFE->AddRef();
386 if (uri) {
387 // A URI was obtained, so pass this through to the DataObject
388 // so it can create a SourceURL for CF_HTML flavour
389 uri->GetSpec(mSourceURL);
392 //-----------------------------------------------------
393 // destruction
394 //-----------------------------------------------------
395 nsDataObj::~nsDataObj()
397 NS_IF_RELEASE(mTransferable);
399 mDataFlavors.Clear();
401 m_enumFE->Release();
403 // Free arbitrary system formats
404 for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
405 CoTaskMemFree(mDataEntryList[idx]->fe.ptd);
406 ReleaseStgMedium(&mDataEntryList[idx]->stgm);
407 CoTaskMemFree(mDataEntryList[idx]);
412 //-----------------------------------------------------
413 // IUnknown interface methods - see inknown.h for documentation
414 //-----------------------------------------------------
415 STDMETHODIMP nsDataObj::QueryInterface(REFIID riid, void** ppv)
417 *ppv=nullptr;
419 if ( (IID_IUnknown == riid) || (IID_IDataObject == riid) ) {
420 *ppv = this;
421 AddRef();
422 return S_OK;
423 } else if (IID_IAsyncOperation == riid) {
424 *ppv = static_cast<IAsyncOperation*>(this);
425 AddRef();
426 return S_OK;
429 return E_NOINTERFACE;
432 //-----------------------------------------------------
433 STDMETHODIMP_(ULONG) nsDataObj::AddRef()
435 ++m_cRef;
436 NS_LOG_ADDREF(this, m_cRef, "nsDataObj", sizeof(*this));
437 return m_cRef;
441 //-----------------------------------------------------
442 STDMETHODIMP_(ULONG) nsDataObj::Release()
444 --m_cRef;
446 NS_LOG_RELEASE(this, m_cRef, "nsDataObj");
447 if (0 != m_cRef)
448 return m_cRef;
450 // We have released our last ref on this object and need to delete the
451 // temp file. External app acting as drop target may still need to open the
452 // temp file. Addref a timer so it can delay deleting file and destroying
453 // this object. Delete file anyway and destroy this obj if there's a problem.
454 if (mCachedTempFile) {
455 nsresult rv;
456 mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
457 if (NS_SUCCEEDED(rv)) {
458 mTimer->InitWithFuncCallback(nsDataObj::RemoveTempFile, this,
459 500, nsITimer::TYPE_ONE_SHOT);
460 return AddRef();
462 mCachedTempFile->Remove(false);
463 mCachedTempFile = nullptr;
466 delete this;
468 return 0;
471 //-----------------------------------------------------
472 BOOL nsDataObj::FormatsMatch(const FORMATETC& source, const FORMATETC& target) const
474 if ((source.cfFormat == target.cfFormat) &&
475 (source.dwAspect & target.dwAspect) &&
476 (source.tymed & target.tymed)) {
477 return TRUE;
478 } else {
479 return FALSE;
483 //-----------------------------------------------------
484 // IDataObject methods
485 //-----------------------------------------------------
486 STDMETHODIMP nsDataObj::GetData(LPFORMATETC aFormat, LPSTGMEDIUM pSTM)
488 if (!mTransferable)
489 return DV_E_FORMATETC;
491 uint32_t dfInx = 0;
493 static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA );
494 static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW );
495 static CLIPFORMAT uniformResourceLocatorA = ::RegisterClipboardFormat( CFSTR_INETURLA );
496 static CLIPFORMAT uniformResourceLocatorW = ::RegisterClipboardFormat( CFSTR_INETURLW );
497 static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS );
498 static CLIPFORMAT PreferredDropEffect = ::RegisterClipboardFormat( CFSTR_PREFERREDDROPEFFECT );
500 // Arbitrary system formats are used for image feedback during drag
501 // and drop. We are responsible for storing these internally during
502 // drag operations.
503 LPDATAENTRY pde;
504 if (LookupArbitraryFormat(aFormat, &pde, FALSE)) {
505 return CopyMediumData(pSTM, &pde->stgm, aFormat, FALSE)
506 ? S_OK : E_UNEXPECTED;
509 // Firefox internal formats
510 ULONG count;
511 FORMATETC fe;
512 m_enumFE->Reset();
513 while (NOERROR == m_enumFE->Next(1, &fe, &count)
514 && dfInx < mDataFlavors.Length()) {
515 nsCString& df = mDataFlavors.ElementAt(dfInx);
516 if (FormatsMatch(fe, *aFormat)) {
517 pSTM->pUnkForRelease = nullptr; // caller is responsible for deleting this data
518 CLIPFORMAT format = aFormat->cfFormat;
519 switch(format) {
521 // Someone is asking for plain or unicode text
522 case CF_TEXT:
523 case CF_UNICODETEXT:
524 return GetText(df, *aFormat, *pSTM);
526 // Some 3rd party apps that receive drag and drop files from the browser
527 // window require support for this.
528 case CF_HDROP:
529 return GetFile(*aFormat, *pSTM);
531 // Someone is asking for an image
532 case CF_DIBV5:
533 case CF_DIB:
534 return GetDib(df, *aFormat, *pSTM);
536 default:
537 if ( format == fileDescriptorFlavorA )
538 return GetFileDescriptor ( *aFormat, *pSTM, false );
539 if ( format == fileDescriptorFlavorW )
540 return GetFileDescriptor ( *aFormat, *pSTM, true);
541 if ( format == uniformResourceLocatorA )
542 return GetUniformResourceLocator( *aFormat, *pSTM, false);
543 if ( format == uniformResourceLocatorW )
544 return GetUniformResourceLocator( *aFormat, *pSTM, true);
545 if ( format == fileFlavor )
546 return GetFileContents ( *aFormat, *pSTM );
547 if ( format == PreferredDropEffect )
548 return GetPreferredDropEffect( *aFormat, *pSTM );
549 //PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
550 // ("***** nsDataObj::GetData - Unknown format %u\n", format));
551 return GetText(df, *aFormat, *pSTM);
552 } //switch
553 } // if
554 dfInx++;
555 } // while
557 return DATA_E_FORMATETC;
560 //-----------------------------------------------------
561 STDMETHODIMP nsDataObj::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
563 return E_FAIL;
567 //-----------------------------------------------------
568 // Other objects querying to see if we support a
569 // particular format
570 //-----------------------------------------------------
571 STDMETHODIMP nsDataObj::QueryGetData(LPFORMATETC pFE)
573 // Arbitrary system formats are used for image feedback during drag
574 // and drop. We are responsible for storing these internally during
575 // drag operations.
576 LPDATAENTRY pde;
577 if (LookupArbitraryFormat(pFE, &pde, FALSE))
578 return S_OK;
580 // Firefox internal formats
581 ULONG count;
582 FORMATETC fe;
583 m_enumFE->Reset();
584 while (NOERROR == m_enumFE->Next(1, &fe, &count)) {
585 if (fe.cfFormat == pFE->cfFormat) {
586 return S_OK;
589 return E_FAIL;
592 //-----------------------------------------------------
593 STDMETHODIMP nsDataObj::GetCanonicalFormatEtc
594 (LPFORMATETC pFEIn, LPFORMATETC pFEOut)
596 return E_NOTIMPL;
599 //-----------------------------------------------------
600 STDMETHODIMP nsDataObj::SetData(LPFORMATETC aFormat, LPSTGMEDIUM aMedium, BOOL shouldRel)
602 // Arbitrary system formats are used for image feedback during drag
603 // and drop. We are responsible for storing these internally during
604 // drag operations.
605 LPDATAENTRY pde;
606 if (LookupArbitraryFormat(aFormat, &pde, TRUE)) {
607 // Release the old data the lookup handed us for this format. This
608 // may have been set in CopyMediumData when we originally stored the
609 // data.
610 if (pde->stgm.tymed) {
611 ReleaseStgMedium(&pde->stgm);
612 memset(&pde->stgm, 0, sizeof(STGMEDIUM));
615 bool result = true;
616 if (shouldRel) {
617 // If shouldRel is TRUE, the data object called owns the storage medium
618 // after the call returns. Store the incoming data in our data array for
619 // release when we are destroyed. This is the common case with arbitrary
620 // data from explorer.
621 pde->stgm = *aMedium;
622 } else {
623 // Copy the incoming data into our data array. (AFAICT, this never gets
624 // called with arbitrary formats for drag images.)
625 result = CopyMediumData(&pde->stgm, aMedium, aFormat, TRUE);
627 pde->fe.tymed = pde->stgm.tymed;
629 return result ? S_OK : DV_E_TYMED;
632 if (shouldRel)
633 ReleaseStgMedium(aMedium);
635 return S_OK;
638 bool
639 nsDataObj::LookupArbitraryFormat(FORMATETC *aFormat, LPDATAENTRY *aDataEntry, BOOL aAddorUpdate)
641 *aDataEntry = nullptr;
643 if (aFormat->ptd != nullptr)
644 return false;
646 // See if it's already in our list. If so return the data entry.
647 for (uint32_t idx = 0; idx < mDataEntryList.Length(); idx++) {
648 if (mDataEntryList[idx]->fe.cfFormat == aFormat->cfFormat &&
649 mDataEntryList[idx]->fe.dwAspect == aFormat->dwAspect &&
650 mDataEntryList[idx]->fe.lindex == aFormat->lindex) {
651 if (aAddorUpdate || (mDataEntryList[idx]->fe.tymed & aFormat->tymed)) {
652 // If the caller requests we update, or if the
653 // medium type matches, return the entry.
654 *aDataEntry = mDataEntryList[idx];
655 return true;
656 } else {
657 // Medium does not match, not found.
658 return false;
663 if (!aAddorUpdate)
664 return false;
666 // Add another entry to mDataEntryList
667 LPDATAENTRY dataEntry = (LPDATAENTRY)CoTaskMemAlloc(sizeof(DATAENTRY));
668 if (!dataEntry)
669 return false;
671 dataEntry->fe = *aFormat;
672 *aDataEntry = dataEntry;
673 memset(&dataEntry->stgm, 0, sizeof(STGMEDIUM));
675 // Add this to our IEnumFORMATETC impl. so we can return it when
676 // it's requested.
677 m_enumFE->AddFormatEtc(aFormat);
679 // Store a copy internally in the arbitrary formats array.
680 mDataEntryList.AppendElement(dataEntry);
682 return true;
685 bool
686 nsDataObj::CopyMediumData(STGMEDIUM *aMediumDst, STGMEDIUM *aMediumSrc, LPFORMATETC aFormat, BOOL aSetData)
688 STGMEDIUM stgmOut = *aMediumSrc;
690 switch (stgmOut.tymed) {
691 case TYMED_ISTREAM:
692 stgmOut.pstm->AddRef();
693 break;
694 case TYMED_ISTORAGE:
695 stgmOut.pstg->AddRef();
696 break;
697 case TYMED_HGLOBAL:
698 if (!aMediumSrc->pUnkForRelease) {
699 if (aSetData) {
700 if (aMediumSrc->tymed != TYMED_HGLOBAL)
701 return false;
702 stgmOut.hGlobal = OleDuplicateData(aMediumSrc->hGlobal, aFormat->cfFormat, 0);
703 if (!stgmOut.hGlobal)
704 return false;
705 } else {
706 // We are returning this data from LookupArbitraryFormat, indicate to the
707 // shell we hold it and will free it.
708 stgmOut.pUnkForRelease = static_cast<IDataObject*>(this);
711 break;
712 default:
713 return false;
716 if (stgmOut.pUnkForRelease)
717 stgmOut.pUnkForRelease->AddRef();
719 *aMediumDst = stgmOut;
721 return true;
724 //-----------------------------------------------------
725 STDMETHODIMP nsDataObj::EnumFormatEtc(DWORD dwDir, LPENUMFORMATETC *ppEnum)
727 switch (dwDir) {
728 case DATADIR_GET:
729 m_enumFE->Clone(ppEnum);
730 break;
731 case DATADIR_SET:
732 // fall through
733 default:
734 *ppEnum = nullptr;
735 } // switch
737 if (nullptr == *ppEnum)
738 return E_FAIL;
740 (*ppEnum)->Reset();
741 // Clone already AddRefed the result so don't addref it again.
742 return NOERROR;
745 //-----------------------------------------------------
746 STDMETHODIMP nsDataObj::DAdvise(LPFORMATETC pFE, DWORD dwFlags,
747 LPADVISESINK pIAdviseSink, DWORD* pdwConn)
749 return OLE_E_ADVISENOTSUPPORTED;
753 //-----------------------------------------------------
754 STDMETHODIMP nsDataObj::DUnadvise(DWORD dwConn)
756 return OLE_E_ADVISENOTSUPPORTED;
759 //-----------------------------------------------------
760 STDMETHODIMP nsDataObj::EnumDAdvise(LPENUMSTATDATA *ppEnum)
762 return OLE_E_ADVISENOTSUPPORTED;
765 // IAsyncOperation methods
766 STDMETHODIMP nsDataObj::EndOperation(HRESULT hResult,
767 IBindCtx *pbcReserved,
768 DWORD dwEffects)
770 mIsInOperation = FALSE;
771 return S_OK;
774 STDMETHODIMP nsDataObj::GetAsyncMode(BOOL *pfIsOpAsync)
776 *pfIsOpAsync = mIsAsyncMode;
778 return S_OK;
781 STDMETHODIMP nsDataObj::InOperation(BOOL *pfInAsyncOp)
783 *pfInAsyncOp = mIsInOperation;
785 return S_OK;
788 STDMETHODIMP nsDataObj::SetAsyncMode(BOOL fDoOpAsync)
790 mIsAsyncMode = fDoOpAsync;
791 return S_OK;
794 STDMETHODIMP nsDataObj::StartOperation(IBindCtx *pbcReserved)
796 mIsInOperation = TRUE;
797 return S_OK;
800 //-----------------------------------------------------
801 // GetData and SetData helper functions
802 //-----------------------------------------------------
803 HRESULT nsDataObj::AddSetFormat(FORMATETC& aFE)
805 return S_OK;
808 //-----------------------------------------------------
809 HRESULT nsDataObj::AddGetFormat(FORMATETC& aFE)
811 return S_OK;
815 // GetDIB
817 // Someone is asking for a bitmap. The data in the transferable will be a straight
818 // imgIContainer, so just QI it.
820 HRESULT
821 nsDataObj::GetDib(const nsACString& inFlavor,
822 FORMATETC &aFormat,
823 STGMEDIUM & aSTG)
825 ULONG result = E_FAIL;
826 uint32_t len = 0;
827 nsCOMPtr<nsISupports> genericDataWrapper;
828 mTransferable->GetTransferData(PromiseFlatCString(inFlavor).get(), getter_AddRefs(genericDataWrapper), &len);
829 nsCOMPtr<imgIContainer> image ( do_QueryInterface(genericDataWrapper) );
830 if ( !image ) {
831 // Check if the image was put in an nsISupportsInterfacePointer wrapper.
832 // This might not be necessary any more, but could be useful for backwards
833 // compatibility.
834 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
835 if ( ptr ) {
836 nsCOMPtr<nsISupports> supports;
837 ptr->GetData(getter_AddRefs(supports));
838 image = do_QueryInterface(supports);
842 if ( image ) {
843 // use the |nsImageToClipboard| helper class to build up a bitmap. We now own
844 // the bits, and pass them back to the OS in |aSTG|.
845 nsImageToClipboard converter(image, aFormat.cfFormat == CF_DIBV5);
846 HANDLE bits = nullptr;
847 nsresult rv = converter.GetPicture ( &bits );
848 if ( NS_SUCCEEDED(rv) && bits ) {
849 aSTG.hGlobal = bits;
850 aSTG.tymed = TYMED_HGLOBAL;
851 result = S_OK;
853 } // if we have an image
854 else
855 NS_WARNING ( "Definitely not an image on clipboard" );
856 return result;
862 // GetFileDescriptor
865 HRESULT
866 nsDataObj :: GetFileDescriptor ( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
868 HRESULT res = S_OK;
870 // How we handle this depends on if we're dealing with an internet
871 // shortcut, since those are done under the covers.
872 if (IsFlavourPresent(kFilePromiseMime) ||
873 IsFlavourPresent(kFileMime))
875 if (aIsUnicode)
876 return GetFileDescriptor_IStreamW(aFE, aSTG);
877 else
878 return GetFileDescriptor_IStreamA(aFE, aSTG);
880 else if (IsFlavourPresent(kURLMime))
882 if ( aIsUnicode )
883 res = GetFileDescriptorInternetShortcutW ( aFE, aSTG );
884 else
885 res = GetFileDescriptorInternetShortcutA ( aFE, aSTG );
887 else
888 NS_WARNING ( "Not yet implemented\n" );
890 return res;
891 } // GetFileDescriptor
895 HRESULT
896 nsDataObj :: GetFileContents ( FORMATETC& aFE, STGMEDIUM& aSTG )
898 HRESULT res = S_OK;
900 // How we handle this depends on if we're dealing with an internet
901 // shortcut, since those are done under the covers.
902 if (IsFlavourPresent(kFilePromiseMime) ||
903 IsFlavourPresent(kFileMime))
904 return GetFileContents_IStream(aFE, aSTG);
905 else if (IsFlavourPresent(kURLMime))
906 return GetFileContentsInternetShortcut ( aFE, aSTG );
907 else
908 NS_WARNING ( "Not yet implemented\n" );
910 return res;
912 } // GetFileContents
915 // Given a unicode string, we ensure that it contains only characters which are valid within
916 // the file system. Remove all forbidden characters from the name, and completely disallow
917 // any title that starts with a forbidden name and extension (e.g. "nul" is invalid, but
918 // "nul." and "nul.txt" are also invalid and will cause problems).
920 // It would seem that this is more functionality suited to being in nsIFile.
922 static void
923 MangleTextToValidFilename(nsString & aText)
925 static const char* forbiddenNames[] = {
926 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
927 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
928 "CON", "PRN", "AUX", "NUL", "CLOCK$"
931 aText.StripChars(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS);
932 aText.CompressWhitespace(true, true);
933 uint32_t nameLen;
934 for (size_t n = 0; n < ArrayLength(forbiddenNames); ++n) {
935 nameLen = (uint32_t) strlen(forbiddenNames[n]);
936 if (aText.EqualsIgnoreCase(forbiddenNames[n], nameLen)) {
937 // invalid name is either the entire string, or a prefix with a period
938 if (aText.Length() == nameLen || aText.CharAt(nameLen) == char16_t('.')) {
939 aText.Truncate();
940 break;
947 // Given a unicode string, convert it down to a valid local charset filename
948 // with the supplied extension. This ensures that we do not cut MBCS characters
949 // in the middle.
951 // It would seem that this is more functionality suited to being in nsIFile.
953 static bool
954 CreateFilenameFromTextA(nsString & aText, const char * aExtension,
955 char * aFilename, uint32_t aFilenameLen)
957 // ensure that the supplied name doesn't have invalid characters. If
958 // a valid mangled filename couldn't be created then it will leave the
959 // text empty.
960 MangleTextToValidFilename(aText);
961 if (aText.IsEmpty())
962 return false;
964 // repeatably call WideCharToMultiByte as long as the title doesn't fit in the buffer
965 // available to us. Continually reduce the length of the source title until the MBCS
966 // version will fit in the buffer with room for the supplied extension. Doing it this
967 // way ensures that even in MBCS environments there will be a valid MBCS filename of
968 // the correct length.
969 int maxUsableFilenameLen = aFilenameLen - strlen(aExtension) - 1; // space for ext + null byte
970 int currLen, textLen = (int) std::min(aText.Length(), aFilenameLen);
971 char defaultChar = '_';
972 do {
973 currLen = WideCharToMultiByte(CP_ACP,
974 WC_COMPOSITECHECK|WC_DEFAULTCHAR,
975 aText.get(), textLen--, aFilename, maxUsableFilenameLen, &defaultChar, nullptr);
977 while (currLen == 0 && textLen > 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
978 if (currLen > 0 && textLen > 0) {
979 strcpy(&aFilename[currLen], aExtension);
980 return true;
982 else {
983 // empty names aren't permitted
984 return false;
988 static bool
989 CreateFilenameFromTextW(nsString & aText, const wchar_t * aExtension,
990 wchar_t * aFilename, uint32_t aFilenameLen)
992 // ensure that the supplied name doesn't have invalid characters. If
993 // a valid mangled filename couldn't be created then it will leave the
994 // text empty.
995 MangleTextToValidFilename(aText);
996 if (aText.IsEmpty())
997 return false;
999 const int extensionLen = wcslen(aExtension);
1000 if (aText.Length() + extensionLen + 1 > aFilenameLen)
1001 aText.Truncate(aFilenameLen - extensionLen - 1);
1002 wcscpy(&aFilename[0], aText.get());
1003 wcscpy(&aFilename[aText.Length()], aExtension);
1004 return true;
1007 #define PAGEINFO_PROPERTIES "chrome://navigator/locale/pageInfo.properties"
1009 static bool
1010 GetLocalizedString(const char16_t * aName, nsXPIDLString & aString)
1012 nsCOMPtr<nsIStringBundleService> stringService =
1013 mozilla::services::GetStringBundleService();
1014 if (!stringService)
1015 return false;
1017 nsCOMPtr<nsIStringBundle> stringBundle;
1018 nsresult rv = stringService->CreateBundle(PAGEINFO_PROPERTIES,
1019 getter_AddRefs(stringBundle));
1020 if (NS_FAILED(rv))
1021 return false;
1023 rv = stringBundle->GetStringFromName(aName, getter_Copies(aString));
1024 return NS_SUCCEEDED(rv);
1028 // GetFileDescriptorInternetShortcut
1030 // Create the special format for an internet shortcut and build up the data
1031 // structures the shell is expecting.
1033 HRESULT
1034 nsDataObj :: GetFileDescriptorInternetShortcutA ( FORMATETC& aFE, STGMEDIUM& aSTG )
1036 // get the title of the shortcut
1037 nsAutoString title;
1038 if ( NS_FAILED(ExtractShortcutTitle(title)) )
1039 return E_OUTOFMEMORY;
1041 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORA));
1042 if (!fileGroupDescHandle)
1043 return E_OUTOFMEMORY;
1045 LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(::GlobalLock(fileGroupDescHandle));
1046 if (!fileGroupDescA) {
1047 ::GlobalFree(fileGroupDescHandle);
1048 return E_OUTOFMEMORY;
1051 // get a valid filename in the following order: 1) from the page title,
1052 // 2) localized string for an untitled page, 3) just use "Untitled.URL"
1053 if (!CreateFilenameFromTextA(title, ".URL",
1054 fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1055 nsXPIDLString untitled;
1056 if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) ||
1057 !CreateFilenameFromTextA(untitled, ".URL",
1058 fileGroupDescA->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1059 strcpy(fileGroupDescA->fgd[0].cFileName, "Untitled.URL");
1063 // one file in the file block
1064 fileGroupDescA->cItems = 1;
1065 fileGroupDescA->fgd[0].dwFlags = FD_LINKUI;
1067 ::GlobalUnlock( fileGroupDescHandle );
1068 aSTG.hGlobal = fileGroupDescHandle;
1069 aSTG.tymed = TYMED_HGLOBAL;
1071 return S_OK;
1072 } // GetFileDescriptorInternetShortcutA
1074 HRESULT
1075 nsDataObj :: GetFileDescriptorInternetShortcutW ( FORMATETC& aFE, STGMEDIUM& aSTG )
1077 // get the title of the shortcut
1078 nsAutoString title;
1079 if ( NS_FAILED(ExtractShortcutTitle(title)) )
1080 return E_OUTOFMEMORY;
1082 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
1083 if (!fileGroupDescHandle)
1084 return E_OUTOFMEMORY;
1086 LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(::GlobalLock(fileGroupDescHandle));
1087 if (!fileGroupDescW) {
1088 ::GlobalFree(fileGroupDescHandle);
1089 return E_OUTOFMEMORY;
1092 // get a valid filename in the following order: 1) from the page title,
1093 // 2) localized string for an untitled page, 3) just use "Untitled.URL"
1094 if (!CreateFilenameFromTextW(title, L".URL",
1095 fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1096 nsXPIDLString untitled;
1097 if (!GetLocalizedString(MOZ_UTF16("noPageTitle"), untitled) ||
1098 !CreateFilenameFromTextW(untitled, L".URL",
1099 fileGroupDescW->fgd[0].cFileName, NS_MAX_FILEDESCRIPTOR)) {
1100 wcscpy(fileGroupDescW->fgd[0].cFileName, L"Untitled.URL");
1104 // one file in the file block
1105 fileGroupDescW->cItems = 1;
1106 fileGroupDescW->fgd[0].dwFlags = FD_LINKUI;
1108 ::GlobalUnlock( fileGroupDescHandle );
1109 aSTG.hGlobal = fileGroupDescHandle;
1110 aSTG.tymed = TYMED_HGLOBAL;
1112 return S_OK;
1113 } // GetFileDescriptorInternetShortcutW
1117 // GetFileContentsInternetShortcut
1119 // Create the special format for an internet shortcut and build up the data
1120 // structures the shell is expecting.
1122 HRESULT
1123 nsDataObj :: GetFileContentsInternetShortcut ( FORMATETC& aFE, STGMEDIUM& aSTG )
1125 static const char * kShellIconPref = "browser.shell.shortcutFavicons";
1126 nsAutoString url;
1127 if ( NS_FAILED(ExtractShortcutURL(url)) )
1128 return E_OUTOFMEMORY;
1130 // will need to change if we ever support iDNS
1131 nsAutoCString asciiUrl;
1132 LossyCopyUTF16toASCII(url, asciiUrl);
1134 nsCOMPtr<nsIFile> icoFile;
1135 nsCOMPtr<nsIURI> aUri;
1136 NS_NewURI(getter_AddRefs(aUri), url);
1138 const char *shortcutFormatStr;
1139 int totalLen;
1140 nsCString path;
1141 if (!Preferences::GetBool(kShellIconPref, true) ||
1142 !IsVistaOrLater()) {
1143 shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n";
1144 const int formatLen = strlen(shortcutFormatStr) - 2; // don't include %s
1145 totalLen = formatLen + asciiUrl.Length(); // don't include null character
1146 } else {
1147 nsCOMPtr<nsIFile> icoFile;
1148 nsCOMPtr<nsIURI> aUri;
1149 NS_NewURI(getter_AddRefs(aUri), url);
1151 nsAutoString aUriHash;
1153 mozilla::widget::FaviconHelper::ObtainCachedIconFile(aUri, aUriHash, mIOThread, true);
1155 nsresult rv = mozilla::widget::FaviconHelper::GetOutputIconPath(aUri, icoFile, true);
1156 NS_ENSURE_SUCCESS(rv, E_FAIL);
1157 rv = icoFile->GetNativePath(path);
1158 NS_ENSURE_SUCCESS(rv, E_FAIL);
1160 shortcutFormatStr = "[InternetShortcut]\r\nURL=%s\r\n"
1161 "IDList=\r\nHotKey=0\r\nIconFile=%s\r\n"
1162 "IconIndex=0\r\n";
1163 const int formatLen = strlen(shortcutFormatStr) - 2 * 2; // no %s twice
1164 totalLen = formatLen + asciiUrl.Length() +
1165 path.Length(); // we don't want a null character on the end
1168 // create a global memory area and build up the file contents w/in it
1169 HGLOBAL hGlobalMemory = ::GlobalAlloc(GMEM_SHARE, totalLen);
1170 if ( !hGlobalMemory )
1171 return E_OUTOFMEMORY;
1173 char* contents = reinterpret_cast<char*>(::GlobalLock(hGlobalMemory));
1174 if ( !contents ) {
1175 ::GlobalFree( hGlobalMemory );
1176 return E_OUTOFMEMORY;
1179 //NOTE: we intentionally use the Microsoft version of snprintf here because it does NOT null
1180 // terminate strings which reach the maximum size of the buffer. Since we know that the
1181 // formatted length here is totalLen, this call to _snprintf will format the string into
1182 // the buffer without appending the null character.
1184 if (!Preferences::GetBool(kShellIconPref, true)) {
1185 _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get());
1186 } else {
1187 _snprintf(contents, totalLen, shortcutFormatStr, asciiUrl.get(), path.get());
1190 ::GlobalUnlock(hGlobalMemory);
1191 aSTG.hGlobal = hGlobalMemory;
1192 aSTG.tymed = TYMED_HGLOBAL;
1194 return S_OK;
1195 } // GetFileContentsInternetShortcut
1197 // check if specified flavour is present in the transferable
1198 bool nsDataObj :: IsFlavourPresent(const char *inFlavour)
1200 bool retval = false;
1201 NS_ENSURE_TRUE(mTransferable, false);
1203 // get the list of flavors available in the transferable
1204 nsCOMPtr<nsISupportsArray> flavorList;
1205 mTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
1206 NS_ENSURE_TRUE(flavorList, false);
1208 // try to find requested flavour
1209 uint32_t cnt;
1210 flavorList->Count(&cnt);
1211 for (uint32_t i = 0; i < cnt; ++i) {
1212 nsCOMPtr<nsISupports> genericFlavor;
1213 flavorList->GetElementAt (i, getter_AddRefs(genericFlavor));
1214 nsCOMPtr<nsISupportsCString> currentFlavor (do_QueryInterface(genericFlavor));
1215 if (currentFlavor) {
1216 nsAutoCString flavorStr;
1217 currentFlavor->GetData(flavorStr);
1218 if (flavorStr.Equals(inFlavour)) {
1219 retval = true; // found it!
1220 break;
1223 } // for each flavor
1225 return retval;
1228 HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG )
1230 HRESULT res = S_OK;
1231 aSTG.tymed = TYMED_HGLOBAL;
1232 aSTG.pUnkForRelease = nullptr;
1233 HGLOBAL hGlobalMemory = nullptr;
1234 hGlobalMemory = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
1235 if (hGlobalMemory) {
1236 DWORD* pdw = (DWORD*) GlobalLock(hGlobalMemory);
1237 // The PreferredDropEffect clipboard format is only registered if a drag/drop
1238 // of an image happens from Mozilla to the desktop. We want its value
1239 // to be DROPEFFECT_MOVE in that case so that the file is moved from the
1240 // temporary location, not copied.
1241 // This value should, ideally, be set on the data object via SetData() but
1242 // our IDataObject implementation doesn't implement SetData. It adds data
1243 // to the data object lazily only when the drop target asks for it.
1244 *pdw = (DWORD) DROPEFFECT_MOVE;
1245 GlobalUnlock(hGlobalMemory);
1247 else {
1248 res = E_OUTOFMEMORY;
1250 aSTG.hGlobal = hGlobalMemory;
1251 return res;
1254 //-----------------------------------------------------
1255 HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
1257 void* data = nullptr;
1258 uint32_t len;
1260 // if someone asks for text/plain, look up text/unicode instead in the transferable.
1261 const char* flavorStr;
1262 const nsPromiseFlatCString& flat = PromiseFlatCString(aDataFlavor);
1263 if (aDataFlavor.EqualsLiteral("text/plain"))
1264 flavorStr = kUnicodeMime;
1265 else
1266 flavorStr = flat.get();
1268 // NOTE: CreateDataFromPrimitive creates new memory, that needs to be deleted
1269 nsCOMPtr<nsISupports> genericDataWrapper;
1270 mTransferable->GetTransferData(flavorStr, getter_AddRefs(genericDataWrapper), &len);
1271 if ( !len )
1272 return E_FAIL;
1273 nsPrimitiveHelpers::CreateDataFromPrimitive ( flavorStr, genericDataWrapper, &data, len );
1274 if ( !data )
1275 return E_FAIL;
1277 HGLOBAL hGlobalMemory = nullptr;
1279 aSTG.tymed = TYMED_HGLOBAL;
1280 aSTG.pUnkForRelease = nullptr;
1282 // We play games under the hood and advertise flavors that we know we
1283 // can support, only they require a bit of conversion or munging of the data.
1284 // Do that here.
1286 // The transferable gives us data that is null-terminated, but this isn't reflected in
1287 // the |len| parameter. Windoze apps expect this null to be there so bump our data buffer
1288 // by the appropriate size to account for the null (one char for CF_TEXT, one char16_t for
1289 // CF_UNICODETEXT).
1290 DWORD allocLen = (DWORD)len;
1291 if ( aFE.cfFormat == CF_TEXT ) {
1292 // Someone is asking for text/plain; convert the unicode (assuming it's present)
1293 // to text with the correct platform encoding.
1294 size_t bufferSize = sizeof(char)*(len + 2);
1295 char* plainTextData = static_cast<char*>(nsMemory::Alloc(bufferSize));
1296 char16_t* castedUnicode = reinterpret_cast<char16_t*>(data);
1297 int32_t plainTextLen = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)castedUnicode, len / 2 + 1, plainTextData, bufferSize, NULL, NULL);
1298 // replace the unicode data with our plaintext data. Recall that |plainTextLen| doesn't include
1299 // the null in the length.
1300 nsMemory::Free(data);
1301 if ( plainTextLen ) {
1302 data = plainTextData;
1303 allocLen = plainTextLen;
1305 else {
1306 nsMemory::Free(plainTextData);
1307 NS_WARNING ( "Oh no, couldn't convert unicode to plain text" );
1308 return S_OK;
1311 else if ( aFE.cfFormat == nsClipboard::CF_HTML ) {
1312 // Someone is asking for win32's HTML flavor. Convert our html fragment
1313 // from unicode to UTF-8 then put it into a format specified by msft.
1314 NS_ConvertUTF16toUTF8 converter ( reinterpret_cast<char16_t*>(data) );
1315 char* utf8HTML = nullptr;
1316 nsresult rv = BuildPlatformHTML ( converter.get(), &utf8HTML ); // null terminates
1318 nsMemory::Free(data);
1319 if ( NS_SUCCEEDED(rv) && utf8HTML ) {
1320 // replace the unicode data with our HTML data. Don't forget the null.
1321 data = utf8HTML;
1322 allocLen = strlen(utf8HTML) + sizeof(char);
1324 else {
1325 NS_WARNING ( "Oh no, couldn't convert to HTML" );
1326 return S_OK;
1329 else {
1330 // we assume that any data that isn't caught above is unicode. This may
1331 // be an erroneous assumption, but is true so far.
1332 allocLen += sizeof(char16_t);
1335 hGlobalMemory = (HGLOBAL)GlobalAlloc(GMEM_MOVEABLE, allocLen);
1337 // Copy text to Global Memory Area
1338 if ( hGlobalMemory ) {
1339 char* dest = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
1340 char* source = reinterpret_cast<char*>(data);
1341 memcpy ( dest, source, allocLen ); // copies the null as well
1342 GlobalUnlock(hGlobalMemory);
1344 aSTG.hGlobal = hGlobalMemory;
1346 // Now, delete the memory that was created by CreateDataFromPrimitive (or our text/plain data)
1347 nsMemory::Free(data);
1349 return S_OK;
1352 //-----------------------------------------------------
1353 HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG)
1355 uint32_t dfInx = 0;
1356 ULONG count;
1357 FORMATETC fe;
1358 m_enumFE->Reset();
1359 while (NOERROR == m_enumFE->Next(1, &fe, &count)
1360 && dfInx < mDataFlavors.Length()) {
1361 if (mDataFlavors[dfInx].EqualsLiteral(kNativeImageMime))
1362 return DropImage(aFE, aSTG);
1363 if (mDataFlavors[dfInx].EqualsLiteral(kFileMime))
1364 return DropFile(aFE, aSTG);
1365 if (mDataFlavors[dfInx].EqualsLiteral(kFilePromiseMime))
1366 return DropTempFile(aFE, aSTG);
1367 dfInx++;
1369 return E_FAIL;
1372 HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG)
1374 nsresult rv;
1375 uint32_t len = 0;
1376 nsCOMPtr<nsISupports> genericDataWrapper;
1378 mTransferable->GetTransferData(kFileMime, getter_AddRefs(genericDataWrapper),
1379 &len);
1380 nsCOMPtr<nsIFile> file ( do_QueryInterface(genericDataWrapper) );
1382 if (!file)
1384 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
1385 if (ptr) {
1386 nsCOMPtr<nsISupports> supports;
1387 ptr->GetData(getter_AddRefs(supports));
1388 file = do_QueryInterface(supports);
1392 if (!file)
1393 return E_FAIL;
1395 aSTG.tymed = TYMED_HGLOBAL;
1396 aSTG.pUnkForRelease = nullptr;
1398 nsAutoString path;
1399 rv = file->GetPath(path);
1400 if (NS_FAILED(rv))
1401 return E_FAIL;
1403 uint32_t allocLen = path.Length() + 2;
1404 HGLOBAL hGlobalMemory = nullptr;
1405 char16_t *dest;
1407 hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) +
1408 allocLen * sizeof(char16_t));
1409 if (!hGlobalMemory)
1410 return E_FAIL;
1412 DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
1414 // First, populate the drop file structure
1415 pDropFile->pFiles = sizeof(DROPFILES); //Offset to start of file name string
1416 pDropFile->fNC = 0;
1417 pDropFile->pt.x = 0;
1418 pDropFile->pt.y = 0;
1419 pDropFile->fWide = TRUE;
1421 // Copy the filename right after the DROPFILES structure
1422 dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
1423 memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t));
1425 // Two null characters are needed at the end of the file name.
1426 // Lookup the CF_HDROP shell clipboard format for more info.
1427 // Add the second null character right after the first one.
1428 dest[allocLen - 1] = L'\0';
1430 GlobalUnlock(hGlobalMemory);
1432 aSTG.hGlobal = hGlobalMemory;
1434 return S_OK;
1437 HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
1439 nsresult rv;
1440 if (!mCachedTempFile) {
1441 uint32_t len = 0;
1442 nsCOMPtr<nsISupports> genericDataWrapper;
1444 mTransferable->GetTransferData(kNativeImageMime, getter_AddRefs(genericDataWrapper), &len);
1445 nsCOMPtr<imgIContainer> image(do_QueryInterface(genericDataWrapper));
1447 if (!image) {
1448 // Check if the image was put in an nsISupportsInterfacePointer wrapper.
1449 // This might not be necessary any more, but could be useful for backwards
1450 // compatibility.
1451 nsCOMPtr<nsISupportsInterfacePointer> ptr(do_QueryInterface(genericDataWrapper));
1452 if (ptr) {
1453 nsCOMPtr<nsISupports> supports;
1454 ptr->GetData(getter_AddRefs(supports));
1455 image = do_QueryInterface(supports);
1459 if (!image)
1460 return E_FAIL;
1462 // Use the clipboard helper class to build up a memory bitmap.
1463 nsImageToClipboard converter(image);
1464 HANDLE bits = nullptr;
1465 rv = converter.GetPicture(&bits); // Clipboard routines return a global handle we own.
1467 if (NS_FAILED(rv) || !bits)
1468 return E_FAIL;
1470 // We now own these bits!
1471 uint32_t bitmapSize = GlobalSize(bits);
1472 if (!bitmapSize) {
1473 GlobalFree(bits);
1474 return E_FAIL;
1477 // Save the bitmap to a temporary location.
1478 nsCOMPtr<nsIFile> dropFile;
1479 rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
1480 if (!dropFile) {
1481 GlobalFree(bits);
1482 return E_FAIL;
1485 // Filename must be random so as not to confuse apps like
1486 // Photoshop which handle multiple drags into a single window.
1487 char buf[13];
1488 nsCString filename;
1489 NS_MakeRandomString(buf, 8);
1490 memcpy(buf+8, ".bmp", 5);
1491 filename.Append(nsDependentCString(buf, 12));
1492 dropFile->AppendNative(filename);
1493 rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
1494 if (NS_FAILED(rv)) {
1495 GlobalFree(bits);
1496 return E_FAIL;
1499 // Cache the temp file so we can delete it later and so
1500 // it doesn't get recreated over and over on multiple calls
1501 // which does occur from windows shell.
1502 dropFile->Clone(getter_AddRefs(mCachedTempFile));
1504 // Write the data to disk.
1505 nsCOMPtr<nsIOutputStream> outStream;
1506 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
1507 if (NS_FAILED(rv)) {
1508 GlobalFree(bits);
1509 return E_FAIL;
1512 char * bm = (char *)GlobalLock(bits);
1514 BITMAPFILEHEADER fileHdr;
1515 BITMAPINFOHEADER *bmpHdr = (BITMAPINFOHEADER*)bm;
1517 fileHdr.bfType = ((WORD) ('M' << 8) | 'B');
1518 fileHdr.bfSize = GlobalSize (bits) + sizeof(fileHdr);
1519 fileHdr.bfReserved1 = 0;
1520 fileHdr.bfReserved2 = 0;
1521 fileHdr.bfOffBits = (DWORD) (sizeof(fileHdr) + bmpHdr->biSize);
1523 uint32_t writeCount = 0;
1524 if (NS_FAILED(outStream->Write((const char *)&fileHdr, sizeof(fileHdr), &writeCount)) ||
1525 NS_FAILED(outStream->Write((const char *)bm, bitmapSize, &writeCount)))
1526 rv = NS_ERROR_FAILURE;
1528 outStream->Close();
1530 GlobalUnlock(bits);
1531 GlobalFree(bits);
1533 if (NS_FAILED(rv))
1534 return E_FAIL;
1537 // Pass the file name back to the drop target so that it can access the file.
1538 nsAutoString path;
1539 rv = mCachedTempFile->GetPath(path);
1540 if (NS_FAILED(rv))
1541 return E_FAIL;
1543 // Two null characters are needed to terminate the file name list.
1544 HGLOBAL hGlobalMemory = nullptr;
1546 uint32_t allocLen = path.Length() + 2;
1548 aSTG.tymed = TYMED_HGLOBAL;
1549 aSTG.pUnkForRelease = nullptr;
1551 hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
1552 if (!hGlobalMemory)
1553 return E_FAIL;
1555 DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
1557 // First, populate the drop file structure.
1558 pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
1559 pDropFile->fNC = 0;
1560 pDropFile->pt.x = 0;
1561 pDropFile->pt.y = 0;
1562 pDropFile->fWide = TRUE;
1564 // Copy the filename right after the DROPFILES structure.
1565 char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
1566 memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
1568 // Two null characters are needed at the end of the file name.
1569 // Lookup the CF_HDROP shell clipboard format for more info.
1570 // Add the second null character right after the first one.
1571 dest[allocLen - 1] = L'\0';
1573 GlobalUnlock(hGlobalMemory);
1575 aSTG.hGlobal = hGlobalMemory;
1577 return S_OK;
1580 HRESULT nsDataObj::DropTempFile(FORMATETC& aFE, STGMEDIUM& aSTG)
1582 nsresult rv;
1583 if (!mCachedTempFile) {
1584 // Tempfile will need a temporary location.
1585 nsCOMPtr<nsIFile> dropFile;
1586 rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dropFile));
1587 if (!dropFile)
1588 return E_FAIL;
1590 // Filename must be random
1591 nsCString filename;
1592 nsAutoString wideFileName;
1593 nsCOMPtr<nsIURI> sourceURI;
1594 HRESULT res;
1595 res = GetDownloadDetails(getter_AddRefs(sourceURI),
1596 wideFileName);
1597 if (FAILED(res))
1598 return res;
1599 NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, filename);
1601 dropFile->AppendNative(filename);
1602 rv = dropFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0660);
1603 if (NS_FAILED(rv))
1604 return E_FAIL;
1606 // Cache the temp file so we can delete it later and so
1607 // it doesn't get recreated over and over on multiple calls
1608 // which does occur from windows shell.
1609 dropFile->Clone(getter_AddRefs(mCachedTempFile));
1611 // Write the data to disk.
1612 nsCOMPtr<nsIOutputStream> outStream;
1613 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), dropFile);
1614 if (NS_FAILED(rv))
1615 return E_FAIL;
1617 IStream *pStream = nullptr;
1618 nsDataObj::CreateStream(&pStream);
1619 NS_ENSURE_TRUE(pStream, E_FAIL);
1621 char buffer[512];
1622 ULONG readCount = 0;
1623 uint32_t writeCount = 0;
1624 while (1) {
1625 HRESULT hres = pStream->Read(buffer, sizeof(buffer), &readCount);
1626 if (FAILED(hres))
1627 return E_FAIL;
1628 if (readCount == 0)
1629 break;
1630 rv = outStream->Write(buffer, readCount, &writeCount);
1631 if (NS_FAILED(rv))
1632 return E_FAIL;
1634 outStream->Close();
1635 pStream->Release();
1638 // Pass the file name back to the drop target so that it can access the file.
1639 nsAutoString path;
1640 rv = mCachedTempFile->GetPath(path);
1641 if (NS_FAILED(rv))
1642 return E_FAIL;
1644 uint32_t allocLen = path.Length() + 2;
1646 // Two null characters are needed to terminate the file name list.
1647 HGLOBAL hGlobalMemory = nullptr;
1649 aSTG.tymed = TYMED_HGLOBAL;
1650 aSTG.pUnkForRelease = nullptr;
1652 hGlobalMemory = GlobalAlloc(GMEM_MOVEABLE, sizeof(DROPFILES) + allocLen * sizeof(char16_t));
1653 if (!hGlobalMemory)
1654 return E_FAIL;
1656 DROPFILES* pDropFile = (DROPFILES*)GlobalLock(hGlobalMemory);
1658 // First, populate the drop file structure.
1659 pDropFile->pFiles = sizeof(DROPFILES); // Offset to start of file name char array.
1660 pDropFile->fNC = 0;
1661 pDropFile->pt.x = 0;
1662 pDropFile->pt.y = 0;
1663 pDropFile->fWide = TRUE;
1665 // Copy the filename right after the DROPFILES structure.
1666 char16_t* dest = (char16_t*)(((char*)pDropFile) + pDropFile->pFiles);
1667 memcpy(dest, path.get(), (allocLen - 1) * sizeof(char16_t)); // Copies the null character in path as well.
1669 // Two null characters are needed at the end of the file name.
1670 // Lookup the CF_HDROP shell clipboard format for more info.
1671 // Add the second null character right after the first one.
1672 dest[allocLen - 1] = L'\0';
1674 GlobalUnlock(hGlobalMemory);
1676 aSTG.hGlobal = hGlobalMemory;
1678 return S_OK;
1681 //-----------------------------------------------------
1682 HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
1684 return E_NOTIMPL;
1687 //-----------------------------------------------------
1688 HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&)
1690 return E_NOTIMPL;
1693 //-----------------------------------------------------
1694 HRESULT nsDataObj::SetDib(FORMATETC&, STGMEDIUM&)
1696 return E_FAIL;
1699 //-----------------------------------------------------
1700 HRESULT nsDataObj::SetText (FORMATETC& aFE, STGMEDIUM& aSTG)
1702 return E_FAIL;
1705 //-----------------------------------------------------
1706 HRESULT nsDataObj::SetMetafilePict(FORMATETC&, STGMEDIUM&)
1708 return E_FAIL;
1713 //-----------------------------------------------------
1714 //-----------------------------------------------------
1715 CLSID nsDataObj::GetClassID() const
1717 return CLSID_nsDataObj;
1720 //-----------------------------------------------------
1721 // Registers the DataFlavor/FE pair.
1722 //-----------------------------------------------------
1723 void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
1725 // These two lists are the mapping to and from data flavors and FEs.
1726 // Later, OLE will tell us it needs a certain type of FORMATETC (text, unicode, etc)
1727 // unicode, etc), so we will look up the data flavor that corresponds to
1728 // the FE and then ask the transferable for that type of data.
1729 mDataFlavors.AppendElement(aDataFlavor);
1730 m_enumFE->AddFormatEtc(aFE);
1733 //-----------------------------------------------------
1734 // Sets the transferable object
1735 //-----------------------------------------------------
1736 void nsDataObj::SetTransferable(nsITransferable * aTransferable)
1738 NS_IF_RELEASE(mTransferable);
1740 mTransferable = aTransferable;
1741 if (nullptr == mTransferable) {
1742 return;
1745 NS_ADDREF(mTransferable);
1747 return;
1752 // ExtractURL
1754 // Roots around in the transferable for the appropriate flavor that indicates
1755 // a url and pulls out the url portion of the data. Used mostly for creating
1756 // internet shortcuts on the desktop. The url flavor is of the format:
1758 // <url> <linefeed> <page title>
1760 nsresult
1761 nsDataObj :: ExtractShortcutURL ( nsString & outURL )
1763 NS_ASSERTION ( mTransferable, "We don't have a good transferable" );
1764 nsresult rv = NS_ERROR_FAILURE;
1766 uint32_t len = 0;
1767 nsCOMPtr<nsISupports> genericURL;
1768 if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
1769 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1770 if ( urlObject ) {
1771 nsAutoString url;
1772 urlObject->GetData ( url );
1773 outURL = url;
1775 // find the first linefeed in the data, that's where the url ends. trunc the
1776 // result string at that point.
1777 int32_t lineIndex = outURL.FindChar ( '\n' );
1778 NS_ASSERTION ( lineIndex > 0, "Format for url flavor is <url> <linefeed> <page title>" );
1779 if ( lineIndex > 0 ) {
1780 outURL.Truncate ( lineIndex );
1781 rv = NS_OK;
1784 } else if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLDataMime, getter_AddRefs(genericURL), &len)) ||
1785 NS_SUCCEEDED(mTransferable->GetTransferData(kURLPrivateMime, getter_AddRefs(genericURL), &len)) ) {
1786 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1787 if ( urlObject ) {
1788 nsAutoString url;
1789 urlObject->GetData ( url );
1790 outURL = url;
1792 rv = NS_OK;
1795 } // if found flavor
1797 return rv;
1799 } // ExtractShortcutURL
1803 // ExtractShortcutTitle
1805 // Roots around in the transferable for the appropriate flavor that indicates
1806 // a url and pulls out the title portion of the data. Used mostly for creating
1807 // internet shortcuts on the desktop. The url flavor is of the format:
1809 // <url> <linefeed> <page title>
1811 nsresult
1812 nsDataObj :: ExtractShortcutTitle ( nsString & outTitle )
1814 NS_ASSERTION ( mTransferable, "We'd don't have a good transferable" );
1815 nsresult rv = NS_ERROR_FAILURE;
1817 uint32_t len = 0;
1818 nsCOMPtr<nsISupports> genericURL;
1819 if ( NS_SUCCEEDED(mTransferable->GetTransferData(kURLMime, getter_AddRefs(genericURL), &len)) ) {
1820 nsCOMPtr<nsISupportsString> urlObject ( do_QueryInterface(genericURL) );
1821 if ( urlObject ) {
1822 nsAutoString url;
1823 urlObject->GetData ( url );
1825 // find the first linefeed in the data, that's where the url ends. we want
1826 // everything after that linefeed. FindChar() returns -1 if we can't find
1827 int32_t lineIndex = url.FindChar ( '\n' );
1828 NS_ASSERTION ( lineIndex != -1, "Format for url flavor is <url> <linefeed> <page title>" );
1829 if ( lineIndex != -1 ) {
1830 url.Mid ( outTitle, lineIndex + 1, (len/2) - (lineIndex + 1) );
1831 rv = NS_OK;
1834 } // if found flavor
1836 return rv;
1838 } // ExtractShortcutTitle
1842 // BuildPlatformHTML
1844 // Munge our HTML data to win32's CF_HTML spec. Basically, put the requisite
1845 // header information on it. This will null terminate |outPlatformHTML|. See
1846 // http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
1847 // for details.
1849 // We assume that |inOurHTML| is already a fragment (ie, doesn't have <HTML>
1850 // or <BODY> tags). We'll wrap the fragment with them to make other apps
1851 // happy.
1853 nsresult
1854 nsDataObj :: BuildPlatformHTML ( const char* inOurHTML, char** outPlatformHTML )
1856 *outPlatformHTML = nullptr;
1858 nsDependentCString inHTMLString(inOurHTML);
1859 const char* const numPlaceholder = "00000000";
1860 const char* const startHTMLPrefix = "Version:0.9\r\nStartHTML:";
1861 const char* const endHTMLPrefix = "\r\nEndHTML:";
1862 const char* const startFragPrefix = "\r\nStartFragment:";
1863 const char* const endFragPrefix = "\r\nEndFragment:";
1864 const char* const startSourceURLPrefix = "\r\nSourceURL:";
1865 const char* const endFragTrailer = "\r\n";
1867 // Do we already have mSourceURL from a drag?
1868 if (mSourceURL.IsEmpty()) {
1869 nsAutoString url;
1870 ExtractShortcutURL(url);
1872 AppendUTF16toUTF8(url, mSourceURL);
1875 const int32_t kSourceURLLength = mSourceURL.Length();
1876 const int32_t kNumberLength = strlen(numPlaceholder);
1878 const int32_t kTotalHeaderLen = strlen(startHTMLPrefix) +
1879 strlen(endHTMLPrefix) +
1880 strlen(startFragPrefix) +
1881 strlen(endFragPrefix) +
1882 strlen(endFragTrailer) +
1883 (kSourceURLLength > 0 ? strlen(startSourceURLPrefix) : 0) +
1884 kSourceURLLength +
1885 (4 * kNumberLength);
1887 NS_NAMED_LITERAL_CSTRING(htmlHeaderString, "<html><body>\r\n");
1889 NS_NAMED_LITERAL_CSTRING(fragmentHeaderString, "<!--StartFragment-->");
1891 nsDependentCString trailingString(
1892 "<!--EndFragment-->\r\n"
1893 "</body>\r\n"
1894 "</html>");
1896 // calculate the offsets
1897 int32_t startHTMLOffset = kTotalHeaderLen;
1898 int32_t startFragOffset = startHTMLOffset
1899 + htmlHeaderString.Length()
1900 + fragmentHeaderString.Length();
1902 int32_t endFragOffset = startFragOffset
1903 + inHTMLString.Length();
1905 int32_t endHTMLOffset = endFragOffset
1906 + trailingString.Length();
1908 // now build the final version
1909 nsCString clipboardString;
1910 clipboardString.SetCapacity(endHTMLOffset);
1912 clipboardString.Append(startHTMLPrefix);
1913 clipboardString.Append(nsPrintfCString("%08u", startHTMLOffset));
1915 clipboardString.Append(endHTMLPrefix);
1916 clipboardString.Append(nsPrintfCString("%08u", endHTMLOffset));
1918 clipboardString.Append(startFragPrefix);
1919 clipboardString.Append(nsPrintfCString("%08u", startFragOffset));
1921 clipboardString.Append(endFragPrefix);
1922 clipboardString.Append(nsPrintfCString("%08u", endFragOffset));
1924 if (kSourceURLLength > 0) {
1925 clipboardString.Append(startSourceURLPrefix);
1926 clipboardString.Append(mSourceURL);
1929 clipboardString.Append(endFragTrailer);
1931 clipboardString.Append(htmlHeaderString);
1932 clipboardString.Append(fragmentHeaderString);
1933 clipboardString.Append(inHTMLString);
1934 clipboardString.Append(trailingString);
1936 *outPlatformHTML = ToNewCString(clipboardString);
1937 if (!*outPlatformHTML)
1938 return NS_ERROR_OUT_OF_MEMORY;
1940 return NS_OK;
1943 HRESULT
1944 nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
1946 HRESULT res = S_OK;
1947 if (IsFlavourPresent(kURLMime)) {
1948 if ( aIsUnicode )
1949 res = ExtractUniformResourceLocatorW( aFE, aSTG );
1950 else
1951 res = ExtractUniformResourceLocatorA( aFE, aSTG );
1953 else
1954 NS_WARNING ("Not yet implemented\n");
1955 return res;
1958 HRESULT
1959 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG )
1961 HRESULT result = S_OK;
1963 nsAutoString url;
1964 if (NS_FAILED(ExtractShortcutURL(url)))
1965 return E_OUTOFMEMORY;
1967 NS_LossyConvertUTF16toASCII asciiUrl(url);
1968 const int totalLen = asciiUrl.Length() + 1;
1969 HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
1970 if (!hGlobalMemory)
1971 return E_OUTOFMEMORY;
1973 char* contents = reinterpret_cast<char*>(GlobalLock(hGlobalMemory));
1974 if (!contents) {
1975 GlobalFree(hGlobalMemory);
1976 return E_OUTOFMEMORY;
1979 strcpy(contents, asciiUrl.get());
1980 GlobalUnlock(hGlobalMemory);
1981 aSTG.hGlobal = hGlobalMemory;
1982 aSTG.tymed = TYMED_HGLOBAL;
1984 return result;
1987 HRESULT
1988 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG )
1990 HRESULT result = S_OK;
1992 nsAutoString url;
1993 if (NS_FAILED(ExtractShortcutURL(url)))
1994 return E_OUTOFMEMORY;
1996 const int totalLen = (url.Length() + 1) * sizeof(char16_t);
1997 HGLOBAL hGlobalMemory = GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE, totalLen);
1998 if (!hGlobalMemory)
1999 return E_OUTOFMEMORY;
2001 wchar_t* contents = reinterpret_cast<wchar_t*>(GlobalLock(hGlobalMemory));
2002 if (!contents) {
2003 GlobalFree(hGlobalMemory);
2004 return E_OUTOFMEMORY;
2007 wcscpy(contents, url.get());
2008 GlobalUnlock(hGlobalMemory);
2009 aSTG.hGlobal = hGlobalMemory;
2010 aSTG.tymed = TYMED_HGLOBAL;
2012 return result;
2016 // Gets the filename from the kFilePromiseURLMime flavour
2017 HRESULT nsDataObj::GetDownloadDetails(nsIURI **aSourceURI,
2018 nsAString &aFilename)
2020 *aSourceURI = nullptr;
2022 NS_ENSURE_TRUE(mTransferable, E_FAIL);
2024 // get the URI from the kFilePromiseURLMime flavor
2025 nsCOMPtr<nsISupports> urlPrimitive;
2026 uint32_t dataSize = 0;
2027 mTransferable->GetTransferData(kFilePromiseURLMime, getter_AddRefs(urlPrimitive), &dataSize);
2028 nsCOMPtr<nsISupportsString> srcUrlPrimitive = do_QueryInterface(urlPrimitive);
2029 NS_ENSURE_TRUE(srcUrlPrimitive, E_FAIL);
2031 nsAutoString srcUri;
2032 srcUrlPrimitive->GetData(srcUri);
2033 if (srcUri.IsEmpty())
2034 return E_FAIL;
2035 nsCOMPtr<nsIURI> sourceURI;
2036 NS_NewURI(getter_AddRefs(sourceURI), srcUri);
2038 nsAutoString srcFileName;
2039 nsCOMPtr<nsISupports> fileNamePrimitive;
2040 mTransferable->GetTransferData(kFilePromiseDestFilename, getter_AddRefs(fileNamePrimitive), &dataSize);
2041 nsCOMPtr<nsISupportsString> srcFileNamePrimitive = do_QueryInterface(fileNamePrimitive);
2042 if (srcFileNamePrimitive) {
2043 srcFileNamePrimitive->GetData(srcFileName);
2044 } else {
2045 nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
2046 if (!sourceURL)
2047 return E_FAIL;
2049 nsAutoCString urlFileName;
2050 sourceURL->GetFileName(urlFileName);
2051 NS_UnescapeURL(urlFileName);
2052 CopyUTF8toUTF16(urlFileName, srcFileName);
2054 if (srcFileName.IsEmpty())
2055 return E_FAIL;
2057 // make the name safe for the filesystem
2058 MangleTextToValidFilename(srcFileName);
2060 sourceURI.swap(*aSourceURI);
2061 aFilename = srcFileName;
2062 return S_OK;
2065 HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG)
2067 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
2068 NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
2070 LPFILEGROUPDESCRIPTORA fileGroupDescA = reinterpret_cast<LPFILEGROUPDESCRIPTORA>(GlobalLock(fileGroupDescHandle));
2071 if (!fileGroupDescA) {
2072 ::GlobalFree(fileGroupDescHandle);
2073 return E_OUTOFMEMORY;
2076 nsAutoString wideFileName;
2077 HRESULT res;
2078 nsCOMPtr<nsIURI> sourceURI;
2079 res = GetDownloadDetails(getter_AddRefs(sourceURI), wideFileName);
2080 if (FAILED(res))
2082 ::GlobalFree(fileGroupDescHandle);
2083 return res;
2086 nsAutoCString nativeFileName;
2087 NS_UTF16ToCString(wideFileName, NS_CSTRING_ENCODING_NATIVE_FILESYSTEM, nativeFileName);
2089 strncpy(fileGroupDescA->fgd[0].cFileName, nativeFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
2090 fileGroupDescA->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
2092 // one file in the file block
2093 fileGroupDescA->cItems = 1;
2094 fileGroupDescA->fgd[0].dwFlags = FD_PROGRESSUI;
2096 GlobalUnlock( fileGroupDescHandle );
2097 aSTG.hGlobal = fileGroupDescHandle;
2098 aSTG.tymed = TYMED_HGLOBAL;
2100 return S_OK;
2103 HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG)
2105 HGLOBAL fileGroupDescHandle = ::GlobalAlloc(GMEM_ZEROINIT|GMEM_SHARE,sizeof(FILEGROUPDESCRIPTORW));
2106 NS_ENSURE_TRUE(fileGroupDescHandle, E_OUTOFMEMORY);
2108 LPFILEGROUPDESCRIPTORW fileGroupDescW = reinterpret_cast<LPFILEGROUPDESCRIPTORW>(GlobalLock(fileGroupDescHandle));
2109 if (!fileGroupDescW) {
2110 ::GlobalFree(fileGroupDescHandle);
2111 return E_OUTOFMEMORY;
2114 nsAutoString wideFileName;
2115 HRESULT res;
2116 nsCOMPtr<nsIURI> sourceURI;
2117 res = GetDownloadDetails(getter_AddRefs(sourceURI),
2118 wideFileName);
2119 if (FAILED(res))
2121 ::GlobalFree(fileGroupDescHandle);
2122 return res;
2125 wcsncpy(fileGroupDescW->fgd[0].cFileName, wideFileName.get(), NS_MAX_FILEDESCRIPTOR - 1);
2126 fileGroupDescW->fgd[0].cFileName[NS_MAX_FILEDESCRIPTOR - 1] = '\0';
2127 // one file in the file block
2128 fileGroupDescW->cItems = 1;
2129 fileGroupDescW->fgd[0].dwFlags = FD_PROGRESSUI;
2131 GlobalUnlock(fileGroupDescHandle);
2132 aSTG.hGlobal = fileGroupDescHandle;
2133 aSTG.tymed = TYMED_HGLOBAL;
2135 return S_OK;
2138 HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
2140 IStream *pStream = nullptr;
2142 nsDataObj::CreateStream(&pStream);
2143 NS_ENSURE_TRUE(pStream, E_FAIL);
2145 aSTG.tymed = TYMED_ISTREAM;
2146 aSTG.pstm = pStream;
2147 aSTG.pUnkForRelease = nullptr;
2149 return S_OK;
2152 void nsDataObj::RemoveTempFile(nsITimer* aTimer, void* aClosure)
2154 nsDataObj *timedDataObj = static_cast<nsDataObj *>(aClosure);
2155 if (timedDataObj->mCachedTempFile) {
2156 timedDataObj->mCachedTempFile->Remove(false);
2157 timedDataObj->mCachedTempFile = nullptr;
2159 timedDataObj->Release();