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