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