Thu, 22 Jan 2015 13:21:57 +0100
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 }