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