diff -r 000000000000 -r 6474c204b198 widget/windows/nsDataObjCollection.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widget/windows/nsDataObjCollection.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,466 @@ +/* -*- 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 + +#include "nsDataObjCollection.h" +#include "nsClipboard.h" +#include "IEnumFE.h" + +#include + +// {25589C3E-1FAC-47b9-BF43-CAEA89B79533} +const IID IID_IDataObjCollection = + {0x25589c3e, 0x1fac, 0x47b9, {0xbf, 0x43, 0xca, 0xea, 0x89, 0xb7, 0x95, 0x33}}; + +/* + * Class nsDataObjCollection + */ + +nsDataObjCollection::nsDataObjCollection() + : m_cRef(0), mIsAsyncMode(FALSE), mIsInOperation(FALSE) +{ + m_enumFE = new CEnumFormatEtc(); + m_enumFE->AddRef(); +} + +nsDataObjCollection::~nsDataObjCollection() +{ + mDataFlavors.Clear(); + mDataObjects.Clear(); + + m_enumFE->Release(); +} + + +// 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(this); + AddRef(); + return NOERROR; + } + + if ( IID_IDataObjCollection == riid ) { + *ppv = static_cast(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; +} + +BOOL nsDataObjCollection::FormatsMatch(const FORMATETC& source, + const FORMATETC& target) const +{ + if ((source.cfFormat == target.cfFormat) && + (source.dwAspect & target.dwAspect) && + (source.tymed & target.tymed)) { + return TRUE; + } else { + return FALSE; + } +} + +// 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::GetCanonicalFormatEtc(LPFORMATETC pFEIn, + LPFORMATETC pFEOut) +{ + return E_NOTIMPL; +} + +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); +} + +STDMETHODIMP nsDataObjCollection::EnumFormatEtc(DWORD dwDir, + LPENUMFORMATETC *ppEnum) +{ + if (dwDir == DATADIR_GET) { + // Clone addref's the new enumerator. + m_enumFE->Clone(ppEnum); + if (!(*ppEnum)) + return E_FAIL; + (*ppEnum)->Reset(); + return S_OK; + } + + return E_NOTIMPL; +} + +STDMETHODIMP nsDataObjCollection::DAdvise(LPFORMATETC pFE, + DWORD dwFlags, + LPADVISESINK pIAdviseSink, + DWORD* pdwConn) +{ + return OLE_E_ADVISENOTSUPPORTED; +} + +STDMETHODIMP nsDataObjCollection::DUnadvise(DWORD dwConn) +{ + return OLE_E_ADVISENOTSUPPORTED; +} + +STDMETHODIMP nsDataObjCollection::EnumDAdvise(LPENUMSTATDATA *ppEnum) +{ + return OLE_E_ADVISENOTSUPPORTED; +} + +// GetData and SetData helper functions +HRESULT nsDataObjCollection::AddSetFormat(FORMATETC& aFE) +{ + return S_OK; +} + +HRESULT nsDataObjCollection::AddGetFormat(FORMATETC& aFE) +{ + return S_OK; +} + +// 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(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(FILEGROUPDESCRIPTOR); + 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, sizeof(FILEDESCRIPTOR)); + 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); + numwanted++; + } + 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; +}