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