827e3588c59d9b0a1a17a673da0b67d66783e67d
[WebKit-https.git] / Source / WebCore / platform / win / ClipboardUtilitiesWin.cpp
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "ClipboardUtilitiesWin.h"
28
29 #include "DocumentFragment.h"
30 #include "KURL.h"
31 #include "TextEncoding.h"
32 #include "markup.h"
33 #include <shlobj.h>
34 #include <shlwapi.h>
35 #include <wininet.h> // for INTERNET_MAX_URL_LENGTH
36 #include <wtf/StringExtras.h>
37 #include <wtf/text/CString.h>
38 #include <wtf/text/WTFString.h>
39
40 #if USE(CF)
41 #include <CoreFoundation/CoreFoundation.h>
42 #include <wtf/RetainPtr.h>
43 #endif
44
45 namespace WebCore {
46
47 #if USE(CF)
48 FORMATETC* cfHDropFormat()
49 {
50     static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
51     return &urlFormat;
52 }
53
54 static bool urlFromPath(CFStringRef path, String& url)
55 {
56     if (!path)
57         return false;
58
59     RetainPtr<CFURLRef> cfURL(AdoptCF, CFURLCreateWithFileSystemPath(0, path, kCFURLWindowsPathStyle, false));
60     if (!cfURL)
61         return false;
62
63     url = CFURLGetString(cfURL.get());
64
65     // Work around <rdar://problem/6708300>, where CFURLCreateWithFileSystemPath makes URLs with "localhost".
66     if (url.startsWith("file://localhost/"))
67         url.remove(7, 9);
68
69     return true;
70 }
71 #endif
72
73 static bool getDataMapItem(const DragDataMap* dataObject, FORMATETC* format, String& item)
74 {
75     DragDataMap::const_iterator found = dataObject->find(format->cfFormat);
76     if (found == dataObject->end())
77         return false;
78     item = found->second[0];
79     return true;
80 }
81
82 static bool getWebLocData(IDataObject* dataObject, String& url, String* title) 
83 {
84     bool succeeded = false;
85 #if USE(CF)
86     WCHAR filename[MAX_PATH];
87     WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH];
88
89     STGMEDIUM medium;
90     if (FAILED(dataObject->GetData(cfHDropFormat(), &medium)))
91         return false;
92
93     HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
94
95     if (!hdrop)
96         return false;
97
98     if (!DragQueryFileW(hdrop, 0, filename, WTF_ARRAY_LENGTH(filename)))
99         goto exit;
100
101     if (_wcsicmp(PathFindExtensionW(filename), L".url"))
102         goto exit;    
103     
104     if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF_ARRAY_LENGTH(urlBuffer), filename))
105         goto exit;
106     
107     if (title) {
108         PathRemoveExtension(filename);
109         *title = String((UChar*)filename);
110     }
111     
112     url = String((UChar*)urlBuffer);
113     succeeded = true;
114
115 exit:
116     // Free up memory.
117     DragFinish(hdrop);
118     GlobalUnlock(medium.hGlobal);
119 #endif
120     return succeeded;
121 }
122
123 static bool getWebLocData(const DragDataMap* dataObject, String& url, String* title) 
124 {
125 #if USE(CF)
126     WCHAR filename[MAX_PATH];
127     WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH];
128
129     if (!dataObject->contains(cfHDropFormat()->cfFormat))
130         return false;
131
132     wcscpy(filename, dataObject->get(cfHDropFormat()->cfFormat)[0].charactersWithNullTermination());
133     if (_wcsicmp(PathFindExtensionW(filename), L".url"))
134         return false;    
135
136     if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF_ARRAY_LENGTH(urlBuffer), filename))
137         return false;
138
139     if (title) {
140         PathRemoveExtension(filename);
141         *title = filename;
142     }
143     
144     url = urlBuffer;
145     return true;
146 #else
147     return false;
148 #endif
149 }
150
151 static String extractURL(const String &inURL, String* title)
152 {
153     String url = inURL;
154     int splitLoc = url.find('\n');
155     if (splitLoc > 0) {
156         if (title)
157             *title = url.substring(splitLoc+1);
158         url.truncate(splitLoc);
159     } else if (title)
160         *title = url;
161     return url;
162 }
163
164 // Firefox text/html
165 static FORMATETC* texthtmlFormat() 
166 {
167     static UINT cf = RegisterClipboardFormat(L"text/html");
168     static FORMATETC texthtmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
169     return &texthtmlFormat;
170 }
171
172 HGLOBAL createGlobalData(const KURL& url, const String& title)
173 {
174     String mutableURL(url.string());
175     String mutableTitle(title);
176     SIZE_T size = mutableURL.length() + mutableTitle.length() + 2; // +1 for "\n" and +1 for null terminator
177     HGLOBAL cbData = ::GlobalAlloc(GPTR, size * sizeof(UChar));
178
179     if (cbData) {
180         PWSTR buffer = static_cast<PWSTR>(GlobalLock(cbData));
181         _snwprintf(buffer, size, L"%s\n%s", mutableURL.charactersWithNullTermination(), mutableTitle.charactersWithNullTermination());
182         GlobalUnlock(cbData);
183     }
184     return cbData;
185 }
186
187 HGLOBAL createGlobalData(const String& str)
188 {
189     HGLOBAL globalData = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar));
190     if (!globalData)
191         return 0;
192     UChar* buffer = static_cast<UChar*>(GlobalLock(globalData));
193     memcpy(buffer, str.characters(), str.length() * sizeof(UChar));
194     buffer[str.length()] = 0;
195     GlobalUnlock(globalData);
196     return globalData;
197 }
198
199 HGLOBAL createGlobalData(const Vector<char>& vector)
200 {
201     HGLOBAL globalData = ::GlobalAlloc(GPTR, vector.size() + 1);
202     if (!globalData)
203         return 0;
204     char* buffer = static_cast<char*>(GlobalLock(globalData));
205     memcpy(buffer, vector.data(), vector.size());
206     buffer[vector.size()] = 0;
207     GlobalUnlock(globalData);
208     return globalData;
209 }
210
211 static String getFullCFHTML(IDataObject* data, bool& success)
212 {
213     STGMEDIUM store;
214     if (SUCCEEDED(data->GetData(htmlFormat(), &store))) {
215         // MS HTML Format parsing
216         char* data = static_cast<char*>(GlobalLock(store.hGlobal));
217         SIZE_T dataSize = ::GlobalSize(store.hGlobal);
218         String cfhtml(UTF8Encoding().decode(data, dataSize));
219         GlobalUnlock(store.hGlobal);
220         ReleaseStgMedium(&store);
221         success = true;
222         return cfhtml;
223     }
224     success = false;
225     return String();
226 }
227
228 static void append(Vector<char>& vector, const char* string)
229 {
230     vector.append(string, strlen(string));
231 }
232
233 static void append(Vector<char>& vector, const CString& string)
234 {
235     vector.append(string.data(), string.length());
236 }
237
238 // Find the markup between "<!--StartFragment -->" and "<!--EndFragment -->", accounting for browser quirks.
239 static String extractMarkupFromCFHTML(const String& cfhtml)
240 {
241     unsigned markupStart = cfhtml.find("<html", 0, false);
242     unsigned tagStart = cfhtml.find("startfragment", markupStart, false);
243     unsigned fragmentStart = cfhtml.find('>', tagStart) + 1;
244     unsigned tagEnd = cfhtml.find("endfragment", fragmentStart, false);
245     unsigned fragmentEnd = cfhtml.reverseFind('<', tagEnd);
246     return cfhtml.substring(fragmentStart, fragmentEnd - fragmentStart).stripWhiteSpace();
247 }
248
249 // Documentation for the CF_HTML format is available at http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
250 void markupToCFHTML(const String& markup, const String& srcURL, Vector<char>& result)
251 {
252     if (markup.isEmpty())
253         return;
254
255     #define MAX_DIGITS 10
256     #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits)
257     #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u"
258     #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS)
259
260     const char* header = "Version:0.9\n"
261         "StartHTML:" NUMBER_FORMAT "\n"
262         "EndHTML:" NUMBER_FORMAT "\n"
263         "StartFragment:" NUMBER_FORMAT "\n"
264         "EndFragment:" NUMBER_FORMAT "\n";
265     const char* sourceURLPrefix = "SourceURL:";
266
267     const char* startMarkup = "<HTML>\n<BODY>\n<!--StartFragment-->\n";
268     const char* endMarkup = "\n<!--EndFragment-->\n</BODY>\n</HTML>";
269
270     CString sourceURLUTF8 = srcURL == blankURL() ? "" : srcURL.utf8();
271     CString markupUTF8 = markup.utf8();
272
273     // calculate offsets
274     unsigned startHTMLOffset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_DIGITS * 4;
275     if (sourceURLUTF8.length())
276         startHTMLOffset += strlen(sourceURLPrefix) + sourceURLUTF8.length() + 1;
277     unsigned startFragmentOffset = startHTMLOffset + strlen(startMarkup);
278     unsigned endFragmentOffset = startFragmentOffset + markupUTF8.length();
279     unsigned endHTMLOffset = endFragmentOffset + strlen(endMarkup);
280
281     unsigned headerBufferLength = startHTMLOffset + 1; // + 1 for '\0' terminator.
282     char* headerBuffer = (char*)malloc(headerBufferLength);
283     snprintf(headerBuffer, headerBufferLength, header, startHTMLOffset, endHTMLOffset, startFragmentOffset, endFragmentOffset);
284     append(result, CString(headerBuffer));
285     free(headerBuffer);
286     if (sourceURLUTF8.length()) {
287         append(result, sourceURLPrefix);
288         append(result, sourceURLUTF8);
289         result.append('\n');
290     }
291     append(result, startMarkup);
292     append(result, markupUTF8);
293     append(result, endMarkup);
294
295     #undef MAX_DIGITS
296     #undef MAKE_NUMBER_FORMAT_1
297     #undef MAKE_NUMBER_FORMAT_2
298     #undef NUMBER_FORMAT
299 }
300
301 void replaceNewlinesWithWindowsStyleNewlines(String& str)
302 {
303     static const UChar Newline = '\n';
304     static const char* const WindowsNewline("\r\n");
305     str.replace(Newline, WindowsNewline);
306 }
307
308 void replaceNBSPWithSpace(String& str)
309 {
310     static const UChar NonBreakingSpaceCharacter = 0xA0;
311     static const UChar SpaceCharacter = ' ';
312     str.replace(NonBreakingSpaceCharacter, SpaceCharacter);
313 }
314
315 FORMATETC* urlWFormat()
316 {
317     static UINT cf = RegisterClipboardFormat(L"UniformResourceLocatorW");
318     static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
319     return &urlFormat;
320 }
321
322 FORMATETC* urlFormat()
323 {
324     static UINT cf = RegisterClipboardFormat(L"UniformResourceLocator");
325     static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
326     return &urlFormat;
327 }
328
329 FORMATETC* plainTextFormat()
330 {
331     static FORMATETC textFormat = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
332     return &textFormat;
333 }
334
335 FORMATETC* plainTextWFormat()
336 {
337     static FORMATETC textFormat = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
338     return &textFormat;
339 }
340
341 FORMATETC* filenameWFormat()
342 {
343     static UINT cf = RegisterClipboardFormat(L"FileNameW");
344     static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
345     return &urlFormat;
346 }
347
348 FORMATETC* filenameFormat()
349 {
350     static UINT cf = RegisterClipboardFormat(L"FileName");
351     static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
352     return &urlFormat;
353 }
354
355 // MSIE HTML Format
356 FORMATETC* htmlFormat() 
357 {
358     static UINT cf = RegisterClipboardFormat(L"HTML Format");
359     static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
360     return &htmlFormat;
361 }
362
363 FORMATETC* smartPasteFormat()
364 {
365     static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
366     static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
367     return &htmlFormat;
368 }
369
370 FORMATETC* fileDescriptorFormat()
371 {
372     static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
373     static FORMATETC fileDescriptorFormat = { cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
374     return &fileDescriptorFormat;
375 }
376
377 FORMATETC* fileContentFormatZero()
378 {
379     static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
380     static FORMATETC fileContentFormat = { cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL };
381     return &fileContentFormat;
382 }
383
384 void getFileDescriptorData(IDataObject* dataObject, int& size, String& pathname)
385 {
386     STGMEDIUM store;
387     size = 0;
388     if (FAILED(dataObject->GetData(fileDescriptorFormat(), &store)))
389         return;
390
391     FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(store.hGlobal));
392     size = fgd->fgd[0].nFileSizeLow;
393     pathname = fgd->fgd[0].cFileName;
394
395     GlobalUnlock(store.hGlobal);
396     ::ReleaseStgMedium(&store);
397 }
398
399 void getFileContentData(IDataObject* dataObject, int size, void* dataBlob)
400 {
401     STGMEDIUM store;
402     if (FAILED(dataObject->GetData(fileContentFormatZero(), &store)))
403         return;
404     void* data = GlobalLock(store.hGlobal);
405     ::CopyMemory(dataBlob, data, size);
406
407     GlobalUnlock(store.hGlobal);
408     ::ReleaseStgMedium(&store);
409 }
410
411 void setFileDescriptorData(IDataObject* dataObject, int size, const String& passedPathname)
412 {
413     String pathname = passedPathname;
414
415     STGMEDIUM medium = { 0 };
416     medium.tymed = TYMED_HGLOBAL;
417
418     medium.hGlobal = ::GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
419     if (!medium.hGlobal)
420         return;
421
422     FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(medium.hGlobal));
423     ::ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR));
424     fgd->cItems = 1;
425     fgd->fgd[0].dwFlags = FD_FILESIZE;
426     fgd->fgd[0].nFileSizeLow = size;
427
428     int maxSize = std::min(pathname.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
429     CopyMemory(fgd->fgd[0].cFileName, pathname.charactersWithNullTermination(), maxSize * sizeof(UChar));
430     GlobalUnlock(medium.hGlobal);
431
432     dataObject->SetData(fileDescriptorFormat(), &medium, TRUE);
433 }
434
435 void setFileContentData(IDataObject* dataObject, int size, void* dataBlob)
436 {
437     STGMEDIUM medium = { 0 };
438     medium.tymed = TYMED_HGLOBAL;
439
440     medium.hGlobal = ::GlobalAlloc(GPTR, size);
441     if (!medium.hGlobal)
442         return;
443     void* fileContents = GlobalLock(medium.hGlobal);
444     ::CopyMemory(fileContents, dataBlob, size);
445     GlobalUnlock(medium.hGlobal);
446
447     dataObject->SetData(fileContentFormatZero(), &medium, TRUE);
448 }
449
450 String getURL(IDataObject* dataObject, DragData::FilenameConversionPolicy filenamePolicy, bool& success, String* title)
451 {
452     STGMEDIUM store;
453     String url;
454     success = false;
455     if (getWebLocData(dataObject, url, title))
456         success = true;
457     else if (SUCCEEDED(dataObject->GetData(urlWFormat(), &store))) {
458         // URL using Unicode
459         UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal));
460         url = extractURL(String(data), title);
461         GlobalUnlock(store.hGlobal);
462         ReleaseStgMedium(&store);
463         success = true;
464     } else if (SUCCEEDED(dataObject->GetData(urlFormat(), &store))) {
465         // URL using ASCII
466         char* data = static_cast<char*>(GlobalLock(store.hGlobal));
467         url = extractURL(String(data), title);
468         GlobalUnlock(store.hGlobal);
469         ReleaseStgMedium(&store);
470         success = true;
471     }
472 #if USE(CF)
473     else if (filenamePolicy == DragData::ConvertFilenames) {
474         if (SUCCEEDED(dataObject->GetData(filenameWFormat(), &store))) {
475             // file using unicode
476             wchar_t* data = static_cast<wchar_t*>(GlobalLock(store.hGlobal));
477             if (data && data[0] && (PathFileExists(data) || PathIsUNC(data))) {
478                 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWithCharacters(kCFAllocatorDefault, (const UniChar*)data, wcslen(data)));
479                 if (urlFromPath(pathAsCFString.get(), url)) {
480                     if (title)
481                         *title = url;
482                     success = true;
483                 }
484             }
485             GlobalUnlock(store.hGlobal);
486             ReleaseStgMedium(&store);
487         } else if (SUCCEEDED(dataObject->GetData(filenameFormat(), &store))) {
488             // filename using ascii
489             char* data = static_cast<char*>(GlobalLock(store.hGlobal));
490             if (data && data[0] && (PathFileExistsA(data) || PathIsUNCA(data))) {
491                 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWithCString(kCFAllocatorDefault, data, kCFStringEncodingASCII));
492                 if (urlFromPath(pathAsCFString.get(), url)) {
493                     if (title)
494                         *title = url;
495                     success = true;
496                 }
497             }
498             GlobalUnlock(store.hGlobal);
499             ReleaseStgMedium(&store);
500         }
501     }
502 #endif
503     return url;
504 }
505
506 String getURL(const DragDataMap* data, DragData::FilenameConversionPolicy filenamePolicy, String* title)
507 {
508     String url;
509
510     if (getWebLocData(data, url, title))
511         return url;
512     if (getDataMapItem(data, urlWFormat(), url))
513         return extractURL(url, title);
514     if (getDataMapItem(data, urlFormat(), url))
515         return extractURL(url, title);
516 #if USE(CF)
517     if (filenamePolicy != DragData::ConvertFilenames)
518         return url;
519
520     String stringData;
521     if (!getDataMapItem(data, filenameWFormat(), stringData))
522         getDataMapItem(data, filenameFormat(), stringData);
523
524     if (stringData.isEmpty() || (!PathFileExists(stringData.charactersWithNullTermination()) && !PathIsUNC(stringData.charactersWithNullTermination())))
525         return url;
526     RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWithCharacters(kCFAllocatorDefault, (const UniChar *)stringData.charactersWithNullTermination(), wcslen(stringData.charactersWithNullTermination())));
527     if (urlFromPath(pathAsCFString.get(), url) && title)
528         *title = url;
529 #endif
530     return url;
531 }
532
533 String getPlainText(IDataObject* dataObject, bool& success)
534 {
535     STGMEDIUM store;
536     String text;
537     success = false;
538     if (SUCCEEDED(dataObject->GetData(plainTextWFormat(), &store))) {
539         // Unicode text
540         UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal));
541         text = String(data);
542         GlobalUnlock(store.hGlobal);
543         ReleaseStgMedium(&store);
544         success = true;
545     } else if (SUCCEEDED(dataObject->GetData(plainTextFormat(), &store))) {
546         // ASCII text
547         char* data = static_cast<char*>(GlobalLock(store.hGlobal));
548         text = String(data);
549         GlobalUnlock(store.hGlobal);
550         ReleaseStgMedium(&store);
551         success = true;
552     } else {
553         // FIXME: Originally, we called getURL() here because dragging and dropping files doesn't
554         // populate the drag with text data. Per https://bugs.webkit.org/show_bug.cgi?id=38826, this
555         // is undesirable, so maybe this line can be removed.
556         text = getURL(dataObject, DragData::DoNotConvertFilenames, success);
557         success = true;
558     }
559     return text;
560 }
561
562 String getPlainText(const DragDataMap* data)
563 {
564     String text;
565     
566     if (getDataMapItem(data, plainTextWFormat(), text))
567         return text;
568     if (getDataMapItem(data, plainTextFormat(), text))
569         return text;
570     return getURL(data, DragData::DoNotConvertFilenames);
571 }
572
573 String getTextHTML(IDataObject* data, bool& success)
574 {
575     STGMEDIUM store;
576     String html;
577     success = false;
578     if (SUCCEEDED(data->GetData(texthtmlFormat(), &store))) {
579         UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal));
580         html = String(data);
581         GlobalUnlock(store.hGlobal);
582         ReleaseStgMedium(&store);
583         success = true;
584     }
585     return html;
586 }
587
588 String getTextHTML(const DragDataMap* data)
589 {
590     String text;
591     getDataMapItem(data, texthtmlFormat(), text);
592     return text;
593 }
594
595 String getCFHTML(IDataObject* data, bool& success)
596 {
597     String cfhtml = getFullCFHTML(data, success);
598     if (success)
599         return extractMarkupFromCFHTML(cfhtml);
600     return String();
601 }
602
603 String getCFHTML(const DragDataMap* dataMap)
604 {
605     String cfhtml;
606     getDataMapItem(dataMap, htmlFormat(), cfhtml);
607     return extractMarkupFromCFHTML(cfhtml);
608 }
609
610 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*)
611 {
612     // FIXME: We should be able to create fragments from files
613     return 0;
614 }
615
616 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*)
617 {
618     // FIXME: We should be able to create fragments from files
619     return 0;
620 }
621
622 bool containsFilenames(const IDataObject*)
623 {
624     // FIXME: We'll want to update this once we can produce fragments from files
625     return false;
626 }
627
628 bool containsFilenames(const DragDataMap*)
629 {
630     // FIXME: We'll want to update this once we can produce fragments from files
631     return false;
632 }
633
634 // Convert a String containing CF_HTML formatted text to a DocumentFragment
635 PassRefPtr<DocumentFragment> fragmentFromCFHTML(Document* doc, const String& cfhtml)
636 {
637     // obtain baseURL if present
638     String srcURLStr("sourceURL:");
639     String srcURL;
640     unsigned lineStart = cfhtml.find(srcURLStr, 0, false);
641     if (lineStart != -1) {
642         unsigned srcEnd = cfhtml.find("\n", lineStart, false);
643         unsigned srcStart = lineStart+srcURLStr.length();
644         String rawSrcURL = cfhtml.substring(srcStart, srcEnd-srcStart);
645         replaceNBSPWithSpace(rawSrcURL);
646         srcURL = rawSrcURL.stripWhiteSpace();
647     }
648
649     String markup = extractMarkupFromCFHTML(cfhtml);
650     return createFragmentFromMarkup(doc, markup, srcURL, FragmentScriptingNotAllowed);
651 }
652
653 PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data) 
654 {
655     if (!doc || !data)
656         return 0;
657
658     bool success = false;
659     String cfhtml = getFullCFHTML(data, success);
660     if (success) {
661         if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(doc, cfhtml))
662             return fragment.release();
663     }
664
665     String html = getTextHTML(data, success);
666     String srcURL;
667     if (success)
668         return createFragmentFromMarkup(doc, html, srcURL, FragmentScriptingNotAllowed);
669
670     return 0;
671 }
672
673 PassRefPtr<DocumentFragment> fragmentFromHTML(Document* document, const DragDataMap* data) 
674 {
675     if (!document || !data || data->isEmpty())
676         return 0;
677
678     String stringData;
679     if (getDataMapItem(data, htmlFormat(), stringData)) {
680         if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(document, stringData))
681             return fragment.release();
682     }
683
684     String srcURL;
685     if (getDataMapItem(data, texthtmlFormat(), stringData))
686         return createFragmentFromMarkup(document, stringData, srcURL, FragmentScriptingNotAllowed);
687
688     return 0;
689 }
690
691 bool containsHTML(IDataObject* data)
692 {
693     return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->QueryGetData(htmlFormat()));
694 }
695
696 bool containsHTML(const DragDataMap* data)
697 {
698     return data->contains(texthtmlFormat()->cfFormat) || data->contains(htmlFormat()->cfFormat);
699 }
700
701 typedef void (*GetStringFunction)(IDataObject*, FORMATETC*, Vector<String>&);
702 typedef void (*SetStringFunction)(IDataObject*, FORMATETC*, const Vector<String>&);
703
704 struct ClipboardDataItem {
705     GetStringFunction getString;
706     SetStringFunction setString;
707     FORMATETC* format;
708
709     ClipboardDataItem(FORMATETC* format, GetStringFunction getString, SetStringFunction setString): format(format), getString(getString), setString(setString) { }
710 };
711
712 typedef HashMap<UINT, ClipboardDataItem*> ClipboardFormatMap;
713
714 // Getter functions.
715
716 template<typename T> void getStringData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings)
717 {
718     STGMEDIUM store;
719     if (FAILED(data->GetData(format, &store)))
720         return;
721     dataStrings.append(String(static_cast<T*>(GlobalLock(store.hGlobal)), ::GlobalSize(store.hGlobal) / sizeof(T)));
722     GlobalUnlock(store.hGlobal);
723     ReleaseStgMedium(&store);
724 }
725
726 void getUtf8Data(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings)
727 {
728     STGMEDIUM store;
729     if (FAILED(data->GetData(format, &store)))
730         return;
731     dataStrings.append(String(UTF8Encoding().decode(static_cast<char*>(GlobalLock(store.hGlobal)), GlobalSize(store.hGlobal))));
732     GlobalUnlock(store.hGlobal);
733     ReleaseStgMedium(&store);
734 }
735
736 #if USE(CF)
737 void getCFData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings)
738 {
739     STGMEDIUM store;
740     if (FAILED(data->GetData(format, &store)))
741         return;
742
743     HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(store.hGlobal));
744     if (!hdrop)
745         return;
746
747     WCHAR filename[MAX_PATH];
748     UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
749     for (UINT i = 0; i < fileCount; i++) {
750         if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename)))
751             continue;
752         dataStrings.append(static_cast<UChar*>(filename));
753     }
754
755     GlobalUnlock(store.hGlobal);
756     ReleaseStgMedium(&store);
757 }
758 #endif
759
760 // Setter functions.
761
762 void setUCharData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings)
763 {
764     STGMEDIUM medium = {0};
765     medium.tymed = TYMED_HGLOBAL;
766
767     medium.hGlobal = createGlobalData(dataStrings.first());
768     if (!medium.hGlobal)
769         return;
770     data->SetData(format, &medium, FALSE);
771     ::GlobalFree(medium.hGlobal);
772 }
773
774 void setUtf8Data(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings)
775 {
776     STGMEDIUM medium = {0};
777     medium.tymed = TYMED_HGLOBAL;
778
779     CString charString = dataStrings.first().utf8();
780     size_t stringLength = charString.length();
781     medium.hGlobal = ::GlobalAlloc(GPTR, stringLength + 1);
782     if (!medium.hGlobal)
783         return;
784     char* buffer = static_cast<char*>(GlobalLock(medium.hGlobal));
785     memcpy(buffer, charString.data(), stringLength);
786     buffer[stringLength] = 0;
787     GlobalUnlock(medium.hGlobal);
788     data->SetData(format, &medium, FALSE);
789     ::GlobalFree(medium.hGlobal);
790 }
791
792 #if USE(CF)
793 void setCFData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings)
794 {
795     STGMEDIUM medium = {0};
796     medium.tymed = TYMED_HGLOBAL;
797
798     SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (dataStrings.first().length() + 2));
799     medium.hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
800     if (!medium.hGlobal) 
801         return;
802
803     DROPFILES* dropFiles = reinterpret_cast<DROPFILES *>(GlobalLock(medium.hGlobal));
804     dropFiles->pFiles = sizeof(DROPFILES);
805     dropFiles->fWide = TRUE;
806     String filename = dataStrings.first();
807     wcscpy(reinterpret_cast<LPWSTR>(dropFiles + 1), filename.charactersWithNullTermination());    
808     GlobalUnlock(medium.hGlobal);
809     data->SetData(format, &medium, FALSE);
810     ::GlobalFree(medium.hGlobal);
811 }
812 #endif
813
814 static const ClipboardFormatMap& getClipboardMap()
815 {
816     static ClipboardFormatMap formatMap;
817     if (formatMap.isEmpty()) {
818         formatMap.add(htmlFormat()->cfFormat, new ClipboardDataItem(htmlFormat(), getUtf8Data, setUtf8Data));
819         formatMap.add(texthtmlFormat()->cfFormat, new ClipboardDataItem(texthtmlFormat(), getStringData<UChar>, setUCharData));
820         formatMap.add(plainTextFormat()->cfFormat,  new ClipboardDataItem(plainTextFormat(), getStringData<char>, setUtf8Data));
821         formatMap.add(plainTextWFormat()->cfFormat,  new ClipboardDataItem(plainTextWFormat(), getStringData<UChar>, setUCharData));
822 #if USE(CF)
823         formatMap.add(cfHDropFormat()->cfFormat,  new ClipboardDataItem(cfHDropFormat(), getCFData, setCFData));
824 #endif
825         formatMap.add(filenameFormat()->cfFormat,  new ClipboardDataItem(filenameFormat(), getStringData<char>, setUtf8Data));
826         formatMap.add(filenameWFormat()->cfFormat,  new ClipboardDataItem(filenameWFormat(), getStringData<UChar>, setUCharData));
827         formatMap.add(urlFormat()->cfFormat,  new ClipboardDataItem(urlFormat(), getStringData<char>, setUtf8Data));
828         formatMap.add(urlWFormat()->cfFormat,  new ClipboardDataItem(urlWFormat(), getStringData<UChar>, setUCharData));
829     }
830     return formatMap;
831 }
832
833 void getClipboardData(IDataObject* dataObject, FORMATETC* format, Vector<String>& dataStrings)
834 {
835     const ClipboardFormatMap& formatMap = getClipboardMap();
836     ClipboardFormatMap::const_iterator found = formatMap.find(format->cfFormat);
837     if (found == formatMap.end())
838         return;
839     found->second->getString(dataObject, found->second->format, dataStrings);
840 }
841
842 void setClipboardData(IDataObject* dataObject, UINT format, const Vector<String>& dataStrings)
843 {
844     const ClipboardFormatMap& formatMap = getClipboardMap();
845     ClipboardFormatMap::const_iterator found = formatMap.find(format);
846     if (found == formatMap.end())
847         return;
848     found->second->setString(dataObject, found->second->format, dataStrings);
849 }
850
851 } // namespace WebCore