1/*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Baidu Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "DragData.h"
29
30#include "COMPtr.h"
31#include "ClipboardUtilitiesWin.h"
32#include "Frame.h"
33#include "DocumentFragment.h"
34#include "Markup.h"
35#include "Range.h"
36#include "TextEncoding.h"
37#include <objidl.h>
38#include <shlwapi.h>
39#include <wininet.h>
40#include <wtf/Forward.h>
41#include <wtf/Hashmap.h>
42#include <wtf/PassRefPtr.h>
43#include <wtf/RefPtr.h>
44#include <wtf/text/WTFString.h>
45
46namespace WebCore {
47
48DragData::DragData(const DragDataMap& data, const IntPoint& clientPosition, const IntPoint& globalPosition,
49    DragOperation sourceOperationMask, DragApplicationFlags flags)
50    : m_clientPosition(clientPosition)
51    , m_globalPosition(globalPosition)
52    , m_platformDragData(0)
53    , m_draggingSourceOperationMask(sourceOperationMask)
54    , m_applicationFlags(flags)
55    , m_dragDataMap(data)
56{
57}
58
59bool DragData::containsURL(Frame*, FilenameConversionPolicy filenamePolicy) const
60{
61    if (m_platformDragData)
62        return SUCCEEDED(m_platformDragData->QueryGetData(urlWFormat()))
63            || SUCCEEDED(m_platformDragData->QueryGetData(urlFormat()))
64            || (filenamePolicy == ConvertFilenames
65                && (SUCCEEDED(m_platformDragData->QueryGetData(filenameWFormat()))
66                    || SUCCEEDED(m_platformDragData->QueryGetData(filenameFormat()))));
67    return m_dragDataMap.contains(urlWFormat()->cfFormat) || m_dragDataMap.contains(urlFormat()->cfFormat)
68        || (filenamePolicy == ConvertFilenames && (m_dragDataMap.contains(filenameWFormat()->cfFormat) || m_dragDataMap.contains(filenameFormat()->cfFormat)));
69}
70
71const DragDataMap& DragData::dragDataMap()
72{
73    if (!m_dragDataMap.isEmpty() || !m_platformDragData)
74        return m_dragDataMap;
75    // Enumerate clipboard content and load it in the map.
76    COMPtr<IEnumFORMATETC> itr;
77
78    if (FAILED(m_platformDragData->EnumFormatEtc(DATADIR_GET, &itr)) || !itr)
79        return m_dragDataMap;
80
81    FORMATETC dataFormat;
82    while (itr->Next(1, &dataFormat, 0) == S_OK) {
83        Vector<String> dataStrings;
84        getClipboardData(m_platformDragData, &dataFormat, dataStrings);
85        if (!dataStrings.isEmpty())
86            m_dragDataMap.set(dataFormat.cfFormat, dataStrings);
87    }
88    return m_dragDataMap;
89}
90
91void DragData::getDragFileDescriptorData(int& size, String& pathname)
92{
93    size = 0;
94    if (m_platformDragData)
95        getFileDescriptorData(m_platformDragData, size, pathname);
96}
97
98void DragData::getDragFileContentData(int size, void* dataBlob)
99{
100    if (m_platformDragData)
101        getFileContentData(m_platformDragData, size, dataBlob);
102}
103
104String DragData::asURL(Frame*, FilenameConversionPolicy filenamePolicy, String* title) const
105{
106    return (m_platformDragData) ? getURL(m_platformDragData, filenamePolicy, title) : getURL(&m_dragDataMap, filenamePolicy, title);
107}
108
109bool DragData::containsFiles() const
110{
111#if USE(CF)
112    return (m_platformDragData) ? SUCCEEDED(m_platformDragData->QueryGetData(cfHDropFormat())) : m_dragDataMap.contains(cfHDropFormat()->cfFormat);
113#else
114    return false;
115#endif
116}
117
118unsigned DragData::numberOfFiles() const
119{
120#if USE(CF)
121    if (!m_platformDragData)
122        return 0;
123
124    STGMEDIUM medium;
125    if (FAILED(m_platformDragData->GetData(cfHDropFormat(), &medium)))
126        return 0;
127
128    HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
129
130    if (!hdrop)
131        return 0;
132
133    unsigned numFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
134
135    DragFinish(hdrop);
136    GlobalUnlock(medium.hGlobal);
137
138    return numFiles;
139#else
140    return 0;
141#endif
142}
143
144void DragData::asFilenames(Vector<String>& result) const
145{
146#if USE(CF)
147    if (m_platformDragData) {
148        WCHAR filename[MAX_PATH];
149
150        STGMEDIUM medium;
151        if (FAILED(m_platformDragData->GetData(cfHDropFormat(), &medium)))
152            return;
153
154        HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal));
155
156        if (!hdrop)
157            return;
158
159        const unsigned numFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
160        for (unsigned i = 0; i < numFiles; i++) {
161            if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename)))
162                continue;
163            result.append(static_cast<UChar*>(filename));
164        }
165
166        // Free up memory from drag
167        DragFinish(hdrop);
168
169        GlobalUnlock(medium.hGlobal);
170        return;
171    }
172    result = m_dragDataMap.get(cfHDropFormat()->cfFormat);
173#endif
174}
175
176bool DragData::containsPlainText() const
177{
178    if (m_platformDragData)
179        return SUCCEEDED(m_platformDragData->QueryGetData(plainTextWFormat()))
180            || SUCCEEDED(m_platformDragData->QueryGetData(plainTextFormat()));
181    return m_dragDataMap.contains(plainTextWFormat()->cfFormat) || m_dragDataMap.contains(plainTextFormat()->cfFormat);
182}
183
184String DragData::asPlainText(Frame*) const
185{
186    return (m_platformDragData) ? getPlainText(m_platformDragData) : getPlainText(&m_dragDataMap);
187}
188
189bool DragData::containsColor() const
190{
191    return false;
192}
193
194bool DragData::canSmartReplace() const
195{
196    if (m_platformDragData)
197        return SUCCEEDED(m_platformDragData->QueryGetData(smartPasteFormat()));
198    return m_dragDataMap.contains(smartPasteFormat()->cfFormat);
199}
200
201bool DragData::containsCompatibleContent() const
202{
203    return containsPlainText() || containsURL(0)
204        || ((m_platformDragData) ? (containsHTML(m_platformDragData) || containsFilenames(m_platformDragData))
205            : (containsHTML(&m_dragDataMap) || containsFilenames(&m_dragDataMap)))
206        || containsColor();
207}
208
209PassRefPtr<DocumentFragment> DragData::asFragment(Frame* frame, Range&, bool, bool&) const
210{
211    /*
212     * Order is richest format first. On OSX this is:
213     * * Web Archive
214     * * Filenames
215     * * HTML
216     * * RTF
217     * * TIFF
218     * * PICT
219     */
220
221    if (m_platformDragData) {
222        if (containsFilenames(m_platformDragData)) {
223            if (PassRefPtr<DocumentFragment> fragment = fragmentFromFilenames(frame->document(), m_platformDragData))
224                return fragment;
225        }
226
227        if (containsHTML(m_platformDragData)) {
228            if (PassRefPtr<DocumentFragment> fragment = fragmentFromHTML(frame->document(), m_platformDragData))
229                return fragment;
230        }
231    } else {
232        if (containsFilenames(&m_dragDataMap)) {
233            if (PassRefPtr<DocumentFragment> fragment = fragmentFromFilenames(frame->document(), &m_dragDataMap))
234                return fragment;
235        }
236
237        if (containsHTML(&m_dragDataMap)) {
238            if (PassRefPtr<DocumentFragment> fragment = fragmentFromHTML(frame->document(), &m_dragDataMap))
239                return fragment;
240        }
241    }
242    return 0;
243}
244
245Color DragData::asColor() const
246{
247    return Color();
248}
249
250}
251