1/* 2 * Copyright (C) 2006, 2007, 2013-2014 Apple Inc. All rights reserved. 3 * Copyright (C) 2013 Xueqing Huang <huangxueqing@baidu.com> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "Pasteboard.h" 29 30#include "BitmapInfo.h" 31#include "CachedImage.h" 32#include "ClipboardUtilitiesWin.h" 33#include "Document.h" 34#include "DocumentFragment.h" 35#include "Editor.h" 36#include "Element.h" 37#include "Frame.h" 38#include "HTMLNames.h" 39#include "HTMLParserIdioms.h" 40#include "HWndDC.h" 41#include "HitTestResult.h" 42#include "Image.h" 43#include "URL.h" 44#include "NotImplemented.h" 45#include "Page.h" 46#include "Range.h" 47#include "RenderImage.h" 48#include "SharedBuffer.h" 49#include "TextEncoding.h" 50#include "WebCoreInstanceHandle.h" 51#include "markup.h" 52#include <wtf/WindowsExtras.h> 53#include <wtf/text/CString.h> 54#include <wtf/text/StringView.h> 55#include <wtf/win/GDIObject.h> 56 57namespace WebCore { 58 59// We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft 60// see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 61 62static UINT HTMLClipboardFormat = 0; 63static UINT BookmarkClipboardFormat = 0; 64static UINT WebSmartPasteFormat = 0; 65 66static LRESULT CALLBACK PasteboardOwnerWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 67{ 68 LRESULT lresult = 0; 69 70 switch (message) { 71 case WM_RENDERFORMAT: 72 // This message comes when SetClipboardData was sent a null data handle 73 // and now it's come time to put the data on the clipboard. 74 break; 75 case WM_RENDERALLFORMATS: 76 // This message comes when SetClipboardData was sent a null data handle 77 // and now this application is about to quit, so it must put data on 78 // the clipboard before it exits. 79 break; 80 case WM_DESTROY: 81 break; 82#if !OS(WINCE) 83 case WM_DRAWCLIPBOARD: 84 break; 85 case WM_CHANGECBCHAIN: 86 break; 87#endif 88 default: 89 lresult = DefWindowProc(hWnd, message, wParam, lParam); 90 break; 91 } 92 return lresult; 93} 94 95PassOwnPtr<Pasteboard> Pasteboard::createForCopyAndPaste() 96{ 97 OwnPtr<Pasteboard> pasteboard = adoptPtr(new Pasteboard); 98 COMPtr<IDataObject> clipboardData; 99#if !OS(WINCE) 100 if (!SUCCEEDED(OleGetClipboard(&clipboardData))) 101 clipboardData = 0; 102#endif 103 pasteboard->setExternalDataObject(clipboardData.get()); 104 return pasteboard.release(); 105} 106 107PassOwnPtr<Pasteboard> Pasteboard::createPrivate() 108{ 109 // Windows has no "Private pasteboard" concept. 110 return createForCopyAndPaste(); 111} 112 113#if ENABLE(DRAG_SUPPORT) 114PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop() 115{ 116 COMPtr<WCDataObject> dataObject; 117 WCDataObject::createInstance(&dataObject); 118 return adoptPtr(new Pasteboard(dataObject.get())); 119} 120 121// static 122PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData) 123{ 124 if (dragData.platformData()) 125 return adoptPtr(new Pasteboard(dragData.platformData())); 126 // FIXME: Should add a const overload of dragDataMap so we don't need a const_cast here. 127 return adoptPtr(new Pasteboard(const_cast<DragData&>(dragData).dragDataMap())); 128} 129#endif 130 131void Pasteboard::finishCreatingPasteboard() 132{ 133 WNDCLASS wc; 134 memset(&wc, 0, sizeof(WNDCLASS)); 135 wc.lpfnWndProc = PasteboardOwnerWndProc; 136 wc.hInstance = WebCore::instanceHandle(); 137 wc.lpszClassName = L"PasteboardOwnerWindowClass"; 138 RegisterClass(&wc); 139 140 m_owner = ::CreateWindow(L"PasteboardOwnerWindowClass", L"PasteboardOwnerWindow", 0, 0, 0, 0, 0, 141 HWND_MESSAGE, 0, 0, 0); 142 143 HTMLClipboardFormat = ::RegisterClipboardFormat(L"HTML Format"); 144 BookmarkClipboardFormat = ::RegisterClipboardFormat(L"UniformResourceLocatorW"); 145 WebSmartPasteFormat = ::RegisterClipboardFormat(L"WebKit Smart Paste Format"); 146} 147 148Pasteboard::Pasteboard() 149 : m_dataObject(0) 150 , m_writableDataObject(0) 151{ 152 finishCreatingPasteboard(); 153} 154 155Pasteboard::Pasteboard(IDataObject* dataObject) 156 : m_dataObject(dataObject) 157 , m_writableDataObject(0) 158{ 159 finishCreatingPasteboard(); 160} 161 162Pasteboard::Pasteboard(WCDataObject* dataObject) 163 : m_dataObject(dataObject) 164 , m_writableDataObject(dataObject) 165{ 166 finishCreatingPasteboard(); 167} 168 169Pasteboard::Pasteboard(const DragDataMap& dataMap) 170 : m_dataObject(0) 171 , m_writableDataObject(0) 172 , m_dragDataMap(dataMap) 173{ 174 finishCreatingPasteboard(); 175} 176 177void Pasteboard::clear() 178{ 179 if (::OpenClipboard(m_owner)) { 180 ::EmptyClipboard(); 181 ::CloseClipboard(); 182 } 183} 184 185enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeTextHTML }; 186 187static ClipboardDataType clipboardTypeFromMIMEType(const String& type) 188{ 189 String qType = type.stripWhiteSpace().lower(); 190 191 // two special cases for IE compatibility 192 if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain;")) 193 return ClipboardDataTypeText; 194 if (qType == "url" || qType == "text/uri-list") 195 return ClipboardDataTypeURL; 196 if (qType == "text/html") 197 return ClipboardDataTypeTextHTML; 198 199 return ClipboardDataTypeNone; 200} 201 202void Pasteboard::clear(const String& type) 203{ 204 if (!m_writableDataObject) 205 return; 206 207 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); 208 209 if (dataType == ClipboardDataTypeURL) { 210 m_writableDataObject->clearData(urlWFormat()->cfFormat); 211 m_writableDataObject->clearData(urlFormat()->cfFormat); 212 } 213 if (dataType == ClipboardDataTypeText) { 214 m_writableDataObject->clearData(plainTextFormat()->cfFormat); 215 m_writableDataObject->clearData(plainTextWFormat()->cfFormat); 216 } 217} 218 219bool Pasteboard::hasData() 220{ 221 if (!m_dataObject && m_dragDataMap.isEmpty()) 222 return false; 223 224 if (m_dataObject) { 225 COMPtr<IEnumFORMATETC> itr; 226 if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) 227 return false; 228 229 if (!itr) 230 return false; 231 232 FORMATETC data; 233 234 // IEnumFORMATETC::Next returns S_FALSE if there are no more items. 235 if (itr->Next(1, &data, 0) == S_OK) { 236 // There is at least one item in the IDataObject 237 return true; 238 } 239 240 return false; 241 } 242 return !m_dragDataMap.isEmpty(); 243} 244 245static void addMimeTypesForFormat(ListHashSet<String>& results, const FORMATETC& format) 246{ 247 // URL and Text are provided for compatibility with IE's model 248 if (format.cfFormat == urlFormat()->cfFormat || format.cfFormat == urlWFormat()->cfFormat) { 249 results.add("URL"); 250 results.add("text/uri-list"); 251 } 252 253 if (format.cfFormat == plainTextWFormat()->cfFormat || format.cfFormat == plainTextFormat()->cfFormat) { 254 results.add("Text"); 255 results.add("text/plain"); 256 } 257} 258 259Vector<String> Pasteboard::types() 260{ 261 ListHashSet<String> results; 262 263 if (!m_dataObject && m_dragDataMap.isEmpty()) 264 return Vector<String>(); 265 266 if (m_dataObject) { 267 COMPtr<IEnumFORMATETC> itr; 268 269 if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) 270 return Vector<String>(); 271 272 if (!itr) 273 return Vector<String>(); 274 275 FORMATETC data; 276 277 // IEnumFORMATETC::Next returns S_FALSE if there are no more items. 278 while (itr->Next(1, &data, 0) == S_OK) 279 addMimeTypesForFormat(results, data); 280 } else { 281 for (DragDataMap::const_iterator it = m_dragDataMap.begin(); it != m_dragDataMap.end(); ++it) { 282 FORMATETC data; 283 data.cfFormat = (*it).key; 284 addMimeTypesForFormat(results, data); 285 } 286 } 287 288 Vector<String> vector; 289 copyToVector(results, vector); 290 return vector; 291} 292 293String Pasteboard::readString(const String& type) 294{ 295 if (!m_dataObject && m_dragDataMap.isEmpty()) 296 return ""; 297 298 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); 299 if (dataType == ClipboardDataTypeText) 300 return m_dataObject ? getPlainText(m_dataObject.get()) : getPlainText(&m_dragDataMap); 301 if (dataType == ClipboardDataTypeURL) 302 return m_dataObject ? getURL(m_dataObject.get(), DragData::DoNotConvertFilenames) : getURL(&m_dragDataMap, DragData::DoNotConvertFilenames); 303 if (dataType == ClipboardDataTypeTextHTML) { 304 String data = m_dataObject ? getTextHTML(m_dataObject.get()) : getTextHTML(&m_dragDataMap); 305 if (!data.isEmpty()) 306 return data; 307 return m_dataObject ? getCFHTML(m_dataObject.get()) : getCFHTML(&m_dragDataMap); 308 } 309 310 return ""; 311} 312 313Vector<String> Pasteboard::readFilenames() 314{ 315 Vector<String> fileNames; 316 317#if USE(CF) 318 if (m_dataObject) { 319 STGMEDIUM medium; 320 if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium))) 321 return fileNames; 322 323 HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal)); 324 if (!hdrop) 325 return fileNames; 326 327 WCHAR filename[MAX_PATH]; 328 UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); 329 for (UINT i = 0; i < fileCount; i++) { 330 if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename))) 331 continue; 332 fileNames.append(filename); 333 } 334 335 GlobalUnlock(medium.hGlobal); 336 ReleaseStgMedium(&medium); 337 return fileNames; 338 } 339 if (!m_dragDataMap.contains(cfHDropFormat()->cfFormat)) 340 return fileNames; 341 return m_dragDataMap.get(cfHDropFormat()->cfFormat); 342#else 343 notImplemented(); 344 return fileNames; 345#endif 346} 347 348static bool writeURL(WCDataObject *data, const URL& url, String title, bool withPlainText, bool withHTML) 349{ 350 ASSERT(data); 351 352 if (url.isEmpty()) 353 return false; 354 355 if (title.isEmpty()) { 356 title = url.lastPathComponent(); 357 if (title.isEmpty()) 358 title = url.host(); 359 } 360 361 STGMEDIUM medium = {0}; 362 medium.tymed = TYMED_HGLOBAL; 363 364 medium.hGlobal = createGlobalData(url, title); 365 bool success = false; 366 if (medium.hGlobal && FAILED(data->SetData(urlWFormat(), &medium, TRUE))) 367 ::GlobalFree(medium.hGlobal); 368 else 369 success = true; 370 371 if (withHTML) { 372 Vector<char> cfhtmlData; 373 markupToCFHTML(urlToMarkup(url, title), "", cfhtmlData); 374 medium.hGlobal = createGlobalData(cfhtmlData); 375 if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE))) 376 ::GlobalFree(medium.hGlobal); 377 else 378 success = true; 379 } 380 381 if (withPlainText) { 382 medium.hGlobal = createGlobalData(url.string()); 383 if (medium.hGlobal && FAILED(data->SetData(plainTextWFormat(), &medium, TRUE))) 384 ::GlobalFree(medium.hGlobal); 385 else 386 success = true; 387 } 388 389 return success; 390} 391 392void Pasteboard::writeString(const String& type, const String& data) 393{ 394 if (!m_writableDataObject) 395 return; 396 397 ClipboardDataType winType = clipboardTypeFromMIMEType(type); 398 399 if (winType == ClipboardDataTypeURL) { 400 WebCore::writeURL(m_writableDataObject.get(), URL(URL(), data), String(), false, true); 401 return; 402 } 403 404 if (winType == ClipboardDataTypeText) { 405 STGMEDIUM medium = {0}; 406 medium.tymed = TYMED_HGLOBAL; 407 medium.hGlobal = createGlobalData(data); 408 if (!medium.hGlobal) 409 return; 410 411 if (FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) 412 ::GlobalFree(medium.hGlobal); 413 } 414} 415 416#if ENABLE(DRAG_SUPPORT) 417void Pasteboard::setDragImage(DragImageRef, const IntPoint&) 418{ 419 // Do nothing in Windows. 420} 421#endif 422 423void Pasteboard::writeRangeToDataObject(Range& selectedRange, Frame& frame) 424{ 425 if (!m_writableDataObject) 426 return; 427 428 STGMEDIUM medium = {0}; 429 medium.tymed = TYMED_HGLOBAL; 430 431 Vector<char> data; 432 markupToCFHTML(createMarkup(selectedRange, 0, AnnotateForInterchange), 433 selectedRange.startContainer()->document().url().string(), data); 434 medium.hGlobal = createGlobalData(data); 435 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE))) 436 ::GlobalFree(medium.hGlobal); 437 438 String str = frame.editor().selectedTextForDataTransfer(); 439 replaceNewlinesWithWindowsStyleNewlines(str); 440 replaceNBSPWithSpace(str); 441 medium.hGlobal = createGlobalData(str); 442 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) 443 ::GlobalFree(medium.hGlobal); 444 445 medium.hGlobal = 0; 446 if (frame.editor().canSmartCopyOrDelete()) 447 m_writableDataObject->SetData(smartPasteFormat(), &medium, TRUE); 448} 449 450void Pasteboard::writeSelection(Range& selectedRange, bool canSmartCopyOrDelete, Frame& frame, ShouldSerializeSelectedTextForDataTransfer shouldSerializeSelectedTextForDataTransfer) 451{ 452 clear(); 453 454 // Put CF_HTML format on the pasteboard 455 if (::OpenClipboard(m_owner)) { 456 Vector<char> data; 457 markupToCFHTML(createMarkup(selectedRange, 0, AnnotateForInterchange), 458 selectedRange.startContainer()->document().url().string(), data); 459 HGLOBAL cbData = createGlobalData(data); 460 if (!::SetClipboardData(HTMLClipboardFormat, cbData)) 461 ::GlobalFree(cbData); 462 ::CloseClipboard(); 463 } 464 465 // Put plain string on the pasteboard. CF_UNICODETEXT covers CF_TEXT as well 466 String str = shouldSerializeSelectedTextForDataTransfer == IncludeImageAltTextForDataTransfer ? frame.editor().selectedTextForDataTransfer() : frame.editor().selectedText(); 467 replaceNewlinesWithWindowsStyleNewlines(str); 468 replaceNBSPWithSpace(str); 469 if (::OpenClipboard(m_owner)) { 470 HGLOBAL cbData = createGlobalData(str); 471 if (!::SetClipboardData(CF_UNICODETEXT, cbData)) 472 ::GlobalFree(cbData); 473 ::CloseClipboard(); 474 } 475 476 // enable smart-replacing later on by putting dummy data on the pasteboard 477 if (canSmartCopyOrDelete) { 478 if (::OpenClipboard(m_owner)) { 479 ::SetClipboardData(WebSmartPasteFormat, 0); 480 ::CloseClipboard(); 481 } 482 } 483 484 writeRangeToDataObject(selectedRange, frame); 485} 486 487void Pasteboard::writePlainTextToDataObject(const String& text, SmartReplaceOption smartReplaceOption) 488{ 489 if (!m_writableDataObject) 490 return; 491 492 STGMEDIUM medium = {0}; 493 medium.tymed = TYMED_HGLOBAL; 494 495 String str = text; 496 replaceNewlinesWithWindowsStyleNewlines(str); 497 replaceNBSPWithSpace(str); 498 medium.hGlobal = createGlobalData(str); 499 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) 500 ::GlobalFree(medium.hGlobal); 501} 502 503void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption) 504{ 505 clear(); 506 507 // Put plain string on the pasteboard. CF_UNICODETEXT covers CF_TEXT as well 508 String str = text; 509 replaceNewlinesWithWindowsStyleNewlines(str); 510 if (::OpenClipboard(m_owner)) { 511 HGLOBAL cbData = createGlobalData(str); 512 if (!::SetClipboardData(CF_UNICODETEXT, cbData)) 513 ::GlobalFree(cbData); 514 ::CloseClipboard(); 515 } 516 517 // enable smart-replacing later on by putting dummy data on the pasteboard 518 if (smartReplaceOption == CanSmartReplace) { 519 if (::OpenClipboard(m_owner)) { 520 ::SetClipboardData(WebSmartPasteFormat, 0); 521 ::CloseClipboard(); 522 } 523 } 524 525 writePlainTextToDataObject(text, smartReplaceOption); 526} 527 528#if !OS(WINCE) 529static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length) 530{ 531 size_t writeTo = 0; 532 size_t readFrom = 0; 533 while (readFrom < length) { 534 UINT type = PathGetCharType(psz[readFrom]); 535 if (!psz[readFrom] || type & (GCT_LFNCHAR | GCT_SHORTCHAR)) 536 psz[writeTo++] = psz[readFrom]; 537 538 readFrom++; 539 } 540 psz[writeTo] = 0; 541} 542#endif 543 544static String filesystemPathFromUrlOrTitle(const String& url, const String& title, const UChar* extension, bool isLink) 545{ 546#if OS(WINCE) 547 notImplemented(); 548 return String(); 549#else 550 static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1; 551 bool usedURL = false; 552 WCHAR fsPathBuffer[MAX_PATH]; 553 fsPathBuffer[0] = 0; 554 int extensionLen = extension ? lstrlen(extension) : 0; 555 int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen; 556 557 if (!title.isEmpty()) { 558 size_t len = std::min<size_t>(title.length(), fsPathMaxLengthExcludingExtension); 559 StringView(title).substring(0, len).getCharactersWithUpconvert(fsPathBuffer); 560 fsPathBuffer[len] = 0; 561 pathRemoveBadFSCharacters(fsPathBuffer, len); 562 } 563 564 if (!lstrlen(fsPathBuffer)) { 565 URL kurl(URL(), url); 566 usedURL = true; 567 // The filename for any content based drag or file url should be the last element of 568 // the path. If we can't find it, or we're coming up with the name for a link 569 // we just use the entire url. 570 DWORD len = fsPathMaxLengthExcludingExtension; 571 String lastComponent = kurl.lastPathComponent(); 572 if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) { 573 len = std::min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length()); 574 StringView(lastComponent).substring(0, len).getCharactersWithUpconvert(fsPathBuffer); 575 } else { 576 len = std::min<DWORD>(fsPathMaxLengthExcludingExtension, url.length()); 577 StringView(url).substring(0, len).getCharactersWithUpconvert(fsPathBuffer); 578 } 579 fsPathBuffer[len] = 0; 580 pathRemoveBadFSCharacters(fsPathBuffer, len); 581 } 582 583 if (!extension) 584 return String(static_cast<UChar*>(fsPathBuffer)); 585 586 if (!isLink && usedURL) { 587 PathRenameExtension(fsPathBuffer, extension); 588 return String(static_cast<UChar*>(fsPathBuffer)); 589 } 590 591 return makeString(static_cast<const UChar*>(fsPathBuffer), extension); 592#endif 593} 594 595// writeFileToDataObject takes ownership of fileDescriptor and fileContent 596static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescriptor, HGLOBAL fileContent, HGLOBAL hDropContent) 597{ 598 HRESULT hr = S_OK; 599 FORMATETC* fe; 600 STGMEDIUM medium = {0}; 601 medium.tymed = TYMED_HGLOBAL; 602 603 if (!fileDescriptor || !fileContent) 604 goto exit; 605 606 // Descriptor 607 fe = fileDescriptorFormat(); 608 609 medium.hGlobal = fileDescriptor; 610 611 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE))) 612 goto exit; 613 614 // Contents 615 fe = fileContentFormatZero(); 616 medium.hGlobal = fileContent; 617 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE))) 618 goto exit; 619 620#if USE(CF) 621 // HDROP 622 if (hDropContent) { 623 medium.hGlobal = hDropContent; 624 hr = dataObject->SetData(cfHDropFormat(), &medium, TRUE); 625 } 626#endif 627 628exit: 629 if (FAILED(hr)) { 630 if (fileDescriptor) 631 GlobalFree(fileDescriptor); 632 if (fileContent) 633 GlobalFree(fileContent); 634 if (hDropContent) 635 GlobalFree(hDropContent); 636 } 637 return hr; 638} 639 640void Pasteboard::writeURLToDataObject(const URL& kurl, const String& titleStr) 641{ 642 if (!m_writableDataObject) 643 return; 644 WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true); 645 646 String url = kurl.string(); 647 ASSERT(url.containsOnlyASCII()); // URL::string() is URL encoded. 648 649 String fsPath = filesystemPathFromUrlOrTitle(url, titleStr, L".URL", true); 650 String contentString("[InternetShortcut]\r\nURL=" + url + "\r\n"); 651 CString content = contentString.latin1(); 652 653 if (fsPath.length() <= 0) 654 return; 655 656 HGLOBAL urlFileDescriptor = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); 657 if (!urlFileDescriptor) 658 return; 659 660 HGLOBAL urlFileContent = GlobalAlloc(GPTR, content.length()); 661 if (!urlFileContent) { 662 GlobalFree(urlFileDescriptor); 663 return; 664 } 665 666 FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(urlFileDescriptor)); 667 if (!fgd) { 668 GlobalFree(urlFileDescriptor); 669 return; 670 } 671 672 ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR)); 673 fgd->cItems = 1; 674 fgd->fgd[0].dwFlags = FD_FILESIZE; 675 fgd->fgd[0].nFileSizeLow = content.length(); 676 677 unsigned maxSize = std::min<unsigned>(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName)); 678 StringView(fsPath).substring(0, maxSize).getCharactersWithUpconvert(fgd->fgd[0].cFileName); 679 GlobalUnlock(urlFileDescriptor); 680 681 char* fileContents = static_cast<char*>(GlobalLock(urlFileContent)); 682 if (!fileContents) { 683 GlobalFree(urlFileDescriptor); 684 return; 685 } 686 687 CopyMemory(fileContents, content.data(), content.length()); 688 GlobalUnlock(urlFileContent); 689 690 writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0); 691} 692 693void Pasteboard::write(const PasteboardURL& pasteboardURL) 694{ 695 ASSERT(!pasteboardURL.url.isEmpty()); 696 697 clear(); 698 699 String title(pasteboardURL.title); 700 if (title.isEmpty()) { 701 title = pasteboardURL.url.lastPathComponent(); 702 if (title.isEmpty()) 703 title = pasteboardURL.url.host(); 704 } 705 706 // write to clipboard in format com.apple.safari.bookmarkdata to be able to paste into the bookmarks view with appropriate title 707 if (::OpenClipboard(m_owner)) { 708 HGLOBAL cbData = createGlobalData(pasteboardURL.url, title); 709 if (!::SetClipboardData(BookmarkClipboardFormat, cbData)) 710 ::GlobalFree(cbData); 711 ::CloseClipboard(); 712 } 713 714 // write to clipboard in format CF_HTML to be able to paste into contenteditable areas as a link 715 if (::OpenClipboard(m_owner)) { 716 Vector<char> data; 717 markupToCFHTML(urlToMarkup(pasteboardURL.url, title), "", data); 718 HGLOBAL cbData = createGlobalData(data); 719 if (!::SetClipboardData(HTMLClipboardFormat, cbData)) 720 ::GlobalFree(cbData); 721 ::CloseClipboard(); 722 } 723 724 // bare-bones CF_UNICODETEXT support 725 if (::OpenClipboard(m_owner)) { 726 HGLOBAL cbData = createGlobalData(pasteboardURL.url.string()); 727 if (!::SetClipboardData(CF_UNICODETEXT, cbData)) 728 ::GlobalFree(cbData); 729 ::CloseClipboard(); 730 } 731 732 writeURLToDataObject(pasteboardURL.url, pasteboardURL.title); 733} 734 735void Pasteboard::writeImage(Element& element, const URL&, const String&) 736{ 737 if (!(element.renderer() && element.renderer()->isRenderImage())) 738 return; 739 740 RenderImage* renderer = toRenderImage(element.renderer()); 741 CachedImage* cachedImage = renderer->cachedImage(); 742 if (!cachedImage || cachedImage->errorOccurred()) 743 return; 744 Image* image = cachedImage->imageForRenderer(renderer); 745 ASSERT(image); 746 747 clear(); 748 749 HWndDC dc(0); 750 auto compatibleDC = adoptGDIObject(::CreateCompatibleDC(0)); 751 auto sourceDC = adoptGDIObject(::CreateCompatibleDC(0)); 752 auto resultBitmap = adoptGDIObject(::CreateCompatibleBitmap(dc, image->width(), image->height())); 753 HGDIOBJ oldBitmap = ::SelectObject(compatibleDC.get(), resultBitmap.get()); 754 755 BitmapInfo bmInfo = BitmapInfo::create(IntSize(image->size())); 756 757 auto coreBitmap = adoptGDIObject(::CreateDIBSection(dc, &bmInfo, DIB_RGB_COLORS, 0, 0, 0)); 758 HGDIOBJ oldSource = ::SelectObject(sourceDC.get(), coreBitmap.get()); 759 image->getHBITMAP(coreBitmap.get()); 760 761 ::BitBlt(compatibleDC.get(), 0, 0, image->width(), image->height(), sourceDC.get(), 0, 0, SRCCOPY); 762 763 ::SelectObject(sourceDC.get(), oldSource); 764 ::SelectObject(compatibleDC.get(), oldBitmap); 765 766 if (::OpenClipboard(m_owner)) { 767 ::SetClipboardData(CF_BITMAP, resultBitmap.leak()); 768 ::CloseClipboard(); 769 } 770} 771 772void Pasteboard::writePasteboard(const Pasteboard& sourcePasteboard) 773{ 774 notImplemented(); 775} 776 777bool Pasteboard::canSmartReplace() 778{ 779 return ::IsClipboardFormatAvailable(WebSmartPasteFormat); 780} 781 782void Pasteboard::read(PasteboardPlainText& text) 783{ 784 if (::IsClipboardFormatAvailable(CF_UNICODETEXT) && ::OpenClipboard(m_owner)) { 785 if (HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT)) { 786 text.text = static_cast<UChar*>(GlobalLock(cbData)); 787 GlobalUnlock(cbData); 788 ::CloseClipboard(); 789 return; 790 } 791 ::CloseClipboard(); 792 } 793 794 if (::IsClipboardFormatAvailable(CF_TEXT) && ::OpenClipboard(m_owner)) { 795 if (HANDLE cbData = ::GetClipboardData(CF_TEXT)) { 796 // FIXME: This treats the characters as Latin-1, not UTF-8 or even Windows Latin-1. Is that the right encoding? 797 text.text = static_cast<char*>(GlobalLock(cbData)); 798 GlobalUnlock(cbData); 799 ::CloseClipboard(); 800 return; 801 } 802 ::CloseClipboard(); 803 } 804} 805 806PassRefPtr<DocumentFragment> Pasteboard::documentFragment(Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText) 807{ 808 chosePlainText = false; 809 810 if (::IsClipboardFormatAvailable(HTMLClipboardFormat) && ::OpenClipboard(m_owner)) { 811 // get data off of clipboard 812 HANDLE cbData = ::GetClipboardData(HTMLClipboardFormat); 813 if (cbData) { 814 SIZE_T dataSize = ::GlobalSize(cbData); 815 String cfhtml(UTF8Encoding().decode(static_cast<char*>(GlobalLock(cbData)), dataSize)); 816 GlobalUnlock(cbData); 817 ::CloseClipboard(); 818 819 RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(frame.document(), cfhtml); 820 if (fragment) 821 return fragment.release(); 822 } else 823 ::CloseClipboard(); 824 } 825 826 if (allowPlainText && ::IsClipboardFormatAvailable(CF_UNICODETEXT)) { 827 chosePlainText = true; 828 if (::OpenClipboard(m_owner)) { 829 HANDLE cbData = ::GetClipboardData(CF_UNICODETEXT); 830 if (cbData) { 831 UChar* buffer = static_cast<UChar*>(GlobalLock(cbData)); 832 String str(buffer); 833 GlobalUnlock(cbData); 834 ::CloseClipboard(); 835 RefPtr<DocumentFragment> fragment = createFragmentFromText(context, str); 836 if (fragment) 837 return fragment.release(); 838 } else 839 ::CloseClipboard(); 840 } 841 } 842 843 if (allowPlainText && ::IsClipboardFormatAvailable(CF_TEXT)) { 844 chosePlainText = true; 845 if (::OpenClipboard(m_owner)) { 846 HANDLE cbData = ::GetClipboardData(CF_TEXT); 847 if (cbData) { 848 char* buffer = static_cast<char*>(GlobalLock(cbData)); 849 String str(buffer); 850 GlobalUnlock(cbData); 851 ::CloseClipboard(); 852 RefPtr<DocumentFragment> fragment = createFragmentFromText(context, str); 853 if (fragment) 854 return fragment.release(); 855 } else 856 ::CloseClipboard(); 857 } 858 } 859 860 return 0; 861} 862 863void Pasteboard::setExternalDataObject(IDataObject *dataObject) 864{ 865 m_writableDataObject = 0; 866 m_dataObject = dataObject; 867} 868 869static CachedImage* getCachedImage(Element& element) 870{ 871 // Attempt to pull CachedImage from element 872 RenderObject* renderer = element.renderer(); 873 if (!renderer || !renderer->isRenderImage()) 874 return 0; 875 876 RenderImage* image = toRenderImage(renderer); 877 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) 878 return image->cachedImage(); 879 880 return 0; 881} 882 883static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image) 884{ 885 ASSERT_ARG(image, image); 886 ASSERT(image->image()->data()); 887 888 HRESULT hr = S_OK; 889 String fsPath; 890 HGLOBAL memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); 891 if (!memObj) 892 return 0; 893 894 FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj); 895 if (!fgd) { 896 GlobalFree(memObj); 897 return 0; 898 } 899 900 memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR)); 901 fgd->cItems = 1; 902 fgd->fgd[0].dwFlags = FD_FILESIZE; 903 fgd->fgd[0].nFileSizeLow = image->image()->data()->size(); 904 905 const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title; 906 String extension = image->image()->filenameExtension(); 907 if (extension.isEmpty()) { 908 // Do not continue processing in the rare and unusual case where a decoded image is not able 909 // to provide a filename extension. Something tricky (like a bait-n-switch) is going on 910 GlobalUnlock(memObj); 911 GlobalFree(memObj); 912 return 0; 913 } 914 extension.insert(".", 0); 915 fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.charactersWithNullTermination().data(), false); 916 917 if (fsPath.length() <= 0) { 918 GlobalUnlock(memObj); 919 GlobalFree(memObj); 920 return 0; 921 } 922 923 int maxSize = std::min<int>(fsPath.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].cFileName)); 924 StringView(fsPath).substring(0, maxSize).getCharactersWithUpconvert(fgd->fgd[0].cFileName); 925 GlobalUnlock(memObj); 926 927 return memObj; 928} 929 930static HGLOBAL createGlobalImageFileContent(SharedBuffer* data) 931{ 932 HGLOBAL memObj = GlobalAlloc(GPTR, data->size()); 933 if (!memObj) 934 return 0; 935 936 char* fileContents = (PSTR)GlobalLock(memObj); 937 if (!fileContents) { 938 GlobalFree(memObj); 939 return 0; 940 } 941 942 if (data->data()) 943 CopyMemory(fileContents, data->data(), data->size()); 944 945 GlobalUnlock(memObj); 946 947 return memObj; 948} 949 950static HGLOBAL createGlobalHDropContent(const URL& url, String& fileName, SharedBuffer* data) 951{ 952 if (fileName.isEmpty() || !data) 953 return 0; 954 955 WCHAR filePath[MAX_PATH]; 956 957 if (url.isLocalFile()) { 958 String localPath = decodeURLEscapeSequences(url.path()); 959 // windows does not enjoy a leading slash on paths 960 if (localPath[0] == '/') 961 localPath = localPath.substring(1); 962 const Vector<UChar>& localPathWide = localPath.charactersWithNullTermination(); 963 LPCWSTR localPathStr = localPathWide.data(); 964 if (localPathStr && wcslen(localPathStr) + 1 < MAX_PATH) 965 wcscpy_s(filePath, MAX_PATH, localPathStr); 966 else 967 return 0; 968 } else { 969#if OS(WINCE) 970 notImplemented(); 971 return 0; 972#else 973 WCHAR tempPath[MAX_PATH]; 974 WCHAR extension[MAX_PATH]; 975 if (!::GetTempPath(WTF_ARRAY_LENGTH(tempPath), tempPath)) 976 return 0; 977 if (!::PathAppend(tempPath, fileName.charactersWithNullTermination().data())) 978 return 0; 979 LPCWSTR foundExtension = ::PathFindExtension(tempPath); 980 if (foundExtension) { 981 if (wcscpy_s(extension, MAX_PATH, foundExtension)) 982 return 0; 983 } else 984 *extension = 0; 985 ::PathRemoveExtension(tempPath); 986 for (int i = 1; i < 10000; i++) { 987 if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1) 988 return 0; 989 if (!::PathFileExists(filePath)) 990 break; 991 } 992 HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 993 if (tempFileHandle == INVALID_HANDLE_VALUE) 994 return 0; 995 996 // Write the data to this temp file. 997 DWORD written; 998 BOOL tempWriteSucceeded = FALSE; 999 if (data->data()) 1000 tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0); 1001 CloseHandle(tempFileHandle); 1002 if (!tempWriteSucceeded) 1003 return 0; 1004#endif 1005 } 1006 1007 SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2)); 1008 HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize); 1009 if (!memObj) 1010 return 0; 1011 1012 DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj); 1013 if (!dropFiles) { 1014 GlobalFree(memObj); 1015 return 0; 1016 } 1017 1018 dropFiles->pFiles = sizeof(DROPFILES); 1019 dropFiles->fWide = TRUE; 1020 wcscpy((LPWSTR)(dropFiles + 1), filePath); 1021 GlobalUnlock(memObj); 1022 1023 return memObj; 1024} 1025 1026void Pasteboard::writeImageToDataObject(Element& element, const URL& url) 1027{ 1028 // Shove image data into a DataObject for use as a file 1029 CachedImage* cachedImage = getCachedImage(element); 1030 if (!cachedImage || !cachedImage->imageForRenderer(element.renderer()) || !cachedImage->isLoaded()) 1031 return; 1032 1033 SharedBuffer* imageBuffer = cachedImage->imageForRenderer(element.renderer())->data(); 1034 if (!imageBuffer || !imageBuffer->size()) 1035 return; 1036 1037 HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element.getAttribute(HTMLNames::altAttr), cachedImage); 1038 if (!imageFileDescriptor) 1039 return; 1040 1041 HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer); 1042 if (!imageFileContent) { 1043 GlobalFree(imageFileDescriptor); 1044 return; 1045 } 1046 1047 String fileName = cachedImage->response().suggestedFilename(); 1048 HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer); 1049 if (!hDropContent) { 1050 GlobalFree(imageFileDescriptor); 1051 GlobalFree(imageFileContent); 1052 return; 1053 } 1054 1055 writeFileToDataObject(m_writableDataObject.get(), imageFileDescriptor, imageFileContent, hDropContent); 1056} 1057 1058void Pasteboard::writeURLToWritableDataObject(const URL& url, const String& title) 1059{ 1060 WebCore::writeURL(m_writableDataObject.get(), url, title, true, false); 1061} 1062 1063void Pasteboard::writeMarkup(const String& markup) 1064{ 1065 Vector<char> data; 1066 markupToCFHTML(markup, "", data); 1067 1068 STGMEDIUM medium = {0}; 1069 medium.tymed = TYMED_HGLOBAL; 1070 1071 medium.hGlobal = createGlobalData(data); 1072 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE))) 1073 GlobalFree(medium.hGlobal); 1074} 1075 1076} // namespace WebCore 1077