1/*
2 * Copyright (C) 2007 Apple Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WCDataObject.h"
28
29#include "ClipboardUtilitiesWin.h"
30#include "DragData.h"
31#include <wtf/text/WTFString.h>
32
33namespace WebCore {
34
35class WCEnumFormatEtc : public IEnumFORMATETC
36{
37public:
38    WCEnumFormatEtc(const Vector<FORMATETC>& formats);
39    WCEnumFormatEtc(const Vector<FORMATETC*>& formats);
40
41    //IUnknown members
42    STDMETHOD(QueryInterface)(REFIID, void**);
43    STDMETHOD_(ULONG, AddRef)(void);
44    STDMETHOD_(ULONG, Release)(void);
45
46    //IEnumFORMATETC members
47    STDMETHOD(Next)(ULONG, LPFORMATETC, ULONG*);
48    STDMETHOD(Skip)(ULONG);
49    STDMETHOD(Reset)(void);
50    STDMETHOD(Clone)(IEnumFORMATETC**);
51
52private:
53    long m_ref;
54    Vector<FORMATETC>  m_formats;
55    size_t m_current;
56};
57
58
59
60WCEnumFormatEtc::WCEnumFormatEtc(const Vector<FORMATETC>& formats)
61: m_ref(1)
62, m_current(0)
63{
64    for(size_t i = 0; i < formats.size(); ++i)
65        m_formats.append(formats[i]);
66}
67
68WCEnumFormatEtc::WCEnumFormatEtc(const Vector<FORMATETC*>& formats)
69: m_ref(1)
70, m_current(0)
71{
72    for(size_t i = 0; i < formats.size(); ++i)
73        m_formats.append(*formats[i]);
74}
75
76STDMETHODIMP  WCEnumFormatEtc::QueryInterface(REFIID riid, void** ppvObject)
77{
78    *ppvObject = 0;
79    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumFORMATETC)) {
80        *ppvObject = this;
81        AddRef();
82        return S_OK;
83    }
84
85    return E_NOINTERFACE;
86}
87
88STDMETHODIMP_(ULONG) WCEnumFormatEtc::AddRef(void)
89{
90    return InterlockedIncrement(&m_ref);
91}
92
93STDMETHODIMP_(ULONG) WCEnumFormatEtc::Release(void)
94{
95    long c = InterlockedDecrement(&m_ref);
96    if (c == 0)
97        delete this;
98    return c;
99}
100
101STDMETHODIMP WCEnumFormatEtc::Next(ULONG celt, LPFORMATETC lpFormatEtc, ULONG* pceltFetched)
102{
103    if(pceltFetched != 0)
104        *pceltFetched=0;
105
106    ULONG cReturn = celt;
107
108    if(celt <= 0 || lpFormatEtc == 0 || m_current >= m_formats.size())
109        return S_FALSE;
110
111    if(pceltFetched == 0 && celt != 1) // pceltFetched can be 0 only for 1 item request
112        return S_FALSE;
113
114    while (m_current < m_formats.size() && cReturn > 0) {
115        *lpFormatEtc++ = m_formats[m_current++];
116        --cReturn;
117    }
118    if (pceltFetched != 0)
119        *pceltFetched = celt - cReturn;
120
121    return (cReturn == 0) ? S_OK : S_FALSE;
122}
123
124STDMETHODIMP WCEnumFormatEtc::Skip(ULONG celt)
125{
126    if((m_current + int(celt)) >= m_formats.size())
127        return S_FALSE;
128    m_current += celt;
129    return S_OK;
130}
131
132STDMETHODIMP WCEnumFormatEtc::Reset(void)
133{
134    m_current = 0;
135    return S_OK;
136}
137
138STDMETHODIMP WCEnumFormatEtc::Clone(IEnumFORMATETC** ppCloneEnumFormatEtc)
139{
140    if(!ppCloneEnumFormatEtc)
141        return E_POINTER;
142
143    WCEnumFormatEtc *newEnum = new WCEnumFormatEtc(m_formats);
144    if(!newEnum)
145        return E_OUTOFMEMORY;
146
147    newEnum->AddRef();
148    newEnum->m_current = m_current;
149    *ppCloneEnumFormatEtc = newEnum;
150    return S_OK;
151}
152
153
154
155//////////////////////////////////////////////////////////////////////////
156
157HRESULT WCDataObject::createInstance(WCDataObject** result)
158{
159    if (!result)
160        return E_POINTER;
161    *result = new WCDataObject();
162    return S_OK;
163}
164
165HRESULT WCDataObject::createInstance(WCDataObject** result, const DragDataMap& dataMap)
166{
167    if (!result)
168        return E_POINTER;
169    *result = new WCDataObject;
170
171    for (DragDataMap::const_iterator it = dataMap.begin(); it != dataMap.end(); ++it)
172        setClipboardData(*result, it->key, it->value);
173    return S_OK;
174}
175
176WCDataObject::WCDataObject()
177: m_ref(1)
178{
179}
180
181WCDataObject::~WCDataObject()
182{
183    for(size_t i = 0; i < m_medium.size(); ++i) {
184        ReleaseStgMedium(m_medium[i]);
185        delete m_medium[i];
186    }
187    WTF::deleteAllValues(m_formats);
188}
189
190STDMETHODIMP WCDataObject::QueryInterface(REFIID riid,void** ppvObject)
191{
192    *ppvObject = 0;
193    if (IsEqualIID(riid, IID_IUnknown) ||
194        IsEqualIID(riid, IID_IDataObject)) {
195        *ppvObject=this;
196    }
197    if (*ppvObject) {
198        AddRef();
199        return S_OK;
200    }
201    return E_NOINTERFACE;
202}
203
204STDMETHODIMP_(ULONG) WCDataObject::AddRef( void)
205{
206    return InterlockedIncrement(&m_ref);
207}
208
209STDMETHODIMP_(ULONG) WCDataObject::Release( void)
210{
211    long c = InterlockedDecrement(&m_ref);
212    if (c == 0)
213        delete this;
214    return c;
215}
216
217STDMETHODIMP WCDataObject::GetData(FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
218{
219    if(!pformatetcIn || !pmedium)
220        return E_POINTER;
221    pmedium->hGlobal = 0;
222
223    for(size_t i=0; i < m_formats.size(); ++i) {
224        if(/*pformatetcIn->tymed & m_formats[i]->tymed &&*/     // tymed can be 0 (TYMED_NULL) - but it can have a medium that contains an pUnkForRelease
225            pformatetcIn->lindex == m_formats[i]->lindex &&
226            pformatetcIn->dwAspect == m_formats[i]->dwAspect &&
227            pformatetcIn->cfFormat == m_formats[i]->cfFormat) {
228            CopyMedium(pmedium, m_medium[i], m_formats[i]);
229            return S_OK;
230        }
231    }
232    return DV_E_FORMATETC;
233}
234
235STDMETHODIMP WCDataObject::GetDataHere(FORMATETC*, STGMEDIUM*)
236{
237    return E_NOTIMPL;
238}
239
240STDMETHODIMP WCDataObject::QueryGetData(FORMATETC* pformatetc)
241{
242    if(!pformatetc)
243        return E_POINTER;
244
245    if (!(DVASPECT_CONTENT & pformatetc->dwAspect))
246        return (DV_E_DVASPECT);
247    HRESULT  hr = DV_E_TYMED;
248    for(size_t i = 0; i < m_formats.size(); ++i) {
249        if(pformatetc->tymed & m_formats[i]->tymed) {
250            if(pformatetc->cfFormat == m_formats[i]->cfFormat)
251                return S_OK;
252            else
253                hr = DV_E_CLIPFORMAT;
254        }
255        else
256            hr = DV_E_TYMED;
257    }
258    return hr;
259}
260
261STDMETHODIMP WCDataObject::GetCanonicalFormatEtc(FORMATETC*, FORMATETC*)
262{
263    return DATA_S_SAMEFORMATETC;
264}
265
266STDMETHODIMP WCDataObject::SetData(FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
267{
268    if(!pformatetc || !pmedium)
269        return E_POINTER;
270
271    FORMATETC* fetc=new FORMATETC;
272    if (!fetc)
273        return E_OUTOFMEMORY;
274
275    STGMEDIUM* pStgMed = new STGMEDIUM;
276
277    if(!pStgMed) {
278        delete fetc;
279        return E_OUTOFMEMORY;
280    }
281
282    ZeroMemory(fetc,sizeof(FORMATETC));
283    ZeroMemory(pStgMed,sizeof(STGMEDIUM));
284
285    *fetc = *pformatetc;
286    m_formats.append(fetc);
287
288    if(fRelease)
289        *pStgMed = *pmedium;
290    else
291        CopyMedium(pStgMed, pmedium, pformatetc);
292    m_medium.append(pStgMed);
293
294    return S_OK;
295}
296
297void WCDataObject::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)
298{
299    switch(pMedSrc->tymed)
300    {
301#if !OS(WINCE)
302    case TYMED_HGLOBAL:
303        pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal,pFmtSrc->cfFormat, 0);
304        break;
305    case TYMED_GDI:
306        pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap,pFmtSrc->cfFormat, 0);
307        break;
308    case TYMED_MFPICT:
309        pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict,pFmtSrc->cfFormat, 0);
310        break;
311    case TYMED_ENHMF:
312        pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile,pFmtSrc->cfFormat, 0);
313        break;
314    case TYMED_FILE:
315        pMedSrc->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName,pFmtSrc->cfFormat, 0);
316        break;
317#endif
318    case TYMED_ISTREAM:
319        pMedDest->pstm = pMedSrc->pstm;
320        pMedSrc->pstm->AddRef();
321        break;
322    case TYMED_ISTORAGE:
323        pMedDest->pstg = pMedSrc->pstg;
324        pMedSrc->pstg->AddRef();
325        break;
326    default:
327        break;
328    }
329    pMedDest->tymed = pMedSrc->tymed;
330    pMedDest->pUnkForRelease = 0;
331    if(pMedSrc->pUnkForRelease != 0) {
332        pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;
333        pMedSrc->pUnkForRelease->AddRef();
334    }
335}
336STDMETHODIMP WCDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
337{
338    if(!ppenumFormatEtc)
339        return E_POINTER;
340
341    *ppenumFormatEtc=0;
342    switch (dwDirection)
343    {
344    case DATADIR_GET:
345        *ppenumFormatEtc= new WCEnumFormatEtc(m_formats);
346        if(!(*ppenumFormatEtc))
347            return E_OUTOFMEMORY;
348        break;
349
350    case DATADIR_SET:
351    default:
352        return E_NOTIMPL;
353        break;
354    }
355
356    return S_OK;
357}
358
359STDMETHODIMP WCDataObject::DAdvise(FORMATETC*, DWORD, IAdviseSink*,DWORD*)
360{
361    return OLE_E_ADVISENOTSUPPORTED;
362}
363
364STDMETHODIMP WCDataObject::DUnadvise(DWORD)
365{
366    return E_NOTIMPL;
367}
368
369HRESULT STDMETHODCALLTYPE WCDataObject::EnumDAdvise(IEnumSTATDATA**)
370{
371    return OLE_E_ADVISENOTSUPPORTED;
372}
373
374void WCDataObject::clearData(CLIPFORMAT format)
375{
376    size_t ptr = 0;
377    while (ptr < m_formats.size()) {
378        if (m_formats[ptr]->cfFormat == format) {
379            FORMATETC* current = m_formats[ptr];
380            m_formats[ptr] = m_formats[m_formats.size() - 1];
381            m_formats[m_formats.size() - 1] = 0;
382            m_formats.removeLast();
383            delete current;
384            STGMEDIUM* medium = m_medium[ptr];
385            m_medium[ptr] = m_medium[m_medium.size() - 1];
386            m_medium[m_medium.size() - 1] = 0;
387            m_medium.removeLast();
388            ReleaseStgMedium(medium);
389            delete medium;
390            continue;
391        }
392        ptr++;
393    }
394}
395
396}
397