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