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