Bug 1890689 accumulate input in LargerReceiverBlockSizeThanDesiredBuffering GTest...
[gecko.git] / widget / windows / nsDataObjCollection.cpp
blob87505636027a932901953772c26bd2d8d886efef
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,
17 0x1fac,
18 0x47b9,
19 {0xbf, 0x43, 0xca, 0xea, 0x89, 0xb7, 0x95, 0x33}};
22 * Class nsDataObjCollection
25 nsDataObjCollection::nsDataObjCollection() {}
27 nsDataObjCollection::~nsDataObjCollection() { mDataObjects.Clear(); }
29 // IUnknown interface methods - see iunknown.h for documentation
30 STDMETHODIMP nsDataObjCollection::QueryInterface(REFIID riid, void** ppv) {
31 *ppv = nullptr;
33 if ((IID_IUnknown == riid) || (IID_IDataObject == riid)) {
34 *ppv = static_cast<IDataObject*>(this);
35 AddRef();
36 return NOERROR;
39 if (IID_IDataObjCollection == riid) {
40 *ppv = static_cast<nsIDataObjCollection*>(this);
41 AddRef();
42 return NOERROR;
44 // offer to operate asynchronously (required by nsDragService)
45 if (IID_IDataObjectAsyncCapability == riid) {
46 *ppv = static_cast<IDataObjectAsyncCapability*>(this);
47 AddRef();
48 return NOERROR;
51 return E_NOINTERFACE;
54 STDMETHODIMP_(ULONG) nsDataObjCollection::AddRef() { return ++m_cRef; }
56 STDMETHODIMP_(ULONG) nsDataObjCollection::Release() {
57 if (0 != --m_cRef) return m_cRef;
59 delete this;
61 return 0;
64 // IDataObject methods
65 STDMETHODIMP nsDataObjCollection::GetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
66 static CLIPFORMAT fileDescriptorFlavorA =
67 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
68 static CLIPFORMAT fileDescriptorFlavorW =
69 ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
70 static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat(CFSTR_FILECONTENTS);
72 switch (pFE->cfFormat) {
73 case CF_TEXT:
74 case CF_UNICODETEXT:
75 return GetText(pFE, pSTM);
76 case CF_HDROP:
77 return GetFile(pFE, pSTM);
78 default:
79 if (pFE->cfFormat == fileDescriptorFlavorA ||
80 pFE->cfFormat == fileDescriptorFlavorW) {
81 return GetFileDescriptors(pFE, pSTM);
83 if (pFE->cfFormat == fileFlavor) {
84 return GetFileContents(pFE, pSTM);
87 return GetFirstSupporting(pFE, pSTM);
90 STDMETHODIMP nsDataObjCollection::GetDataHere(LPFORMATETC pFE,
91 LPSTGMEDIUM pSTM) {
92 return E_FAIL;
95 // Other objects querying to see if we support a particular format
96 STDMETHODIMP nsDataObjCollection::QueryGetData(LPFORMATETC pFE) {
97 UINT format = nsClipboard::GetFormat(MULTI_MIME);
99 if (format == pFE->cfFormat) {
100 return S_OK;
103 for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
104 IDataObject* dataObj = mDataObjects.ElementAt(i);
105 if (S_OK == dataObj->QueryGetData(pFE)) {
106 return S_OK;
110 return DV_E_FORMATETC;
113 STDMETHODIMP nsDataObjCollection::SetData(LPFORMATETC pFE, LPSTGMEDIUM pSTM,
114 BOOL fRelease) {
115 // Set arbitrary data formats on the first object in the collection and let
116 // it handle the heavy lifting
117 if (mDataObjects.Length() == 0) return E_FAIL;
118 return mDataObjects.ElementAt(0)->SetData(pFE, pSTM, fRelease);
121 // Registers a DataFlavor/FE pair
122 void nsDataObjCollection::AddDataFlavor(const char* aDataFlavor,
123 LPFORMATETC aFE) {
124 // Add the FormatEtc to our list if it's not already there. We don't care
125 // about the internal aDataFlavor because nsDataObj handles that.
126 IEnumFORMATETC* ifEtc;
127 FORMATETC fEtc;
128 ULONG num;
129 if (S_OK != this->EnumFormatEtc(DATADIR_GET, &ifEtc)) return;
130 while (S_OK == ifEtc->Next(1, &fEtc, &num)) {
131 NS_ASSERTION(
132 1 == num,
133 "Bit off more than we can chew in nsDataObjCollection::AddDataFlavor");
134 if (FormatsMatch(fEtc, *aFE)) {
135 ifEtc->Release();
136 return;
138 } // If we didn't find a matching format, add this one
139 ifEtc->Release();
140 m_enumFE->AddFormatEtc(aFE);
143 // We accept ownership of the nsDataObj which we free on destruction
144 void nsDataObjCollection::AddDataObject(IDataObject* aDataObj) {
145 nsDataObj* dataObj = reinterpret_cast<nsDataObj*>(aDataObj);
146 mDataObjects.AppendElement(dataObj);
149 // Methods for getting data
150 HRESULT nsDataObjCollection::GetFile(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
151 STGMEDIUM workingmedium;
152 FORMATETC fe = *pFE;
153 HGLOBAL hGlobalMemory;
154 HRESULT hr;
155 // Make enough space for the header and the trailing null
156 uint32_t buffersize = sizeof(DROPFILES) + sizeof(char16_t);
157 uint32_t alloclen = 0;
158 char16_t* realbuffer;
159 nsAutoString filename;
161 hGlobalMemory = GlobalAlloc(GHND, buffersize);
163 for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
164 nsDataObj* dataObj = mDataObjects.ElementAt(i);
165 hr = dataObj->GetData(&fe, &workingmedium);
166 if (hr != S_OK) {
167 switch (hr) {
168 case DV_E_FORMATETC:
169 continue;
170 default:
171 return hr;
174 // Now we need to pull out the filename
175 char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
176 if (buffer == nullptr) return E_FAIL;
177 buffer += sizeof(DROPFILES) / sizeof(char16_t);
178 filename = buffer;
179 GlobalUnlock(workingmedium.hGlobal);
180 ReleaseStgMedium(&workingmedium);
181 // Now put the filename into our buffer
182 alloclen = (filename.Length() + 1) * sizeof(char16_t);
183 hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
184 if (hGlobalMemory == nullptr) return E_FAIL;
185 realbuffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
186 if (!realbuffer) return E_FAIL;
187 realbuffer--; // Overwrite the preceding null
188 memcpy(realbuffer, filename.get(), alloclen);
189 GlobalUnlock(hGlobalMemory);
190 buffersize += alloclen;
192 // We get the last null (on the double null terminator) for free since we used
193 // the zero memory flag when we allocated. All we need to do is fill the
194 // DROPFILES structure
195 DROPFILES* df = (DROPFILES*)GlobalLock(hGlobalMemory);
196 if (!df) return E_FAIL;
197 df->pFiles = sizeof(DROPFILES); // Offset to start of file name string
198 df->fNC = 0;
199 df->pt.x = 0;
200 df->pt.y = 0;
201 df->fWide = TRUE; // utf-16 chars
202 GlobalUnlock(hGlobalMemory);
203 // Finally fill out the STGMEDIUM struct
204 pSTM->tymed = TYMED_HGLOBAL;
205 pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
206 pSTM->hGlobal = hGlobalMemory;
207 return S_OK;
210 HRESULT nsDataObjCollection::GetText(LPFORMATETC pFE, LPSTGMEDIUM pSTM) {
211 STGMEDIUM workingmedium;
212 FORMATETC fe = *pFE;
213 HGLOBAL hGlobalMemory;
214 HRESULT hr;
215 uint32_t buffersize = 1;
216 uint32_t alloclen = 0;
218 hGlobalMemory = GlobalAlloc(GHND, buffersize);
220 if (pFE->cfFormat == CF_TEXT) {
221 nsAutoCString text;
222 for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
223 nsDataObj* dataObj = mDataObjects.ElementAt(i);
224 hr = dataObj->GetData(&fe, &workingmedium);
225 if (hr != S_OK) {
226 switch (hr) {
227 case DV_E_FORMATETC:
228 continue;
229 default:
230 return hr;
233 // Now we need to pull out the text
234 char* buffer = (char*)GlobalLock(workingmedium.hGlobal);
235 if (buffer == nullptr) return E_FAIL;
236 text = buffer;
237 GlobalUnlock(workingmedium.hGlobal);
238 ReleaseStgMedium(&workingmedium);
239 // Now put the text into our buffer
240 alloclen = text.Length();
241 hGlobalMemory =
242 ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
243 if (hGlobalMemory == nullptr) return E_FAIL;
244 buffer = ((char*)GlobalLock(hGlobalMemory) + buffersize);
245 if (!buffer) return E_FAIL;
246 buffer--; // Overwrite the preceding null
247 memcpy(buffer, text.get(), alloclen);
248 GlobalUnlock(hGlobalMemory);
249 buffersize += alloclen;
251 pSTM->tymed = TYMED_HGLOBAL;
252 pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
253 pSTM->hGlobal = hGlobalMemory;
254 return S_OK;
256 if (pFE->cfFormat == CF_UNICODETEXT) {
257 buffersize = sizeof(char16_t);
258 nsAutoString text;
259 for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
260 nsDataObj* dataObj = mDataObjects.ElementAt(i);
261 hr = dataObj->GetData(&fe, &workingmedium);
262 if (hr != S_OK) {
263 switch (hr) {
264 case DV_E_FORMATETC:
265 continue;
266 default:
267 return hr;
270 // Now we need to pull out the text
271 char16_t* buffer = (char16_t*)GlobalLock(workingmedium.hGlobal);
272 if (buffer == nullptr) return E_FAIL;
273 text = buffer;
274 GlobalUnlock(workingmedium.hGlobal);
275 ReleaseStgMedium(&workingmedium);
276 // Now put the text into our buffer
277 alloclen = text.Length() * sizeof(char16_t);
278 hGlobalMemory =
279 ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
280 if (hGlobalMemory == nullptr) return E_FAIL;
281 buffer = (char16_t*)((char*)GlobalLock(hGlobalMemory) + buffersize);
282 if (!buffer) return E_FAIL;
283 buffer--; // Overwrite the preceding null
284 memcpy(buffer, text.get(), alloclen);
285 GlobalUnlock(hGlobalMemory);
286 buffersize += alloclen;
288 pSTM->tymed = TYMED_HGLOBAL;
289 pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
290 pSTM->hGlobal = hGlobalMemory;
291 return S_OK;
294 return E_FAIL;
297 HRESULT nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE,
298 LPSTGMEDIUM pSTM) {
299 STGMEDIUM workingmedium;
300 FORMATETC fe = *pFE;
301 HGLOBAL hGlobalMemory;
302 HRESULT hr;
303 uint32_t buffersize = sizeof(UINT);
304 uint32_t alloclen = sizeof(FILEDESCRIPTOR);
306 hGlobalMemory = GlobalAlloc(GHND, buffersize);
308 for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
309 nsDataObj* dataObj = mDataObjects.ElementAt(i);
310 hr = dataObj->GetData(&fe, &workingmedium);
311 if (hr != S_OK) {
312 switch (hr) {
313 case DV_E_FORMATETC:
314 continue;
315 default:
316 return hr;
319 // Now we need to pull out the filedescriptor
320 FILEDESCRIPTOR* buffer =
321 (FILEDESCRIPTOR*)((char*)GlobalLock(workingmedium.hGlobal) +
322 sizeof(UINT));
323 if (buffer == nullptr) return E_FAIL;
324 hGlobalMemory = ::GlobalReAlloc(hGlobalMemory, buffersize + alloclen, GHND);
325 if (hGlobalMemory == nullptr) return E_FAIL;
326 FILEGROUPDESCRIPTOR* realbuffer =
327 (FILEGROUPDESCRIPTOR*)GlobalLock(hGlobalMemory);
328 if (!realbuffer) return E_FAIL;
329 FILEDESCRIPTOR* copyloc = (FILEDESCRIPTOR*)((char*)realbuffer + buffersize);
330 memcpy(copyloc, buffer, alloclen);
331 realbuffer->cItems++;
332 GlobalUnlock(hGlobalMemory);
333 GlobalUnlock(workingmedium.hGlobal);
334 ReleaseStgMedium(&workingmedium);
335 buffersize += alloclen;
337 pSTM->tymed = TYMED_HGLOBAL;
338 pSTM->pUnkForRelease = nullptr; // Caller gets to free the data
339 pSTM->hGlobal = hGlobalMemory;
340 return S_OK;
343 HRESULT nsDataObjCollection::GetFileContents(LPFORMATETC pFE,
344 LPSTGMEDIUM pSTM) {
345 ULONG num = 0;
346 ULONG numwanted = (pFE->lindex == -1) ? 0 : pFE->lindex;
347 FORMATETC fEtc = *pFE;
348 fEtc.lindex = -1; // We're lying to the data object so it thinks it's alone
350 // The key for this data type is to figure out which data object the index
351 // corresponds to and then just pass it along
352 for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
353 nsDataObj* dataObj = mDataObjects.ElementAt(i);
354 if (dataObj->QueryGetData(&fEtc) != S_OK) continue;
355 if (num == numwanted) return dataObj->GetData(pFE, pSTM);
356 num++;
358 return DV_E_LINDEX;
361 HRESULT nsDataObjCollection::GetFirstSupporting(LPFORMATETC pFE,
362 LPSTGMEDIUM pSTM) {
363 // There is no way to pass more than one of this, so just find the first data
364 // object that supports it and pass it along
365 for (uint32_t i = 0; i < mDataObjects.Length(); ++i) {
366 if (mDataObjects.ElementAt(i)->QueryGetData(pFE) == S_OK)
367 return mDataObjects.ElementAt(i)->GetData(pFE, pSTM);
369 return DV_E_FORMATETC;