widget/windows/nsDataObjCollection.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 <shlobj.h>
     8 #include "nsDataObjCollection.h"
     9 #include "nsClipboard.h"
    10 #include "IEnumFE.h"
    12 #include <ole2.h>
    14 // {25589C3E-1FAC-47b9-BF43-CAEA89B79533}
    15 const IID IID_IDataObjCollection =
    16   {0x25589c3e, 0x1fac, 0x47b9, {0xbf, 0x43, 0xca, 0xea, 0x89, 0xb7, 0x95, 0x33}};
    18 /*
    19  * Class nsDataObjCollection
    20  */
    22 nsDataObjCollection::nsDataObjCollection()
    23   : m_cRef(0), mIsAsyncMode(FALSE), mIsInOperation(FALSE)
    24 {
    25   m_enumFE = new CEnumFormatEtc();
    26   m_enumFE->AddRef();
    27 }
    29 nsDataObjCollection::~nsDataObjCollection()
    30 {
    31   mDataFlavors.Clear();
    32   mDataObjects.Clear();
    34   m_enumFE->Release();
    35 }
    38 // IUnknown interface methods - see iunknown.h for documentation
    39 STDMETHODIMP nsDataObjCollection::QueryInterface(REFIID riid, void** ppv)
    40 {
    41   *ppv=nullptr;
    43   if ( (IID_IUnknown == riid) || (IID_IDataObject  == riid) ) {
    44     *ppv = static_cast<IDataObject*>(this); 
    45     AddRef();
    46     return NOERROR;
    47   }
    49   if ( IID_IDataObjCollection  == riid ) {
    50     *ppv = static_cast<nsIDataObjCollection*>(this); 
    51     AddRef();
    52     return NOERROR;
    53   }
    55   return E_NOINTERFACE;
    56 }
    58 STDMETHODIMP_(ULONG) nsDataObjCollection::AddRef()
    59 {
    60   return ++m_cRef;
    61 }
    63 STDMETHODIMP_(ULONG) nsDataObjCollection::Release()
    64 {
    65   if (0 != --m_cRef)
    66     return m_cRef;
    68   delete this;
    70   return 0;
    71 }
    73 BOOL nsDataObjCollection::FormatsMatch(const FORMATETC& source,
    74                                        const FORMATETC& target) const
    75 {
    76   if ((source.cfFormat == target.cfFormat) &&
    77       (source.dwAspect & target.dwAspect)  &&
    78       (source.tymed    & target.tymed)) {
    79     return TRUE;
    80   } else {
    81     return FALSE;
    82   }
    83 }
    85 // IDataObject methods
    86 STDMETHODIMP nsDataObjCollection::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
    87 {
    88   static CLIPFORMAT fileDescriptorFlavorA =
    89                                ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
    90   static CLIPFORMAT fileDescriptorFlavorW =
    91                                ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
    92   static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
    94   switch (pFE->cfFormat) {
    95   case CF_TEXT:
    96   case CF_UNICODETEXT:
    97     return GetText(pFE, pSTM);
    98   case CF_HDROP:
    99     return GetFile(pFE, pSTM);
   100   default:
   101     if (pFE->cfFormat == fileDescriptorFlavorA ||
   102         pFE->cfFormat == fileDescriptorFlavorW) {
   103       return GetFileDescriptors(pFE, pSTM);
   104     }
   105     if (pFE->cfFormat == fileFlavor) {
   106       return GetFileContents(pFE, pSTM);
   107     }
   108   }
   109   return GetFirstSupporting(pFE, pSTM);
   110 }
   112 STDMETHODIMP nsDataObjCollection::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
   113 {
   114   return E_FAIL;
   115 }
   117 // Other objects querying to see if we support a particular format
   118 STDMETHODIMP nsDataObjCollection::QueryGetData(LPFORMATETC pFE)
   119 {
   120   UINT format = nsClipboard::GetFormat(MULTI_MIME);
   122   if (format == pFE->cfFormat) {
   123     return S_OK;
   124   }
   126   for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
   127     IDataObject * dataObj = mDataObjects.ElementAt(i);
   128     if (S_OK == dataObj->QueryGetData(pFE)) {
   129       return S_OK;
   130     }
   131   }
   133   return DV_E_FORMATETC;
   134 }
   136 STDMETHODIMP nsDataObjCollection::GetCanonicalFormatEtc(LPFORMATETC pFEIn,
   137                                                         LPFORMATETC pFEOut)
   138 {
   139   return E_NOTIMPL;
   140 }
   142 STDMETHODIMP nsDataObjCollection::SetData(LPFORMATETC pFE,
   143                                           LPSTGMEDIUM pSTM,
   144                                           BOOL fRelease)
   145 {
   146   // Set arbitrary data formats on the first object in the collection and let
   147   // it handle the heavy lifting
   148   if (mDataObjects.Length() == 0)
   149     return E_FAIL;
   150   return mDataObjects.ElementAt(0)->SetData(pFE, pSTM, fRelease);
   151 }
   153 STDMETHODIMP nsDataObjCollection::EnumFormatEtc(DWORD dwDir,
   154                                                 LPENUMFORMATETC *ppEnum)
   155 {
   156   if (dwDir == DATADIR_GET) {
   157     // Clone addref's the new enumerator.
   158     m_enumFE->Clone(ppEnum);
   159     if (!(*ppEnum))
   160       return E_FAIL;
   161     (*ppEnum)->Reset();
   162     return S_OK;
   163   }
   165   return E_NOTIMPL;
   166 }
   168 STDMETHODIMP nsDataObjCollection::DAdvise(LPFORMATETC pFE,
   169                                           DWORD dwFlags,
   170                                           LPADVISESINK pIAdviseSink,
   171                                           DWORD* pdwConn)
   172 {
   173   return OLE_E_ADVISENOTSUPPORTED;
   174 }
   176 STDMETHODIMP nsDataObjCollection::DUnadvise(DWORD dwConn)
   177 {
   178   return OLE_E_ADVISENOTSUPPORTED;
   179 }
   181 STDMETHODIMP nsDataObjCollection::EnumDAdvise(LPENUMSTATDATA *ppEnum)
   182 {
   183   return OLE_E_ADVISENOTSUPPORTED;
   184 }
   186 // GetData and SetData helper functions
   187 HRESULT nsDataObjCollection::AddSetFormat(FORMATETC& aFE)
   188 {
   189   return S_OK;
   190 }
   192 HRESULT nsDataObjCollection::AddGetFormat(FORMATETC& aFE)
   193 {
   194   return S_OK;
   195 }
   197 // Registers a DataFlavor/FE pair
   198 void nsDataObjCollection::AddDataFlavor(const char * aDataFlavor,
   199                                         LPFORMATETC aFE)
   200 {
   201   // Add the FormatEtc to our list if it's not already there.  We don't care
   202   // about the internal aDataFlavor because nsDataObj handles that.
   203   IEnumFORMATETC * ifEtc;
   204   FORMATETC fEtc;
   205   ULONG num;
   206   if (S_OK != this->EnumFormatEtc(DATADIR_GET, &ifEtc))
   207     return;
   208   while (S_OK == ifEtc->Next(1, &fEtc, &num)) {
   209     NS_ASSERTION(1 == num,
   210          "Bit off more than we can chew in nsDataObjCollection::AddDataFlavor");
   211     if (FormatsMatch(fEtc, *aFE)) {
   212       ifEtc->Release();
   213       return;
   214     }
   215   } // If we didn't find a matching format, add this one
   216   ifEtc->Release();
   217   m_enumFE->AddFormatEtc(aFE);
   218 }
   220 // We accept ownership of the nsDataObj which we free on destruction
   221 void nsDataObjCollection::AddDataObject(IDataObject * aDataObj)
   222 {
   223   nsDataObj* dataObj = reinterpret_cast<nsDataObj*>(aDataObj);
   224   mDataObjects.AppendElement(dataObj);
   225 }
   227 // Methods for getting data
   228 HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
   229 {
   230   STGMEDIUM workingmedium;
   231   FORMATETC fe = *pFE;
   232   HGLOBAL hGlobalMemory;
   233   HRESULT hr;
   234   // Make enough space for the header and the trailing null
   235   uint32_t buffersize = sizeof(DROPFILES) + sizeof(char16_t);
   236   uint32_t alloclen = 0;
   237   char16_t* realbuffer;
   238   nsAutoString filename;
   240   hGlobalMemory = GlobalAlloc(GHND, buffersize);
   242   for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
   243     nsDataObj* dataObj = mDataObjects.ElementAt(i);
   244     hr = dataObj->GetData(&fe, &workingmedium);
   245     if (hr != S_OK) {
   246       switch (hr) {
   247       case DV_E_FORMATETC:
   248         continue;
   249       default:
   250         return hr;
   251       }
   252     }
   253     // Now we need to pull out the filename
   254     char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
   255     if (buffer == nullptr)
   256       return E_FAIL;
   257     buffer += sizeof(DROPFILES)/sizeof(char16_t);
   258     filename = buffer;
   259     GlobalUnlock(workingmedium.hGlobal);
   260     ReleaseStgMedium(&workingmedium);
   261     // Now put the filename into our buffer
   262     alloclen = (filename.Length() + 1) * sizeof(char16_t);
   263     hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
   264     if (hGlobalMemory == nullptr)
   265       return E_FAIL;
   266     realbuffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
   267     if (!realbuffer)
   268       return E_FAIL;
   269     realbuffer--; // Overwrite the preceding null
   270     memcpy(realbuffer, filename.get(), alloclen);
   271     GlobalUnlock(hGlobalMemory);
   272     buffersize += alloclen;
   273   }
   274   // We get the last null (on the double null terminator) for free since we used
   275   // the zero memory flag when we allocated.  All we need to do is fill the
   276   // DROPFILES structure
   277   DROPFILES* df = (DROPFILES*)GlobalLock(hGlobalMemory);
   278   if (!df)
   279     return E_FAIL;
   280   df->pFiles = sizeof(DROPFILES); //Offset to start of file name string
   281   df->fNC    = 0;
   282   df->pt.x   = 0;
   283   df->pt.y   = 0;
   284   df->fWide  = TRUE; // utf-16 chars
   285   GlobalUnlock(hGlobalMemory);
   286   // Finally fill out the STGMEDIUM struct
   287   pSTM->tymed = TYMED_HGLOBAL;
   288   pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
   289   pSTM->hGlobal = hGlobalMemory;
   290   return S_OK;
   291 }
   293 HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
   294 {
   295   STGMEDIUM workingmedium;
   296   FORMATETC fe = *pFE;
   297   HGLOBAL hGlobalMemory;
   298   HRESULT hr;
   299   uint32_t buffersize = 1;
   300   uint32_t alloclen = 0;
   302   hGlobalMemory = GlobalAlloc(GHND, buffersize);
   304   if (pFE->cfFormat == CF_TEXT) {
   305     nsAutoCString text;
   306     for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
   307       nsDataObj* dataObj = mDataObjects.ElementAt(i);
   308       hr = dataObj->GetData(&fe, &workingmedium);
   309       if (hr != S_OK) {
   310         switch (hr) {
   311         case DV_E_FORMATETC:
   312           continue;
   313         default:
   314           return hr;
   315         }
   316       }
   317       // Now we need to pull out the text
   318       char* buffer = (char*)GlobalLock(workingmedium.hGlobal);
   319       if (buffer == nullptr)
   320         return E_FAIL;
   321       text = buffer;
   322       GlobalUnlock(workingmedium.hGlobal);
   323       ReleaseStgMedium(&workingmedium);
   324       // Now put the text into our buffer
   325       alloclen = text.Length();
   326       hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen,
   327                                       GHND);
   328       if (hGlobalMemory == nullptr)
   329         return E_FAIL;
   330       buffer = ((char*)GlobalLock(hGlobalMemory) + buffersize);
   331       if (!buffer)
   332         return E_FAIL;
   333       buffer--; // Overwrite the preceding null
   334       memcpy(buffer, text.get(), alloclen);
   335       GlobalUnlock(hGlobalMemory);
   336       buffersize += alloclen;
   337     }
   338     pSTM->tymed = TYMED_HGLOBAL;
   339     pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
   340     pSTM->hGlobal = hGlobalMemory;
   341     return S_OK;
   342   }
   343   if (pFE->cfFormat == CF_UNICODETEXT) {
   344     buffersize = sizeof(char16_t);
   345     nsAutoString text;
   346     for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
   347       nsDataObj* dataObj = mDataObjects.ElementAt(i);
   348       hr = dataObj->GetData(&fe, &workingmedium);
   349       if (hr != S_OK) {
   350         switch (hr) {
   351         case DV_E_FORMATETC:
   352           continue;
   353         default:
   354           return hr;
   355         }
   356       }
   357       // Now we need to pull out the text
   358       char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
   359       if (buffer == nullptr)
   360         return E_FAIL;
   361       text = buffer;
   362       GlobalUnlock(workingmedium.hGlobal);
   363       ReleaseStgMedium(&workingmedium);
   364       // Now put the text into our buffer
   365       alloclen = text.Length() * sizeof(char16_t);
   366       hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen,
   367                                       GHND);
   368       if (hGlobalMemory == nullptr)
   369         return E_FAIL;
   370       buffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
   371       if (!buffer)
   372         return E_FAIL;
   373       buffer--; // Overwrite the preceding null
   374       memcpy(buffer, text.get(), alloclen);
   375       GlobalUnlock(hGlobalMemory);
   376       buffersize += alloclen;
   377     }
   378     pSTM->tymed = TYMED_HGLOBAL;
   379     pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
   380     pSTM->hGlobal = hGlobalMemory;
   381     return S_OK;
   382   }
   384   return E_FAIL;
   385 }
   387 HRESULT nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE,
   388                                                 LPSTGMEDIUM pSTM)
   389 {
   390   STGMEDIUM workingmedium;
   391   FORMATETC fe = *pFE;
   392   HGLOBAL hGlobalMemory;
   393   HRESULT hr;
   394   uint32_t buffersize = sizeof(FILEGROUPDESCRIPTOR);
   395   uint32_t alloclen = sizeof(FILEDESCRIPTOR);
   397   hGlobalMemory = GlobalAlloc(GHND, buffersize);
   399   for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
   400     nsDataObj* dataObj = mDataObjects.ElementAt(i);
   401     hr = dataObj->GetData(&fe, &workingmedium);
   402     if (hr != S_OK) {
   403       switch (hr) {
   404       case DV_E_FORMATETC:
   405         continue;
   406       default:
   407         return hr;
   408       }
   409     }
   410     // Now we need to pull out the filedescriptor
   411     FILEDESCRIPTOR* buffer =
   412      (FILEDESCRIPTOR*)((char*)GlobalLock(workingmedium.hGlobal) + sizeof(UINT));
   413     if (buffer == nullptr)
   414       return E_FAIL;
   415     hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
   416     if (hGlobalMemory == nullptr)
   417       return E_FAIL;
   418     FILEGROUPDESCRIPTOR* realbuffer =
   419                                 (FILEGROUPDESCRIPTOR*)GlobalLock(hGlobalMemory);
   420     if (!realbuffer)
   421       return E_FAIL;
   422     FILEDESCRIPTOR* copyloc = (FILEDESCRIPTOR*)((char*)realbuffer + buffersize);
   423     memcpy(copyloc, buffer, sizeof(FILEDESCRIPTOR));
   424     realbuffer->cItems++;
   425     GlobalUnlock(hGlobalMemory);
   426     GlobalUnlock(workingmedium.hGlobal);
   427     ReleaseStgMedium(&workingmedium);
   428     buffersize += alloclen;
   429   }
   430   pSTM->tymed = TYMED_HGLOBAL;
   431   pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
   432   pSTM->hGlobal = hGlobalMemory;
   433   return S_OK;
   434 }
   436 HRESULT nsDataObjCollection::GetFileContents(LPFORMATETC pFE, LPSTGMEDIUM pSTM)
   437 {
   438   ULONG num = 0;
   439   ULONG numwanted = (pFE->lindex == -1) ? 0 : pFE->lindex;
   440   FORMATETC fEtc = *pFE;
   441   fEtc.lindex = -1;  // We're lying to the data object so it thinks it's alone
   443   // The key for this data type is to figure out which data object the index
   444   // corresponds to and then just pass it along
   445   for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
   446     nsDataObj* dataObj = mDataObjects.ElementAt(i);
   447     if (dataObj->QueryGetData(&fEtc) != S_OK)
   448       continue;
   449     if (num == numwanted)
   450       return dataObj->GetData(pFE, pSTM);
   451     numwanted++;
   452   }
   453   return DV_E_LINDEX;
   454 }
   456 HRESULT nsDataObjCollection::GetFirstSupporting(LPFORMATETC pFE,
   457                                                 LPSTGMEDIUM pSTM)
   458 {
   459   // There is no way to pass more than one of this, so just find the first data
   460   // object that supports it and pass it along
   461   for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
   462     if (mDataObjects.ElementAt(i)->QueryGetData(pFE) == S_OK)
   463       return mDataObjects.ElementAt(i)->GetData(pFE, pSTM);
   464   }
   465   return DV_E_FORMATETC;
   466 }

mercurial