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 49namespace WebCore { 50 51#if USE(CF) 52FORMATETC* cfHDropFormat() 53{ 54 static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 55 return &urlFormat; 56} 57 58static bool urlFromPath(CFStringRef path, String& url) 59{ 60 if (!path) 61 return false; 62 63 RetainPtr<CFURLRef> cfURL = adoptCF(CFURLCreateWithFileSystemPath(0, path, kCFURLWindowsPathStyle, 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 77static bool getDataMapItem(const DragDataMap* dataObject, FORMATETC* format, String& 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 86static 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 119exit: 120 // Free up memory. 121 DragFinish(hdrop); 122 GlobalUnlock(medium.hGlobal); 123#endif 124 return succeeded; 125} 126 127static bool getWebLocData(const DragDataMap* dataObject, String& url, String* title) 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].charactersWithNullTermination()); 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 155static 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 169static FORMATETC* texthtmlFormat() 170{ 171 static UINT cf = RegisterClipboardFormat(L"text/html"); 172 static FORMATETC texthtmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 173 return &texthtmlFormat; 174} 175 176HGLOBAL 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.charactersWithNullTermination(), mutableTitle.charactersWithNullTermination()); 186 GlobalUnlock(cbData); 187 } 188 return cbData; 189} 190 191HGLOBAL createGlobalData(const String& str) 192{ 193 HGLOBAL vm = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar)); 194 if (!vm) 195 return 0; 196 UChar* buffer = static_cast<UChar*>(GlobalLock(vm)); 197 memcpy(buffer, str.characters(), str.length() * sizeof(UChar)); 198 buffer[str.length()] = 0; 199 GlobalUnlock(vm); 200 return vm; 201} 202 203HGLOBAL createGlobalData(const Vector<char>& vector) 204{ 205 HGLOBAL vm = ::GlobalAlloc(GPTR, vector.size() + 1); 206 if (!vm) 207 return 0; 208 char* buffer = static_cast<char*>(GlobalLock(vm)); 209 memcpy(buffer, vector.data(), vector.size()); 210 buffer[vector.size()] = 0; 211 GlobalUnlock(vm); 212 return vm; 213} 214 215static 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 230static void append(Vector<char>& vector, const char* string) 231{ 232 vector.append(string, strlen(string)); 233} 234 235static 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 -->", accounting for browser quirks. 241static 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).stripWhiteSpace(); 249} 250 251// Documentation for the CF_HTML format is available at http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp 252void markupToCFHTML(const String& markup, const String& srcURL, Vector<char>& result) 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' terminator. 284 char* headerBuffer = (char*)malloc(headerBufferLength); 285 snprintf(headerBuffer, headerBufferLength, header, startHTMLOffset, endHTMLOffset, 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 303void 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 316void replaceNBSPWithSpace(String& str) 317{ 318 static const UChar NonBreakingSpaceCharacter = 0xA0; 319 static const UChar SpaceCharacter = ' '; 320 str.replace(NonBreakingSpaceCharacter, SpaceCharacter); 321} 322 323FORMATETC* 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 330FORMATETC* 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 337FORMATETC* plainTextFormat() 338{ 339 static FORMATETC textFormat = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 340 return &textFormat; 341} 342 343FORMATETC* plainTextWFormat() 344{ 345 static FORMATETC textFormat = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 346 return &textFormat; 347} 348 349FORMATETC* 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 356FORMATETC* 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 364FORMATETC* 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 371FORMATETC* 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 378FORMATETC* 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 385FORMATETC* fileContentFormatZero() 386{ 387 static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS); 388 static FORMATETC fileContentFormat = { cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL }; 389 return &fileContentFormat; 390} 391 392void 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(store.hGlobal)); 400 size = fgd->fgd[0].nFileSizeLow; 401 pathname = fgd->fgd[0].cFileName; 402 403 GlobalUnlock(store.hGlobal); 404 ::ReleaseStgMedium(&store); 405} 406 407void 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 419void setFileDescriptorData(IDataObject* dataObject, int size, const String& passedPathname) 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(medium.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 443void 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 458String getURL(IDataObject* dataObject, DragData::FilenameConversionPolicy filenamePolicy, 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(CFStringCreateWithCharacters(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(CFStringCreateWithCString(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 506String getURL(const DragDataMap* data, DragData::FilenameConversionPolicy filenamePolicy, 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.charactersWithNullTermination()) && !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 533String 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 dropping files doesn't 551 // populate the drag with text data. Per https://bugs.webkit.org/show_bug.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 558String 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 569String 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 582String getTextHTML(const DragDataMap* data) 583{ 584 String text; 585 getDataMapItem(data, texthtmlFormat(), text); 586 return text; 587} 588 589String getCFHTML(IDataObject* data) 590{ 591 String cfhtml = getFullCFHTML(data); 592 if (!cfhtml.isEmpty()) 593 return extractMarkupFromCFHTML(cfhtml); 594 return String(); 595} 596 597String getCFHTML(const DragDataMap* dataMap) 598{ 599 String cfhtml; 600 getDataMapItem(dataMap, htmlFormat(), cfhtml); 601 return extractMarkupFromCFHTML(cfhtml); 602} 603 604PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*) 605{ 606 // FIXME: We should be able to create fragments from files 607 return 0; 608} 609 610PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*) 611{ 612 // FIXME: We should be able to create fragments from files 613 return 0; 614} 615 616bool containsFilenames(const IDataObject*) 617{ 618 // FIXME: We'll want to update this once we can produce fragments from files 619 return false; 620} 621 622bool 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 629PassRefPtr<DocumentFragment> fragmentFromCFHTML(Document* doc, const String& cfhtml) 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, DisallowScriptingAndPluginContent); 645} 646 647PassRefPtr<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, DisallowScriptingAndPluginContent); 662 663 return 0; 664} 665 666PassRefPtr<DocumentFragment> fragmentFromHTML(Document* document, const DragDataMap* 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, stringData)) 674 return fragment.release(); 675 } 676 677 String srcURL; 678 if (getDataMapItem(data, texthtmlFormat(), stringData)) 679 return createFragmentFromMarkup(document, stringData, srcURL, DisallowScriptingAndPluginContent); 680 681 return 0; 682} 683 684bool containsHTML(IDataObject* data) 685{ 686 return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->QueryGetData(htmlFormat())); 687} 688 689bool containsHTML(const DragDataMap* data) 690{ 691 return data->contains(texthtmlFormat()->cfFormat) || data->contains(htmlFormat()->cfFormat); 692} 693 694typedef void (*GetStringFunction)(IDataObject*, FORMATETC*, Vector<String>&); 695typedef void (*SetStringFunction)(IDataObject*, FORMATETC*, const Vector<String>&); 696 697struct ClipboardDataItem { 698 GetStringFunction getString; 699 SetStringFunction setString; 700 FORMATETC* format; 701 702 ClipboardDataItem(FORMATETC* format, GetStringFunction getString, SetStringFunction setString): format(format), getString(getString), setString(setString) { } 703}; 704 705typedef HashMap<UINT, ClipboardDataItem*> ClipboardFormatMap; 706 707// Getter functions. 708 709template<typename T> void getStringData(IDataObject* data, FORMATETC* format, Vector<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)), ::GlobalSize(store.hGlobal) / sizeof(T))); 715 GlobalUnlock(store.hGlobal); 716 ReleaseStgMedium(&store); 717} 718 719void getUtf8Data(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings) 720{ 721 STGMEDIUM store; 722 if (FAILED(data->GetData(format, &store))) 723 return; 724 dataStrings.append(String(UTF8Encoding().decode(static_cast<char*>(GlobalLock(store.hGlobal)), GlobalSize(store.hGlobal)))); 725 GlobalUnlock(store.hGlobal); 726 ReleaseStgMedium(&store); 727} 728 729#if USE(CF) 730void 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 755void setUCharData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings) 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 767void setUtf8Data(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings) 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) 786void setCFData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings) 787{ 788 STGMEDIUM medium = {0}; 789 medium.tymed = TYMED_HGLOBAL; 790 791 SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (dataStrings.first().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.hGlobal)); 797 dropFiles->pFiles = sizeof(DROPFILES); 798 dropFiles->fWide = TRUE; 799 String filename = dataStrings.first(); 800 wcscpy(reinterpret_cast<LPWSTR>(dropFiles + 1), filename.charactersWithNullTermination()); 801 GlobalUnlock(medium.hGlobal); 802 data->SetData(format, &medium, FALSE); 803 ::GlobalFree(medium.hGlobal); 804} 805#endif 806 807static 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(texthtmlFormat(), getStringData<UChar>, setUCharData)); 813 formatMap.add(plainTextFormat()->cfFormat, new ClipboardDataItem(plainTextFormat(), getStringData<char>, setUtf8Data)); 814 formatMap.add(plainTextWFormat()->cfFormat, new ClipboardDataItem(plainTextWFormat(), getStringData<UChar>, setUCharData)); 815#if USE(CF) 816 formatMap.add(cfHDropFormat()->cfFormat, new ClipboardDataItem(cfHDropFormat(), getCFData, setCFData)); 817#endif 818 formatMap.add(filenameFormat()->cfFormat, new ClipboardDataItem(filenameFormat(), getStringData<char>, setUtf8Data)); 819 formatMap.add(filenameWFormat()->cfFormat, new ClipboardDataItem(filenameWFormat(), 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 826void 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 835void 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 845