OLD | NEW |
| (Empty) |
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 <shlwapi.h> | |
28 #include <wininet.h> | |
29 | |
30 #pragma warning(push, 0) | |
31 #include "ClipboardWin.h" | |
32 #include "CachedImage.h" | |
33 #include "ClipboardUtilitiesWin.h" | |
34 #include "csshelper.h" | |
35 #include "CString.h" | |
36 #include "Document.h" | |
37 #include "DragData.h" | |
38 #include "Editor.h" | |
39 #include "Element.h" | |
40 #include "EventHandler.h" | |
41 #include "Frame.h" | |
42 #include "FrameLoader.h" | |
43 #include "FrameView.h" | |
44 #include "HTMLNames.h" | |
45 #include "Image.h" | |
46 #include "MIMETypeRegistry.h" | |
47 #include "markup.h" | |
48 #include "Page.h" | |
49 #include "Pasteboard.h" | |
50 #include "PlatformMouseEvent.h" | |
51 #include "PlatformString.h" | |
52 #include "Range.h" | |
53 #include "RenderImage.h" | |
54 #include "ResourceResponse.h" | |
55 #include "StringBuilder.h" | |
56 #include "StringHash.h" | |
57 #include "WCDataObject.h" | |
58 #include <wtf/RefPtr.h> | |
59 #pragma warning(pop) | |
60 | |
61 #undef LOG | |
62 #include "base/clipboard_util.h" | |
63 #include "base/string_util.h" | |
64 #include "googleurl/src/gurl.h" | |
65 #include "webkit/glue/glue_util.h" | |
66 #include "webkit/glue/webkit_glue.h" | |
67 | |
68 namespace WebCore { | |
69 | |
70 using namespace HTMLNames; | |
71 | |
72 // format string for | |
73 static const char szShellDotUrlTemplate[] = "[InternetShortcut]\r\nURL=%s\r\n"; | |
74 | |
75 // We provide the IE clipboard types (URL and Text), and the clipboard types spe
cified in the WHATWG Web Applications 1.0 draft | |
76 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 | |
77 | |
78 enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardD
ataTypeText }; | |
79 | |
80 static ClipboardDataType clipboardTypeFromMIMEType(const String& type) | |
81 { | |
82 String qType = type.stripWhiteSpace().lower(); | |
83 | |
84 // two special cases for IE compatibility | |
85 if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain
;")) | |
86 return ClipboardDataTypeText; | |
87 if (qType == "url" || qType == "text/uri-list") | |
88 return ClipboardDataTypeURL; | |
89 | |
90 return ClipboardDataTypeNone; | |
91 } | |
92 | |
93 static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length) | |
94 { | |
95 size_t writeTo = 0; | |
96 size_t readFrom = 0; | |
97 while (readFrom < length) { | |
98 UINT type = PathGetCharType(psz[readFrom]); | |
99 if (psz[readFrom] == 0 || type & (GCT_LFNCHAR | GCT_SHORTCHAR)) { | |
100 psz[writeTo++] = psz[readFrom]; | |
101 } | |
102 | |
103 readFrom++; | |
104 } | |
105 psz[writeTo] = 0; | |
106 } | |
107 | |
108 static String filesystemPathFromUrlOrTitle(const String& url, const String& titl
e, TCHAR* extension, bool isLink) | |
109 { | |
110 bool usedURL = false; | |
111 WCHAR fsPathBuffer[MAX_PATH]; | |
112 fsPathBuffer[0] = 0; | |
113 int extensionLen = extension ? min(MAX_PATH - 1, lstrlen(extension)) : 0; | |
114 String extensionString = extension ? String(extension, extensionLen) : Strin
g(L""); | |
115 | |
116 // We make the filename based on the title only if there's an extension. | |
117 if (!title.isEmpty() && extension) { | |
118 size_t len = min<size_t>(title.length(), MAX_PATH - extensionLen - 1); | |
119 CopyMemory(fsPathBuffer, title.characters(), len * sizeof(UChar)); | |
120 fsPathBuffer[len] = 0; | |
121 pathRemoveBadFSCharacters(fsPathBuffer, len); | |
122 } | |
123 | |
124 if (!lstrlen(fsPathBuffer)) { | |
125 DWORD len = MAX_PATH - 1; | |
126 String nullTermURL = url; | |
127 usedURL = true; | |
128 if (UrlIsFileUrl((LPCWSTR)nullTermURL.charactersWithNullTermination()) | |
129 && SUCCEEDED(PathCreateFromUrl((LPCWSTR)nullTermURL.charactersWithNu
llTermination(), fsPathBuffer, &len, 0))) { | |
130 // When linking to a file URL we can trivially find the file name | |
131 PWSTR fn = PathFindFileName(fsPathBuffer); | |
132 if (fn && fn != fsPathBuffer) | |
133 lstrcpyn(fsPathBuffer, fn, lstrlen(fn) + 1); | |
134 } else { | |
135 // The filename for any content based drag should be the last elemen
t of | |
136 // the path. If we can't find it, or we're coming up with the name
for a link | |
137 // we just use the entire url. | |
138 KURL kurl(url); | |
139 String lastComponent; | |
140 if (!isLink && !(lastComponent = kurl.lastPathComponent()).isEmpty()
) { | |
141 len = min<DWORD>(MAX_PATH - 1, lastComponent.length()); | |
142 CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeo
f(UChar)); | |
143 } else { | |
144 len = min<DWORD>(MAX_PATH - 1, nullTermURL.length()); | |
145 CopyMemory(fsPathBuffer, nullTermURL.characters(), len * sizeof(
UChar)); | |
146 } | |
147 fsPathBuffer[len] = 0; | |
148 pathRemoveBadFSCharacters(fsPathBuffer, len); | |
149 } | |
150 } | |
151 | |
152 if (!extension) | |
153 return String((UChar*)fsPathBuffer); | |
154 | |
155 if (!isLink && usedURL) { | |
156 PathRenameExtension(fsPathBuffer, extensionString.charactersWithNullTerm
ination()); | |
157 return String((UChar*)fsPathBuffer); | |
158 } | |
159 | |
160 String result((UChar*)fsPathBuffer); | |
161 result += extensionString; | |
162 return result.length() >= MAX_PATH ? result.substring(0, MAX_PATH - 1) : res
ult; | |
163 } | |
164 | |
165 static HGLOBAL createGlobalURLContent(const String& url, int estimatedFileSize) | |
166 { | |
167 HRESULT hr = S_OK; | |
168 HGLOBAL memObj = 0; | |
169 | |
170 char* fileContents; | |
171 char ansiUrl[INTERNET_MAX_URL_LENGTH + 1]; | |
172 // Used to generate the buffer. This is null terminated whereas the fileCont
ents won't be. | |
173 char contentGenerationBuffer[INTERNET_MAX_URL_LENGTH + ARRAYSIZE(szShellDotU
rlTemplate) + 1]; | |
174 | |
175 if (estimatedFileSize > 0 && estimatedFileSize > ARRAYSIZE(contentGeneration
Buffer)) | |
176 return 0; | |
177 | |
178 int ansiUrlSize = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)url.characters()
, url.length(), ansiUrl, ARRAYSIZE(ansiUrl) - 1, 0, 0); | |
179 if (!ansiUrlSize) | |
180 return 0; | |
181 | |
182 ansiUrl[ansiUrlSize] = 0; | |
183 | |
184 int fileSize = (int) (ansiUrlSize+strlen(szShellDotUrlTemplate)-2); // -2 to
remove the %s | |
185 ASSERT(estimatedFileSize < 0 || fileSize == estimatedFileSize); | |
186 | |
187 memObj = GlobalAlloc(GPTR, fileSize); | |
188 if (!memObj) | |
189 return 0; | |
190 | |
191 fileContents = (PSTR)GlobalLock(memObj); | |
192 | |
193 sprintf_s(contentGenerationBuffer, ARRAYSIZE(contentGenerationBuffer), szShe
llDotUrlTemplate, ansiUrl); | |
194 CopyMemory(fileContents, contentGenerationBuffer, fileSize); | |
195 | |
196 GlobalUnlock(memObj); | |
197 | |
198 return memObj; | |
199 } | |
200 | |
201 static HGLOBAL createGlobalImageFileContent(SharedBuffer* data) | |
202 { | |
203 HGLOBAL memObj = GlobalAlloc(GPTR, data->size()); | |
204 if (!memObj) | |
205 return 0; | |
206 | |
207 char* fileContents = (PSTR)GlobalLock(memObj); | |
208 | |
209 CopyMemory(fileContents, data->data(), data->size()); | |
210 | |
211 GlobalUnlock(memObj); | |
212 | |
213 return memObj; | |
214 } | |
215 | |
216 static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, Share
dBuffer* data) | |
217 { | |
218 if (fileName.isEmpty() || !data ) | |
219 return 0; | |
220 | |
221 WCHAR filePath[MAX_PATH]; | |
222 | |
223 if (url.isLocalFile()) { | |
224 String localPath = url.path(); | |
225 // windows does not enjoy a leading slash on paths | |
226 if (localPath[0] == '/') | |
227 localPath = localPath.substring(1); | |
228 LPCTSTR localPathStr = localPath.charactersWithNullTermination(); | |
229 if (wcslen(localPathStr) + 1 < MAX_PATH) | |
230 wcscpy_s(filePath, MAX_PATH, localPathStr); | |
231 else | |
232 return 0; | |
233 } else { | |
234 WCHAR tempPath[MAX_PATH]; | |
235 WCHAR extension[MAX_PATH]; | |
236 if (!::GetTempPath(ARRAYSIZE(tempPath), tempPath)) | |
237 return 0; | |
238 if (!::PathAppend(tempPath, fileName.charactersWithNullTermination())) | |
239 return 0; | |
240 LPCWSTR foundExtension = ::PathFindExtension(tempPath); | |
241 if (foundExtension) { | |
242 if (wcscpy_s(extension, MAX_PATH, foundExtension)) | |
243 return 0; | |
244 } else | |
245 *extension = 0; | |
246 ::PathRemoveExtension(tempPath); | |
247 for (int i = 1; i < 10000; i++) { | |
248 if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, ext
ension) == -1) | |
249 return 0; | |
250 if (!::PathFileExists(filePath)) | |
251 break; | |
252 } | |
253 HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRIT
E, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); | |
254 if (tempFileHandle == INVALID_HANDLE_VALUE) | |
255 return 0; | |
256 | |
257 // Write the data to this temp file. | |
258 DWORD written; | |
259 BOOL tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->
size(), &written, 0); | |
260 CloseHandle(tempFileHandle); | |
261 if (!tempWriteSucceeded) | |
262 return 0; | |
263 } | |
264 | |
265 SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath
) + 2)); | |
266 HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize); | |
267 if (!memObj) | |
268 return 0; | |
269 | |
270 DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj); | |
271 dropFiles->pFiles = sizeof(DROPFILES); | |
272 dropFiles->fWide = TRUE; | |
273 wcscpy((LPWSTR)(dropFiles + 1), filePath); | |
274 GlobalUnlock(memObj); | |
275 | |
276 return memObj; | |
277 } | |
278 | |
279 static HGLOBAL createGlobalUrlFileDescriptor(const String& url, const String& ti
tle, int& /*out*/ estimatedSize) | |
280 { | |
281 HRESULT hr = S_OK; | |
282 HGLOBAL memObj = 0; | |
283 String fsPath; | |
284 memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); | |
285 if (!memObj) | |
286 return 0; | |
287 | |
288 FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj); | |
289 memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR)); | |
290 fgd->cItems = 1; | |
291 fgd->fgd[0].dwFlags = FD_FILESIZE; | |
292 int fileSize = ::WideCharToMultiByte(CP_ACP, 0, url.characters(), url.length
(), 0, 0, 0, 0); | |
293 fileSize += strlen(szShellDotUrlTemplate) - 2; // -2 is for getting rid of
%s in the template string | |
294 fgd->fgd[0].nFileSizeLow = fileSize; | |
295 estimatedSize = fileSize; | |
296 fsPath = filesystemPathFromUrlOrTitle(url, title, L".URL", true); | |
297 | |
298 if (fsPath.length() <= 0) { | |
299 GlobalUnlock(memObj); | |
300 GlobalFree(memObj); | |
301 return 0; | |
302 } | |
303 | |
304 int maxSize = min(fsPath.length(), ARRAYSIZE(fgd->fgd[0].cFileName)); | |
305 CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * si
zeof(UChar)); | |
306 GlobalUnlock(memObj); | |
307 | |
308 return memObj; | |
309 } | |
310 | |
311 | |
312 static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String&
title, CachedImage* image) | |
313 { | |
314 ASSERT_ARG(image, image); | |
315 ASSERT(image->image()->data()); | |
316 | |
317 HRESULT hr = S_OK; | |
318 HGLOBAL memObj = 0; | |
319 String fsPath; | |
320 memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); | |
321 if (!memObj) | |
322 return 0; | |
323 | |
324 FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj); | |
325 memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR)); | |
326 fgd->cItems = 1; | |
327 fgd->fgd[0].dwFlags = FD_FILESIZE; | |
328 fgd->fgd[0].nFileSizeLow = image->image()->data()->size(); | |
329 | |
330 String extension("."); | |
331 extension += WebCore::MIMETypeRegistry::getPreferredExtensionForMIMEType(ima
ge->response().mimeType()); | |
332 const String& preferredTitle = title.isEmpty() ? image->response().suggested
Filename() : title; | |
333 fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.isEmpty
() ? 0 : (TCHAR*)extension.charactersWithNullTermination(), false); | |
334 | |
335 if (fsPath.length() <= 0) { | |
336 GlobalUnlock(memObj); | |
337 GlobalFree(memObj); | |
338 return 0; | |
339 } | |
340 | |
341 int maxSize = min(fsPath.length(), ARRAYSIZE(fgd->fgd[0].cFileName)); | |
342 CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * si
zeof(UChar)); | |
343 GlobalUnlock(memObj); | |
344 | |
345 return memObj; | |
346 } | |
347 | |
348 | |
349 // writeFileToDataObject takes ownership of fileDescriptor and fileContent | |
350 static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescri
ptor, HGLOBAL fileContent, HGLOBAL hDropContent) | |
351 { | |
352 HRESULT hr = S_OK; | |
353 FORMATETC* fe; | |
354 STGMEDIUM medium = {0}; | |
355 medium.tymed = TYMED_HGLOBAL; | |
356 | |
357 if (!fileDescriptor || !fileContent) | |
358 goto exit; | |
359 | |
360 // Descriptor | |
361 fe = ClipboardUtil::GetFileDescriptorFormat(); | |
362 | |
363 medium.hGlobal = fileDescriptor; | |
364 | |
365 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE))) | |
366 goto exit; | |
367 | |
368 // Contents | |
369 fe = ClipboardUtil::GetFileContentFormatZero(); | |
370 medium.hGlobal = fileContent; | |
371 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE))) | |
372 goto exit; | |
373 | |
374 // HDROP | |
375 if (hDropContent) { | |
376 medium.hGlobal = hDropContent; | |
377 hr = dataObject->SetData(ClipboardUtil::GetCFHDropFormat(), &medium, TRU
E); | |
378 } | |
379 | |
380 exit: | |
381 if (FAILED(hr)) { | |
382 if (fileDescriptor) | |
383 GlobalFree(fileDescriptor); | |
384 if (fileContent) | |
385 GlobalFree(fileContent); | |
386 if (hDropContent) | |
387 GlobalFree(hDropContent); | |
388 } | |
389 return hr; | |
390 } | |
391 | |
392 ClipboardWin::ClipboardWin(bool isForDragging, IDataObject* dataObject, Clipboar
dAccessPolicy policy) | |
393 : Clipboard(policy, isForDragging) | |
394 , m_dataObject(dataObject) | |
395 , m_writableDataObject(0) | |
396 { | |
397 } | |
398 | |
399 ClipboardWin::ClipboardWin(bool isForDragging, WCDataObject* dataObject, Clipboa
rdAccessPolicy policy) | |
400 : Clipboard(policy, isForDragging) | |
401 , m_dataObject(dataObject) | |
402 , m_writableDataObject(dataObject) | |
403 { | |
404 } | |
405 | |
406 ClipboardWin::~ClipboardWin() | |
407 { | |
408 } | |
409 | |
410 PassRefPtr<ClipboardWin> ClipboardWin::create(bool isForDragging, IDataObject* d
ataObject, ClipboardAccessPolicy policy) | |
411 { | |
412 return adoptRef(new ClipboardWin(isForDragging, dataObject, policy)); | |
413 } | |
414 | |
415 PassRefPtr<ClipboardWin> ClipboardWin::create(bool isForDragging, WCDataObject*
dataObject, ClipboardAccessPolicy policy) | |
416 { | |
417 return adoptRef(new ClipboardWin(isForDragging, dataObject, policy)); | |
418 } | |
419 | |
420 static bool writeURL(WCDataObject *data, const KURL& url, String title, bool wit
hPlainText, bool withHTML) | |
421 { | |
422 ASSERT(data); | |
423 | |
424 if (url.isEmpty()) | |
425 return false; | |
426 | |
427 if (title.isEmpty()) { | |
428 title = url.lastPathComponent(); | |
429 if (title.isEmpty()) | |
430 title = url.host(); | |
431 } | |
432 | |
433 STGMEDIUM medium = {0}; | |
434 medium.tymed = TYMED_HGLOBAL; | |
435 | |
436 medium.hGlobal = createGlobalData(url, title); | |
437 bool success = false; | |
438 if (medium.hGlobal && FAILED(data->SetData(ClipboardUtil::GetUrlWFormat(), &
medium, TRUE))) | |
439 ::GlobalFree(medium.hGlobal); | |
440 else | |
441 success = true; | |
442 | |
443 if (withHTML) { | |
444 Vector<char> cfhtmlData; | |
445 markupToCF_HTML(urlToMarkup(url, title), "", cfhtmlData); | |
446 medium.hGlobal = createGlobalData(cfhtmlData); | |
447 if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE))
) | |
448 ::GlobalFree(medium.hGlobal); | |
449 else | |
450 success = true; | |
451 } | |
452 | |
453 if (withPlainText) { | |
454 medium.hGlobal = createGlobalData(url.string()); | |
455 if (medium.hGlobal && FAILED(data->SetData(ClipboardUtil::GetPlainTextWF
ormat(), &medium, TRUE))) | |
456 ::GlobalFree(medium.hGlobal); | |
457 else | |
458 success = true; | |
459 } | |
460 | |
461 return success; | |
462 } | |
463 | |
464 void ClipboardWin::clearData(const String& type) | |
465 { | |
466 //FIXME: Need to be able to write to the system clipboard <rdar://problem/50
15941> | |
467 ASSERT(isForDragging()); | |
468 if (policy() != ClipboardWritable || !m_writableDataObject) | |
469 return; | |
470 | |
471 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); | |
472 | |
473 if (dataType == ClipboardDataTypeURL) { | |
474 m_writableDataObject->clearData(ClipboardUtil::GetUrlWFormat()->cfFormat
); | |
475 m_writableDataObject->clearData(ClipboardUtil::GetUrlFormat()->cfFormat)
; | |
476 } | |
477 if (dataType == ClipboardDataTypeText) { | |
478 m_writableDataObject->clearData(ClipboardUtil::GetPlainTextFormat()->cfF
ormat); | |
479 m_writableDataObject->clearData(ClipboardUtil::GetPlainTextWFormat()->cf
Format); | |
480 } | |
481 | |
482 } | |
483 | |
484 void ClipboardWin::clearAllData() | |
485 { | |
486 //FIXME: Need to be able to write to the system clipboard <rdar://problem/50
15941> | |
487 ASSERT(isForDragging()); | |
488 if (policy() != ClipboardWritable) | |
489 return; | |
490 | |
491 m_writableDataObject = 0; | |
492 WCDataObject::createInstance(&m_writableDataObject); | |
493 m_dataObject = m_writableDataObject; | |
494 } | |
495 | |
496 String ClipboardWin::getData(const String& type, bool& success) const | |
497 { | |
498 success = false; | |
499 if (policy() != ClipboardReadable || !m_dataObject) { | |
500 return ""; | |
501 } | |
502 | |
503 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); | |
504 if (dataType == ClipboardDataTypeText) { | |
505 std::wstring text; | |
506 if (!isForDragging()) { | |
507 // If this isn't for a drag, it's for a cut/paste event handler. | |
508 // In this case, we need to use our glue methods to access the | |
509 // clipboard contents. | |
510 webkit_glue::ClipboardReadText(&text); | |
511 if (text.empty()) { | |
512 std::string asciiText; | |
513 webkit_glue::ClipboardReadAsciiText(&asciiText); | |
514 text = ASCIIToWide(asciiText); | |
515 } | |
516 success = !text.empty(); | |
517 } else { | |
518 success = ClipboardUtil::GetPlainText(m_dataObject.get(), &text); | |
519 } | |
520 return webkit_glue::StdWStringToString(text); | |
521 } else if (dataType == ClipboardDataTypeURL) { | |
522 std::wstring url; | |
523 std::wstring title; | |
524 success = ClipboardUtil::GetUrl(m_dataObject.get(), &url, &title); | |
525 return webkit_glue::StdWStringToString(url); | |
526 } | |
527 | |
528 return ""; | |
529 } | |
530 | |
531 bool ClipboardWin::setData(const String& type, const String& data) | |
532 { | |
533 // FIXME: Need to be able to write to the system clipboard <rdar://problem/5
015941> | |
534 ASSERT(isForDragging()); | |
535 if (policy() != ClipboardWritable || !m_writableDataObject) | |
536 return false; | |
537 | |
538 ClipboardDataType winType = clipboardTypeFromMIMEType(type); | |
539 | |
540 if (winType == ClipboardDataTypeURL) | |
541 return WebCore::writeURL(m_writableDataObject.get(), KURL(data), String(
), false, true); | |
542 | |
543 if (winType == ClipboardDataTypeText) { | |
544 STGMEDIUM medium = {0}; | |
545 medium.tymed = TYMED_HGLOBAL; | |
546 medium.hGlobal = createGlobalData(data); | |
547 if (!medium.hGlobal) | |
548 return false; | |
549 | |
550 if (FAILED(m_writableDataObject->SetData(ClipboardUtil::GetPlainTextWFor
mat(), &medium, TRUE))) { | |
551 ::GlobalFree(medium.hGlobal); | |
552 return false; | |
553 } | |
554 return true; | |
555 } | |
556 return false; | |
557 } | |
558 | |
559 static void addMimeTypesForFormat(HashSet<String>& results, FORMATETC& format) | |
560 { | |
561 // URL and Text are provided for compatibility with IE's model | |
562 if (format.cfFormat == ClipboardUtil::GetUrlFormat()->cfFormat || | |
563 format.cfFormat == ClipboardUtil::GetUrlWFormat()->cfFormat) { | |
564 results.add("URL"); | |
565 results.add("text/uri-list"); | |
566 } | |
567 | |
568 if (format.cfFormat == ClipboardUtil::GetPlainTextWFormat()->cfFormat || | |
569 format.cfFormat == ClipboardUtil::GetPlainTextFormat()->cfFormat) { | |
570 results.add("Text"); | |
571 results.add("text/plain"); | |
572 } | |
573 } | |
574 | |
575 // extensions beyond IE's API | |
576 HashSet<String> ClipboardWin::types() const | |
577 { | |
578 HashSet<String> results; | |
579 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) | |
580 return results; | |
581 | |
582 if (!m_dataObject) | |
583 return results; | |
584 | |
585 COMPtr<IEnumFORMATETC> itr; | |
586 | |
587 if (FAILED(m_dataObject->EnumFormatEtc(0, &itr))) | |
588 return results; | |
589 | |
590 if (!itr) | |
591 return results; | |
592 | |
593 FORMATETC data; | |
594 | |
595 while (SUCCEEDED(itr->Next(1, &data, 0))) { | |
596 addMimeTypesForFormat(results, data); | |
597 } | |
598 | |
599 return results; | |
600 } | |
601 | |
602 void ClipboardWin::setDragImage(CachedImage* image, Node *node, const IntPoint &
loc) | |
603 { | |
604 if (policy() != ClipboardImageWritable && policy() != ClipboardWritable) | |
605 return; | |
606 | |
607 if (m_dragImage) | |
608 m_dragImage->removeClient(this); | |
609 m_dragImage = image; | |
610 if (m_dragImage) | |
611 m_dragImage->addClient(this); | |
612 | |
613 m_dragLoc = loc; | |
614 m_dragImageElement = node; | |
615 } | |
616 | |
617 void ClipboardWin::setDragImage(CachedImage* img, const IntPoint &loc) | |
618 { | |
619 setDragImage(img, 0, loc); | |
620 } | |
621 | |
622 void ClipboardWin::setDragImageElement(Node *node, const IntPoint &loc) | |
623 { | |
624 setDragImage(0, node, loc); | |
625 } | |
626 | |
627 DragImageRef ClipboardWin::createDragImage(IntPoint& loc) const | |
628 { | |
629 HBITMAP result = 0; | |
630 //FIXME: Need to be able to draw element <rdar://problem/5015942> | |
631 if (m_dragImage) { | |
632 result = createDragImageFromImage(m_dragImage->image()); | |
633 loc = m_dragLoc; | |
634 } | |
635 return result; | |
636 } | |
637 | |
638 static String imageToMarkup(const String& url, Element* element) | |
639 { | |
640 StringBuilder markup; | |
641 markup.append("<img src=\""); | |
642 markup.append(url); | |
643 markup.append("\""); | |
644 // Copy over attributes. If we are dragging an image, we expect things like | |
645 // the id to be copied as well. | |
646 NamedAttrMap* attrs = element->attributes(); | |
647 unsigned int length = attrs->length(); | |
648 for (unsigned int i = 0; i < length; ++i) { | |
649 Attribute* attr = attrs->attributeItem(i); | |
650 if (attr->localName() == "src") | |
651 continue; | |
652 markup.append(" "); | |
653 markup.append(attr->localName()); | |
654 markup.append("=\""); | |
655 String escapedAttr = attr->value(); | |
656 escapedAttr.replace("\"", """); | |
657 markup.append(escapedAttr); | |
658 markup.append("\""); | |
659 } | |
660 | |
661 markup.append("/>"); | |
662 return markup.toString(); | |
663 } | |
664 | |
665 static CachedImage* getCachedImage(Element* element) | |
666 { | |
667 // Attempt to pull CachedImage from element | |
668 ASSERT(element); | |
669 RenderObject* renderer = element->renderer(); | |
670 if (!renderer || !renderer->isImage()) | |
671 return 0; | |
672 | |
673 RenderImage* image = static_cast<RenderImage*>(renderer); | |
674 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) | |
675 return image->cachedImage(); | |
676 | |
677 return 0; | |
678 } | |
679 | |
680 static void writeImageToDataObject(IDataObject* dataObject, Element* element, co
nst KURL& url) | |
681 { | |
682 // Shove image data into a DataObject for use as a file | |
683 CachedImage* cachedImage = getCachedImage(element); | |
684 if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) | |
685 return; | |
686 | |
687 SharedBuffer* imageBuffer = cachedImage->image()->data(); | |
688 if (!imageBuffer || !imageBuffer->size()) | |
689 return; | |
690 | |
691 HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(),
element->getAttribute(altAttr), cachedImage); | |
692 if (!imageFileDescriptor) | |
693 return; | |
694 | |
695 HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer); | |
696 if (!imageFileContent) { | |
697 GlobalFree(imageFileDescriptor); | |
698 return; | |
699 } | |
700 | |
701 // When running in the sandbox, it's possible that hDropContent is NULL. | |
702 // That's ok, we should keep going anyway. | |
703 String fileName = cachedImage->response().suggestedFilename(); | |
704 HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer); | |
705 | |
706 writeFileToDataObject(dataObject, imageFileDescriptor, imageFileContent, hDr
opContent); | |
707 } | |
708 | |
709 void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, c
onst String& title, Frame* frame) | |
710 { | |
711 // Order is important here for Explorer's sake | |
712 if (!m_writableDataObject) | |
713 return; | |
714 WebCore::writeURL(m_writableDataObject.get(), url, title, true, false); | |
715 | |
716 writeImageToDataObject(m_writableDataObject.get(), element, url); | |
717 | |
718 AtomicString imageURL = element->getAttribute(srcAttr); | |
719 if (imageURL.isEmpty()) | |
720 return; | |
721 | |
722 String fullURL = frame->document()->completeURL(parseURL(imageURL)); | |
723 if (fullURL.isEmpty()) | |
724 return; | |
725 STGMEDIUM medium = {0}; | |
726 medium.tymed = TYMED_HGLOBAL; | |
727 ExceptionCode ec = 0; | |
728 | |
729 // Put img tag on the clipboard referencing the image | |
730 Vector<char> data; | |
731 markupToCF_HTML(imageToMarkup(fullURL, element), "", data); | |
732 medium.hGlobal = createGlobalData(data); | |
733 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &me
dium, TRUE))) | |
734 ::GlobalFree(medium.hGlobal); | |
735 } | |
736 | |
737 void ClipboardWin::writeURL(const KURL& kurl, const String& titleStr, Frame*) | |
738 { | |
739 if (!m_writableDataObject) | |
740 return; | |
741 WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true); | |
742 | |
743 int estimatedSize = 0; | |
744 String url = kurl.string(); | |
745 | |
746 HGLOBAL urlFileDescriptor = createGlobalUrlFileDescriptor(url, titleStr, est
imatedSize); | |
747 if (!urlFileDescriptor) | |
748 return; | |
749 HGLOBAL urlFileContent = createGlobalURLContent(url, estimatedSize); | |
750 if (!urlFileContent) { | |
751 GlobalFree(urlFileDescriptor); | |
752 return; | |
753 } | |
754 writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFile
Content, 0); | |
755 } | |
756 | |
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 markupToCF_HTML(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(), &me
dium, TRUE))) | |
772 ::GlobalFree(medium.hGlobal); | |
773 | |
774 String str = frame->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 | |
786 bool ClipboardWin::hasData() | |
787 { | |
788 if (!m_dataObject) | |
789 return false; | |
790 | |
791 COMPtr<IEnumFORMATETC> itr; | |
792 if (FAILED(m_dataObject->EnumFormatEtc(0, &itr))) | |
793 return false; | |
794 | |
795 if (!itr) | |
796 return false; | |
797 | |
798 FORMATETC data; | |
799 | |
800 if (SUCCEEDED(itr->Next(1, &data, 0))) { | |
801 // There is at least one item in the IDataObject | |
802 return true; | |
803 } | |
804 | |
805 return false; | |
806 } | |
807 | |
808 } // namespace WebCore | |
OLD | NEW |