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