1/*
2 * Copyright (C) 2006, 2007, 2013-2014 Apple Inc.  All rights reserved.
3 * Copyright (C) 2013 Xueqing Huang <huangxueqing@baidu.com>
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 "Pasteboard.h"
29
30#include "BitmapInfo.h"
31#include "CachedImage.h"
32#include "ClipboardUtilitiesWin.h"
33#include "Document.h"
34#include "DocumentFragment.h"
35#include "Editor.h"
36#include "Element.h"
37#include "Frame.h"
38#include "HTMLNames.h"
39#include "HTMLParserIdioms.h"
40#include "HWndDC.h"
41#include "HitTestResult.h"
42#include "Image.h"
43#include "URL.h"
44#include "NotImplemented.h"
45#include "Page.h"
46#include "Range.h"
47#include "RenderImage.h"
48#include "SharedBuffer.h"
49#include "TextEncoding.h"
50#include "WebCoreInstanceHandle.h"
51#include "markup.h"
52#include <wtf/WindowsExtras.h>
53#include <wtf/text/CString.h>
54#include <wtf/text/StringView.h>
55#include <wtf/win/GDIObject.h>
56
57namespace WebCore {
58
59// We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
60// see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
61
62static UINT HTMLClipboardFormat = 0;
63static UINT BookmarkClipboardFormat = 0;
64static UINT WebSmartPasteFormat = 0;
65
66static LRESULT CALLBACK PasteboardOwnerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
67{
68    LRESULT lresult = 0;
69
70    switch (message) {
71    case WM_RENDERFORMAT:
72        // This message comes when SetClipboardData was sent a null data handle
73        // and now it's come time to put the data on the clipboard.
74        break;
75    case WM_RENDERALLFORMATS:
76        // This message comes when SetClipboardData was sent a null data handle
77        // and now this application is about to quit, so it must put data on
78        // the clipboard before it exits.
79        break;
80    case WM_DESTROY:
81        break;
82#if !OS(WINCE)
83    case WM_DRAWCLIPBOARD:
84        break;
85    case WM_CHANGECBCHAIN:
86        break;
87#endif
88    default:
89        lresult = DefWindowProc(hWnd, message, wParam, lParam);
90        break;
91    }
92    return lresult;
93}
94
95PassOwnPtr<Pasteboard> Pasteboard::createForCopyAndPaste()
96{
97    OwnPtr<Pasteboard> pasteboard = adoptPtr(new Pasteboard);
98    COMPtr<IDataObject> clipboardData;
99#if !OS(WINCE)
100    if (!SUCCEEDED(OleGetClipboard(&clipboardData)))
101        clipboardData = 0;
102#endif
103    pasteboard->setExternalDataObject(clipboardData.get());
104    return pasteboard.release();
105}
106
107PassOwnPtr<Pasteboard> Pasteboard::createPrivate()
108{
109    // Windows has no "Private pasteboard" concept.
110    return createForCopyAndPaste();
111}
112
113#if ENABLE(DRAG_SUPPORT)
114PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop()
115{
116    COMPtr<WCDataObject> dataObject;
117    WCDataObject::createInstance(&dataObject);
118    return adoptPtr(new Pasteboard(dataObject.get()));
119}
120
121// static
122PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData)
123{
124    if (dragData.platformData())
125        return adoptPtr(new Pasteboard(dragData.platformData()));
126    // FIXME: Should add a const overload of dragDataMap so we don't need a const_cast here.
127    return adoptPtr(new Pasteboard(const_cast<DragData&>(dragData).dragDataMap()));
128}
129#endif
130
131void Pasteboard::finishCreatingPasteboard()
132{
133    WNDCLASS wc;
134    memset(&wc, 0, sizeof(WNDCLASS));
135    wc.lpfnWndProc    = PasteboardOwnerWndProc;
136    wc.hInstance      = WebCore::instanceHandle();
137    wc.lpszClassName  = L"PasteboardOwnerWindowClass";
138    RegisterClass(&wc);
139
140    m_owner = ::CreateWindow(L"PasteboardOwnerWindowClass", L"PasteboardOwnerWindow", 0, 0, 0, 0, 0,
141        HWND_MESSAGE, 0, 0, 0);
142
143    HTMLClipboardFormat = ::RegisterClipboardFormat(L"HTML Format");
144    BookmarkClipboardFormat = ::RegisterClipboardFormat(L"UniformResourceLocatorW");
145    WebSmartPasteFormat = ::RegisterClipboardFormat(L"WebKit Smart Paste Format");
146}
147
148Pasteboard::Pasteboard()
149    : m_dataObject(0)
150    , m_writableDataObject(0)
151{
152    finishCreatingPasteboard();
153}
154
155Pasteboard::Pasteboard(IDataObject* dataObject)
156    : m_dataObject(dataObject)
157    , m_writableDataObject(0)
158{
159    finishCreatingPasteboard();
160}
161
162Pasteboard::Pasteboard(WCDataObject* dataObject)
163    : m_dataObject(dataObject)
164    , m_writableDataObject(dataObject)
165{
166    finishCreatingPasteboard();
167}
168
169Pasteboard::Pasteboard(const DragDataMap& dataMap)
170    : m_dataObject(0)
171    , m_writableDataObject(0)
172    , m_dragDataMap(dataMap)
173{
174    finishCreatingPasteboard();
175}
176
177void Pasteboard::clear()
178{
179    if (::OpenClipboard(m_owner)) {
180        ::EmptyClipboard();
181        ::CloseClipboard();
182    }
183}
184
185enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeTextHTML };
186
187static ClipboardDataType clipboardTypeFromMIMEType(const String& type)
188{
189    String qType = type.stripWhiteSpace().lower();
190
191    // two special cases for IE compatibility
192    if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain;"))
193        return ClipboardDataTypeText;
194    if (qType == "url" || qType == "text/uri-list")
195        return ClipboardDataTypeURL;
196    if (qType == "text/html")
197        return ClipboardDataTypeTextHTML;
198
199    return ClipboardDataTypeNone;
200}
201
202void Pasteboard::clear(const String& type)
203{
204    if (!m_writableDataObject)
205        return;
206
207    ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
208
209    if (dataType == ClipboardDataTypeURL) {
210        m_writableDataObject->clearData(urlWFormat()->cfFormat);
211        m_writableDataObject->clearData(urlFormat()->cfFormat);
212    }
213    if (dataType == ClipboardDataTypeText) {
214        m_writableDataObject->clearData(plainTextFormat()->cfFormat);
215        m_writableDataObject->clearData(plainTextWFormat()->cfFormat);
216    }
217}
218
219bool Pasteboard::hasData()
220{
221    if (!m_dataObject && m_dragDataMap.isEmpty())
222        return false;
223
224    if (m_dataObject) {
225        COMPtr<IEnumFORMATETC> itr;
226        if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
227            return false;
228
229        if (!itr)
230            return false;
231
232        FORMATETC data;
233
234        // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
235        if (itr->Next(1, &data, 0) == S_OK) {
236            // There is at least one item in the IDataObject
237            return true;
238        }
239
240        return false;
241    }
242    return !m_dragDataMap.isEmpty();
243}
244
245static void addMimeTypesForFormat(ListHashSet<String>& results, const FORMATETC& format)
246{
247    // URL and Text are provided for compatibility with IE's model
248    if (format.cfFormat == urlFormat()->cfFormat || format.cfFormat == urlWFormat()->cfFormat) {
249        results.add("URL");
250        results.add("text/uri-list");
251    }
252
253    if (format.cfFormat == plainTextWFormat()->cfFormat || format.cfFormat == plainTextFormat()->cfFormat) {
254        results.add("Text");
255        results.add("text/plain");
256    }
257}
258
259Vector<String> Pasteboard::types()
260{
261    ListHashSet<String> results;
262
263    if (!m_dataObject && m_dragDataMap.isEmpty())
264        return Vector<String>();
265
266    if (m_dataObject) {
267        COMPtr<IEnumFORMATETC> itr;
268
269        if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
270            return Vector<String>();
271
272        if (!itr)
273            return Vector<String>();
274
275        FORMATETC data;
276
277        // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
278        while (itr->Next(1, &data, 0) == S_OK)
279            addMimeTypesForFormat(results, data);
280    } else {
281        for (DragDataMap::const_iterator it = m_dragDataMap.begin(); it != m_dragDataMap.end(); ++it) {
282            FORMATETC data;
283            data.cfFormat = (*it).key;
284            addMimeTypesForFormat(results, data);
285        }
286    }
287
288    Vector<String> vector;
289    copyToVector(results, vector);
290    return vector;
291}
292
293String Pasteboard::readString(const String& type)
294{
295    if (!m_dataObject && m_dragDataMap.isEmpty())
296        return "";
297
298    ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
299    if (dataType == ClipboardDataTypeText)
300        return m_dataObject ? getPlainText(m_dataObject.get()) : getPlainText(&m_dragDataMap);
301    if (dataType == ClipboardDataTypeURL)
302        return m_dataObject ? getURL(m_dataObject.get(), DragData::DoNotConvertFilenames) : getURL(&m_dragDataMap, DragData::DoNotConvertFilenames);
303    if (dataType == ClipboardDataTypeTextHTML) {
304        String data = m_dataObject ? getTextHTML(m_dataObject.get()) : getTextHTML(&m_dragDataMap);
305        if (!data.isEmpty())
306            return data;
307        return m_dataObject ? getCFHTML(m_dataObject.get()) : getCFHTML(&m_dragDataMap);
308    }
309
310    return "";
311}
312
313Vector<String> Pasteboard::readFilenames()
314{
315    Vector<String> fileNames;
316
317#if USE(CF)
318    if (m_dataObject) {
319        STGMEDIUM medium;
320        if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium)))
321            return fileNames;
322
323        HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal));
324        if (!hdrop)
325            return fileNames;
326
327        WCHAR filename[MAX_PATH];
328        UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
329        for (UINT i = 0; i < fileCount; i++) {
330            if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename)))
331                continue;
332            fileNames.append(filename);
333        }
334
335        GlobalUnlock(medium.hGlobal);
336        ReleaseStgMedium(&medium);
337        return fileNames;
338    }
339    if (!m_dragDataMap.contains(cfHDropFormat()->cfFormat))
340        return fileNames;
341    return m_dragDataMap.get(cfHDropFormat()->cfFormat);
342#else
343    notImplemented();
344    return fileNames;
345#endif
346}
347
348static bool writeURL(WCDataObject *data, const URL& url, String title, bool withPlainText, bool withHTML)
349{
350    ASSERT(data);
351
352    if (url.isEmpty())
353        return false;
354
355    if (title.isEmpty()) {
356        title = url.lastPathComponent();
357        if (title.isEmpty())
358            title = url.host();
359    }
360
361    STGMEDIUM medium = {0};
362    medium.tymed = TYMED_HGLOBAL;
363
364    medium.hGlobal = createGlobalData(url, title);
365    bool success = false;
366    if (medium.hGlobal && FAILED(data->SetData(urlWFormat(), &medium, TRUE)))
367        ::GlobalFree(medium.hGlobal);
368    else
369        success = true;
370
371    if (withHTML) {
372        Vector<char> cfhtmlData;
373        markupToCFHTML(urlToMarkup(url, title), "", cfhtmlData);
374        medium.hGlobal = createGlobalData(cfhtmlData);
375        if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE)))
376            ::GlobalFree(medium.hGlobal);
377        else
378            success = true;
379    }
380
381    if (withPlainText) {
382        medium.hGlobal = createGlobalData(url.string());
383        if (medium.hGlobal && FAILED(data->SetData(plainTextWFormat(), &medium, TRUE)))
384            ::GlobalFree(medium.hGlobal);
385        else
386            success = true;
387    }
388
389    return success;
390}
391
392void Pasteboard::writeString(const String& type, const String& data)
393{
394    if (!m_writableDataObject)
395        return;
396
397    ClipboardDataType winType = clipboardTypeFromMIMEType(type);
398
399    if (winType == ClipboardDataTypeURL) {
400        WebCore::writeURL(m_writableDataObject.get(), URL(URL(), data), String(), false, true);
401        return;
402    }
403
404    if (winType == ClipboardDataTypeText) {
405        STGMEDIUM medium = {0};
406        medium.tymed = TYMED_HGLOBAL;
407        medium.hGlobal = createGlobalData(data);
408        if (!medium.hGlobal)
409            return;
410
411        if (FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
412            ::GlobalFree(medium.hGlobal);
413    }
414}
415
416#if ENABLE(DRAG_SUPPORT)
417void Pasteboard::setDragImage(DragImageRef, const IntPoint&)
418{
419    // Do nothing in Windows.
420}
421#endif
422
423void Pasteboard::writeRangeToDataObject(Range& selectedRange, Frame& frame)
424{
425    if (!m_writableDataObject)
426        return;
427
428    STGMEDIUM medium = {0};
429    medium.tymed = TYMED_HGLOBAL;
430
431    Vector<char> data;
432    markupToCFHTML(createMarkup(selectedRange, 0, AnnotateForInterchange),
433        selectedRange.startContainer()->document().url().string(), data);
434    medium.hGlobal = createGlobalData(data);
435    if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
436        ::GlobalFree(medium.hGlobal);
437
438    String str = frame.editor().selectedTextForDataTransfer();
439    replaceNewlinesWithWindowsStyleNewlines(str);
440    replaceNBSPWithSpace(str);
441    medium.hGlobal = createGlobalData(str);
442    if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
443        ::GlobalFree(medium.hGlobal);
444
445    medium.hGlobal = 0;
446    if (frame.editor().canSmartCopyOrDelete())
447        m_writableDataObject->SetData(smartPasteFormat(), &medium, TRUE);
448}
449
450void Pasteboard::writeSelection(Range& selectedRange, bool canSmartCopyOrDelete, Frame& frame, ShouldSerializeSelectedTextForDataTransfer shouldSerializeSelectedTextForDataTransfer)
451{
452    clear();
453
454    // Put CF_HTML format on the pasteboard
455    if (::OpenClipboard(m_owner)) {
456        Vector<char> data;
457        markupToCFHTML(createMarkup(selectedRange, 0, AnnotateForInterchange),
458            selectedRange.startContainer()->document().url().string(), data);
459        HGLOBAL cbData = createGlobalData(data);
460        if (!::SetClipboardData(HTMLClipboardFormat, cbData))
461            ::GlobalFree(cbData);
462        ::CloseClipboard();
463    }
464
465    // Put plain string on the pasteboard. CF_UNICODETEXT covers CF_TEXT as well
466    String str = shouldSerializeSelectedTextForDataTransfer == IncludeImageAltTextForDataTransfer ? frame.editor().selectedTextForDataTransfer() : frame.editor().selectedText();
467    replaceNewlinesWithWindowsStyleNewlines(str);
468    replaceNBSPWithSpace(str);
469    if (::OpenClipboard(m_owner)) {
470        HGLOBAL cbData = createGlobalData(str);
471        if (!::SetClipboardData(CF_UNICODETEXT, cbData))
472            ::GlobalFree(cbData);
473        ::CloseClipboard();
474    }
475
476    // enable smart-replacing later on by putting dummy data on the pasteboard
477    if (canSmartCopyOrDelete) {
478        if (::OpenClipboard(m_owner)) {
479            ::SetClipboardData(WebSmartPasteFormat, 0);
480            ::CloseClipboard();
481        }
482    }
483
484    writeRangeToDataObject(selectedRange, frame);
485}
486
487void Pasteboard::writePlainTextToDataObject(const String& text, SmartReplaceOption smartReplaceOption)
488{
489    if (!m_writableDataObject)
490        return;
491
492    STGMEDIUM medium = {0};
493    medium.tymed = TYMED_HGLOBAL;
494
495    String str = text;
496    replaceNewlinesWithWindowsStyleNewlines(str);
497    replaceNBSPWithSpace(str);
498    medium.hGlobal = createGlobalData(str);
499    if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
500        ::GlobalFree(medium.hGlobal);
501}
502
503void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption)
504{
505    clear();
506
507    // Put plain string on the pasteboard. CF_UNICODETEXT covers CF_TEXT as well
508    String str = text;
509    replaceNewlinesWithWindowsStyleNewlines(str);
510    if (::OpenClipboard(m_owner)) {
511        HGLOBAL cbData = createGlobalData(str);
512        if (!::SetClipboardData(CF_UNICODETEXT, cbData))
513            ::GlobalFree(cbData);
514        ::CloseClipboard();
515    }
516
517    // enable smart-replacing later on by putting dummy data on the pasteboard
518    if (smartReplaceOption == CanSmartReplace) {
519        if (::OpenClipboard(m_owner)) {
520            ::SetClipboardData(WebSmartPasteFormat, 0);
521            ::CloseClipboard();
522        }
523    }
524
525    writePlainTextToDataObject(text, smartReplaceOption);
526}
527
528#if !OS(WINCE)
529static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length)
530{
531    size_t writeTo = 0;
532    size_t readFrom = 0;
533    while (readFrom < length) {
534        UINT type = PathGetCharType(psz[readFrom]);
535        if (!psz[readFrom] || type & (GCT_LFNCHAR | GCT_SHORTCHAR))
536            psz[writeTo++] = psz[readFrom];
537
538        readFrom++;
539    }
540    psz[writeTo] = 0;
541}
542#endif
543
544static String filesystemPathFromUrlOrTitle(const String& url, const String& title, const UChar* extension, bool isLink)
545{
546#if OS(WINCE)
547    notImplemented();
548    return String();
549#else
550    static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1;
551    bool usedURL = false;
552    WCHAR fsPathBuffer[MAX_PATH];
553    fsPathBuffer[0] = 0;
554    int extensionLen = extension ? lstrlen(extension) : 0;
555    int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen;
556
557    if (!title.isEmpty()) {
558        size_t len = std::min<size_t>(title.length(), fsPathMaxLengthExcludingExtension);
559        StringView(title).substring(0, len).getCharactersWithUpconvert(fsPathBuffer);
560        fsPathBuffer[len] = 0;
561        pathRemoveBadFSCharacters(fsPathBuffer, len);
562    }
563
564    if (!lstrlen(fsPathBuffer)) {
565        URL kurl(URL(), url);
566        usedURL = true;
567        // The filename for any content based drag or file url should be the last element of
568        // the path. If we can't find it, or we're coming up with the name for a link
569        // we just use the entire url.
570        DWORD len = fsPathMaxLengthExcludingExtension;
571        String lastComponent = kurl.lastPathComponent();
572        if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) {
573            len = std::min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length());
574            StringView(lastComponent).substring(0, len).getCharactersWithUpconvert(fsPathBuffer);
575        } else {
576            len = std::min<DWORD>(fsPathMaxLengthExcludingExtension, url.length());
577            StringView(url).substring(0, len).getCharactersWithUpconvert(fsPathBuffer);
578        }
579        fsPathBuffer[len] = 0;
580        pathRemoveBadFSCharacters(fsPathBuffer, len);
581    }
582
583    if (!extension)
584        return String(static_cast<UChar*>(fsPathBuffer));
585
586    if (!isLink && usedURL) {
587        PathRenameExtension(fsPathBuffer, extension);
588        return String(static_cast<UChar*>(fsPathBuffer));
589    }
590
591    return makeString(static_cast<const UChar*>(fsPathBuffer), extension);
592#endif
593}
594
595// writeFileToDataObject takes ownership of fileDescriptor and fileContent
596static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescriptor, HGLOBAL fileContent, HGLOBAL hDropContent)
597{
598    HRESULT hr = S_OK;
599    FORMATETC* fe;
600    STGMEDIUM medium = {0};
601    medium.tymed = TYMED_HGLOBAL;
602
603    if (!fileDescriptor || !fileContent)
604        goto exit;
605
606    // Descriptor
607    fe = fileDescriptorFormat();
608
609    medium.hGlobal = fileDescriptor;
610
611    if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
612        goto exit;
613
614    // Contents
615    fe = fileContentFormatZero();
616    medium.hGlobal = fileContent;
617    if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
618        goto exit;
619
620#if USE(CF)
621    // HDROP
622    if (hDropContent) {
623        medium.hGlobal = hDropContent;
624        hr = dataObject->SetData(cfHDropFormat(), &medium, TRUE);
625    }
626#endif
627
628exit:
629    if (FAILED(hr)) {
630        if (fileDescriptor)
631            GlobalFree(fileDescriptor);
632        if (fileContent)
633            GlobalFree(fileContent);
634        if (hDropContent)
635            GlobalFree(hDropContent);
636    }
637    return hr;
638}
639
640void Pasteboard::writeURLToDataObject(const URL& kurl, const String& titleStr)
641{
642    if (!m_writableDataObject)
643        return;
644    WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true);
645
646    String url = kurl.string();
647    ASSERT(url.containsOnlyASCII()); // URL::string() is URL encoded.
648
649    String fsPath = filesystemPathFromUrlOrTitle(url, titleStr, L".URL", true);
650    String contentString("[InternetShortcut]\r\nURL=" + url + "\r\n");
651    CString content = contentString.latin1();
652
653    if (fsPath.length() <= 0)
654        return;
655
656    HGLOBAL urlFileDescriptor = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
657    if (!urlFileDescriptor)
658        return;
659
660    HGLOBAL urlFileContent = GlobalAlloc(GPTR, content.length());
661    if (!urlFileContent) {
662        GlobalFree(urlFileDescriptor);
663        return;
664    }
665
666    FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(urlFileDescriptor));
667    if (!fgd) {
668        GlobalFree(urlFileDescriptor);
669        return;
670    }
671
672    ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR));
673    fgd->cItems = 1;
674    fgd->fgd[0].dwFlags = FD_FILESIZE;
675    fgd->fgd[0].nFileSizeLow = content.length();
676
677    unsigned maxSize = std::min<unsigned>(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
678    StringView(fsPath).substring(0, maxSize).getCharactersWithUpconvert(fgd->fgd[0].cFileName);
679    GlobalUnlock(urlFileDescriptor);
680
681    char* fileContents = static_cast<char*>(GlobalLock(urlFileContent));
682    if (!fileContents) {
683        GlobalFree(urlFileDescriptor);
684        return;
685    }
686
687    CopyMemory(fileContents, content.data(), content.length());
688    GlobalUnlock(urlFileContent);
689
690    writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0);
691}
692
693void Pasteboard::write(const PasteboardURL& pasteboardURL)
694{
695    ASSERT(!pasteboardURL.url.isEmpty());
696
697    clear();
698
699    String title(pasteboardURL.title);
700    if (title.isEmpty()) {
701        title = pasteboardURL.url.lastPathComponent();
702        if (title.isEmpty())
703            title = pasteboardURL.url.host();
704    }
705
706    // write to clipboard in format com.apple.safari.bookmarkdata to be able to paste into the bookmarks view with appropriate title
707    if (::OpenClipboard(m_owner)) {
708        HGLOBAL cbData = createGlobalData(pasteboardURL.url, title);
709        if (!::SetClipboardData(BookmarkClipboardFormat, cbData))
710            ::GlobalFree(cbData);
711        ::CloseClipboard();
712    }
713
714    // write to clipboard in format CF_HTML to be able to paste into contenteditable areas as a link
715    if (::OpenClipboard(m_owner)) {
716        Vector<char> data;
717        markupToCFHTML(urlToMarkup(pasteboardURL.url, title), "", data);
718        HGLOBAL cbData = createGlobalData(data);
719        if (!::SetClipboardData(HTMLClipboardFormat, cbData))
720            ::GlobalFree(cbData);
721        ::CloseClipboard();
722    }
723
724    // bare-bones CF_UNICODETEXT support
725    if (::OpenClipboard(m_owner)) {
726        HGLOBAL cbData = createGlobalData(pasteboardURL.url.string());
727        if (!::SetClipboardData(CF_UNICODETEXT, cbData))
728            ::GlobalFree(cbData);
729        ::CloseClipboard();
730    }
731
732    writeURLToDataObject(pasteboardURL.url, pasteboardURL.title);
733}
734
735void Pasteboard::writeImage(Element& element, const URL&, const String&)
736{
737    if (!(element.renderer() && element.renderer()->isRenderImage()))
738        return;
739
740    RenderImage* renderer = toRenderImage(element.renderer());
741    CachedImage* cachedImage = renderer->cachedImage();
742    if (!cachedImage || cachedImage->errorOccurred())
743        return;
744    Image* image = cachedImage->imageForRenderer(renderer);
745    ASSERT(image);
746
747    clear();
748
749    HWndDC dc(0);
750    auto compatibleDC = adoptGDIObject(::CreateCompatibleDC(0));
751    auto sourceDC = adoptGDIObject(::CreateCompatibleDC(0));
752    auto resultBitmap = adoptGDIObject(::CreateCompatibleBitmap(dc, image->width(), image->height()));
753    HGDIOBJ oldBitmap = ::SelectObject(compatibleDC.get(), resultBitmap.get());
754
755    BitmapInfo bmInfo = BitmapInfo::create(IntSize(image->size()));
756
757    auto coreBitmap = adoptGDIObject(::CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0));
758    HGDIOBJ oldSource = ::SelectObject(sourceDC.get(), coreBitmap.get());
759    image->getHBITMAP(coreBitmap.get());
760
761    ::BitBlt(compatibleDC.get(), 0, 0, image->width(), image->height(), sourceDC.get(), 0, 0, SRCCOPY);
762
763    ::SelectObject(sourceDC.get(), oldSource);
764    ::SelectObject(compatibleDC.get(), oldBitmap);
765
766    if (::OpenClipboard(m_owner)) {
767        ::SetClipboardData(CF_BITMAP, resultBitmap.leak());
768        ::CloseClipboard();
769    }
770}
771
772void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard)
773{
774    notImplemented();
775}
776
777bool Pasteboard::canSmartReplace()
778{
779    return ::IsClipboardFormatAvailable(WebSmartPasteFormat);
780}
781
782void Pasteboard::read(PasteboardPlainText& text)
783{
784    if (::IsClipboardFormatAvailable(CF_UNICODETEXT) && ::OpenClipboard(m_owner)) {
785        if (HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT)) {
786            text.text = static_cast<UChar*>(GlobalLock(cbData));
787            GlobalUnlock(cbData);
788            ::CloseClipboard();
789            return;
790        }
791        ::CloseClipboard();
792    }
793
794    if (::IsClipboardFormatAvailable(CF_TEXT) && ::OpenClipboard(m_owner)) {
795        if (HANDLE cbData = ::GetClipboardData(CF_TEXT)) {
796            // FIXME: This treats the characters as Latin-1, not UTF-8 or even Windows Latin-1. Is that the right encoding?
797            text.text = static_cast<char*>(GlobalLock(cbData));
798            GlobalUnlock(cbData);
799            ::CloseClipboard();
800            return;
801        }
802        ::CloseClipboard();
803    }
804}
805
806PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText)
807{
808    chosePlainText = false;
809
810    if (::IsClipboardFormatAvailable(HTMLClipboardFormat) && ::OpenClipboard(m_owner)) {
811        // get data off of clipboard
812        HANDLE cbData = ::GetClipboardData(HTMLClipboardFormat);
813        if (cbData) {
814            SIZE_T dataSize = ::GlobalSize(cbData);
815            String cfhtml(UTF8Encoding().decode(static_cast<char*>(GlobalLock(cbData)), dataSize));
816            GlobalUnlock(cbData);
817            ::CloseClipboard();
818
819            RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(frame.document(), cfhtml);
820            if (fragment)
821                return fragment.release();
822        } else
823            ::CloseClipboard();
824    }
825
826    if (allowPlainText && ::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
827        chosePlainText = true;
828        if (::OpenClipboard(m_owner)) {
829            HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT);
830            if (cbData) {
831                UChar* buffer = static_cast<UChar*>(GlobalLock(cbData));
832                String str(buffer);
833                GlobalUnlock(cbData);
834                ::CloseClipboard();
835                RefPtr<DocumentFragment> fragment = createFragmentFromText(context, str);
836                if (fragment)
837                    return fragment.release();
838            } else
839                ::CloseClipboard();
840        }
841    }
842
843    if (allowPlainText && ::IsClipboardFormatAvailable(CF_TEXT)) {
844        chosePlainText = true;
845        if (::OpenClipboard(m_owner)) {
846            HANDLE cbData = ::GetClipboardData(CF_TEXT);
847            if (cbData) {
848                char* buffer = static_cast<char*>(GlobalLock(cbData));
849                String str(buffer);
850                GlobalUnlock(cbData);
851                ::CloseClipboard();
852                RefPtr<DocumentFragment> fragment = createFragmentFromText(context, str);
853                if (fragment)
854                    return fragment.release();
855            } else
856                ::CloseClipboard();
857        }
858    }
859
860    return 0;
861}
862
863void Pasteboard::setExternalDataObject(IDataObject *dataObject)
864{
865    m_writableDataObject = 0;
866    m_dataObject = dataObject;
867}
868
869static CachedImage* getCachedImage(Element& element)
870{
871    // Attempt to pull CachedImage from element
872    RenderObject* renderer = element.renderer();
873    if (!renderer || !renderer->isRenderImage())
874        return 0;
875
876    RenderImage* image = toRenderImage(renderer);
877    if (image->cachedImage() && !image->cachedImage()->errorOccurred())
878        return image->cachedImage();
879
880    return 0;
881}
882
883static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image)
884{
885    ASSERT_ARG(image, image);
886    ASSERT(image->image()->data());
887
888    HRESULT hr = S_OK;
889    String fsPath;
890    HGLOBAL memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
891    if (!memObj)
892        return 0;
893
894    FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
895    if (!fgd) {
896        GlobalFree(memObj);
897        return 0;
898    }
899
900    memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
901    fgd->cItems = 1;
902    fgd->fgd[0].dwFlags = FD_FILESIZE;
903    fgd->fgd[0].nFileSizeLow = image->image()->data()->size();
904
905    const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title;
906    String extension = image->image()->filenameExtension();
907    if (extension.isEmpty()) {
908        // Do not continue processing in the rare and unusual case where a decoded image is not able
909        // to provide a filename extension. Something tricky (like a bait-n-switch) is going on
910        GlobalUnlock(memObj);
911        GlobalFree(memObj);
912        return 0;
913    }
914    extension.insert(".", 0);
915    fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.charactersWithNullTermination().data(), false);
916
917    if (fsPath.length() <= 0) {
918        GlobalUnlock(memObj);
919        GlobalFree(memObj);
920        return 0;
921    }
922
923    int maxSize = std::min<int>(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
924    StringView(fsPath).substring(0, maxSize).getCharactersWithUpconvert(fgd->fgd[0].cFileName);
925    GlobalUnlock(memObj);
926
927    return memObj;
928}
929
930static HGLOBAL createGlobalImageFileContent(SharedBuffer* data)
931{
932    HGLOBAL memObj = GlobalAlloc(GPTR, data->size());
933    if (!memObj)
934        return 0;
935
936    char* fileContents = (PSTR)GlobalLock(memObj);
937    if (!fileContents) {
938        GlobalFree(memObj);
939        return 0;
940    }
941
942    if (data->data())
943        CopyMemory(fileContents, data->data(), data->size());
944
945    GlobalUnlock(memObj);
946
947    return memObj;
948}
949
950static HGLOBAL createGlobalHDropContent(const URL& url, String& fileName, SharedBuffer* data)
951{
952    if (fileName.isEmpty() || !data)
953        return 0;
954
955    WCHAR filePath[MAX_PATH];
956
957    if (url.isLocalFile()) {
958        String localPath = decodeURLEscapeSequences(url.path());
959        // windows does not enjoy a leading slash on paths
960        if (localPath[0] == '/')
961            localPath = localPath.substring(1);
962        const Vector<UChar>& localPathWide = localPath.charactersWithNullTermination();
963        LPCWSTR localPathStr = localPathWide.data();
964        if (localPathStr && wcslen(localPathStr) + 1 < MAX_PATH)
965            wcscpy_s(filePath, MAX_PATH, localPathStr);
966        else
967            return 0;
968    } else {
969#if OS(WINCE)
970        notImplemented();
971        return 0;
972#else
973        WCHAR tempPath[MAX_PATH];
974        WCHAR extension[MAX_PATH];
975        if (!::GetTempPath(WTF_ARRAY_LENGTH(tempPath), tempPath))
976            return 0;
977        if (!::PathAppend(tempPath, fileName.charactersWithNullTermination().data()))
978            return 0;
979        LPCWSTR foundExtension = ::PathFindExtension(tempPath);
980        if (foundExtension) {
981            if (wcscpy_s(extension, MAX_PATH, foundExtension))
982                return 0;
983        } else
984            *extension = 0;
985        ::PathRemoveExtension(tempPath);
986        for (int i = 1; i < 10000; i++) {
987            if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1)
988                return 0;
989            if (!::PathFileExists(filePath))
990                break;
991        }
992        HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
993        if (tempFileHandle == INVALID_HANDLE_VALUE)
994            return 0;
995
996        // Write the data to this temp file.
997        DWORD written;
998        BOOL tempWriteSucceeded = FALSE;
999        if (data->data())
1000            tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0);
1001        CloseHandle(tempFileHandle);
1002        if (!tempWriteSucceeded)
1003            return 0;
1004#endif
1005    }
1006
1007    SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2));
1008    HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
1009    if (!memObj)
1010        return 0;
1011
1012    DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj);
1013    if (!dropFiles) {
1014        GlobalFree(memObj);
1015        return 0;
1016    }
1017
1018    dropFiles->pFiles = sizeof(DROPFILES);
1019    dropFiles->fWide = TRUE;
1020    wcscpy((LPWSTR)(dropFiles + 1), filePath);
1021    GlobalUnlock(memObj);
1022
1023    return memObj;
1024}
1025
1026void Pasteboard::writeImageToDataObject(Element& element, const URL& url)
1027{
1028    // Shove image data into a DataObject for use as a file
1029    CachedImage* cachedImage = getCachedImage(element);
1030    if (!cachedImage || !cachedImage->imageForRenderer(element.renderer()) || !cachedImage->isLoaded())
1031        return;
1032
1033    SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element.renderer())->data();
1034    if (!imageBuffer || !imageBuffer->size())
1035        return;
1036
1037    HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element.getAttribute(HTMLNames::altAttr), cachedImage);
1038    if (!imageFileDescriptor)
1039        return;
1040
1041    HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer);
1042    if (!imageFileContent) {
1043        GlobalFree(imageFileDescriptor);
1044        return;
1045    }
1046
1047    String fileName = cachedImage->response().suggestedFilename();
1048    HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer);
1049    if (!hDropContent) {
1050        GlobalFree(imageFileDescriptor);
1051        GlobalFree(imageFileContent);
1052        return;
1053    }
1054
1055    writeFileToDataObject(m_writableDataObject.get(), imageFileDescriptor, imageFileContent, hDropContent);
1056}
1057
1058void Pasteboard::writeURLToWritableDataObject(const URL& url, const String& title)
1059{
1060    WebCore::writeURL(m_writableDataObject.get(), url, title, true, false);
1061}
1062
1063void Pasteboard::writeMarkup(const String& markup)
1064{
1065    Vector<char> data;
1066    markupToCFHTML(markup, "", data);
1067
1068    STGMEDIUM medium = {0};
1069    medium.tymed = TYMED_HGLOBAL;
1070
1071    medium.hGlobal = createGlobalData(data);
1072    if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
1073        GlobalFree(medium.hGlobal);
1074}
1075
1076} // namespace WebCore
1077