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