summaryrefslogtreecommitdiff
path: root/widget/windows/nsDataObjCollection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/windows/nsDataObjCollection.cpp')
-rw-r--r--widget/windows/nsDataObjCollection.cpp405
1 files changed, 405 insertions, 0 deletions
diff --git a/widget/windows/nsDataObjCollection.cpp b/widget/windows/nsDataObjCollection.cpp
new file mode 100644
index 0000000000..7399272e78
--- /dev/null
+++ b/widget/windows/nsDataObjCollection.cpp
@@ -0,0 +1,405 @@
+/* -*- 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()
+ : m_cRef(0)
+{
+}
+
+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_IAsyncOperation == riid) {
+ *ppv = static_cast<IAsyncOperation*>(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;
+}