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