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