mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	* Removed dead code. * Removed a duplicated field (`m_cRef`) from the derived class. * Removed #ifdefs for older SDKs that we no longer support. * Made access specifiers more restrictive. * Added `override` and `final`. * De-virtualized member functions unless it is neccessary. Differential Revision: https://phabricator.services.mozilla.com/D21864 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			370 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include <shlobj.h>
 | 
						|
 | 
						|
#include "nsDataObjCollection.h"
 | 
						|
#include "nsClipboard.h"
 | 
						|
#include "IEnumFE.h"
 | 
						|
 | 
						|
#include <ole2.h>
 | 
						|
 | 
						|
// {25589C3E-1FAC-47b9-BF43-CAEA89B79533}
 | 
						|
const IID IID_IDataObjCollection = {
 | 
						|
    0x25589c3e,
 | 
						|
    0x1fac,
 | 
						|
    0x47b9,
 | 
						|
    {0xbf, 0x43, 0xca, 0xea, 0x89, 0xb7, 0x95, 0x33}};
 | 
						|
 | 
						|
/*
 | 
						|
 * Class nsDataObjCollection
 | 
						|
 */
 | 
						|
 | 
						|
nsDataObjCollection::nsDataObjCollection() {}
 | 
						|
 | 
						|
nsDataObjCollection::~nsDataObjCollection() { mDataObjects.Clear(); }
 | 
						|
 | 
						|
// IUnknown interface methods - see iunknown.h for documentation
 | 
						|
