Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 2006, 2007 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 "ClipboardWin.h"
     28 
     29 #include "CachedImage.h"
     30 #include "ClipboardUtilitiesWin.h"
     31 #include "Document.h"
     32 #include "DragData.h"
     33 #include "Editor.h"
     34 #include "Element.h"
     35 #include "EventHandler.h"
     36 #include "FileList.h"
     37 #include "Frame.h"
     38 #include "FrameLoader.h"
     39 #include "FrameView.h"
     40 #include "HTMLNames.h"
     41 #include "HTMLParserIdioms.h"
     42 #include "Image.h"
     43 #include "MIMETypeRegistry.h"
     44 #include "NotImplemented.h"
     45 #include "Page.h"
     46 #include "Pasteboard.h"
     47 #include "PlatformMouseEvent.h"
     48 #include "PlatformString.h"
     49 #include "Range.h"
     50 #include "RenderImage.h"
     51 #include "ResourceResponse.h"
     52 #include "SharedBuffer.h"
     53 #include "WCDataObject.h"
     54 #include "markup.h"
     55 #include <shlwapi.h>
     56 #include <wininet.h>
     57 #include <wtf/RefPtr.h>
     58 #include <wtf/text/CString.h>
     59 #include <wtf/text/StringConcatenate.h>
     60 #include <wtf/text/StringHash.h>
     61 
     62 using namespace std;
     63 
     64 namespace WebCore {
     65 
     66 using namespace HTMLNames;
     67 
     68 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft
     69 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3
     70 
     71 enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeTextHTML };
     72 
     73 static ClipboardDataType clipboardTypeFromMIMEType(const String& type)
     74 {
     75     String qType = type.stripWhiteSpace().lower();
     76 
     77     // two special cases for IE compatibility
     78     if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain;"))
     79         return ClipboardDataTypeText;
     80     if (qType == "url" || qType == "text/uri-list")
     81         return ClipboardDataTypeURL;
     82     if (qType == "text/html")
     83         return ClipboardDataTypeTextHTML;
     84 
     85     return ClipboardDataTypeNone;
     86 }
     87 
     88 static inline FORMATETC* fileDescriptorFormat()
     89 {
     90     static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
     91     static FORMATETC fileDescriptorFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
     92     return &fileDescriptorFormat;
     93 }
     94 
     95 static inline FORMATETC* fileContentFormatZero()
     96 {
     97     static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
     98     static FORMATETC fileContentFormat = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
     99     return &fileContentFormat;
    100 }
    101 
    102 #if !OS(WINCE)
    103 static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length)
    104 {
    105     size_t writeTo = 0;
    106     size_t readFrom = 0;
    107     while (readFrom < length) {
    108         UINT type = PathGetCharType(psz[readFrom]);
    109         if (!psz[readFrom] || type & (GCT_LFNCHAR | GCT_SHORTCHAR))
    110             psz[writeTo++] = psz[readFrom];
    111 
    112         readFrom++;
    113     }
    114     psz[writeTo] = 0;
    115 }
    116 #endif
    117 
    118 static String filesystemPathFromUrlOrTitle(const String& url, const String& title, const UChar* extension, bool isLink)
    119 {
    120 #if OS(WINCE)
    121     notImplemented();
    122     return String();
    123 #else
    124     static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1;
    125     bool usedURL = false;
    126     WCHAR fsPathBuffer[MAX_PATH];
    127     fsPathBuffer[0] = 0;
    128     int extensionLen = extension ? lstrlen(extension) : 0;
    129     int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen;
    130 
    131     if (!title.isEmpty()) {
    132         size_t len = min<size_t>(title.length(), fsPathMaxLengthExcludingExtension);
    133         CopyMemory(fsPathBuffer, title.characters(), len * sizeof(UChar));
    134         fsPathBuffer[len] = 0;
    135         pathRemoveBadFSCharacters(fsPathBuffer, len);
    136     }
    137 
    138     if (!lstrlen(fsPathBuffer)) {
    139         KURL kurl(ParsedURLString, url);
    140         usedURL = true;
    141         // The filename for any content based drag or file url should be the last element of
    142         // the path.  If we can't find it, or we're coming up with the name for a link
    143         // we just use the entire url.
    144         DWORD len = fsPathMaxLengthExcludingExtension;
    145         String lastComponent = kurl.lastPathComponent();
    146         if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) {
    147             len = min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length());
    148             CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeof(UChar));
    149         } else {
    150             len = min<DWORD>(fsPathMaxLengthExcludingExtension, url.length());
    151             CopyMemory(fsPathBuffer, url.characters(), len * sizeof(UChar));
    152         }
    153         fsPathBuffer[len] = 0;
    154         pathRemoveBadFSCharacters(fsPathBuffer, len);
    155     }
    156 
    157     if (!extension)
    158         return String(static_cast<UChar*>(fsPathBuffer));
    159 
    160     if (!isLink && usedURL) {
    161         PathRenameExtension(fsPathBuffer, extension);
    162         return String(static_cast<UChar*>(fsPathBuffer));
    163     }
    164 
    165     String result(static_cast<UChar*>(fsPathBuffer));
    166     result += String(extension);
    167     return result;
    168 #endif
    169 }
    170 
    171 static HGLOBAL createGlobalImageFileContent(SharedBuffer* data)
    172 {
    173     HGLOBAL memObj = GlobalAlloc(GPTR, data->size());
    174     if (!memObj)
    175         return 0;
    176 
    177     char* fileContents = (PSTR)GlobalLock(memObj);
    178 
    179     CopyMemory(fileContents, data->data(), data->size());
    180 
    181     GlobalUnlock(memObj);
    182 
    183     return memObj;
    184 }
    185 
    186 static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, SharedBuffer* data)
    187 {
    188     if (fileName.isEmpty() || !data)
    189         return 0;
    190 
    191     WCHAR filePath[MAX_PATH];
    192 
    193     if (url.isLocalFile()) {
    194         String localPath = url.path();
    195         // windows does not enjoy a leading slash on paths
    196         if (localPath[0] == '/')
    197             localPath = localPath.substring(1);
    198         LPCWSTR localPathStr = localPath.charactersWithNullTermination();
    199         if (wcslen(localPathStr) + 1 < MAX_PATH)
    200             wcscpy_s(filePath, MAX_PATH, localPathStr);
    201         else
    202             return 0;
    203     } else {
    204 #if OS(WINCE)
    205         notImplemented();
    206         return 0;
    207 #else
    208         WCHAR tempPath[MAX_PATH];
    209         WCHAR extension[MAX_PATH];
    210         if (!::GetTempPath(WTF_ARRAY_LENGTH(tempPath), tempPath))
    211             return 0;
    212         if (!::PathAppend(tempPath, fileName.charactersWithNullTermination()))
    213             return 0;
    214         LPCWSTR foundExtension = ::PathFindExtension(tempPath);
    215         if (foundExtension) {
    216             if (wcscpy_s(extension, MAX_PATH, foundExtension))
    217                 return 0;
    218         } else
    219             *extension = 0;
    220         ::PathRemoveExtension(tempPath);
    221         for (int i = 1; i < 10000; i++) {
    222             if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1)
    223                 return 0;
    224             if (!::PathFileExists(filePath))
    225                 break;
    226         }
    227         HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    228         if (tempFileHandle == INVALID_HANDLE_VALUE)
    229             return 0;
    230 
    231         // Write the data to this temp file.
    232         DWORD written;
    233         BOOL tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0);
    234         CloseHandle(tempFileHandle);
    235         if (!tempWriteSucceeded)
    236             return 0;
    237 #endif
    238     }
    239 
    240     SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2));
    241     HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
    242     if (!memObj)
    243         return 0;
    244 
    245     DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj);
    246     dropFiles->pFiles = sizeof(DROPFILES);
    247     dropFiles->fWide = TRUE;
    248     wcscpy((LPWSTR)(dropFiles + 1), filePath);
    249     GlobalUnlock(memObj);
    250 
    251     return memObj;
    252 }
    253 
    254 static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image)
    255 {
    256     ASSERT_ARG(image, image);
    257     ASSERT(image->image()->data());
    258 
    259     HRESULT hr = S_OK;
    260     HGLOBAL memObj = 0;
    261     String fsPath;
    262     memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
    263     if (!memObj)
    264         return 0;
    265 
    266     FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
    267     memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
    268     fgd->cItems = 1;
    269     fgd->fgd[0].dwFlags = FD_FILESIZE;
    270     fgd->fgd[0].nFileSizeLow = image->image()->data()->size();
    271 
    272     const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title;
    273     String extension = image->image()->filenameExtension();
    274     if (extension.isEmpty()) {
    275         // Do not continue processing in the rare and unusual case where a decoded image is not able
    276         // to provide a filename extension. Something tricky (like a bait-n-switch) is going on
    277         return 0;
    278     }
    279     extension.insert(".", 0);
    280     fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.charactersWithNullTermination(), false);
    281 
    282     if (fsPath.length() <= 0) {
    283         GlobalUnlock(memObj);
    284         GlobalFree(memObj);
    285         return 0;
    286     }
    287 
    288     int maxSize = min(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
    289     CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * sizeof(UChar));
    290     GlobalUnlock(memObj);
    291 
    292     return memObj;
    293 }
    294 
    295 
    296 // writeFileToDataObject takes ownership of fileDescriptor and fileContent
    297 static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescriptor, HGLOBAL fileContent, HGLOBAL hDropContent)
    298 {
    299     HRESULT hr = S_OK;
    300     FORMATETC* fe;
    301     STGMEDIUM medium = {0};
    302     medium.tymed = TYMED_HGLOBAL;
    303 
    304     if (!fileDescriptor || !fileContent)
    305         goto exit;
    306 
    307     // Descriptor
    308     fe = fileDescriptorFormat();
    309 
    310     medium.hGlobal = fileDescriptor;
    311 
    312     if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
    313         goto exit;
    314 
    315     // Contents
    316     fe = fileContentFormatZero();
    317     medium.hGlobal = fileContent;
    318     if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE)))
    319         goto exit;
    320 
    321 #if USE(CF)
    322     // HDROP
    323     if (hDropContent) {
    324         medium.hGlobal = hDropContent;
    325         hr = dataObject->SetData(cfHDropFormat(), &medium, TRUE);
    326     }
    327 #endif
    328 
    329 exit:
    330     if (FAILED(hr)) {
    331         if (fileDescriptor)
    332             GlobalFree(fileDescriptor);
    333         if (fileContent)
    334             GlobalFree(fileContent);
    335         if (hDropContent)
    336             GlobalFree(hDropContent);
    337     }
    338     return hr;
    339 }
    340 
    341 PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame)
    342 {
    343     if (dragData->platformData())
    344         return ClipboardWin::create(DragAndDrop, dragData->platformData(), policy, frame);
    345     return ClipboardWin::create(DragAndDrop, dragData->dragDataMap(), policy, frame);
    346 }
    347 
    348 ClipboardWin::ClipboardWin(ClipboardType clipboardType, IDataObject* dataObject, ClipboardAccessPolicy policy, Frame* frame)
    349     : Clipboard(policy, clipboardType)
    350     , m_dataObject(dataObject)
    351     , m_writableDataObject(0)
    352     , m_frame(frame)
    353 {
    354 }
    355 
    356 ClipboardWin::ClipboardWin(ClipboardType clipboardType, WCDataObject* dataObject, ClipboardAccessPolicy policy, Frame* frame)
    357     : Clipboard(policy, clipboardType)
    358     , m_dataObject(dataObject)
    359     , m_writableDataObject(dataObject)
    360     , m_frame(frame)
    361 {
    362 }
    363 
    364 ClipboardWin::ClipboardWin(ClipboardType clipboardType, const DragDataMap& dataMap, ClipboardAccessPolicy policy, Frame* frame)
    365     : Clipboard(policy, clipboardType)
    366     , m_dataObject(0)
    367     , m_writableDataObject(0)
    368     , m_frame(frame)
    369     , m_dragDataMap(dataMap)
    370 {
    371 }
    372 
    373 ClipboardWin::~ClipboardWin()
    374 {
    375 }
    376 
    377 static bool writeURL(WCDataObject *data, const KURL& url, String title, bool withPlainText, bool withHTML)
    378 {
    379     ASSERT(data);
    380 
    381     if (url.isEmpty())
    382         return false;
    383 
    384     if (title.isEmpty()) {
    385         title = url.lastPathComponent();
    386         if (title.isEmpty())
    387             title = url.host();
    388     }
    389 
    390     STGMEDIUM medium = {0};
    391     medium.tymed = TYMED_HGLOBAL;
    392 
    393     medium.hGlobal = createGlobalData(url, title);
    394     bool success = false;
    395     if (medium.hGlobal && FAILED(data->SetData(urlWFormat(), &medium, TRUE)))
    396         ::GlobalFree(medium.hGlobal);
    397     else
    398         success = true;
    399 
    400     if (withHTML) {
    401         Vector<char> cfhtmlData;
    402         markupToCFHTML(urlToMarkup(url, title), "", cfhtmlData);
    403         medium.hGlobal = createGlobalData(cfhtmlData);
    404         if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE)))
    405             ::GlobalFree(medium.hGlobal);
    406         else
    407             success = true;
    408     }
    409 
    410     if (withPlainText) {
    411         medium.hGlobal = createGlobalData(url.string());
    412         if (medium.hGlobal && FAILED(data->SetData(plainTextWFormat(), &medium, TRUE)))
    413             ::GlobalFree(medium.hGlobal);
    414         else
    415             success = true;
    416     }
    417 
    418     return success;
    419 }
    420 
    421 void ClipboardWin::clearData(const String& type)
    422 {
    423     // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
    424     ASSERT(isForDragAndDrop());
    425     if (policy() != ClipboardWritable || !m_writableDataObject)
    426         return;
    427 
    428     ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
    429 
    430     if (dataType == ClipboardDataTypeURL) {
    431         m_writableDataObject->clearData(urlWFormat()->cfFormat);
    432         m_writableDataObject->clearData(urlFormat()->cfFormat);
    433     }
    434     if (dataType == ClipboardDataTypeText) {
    435         m_writableDataObject->clearData(plainTextFormat()->cfFormat);
    436         m_writableDataObject->clearData(plainTextWFormat()->cfFormat);
    437     }
    438 
    439 }
    440 
    441 void ClipboardWin::clearAllData()
    442 {
    443     // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
    444     ASSERT(isForDragAndDrop());
    445     if (policy() != ClipboardWritable)
    446         return;
    447 
    448     m_writableDataObject = 0;
    449     WCDataObject::createInstance(&m_writableDataObject);
    450     m_dataObject = m_writableDataObject;
    451 }
    452 
    453 String ClipboardWin::getData(const String& type, bool& success) const
    454 {
    455     success = false;
    456     if (policy() != ClipboardReadable || (!m_dataObject && m_dragDataMap.isEmpty()))
    457         return "";
    458 
    459     ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
    460     if (dataType == ClipboardDataTypeText)
    461         return m_dataObject ? getPlainText(m_dataObject.get(), success) : getPlainText(&m_dragDataMap);
    462     if (dataType == ClipboardDataTypeURL)
    463         return m_dataObject ? getURL(m_dataObject.get(), DragData::DoNotConvertFilenames, success) : getURL(&m_dragDataMap, DragData::DoNotConvertFilenames);
    464     else if (dataType == ClipboardDataTypeTextHTML) {
    465         String data = m_dataObject ? getTextHTML(m_dataObject.get(), success) : getTextHTML(&m_dragDataMap);
    466         if (success)
    467             return data;
    468         return m_dataObject ? getCFHTML(m_dataObject.get(), success) : getCFHTML(&m_dragDataMap);
    469     }
    470 
    471     return "";
    472 }
    473 
    474 bool ClipboardWin::setData(const String& type, const String& data)
    475 {
    476     // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
    477     ASSERT(isForDragAndDrop());
    478     if (policy() != ClipboardWritable || !m_writableDataObject)
    479         return false;
    480 
    481     ClipboardDataType winType = clipboardTypeFromMIMEType(type);
    482 
    483     if (winType == ClipboardDataTypeURL)
    484         return WebCore::writeURL(m_writableDataObject.get(), KURL(ParsedURLString, data), String(), false, true);
    485 
    486     if (winType == ClipboardDataTypeText) {
    487         STGMEDIUM medium = {0};
    488         medium.tymed = TYMED_HGLOBAL;
    489         medium.hGlobal = createGlobalData(data);
    490         if (!medium.hGlobal)
    491             return false;
    492 
    493         if (FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) {
    494             ::GlobalFree(medium.hGlobal);
    495             return false;
    496         }
    497         return true;
    498     }
    499 
    500     return false;
    501 }
    502 
    503 static void addMimeTypesForFormat(HashSet<String>& results, const FORMATETC& format)
    504 {
    505     // URL and Text are provided for compatibility with IE's model
    506     if (format.cfFormat == urlFormat()->cfFormat || format.cfFormat == urlWFormat()->cfFormat) {
    507         results.add("URL");
    508         results.add("text/uri-list");
    509     }
    510 
    511     if (format.cfFormat == plainTextWFormat()->cfFormat || format.cfFormat == plainTextFormat()->cfFormat) {
    512         results.add("Text");
    513         results.add("text/plain");
    514     }
    515 }
    516 
    517 // extensions beyond IE's API
    518 HashSet<String> ClipboardWin::types() const
    519 {
    520     HashSet<String> results;
    521     if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
    522         return results;
    523 
    524     if (!m_dataObject && m_dragDataMap.isEmpty())
    525         return results;
    526 
    527     if (m_dataObject) {
    528         COMPtr<IEnumFORMATETC> itr;
    529 
    530         if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
    531             return results;
    532 
    533         if (!itr)
    534             return results;
    535 
    536         FORMATETC data;
    537 
    538         // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
    539         while (itr->Next(1, &data, 0) == S_OK)
    540             addMimeTypesForFormat(results, data);
    541     } else {
    542         for (DragDataMap::const_iterator it = m_dragDataMap.begin(); it != m_dragDataMap.end(); ++it) {
    543             FORMATETC data;
    544             data.cfFormat = (*it).first;
    545             addMimeTypesForFormat(results, data);
    546         }
    547     }
    548 
    549     return results;
    550 }
    551 
    552 PassRefPtr<FileList> ClipboardWin::files() const
    553 {
    554 #if OS(WINCE)
    555     notImplemented();
    556     return 0;
    557 #else
    558     RefPtr<FileList> files = FileList::create();
    559     if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
    560         return files.release();
    561 
    562     if (!m_dataObject && m_dragDataMap.isEmpty())
    563         return files.release();
    564 
    565     if (m_dataObject) {
    566         STGMEDIUM medium;
    567         if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium)))
    568             return files.release();
    569 
    570         HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal));
    571         if (!hdrop)
    572             return files.release();
    573 
    574         WCHAR filename[MAX_PATH];
    575         UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
    576         for (UINT i = 0; i < fileCount; i++) {
    577             if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename)))
    578                 continue;
    579             files->append(File::create(reinterpret_cast<UChar*>(filename)));
    580         }
    581 
    582         GlobalUnlock(medium.hGlobal);
    583         ReleaseStgMedium(&medium);
    584         return files.release();
    585     }
    586     if (!m_dragDataMap.contains(cfHDropFormat()->cfFormat))
    587         return files.release();
    588     Vector<String> filesVector = m_dragDataMap.get(cfHDropFormat()->cfFormat);
    589     for (Vector<String>::iterator it = filesVector.begin(); it != filesVector.end(); ++it)
    590         files->append(File::create((*it).characters()));
    591     return files.release();
    592 #endif
    593 }
    594 
    595 void ClipboardWin::setDragImage(CachedImage* image, Node *node, const IntPoint &loc)
    596 {
    597     if (policy() != ClipboardImageWritable && policy() != ClipboardWritable)
    598         return;
    599 
    600     if (m_dragImage)
    601         m_dragImage->removeClient(this);
    602     m_dragImage = image;
    603     if (m_dragImage)
    604         m_dragImage->addClient(this);
    605 
    606     m_dragLoc = loc;
    607     m_dragImageElement = node;
    608 }
    609 
    610 void ClipboardWin::setDragImage(CachedImage* img, const IntPoint &loc)
    611 {
    612     setDragImage(img, 0, loc);
    613 }
    614 
    615 void ClipboardWin::setDragImageElement(Node *node, const IntPoint &loc)
    616 {
    617     setDragImage(0, node, loc);
    618 }
    619 
    620 DragImageRef ClipboardWin::createDragImage(IntPoint& loc) const
    621 {
    622     HBITMAP result = 0;
    623     if (m_dragImage) {
    624         result = createDragImageFromImage(m_dragImage->image());
    625         loc = m_dragLoc;
    626     } else if (m_dragImageElement) {
    627         Node* node = m_dragImageElement.get();
    628         result = node->document()->frame()->nodeImage(node);
    629         loc = m_dragLoc;
    630     }
    631     return result;
    632 }
    633 
    634 static String imageToMarkup(const String& url)
    635 {
    636     String markup("<img src=\"");
    637     markup.append(url);
    638     markup.append("\"/>");
    639     return markup;
    640 }
    641 
    642 static CachedImage* getCachedImage(Element* element)
    643 {
    644     // Attempt to pull CachedImage from element
    645     ASSERT(element);
    646     RenderObject* renderer = element->renderer();
    647     if (!renderer || !renderer->isImage())
    648         return 0;
    649 
    650     RenderImage* image = toRenderImage(renderer);
    651     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
    652         return image->cachedImage();
    653 
    654     return 0;
    655 }
    656 
    657 static void writeImageToDataObject(IDataObject* dataObject, Element* element, const KURL& url)
    658 {
    659     // Shove image data into a DataObject for use as a file
    660     CachedImage* cachedImage = getCachedImage(element);
    661     if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded())
    662         return;
    663 
    664     SharedBuffer* imageBuffer = cachedImage->image()->data();
    665     if (!imageBuffer || !imageBuffer->size())
    666         return;
    667 
    668     HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element->getAttribute(altAttr), cachedImage);
    669     if (!imageFileDescriptor)
    670         return;
    671 
    672     HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer);
    673     if (!imageFileContent) {
    674         GlobalFree(imageFileDescriptor);
    675         return;
    676     }
    677 
    678     String fileName = cachedImage->response().suggestedFilename();
    679     HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer);
    680     if (!hDropContent) {
    681         GlobalFree(hDropContent);
    682         return;
    683     }
    684 
    685     writeFileToDataObject(dataObject, imageFileDescriptor, imageFileContent, hDropContent);
    686 }
    687 
    688 void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
    689 {
    690     // Order is important here for Explorer's sake
    691     if (!m_writableDataObject)
    692          return;
    693     WebCore::writeURL(m_writableDataObject.get(), url, title, true, false);
    694 
    695     writeImageToDataObject(m_writableDataObject.get(), element, url);
    696 
    697     AtomicString imageURL = element->getAttribute(srcAttr);
    698     if (imageURL.isEmpty())
    699         return;
    700 
    701     String fullURL = frame->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)).string();
    702     if (fullURL.isEmpty())
    703         return;
    704     STGMEDIUM medium = {0};
    705     medium.tymed = TYMED_HGLOBAL;
    706 
    707     // Put img tag on the clipboard referencing the image
    708     Vector<char> data;
    709     markupToCFHTML(imageToMarkup(fullURL), "", data);
    710     medium.hGlobal = createGlobalData(data);
    711     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
    712         ::GlobalFree(medium.hGlobal);
    713 }
    714 
    715 void ClipboardWin::writeURL(const KURL& kurl, const String& titleStr, Frame*)
    716 {
    717     if (!m_writableDataObject)
    718          return;
    719     WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true);
    720 
    721     String url = kurl.string();
    722     ASSERT(url.containsOnlyASCII()); // KURL::string() is URL encoded.
    723 
    724     String fsPath = filesystemPathFromUrlOrTitle(url, titleStr, L".URL", true);
    725     CString content = makeString("[InternetShortcut]\r\nURL=", url, "\r\n").ascii();
    726 
    727     if (fsPath.length() <= 0)
    728         return;
    729 
    730     HGLOBAL urlFileDescriptor = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
    731     if (!urlFileDescriptor)
    732         return;
    733 
    734     HGLOBAL urlFileContent = GlobalAlloc(GPTR, content.length());
    735     if (!urlFileContent) {
    736         GlobalFree(urlFileDescriptor);
    737         return;
    738     }
    739 
    740     FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(urlFileDescriptor));
    741     ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR));
    742     fgd->cItems = 1;
    743     fgd->fgd[0].dwFlags = FD_FILESIZE;
    744     fgd->fgd[0].nFileSizeLow = content.length();
    745 
    746     unsigned maxSize = min(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName));
    747     CopyMemory(fgd->fgd[0].cFileName, fsPath.characters(), maxSize * sizeof(UChar));
    748     GlobalUnlock(urlFileDescriptor);
    749 
    750     char* fileContents = static_cast<char*>(GlobalLock(urlFileContent));
    751     CopyMemory(fileContents, content.data(), content.length());
    752     GlobalUnlock(urlFileContent);
    753 
    754     writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0);
    755 }
    756 
    757 void ClipboardWin::writeRange(Range* selectedRange, Frame* frame)
    758 {
    759     ASSERT(selectedRange);
    760     if (!m_writableDataObject)
    761          return;
    762 
    763     STGMEDIUM medium = {0};
    764     medium.tymed = TYMED_HGLOBAL;
    765     ExceptionCode ec = 0;
    766 
    767     Vector<char> data;
    768     markupToCFHTML(createMarkup(selectedRange, 0, AnnotateForInterchange),
    769         selectedRange->startContainer(ec)->document()->url().string(), data);
    770     medium.hGlobal = createGlobalData(data);
    771     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
    772         ::GlobalFree(medium.hGlobal);
    773 
    774     String str = frame->editor()->selectedText();
    775     replaceNewlinesWithWindowsStyleNewlines(str);
    776     replaceNBSPWithSpace(str);
    777     medium.hGlobal = createGlobalData(str);
    778     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
    779         ::GlobalFree(medium.hGlobal);
    780 
    781     medium.hGlobal = 0;
    782     if (frame->editor()->canSmartCopyOrDelete())
    783         m_writableDataObject->SetData(smartPasteFormat(), &medium, TRUE);
    784 }
    785 
    786 void ClipboardWin::writePlainText(const String& text)
    787 {
    788     if (!m_writableDataObject)
    789         return;
    790 
    791     STGMEDIUM medium = {0};
    792     medium.tymed = TYMED_HGLOBAL;
    793 
    794     String str = text;
    795     replaceNewlinesWithWindowsStyleNewlines(str);
    796     replaceNBSPWithSpace(str);
    797     medium.hGlobal = createGlobalData(str);
    798     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
    799         ::GlobalFree(medium.hGlobal);
    800 
    801     medium.hGlobal = 0;
    802 }
    803 
    804 bool ClipboardWin::hasData()
    805 {
    806     if (!m_dataObject && m_dragDataMap.isEmpty())
    807         return false;
    808 
    809     if (m_dataObject) {
    810         COMPtr<IEnumFORMATETC> itr;
    811         if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
    812             return false;
    813 
    814         if (!itr)
    815             return false;
    816 
    817         FORMATETC data;
    818 
    819         // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
    820         if (itr->Next(1, &data, 0) == S_OK) {
    821             // There is at least one item in the IDataObject
    822             return true;
    823         }
    824 
    825         return false;
    826     }
    827     return !m_dragDataMap.isEmpty();
    828 }
    829 
    830 void ClipboardWin::setExternalDataObject(IDataObject *dataObject)
    831 {
    832     ASSERT(isForDragAndDrop());
    833 
    834     m_writableDataObject = 0;
    835     m_dataObject = dataObject;
    836 }
    837 
    838 } // namespace WebCore
    839