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