STDMETHODIMP nsDataObjCollection::QueryInterface(REFIID riid, void** ppv) {
 | 
						|
  *ppv = nullptr;
 | 
						|
 | 
						|
  if ((IID_IUnknown == riid) || (IID_IDataObject == riid)) {
 | 
						|
    *ppv = static_cast<IDataObject*>(this);
 | 
						|
    AddRef();
 | 
						|
    return NOERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  if (IID_IDataObjCollection == riid) {
 | 
						|
    *ppv = static_cast<nsIDataObjCollection*>(this);
 | 
						|
    AddRef();
 | 
						|
    return NOERROR;
 | 
						|
  }
 | 
						|
  // offer to operate asynchronously (required by nsDragService)
 | 
						|
  if (IID_IDataObjectAsyncCapability == riid) {
 | 
						|
    *ppv = static_cast<IDataObjectAsyncCapability*>(this);
 | 
						|
    AddRef();
 | 
						|
    return NOERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  return E_NOINTERFACE;
 | 
						|
}
 | 
						|
 | 
						|
STDMETHODIMP_(ULONG) nsDataObjCollection::AddRef() { return ++m_cRef; }
 | 
						|
 | 
						|
STDMETHODIMP_(ULONG) nsDataObjCollection::Release() {
 | 
						|
  if (0 != --m_cRef) return m_cRef;
 | 
						|
 | 
						|
  delete this;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
// IDataObject methods
 | 
						|
STDMETHODIMP nsDataObjCollection::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
 | 
						|
  static CLIPFORMAT fileDescriptorFlavorA =
 | 
						|
      ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
 | 
						|
  static CLIPFORMAT fileDescriptorFlavorW =
 | 
						|
      ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
 | 
						|
  static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
 | 
						|
 | 
						|
  switch (pFE->cfFormat) {
 | 
						|
    case CF_TEXT:
 | 
						|
    case CF_UNICODETEXT:
 | 
						|
      return GetText(pFE, pSTM);
 | 
						|
    case CF_HDROP:
 | 
						|
      return GetFile(pFE, pSTM);
 | 
						|
    default:
 | 
						|
      if (pFE->cfFormat == fileDescriptorFlavorA ||
 | 
						|
          pFE->cfFormat == fileDescriptorFlavorW) {
 | 
						|
        return GetFileDescriptors(pFE, pSTM);
 | 
						|
      }
 | 
						|
      if (pFE->cfFormat == fileFlavor) {
 | 
						|
        return GetFileContents(pFE, pSTM);
 | 
						|
      }
 | 
						|
  }
 | 
						|
  return GetFirstSupporting(pFE, pSTM);
 | 
						|
}
 | 
						|
 | 
						|
STDMETHODIMP nsDataObjCollection::GetDataHere(LPFORMATETC pFE,
 | 
						|
                                              LPSTGMEDIUM pSTM) {
 | 
						|
  return E_FAIL;
 | 
						|
}
 | 
						|
 | 
						|
// Other objects querying to see if we support a particular format
 | 
						|
STDMETHODIMP nsDataObjCollection::QueryGetData(LPFORMATETC pFE) {
 | 
						|
  UINT format = nsClipboard::GetFormat(MULTI_MIME);
 | 
						|
 | 
						|
  if (format == pFE->cfFormat) {
 | 
						|
    return S_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    IDataObject* dataObj = mDataObjects.ElementAt(i);
 | 
						|
    if (S_OK == dataObj->QueryGetData(pFE)) {
 | 
						|
      return S_OK;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return DV_E_FORMATETC;
 | 
						|
}
 | 
						|
 | 
						|
STDMETHODIMP nsDataObjCollection::SetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM,
 | 
						|
                                          BOOL fRelease) {
 | 
						|
  // Set arbitrary data formats on the first object in the collection and let
 | 
						|
  // it handle the heavy lifting
 | 
						|
  if (mDataObjects.Length() == 0) return E_FAIL;
 | 
						|
  return mDataObjects.ElementAt(0)->SetData(pFE, pSTM, fRelease);
 | 
						|
}
 | 
						|
 | 
						|
// Registers a DataFlavor/FE pair
 | 
						|
void nsDataObjCollection::AddDataFlavor(const char* aDataFlavor,
 | 
						|
                                        LPFORMATETC aFE) {
 | 
						|
  // Add the FormatEtc to our list if it's not already there.  We don't care
 | 
						|
  // about the internal aDataFlavor because nsDataObj handles that.
 | 
						|
  IEnumFORMATETC* ifEtc;
 | 
						|
  FORMATETC fEtc;
 | 
						|
  ULONG num;
 | 
						|
  if (S_OK != this->EnumFormatEtc(DATADIR_GET, &ifEtc)) return;
 | 
						|
  while (S_OK == ifEtc->Next(1, &fEtc, &num)) {
 | 
						|
    NS_ASSERTION(
 | 
						|
        1 == num,
 | 
						|
        "Bit off more than we can chew in nsDataObjCollection::AddDataFlavor");
 | 
						|
    if (FormatsMatch(fEtc, *aFE)) {
 | 
						|
      ifEtc->Release();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }  // If we didn't find a matching format, add this one
 | 
						|
  ifEtc->Release();
 | 
						|
  m_enumFE->AddFormatEtc(aFE);
 | 
						|
}
 | 
						|
 | 
						|
// We accept ownership of the nsDataObj which we free on destruction
 | 
						|
void nsDataObjCollection::AddDataObject(IDataObject* aDataObj) {
 | 
						|
  nsDataObj* dataObj = reinterpret_cast<nsDataObj*>(aDataObj);
 | 
						|
  mDataObjects.AppendElement(dataObj);
 | 
						|
}
 | 
						|
 | 
						|
// Methods for getting data
 | 
						|
HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
 | 
						|
  STGMEDIUM workingmedium;
 | 
						|
  FORMATETC fe = *pFE;
 | 
						|
  HGLOBAL hGlobalMemory;
 | 
						|
  HRESULT hr;
 | 
						|
  // Make enough space for the header and the trailing null
 | 
						|
  uint32_t buffersize = sizeof(DROPFILES) + sizeof(char16_t);
 | 
						|
  uint32_t alloclen = 0;
 | 
						|
  char16_t* realbuffer;
 | 
						|
  nsAutoString filename;
 | 
						|
 | 
						|
  hGlobalMemory = GlobalAlloc(GHND, buffersize);
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
    hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
    if (hr != S_OK) {
 | 
						|
      switch (hr) {
 | 
						|
        case DV_E_FORMATETC:
 | 
						|
          continue;
 | 
						|
        default:
 | 
						|
          return hr;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // Now we need to pull out the filename
 | 
						|
    char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
 | 
						|
    if (buffer == nullptr) return E_FAIL;
 | 
						|
    buffer += sizeof(DROPFILES) / sizeof(char16_t);
 | 
						|
    filename = buffer;
 | 
						|
    GlobalUnlock(workingmedium.hGlobal);
 | 
						|
    ReleaseStgMedium(&workingmedium);
 | 
						|
    // Now put the filename into our buffer
 | 
						|
    alloclen = (filename.Length() + 1) * sizeof(char16_t);
 | 
						|
    hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
 | 
						|
    if (hGlobalMemory == nullptr) return E_FAIL;
 | 
						|
    realbuffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
 | 
						|
    if (!realbuffer) return E_FAIL;
 | 
						|
    realbuffer--;  // Overwrite the preceding null
 | 
						|
    memcpy(realbuffer, filename.get(), alloclen);
 | 
						|
    GlobalUnlock(hGlobalMemory);
 | 
						|
    buffersize += alloclen;
 | 
						|
  }
 | 
						|
  // We get the last null (on the double null terminator) for free since we used
 | 
						|
  // the zero memory flag when we allocated.  All we need to do is fill the
 | 
						|
  // DROPFILES structure
 | 
						|
  DROPFILES* df = (DROPFILES*)GlobalLock(hGlobalMemory);
 | 
						|
  if (!df) return E_FAIL;
 | 
						|
  df->pFiles = sizeof(DROPFILES);  // Offset to start of file name string
 | 
						|
  df->fNC = 0;
 | 
						|
  df->pt.x = 0;
 | 
						|
  df->pt.y = 0;
 | 
						|
  df->fWide = TRUE;  // utf-16 chars
 | 
						|
  GlobalUnlock(hGlobalMemory);
 | 
						|
  // Finally fill out the STGMEDIUM struct
 | 
						|
  pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
  pSTM->pUnkForRelease = nullptr;  // Caller gets to free the data
 | 
						|
  pSTM->hGlobal = hGlobalMemory;
 | 
						|
  return S_OK;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
 | 
						|
  STGMEDIUM workingmedium;
 | 
						|
  FORMATETC fe = *pFE;
 | 
						|
  HGLOBAL hGlobalMemory;
 | 
						|
  HRESULT hr;
 | 
						|
  uint32_t buffersize = 1;
 | 
						|
  uint32_t alloclen = 0;
 | 
						|
 | 
						|
  hGlobalMemory = GlobalAlloc(GHND, buffersize);
 | 
						|
 | 
						|
  if (pFE->cfFormat == CF_TEXT) {
 | 
						|
    nsAutoCString text;
 | 
						|
    for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
      nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
      hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
      if (hr != S_OK) {
 | 
						|
        switch (hr) {
 | 
						|
          case DV_E_FORMATETC:
 | 
						|
            continue;
 | 
						|
          default:
 | 
						|
            return hr;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      // Now we need to pull out the text
 | 
						|
      char* buffer = (char*)GlobalLock(workingmedium.hGlobal);
 | 
						|
      if (buffer == nullptr) return E_FAIL;
 | 
						|
      text = buffer;
 | 
						|
      GlobalUnlock(workingmedium.hGlobal);
 | 
						|
      ReleaseStgMedium(&workingmedium);
 | 
						|
      // Now put the text into our buffer
 | 
						|
      alloclen = text.Length();
 | 
						|
      hGlobalMemory =
 | 
						|
          ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
 | 
						|
      if (hGlobalMemory == nullptr) return E_FAIL;
 | 
						|
      buffer = ((char*)GlobalLock(hGlobalMemory) + buffersize);
 | 
						|
      if (!buffer) return E_FAIL;
 | 
						|
      buffer--;  // Overwrite the preceding null
 | 
						|
      memcpy(buffer, text.get(), alloclen);
 | 
						|
      GlobalUnlock(hGlobalMemory);
 | 
						|
      buffersize += alloclen;
 | 
						|
    }
 | 
						|
    pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
    pSTM->pUnkForRelease = nullptr;  // Caller gets to free the data
 | 
						|
    pSTM->hGlobal = hGlobalMemory;
 | 
						|
    return S_OK;
 | 
						|
  }
 | 
						|
  if (pFE->cfFormat == CF_UNICODETEXT) {
 | 
						|
    buffersize = sizeof(char16_t);
 | 
						|
    nsAutoString text;
 | 
						|
    for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
      nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
      hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
      if (hr != S_OK) {
 | 
						|
        switch (hr) {
 | 
						|
          case DV_E_FORMATETC:
 | 
						|
            continue;
 | 
						|
          default:
 | 
						|
            return hr;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      // Now we need to pull out the text
 | 
						|
      char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
 | 
						|
      if (buffer == nullptr) return E_FAIL;
 | 
						|
      text = buffer;
 | 
						|
      GlobalUnlock(workingmedium.hGlobal);
 | 
						|
      ReleaseStgMedium(&workingmedium);
 | 
						|
      // Now put the text into our buffer
 | 
						|
      alloclen = text.Length() * sizeof(char16_t);
 | 
						|
      hGlobalMemory =
 | 
						|
          ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
 | 
						|
      if (hGlobalMemory == nullptr) return E_FAIL;
 | 
						|
      buffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
 | 
						|
      if (!buffer) return E_FAIL;
 | 
						|
      buffer--;  // Overwrite the preceding null
 | 
						|
      memcpy(buffer, text.get(), alloclen);
 | 
						|
      GlobalUnlock(hGlobalMemory);
 | 
						|
      buffersize += alloclen;
 | 
						|
    }
 | 
						|
    pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
    pSTM->pUnkForRelease = nullptr;  // Caller gets to free the data
 | 
						|
    pSTM->hGlobal = hGlobalMemory;
 | 
						|
    return S_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  return E_FAIL;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE,
 | 
						|
                                                LPSTGMEDIUM pSTM) {
 | 
						|
  STGMEDIUM workingmedium;
 | 
						|
  FORMATETC fe = *pFE;
 | 
						|
  HGLOBAL hGlobalMemory;
 | 
						|
  HRESULT hr;
 | 
						|
  uint32_t buffersize = sizeof(UINT);
 | 
						|
  uint32_t alloclen = sizeof(FILEDESCRIPTOR);
 | 
						|
 | 
						|
  hGlobalMemory = GlobalAlloc(GHND, buffersize);
 | 
						|
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
    hr = dataObj->GetData(&fe, &workingmedium);
 | 
						|
    if (hr != S_OK) {
 | 
						|
      switch (hr) {
 | 
						|
        case DV_E_FORMATETC:
 | 
						|
          continue;
 | 
						|
        default:
 | 
						|
          return hr;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // Now we need to pull out the filedescriptor
 | 
						|
    FILEDESCRIPTOR* buffer =
 | 
						|
        (FILEDESCRIPTOR*)((char*)GlobalLock(workingmedium.hGlobal) +
 | 
						|
                          sizeof(UINT));
 | 
						|
    if (buffer == nullptr) return E_FAIL;
 | 
						|
    hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
 | 
						|
    if (hGlobalMemory == nullptr) return E_FAIL;
 | 
						|
    FILEGROUPDESCRIPTOR* realbuffer =
 | 
						|
        (FILEGROUPDESCRIPTOR*)GlobalLock(hGlobalMemory);
 | 
						|
    if (!realbuffer) return E_FAIL;
 | 
						|
    FILEDESCRIPTOR* copyloc = (FILEDESCRIPTOR*)((char*)realbuffer + buffersize);
 | 
						|
    memcpy(copyloc, buffer, alloclen);
 | 
						|
    realbuffer->cItems++;
 | 
						|
    GlobalUnlock(hGlobalMemory);
 | 
						|
    GlobalUnlock(workingmedium.hGlobal);
 | 
						|
    ReleaseStgMedium(&workingmedium);
 | 
						|
    buffersize += alloclen;
 | 
						|
  }
 | 
						|
  pSTM->tymed = TYMED_HGLOBAL;
 | 
						|
  pSTM->pUnkForRelease = nullptr;  // Caller gets to free the data
 | 
						|
  pSTM->hGlobal = hGlobalMemory;
 | 
						|
  return S_OK;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetFileContents(LPFORMATETC pFE,
 | 
						|
                                             LPSTGMEDIUM pSTM) {
 | 
						|
  ULONG num = 0;
 | 
						|
  ULONG numwanted = (pFE->lindex == -1) ? 0 : pFE->lindex;
 | 
						|
  FORMATETC fEtc = *pFE;
 | 
						|
  fEtc.lindex = -1;  // We're lying to the data object so it thinks it's alone
 | 
						|
 | 
						|
  // The key for this data type is to figure out which data object the index
 | 
						|
  // corresponds to and then just pass it along
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    nsDataObj* dataObj = mDataObjects.ElementAt(i);
 | 
						|
    if (dataObj->QueryGetData(&fEtc) != S_OK) continue;
 | 
						|
    if (num == numwanted) return dataObj->GetData(pFE, pSTM);
 | 
						|
    num++;
 | 
						|
  }
 | 
						|
  return DV_E_LINDEX;
 | 
						|
}
 | 
						|
 | 
						|
HRESULT nsDataObjCollection::GetFirstSupporting(LPFORMATETC pFE,
 | 
						|
                                                LPSTGMEDIUM pSTM) {
 | 
						|
  // There is no way to pass more than one of this, so just find the first data
 | 
						|
  // object that supports it and pass it along
 | 
						|
  for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
 | 
						|
    if (mDataObjects.ElementAt(i)->QueryGetData(pFE) == S_OK)
 | 
						|
      return mDataObjects.ElementAt(i)->GetData(pFE, pSTM);
 | 
						|
  }
 | 
						|
  return DV_E_FORMATETC;
 | 
						|
}
 |