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