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