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