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