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/. */
8 #include "nsDataObjCollection.h"
9 #include "nsClipboard.h"
14 // {25589C3E-1FAC-47b9-BF43-CAEA89B79533}
15 const IID IID_IDataObjCollection
= {
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
) {
33 if ((IID_IUnknown
== riid
) || (IID_IDataObject
== riid
)) {
34 *ppv
= static_cast<IDataObject
*>(this);
39 if (IID_IDataObjCollection
== riid
) {
40 *ppv
= static_cast<nsIDataObjCollection
*>(this);
44 // offer to operate asynchronously (required by nsDragService)
45 if (IID_IDataObjectAsyncCapability
== riid
) {
46 *ppv
= static_cast<IDataObjectAsyncCapability
*>(this);
54 STDMETHODIMP_(ULONG
) nsDataObjCollection::AddRef() { return ++m_cRef
; }
56 STDMETHODIMP_(ULONG
) nsDataObjCollection::Release() {
57 if (0 != --m_cRef
) return m_cRef
;
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
) {
75 return GetText(pFE
, pSTM
);
77 return GetFile(pFE
, pSTM
);
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
,
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
) {
103 for (uint32_t i
= 0; i
< mDataObjects
.Length(); ++i
) {
104 IDataObject
* dataObj
= mDataObjects
.ElementAt(i
);
105 if (S_OK
== dataObj
->QueryGetData(pFE
)) {
110 return DV_E_FORMATETC
;
113 STDMETHODIMP
nsDataObjCollection::SetData(LPFORMATETC pFE
, LPSTGMEDIUM pSTM
,
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
,
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
;
129 if (S_OK
!= this->EnumFormatEtc(DATADIR_GET
, &ifEtc
)) return;
130 while (S_OK
== ifEtc
->Next(1, &fEtc
, &num
)) {
133 "Bit off more than we can chew in nsDataObjCollection::AddDataFlavor");
134 if (FormatsMatch(fEtc
, *aFE
)) {
138 } // If we didn't find a matching format, add this one
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
;
153 HGLOBAL hGlobalMemory
;
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
);
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
);
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
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
;
210 HRESULT
nsDataObjCollection::GetText(LPFORMATETC pFE
, LPSTGMEDIUM pSTM
) {
211 STGMEDIUM workingmedium
;
213 HGLOBAL hGlobalMemory
;
215 uint32_t buffersize
= 1;
216 uint32_t alloclen
= 0;
218 hGlobalMemory
= GlobalAlloc(GHND
, buffersize
);
220 if (pFE
->cfFormat
== CF_TEXT
) {
222 for (uint32_t i
= 0; i
< mDataObjects
.Length(); ++i
) {
223 nsDataObj
* dataObj
= mDataObjects
.ElementAt(i
);
224 hr
= dataObj
->GetData(&fe
, &workingmedium
);
233 // Now we need to pull out the text
234 char* buffer
= (char*)GlobalLock(workingmedium
.hGlobal
);
235 if (buffer
== nullptr) return E_FAIL
;
237 GlobalUnlock(workingmedium
.hGlobal
);
238 ReleaseStgMedium(&workingmedium
);
239 // Now put the text into our buffer
240 alloclen
= text
.Length();
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
;
256 if (pFE
->cfFormat
== CF_UNICODETEXT
) {
257 buffersize
= sizeof(char16_t
);
259 for (uint32_t i
= 0; i
< mDataObjects
.Length(); ++i
) {
260 nsDataObj
* dataObj
= mDataObjects
.ElementAt(i
);
261 hr
= dataObj
->GetData(&fe
, &workingmedium
);
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
;
274 GlobalUnlock(workingmedium
.hGlobal
);
275 ReleaseStgMedium(&workingmedium
);
276 // Now put the text into our buffer
277 alloclen
= text
.Length() * sizeof(char16_t
);
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
;
297 HRESULT
nsDataObjCollection::GetFileDescriptors(LPFORMATETC pFE
,
299 STGMEDIUM workingmedium
;
301 HGLOBAL hGlobalMemory
;
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
);
319 // Now we need to pull out the filedescriptor
320 FILEDESCRIPTOR
* buffer
=
321 (FILEDESCRIPTOR
*)((char*)GlobalLock(workingmedium
.hGlobal
) +
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
;
343 HRESULT
nsDataObjCollection::GetFileContents(LPFORMATETC pFE
,
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
);
361 HRESULT
nsDataObjCollection::GetFirstSupporting(LPFORMATETC pFE
,
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
;