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