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