a5fd930ae91b917b9cba0864bba5534ecb2c1e13
[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(nullTerminatedWCharToString(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 UChar* extension, bool isLink)
559 {
560     static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1;
561     bool usedURL = false;
562     WCHAR fsPathBuffer[MAX_PATH];
563     fsPathBuffer[0] = 0;
564     int extensionLen = extension ? lstrlen(extension) : 0;
565     int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen;
566
567     if (!title.isEmpty()) {
568         size_t len = std::min<size_t>(title.length(), fsPathMaxLengthExcludingExtension);
569         StringView(title).substring(0, len).getCharactersWithUpconvert(fsPathBuffer);
570         fsPathBuffer[len] = 0;
571         pathRemoveBadFSCharacters(fsPathBuffer, len);
572     }
573
574     if (!lstrlen(fsPathBuffer)) {
575         URL kurl(URL(), url);
576         usedURL = true;
577         // The filename for any content based drag or file url should be the last element of 
578         // the path. If we can't find it, or we're coming up with the name for a link
579         // we just use the entire url.
580         DWORD len = fsPathMaxLengthExcludingExtension;
581         String lastComponent = kurl.lastPathComponent();
582         if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) {
583             len = std::min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length());
584             StringView(lastComponent).substring(0, len).getCharactersWithUpconvert(fsPathBuffer);
585         } else {
586             len = std::min<DWORD>(fsPathMaxLengthExcludingExtension, url.length());
587             StringView(url).substring(0, len).getCharactersWithUpconvert(fsPathBuffer);
588         }
589         fsPathBuffer[len] = 0;
590         pathRemoveBadFSCharacters(fsPathBuffer, len);
591     }
592
593     if (!extension)
594         return String(static_cast<UChar*>(fsPathBuffer));
595
596     if (!isLink && usedURL) {
597         PathRenameExtension(fsPathBuffer, extension);
598         return String(static_cast<UChar*>(fsPathBuffer));
599     }
600
601     return makeString(static_cast<const UChar*>(fsPathBuffer), extension);
602 }
603
604 // writeFileToDataObject takes ownership of fileDescriptor and fileContent
605 static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescriptor, HGLOBAL fileContent, HGLOBAL hDropContent)
606 {
607     HRESULT hr = S_OK;
608     FORMATETC* fe;
609     STGMEDIUM medium { };
610     medium.tymed = TYMED_HGLOBAL;
611
612     if (!fileDescriptor || !fileContent)
613         goto exit;
614
615     // Descriptor
616     fe = fileDescriptorFormat();
617
618     medium.hGlobal = fileDescriptor;
619
620     if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
621         goto exit;
622
623     // Contents
624     fe = fileContentFormatZero();
625     medium.hGlobal = fileContent;
626     if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
627         goto exit;
628
629 #if USE(CF)
630     // HDROP
631     if (hDropContent) {
632         medium.hGlobal = hDropContent;
633         hr = dataObject->SetData(cfHDropFormat(), &medium, TRUE);
634     }
635 #endif
636
637 exit:
638     if (FAILED(hr)) {
639         if (fileDescriptor)
640             GlobalFree(fileDescriptor);
641         if (fileContent)
642             GlobalFree(fileContent);
643         if (hDropContent)
644             GlobalFree(hDropContent);
645     }
646     return hr;
647 }
648
649 void Pasteboard::writeURLToDataObject(const URL& kurl, const String& titleStr)
650 {
651     if (!m_writableDataObject)
652         return;
653     WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true);
654
655     String url = kurl.string();
656     ASSERT(url.isAllASCII()); // URL::string() is URL encoded.
657
658     String fsPath = filesystemPathFromUrlOrTitle(url, titleStr, L".URL", true);
659     String contentString("[InternetShortcut]\r\nURL=" + url + "\r\n");
660     CString content = contentString.latin1();
661
662     if (fsPath.length() <= 0)
663         return;
664
665     HGLOBAL urlFileDescriptor = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
666     if (!urlFileDescriptor)
667         return;
668
669     HGLOBAL urlFileContent = GlobalAlloc(GPTR, content.length());
670     if (!urlFileContent) {
671         GlobalFree(urlFileDescriptor);
672         return;
673     }
674
675     FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(urlFileDescriptor));
676     if (!fgd) {
677         GlobalFree(urlFileDescriptor);
678         return;
679     }
680
681     ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR));
682     fgd->cItems = 1;
683     fgd->fgd[0].dwFlags = FD_FILESIZE;
684     fgd->fgd[0].nFileSizeLow = content.length();
685
686     unsigned maxSize = std::min<unsigned>(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
687     StringView(fsPath).substring(0, maxSize).getCharactersWithUpconvert(fgd->fgd[0].cFileName);
688     GlobalUnlock(urlFileDescriptor);
689
690     char* fileContents = static_cast<char*>(GlobalLock(urlFileContent));
691     if (!fileContents) {
692         GlobalFree(urlFileDescriptor);
693         return;
694     }
695
696     CopyMemory(fileContents, content.data(), content.length());
697     GlobalUnlock(urlFileContent);
698
699     writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0);
700 }
701
702 void Pasteboard::write(const PasteboardURL& pasteboardURL)
703 {
704     ASSERT(!pasteboardURL.url.isEmpty());
705
706     clear();
707
708     String title(pasteboardURL.title);
709     if (title.isEmpty()) {
710         title = pasteboardURL.url.lastPathComponent();
711         if (title.isEmpty())
712             title = pasteboardURL.url.host().toString();
713     }
714
715     // write to clipboard in format com.apple.safari.bookmarkdata to be able to paste into the bookmarks view with appropriate title
716     if (::OpenClipboard(m_owner)) {
717         HGLOBAL cbData = createGlobalData(pasteboardURL.url, title);
718         if (!::SetClipboardData(BookmarkClipboardFormat, cbData))
719             ::GlobalFree(cbData);
720         ::CloseClipboard();
721     }
722
723     // write to clipboard in format CF_HTML to be able to paste into contenteditable areas as a link
724     if (::OpenClipboard(m_owner)) {
725         Vector<char> data;
726         markupToCFHTML(urlToMarkup(pasteboardURL.url, title), "", data);
727         HGLOBAL cbData = createGlobalData(data);
728         if (!::SetClipboardData(HTMLClipboardFormat, cbData))
729             ::GlobalFree(cbData);
730         ::CloseClipboard();
731     }
732
733     // bare-bones CF_UNICODETEXT support
734     if (::OpenClipboard(m_owner)) {
735         HGLOBAL cbData = createGlobalData(pasteboardURL.url.string());
736         if (!::SetClipboardData(CF_UNICODETEXT, cbData))
737             ::GlobalFree(cbData);
738         ::CloseClipboard();
739     }
740
741     writeURLToDataObject(pasteboardURL.url, pasteboardURL.title);
742 }
743
744 void Pasteboard::writeTrustworthyWebURLsPboardType(const PasteboardURL&)
745 {
746     notImplemented();
747 }
748
749 void Pasteboard::writeImage(Element& element, const URL&, const String&)
750 {
751     if (!is<RenderImage>(element.renderer()))
752         return;
753
754     auto& renderer = downcast<RenderImage>(*element.renderer());
755     CachedImage* cachedImage = renderer.cachedImage();
756     if (!cachedImage || cachedImage->errorOccurred())
757         return;
758     Image* image = cachedImage->imageForRenderer(&renderer);
759     ASSERT(image);
760
761     clear();
762
763     HWndDC dc(0);
764     auto compatibleDC = adoptGDIObject(::CreateCompatibleDC(0));
765     auto sourceDC = adoptGDIObject(::CreateCompatibleDC(0));
766     auto resultBitmap = adoptGDIObject(::CreateCompatibleBitmap(dc, image->width(), image->height()));
767     HGDIOBJ oldBitmap = ::SelectObject(compatibleDC.get(), resultBitmap.get());
768
769     BitmapInfo bmInfo = BitmapInfo::create(IntSize(image->size()));
770
771     auto coreBitmap = adoptGDIObject(::CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0));
772     HGDIOBJ oldSource = ::SelectObject(sourceDC.get(), coreBitmap.get());
773     image->getHBITMAP(coreBitmap.get());
774
775     ::BitBlt(compatibleDC.get(), 0, 0, image->width(), image->height(), sourceDC.get(), 0, 0, SRCCOPY);
776
777     ::SelectObject(sourceDC.get(), oldSource);
778     ::SelectObject(compatibleDC.get(), oldBitmap);
779
780     if (::OpenClipboard(m_owner)) {
781         ::SetClipboardData(CF_BITMAP, resultBitmap.leak());
782         ::CloseClipboard();
783     }
784 }
785
786 bool Pasteboard::canSmartReplace()
787
788     return ::IsClipboardFormatAvailable(WebSmartPasteFormat);
789 }
790
791 void Pasteboard::read(PasteboardPlainText& text)
792 {
793     if (::IsClipboardFormatAvailable(CF_UNICODETEXT) && ::OpenClipboard(m_owner)) {
794         if (HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT)) {
795             text.text = static_cast<UChar*>(GlobalLock(cbData));
796             GlobalUnlock(cbData);
797             ::CloseClipboard();
798             return;
799         }
800         ::CloseClipboard();
801     }
802
803     if (::IsClipboardFormatAvailable(CF_TEXT) && ::OpenClipboard(m_owner)) {
804         if (HANDLE cbData = ::GetClipboardData(CF_TEXT)) {
805             // FIXME: This treats the characters as Latin-1, not UTF-8 or even Windows Latin-1. Is that the right encoding?
806             text.text = static_cast<char*>(GlobalLock(cbData));
807             GlobalUnlock(cbData);
808             ::CloseClipboard();
809             return;
810         }
811         ::CloseClipboard();
812     }
813 }
814
815 RefPtr<DocumentFragment> Pasteboard::documentFragment(Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText)
816 {
817     chosePlainText = false;
818     
819     if (::IsClipboardFormatAvailable(HTMLClipboardFormat) && ::OpenClipboard(m_owner)) {
820         // get data off of clipboard
821         HANDLE cbData = ::GetClipboardData(HTMLClipboardFormat);
822         if (cbData) {
823             SIZE_T dataSize = ::GlobalSize(cbData);
824             String cfhtml(UTF8Encoding().decode(static_cast<char*>(GlobalLock(cbData)), dataSize));
825             GlobalUnlock(cbData);
826             ::CloseClipboard();
827
828             return fragmentFromCFHTML(frame.document(), cfhtml);
829         } else 
830             ::CloseClipboard();
831     }
832      
833     if (allowPlainText && ::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
834         chosePlainText = true;
835         if (::OpenClipboard(m_owner)) {
836             HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT);
837             if (cbData) {
838                 UChar* buffer = static_cast<UChar*>(GlobalLock(cbData));
839                 String str(buffer);
840                 GlobalUnlock(cbData);
841                 ::CloseClipboard();
842                 return createFragmentFromText(context, str);
843             } else 
844                 ::CloseClipboard();
845         }
846     }
847
848     if (allowPlainText && ::IsClipboardFormatAvailable(CF_TEXT)) {
849         chosePlainText = true;
850         if (::OpenClipboard(m_owner)) {
851             HANDLE cbData = ::GetClipboardData(CF_TEXT);
852             if (cbData) {
853                 char* buffer = static_cast<char*>(GlobalLock(cbData));
854                 String str(buffer);
855                 GlobalUnlock(cbData);
856                 ::CloseClipboard();
857                 return createFragmentFromText(context, str);
858             } else
859                 ::CloseClipboard();
860         }
861     }
862     
863     return nullptr;
864 }
865
866 void Pasteboard::setExternalDataObject(IDataObject *dataObject)
867 {
868     m_writableDataObject = 0;
869     m_dataObject = dataObject;
870 }
871
872 static CachedImage* getCachedImage(Element& element)
873 {
874     // Attempt to pull CachedImage from element
875     RenderObject* renderer = element.renderer();
876     if (!is<RenderImage>(renderer))
877         return nullptr;
878
879     auto* image = downcast<RenderImage>(renderer);
880     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
881         return image->cachedImage();
882
883     return nullptr;
884 }
885
886 static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image)
887 {
888     ASSERT_ARG(image, image);
889     ASSERT(image->image()->data());
890
891     String fsPath;
892     HGLOBAL memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
893     if (!memObj)
894         return 0;
895
896     FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
897     if (!fgd) {
898         GlobalFree(memObj);
899         return 0;
900     }
901
902     memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
903     fgd->cItems = 1;
904     fgd->fgd[0].dwFlags = FD_FILESIZE;
905     fgd->fgd[0].nFileSizeLow = image->image()->data()->size();
906
907     const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title;
908     String extension = image->image()->filenameExtension();
909     if (extension.isEmpty()) {
910         // Do not continue processing in the rare and unusual case where a decoded image is not able 
911         // to provide a filename extension. Something tricky (like a bait-n-switch) is going on
912         GlobalUnlock(memObj);
913         GlobalFree(memObj);
914         return 0;
915     }
916     extension.insert(".", 0);
917     fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.charactersWithNullTermination().data(), false);
918
919     if (fsPath.length() <= 0) {
920         GlobalUnlock(memObj);
921         GlobalFree(memObj);
922         return 0;
923     }
924
925     int maxSize = std::min<int>(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
926     StringView(fsPath).substring(0, maxSize).getCharactersWithUpconvert(fgd->fgd[0].cFileName);
927     GlobalUnlock(memObj);
928
929     return memObj;
930 }
931
932 static HGLOBAL createGlobalImageFileContent(SharedBuffer* data)
933 {
934     HGLOBAL memObj = GlobalAlloc(GPTR, data->size());
935     if (!memObj) 
936         return 0;
937
938     char* fileContents = (PSTR)GlobalLock(memObj);
939     if (!fileContents) {
940         GlobalFree(memObj);
941         return 0;
942     }
943
944     if (data->data())
945         CopyMemory(fileContents, data->data(), data->size());
946
947     GlobalUnlock(memObj);
948
949     return memObj;
950 }
951
952 static HGLOBAL createGlobalHDropContent(const URL& url, String& fileName, SharedBuffer* data)
953 {
954     if (fileName.isEmpty() || !data)
955         return 0;
956
957     WCHAR filePath[MAX_PATH];
958
959     if (url.isLocalFile()) {
960         String localPath = decodeURLEscapeSequences(url.path());
961         // windows does not enjoy a leading slash on paths
962         if (localPath[0] == '/')
963             localPath = localPath.substring(1);
964         auto localPathWide = stringToNullTerminatedWChar(localPath);
965         LPCWSTR localPathStr = localPathWide.data();
966         if (localPathStr && wcslen(localPathStr) + 1 < MAX_PATH)
967             wcscpy_s(filePath, MAX_PATH, localPathStr);
968         else
969             return 0;
970     } else {
971         WCHAR tempPath[MAX_PATH];
972         WCHAR extension[MAX_PATH];
973         if (!::GetTempPath(WTF_ARRAY_LENGTH(tempPath), tempPath))
974             return 0;
975         if (!::PathAppend(tempPath, stringToNullTerminatedWChar(fileName).data()))
976             return 0;
977         LPCWSTR foundExtension = ::PathFindExtension(tempPath);
978         if (foundExtension) {
979             if (wcscpy_s(extension, MAX_PATH, foundExtension))
980                 return 0;
981         } else
982             *extension = 0;
983         ::PathRemoveExtension(tempPath);
984         for (int i = 1; i < 10000; i++) {
985             if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1)
986                 return 0;
987             if (!::PathFileExists(filePath))
988                 break;
989         }
990         HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
991         if (tempFileHandle == INVALID_HANDLE_VALUE)
992             return 0;
993
994         // Write the data to this temp file.
995         DWORD written;
996         BOOL tempWriteSucceeded = FALSE;
997         if (data->data())
998             tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0);
999         CloseHandle(tempFileHandle);
1000         if (!tempWriteSucceeded)
1001             return 0;
1002     }
1003
1004     SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2));
1005     HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
1006     if (!memObj) 
1007         return 0;
1008
1009     DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj);
1010     if (!dropFiles) {
1011         GlobalFree(memObj);
1012         return 0;
1013     }
1014
1015     dropFiles->pFiles = sizeof(DROPFILES);
1016     dropFiles->fWide = TRUE;
1017     wcscpy(reinterpret_cast<LPWSTR>(dropFiles + 1), filePath);
1018     GlobalUnlock(memObj);
1019
1020     return memObj;
1021 }
1022
1023 void Pasteboard::writeImageToDataObject(Element& element, const URL& url)
1024 {
1025     // Shove image data into a DataObject for use as a file
1026     CachedImage* cachedImage = getCachedImage(element);
1027     if (!cachedImage || !cachedImage->imageForRenderer(element.renderer()) || !cachedImage->isLoaded())
1028         return;
1029
1030     SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element.renderer())->data();
1031     if (!imageBuffer || !imageBuffer->size())
1032         return;
1033
1034     HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element.attributeWithoutSynchronization(HTMLNames::altAttr), cachedImage);
1035     if (!imageFileDescriptor)
1036         return;
1037
1038     HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer);
1039     if (!imageFileContent) {
1040         GlobalFree(imageFileDescriptor);
1041         return;
1042     }
1043
1044     String fileName = cachedImage->response().suggestedFilename();
1045     HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer);
1046     if (!hDropContent) {
1047         GlobalFree(imageFileDescriptor);
1048         GlobalFree(imageFileContent);
1049         return;
1050     }
1051
1052     writeFileToDataObject(m_writableDataObject.get(), imageFileDescriptor, imageFileContent, hDropContent);
1053 }
1054
1055 void Pasteboard::writeURLToWritableDataObject(const URL& url, const String& title)
1056 {
1057     WebCore::writeURL(m_writableDataObject.get(), url, title, true, false);
1058 }
1059
1060 void Pasteboard::writeMarkup(const String& markup)
1061 {
1062     Vector<char> data;
1063     markupToCFHTML(markup, "", data);
1064
1065     STGMEDIUM medium { };
1066     medium.tymed = TYMED_HGLOBAL;
1067
1068     medium.hGlobal = createGlobalData(data);
1069     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
1070         GlobalFree(medium.hGlobal);
1071 }
1072
1073 void Pasteboard::write(const PasteboardWebContent&)
1074 {
1075 }
1076
1077 void Pasteboard::read(PasteboardWebContentReader&, WebContentReadingPolicy)
1078 {
1079 }
1080
1081 void Pasteboard::write(const PasteboardImage&)
1082 {
1083 }
1084
1085 void Pasteboard::writeCustomData(const PasteboardCustomData&)
1086 {
1087 }
1088
1089 void Pasteboard::write(const Color&)
1090 {
1091 }
1092
1093 } // namespace WebCore