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