widget/windows/nsDataObj.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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);
  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 )
  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;
  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");
  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 )
  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;
  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");
  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 )
  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
  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;
  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());
  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)
  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;
  1203   } // for each flavor
  1205   return retval;
  1208 HRESULT nsDataObj::GetPreferredDropEffect ( FORMATETC& aFE, STGMEDIUM& aSTG )
  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);
  1227   else {
  1228     res = E_OUTOFMEMORY;
  1230   aSTG.hGlobal = hGlobalMemory;
  1231   return res;
  1234 //-----------------------------------------------------
  1235 HRESULT nsDataObj::GetText(const nsACString & aDataFlavor, FORMATETC& aFE, STGMEDIUM& aSTG)
  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);
  1286     else {
  1287       NS_WARNING ( "Oh no, couldn't convert unicode to plain text" );
  1288       return S_OK;
  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);
  1304     else {
  1305       NS_WARNING ( "Oh no, couldn't convert to HTML" );
  1306       return S_OK;
  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);
  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);
  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;
  1332 //-----------------------------------------------------
  1333 HRESULT nsDataObj::GetFile(FORMATETC& aFE, STGMEDIUM& aSTG)
  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++;
  1349   return E_FAIL;
  1352 HRESULT nsDataObj::DropFile(FORMATETC& aFE, STGMEDIUM& aSTG)
  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)
  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);
  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;
  1417 HRESULT nsDataObj::DropImage(FORMATETC& aFE, STGMEDIUM& aSTG)
  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);
  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;
  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;
  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;
  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;
  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;
  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;
  1560 HRESULT nsDataObj::DropTempFile(FORMATETC& aFE, STGMEDIUM& aSTG)
  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;
  1614     outStream->Close();
  1615     pStream->Release();
  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;
  1661 //-----------------------------------------------------
  1662 HRESULT nsDataObj::GetMetafilePict(FORMATETC&, STGMEDIUM&)
  1664 	return E_NOTIMPL;
  1667 //-----------------------------------------------------
  1668 HRESULT nsDataObj::SetBitmap(FORMATETC&, STGMEDIUM&)
  1670 	return E_NOTIMPL;
  1673 //-----------------------------------------------------
  1674 HRESULT nsDataObj::SetDib(FORMATETC&, STGMEDIUM&)
  1676 	return E_FAIL;
  1679 //-----------------------------------------------------
  1680 HRESULT nsDataObj::SetText  (FORMATETC& aFE, STGMEDIUM& aSTG)
  1682 	return E_FAIL;
  1685 //-----------------------------------------------------
  1686 HRESULT nsDataObj::SetMetafilePict(FORMATETC&, STGMEDIUM&)
  1688 	return E_FAIL;
  1693 //-----------------------------------------------------
  1694 //-----------------------------------------------------
  1695 CLSID nsDataObj::GetClassID() const
  1697 	return CLSID_nsDataObj;
  1700 //-----------------------------------------------------
  1701 // Registers the DataFlavor/FE pair.
  1702 //-----------------------------------------------------
  1703 void nsDataObj::AddDataFlavor(const char* aDataFlavor, LPFORMATETC aFE)
  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);
  1713 //-----------------------------------------------------
  1714 // Sets the transferable object
  1715 //-----------------------------------------------------
  1716 void nsDataObj::SetTransferable(nsITransferable * aTransferable)
  1718     NS_IF_RELEASE(mTransferable);
  1720   mTransferable = aTransferable;
  1721   if (nullptr == mTransferable) {
  1722     return;
  1725   NS_ADDREF(mTransferable);
  1727   return;
  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 )
  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;    
  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;    
  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 )
  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;    
  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 ) 
  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);
  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);
  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;
  1923 HRESULT 
  1924 nsDataObj :: GetUniformResourceLocator( FORMATETC& aFE, STGMEDIUM& aSTG, bool aIsUnicode )
  1926   HRESULT res = S_OK;
  1927   if (IsFlavourPresent(kURLMime)) {
  1928     if ( aIsUnicode )
  1929       res = ExtractUniformResourceLocatorW( aFE, aSTG );
  1930     else
  1931       res = ExtractUniformResourceLocatorA( aFE, aSTG );
  1933   else
  1934     NS_WARNING ("Not yet implemented\n");
  1935   return res;
  1938 HRESULT
  1939 nsDataObj::ExtractUniformResourceLocatorA(FORMATETC& aFE, STGMEDIUM& aSTG )
  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;
  1959   strcpy(contents, asciiUrl.get());
  1960   GlobalUnlock(hGlobalMemory);
  1961   aSTG.hGlobal = hGlobalMemory;
  1962   aSTG.tymed = TYMED_HGLOBAL;
  1964   return result;
  1967 HRESULT
  1968 nsDataObj::ExtractUniformResourceLocatorW(FORMATETC& aFE, STGMEDIUM& aSTG )
  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;
  1987   wcscpy(contents, url.get());
  1988   GlobalUnlock(hGlobalMemory);
  1989   aSTG.hGlobal = hGlobalMemory;
  1990   aSTG.tymed = TYMED_HGLOBAL;
  1992   return result;
  1996 // Gets the filename from the kFilePromiseURLMime flavour
  1997 HRESULT nsDataObj::GetDownloadDetails(nsIURI **aSourceURI,
  1998                                       nsAString &aFilename)
  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);
  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;
  2045 HRESULT nsDataObj::GetFileDescriptor_IStreamA(FORMATETC& aFE, STGMEDIUM& aSTG)
  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;
  2056   nsAutoString wideFileName;
  2057   HRESULT res;
  2058   nsCOMPtr<nsIURI> sourceURI;
  2059   res = GetDownloadDetails(getter_AddRefs(sourceURI), wideFileName);
  2060   if (FAILED(res))
  2062     ::GlobalFree(fileGroupDescHandle);
  2063     return res;
  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;
  2083 HRESULT nsDataObj::GetFileDescriptor_IStreamW(FORMATETC& aFE, STGMEDIUM& aSTG)
  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;
  2094   nsAutoString wideFileName;
  2095   HRESULT res;
  2096   nsCOMPtr<nsIURI> sourceURI;
  2097   res = GetDownloadDetails(getter_AddRefs(sourceURI),
  2098                            wideFileName);
  2099   if (FAILED(res))
  2101     ::GlobalFree(fileGroupDescHandle);
  2102     return res;
  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;
  2118 HRESULT nsDataObj::GetFileContents_IStream(FORMATETC& aFE, STGMEDIUM& aSTG)
  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;
  2132 void nsDataObj::RemoveTempFile(nsITimer* aTimer, void* aClosure)
  2134   nsDataObj *timedDataObj = static_cast<nsDataObj *>(aClosure);
  2135   if (timedDataObj->mCachedTempFile) {
  2136     timedDataObj->mCachedTempFile->Remove(false);
  2137     timedDataObj->mCachedTempFile = nullptr;
  2139   timedDataObj->Release();

mercurial