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) 49 FORMATETC* cfHDropFormat() 50 { 51 static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 52 return &urlFormat; 53 } 54 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 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 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 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 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 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 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 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 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 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 229 static void append(Vector<char>& vector, const char* string) 230 { 231 vector.append(string, strlen(string)); 232 } 233 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. 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 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 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 309 void replaceNBSPWithSpace(String& str) 310 { 311 static const UChar NonBreakingSpaceCharacter = 0xA0; 312 static const UChar SpaceCharacter = ' '; 313 str.replace(NonBreakingSpaceCharacter, SpaceCharacter); 314 } 315 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 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 330 FORMATETC* plainTextFormat() 331 { 332 static FORMATETC textFormat = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 333 return &textFormat; 334 } 335 336 FORMATETC* plainTextWFormat() 337 { 338 static FORMATETC textFormat = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 339 return &textFormat; 340 } 341 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 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 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 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 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 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 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 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 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 509 String getTextHTML(const DragDataMap* data) 510 { 511 String text; 512 getDataMapItem(data, texthtmlFormat(), text); 513 return text; 514 } 515 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 524 String getCFHTML(const DragDataMap* dataMap) 525 { 526 String cfhtml; 527 getDataMapItem(dataMap, htmlFormat(), cfhtml); 528 return extractMarkupFromCFHTML(cfhtml); 529 } 530 531 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*) 532 { 533 // FIXME: We should be able to create fragments from files 534 return 0; 535 } 536 537 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*) 538 { 539 // FIXME: We should be able to create fragments from files 540 return 0; 541 } 542 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 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 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 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 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 612 bool containsHTML(IDataObject* data) 613 { 614 return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->QueryGetData(htmlFormat())); 615 } 616 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 630 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 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 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) 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 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 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) 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 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 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 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