• 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     static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1;
121     bool usedURL = false;
122     WCHAR fsPathBuffer[MAX_PATH];
123     fsPathBuffer[0] = 0;
124     int extensionLen = extension ? lstrlen(extension) : 0;
125     int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen;
126 
127     if (!title.isEmpty()) {
128         size_t len = min<size_t>(title.length(), fsPathMaxLengthExcludingExtension);
129         CopyMemory(fsPathBuffer, title.characters(), len * sizeof(UChar));
130         fsPathBuffer[len] = 0;
131         pathRemoveBadFSCharacters(fsPathBuffer, len);
132     }
133 
134     if (!lstrlen(fsPathBuffer)) {
135         KURL kurl(ParsedURLString, url);
136         usedURL = true;
137         // The filename for any content based drag or file url should be the last element of
138         // the path.  If we can't find it, or we're coming up with the name for a link
139         // we just use the entire url.
140         DWORD len = fsPathMaxLengthExcludingExtension;
141         String lastComponent = kurl.lastPathComponent();
142         if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) {
143             len = min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length());
144             CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeof(UChar));
145         } else {
146             len = min<DWORD>(fsPathMaxLengthExcludingExtension, url.length());
147             CopyMemory(fsPathBuffer, url.characters(), len * sizeof(UChar));
148         }
149         fsPathBuffer[len] = 0;
150         pathRemoveBadFSCharacters(fsPathBuffer, len);
151     }
152 
153     if (!extension)
154         return String(static_cast<UChar*>(fsPathBuffer));
155 
156     if (!isLink && usedURL) {
157         PathRenameExtension(fsPathBuffer, extension);
158         return String(static_cast<UChar*>(fsPathBuffer));
159     }
160 
161     String result(static_cast<UChar*>(fsPathBuffer));
162     result += String(static_cast<UChar*>(extension));
163     return result;
164 }
165 
createGlobalURLContent(const String & url,int estimatedFileSize)166 static HGLOBAL createGlobalURLContent(const String& url, int estimatedFileSize)
167 {
168     HRESULT hr = S_OK;
169     HGLOBAL memObj = 0;
170 
171     char* fileContents;
172     char ansiUrl[INTERNET_MAX_URL_LENGTH + 1];
173     // Used to generate the buffer. This is null terminated whereas the fileContents won't be.
174     char contentGenerationBuffer[INTERNET_MAX_URL_LENGTH + ARRAYSIZE(szShellDotUrlTemplate) + 1];
175 
176     if (estimatedFileSize > 0 && estimatedFileSize > ARRAYSIZE(contentGenerationBuffer))
177         return 0;
178 
179     int ansiUrlSize = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)url.characters(), url.length(), ansiUrl, ARRAYSIZE(ansiUrl) - 1, 0, 0);
180     if (!ansiUrlSize)
181         return 0;
182 
183     ansiUrl[ansiUrlSize] = 0;
184 
185     int fileSize = (int) (ansiUrlSize+strlen(szShellDotUrlTemplate)-2); // -2 to remove the %s
186     ASSERT(estimatedFileSize < 0 || fileSize == estimatedFileSize);
187 
188     memObj = GlobalAlloc(GPTR, fileSize);
189     if (!memObj)
190         return 0;
191 
192     fileContents = (PSTR)GlobalLock(memObj);
193 
194     sprintf_s(contentGenerationBuffer, ARRAYSIZE(contentGenerationBuffer), szShellDotUrlTemplate, ansiUrl);
195     CopyMemory(fileContents, contentGenerationBuffer, fileSize);
196 
197     GlobalUnlock(memObj);
198 
199     return memObj;
200 }
201 
createGlobalImageFileContent(SharedBuffer * data)202 static HGLOBAL createGlobalImageFileContent(SharedBuffer* data)
203 {
204     HGLOBAL memObj = GlobalAlloc(GPTR, data->size());
205     if (!memObj)
206         return 0;
207 
208     char* fileContents = (PSTR)GlobalLock(memObj);
209 
210     CopyMemory(fileContents, data->data(), data->size());
211 
212     GlobalUnlock(memObj);
213 
214     return memObj;
215 }
216 
createGlobalHDropContent(const KURL & url,String & fileName,SharedBuffer * data)217 static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, SharedBuffer* data)
218 {
219     if (fileName.isEmpty() || !data )
220         return 0;
221 
222     WCHAR filePath[MAX_PATH];
223 
224     if (url.isLocalFile()) {
225         String localPath = url.path();
226         // windows does not enjoy a leading slash on paths
227         if (localPath[0] == '/')
228             localPath = localPath.substring(1);
229         LPCTSTR localPathStr = localPath.charactersWithNullTermination();
230         if (wcslen(localPathStr) + 1 < MAX_PATH)
231             wcscpy_s(filePath, MAX_PATH, localPathStr);
232         else
233             return 0;
234     } else {
235         WCHAR tempPath[MAX_PATH];
236         WCHAR extension[MAX_PATH];
237         if (!::GetTempPath(ARRAYSIZE(tempPath), tempPath))
238             return 0;
239         if (!::PathAppend(tempPath, fileName.charactersWithNullTermination()))
240             return 0;
241         LPCWSTR foundExtension = ::PathFindExtension(tempPath);
242         if (foundExtension) {
243             if (wcscpy_s(extension, MAX_PATH, foundExtension))
244                 return 0;
245         } else
246             *extension = 0;
247         ::PathRemoveExtension(tempPath);
248         for (int i = 1; i < 10000; i++) {
249             if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1)
250                 return 0;
251             if (!::PathFileExists(filePath))
252                 break;
253         }
254         HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
255         if (tempFileHandle == INVALID_HANDLE_VALUE)
256             return 0;
257 
258         // Write the data to this temp file.
259         DWORD written;
260         BOOL tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0);
261         CloseHandle(tempFileHandle);
262         if (!tempWriteSucceeded)
263             return 0;
264     }
265 
266     SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2));
267     HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize);
268     if (!memObj)
269         return 0;
270 
271     DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj);
272     dropFiles->pFiles = sizeof(DROPFILES);
273     dropFiles->fWide = TRUE;
274     wcscpy((LPWSTR)(dropFiles + 1), filePath);
275     GlobalUnlock(memObj);
276 
277     return memObj;
278 }
279 
createGlobalUrlFileDescriptor(const String & url,const String & title,int & estimatedSize)280 static HGLOBAL createGlobalUrlFileDescriptor(const String& url, const String& title, int& /*out*/ estimatedSize)
281 {
282     HRESULT hr = S_OK;
283     HGLOBAL memObj = 0;
284     String fsPath;
285     memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
286     if (!memObj)
287         return 0;
288 
289     FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
290     memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
291     fgd->cItems = 1;
292     fgd->fgd[0].dwFlags = FD_FILESIZE;
293     int fileSize = ::WideCharToMultiByte(CP_ACP, 0, url.characters(), url.length(), 0, 0, 0, 0);
294     fileSize += strlen(szShellDotUrlTemplate) - 2;  // -2 is for getting rid of %s in the template string
295     fgd->fgd[0].nFileSizeLow = fileSize;
296     estimatedSize = fileSize;
297     fsPath = filesystemPathFromUrlOrTitle(url, title, L".URL", true);
298 
299     if (fsPath.length() <= 0) {
300         GlobalUnlock(memObj);
301         GlobalFree(memObj);
302         return 0;
303     }
304 
305     int maxSize = min(fsPath.length(), ARRAYSIZE(fgd->fgd[0].cFileName));
306     CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * sizeof(UChar));
307     GlobalUnlock(memObj);
308 
309     return memObj;
310 }
311 
312 
createGlobalImageFileDescriptor(const String & url,const String & title,CachedImage * image)313 static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image)
314 {
315     ASSERT_ARG(image, image);
316     ASSERT(image->image()->data());
317 
318     HRESULT hr = S_OK;
319     HGLOBAL memObj = 0;
320     String fsPath;
321     memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR));
322     if (!memObj)
323         return 0;
324 
325     FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj);
326     memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR));
327     fgd->cItems = 1;
328     fgd->fgd[0].dwFlags = FD_FILESIZE;
329     fgd->fgd[0].nFileSizeLow = image->image()->data()->size();
330 
331     const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title;
332     String extension = image->image()->filenameExtension();
333     if (extension.isEmpty()) {
334         // Do not continue processing in the rare and unusual case where a decoded image is not able
335         // to provide a filename extension. Something tricky (like a bait-n-switch) is going on
336         return 0;
337     }
338     extension.insert(".", 0);
339     fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, (TCHAR*)extension.charactersWithNullTermination(), 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
writeFileToDataObject(IDataObject * dataObject,HGLOBAL fileDescriptor,HGLOBAL fileContent,HGLOBAL hDropContent)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 
ClipboardWin(bool isForDragging,IDataObject * dataObject,ClipboardAccessPolicy policy)398 ClipboardWin::ClipboardWin(bool isForDragging, IDataObject* dataObject, ClipboardAccessPolicy policy)
399     : Clipboard(policy, isForDragging)
400     , m_dataObject(dataObject)
401     , m_writableDataObject(0)
402 {
403 }
404 
ClipboardWin(bool isForDragging,WCDataObject * dataObject,ClipboardAccessPolicy policy)405 ClipboardWin::ClipboardWin(bool isForDragging, WCDataObject* dataObject, ClipboardAccessPolicy policy)
406     : Clipboard(policy, isForDragging)
407     , m_dataObject(dataObject)
408     , m_writableDataObject(dataObject)
409 {
410 }
411 
~ClipboardWin()412 ClipboardWin::~ClipboardWin()
413 {
414 }
415 
writeURL(WCDataObject * data,const KURL & url,String title,bool withPlainText,bool withHTML)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         Vector<char> cfhtmlData;
441         markupToCF_HTML(urlToMarkup(url, title), "", cfhtmlData);
442         medium.hGlobal = createGlobalData(cfhtmlData);
443         if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE)))
444             ::GlobalFree(medium.hGlobal);
445         else
446             success = true;
447     }
448 
449     if (withPlainText) {
450         medium.hGlobal = createGlobalData(url.string());
451         if (medium.hGlobal && FAILED(data->SetData(plainTextWFormat(), &medium, TRUE)))
452             ::GlobalFree(medium.hGlobal);
453         else
454             success = true;
455     }
456 
457     return success;
458 }
459 
clearData(const String & type)460 void ClipboardWin::clearData(const String& type)
461 {
462     //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
463     ASSERT(isForDragging());
464     if (policy() != ClipboardWritable || !m_writableDataObject)
465         return;
466 
467     ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
468 
469     if (dataType == ClipboardDataTypeURL) {
470         m_writableDataObject->clearData(urlWFormat()->cfFormat);
471         m_writableDataObject->clearData(urlFormat()->cfFormat);
472     }
473     if (dataType == ClipboardDataTypeText) {
474         m_writableDataObject->clearData(plainTextFormat()->cfFormat);
475         m_writableDataObject->clearData(plainTextWFormat()->cfFormat);
476     }
477 
478 }
479 
clearAllData()480 void ClipboardWin::clearAllData()
481 {
482     //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
483     ASSERT(isForDragging());
484     if (policy() != ClipboardWritable)
485         return;
486 
487     m_writableDataObject = 0;
488     WCDataObject::createInstance(&m_writableDataObject);
489     m_dataObject = m_writableDataObject;
490 }
491 
getData(const String & type,bool & success) const492 String ClipboardWin::getData(const String& type, bool& success) const
493 {
494     success = false;
495     if (policy() != ClipboardReadable || !m_dataObject) {
496         return "";
497     }
498 
499     ClipboardDataType dataType = clipboardTypeFromMIMEType(type);
500     if (dataType == ClipboardDataTypeText)
501         return getPlainText(m_dataObject.get(), success);
502     else if (dataType == ClipboardDataTypeURL)
503         return getURL(m_dataObject.get(), success);
504 
505     return "";
506 }
507 
setData(const String & type,const String & data)508 bool ClipboardWin::setData(const String& type, const String& data)
509 {
510     // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941>
511     ASSERT(isForDragging());
512     if (policy() != ClipboardWritable || !m_writableDataObject)
513         return false;
514 
515     ClipboardDataType winType = clipboardTypeFromMIMEType(type);
516 
517     if (winType == ClipboardDataTypeURL)
518         return WebCore::writeURL(m_writableDataObject.get(), KURL(ParsedURLString, data), String(), false, true);
519 
520     if (winType == ClipboardDataTypeText) {
521         STGMEDIUM medium = {0};
522         medium.tymed = TYMED_HGLOBAL;
523         medium.hGlobal = createGlobalData(data);
524         if (!medium.hGlobal)
525             return false;
526 
527         if (FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) {
528             ::GlobalFree(medium.hGlobal);
529             return false;
530         }
531         return true;
532     }
533 
534     return false;
535 }
536 
addMimeTypesForFormat(HashSet<String> & results,const FORMATETC & format)537 static void addMimeTypesForFormat(HashSet<String>& results, const FORMATETC& format)
538 {
539     // URL and Text are provided for compatibility with IE's model
540     if (format.cfFormat == urlFormat()->cfFormat || format.cfFormat == urlWFormat()->cfFormat) {
541         results.add("URL");
542         results.add("text/uri-list");
543     }
544 
545     if (format.cfFormat == plainTextWFormat()->cfFormat || format.cfFormat == plainTextFormat()->cfFormat) {
546         results.add("Text");
547         results.add("text/plain");
548     }
549 }
550 
551 // extensions beyond IE's API
types() const552 HashSet<String> ClipboardWin::types() const
553 {
554     HashSet<String> results;
555     if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
556         return results;
557 
558     if (!m_dataObject)
559         return results;
560 
561     COMPtr<IEnumFORMATETC> itr;
562 
563     if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
564         return results;
565 
566     if (!itr)
567         return results;
568 
569     FORMATETC data;
570 
571     // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
572     while (itr->Next(1, &data, 0) == S_OK) {
573         addMimeTypesForFormat(results, data);
574     }
575 
576     return results;
577 }
578 
files() const579 PassRefPtr<FileList> ClipboardWin::files() const
580 {
581     RefPtr<FileList> files = FileList::create();
582     if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable)
583         return files.release();
584 
585     if (!m_dataObject)
586         return files.release();
587 
588     STGMEDIUM medium;
589     if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium)))
590         return files.release();
591 
592     HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal));
593     if (!hdrop)
594         return files.release();
595 
596     WCHAR filename[MAX_PATH];
597     UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0);
598     for (UINT i = 0; i < fileCount; i++) {
599         if (!DragQueryFileW(hdrop, i, filename, ARRAYSIZE(filename)))
600             continue;
601         files->append(File::create(reinterpret_cast<UChar*>(filename)));
602     }
603 
604     GlobalUnlock(medium.hGlobal);
605     ReleaseStgMedium(&medium);
606     return files.release();
607 }
608 
setDragImage(CachedImage * image,Node * node,const IntPoint & loc)609 void ClipboardWin::setDragImage(CachedImage* image, Node *node, const IntPoint &loc)
610 {
611     if (policy() != ClipboardImageWritable && policy() != ClipboardWritable)
612         return;
613 
614     if (m_dragImage)
615         m_dragImage->removeClient(this);
616     m_dragImage = image;
617     if (m_dragImage)
618         m_dragImage->addClient(this);
619 
620     m_dragLoc = loc;
621     m_dragImageElement = node;
622 }
623 
setDragImage(CachedImage * img,const IntPoint & loc)624 void ClipboardWin::setDragImage(CachedImage* img, const IntPoint &loc)
625 {
626     setDragImage(img, 0, loc);
627 }
628 
setDragImageElement(Node * node,const IntPoint & loc)629 void ClipboardWin::setDragImageElement(Node *node, const IntPoint &loc)
630 {
631     setDragImage(0, node, loc);
632 }
633 
createDragImage(IntPoint & loc) const634 DragImageRef ClipboardWin::createDragImage(IntPoint& loc) const
635 {
636     HBITMAP result = 0;
637     if (m_dragImage) {
638         result = createDragImageFromImage(m_dragImage->image());
639         loc = m_dragLoc;
640     } else if (m_dragImageElement) {
641         Node* node = m_dragImageElement.get();
642         result = node->document()->frame()->nodeImage(node);
643         loc = m_dragLoc;
644     }
645     return result;
646 }
647 
imageToMarkup(const String & url)648 static String imageToMarkup(const String& url)
649 {
650     String markup("<img src=\"");
651     markup.append(url);
652     markup.append("\"/>");
653     return markup;
654 }
655 
getCachedImage(Element * element)656 static CachedImage* getCachedImage(Element* element)
657 {
658     // Attempt to pull CachedImage from element
659     ASSERT(element);
660     RenderObject* renderer = element->renderer();
661     if (!renderer || !renderer->isImage())
662         return 0;
663 
664     RenderImage* image = toRenderImage(renderer);
665     if (image->cachedImage() && !image->cachedImage()->errorOccurred())
666         return image->cachedImage();
667 
668     return 0;
669 }
670 
writeImageToDataObject(IDataObject * dataObject,Element * element,const KURL & url)671 static void writeImageToDataObject(IDataObject* dataObject, Element* element, const KURL& url)
672 {
673     // Shove image data into a DataObject for use as a file
674     CachedImage* cachedImage = getCachedImage(element);
675     if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded())
676         return;
677 
678     SharedBuffer* imageBuffer = cachedImage->image()->data();
679     if (!imageBuffer || !imageBuffer->size())
680         return;
681 
682     HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element->getAttribute(altAttr), cachedImage);
683     if (!imageFileDescriptor)
684         return;
685 
686     HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer);
687     if (!imageFileContent) {
688         GlobalFree(imageFileDescriptor);
689         return;
690     }
691 
692     String fileName = cachedImage->response().suggestedFilename();
693     HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer);
694     if (!hDropContent) {
695         GlobalFree(hDropContent);
696         return;
697     }
698 
699     writeFileToDataObject(dataObject, imageFileDescriptor, imageFileContent, hDropContent);
700 }
701 
declareAndWriteDragImage(Element * element,const KURL & url,const String & title,Frame * frame)702 void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame)
703 {
704     // Order is important here for Explorer's sake
705     if (!m_writableDataObject)
706          return;
707     WebCore::writeURL(m_writableDataObject.get(), url, title, true, false);
708 
709     writeImageToDataObject(m_writableDataObject.get(), element, url);
710 
711     AtomicString imageURL = element->getAttribute(srcAttr);
712     if (imageURL.isEmpty())
713         return;
714 
715     String fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL)).string();
716     if (fullURL.isEmpty())
717         return;
718     STGMEDIUM medium = {0};
719     medium.tymed = TYMED_HGLOBAL;
720     ExceptionCode ec = 0;
721 
722     // Put img tag on the clipboard referencing the image
723     Vector<char> data;
724     markupToCF_HTML(imageToMarkup(fullURL), "", data);
725     medium.hGlobal = createGlobalData(data);
726     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
727         ::GlobalFree(medium.hGlobal);
728 }
729 
writeURL(const KURL & kurl,const String & titleStr,Frame *)730 void ClipboardWin::writeURL(const KURL& kurl, const String& titleStr, Frame*)
731 {
732     if (!m_writableDataObject)
733          return;
734     WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true);
735 
736     int estimatedSize = 0;
737     String url = kurl.string();
738 
739     HGLOBAL urlFileDescriptor = createGlobalUrlFileDescriptor(url, titleStr, estimatedSize);
740     if (!urlFileDescriptor)
741         return;
742     HGLOBAL urlFileContent = createGlobalURLContent(url, estimatedSize);
743     if (!urlFileContent) {
744         GlobalFree(urlFileDescriptor);
745         return;
746     }
747     writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0);
748 }
749 
writeRange(Range * selectedRange,Frame * frame)750 void ClipboardWin::writeRange(Range* selectedRange, Frame* frame)
751 {
752     ASSERT(selectedRange);
753     if (!m_writableDataObject)
754          return;
755 
756     STGMEDIUM medium = {0};
757     medium.tymed = TYMED_HGLOBAL;
758     ExceptionCode ec = 0;
759 
760     Vector<char> data;
761     markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange),
762         selectedRange->startContainer(ec)->document()->url().string(), data);
763     medium.hGlobal = createGlobalData(data);
764     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE)))
765         ::GlobalFree(medium.hGlobal);
766 
767     String str = frame->selectedText();
768     replaceNewlinesWithWindowsStyleNewlines(str);
769     replaceNBSPWithSpace(str);
770     medium.hGlobal = createGlobalData(str);
771     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
772         ::GlobalFree(medium.hGlobal);
773 
774     medium.hGlobal = 0;
775     if (frame->editor()->canSmartCopyOrDelete())
776         m_writableDataObject->SetData(smartPasteFormat(), &medium, TRUE);
777 }
778 
writePlainText(const String & text)779 void ClipboardWin::writePlainText(const String& text)
780 {
781     if (!m_writableDataObject)
782         return;
783 
784     STGMEDIUM medium = {0};
785     medium.tymed = TYMED_HGLOBAL;
786     ExceptionCode ec = 0;
787 
788     String str = text;
789     replaceNewlinesWithWindowsStyleNewlines(str);
790     replaceNBSPWithSpace(str);
791     medium.hGlobal = createGlobalData(str);
792     if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE)))
793         ::GlobalFree(medium.hGlobal);
794 
795     medium.hGlobal = 0;
796 }
797 
hasData()798 bool ClipboardWin::hasData()
799 {
800     if (!m_dataObject)
801         return false;
802 
803     COMPtr<IEnumFORMATETC> itr;
804     if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr)))
805         return false;
806 
807     if (!itr)
808         return false;
809 
810     FORMATETC data;
811 
812     // IEnumFORMATETC::Next returns S_FALSE if there are no more items.
813     if (itr->Next(1, &data, 0) == S_OK) {
814         // There is at least one item in the IDataObject
815         return true;
816     }
817 
818     return false;
819 }
820 
setExternalDataObject(IDataObject * dataObject)821 void ClipboardWin::setExternalDataObject(IDataObject *dataObject)
822 {
823     ASSERT(isForDragging());
824 
825     m_writableDataObject = 0;
826     m_dataObject = dataObject;
827 }
828 
829 } // namespace WebCore
830