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