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