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 62using namespace std; 63 64namespace WebCore { 65 66using 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 71enum ClipboardDataType { ClipboardDataTypeNone, ClipboardDataTypeURL, ClipboardDataTypeText, ClipboardDataTypeTextHTML }; 72 73static 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 88static 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 95static 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) 103static 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 118static 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 171static 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 186static 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 254static 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 297static 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 329exit: 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 341PassRefPtr<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 348ClipboardWin::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 356ClipboardWin::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 364ClipboardWin::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 373ClipboardWin::~ClipboardWin() 374{ 375} 376 377static 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 421void 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 441void 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 453String 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 474bool 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 503static 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 518HashSet<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 552PassRefPtr<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 595void 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 610void ClipboardWin::setDragImage(CachedImage* img, const IntPoint &loc) 611{ 612 setDragImage(img, 0, loc); 613} 614 615void ClipboardWin::setDragImageElement(Node *node, const IntPoint &loc) 616{ 617 setDragImage(0, node, loc); 618} 619 620DragImageRef 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 634static String imageToMarkup(const String& url) 635{ 636 String markup("<img src=\""); 637 markup.append(url); 638 markup.append("\"/>"); 639 return markup; 640} 641 642static 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 657static 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 688void 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 715void 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 757void 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 786void 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 804bool 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 830void ClipboardWin::setExternalDataObject(IDataObject *dataObject) 831{ 832 ASSERT(isForDragAndDrop()); 833 834 m_writableDataObject = 0; 835 m_dataObject = dataObject; 836} 837 838} // namespace WebCore 839