OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2007, 2008 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 "ClipboardUtilitiesWin.h" | |
28 | |
29 #include "DocumentFragment.h" | |
30 #include "KURL.h" | |
31 #include "TextEncoding.h" | |
32 #include "markup.h" | |
33 #include <shlobj.h> | |
34 #include <wininet.h> // for INTERNET_MAX_URL_LENGTH | |
35 #include <wtf/StringExtras.h> | |
36 #include <wtf/text/CString.h> | |
37 #include <wtf/text/StringBuilder.h> | |
38 #include <wtf/text/WTFString.h> | |
39 | |
40 #if !OS(WINCE) | |
41 #include <shlwapi.h> | |
42 #endif | |
43 | |
44 #if USE(CF) | |
45 #include <CoreFoundation/CoreFoundation.h> | |
46 #include <wtf/RetainPtr.h> | |
47 #endif | |
48 | |
49 namespace WebCore { | |
50 | |
51 #if USE(CF) | |
52 FORMATETC* cfHDropFormat() | |
53 { | |
54 static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOB
AL}; | |
55 return &urlFormat; | |
56 } | |
57 | |
58 static bool urlFromPath(CFStringRef path, String& url) | |
59 { | |
60 if (!path) | |
61 return false; | |
62 | |
63 RetainPtr<CFURLRef> cfURL(AdoptCF, CFURLCreateWithFileSystemPath(0, path, kC
FURLWindowsPathStyle, false)); | |
64 if (!cfURL) | |
65 return false; | |
66 | |
67 url = CFURLGetString(cfURL.get()); | |
68 | |
69 // Work around <rdar://problem/6708300>, where CFURLCreateWithFileSystemPath
makes URLs with "localhost". | |
70 if (url.startsWith("file://localhost/")) | |
71 url.remove(7, 9); | |
72 | |
73 return true; | |
74 } | |
75 #endif | |
76 | |
77 static bool getDataMapItem(const DragDataMap* dataObject, FORMATETC* format, Str
ing& item) | |
78 { | |
79 DragDataMap::const_iterator found = dataObject->find(format->cfFormat); | |
80 if (found == dataObject->end()) | |
81 return false; | |
82 item = found->value[0]; | |
83 return true; | |
84 } | |
85 | |
86 static bool getWebLocData(IDataObject* dataObject, String& url, String* title) | |
87 { | |
88 bool succeeded = false; | |
89 #if USE(CF) | |
90 WCHAR filename[MAX_PATH]; | |
91 WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH]; | |
92 | |
93 STGMEDIUM medium; | |
94 if (FAILED(dataObject->GetData(cfHDropFormat(), &medium))) | |
95 return false; | |
96 | |
97 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal)); | |
98 | |
99 if (!hdrop) | |
100 return false; | |
101 | |
102 if (!DragQueryFileW(hdrop, 0, filename, WTF_ARRAY_LENGTH(filename))) | |
103 goto exit; | |
104 | |
105 if (_wcsicmp(PathFindExtensionW(filename), L".url")) | |
106 goto exit; | |
107 | |
108 if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF
_ARRAY_LENGTH(urlBuffer), filename)) | |
109 goto exit; | |
110 | |
111 if (title) { | |
112 PathRemoveExtension(filename); | |
113 *title = String((UChar*)filename); | |
114 } | |
115 | |
116 url = String((UChar*)urlBuffer); | |
117 succeeded = true; | |
118 | |
119 exit: | |
120 // Free up memory. | |
121 DragFinish(hdrop); | |
122 GlobalUnlock(medium.hGlobal); | |
123 #endif | |
124 return succeeded; | |
125 } | |
126 | |
127 static bool getWebLocData(const DragDataMap* dataObject, String& url, String* ti
tle) | |
128 { | |
129 #if USE(CF) | |
130 WCHAR filename[MAX_PATH]; | |
131 WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH]; | |
132 | |
133 if (!dataObject->contains(cfHDropFormat()->cfFormat)) | |
134 return false; | |
135 | |
136 wcscpy(filename, dataObject->get(cfHDropFormat()->cfFormat)[0].charactersWit
hNullTermination()); | |
137 if (_wcsicmp(PathFindExtensionW(filename), L".url")) | |
138 return false; | |
139 | |
140 if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF
_ARRAY_LENGTH(urlBuffer), filename)) | |
141 return false; | |
142 | |
143 if (title) { | |
144 PathRemoveExtension(filename); | |
145 *title = filename; | |
146 } | |
147 | |
148 url = urlBuffer; | |
149 return true; | |
150 #else | |
151 return false; | |
152 #endif | |
153 } | |
154 | |
155 static String extractURL(const String &inURL, String* title) | |
156 { | |
157 String url = inURL; | |
158 int splitLoc = url.find('\n'); | |
159 if (splitLoc > 0) { | |
160 if (title) | |
161 *title = url.substring(splitLoc+1); | |
162 url.truncate(splitLoc); | |
163 } else if (title) | |
164 *title = url; | |
165 return url; | |
166 } | |
167 | |
168 // Firefox text/html | |
169 static FORMATETC* texthtmlFormat() | |
170 { | |
171 static UINT cf = RegisterClipboardFormat(L"text/html"); | |
172 static FORMATETC texthtmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBA
L}; | |
173 return &texthtmlFormat; | |
174 } | |
175 | |
176 HGLOBAL createGlobalData(const KURL& url, const String& title) | |
177 { | |
178 String mutableURL(url.string()); | |
179 String mutableTitle(title); | |
180 SIZE_T size = mutableURL.length() + mutableTitle.length() + 2; // +1 for "\n
" and +1 for null terminator | |
181 HGLOBAL cbData = ::GlobalAlloc(GPTR, size * sizeof(UChar)); | |
182 | |
183 if (cbData) { | |
184 PWSTR buffer = static_cast<PWSTR>(GlobalLock(cbData)); | |
185 _snwprintf(buffer, size, L"%s\n%s", mutableURL.charactersWithNullTermina
tion(), mutableTitle.charactersWithNullTermination()); | |
186 GlobalUnlock(cbData); | |
187 } | |
188 return cbData; | |
189 } | |
190 | |
191 HGLOBAL createGlobalData(const String& str) | |
192 { | |
193 HGLOBAL globalData = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar))
; | |
194 if (!globalData) | |
195 return 0; | |
196 UChar* buffer = static_cast<UChar*>(GlobalLock(globalData)); | |
197 memcpy(buffer, str.characters(), str.length() * sizeof(UChar)); | |
198 buffer[str.length()] = 0; | |
199 GlobalUnlock(globalData); | |
200 return globalData; | |
201 } | |
202 | |
203 HGLOBAL createGlobalData(const Vector<char>& vector) | |
204 { | |
205 HGLOBAL globalData = ::GlobalAlloc(GPTR, vector.size() + 1); | |
206 if (!globalData) | |
207 return 0; | |
208 char* buffer = static_cast<char*>(GlobalLock(globalData)); | |
209 memcpy(buffer, vector.data(), vector.size()); | |
210 buffer[vector.size()] = 0; | |
211 GlobalUnlock(globalData); | |
212 return globalData; | |
213 } | |
214 | |
215 static String getFullCFHTML(IDataObject* data) | |
216 { | |
217 STGMEDIUM store; | |
218 if (SUCCEEDED(data->GetData(htmlFormat(), &store))) { | |
219 // MS HTML Format parsing | |
220 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
221 SIZE_T dataSize = ::GlobalSize(store.hGlobal); | |
222 String cfhtml(UTF8Encoding().decode(data, dataSize)); | |
223 GlobalUnlock(store.hGlobal); | |
224 ReleaseStgMedium(&store); | |
225 return cfhtml; | |
226 } | |
227 return String(); | |
228 } | |
229 | |
230 static void append(Vector<char>& vector, const char* string) | |
231 { | |
232 vector.append(string, strlen(string)); | |
233 } | |
234 | |
235 static void append(Vector<char>& vector, const CString& string) | |
236 { | |
237 vector.append(string.data(), string.length()); | |
238 } | |
239 | |
240 // Find the markup between "<!--StartFragment -->" and "<!--EndFragment -->", ac
counting for browser quirks. | |
241 static String extractMarkupFromCFHTML(const String& cfhtml) | |
242 { | |
243 unsigned markupStart = cfhtml.find("<html", 0, false); | |
244 unsigned tagStart = cfhtml.find("startfragment", markupStart, false); | |
245 unsigned fragmentStart = cfhtml.find('>', tagStart) + 1; | |
246 unsigned tagEnd = cfhtml.find("endfragment", fragmentStart, false); | |
247 unsigned fragmentEnd = cfhtml.reverseFind('<', tagEnd); | |
248 return cfhtml.substring(fragmentStart, fragmentEnd - fragmentStart).stripWhi
teSpace(); | |
249 } | |
250 | |
251 // Documentation for the CF_HTML format is available at http://msdn.microsoft.co
m/workshop/networking/clipboard/htmlclipboard.asp | |
252 void markupToCFHTML(const String& markup, const String& srcURL, Vector<char>& re
sult) | |
253 { | |
254 if (markup.isEmpty()) | |
255 return; | |
256 | |
257 #define MAX_DIGITS 10 | |
258 #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits) | |
259 #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u" | |
260 #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS) | |
261 | |
262 const char* header = "Version:0.9\n" | |
263 "StartHTML:" NUMBER_FORMAT "\n" | |
264 "EndHTML:" NUMBER_FORMAT "\n" | |
265 "StartFragment:" NUMBER_FORMAT "\n" | |
266 "EndFragment:" NUMBER_FORMAT "\n"; | |
267 const char* sourceURLPrefix = "SourceURL:"; | |
268 | |
269 const char* startMarkup = "<HTML>\n<BODY>\n<!--StartFragment-->\n"; | |
270 const char* endMarkup = "\n<!--EndFragment-->\n</BODY>\n</HTML>"; | |
271 | |
272 CString sourceURLUTF8 = srcURL == blankURL() ? "" : srcURL.utf8(); | |
273 CString markupUTF8 = markup.utf8(); | |
274 | |
275 // calculate offsets | |
276 unsigned startHTMLOffset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_
DIGITS * 4; | |
277 if (sourceURLUTF8.length()) | |
278 startHTMLOffset += strlen(sourceURLPrefix) + sourceURLUTF8.length() + 1; | |
279 unsigned startFragmentOffset = startHTMLOffset + strlen(startMarkup); | |
280 unsigned endFragmentOffset = startFragmentOffset + markupUTF8.length(); | |
281 unsigned endHTMLOffset = endFragmentOffset + strlen(endMarkup); | |
282 | |
283 unsigned headerBufferLength = startHTMLOffset + 1; // + 1 for '\0' terminato
r. | |
284 char* headerBuffer = (char*)malloc(headerBufferLength); | |
285 snprintf(headerBuffer, headerBufferLength, header, startHTMLOffset, endHTMLO
ffset, startFragmentOffset, endFragmentOffset); | |
286 append(result, CString(headerBuffer)); | |
287 free(headerBuffer); | |
288 if (sourceURLUTF8.length()) { | |
289 append(result, sourceURLPrefix); | |
290 append(result, sourceURLUTF8); | |
291 result.append('\n'); | |
292 } | |
293 append(result, startMarkup); | |
294 append(result, markupUTF8); | |
295 append(result, endMarkup); | |
296 | |
297 #undef MAX_DIGITS | |
298 #undef MAKE_NUMBER_FORMAT_1 | |
299 #undef MAKE_NUMBER_FORMAT_2 | |
300 #undef NUMBER_FORMAT | |
301 } | |
302 | |
303 void replaceNewlinesWithWindowsStyleNewlines(String& str) | |
304 { | |
305 DEFINE_STATIC_LOCAL(String, windowsNewline, (ASCIILiteral("\r\n"))); | |
306 StringBuilder result; | |
307 for (unsigned index = 0; index < str.length(); ++index) { | |
308 if (str[index] != '\n' || (index > 0 && str[index - 1] == '\r')) | |
309 result.append(str[index]); | |
310 else | |
311 result.append(windowsNewline); | |
312 } | |
313 str = result.toString(); | |
314 } | |
315 | |
316 void replaceNBSPWithSpace(String& str) | |
317 { | |
318 static const UChar NonBreakingSpaceCharacter = 0xA0; | |
319 static const UChar SpaceCharacter = ' '; | |
320 str.replace(NonBreakingSpaceCharacter, SpaceCharacter); | |
321 } | |
322 | |
323 FORMATETC* urlWFormat() | |
324 { | |
325 static UINT cf = RegisterClipboardFormat(L"UniformResourceLocatorW"); | |
326 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
327 return &urlFormat; | |
328 } | |
329 | |
330 FORMATETC* urlFormat() | |
331 { | |
332 static UINT cf = RegisterClipboardFormat(L"UniformResourceLocator"); | |
333 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
334 return &urlFormat; | |
335 } | |
336 | |
337 FORMATETC* plainTextFormat() | |
338 { | |
339 static FORMATETC textFormat = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOB
AL}; | |
340 return &textFormat; | |
341 } | |
342 | |
343 FORMATETC* plainTextWFormat() | |
344 { | |
345 static FORMATETC textFormat = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYME
D_HGLOBAL}; | |
346 return &textFormat; | |
347 } | |
348 | |
349 FORMATETC* filenameWFormat() | |
350 { | |
351 static UINT cf = RegisterClipboardFormat(L"FileNameW"); | |
352 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
353 return &urlFormat; | |
354 } | |
355 | |
356 FORMATETC* filenameFormat() | |
357 { | |
358 static UINT cf = RegisterClipboardFormat(L"FileName"); | |
359 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
360 return &urlFormat; | |
361 } | |
362 | |
363 // MSIE HTML Format | |
364 FORMATETC* htmlFormat() | |
365 { | |
366 static UINT cf = RegisterClipboardFormat(L"HTML Format"); | |
367 static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
368 return &htmlFormat; | |
369 } | |
370 | |
371 FORMATETC* smartPasteFormat() | |
372 { | |
373 static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format"); | |
374 static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
375 return &htmlFormat; | |
376 } | |
377 | |
378 FORMATETC* fileDescriptorFormat() | |
379 { | |
380 static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); | |
381 static FORMATETC fileDescriptorFormat = { cf, 0, DVASPECT_CONTENT, -1, TYMED
_HGLOBAL }; | |
382 return &fileDescriptorFormat; | |
383 } | |
384 | |
385 FORMATETC* fileContentFormatZero() | |
386 { | |
387 static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS); | |
388 static FORMATETC fileContentFormat = { cf, 0, DVASPECT_CONTENT, 0, TYMED_HGL
OBAL }; | |
389 return &fileContentFormat; | |
390 } | |
391 | |
392 void getFileDescriptorData(IDataObject* dataObject, int& size, String& pathname) | |
393 { | |
394 STGMEDIUM store; | |
395 size = 0; | |
396 if (FAILED(dataObject->GetData(fileDescriptorFormat(), &store))) | |
397 return; | |
398 | |
399 FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(stor
e.hGlobal)); | |
400 size = fgd->fgd[0].nFileSizeLow; | |
401 pathname = fgd->fgd[0].cFileName; | |
402 | |
403 GlobalUnlock(store.hGlobal); | |
404 ::ReleaseStgMedium(&store); | |
405 } | |
406 | |
407 void getFileContentData(IDataObject* dataObject, int size, void* dataBlob) | |
408 { | |
409 STGMEDIUM store; | |
410 if (FAILED(dataObject->GetData(fileContentFormatZero(), &store))) | |
411 return; | |
412 void* data = GlobalLock(store.hGlobal); | |
413 ::CopyMemory(dataBlob, data, size); | |
414 | |
415 GlobalUnlock(store.hGlobal); | |
416 ::ReleaseStgMedium(&store); | |
417 } | |
418 | |
419 void setFileDescriptorData(IDataObject* dataObject, int size, const String& pass
edPathname) | |
420 { | |
421 String pathname = passedPathname; | |
422 | |
423 STGMEDIUM medium = { 0 }; | |
424 medium.tymed = TYMED_HGLOBAL; | |
425 | |
426 medium.hGlobal = ::GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); | |
427 if (!medium.hGlobal) | |
428 return; | |
429 | |
430 FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(medi
um.hGlobal)); | |
431 ::ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR)); | |
432 fgd->cItems = 1; | |
433 fgd->fgd[0].dwFlags = FD_FILESIZE; | |
434 fgd->fgd[0].nFileSizeLow = size; | |
435 | |
436 int maxSize = std::min<int>(pathname.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].
cFileName)); | |
437 CopyMemory(fgd->fgd[0].cFileName, pathname.charactersWithNullTermination(),
maxSize * sizeof(UChar)); | |
438 GlobalUnlock(medium.hGlobal); | |
439 | |
440 dataObject->SetData(fileDescriptorFormat(), &medium, TRUE); | |
441 } | |
442 | |
443 void setFileContentData(IDataObject* dataObject, int size, void* dataBlob) | |
444 { | |
445 STGMEDIUM medium = { 0 }; | |
446 medium.tymed = TYMED_HGLOBAL; | |
447 | |
448 medium.hGlobal = ::GlobalAlloc(GPTR, size); | |
449 if (!medium.hGlobal) | |
450 return; | |
451 void* fileContents = GlobalLock(medium.hGlobal); | |
452 ::CopyMemory(fileContents, dataBlob, size); | |
453 GlobalUnlock(medium.hGlobal); | |
454 | |
455 dataObject->SetData(fileContentFormatZero(), &medium, TRUE); | |
456 } | |
457 | |
458 String getURL(IDataObject* dataObject, DragData::FilenameConversionPolicy filena
mePolicy, String* title) | |
459 { | |
460 STGMEDIUM store; | |
461 String url; | |
462 if (getWebLocData(dataObject, url, title)) | |
463 return url; | |
464 | |
465 if (SUCCEEDED(dataObject->GetData(urlWFormat(), &store))) { | |
466 // URL using Unicode | |
467 UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal)); | |
468 url = extractURL(String(data), title); | |
469 GlobalUnlock(store.hGlobal); | |
470 ReleaseStgMedium(&store); | |
471 } else if (SUCCEEDED(dataObject->GetData(urlFormat(), &store))) { | |
472 // URL using ASCII | |
473 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
474 url = extractURL(String(data), title); | |
475 GlobalUnlock(store.hGlobal); | |
476 ReleaseStgMedium(&store); | |
477 } | |
478 #if USE(CF) | |
479 else if (filenamePolicy == DragData::ConvertFilenames) { | |
480 if (SUCCEEDED(dataObject->GetData(filenameWFormat(), &store))) { | |
481 // file using unicode | |
482 wchar_t* data = static_cast<wchar_t*>(GlobalLock(store.hGlobal)); | |
483 if (data && data[0] && (PathFileExists(data) || PathIsUNC(data))) { | |
484 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWit
hCharacters(kCFAllocatorDefault, (const UniChar*)data, wcslen(data))); | |
485 if (urlFromPath(pathAsCFString.get(), url) && title) | |
486 *title = url; | |
487 } | |
488 GlobalUnlock(store.hGlobal); | |
489 ReleaseStgMedium(&store); | |
490 } else if (SUCCEEDED(dataObject->GetData(filenameFormat(), &store))) { | |
491 // filename using ascii | |
492 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
493 if (data && data[0] && (PathFileExistsA(data) || PathIsUNCA(data)))
{ | |
494 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWit
hCString(kCFAllocatorDefault, data, kCFStringEncodingASCII)); | |
495 if (urlFromPath(pathAsCFString.get(), url) && title) | |
496 *title = url; | |
497 } | |
498 GlobalUnlock(store.hGlobal); | |
499 ReleaseStgMedium(&store); | |
500 } | |
501 } | |
502 #endif | |
503 return url; | |
504 } | |
505 | |
506 String getURL(const DragDataMap* data, DragData::FilenameConversionPolicy filena
mePolicy, String* title) | |
507 { | |
508 String url; | |
509 | |
510 if (getWebLocData(data, url, title)) | |
511 return url; | |
512 if (getDataMapItem(data, urlWFormat(), url)) | |
513 return extractURL(url, title); | |
514 if (getDataMapItem(data, urlFormat(), url)) | |
515 return extractURL(url, title); | |
516 #if USE(CF) | |
517 if (filenamePolicy != DragData::ConvertFilenames) | |
518 return url; | |
519 | |
520 String stringData; | |
521 if (!getDataMapItem(data, filenameWFormat(), stringData)) | |
522 getDataMapItem(data, filenameFormat(), stringData); | |
523 | |
524 if (stringData.isEmpty() || (!PathFileExists(stringData.charactersWithNullTe
rmination()) && !PathIsUNC(stringData.charactersWithNullTermination()))) | |
525 return url; | |
526 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWithCharacters(
kCFAllocatorDefault, (const UniChar *)stringData.charactersWithNullTermination()
, wcslen(stringData.charactersWithNullTermination()))); | |
527 if (urlFromPath(pathAsCFString.get(), url) && title) | |
528 *title = url; | |
529 #endif | |
530 return url; | |
531 } | |
532 | |
533 String getPlainText(IDataObject* dataObject) | |
534 { | |
535 STGMEDIUM store; | |
536 String text; | |
537 if (SUCCEEDED(dataObject->GetData(plainTextWFormat(), &store))) { | |
538 // Unicode text | |
539 UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal)); | |
540 text = String(data); | |
541 GlobalUnlock(store.hGlobal); | |
542 ReleaseStgMedium(&store); | |
543 } else if (SUCCEEDED(dataObject->GetData(plainTextFormat(), &store))) { | |
544 // ASCII text | |
545 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
546 text = String(data); | |
547 GlobalUnlock(store.hGlobal); | |
548 ReleaseStgMedium(&store); | |
549 } else { | |
550 // FIXME: Originally, we called getURL() here because dragging and dropp
ing files doesn't | |
551 // populate the drag with text data. Per https://bugs.webkit.org/show_bu
g.cgi?id=38826, this | |
552 // is undesirable, so maybe this line can be removed. | |
553 text = getURL(dataObject, DragData::DoNotConvertFilenames); | |
554 } | |
555 return text; | |
556 } | |
557 | |
558 String getPlainText(const DragDataMap* data) | |
559 { | |
560 String text; | |
561 | |
562 if (getDataMapItem(data, plainTextWFormat(), text)) | |
563 return text; | |
564 if (getDataMapItem(data, plainTextFormat(), text)) | |
565 return text; | |
566 return getURL(data, DragData::DoNotConvertFilenames); | |
567 } | |
568 | |
569 String getTextHTML(IDataObject* data) | |
570 { | |
571 STGMEDIUM store; | |
572 String html; | |
573 if (SUCCEEDED(data->GetData(texthtmlFormat(), &store))) { | |
574 UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal)); | |
575 html = String(data); | |
576 GlobalUnlock(store.hGlobal); | |
577 ReleaseStgMedium(&store); | |
578 } | |
579 return html; | |
580 } | |
581 | |
582 String getTextHTML(const DragDataMap* data) | |
583 { | |
584 String text; | |
585 getDataMapItem(data, texthtmlFormat(), text); | |
586 return text; | |
587 } | |
588 | |
589 String getCFHTML(IDataObject* data) | |
590 { | |
591 String cfhtml = getFullCFHTML(data); | |
592 if (!cfhtml.isEmpty()) | |
593 return extractMarkupFromCFHTML(cfhtml); | |
594 return String(); | |
595 } | |
596 | |
597 String getCFHTML(const DragDataMap* dataMap) | |
598 { | |
599 String cfhtml; | |
600 getDataMapItem(dataMap, htmlFormat(), cfhtml); | |
601 return extractMarkupFromCFHTML(cfhtml); | |
602 } | |
603 | |
604 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*
) | |
605 { | |
606 // FIXME: We should be able to create fragments from files | |
607 return 0; | |
608 } | |
609 | |
610 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*
) | |
611 { | |
612 // FIXME: We should be able to create fragments from files | |
613 return 0; | |
614 } | |
615 | |
616 bool containsFilenames(const IDataObject*) | |
617 { | |
618 // FIXME: We'll want to update this once we can produce fragments from files | |
619 return false; | |
620 } | |
621 | |
622 bool containsFilenames(const DragDataMap*) | |
623 { | |
624 // FIXME: We'll want to update this once we can produce fragments from files | |
625 return false; | |
626 } | |
627 | |
628 // Convert a String containing CF_HTML formatted text to a DocumentFragment | |
629 PassRefPtr<DocumentFragment> fragmentFromCFHTML(Document* doc, const String& cfh
tml) | |
630 { | |
631 // obtain baseURL if present | |
632 String srcURLStr("sourceURL:"); | |
633 String srcURL; | |
634 unsigned lineStart = cfhtml.find(srcURLStr, 0, false); | |
635 if (lineStart != -1) { | |
636 unsigned srcEnd = cfhtml.find("\n", lineStart, false); | |
637 unsigned srcStart = lineStart+srcURLStr.length(); | |
638 String rawSrcURL = cfhtml.substring(srcStart, srcEnd-srcStart); | |
639 replaceNBSPWithSpace(rawSrcURL); | |
640 srcURL = rawSrcURL.stripWhiteSpace(); | |
641 } | |
642 | |
643 String markup = extractMarkupFromCFHTML(cfhtml); | |
644 return createFragmentFromMarkup(doc, markup, srcURL, DisallowScriptingAndPlu
ginContent); | |
645 } | |
646 | |
647 PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data) | |
648 { | |
649 if (!doc || !data) | |
650 return 0; | |
651 | |
652 String cfhtml = getFullCFHTML(data); | |
653 if (!cfhtml.isEmpty()) { | |
654 if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(doc, cfhtml)) | |
655 return fragment.release(); | |
656 } | |
657 | |
658 String html = getTextHTML(data); | |
659 String srcURL; | |
660 if (!html.isEmpty()) | |
661 return createFragmentFromMarkup(doc, html, srcURL, DisallowScriptingAndP
luginContent); | |
662 | |
663 return 0; | |
664 } | |
665 | |
666 PassRefPtr<DocumentFragment> fragmentFromHTML(Document* document, const DragData
Map* data) | |
667 { | |
668 if (!document || !data || data->isEmpty()) | |
669 return 0; | |
670 | |
671 String stringData; | |
672 if (getDataMapItem(data, htmlFormat(), stringData)) { | |
673 if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(document, str
ingData)) | |
674 return fragment.release(); | |
675 } | |
676 | |
677 String srcURL; | |
678 if (getDataMapItem(data, texthtmlFormat(), stringData)) | |
679 return createFragmentFromMarkup(document, stringData, srcURL, DisallowSc
riptingAndPluginContent); | |
680 | |
681 return 0; | |
682 } | |
683 | |
684 bool containsHTML(IDataObject* data) | |
685 { | |
686 return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->Qu
eryGetData(htmlFormat())); | |
687 } | |
688 | |
689 bool containsHTML(const DragDataMap* data) | |
690 { | |
691 return data->contains(texthtmlFormat()->cfFormat) || data->contains(htmlForm
at()->cfFormat); | |
692 } | |
693 | |
694 typedef void (*GetStringFunction)(IDataObject*, FORMATETC*, Vector<String>&); | |
695 typedef void (*SetStringFunction)(IDataObject*, FORMATETC*, const Vector<String>
&); | |
696 | |
697 struct ClipboardDataItem { | |
698 GetStringFunction getString; | |
699 SetStringFunction setString; | |
700 FORMATETC* format; | |
701 | |
702 ClipboardDataItem(FORMATETC* format, GetStringFunction getString, SetStringF
unction setString): format(format), getString(getString), setString(setString) {
} | |
703 }; | |
704 | |
705 typedef HashMap<UINT, ClipboardDataItem*> ClipboardFormatMap; | |
706 | |
707 // Getter functions. | |
708 | |
709 template<typename T> void getStringData(IDataObject* data, FORMATETC* format, Ve
ctor<String>& dataStrings) | |
710 { | |
711 STGMEDIUM store; | |
712 if (FAILED(data->GetData(format, &store))) | |
713 return; | |
714 dataStrings.append(String(static_cast<T*>(GlobalLock(store.hGlobal)), ::Glob
alSize(store.hGlobal) / sizeof(T))); | |
715 GlobalUnlock(store.hGlobal); | |
716 ReleaseStgMedium(&store); | |
717 } | |
718 | |
719 void getUtf8Data(IDataObject* data, FORMATETC* format, Vector<String>& dataStrin
gs) | |
720 { | |
721 STGMEDIUM store; | |
722 if (FAILED(data->GetData(format, &store))) | |
723 return; | |
724 dataStrings.append(String(UTF8Encoding().decode(static_cast<char*>(GlobalLoc
k(store.hGlobal)), GlobalSize(store.hGlobal)))); | |
725 GlobalUnlock(store.hGlobal); | |
726 ReleaseStgMedium(&store); | |
727 } | |
728 | |
729 #if USE(CF) | |
730 void getCFData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings
) | |
731 { | |
732 STGMEDIUM store; | |
733 if (FAILED(data->GetData(format, &store))) | |
734 return; | |
735 | |
736 HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(store.hGlobal)); | |
737 if (!hdrop) | |
738 return; | |
739 | |
740 WCHAR filename[MAX_PATH]; | |
741 UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); | |
742 for (UINT i = 0; i < fileCount; i++) { | |
743 if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename))) | |
744 continue; | |
745 dataStrings.append(static_cast<UChar*>(filename)); | |
746 } | |
747 | |
748 GlobalUnlock(store.hGlobal); | |
749 ReleaseStgMedium(&store); | |
750 } | |
751 #endif | |
752 | |
753 // Setter functions. | |
754 | |
755 void setUCharData(IDataObject* data, FORMATETC* format, const Vector<String>& da
taStrings) | |
756 { | |
757 STGMEDIUM medium = {0}; | |
758 medium.tymed = TYMED_HGLOBAL; | |
759 | |
760 medium.hGlobal = createGlobalData(dataStrings.first()); | |
761 if (!medium.hGlobal) | |
762 return; | |
763 data->SetData(format, &medium, FALSE); | |
764 ::GlobalFree(medium.hGlobal); | |
765 } | |
766 | |
767 void setUtf8Data(IDataObject* data, FORMATETC* format, const Vector<String>& dat
aStrings) | |
768 { | |
769 STGMEDIUM medium = {0}; | |
770 medium.tymed = TYMED_HGLOBAL; | |
771 | |
772 CString charString = dataStrings.first().utf8(); | |
773 size_t stringLength = charString.length(); | |
774 medium.hGlobal = ::GlobalAlloc(GPTR, stringLength + 1); | |
775 if (!medium.hGlobal) | |
776 return; | |
777 char* buffer = static_cast<char*>(GlobalLock(medium.hGlobal)); | |
778 memcpy(buffer, charString.data(), stringLength); | |
779 buffer[stringLength] = 0; | |
780 GlobalUnlock(medium.hGlobal); | |
781 data->SetData(format, &medium, FALSE); | |
782 ::GlobalFree(medium.hGlobal); | |
783 } | |
784 | |
785 #if USE(CF) | |
786 void setCFData(IDataObject* data, FORMATETC* format, const Vector<String>& dataS
trings) | |
787 { | |
788 STGMEDIUM medium = {0}; | |
789 medium.tymed = TYMED_HGLOBAL; | |
790 | |
791 SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (dataStrings.fir
st().length() + 2)); | |
792 medium.hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize); | |
793 if (!medium.hGlobal) | |
794 return; | |
795 | |
796 DROPFILES* dropFiles = reinterpret_cast<DROPFILES *>(GlobalLock(medium.hGlob
al)); | |
797 dropFiles->pFiles = sizeof(DROPFILES); | |
798 dropFiles->fWide = TRUE; | |
799 String filename = dataStrings.first(); | |
800 wcscpy(reinterpret_cast<LPWSTR>(dropFiles + 1), filename.charactersWithNullT
ermination()); | |
801 GlobalUnlock(medium.hGlobal); | |
802 data->SetData(format, &medium, FALSE); | |
803 ::GlobalFree(medium.hGlobal); | |
804 } | |
805 #endif | |
806 | |
807 static const ClipboardFormatMap& getClipboardMap() | |
808 { | |
809 static ClipboardFormatMap formatMap; | |
810 if (formatMap.isEmpty()) { | |
811 formatMap.add(htmlFormat()->cfFormat, new ClipboardDataItem(htmlFormat()
, getUtf8Data, setUtf8Data)); | |
812 formatMap.add(texthtmlFormat()->cfFormat, new ClipboardDataItem(texthtml
Format(), getStringData<UChar>, setUCharData)); | |
813 formatMap.add(plainTextFormat()->cfFormat, new ClipboardDataItem(plainT
extFormat(), getStringData<char>, setUtf8Data)); | |
814 formatMap.add(plainTextWFormat()->cfFormat, new ClipboardDataItem(plain
TextWFormat(), getStringData<UChar>, setUCharData)); | |
815 #if USE(CF) | |
816 formatMap.add(cfHDropFormat()->cfFormat, new ClipboardDataItem(cfHDropF
ormat(), getCFData, setCFData)); | |
817 #endif | |
818 formatMap.add(filenameFormat()->cfFormat, new ClipboardDataItem(filenam
eFormat(), getStringData<char>, setUtf8Data)); | |
819 formatMap.add(filenameWFormat()->cfFormat, new ClipboardDataItem(filena
meWFormat(), getStringData<UChar>, setUCharData)); | |
820 formatMap.add(urlFormat()->cfFormat, new ClipboardDataItem(urlFormat(),
getStringData<char>, setUtf8Data)); | |
821 formatMap.add(urlWFormat()->cfFormat, new ClipboardDataItem(urlWFormat(
), getStringData<UChar>, setUCharData)); | |
822 } | |
823 return formatMap; | |
824 } | |
825 | |
826 void getClipboardData(IDataObject* dataObject, FORMATETC* format, Vector<String>
& dataStrings) | |
827 { | |
828 const ClipboardFormatMap& formatMap = getClipboardMap(); | |
829 ClipboardFormatMap::const_iterator found = formatMap.find(format->cfFormat); | |
830 if (found == formatMap.end()) | |
831 return; | |
832 found->value->getString(dataObject, found->value->format, dataStrings); | |
833 } | |
834 | |
835 void setClipboardData(IDataObject* dataObject, UINT format, const Vector<String>
& dataStrings) | |
836 { | |
837 const ClipboardFormatMap& formatMap = getClipboardMap(); | |
838 ClipboardFormatMap::const_iterator found = formatMap.find(format); | |
839 if (found == formatMap.end()) | |
840 return; | |
841 found->value->setString(dataObject, found->value->format, dataStrings); | |
842 } | |
843 | |
844 } // namespace WebCore | |
OLD | NEW |