1 /* 2 * Copyright (C) 2006, 2007 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 "ClipboardWin.h" 28 29 #include "CString.h" 30 #include "CachedImage.h" 31 #include "ClipboardUtilitiesWin.h" 32 #include "Document.h" 33 #include "DragData.h" 34 #include "Editor.h" 35 #include "Element.h" 36 #include "EventHandler.h" 37 #include "FileList.h" 38 #include "Frame.h" 39 #include "FrameLoader.h" 40 #include "FrameView.h" 41 #include "HTMLNames.h" 42 #include "Image.h" 43 #include "MIMETypeRegistry.h" 44 #include "NotImplemented.h" 45 #include "Page.h" 46 #include "Pasteboard.h" 47 #include "PlatformMouseEvent.h" 48 #include "PlatformString.h" 49 #include "Range.h" 50 #include "RenderImage.h" 51 #include "ResourceResponse.h" 52 #include "StringHash.h" 53 #include "WCDataObject.h" 54 #include "csshelper.h" 55 #include "markup.h" 56 57 #include <shlwapi.h> 58 #include <wininet.h> 59 60 #include <wtf/RefPtr.h> 61 62 using namespace std; 63 64 namespace WebCore { 65 66 using namespace HTMLNames; 67 68 // format string for 69 static const char szShellDotUrlTemplate[] = "[InternetShortcut]\r\nURL=%s\r\n"; 70 71 // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft 72 // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 73 74 enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText }; 75 76 static ClipboardDataType clipboardTypeFromMIMEType(const String& type) 77 { 78 String qType = type.stripWhiteSpace().lower(); 79 80 // two special cases for IE compatibility 81 if (qType == "text" || qType == "text/plain" || qType.startsWith("text/plain;")) 82 return ClipboardDataTypeText; 83 if (qType == "url" || qType == "text/uri-list") 84 return ClipboardDataTypeURL; 85 86 return ClipboardDataTypeNone; 87 } 88 89 static inline FORMATETC* fileDescriptorFormat() 90 { 91 static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); 92 static FORMATETC fileDescriptorFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; 93 return &fileDescriptorFormat; 94 } 95 96 static inline FORMATETC* fileContentFormatZero() 97 { 98 static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS); 99 static FORMATETC fileContentFormat = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL}; 100 return &fileContentFormat; 101 } 102 103 static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length) 104 { 105 size_t writeTo = 0; 106 size_t readFrom = 0; 107 while (readFrom < length) { 108 UINT type = PathGetCharType(psz[readFrom]); 109 if (psz[readFrom] == 0 || type & (GCT_LFNCHAR | GCT_SHORTCHAR)) { 110 psz[writeTo++] = psz[readFrom]; 111 } 112 113 readFrom++; 114 } 115 psz[writeTo] = 0; 116 } 117 118 static String filesystemPathFromUrlOrTitle(const String& url, const String& title, TCHAR* extension, bool isLink) 119 { 120 static const size_t fsPathMaxLengthExcludingNullTerminator = MAX_PATH - 1; 121 bool usedURL = false; 122 WCHAR fsPathBuffer[MAX_PATH]; 123 fsPathBuffer[0] = 0; 124 int extensionLen = extension ? lstrlen(extension) : 0; 125 int fsPathMaxLengthExcludingExtension = fsPathMaxLengthExcludingNullTerminator - extensionLen; 126 127 if (!title.isEmpty()) { 128 size_t len = min<size_t>(title.length(), fsPathMaxLengthExcludingExtension); 129 CopyMemory(fsPathBuffer, title.characters(), len * sizeof(UChar)); 130 fsPathBuffer[len] = 0; 131 pathRemoveBadFSCharacters(fsPathBuffer, len); 132 } 133 134 if (!lstrlen(fsPathBuffer)) { 135 KURL kurl(ParsedURLString, url); 136 usedURL = true; 137 // The filename for any content based drag or file url should be the last element of 138 // the path. If we can't find it, or we're coming up with the name for a link 139 // we just use the entire url. 140 DWORD len = fsPathMaxLengthExcludingExtension; 141 String lastComponent = kurl.lastPathComponent(); 142 if (kurl.isLocalFile() || (!isLink && !lastComponent.isEmpty())) { 143 len = min<DWORD>(fsPathMaxLengthExcludingExtension, lastComponent.length()); 144 CopyMemory(fsPathBuffer, lastComponent.characters(), len * sizeof(UChar)); 145 } else { 146 len = min<DWORD>(fsPathMaxLengthExcludingExtension, url.length()); 147 CopyMemory(fsPathBuffer, url.characters(), len * sizeof(UChar)); 148 } 149 fsPathBuffer[len] = 0; 150 pathRemoveBadFSCharacters(fsPathBuffer, len); 151 } 152 153 if (!extension) 154 return String(static_cast<UChar*>(fsPathBuffer)); 155 156 if (!isLink && usedURL) { 157 PathRenameExtension(fsPathBuffer, extension); 158 return String(static_cast<UChar*>(fsPathBuffer)); 159 } 160 161 String result(static_cast<UChar*>(fsPathBuffer)); 162 result += String(static_cast<UChar*>(extension)); 163 return result; 164 } 165 166 static HGLOBAL createGlobalURLContent(const String& url, int estimatedFileSize) 167 { 168 HRESULT hr = S_OK; 169 HGLOBAL memObj = 0; 170 171 char* fileContents; 172 char ansiUrl[INTERNET_MAX_URL_LENGTH + 1]; 173 // Used to generate the buffer. This is null terminated whereas the fileContents won't be. 174 char contentGenerationBuffer[INTERNET_MAX_URL_LENGTH + ARRAYSIZE(szShellDotUrlTemplate) + 1]; 175 176 if (estimatedFileSize > 0 && estimatedFileSize > ARRAYSIZE(contentGenerationBuffer)) 177 return 0; 178 179 int ansiUrlSize = ::WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)url.characters(), url.length(), ansiUrl, ARRAYSIZE(ansiUrl) - 1, 0, 0); 180 if (!ansiUrlSize) 181 return 0; 182 183 ansiUrl[ansiUrlSize] = 0; 184 185 int fileSize = (int) (ansiUrlSize+strlen(szShellDotUrlTemplate)-2); // -2 to remove the %s 186 ASSERT(estimatedFileSize < 0 || fileSize == estimatedFileSize); 187 188 memObj = GlobalAlloc(GPTR, fileSize); 189 if (!memObj) 190 return 0; 191 192 fileContents = (PSTR)GlobalLock(memObj); 193 194 sprintf_s(contentGenerationBuffer, ARRAYSIZE(contentGenerationBuffer), szShellDotUrlTemplate, ansiUrl); 195 CopyMemory(fileContents, contentGenerationBuffer, fileSize); 196 197 GlobalUnlock(memObj); 198 199 return memObj; 200 } 201 202 static HGLOBAL createGlobalImageFileContent(SharedBuffer* data) 203 { 204 HGLOBAL memObj = GlobalAlloc(GPTR, data->size()); 205 if (!memObj) 206 return 0; 207 208 char* fileContents = (PSTR)GlobalLock(memObj); 209 210 CopyMemory(fileContents, data->data(), data->size()); 211 212 GlobalUnlock(memObj); 213 214 return memObj; 215 } 216 217 static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, SharedBuffer* data) 218 { 219 if (fileName.isEmpty() || !data ) 220 return 0; 221 222 WCHAR filePath[MAX_PATH]; 223 224 if (url.isLocalFile()) { 225 String localPath = url.path(); 226 // windows does not enjoy a leading slash on paths 227 if (localPath[0] == '/') 228 localPath = localPath.substring(1); 229 LPCTSTR localPathStr = localPath.charactersWithNullTermination(); 230 if (wcslen(localPathStr) + 1 < MAX_PATH) 231 wcscpy_s(filePath, MAX_PATH, localPathStr); 232 else 233 return 0; 234 } else { 235 WCHAR tempPath[MAX_PATH]; 236 WCHAR extension[MAX_PATH]; 237 if (!::GetTempPath(ARRAYSIZE(tempPath), tempPath)) 238 return 0; 239 if (!::PathAppend(tempPath, fileName.charactersWithNullTermination())) 240 return 0; 241 LPCWSTR foundExtension = ::PathFindExtension(tempPath); 242 if (foundExtension) { 243 if (wcscpy_s(extension, MAX_PATH, foundExtension)) 244 return 0; 245 } else 246 *extension = 0; 247 ::PathRemoveExtension(tempPath); 248 for (int i = 1; i < 10000; i++) { 249 if (swprintf_s(filePath, MAX_PATH, TEXT("%s-%d%s"), tempPath, i, extension) == -1) 250 return 0; 251 if (!::PathFileExists(filePath)) 252 break; 253 } 254 HANDLE tempFileHandle = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); 255 if (tempFileHandle == INVALID_HANDLE_VALUE) 256 return 0; 257 258 // Write the data to this temp file. 259 DWORD written; 260 BOOL tempWriteSucceeded = WriteFile(tempFileHandle, data->data(), data->size(), &written, 0); 261 CloseHandle(tempFileHandle); 262 if (!tempWriteSucceeded) 263 return 0; 264 } 265 266 SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (wcslen(filePath) + 2)); 267 HGLOBAL memObj = GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize); 268 if (!memObj) 269 return 0; 270 271 DROPFILES* dropFiles = (DROPFILES*) GlobalLock(memObj); 272 dropFiles->pFiles = sizeof(DROPFILES); 273 dropFiles->fWide = TRUE; 274 wcscpy((LPWSTR)(dropFiles + 1), filePath); 275 GlobalUnlock(memObj); 276 277 return memObj; 278 } 279 280 static HGLOBAL createGlobalUrlFileDescriptor(const String& url, const String& title, int& /*out*/ estimatedSize) 281 { 282 HRESULT hr = S_OK; 283 HGLOBAL memObj = 0; 284 String fsPath; 285 memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); 286 if (!memObj) 287 return 0; 288 289 FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj); 290 memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR)); 291 fgd->cItems = 1; 292 fgd->fgd[0].dwFlags = FD_FILESIZE; 293 int fileSize = ::WideCharToMultiByte(CP_ACP, 0, url.characters(), url.length(), 0, 0, 0, 0); 294 fileSize += strlen(szShellDotUrlTemplate) - 2; // -2 is for getting rid of %s in the template string 295 fgd->fgd[0].nFileSizeLow = fileSize; 296 estimatedSize = fileSize; 297 fsPath = filesystemPathFromUrlOrTitle(url, title, L".URL", true); 298 299 if (fsPath.length() <= 0) { 300 GlobalUnlock(memObj); 301 GlobalFree(memObj); 302 return 0; 303 } 304 305 int maxSize = min(fsPath.length(), ARRAYSIZE(fgd->fgd[0].cFileName)); 306 CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * sizeof(UChar)); 307 GlobalUnlock(memObj); 308 309 return memObj; 310 } 311 312 313 static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& title, CachedImage* image) 314 { 315 ASSERT_ARG(image, image); 316 ASSERT(image->image()->data()); 317 318 HRESULT hr = S_OK; 319 HGLOBAL memObj = 0; 320 String fsPath; 321 memObj = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); 322 if (!memObj) 323 return 0; 324 325 FILEGROUPDESCRIPTOR* fgd = (FILEGROUPDESCRIPTOR*)GlobalLock(memObj); 326 memset(fgd, 0, sizeof(FILEGROUPDESCRIPTOR)); 327 fgd->cItems = 1; 328 fgd->fgd[0].dwFlags = FD_FILESIZE; 329 fgd->fgd[0].nFileSizeLow = image->image()->data()->size(); 330 331 const String& preferredTitle = title.isEmpty() ? image->response().suggestedFilename() : title; 332 String extension = image->image()->filenameExtension(); 333 if (extension.isEmpty()) { 334 // Do not continue processing in the rare and unusual case where a decoded image is not able 335 // to provide a filename extension. Something tricky (like a bait-n-switch) is going on 336 return 0; 337 } 338 extension.insert(".", 0); 339 fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, (TCHAR*)extension.charactersWithNullTermination(), false); 340 341 if (fsPath.length() <= 0) { 342 GlobalUnlock(memObj); 343 GlobalFree(memObj); 344 return 0; 345 } 346 347 int maxSize = min(fsPath.length(), ARRAYSIZE(fgd->fgd[0].cFileName)); 348 CopyMemory(fgd->fgd[0].cFileName, (LPCWSTR)fsPath.characters(), maxSize * sizeof(UChar)); 349 GlobalUnlock(memObj); 350 351 return memObj; 352 } 353 354 355 // writeFileToDataObject takes ownership of fileDescriptor and fileContent 356 static HRESULT writeFileToDataObject(IDataObject* dataObject, HGLOBAL fileDescriptor, HGLOBAL fileContent, HGLOBAL hDropContent) 357 { 358 HRESULT hr = S_OK; 359 FORMATETC* fe; 360 STGMEDIUM medium = {0}; 361 medium.tymed = TYMED_HGLOBAL; 362 363 if (!fileDescriptor || !fileContent) 364 goto exit; 365 366 // Descriptor 367 fe = fileDescriptorFormat(); 368 369 medium.hGlobal = fileDescriptor; 370 371 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE))) 372 goto exit; 373 374 // Contents 375 fe = fileContentFormatZero(); 376 medium.hGlobal = fileContent; 377 if (FAILED(hr = dataObject->SetData(fe, &medium, TRUE))) 378 goto exit; 379 380 // HDROP 381 if (hDropContent) { 382 medium.hGlobal = hDropContent; 383 hr = dataObject->SetData(cfHDropFormat(), &medium, TRUE); 384 } 385 386 exit: 387 if (FAILED(hr)) { 388 if (fileDescriptor) 389 GlobalFree(fileDescriptor); 390 if (fileContent) 391 GlobalFree(fileContent); 392 if (hDropContent) 393 GlobalFree(hDropContent); 394 } 395 return hr; 396 } 397 398 ClipboardWin::ClipboardWin(bool isForDragging, IDataObject* dataObject, ClipboardAccessPolicy policy) 399 : Clipboard(policy, isForDragging) 400 , m_dataObject(dataObject) 401 , m_writableDataObject(0) 402 { 403 } 404 405 ClipboardWin::ClipboardWin(bool isForDragging, WCDataObject* dataObject, ClipboardAccessPolicy policy) 406 : Clipboard(policy, isForDragging) 407 , m_dataObject(dataObject) 408 , m_writableDataObject(dataObject) 409 { 410 } 411 412 ClipboardWin::~ClipboardWin() 413 { 414 } 415 416 static bool writeURL(WCDataObject *data, const KURL& url, String title, bool withPlainText, bool withHTML) 417 { 418 ASSERT(data); 419 420 if (url.isEmpty()) 421 return false; 422 423 if (title.isEmpty()) { 424 title = url.lastPathComponent(); 425 if (title.isEmpty()) 426 title = url.host(); 427 } 428 429 STGMEDIUM medium = {0}; 430 medium.tymed = TYMED_HGLOBAL; 431 432 medium.hGlobal = createGlobalData(url, title); 433 bool success = false; 434 if (medium.hGlobal && FAILED(data->SetData(urlWFormat(), &medium, TRUE))) 435 ::GlobalFree(medium.hGlobal); 436 else 437 success = true; 438 439 if (withHTML) { 440 Vector<char> cfhtmlData; 441 markupToCF_HTML(urlToMarkup(url, title), "", cfhtmlData); 442 medium.hGlobal = createGlobalData(cfhtmlData); 443 if (medium.hGlobal && FAILED(data->SetData(htmlFormat(), &medium, TRUE))) 444 ::GlobalFree(medium.hGlobal); 445 else 446 success = true; 447 } 448 449 if (withPlainText) { 450 medium.hGlobal = createGlobalData(url.string()); 451 if (medium.hGlobal && FAILED(data->SetData(plainTextWFormat(), &medium, TRUE))) 452 ::GlobalFree(medium.hGlobal); 453 else 454 success = true; 455 } 456 457 return success; 458 } 459 460 void ClipboardWin::clearData(const String& type) 461 { 462 //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941> 463 ASSERT(isForDragging()); 464 if (policy() != ClipboardWritable || !m_writableDataObject) 465 return; 466 467 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); 468 469 if (dataType == ClipboardDataTypeURL) { 470 m_writableDataObject->clearData(urlWFormat()->cfFormat); 471 m_writableDataObject->clearData(urlFormat()->cfFormat); 472 } 473 if (dataType == ClipboardDataTypeText) { 474 m_writableDataObject->clearData(plainTextFormat()->cfFormat); 475 m_writableDataObject->clearData(plainTextWFormat()->cfFormat); 476 } 477 478 } 479 480 void ClipboardWin::clearAllData() 481 { 482 //FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941> 483 ASSERT(isForDragging()); 484 if (policy() != ClipboardWritable) 485 return; 486 487 m_writableDataObject = 0; 488 WCDataObject::createInstance(&m_writableDataObject); 489 m_dataObject = m_writableDataObject; 490 } 491 492 String ClipboardWin::getData(const String& type, bool& success) const 493 { 494 success = false; 495 if (policy() != ClipboardReadable || !m_dataObject) { 496 return ""; 497 } 498 499 ClipboardDataType dataType = clipboardTypeFromMIMEType(type); 500 if (dataType == ClipboardDataTypeText) 501 return getPlainText(m_dataObject.get(), success); 502 else if (dataType == ClipboardDataTypeURL) 503 return getURL(m_dataObject.get(), success); 504 505 return ""; 506 } 507 508 bool ClipboardWin::setData(const String& type, const String& data) 509 { 510 // FIXME: Need to be able to write to the system clipboard <rdar://problem/5015941> 511 ASSERT(isForDragging()); 512 if (policy() != ClipboardWritable || !m_writableDataObject) 513 return false; 514 515 ClipboardDataType winType = clipboardTypeFromMIMEType(type); 516 517 if (winType == ClipboardDataTypeURL) 518 return WebCore::writeURL(m_writableDataObject.get(), KURL(ParsedURLString, data), String(), false, true); 519 520 if (winType == ClipboardDataTypeText) { 521 STGMEDIUM medium = {0}; 522 medium.tymed = TYMED_HGLOBAL; 523 medium.hGlobal = createGlobalData(data); 524 if (!medium.hGlobal) 525 return false; 526 527 if (FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) { 528 ::GlobalFree(medium.hGlobal); 529 return false; 530 } 531 return true; 532 } 533 534 return false; 535 } 536 537 static void addMimeTypesForFormat(HashSet<String>& results, const FORMATETC& format) 538 { 539 // URL and Text are provided for compatibility with IE's model 540 if (format.cfFormat == urlFormat()->cfFormat || format.cfFormat == urlWFormat()->cfFormat) { 541 results.add("URL"); 542 results.add("text/uri-list"); 543 } 544 545 if (format.cfFormat == plainTextWFormat()->cfFormat || format.cfFormat == plainTextFormat()->cfFormat) { 546 results.add("Text"); 547 results.add("text/plain"); 548 } 549 } 550 551 // extensions beyond IE's API 552 HashSet<String> ClipboardWin::types() const 553 { 554 HashSet<String> results; 555 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) 556 return results; 557 558 if (!m_dataObject) 559 return results; 560 561 COMPtr<IEnumFORMATETC> itr; 562 563 if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) 564 return results; 565 566 if (!itr) 567 return results; 568 569 FORMATETC data; 570 571 // IEnumFORMATETC::Next returns S_FALSE if there are no more items. 572 while (itr->Next(1, &data, 0) == S_OK) { 573 addMimeTypesForFormat(results, data); 574 } 575 576 return results; 577 } 578 579 PassRefPtr<FileList> ClipboardWin::files() const 580 { 581 RefPtr<FileList> files = FileList::create(); 582 if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) 583 return files.release(); 584 585 if (!m_dataObject) 586 return files.release(); 587 588 STGMEDIUM medium; 589 if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium))) 590 return files.release(); 591 592 HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal)); 593 if (!hdrop) 594 return files.release(); 595 596 WCHAR filename[MAX_PATH]; 597 UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); 598 for (UINT i = 0; i < fileCount; i++) { 599 if (!DragQueryFileW(hdrop, i, filename, ARRAYSIZE(filename))) 600 continue; 601 files->append(File::create(reinterpret_cast<UChar*>(filename))); 602 } 603 604 GlobalUnlock(medium.hGlobal); 605 ReleaseStgMedium(&medium); 606 return files.release(); 607 } 608 609 void ClipboardWin::setDragImage(CachedImage* image, Node *node, const IntPoint &loc) 610 { 611 if (policy() != ClipboardImageWritable && policy() != ClipboardWritable) 612 return; 613 614 if (m_dragImage) 615 m_dragImage->removeClient(this); 616 m_dragImage = image; 617 if (m_dragImage) 618 m_dragImage->addClient(this); 619 620 m_dragLoc = loc; 621 m_dragImageElement = node; 622 } 623 624 void ClipboardWin::setDragImage(CachedImage* img, const IntPoint &loc) 625 { 626 setDragImage(img, 0, loc); 627 } 628 629 void ClipboardWin::setDragImageElement(Node *node, const IntPoint &loc) 630 { 631 setDragImage(0, node, loc); 632 } 633 634 DragImageRef ClipboardWin::createDragImage(IntPoint& loc) const 635 { 636 HBITMAP result = 0; 637 if (m_dragImage) { 638 result = createDragImageFromImage(m_dragImage->image()); 639 loc = m_dragLoc; 640 } else if (m_dragImageElement) { 641 Node* node = m_dragImageElement.get(); 642 result = node->document()->frame()->nodeImage(node); 643 loc = m_dragLoc; 644 } 645 return result; 646 } 647 648 static String imageToMarkup(const String& url) 649 { 650 String markup("<img src=\""); 651 markup.append(url); 652 markup.append("\"/>"); 653 return markup; 654 } 655 656 static CachedImage* getCachedImage(Element* element) 657 { 658 // Attempt to pull CachedImage from element 659 ASSERT(element); 660 RenderObject* renderer = element->renderer(); 661 if (!renderer || !renderer->isImage()) 662 return 0; 663 664 RenderImage* image = toRenderImage(renderer); 665 if (image->cachedImage() && !image->cachedImage()->errorOccurred()) 666 return image->cachedImage(); 667 668 return 0; 669 } 670 671 static void writeImageToDataObject(IDataObject* dataObject, Element* element, const KURL& url) 672 { 673 // Shove image data into a DataObject for use as a file 674 CachedImage* cachedImage = getCachedImage(element); 675 if (!cachedImage || !cachedImage->image() || !cachedImage->isLoaded()) 676 return; 677 678 SharedBuffer* imageBuffer = cachedImage->image()->data(); 679 if (!imageBuffer || !imageBuffer->size()) 680 return; 681 682 HGLOBAL imageFileDescriptor = createGlobalImageFileDescriptor(url.string(), element->getAttribute(altAttr), cachedImage); 683 if (!imageFileDescriptor) 684 return; 685 686 HGLOBAL imageFileContent = createGlobalImageFileContent(imageBuffer); 687 if (!imageFileContent) { 688 GlobalFree(imageFileDescriptor); 689 return; 690 } 691 692 String fileName = cachedImage->response().suggestedFilename(); 693 HGLOBAL hDropContent = createGlobalHDropContent(url, fileName, imageBuffer); 694 if (!hDropContent) { 695 GlobalFree(hDropContent); 696 return; 697 } 698 699 writeFileToDataObject(dataObject, imageFileDescriptor, imageFileContent, hDropContent); 700 } 701 702 void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) 703 { 704 // Order is important here for Explorer's sake 705 if (!m_writableDataObject) 706 return; 707 WebCore::writeURL(m_writableDataObject.get(), url, title, true, false); 708 709 writeImageToDataObject(m_writableDataObject.get(), element, url); 710 711 AtomicString imageURL = element->getAttribute(srcAttr); 712 if (imageURL.isEmpty()) 713 return; 714 715 String fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL)).string(); 716 if (fullURL.isEmpty()) 717 return; 718 STGMEDIUM medium = {0}; 719 medium.tymed = TYMED_HGLOBAL; 720 ExceptionCode ec = 0; 721 722 // Put img tag on the clipboard referencing the image 723 Vector<char> data; 724 markupToCF_HTML(imageToMarkup(fullURL), "", data); 725 medium.hGlobal = createGlobalData(data); 726 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE))) 727 ::GlobalFree(medium.hGlobal); 728 } 729 730 void ClipboardWin::writeURL(const KURL& kurl, const String& titleStr, Frame*) 731 { 732 if (!m_writableDataObject) 733 return; 734 WebCore::writeURL(m_writableDataObject.get(), kurl, titleStr, true, true); 735 736 int estimatedSize = 0; 737 String url = kurl.string(); 738 739 HGLOBAL urlFileDescriptor = createGlobalUrlFileDescriptor(url, titleStr, estimatedSize); 740 if (!urlFileDescriptor) 741 return; 742 HGLOBAL urlFileContent = createGlobalURLContent(url, estimatedSize); 743 if (!urlFileContent) { 744 GlobalFree(urlFileDescriptor); 745 return; 746 } 747 writeFileToDataObject(m_writableDataObject.get(), urlFileDescriptor, urlFileContent, 0); 748 } 749 750 void ClipboardWin::writeRange(Range* selectedRange, Frame* frame) 751 { 752 ASSERT(selectedRange); 753 if (!m_writableDataObject) 754 return; 755 756 STGMEDIUM medium = {0}; 757 medium.tymed = TYMED_HGLOBAL; 758 ExceptionCode ec = 0; 759 760 Vector<char> data; 761 markupToCF_HTML(createMarkup(selectedRange, 0, AnnotateForInterchange), 762 selectedRange->startContainer(ec)->document()->url().string(), data); 763 medium.hGlobal = createGlobalData(data); 764 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(htmlFormat(), &medium, TRUE))) 765 ::GlobalFree(medium.hGlobal); 766 767 String str = frame->selectedText(); 768 replaceNewlinesWithWindowsStyleNewlines(str); 769 replaceNBSPWithSpace(str); 770 medium.hGlobal = createGlobalData(str); 771 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) 772 ::GlobalFree(medium.hGlobal); 773 774 medium.hGlobal = 0; 775 if (frame->editor()->canSmartCopyOrDelete()) 776 m_writableDataObject->SetData(smartPasteFormat(), &medium, TRUE); 777 } 778 779 void ClipboardWin::writePlainText(const String& text) 780 { 781 if (!m_writableDataObject) 782 return; 783 784 STGMEDIUM medium = {0}; 785 medium.tymed = TYMED_HGLOBAL; 786 ExceptionCode ec = 0; 787 788 String str = text; 789 replaceNewlinesWithWindowsStyleNewlines(str); 790 replaceNBSPWithSpace(str); 791 medium.hGlobal = createGlobalData(str); 792 if (medium.hGlobal && FAILED(m_writableDataObject->SetData(plainTextWFormat(), &medium, TRUE))) 793 ::GlobalFree(medium.hGlobal); 794 795 medium.hGlobal = 0; 796 } 797 798 bool ClipboardWin::hasData() 799 { 800 if (!m_dataObject) 801 return false; 802 803 COMPtr<IEnumFORMATETC> itr; 804 if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) 805 return false; 806 807 if (!itr) 808 return false; 809 810 FORMATETC data; 811 812 // IEnumFORMATETC::Next returns S_FALSE if there are no more items. 813 if (itr->Next(1, &data, 0) == S_OK) { 814 // There is at least one item in the IDataObject 815 return true; 816 } 817 818 return false; 819 } 820 821 void ClipboardWin::setExternalDataObject(IDataObject *dataObject) 822 { 823 ASSERT(isForDragging()); 824 825 m_writableDataObject = 0; 826 m_dataObject = dataObject; 827 } 828 829 } // namespace WebCore 830