| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2007, 2008 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 "ClipboardUtilitiesWin.h" | |
| 28 | |
| 29 #include "DocumentFragment.h" | |
| 30 #include "KURL.h" | |
| 31 #include "TextEncoding.h" | |
| 32 #include "markup.h" | |
| 33 #include <shlobj.h> | |
| 34 #include <wininet.h> // for INTERNET_MAX_URL_LENGTH | |
| 35 #include <wtf/StringExtras.h> | |
| 36 #include <wtf/text/CString.h> | |
| 37 #include <wtf/text/StringBuilder.h> | |
| 38 #include <wtf/text/WTFString.h> | |
| 39 | |
| 40 #if !OS(WINCE) | |
| 41 #include <shlwapi.h> | |
| 42 #endif | |
| 43 | |
| 44 #if USE(CF) | |
| 45 #include <CoreFoundation/CoreFoundation.h> | |
| 46 #include <wtf/RetainPtr.h> | |
| 47 #endif | |
| 48 | |
| 49 namespace WebCore { | |
| 50 | |
| 51 #if USE(CF) | |
| 52 FORMATETC* cfHDropFormat() | |
| 53 { | |
| 54 static FORMATETC urlFormat = {CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOB
AL}; | |
| 55 return &urlFormat; | |
| 56 } | |
| 57 | |
| 58 static bool urlFromPath(CFStringRef path, String& url) | |
| 59 { | |
| 60 if (!path) | |
| 61 return false; | |
| 62 | |
| 63 RetainPtr<CFURLRef> cfURL(AdoptCF, CFURLCreateWithFileSystemPath(0, path, kC
FURLWindowsPathStyle, false)); | |
| 64 if (!cfURL) | |
| 65 return false; | |
| 66 | |
| 67 url = CFURLGetString(cfURL.get()); | |
| 68 | |
| 69 // Work around <rdar://problem/6708300>, where CFURLCreateWithFileSystemPath
makes URLs with "localhost". | |
| 70 if (url.startsWith("file://localhost/")) | |
| 71 url.remove(7, 9); | |
| 72 | |
| 73 return true; | |
| 74 } | |
| 75 #endif | |
| 76 | |
| 77 static bool getDataMapItem(const DragDataMap* dataObject, FORMATETC* format, Str
ing& item) | |
| 78 { | |
| 79 DragDataMap::const_iterator found = dataObject->find(format->cfFormat); | |
| 80 if (found == dataObject->end()) | |
| 81 return false; | |
| 82 item = found->value[0]; | |
| 83 return true; | |
| 84 } | |
| 85 | |
| 86 static bool getWebLocData(IDataObject* dataObject, String& url, String* title) | |
| 87 { | |
| 88 bool succeeded = false; | |
| 89 #if USE(CF) | |
| 90 WCHAR filename[MAX_PATH]; | |
| 91 WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH]; | |
| 92 | |
| 93 STGMEDIUM medium; | |
| 94 if (FAILED(dataObject->GetData(cfHDropFormat(), &medium))) | |
| 95 return false; | |
| 96 | |
| 97 HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal)); | |
| 98 | |
| 99 if (!hdrop) | |
| 100 return false; | |
| 101 | |
| 102 if (!DragQueryFileW(hdrop, 0, filename, WTF_ARRAY_LENGTH(filename))) | |
| 103 goto exit; | |
| 104 | |
| 105 if (_wcsicmp(PathFindExtensionW(filename), L".url")) | |
| 106 goto exit; | |
| 107 | |
| 108 if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF
_ARRAY_LENGTH(urlBuffer), filename)) | |
| 109 goto exit; | |
| 110 | |
| 111 if (title) { | |
| 112 PathRemoveExtension(filename); | |
| 113 *title = String((UChar*)filename); | |
| 114 } | |
| 115 | |
| 116 url = String((UChar*)urlBuffer); | |
| 117 succeeded = true; | |
| 118 | |
| 119 exit: | |
| 120 // Free up memory. | |
| 121 DragFinish(hdrop); | |
| 122 GlobalUnlock(medium.hGlobal); | |
| 123 #endif | |
| 124 return succeeded; | |
| 125 } | |
| 126 | |
| 127 static bool getWebLocData(const DragDataMap* dataObject, String& url, String* ti
tle) | |
| 128 { | |
| 129 #if USE(CF) | |
| 130 WCHAR filename[MAX_PATH]; | |
| 131 WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH]; | |
| 132 | |
| 133 if (!dataObject->contains(cfHDropFormat()->cfFormat)) | |
| 134 return false; | |
| 135 | |
| 136 wcscpy(filename, dataObject->get(cfHDropFormat()->cfFormat)[0].charactersWit
hNullTermination()); | |
| 137 if (_wcsicmp(PathFindExtensionW(filename), L".url")) | |
| 138 return false; | |
| 139 | |
| 140 if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF
_ARRAY_LENGTH(urlBuffer), filename)) | |
| 141 return false; | |
| 142 | |
| 143 if (title) { | |
| 144 PathRemoveExtension(filename); | |
| 145 *title = filename; | |
| 146 } | |
| 147 | |
| 148 url = urlBuffer; | |
| 149 return true; | |
| 150 #else | |
| 151 return false; | |
| 152 #endif | |
| 153 } | |
| 154 | |
| 155 static String extractURL(const String &inURL, String* title) | |
| 156 { | |
| 157 String url = inURL; | |
| 158 int splitLoc = url.find('\n'); | |
| 159 if (splitLoc > 0) { | |
| 160 if (title) | |
| 161 *title = url.substring(splitLoc+1); | |
| 162 url.truncate(splitLoc); | |
| 163 } else if (title) | |
| 164 *title = url; | |
| 165 return url; | |
| 166 } | |
| 167 | |
| 168 // Firefox text/html | |
| 169 static FORMATETC* texthtmlFormat() | |
| 170 { | |
| 171 static UINT cf = RegisterClipboardFormat(L"text/html"); | |
| 172 static FORMATETC texthtmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBA
L}; | |
| 173 return &texthtmlFormat; | |
| 174 } | |
| 175 | |
| 176 HGLOBAL createGlobalData(const KURL& url, const String& title) | |
| 177 { | |
| 178 String mutableURL(url.string()); | |
| 179 String mutableTitle(title); | |
| 180 SIZE_T size = mutableURL.length() + mutableTitle.length() + 2; // +1 for "\n
" and +1 for null terminator | |
| 181 HGLOBAL cbData = ::GlobalAlloc(GPTR, size * sizeof(UChar)); | |
| 182 | |
| 183 if (cbData) { | |
| 184 PWSTR buffer = static_cast<PWSTR>(GlobalLock(cbData)); | |
| 185 _snwprintf(buffer, size, L"%s\n%s", mutableURL.charactersWithNullTermina
tion(), mutableTitle.charactersWithNullTermination()); | |
| 186 GlobalUnlock(cbData); | |
| 187 } | |
| 188 return cbData; | |
| 189 } | |
| 190 | |
| 191 HGLOBAL createGlobalData(const String& str) | |
| 192 { | |
| 193 HGLOBAL globalData = ::GlobalAlloc(GPTR, (str.length() + 1) * sizeof(UChar))
; | |
| 194 if (!globalData) | |
| 195 return 0; | |
| 196 UChar* buffer = static_cast<UChar*>(GlobalLock(globalData)); | |
| 197 memcpy(buffer, str.characters(), str.length() * sizeof(UChar)); | |
| 198 buffer[str.length()] = 0; | |
| 199 GlobalUnlock(globalData); | |
| 200 return globalData; | |
| 201 } | |
| 202 | |
| 203 HGLOBAL createGlobalData(const Vector<char>& vector) | |
| 204 { | |
| 205 HGLOBAL globalData = ::GlobalAlloc(GPTR, vector.size() + 1); | |
| 206 if (!globalData) | |
| 207 return 0; | |
| 208 char* buffer = static_cast<char*>(GlobalLock(globalData)); | |
| 209 memcpy(buffer, vector.data(), vector.size()); | |
| 210 buffer[vector.size()] = 0; | |
| 211 GlobalUnlock(globalData); | |
| 212 return globalData; | |
| 213 } | |
| 214 | |
| 215 static String getFullCFHTML(IDataObject* data) | |
| 216 { | |
| 217 STGMEDIUM store; | |
| 218 if (SUCCEEDED(data->GetData(htmlFormat(), &store))) { | |
| 219 // MS HTML Format parsing | |
| 220 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
| 221 SIZE_T dataSize = ::GlobalSize(store.hGlobal); | |
| 222 String cfhtml(UTF8Encoding().decode(data, dataSize)); | |
| 223 GlobalUnlock(store.hGlobal); | |
| 224 ReleaseStgMedium(&store); | |
| 225 return cfhtml; | |
| 226 } | |
| 227 return String(); | |
| 228 } | |
| 229 | |
| 230 static void append(Vector<char>& vector, const char* string) | |
| 231 { | |
| 232 vector.append(string, strlen(string)); | |
| 233 } | |
| 234 | |
| 235 static void append(Vector<char>& vector, const CString& string) | |
| 236 { | |
| 237 vector.append(string.data(), string.length()); | |
| 238 } | |
| 239 | |
| 240 // Find the markup between "<!--StartFragment -->" and "<!--EndFragment -->", ac
counting for browser quirks. | |
| 241 static String extractMarkupFromCFHTML(const String& cfhtml) | |
| 242 { | |
| 243 unsigned markupStart = cfhtml.find("<html", 0, false); | |
| 244 unsigned tagStart = cfhtml.find("startfragment", markupStart, false); | |
| 245 unsigned fragmentStart = cfhtml.find('>', tagStart) + 1; | |
| 246 unsigned tagEnd = cfhtml.find("endfragment", fragmentStart, false); | |
| 247 unsigned fragmentEnd = cfhtml.reverseFind('<', tagEnd); | |
| 248 return cfhtml.substring(fragmentStart, fragmentEnd - fragmentStart).stripWhi
teSpace(); | |
| 249 } | |
| 250 | |
| 251 // Documentation for the CF_HTML format is available at http://msdn.microsoft.co
m/workshop/networking/clipboard/htmlclipboard.asp | |
| 252 void markupToCFHTML(const String& markup, const String& srcURL, Vector<char>& re
sult) | |
| 253 { | |
| 254 if (markup.isEmpty()) | |
| 255 return; | |
| 256 | |
| 257 #define MAX_DIGITS 10 | |
| 258 #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits) | |
| 259 #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u" | |
| 260 #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS) | |
| 261 | |
| 262 const char* header = "Version:0.9\n" | |
| 263 "StartHTML:" NUMBER_FORMAT "\n" | |
| 264 "EndHTML:" NUMBER_FORMAT "\n" | |
| 265 "StartFragment:" NUMBER_FORMAT "\n" | |
| 266 "EndFragment:" NUMBER_FORMAT "\n"; | |
| 267 const char* sourceURLPrefix = "SourceURL:"; | |
| 268 | |
| 269 const char* startMarkup = "<HTML>\n<BODY>\n<!--StartFragment-->\n"; | |
| 270 const char* endMarkup = "\n<!--EndFragment-->\n</BODY>\n</HTML>"; | |
| 271 | |
| 272 CString sourceURLUTF8 = srcURL == blankURL() ? "" : srcURL.utf8(); | |
| 273 CString markupUTF8 = markup.utf8(); | |
| 274 | |
| 275 // calculate offsets | |
| 276 unsigned startHTMLOffset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + MAX_
DIGITS * 4; | |
| 277 if (sourceURLUTF8.length()) | |
| 278 startHTMLOffset += strlen(sourceURLPrefix) + sourceURLUTF8.length() + 1; | |
| 279 unsigned startFragmentOffset = startHTMLOffset + strlen(startMarkup); | |
| 280 unsigned endFragmentOffset = startFragmentOffset + markupUTF8.length(); | |
| 281 unsigned endHTMLOffset = endFragmentOffset + strlen(endMarkup); | |
| 282 | |
| 283 unsigned headerBufferLength = startHTMLOffset + 1; // + 1 for '\0' terminato
r. | |
| 284 char* headerBuffer = (char*)malloc(headerBufferLength); | |
| 285 snprintf(headerBuffer, headerBufferLength, header, startHTMLOffset, endHTMLO
ffset, startFragmentOffset, endFragmentOffset); | |
| 286 append(result, CString(headerBuffer)); | |
| 287 free(headerBuffer); | |
| 288 if (sourceURLUTF8.length()) { | |
| 289 append(result, sourceURLPrefix); | |
| 290 append(result, sourceURLUTF8); | |
| 291 result.append('\n'); | |
| 292 } | |
| 293 append(result, startMarkup); | |
| 294 append(result, markupUTF8); | |
| 295 append(result, endMarkup); | |
| 296 | |
| 297 #undef MAX_DIGITS | |
| 298 #undef MAKE_NUMBER_FORMAT_1 | |
| 299 #undef MAKE_NUMBER_FORMAT_2 | |
| 300 #undef NUMBER_FORMAT | |
| 301 } | |
| 302 | |
| 303 void replaceNewlinesWithWindowsStyleNewlines(String& str) | |
| 304 { | |
| 305 DEFINE_STATIC_LOCAL(String, windowsNewline, (ASCIILiteral("\r\n"))); | |
| 306 StringBuilder result; | |
| 307 for (unsigned index = 0; index < str.length(); ++index) { | |
| 308 if (str[index] != '\n' || (index > 0 && str[index - 1] == '\r')) | |
| 309 result.append(str[index]); | |
| 310 else | |
| 311 result.append(windowsNewline); | |
| 312 } | |
| 313 str = result.toString(); | |
| 314 } | |
| 315 | |
| 316 void replaceNBSPWithSpace(String& str) | |
| 317 { | |
| 318 static const UChar NonBreakingSpaceCharacter = 0xA0; | |
| 319 static const UChar SpaceCharacter = ' '; | |
| 320 str.replace(NonBreakingSpaceCharacter, SpaceCharacter); | |
| 321 } | |
| 322 | |
| 323 FORMATETC* urlWFormat() | |
| 324 { | |
| 325 static UINT cf = RegisterClipboardFormat(L"UniformResourceLocatorW"); | |
| 326 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
| 327 return &urlFormat; | |
| 328 } | |
| 329 | |
| 330 FORMATETC* urlFormat() | |
| 331 { | |
| 332 static UINT cf = RegisterClipboardFormat(L"UniformResourceLocator"); | |
| 333 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
| 334 return &urlFormat; | |
| 335 } | |
| 336 | |
| 337 FORMATETC* plainTextFormat() | |
| 338 { | |
| 339 static FORMATETC textFormat = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOB
AL}; | |
| 340 return &textFormat; | |
| 341 } | |
| 342 | |
| 343 FORMATETC* plainTextWFormat() | |
| 344 { | |
| 345 static FORMATETC textFormat = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, TYME
D_HGLOBAL}; | |
| 346 return &textFormat; | |
| 347 } | |
| 348 | |
| 349 FORMATETC* filenameWFormat() | |
| 350 { | |
| 351 static UINT cf = RegisterClipboardFormat(L"FileNameW"); | |
| 352 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
| 353 return &urlFormat; | |
| 354 } | |
| 355 | |
| 356 FORMATETC* filenameFormat() | |
| 357 { | |
| 358 static UINT cf = RegisterClipboardFormat(L"FileName"); | |
| 359 static FORMATETC urlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
| 360 return &urlFormat; | |
| 361 } | |
| 362 | |
| 363 // MSIE HTML Format | |
| 364 FORMATETC* htmlFormat() | |
| 365 { | |
| 366 static UINT cf = RegisterClipboardFormat(L"HTML Format"); | |
| 367 static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
| 368 return &htmlFormat; | |
| 369 } | |
| 370 | |
| 371 FORMATETC* smartPasteFormat() | |
| 372 { | |
| 373 static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format"); | |
| 374 static FORMATETC htmlFormat = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; | |
| 375 return &htmlFormat; | |
| 376 } | |
| 377 | |
| 378 FORMATETC* fileDescriptorFormat() | |
| 379 { | |
| 380 static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); | |
| 381 static FORMATETC fileDescriptorFormat = { cf, 0, DVASPECT_CONTENT, -1, TYMED
_HGLOBAL }; | |
| 382 return &fileDescriptorFormat; | |
| 383 } | |
| 384 | |
| 385 FORMATETC* fileContentFormatZero() | |
| 386 { | |
| 387 static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS); | |
| 388 static FORMATETC fileContentFormat = { cf, 0, DVASPECT_CONTENT, 0, TYMED_HGL
OBAL }; | |
| 389 return &fileContentFormat; | |
| 390 } | |
| 391 | |
| 392 void getFileDescriptorData(IDataObject* dataObject, int& size, String& pathname) | |
| 393 { | |
| 394 STGMEDIUM store; | |
| 395 size = 0; | |
| 396 if (FAILED(dataObject->GetData(fileDescriptorFormat(), &store))) | |
| 397 return; | |
| 398 | |
| 399 FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(stor
e.hGlobal)); | |
| 400 size = fgd->fgd[0].nFileSizeLow; | |
| 401 pathname = fgd->fgd[0].cFileName; | |
| 402 | |
| 403 GlobalUnlock(store.hGlobal); | |
| 404 ::ReleaseStgMedium(&store); | |
| 405 } | |
| 406 | |
| 407 void getFileContentData(IDataObject* dataObject, int size, void* dataBlob) | |
| 408 { | |
| 409 STGMEDIUM store; | |
| 410 if (FAILED(dataObject->GetData(fileContentFormatZero(), &store))) | |
| 411 return; | |
| 412 void* data = GlobalLock(store.hGlobal); | |
| 413 ::CopyMemory(dataBlob, data, size); | |
| 414 | |
| 415 GlobalUnlock(store.hGlobal); | |
| 416 ::ReleaseStgMedium(&store); | |
| 417 } | |
| 418 | |
| 419 void setFileDescriptorData(IDataObject* dataObject, int size, const String& pass
edPathname) | |
| 420 { | |
| 421 String pathname = passedPathname; | |
| 422 | |
| 423 STGMEDIUM medium = { 0 }; | |
| 424 medium.tymed = TYMED_HGLOBAL; | |
| 425 | |
| 426 medium.hGlobal = ::GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); | |
| 427 if (!medium.hGlobal) | |
| 428 return; | |
| 429 | |
| 430 FILEGROUPDESCRIPTOR* fgd = static_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(medi
um.hGlobal)); | |
| 431 ::ZeroMemory(fgd, sizeof(FILEGROUPDESCRIPTOR)); | |
| 432 fgd->cItems = 1; | |
| 433 fgd->fgd[0].dwFlags = FD_FILESIZE; | |
| 434 fgd->fgd[0].nFileSizeLow = size; | |
| 435 | |
| 436 int maxSize = std::min<int>(pathname.length(), WTF_ARRAY_LENGTH(fgd->fgd[0].
cFileName)); | |
| 437 CopyMemory(fgd->fgd[0].cFileName, pathname.charactersWithNullTermination(),
maxSize * sizeof(UChar)); | |
| 438 GlobalUnlock(medium.hGlobal); | |
| 439 | |
| 440 dataObject->SetData(fileDescriptorFormat(), &medium, TRUE); | |
| 441 } | |
| 442 | |
| 443 void setFileContentData(IDataObject* dataObject, int size, void* dataBlob) | |
| 444 { | |
| 445 STGMEDIUM medium = { 0 }; | |
| 446 medium.tymed = TYMED_HGLOBAL; | |
| 447 | |
| 448 medium.hGlobal = ::GlobalAlloc(GPTR, size); | |
| 449 if (!medium.hGlobal) | |
| 450 return; | |
| 451 void* fileContents = GlobalLock(medium.hGlobal); | |
| 452 ::CopyMemory(fileContents, dataBlob, size); | |
| 453 GlobalUnlock(medium.hGlobal); | |
| 454 | |
| 455 dataObject->SetData(fileContentFormatZero(), &medium, TRUE); | |
| 456 } | |
| 457 | |
| 458 String getURL(IDataObject* dataObject, DragData::FilenameConversionPolicy filena
mePolicy, String* title) | |
| 459 { | |
| 460 STGMEDIUM store; | |
| 461 String url; | |
| 462 if (getWebLocData(dataObject, url, title)) | |
| 463 return url; | |
| 464 | |
| 465 if (SUCCEEDED(dataObject->GetData(urlWFormat(), &store))) { | |
| 466 // URL using Unicode | |
| 467 UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal)); | |
| 468 url = extractURL(String(data), title); | |
| 469 GlobalUnlock(store.hGlobal); | |
| 470 ReleaseStgMedium(&store); | |
| 471 } else if (SUCCEEDED(dataObject->GetData(urlFormat(), &store))) { | |
| 472 // URL using ASCII | |
| 473 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
| 474 url = extractURL(String(data), title); | |
| 475 GlobalUnlock(store.hGlobal); | |
| 476 ReleaseStgMedium(&store); | |
| 477 } | |
| 478 #if USE(CF) | |
| 479 else if (filenamePolicy == DragData::ConvertFilenames) { | |
| 480 if (SUCCEEDED(dataObject->GetData(filenameWFormat(), &store))) { | |
| 481 // file using unicode | |
| 482 wchar_t* data = static_cast<wchar_t*>(GlobalLock(store.hGlobal)); | |
| 483 if (data && data[0] && (PathFileExists(data) || PathIsUNC(data))) { | |
| 484 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWit
hCharacters(kCFAllocatorDefault, (const UniChar*)data, wcslen(data))); | |
| 485 if (urlFromPath(pathAsCFString.get(), url) && title) | |
| 486 *title = url; | |
| 487 } | |
| 488 GlobalUnlock(store.hGlobal); | |
| 489 ReleaseStgMedium(&store); | |
| 490 } else if (SUCCEEDED(dataObject->GetData(filenameFormat(), &store))) { | |
| 491 // filename using ascii | |
| 492 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
| 493 if (data && data[0] && (PathFileExistsA(data) || PathIsUNCA(data)))
{ | |
| 494 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWit
hCString(kCFAllocatorDefault, data, kCFStringEncodingASCII)); | |
| 495 if (urlFromPath(pathAsCFString.get(), url) && title) | |
| 496 *title = url; | |
| 497 } | |
| 498 GlobalUnlock(store.hGlobal); | |
| 499 ReleaseStgMedium(&store); | |
| 500 } | |
| 501 } | |
| 502 #endif | |
| 503 return url; | |
| 504 } | |
| 505 | |
| 506 String getURL(const DragDataMap* data, DragData::FilenameConversionPolicy filena
mePolicy, String* title) | |
| 507 { | |
| 508 String url; | |
| 509 | |
| 510 if (getWebLocData(data, url, title)) | |
| 511 return url; | |
| 512 if (getDataMapItem(data, urlWFormat(), url)) | |
| 513 return extractURL(url, title); | |
| 514 if (getDataMapItem(data, urlFormat(), url)) | |
| 515 return extractURL(url, title); | |
| 516 #if USE(CF) | |
| 517 if (filenamePolicy != DragData::ConvertFilenames) | |
| 518 return url; | |
| 519 | |
| 520 String stringData; | |
| 521 if (!getDataMapItem(data, filenameWFormat(), stringData)) | |
| 522 getDataMapItem(data, filenameFormat(), stringData); | |
| 523 | |
| 524 if (stringData.isEmpty() || (!PathFileExists(stringData.charactersWithNullTe
rmination()) && !PathIsUNC(stringData.charactersWithNullTermination()))) | |
| 525 return url; | |
| 526 RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWithCharacters(
kCFAllocatorDefault, (const UniChar *)stringData.charactersWithNullTermination()
, wcslen(stringData.charactersWithNullTermination()))); | |
| 527 if (urlFromPath(pathAsCFString.get(), url) && title) | |
| 528 *title = url; | |
| 529 #endif | |
| 530 return url; | |
| 531 } | |
| 532 | |
| 533 String getPlainText(IDataObject* dataObject) | |
| 534 { | |
| 535 STGMEDIUM store; | |
| 536 String text; | |
| 537 if (SUCCEEDED(dataObject->GetData(plainTextWFormat(), &store))) { | |
| 538 // Unicode text | |
| 539 UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal)); | |
| 540 text = String(data); | |
| 541 GlobalUnlock(store.hGlobal); | |
| 542 ReleaseStgMedium(&store); | |
| 543 } else if (SUCCEEDED(dataObject->GetData(plainTextFormat(), &store))) { | |
| 544 // ASCII text | |
| 545 char* data = static_cast<char*>(GlobalLock(store.hGlobal)); | |
| 546 text = String(data); | |
| 547 GlobalUnlock(store.hGlobal); | |
| 548 ReleaseStgMedium(&store); | |
| 549 } else { | |
| 550 // FIXME: Originally, we called getURL() here because dragging and dropp
ing files doesn't | |
| 551 // populate the drag with text data. Per https://bugs.webkit.org/show_bu
g.cgi?id=38826, this | |
| 552 // is undesirable, so maybe this line can be removed. | |
| 553 text = getURL(dataObject, DragData::DoNotConvertFilenames); | |
| 554 } | |
| 555 return text; | |
| 556 } | |
| 557 | |
| 558 String getPlainText(const DragDataMap* data) | |
| 559 { | |
| 560 String text; | |
| 561 | |
| 562 if (getDataMapItem(data, plainTextWFormat(), text)) | |
| 563 return text; | |
| 564 if (getDataMapItem(data, plainTextFormat(), text)) | |
| 565 return text; | |
| 566 return getURL(data, DragData::DoNotConvertFilenames); | |
| 567 } | |
| 568 | |
| 569 String getTextHTML(IDataObject* data) | |
| 570 { | |
| 571 STGMEDIUM store; | |
| 572 String html; | |
| 573 if (SUCCEEDED(data->GetData(texthtmlFormat(), &store))) { | |
| 574 UChar* data = static_cast<UChar*>(GlobalLock(store.hGlobal)); | |
| 575 html = String(data); | |
| 576 GlobalUnlock(store.hGlobal); | |
| 577 ReleaseStgMedium(&store); | |
| 578 } | |
| 579 return html; | |
| 580 } | |
| 581 | |
| 582 String getTextHTML(const DragDataMap* data) | |
| 583 { | |
| 584 String text; | |
| 585 getDataMapItem(data, texthtmlFormat(), text); | |
| 586 return text; | |
| 587 } | |
| 588 | |
| 589 String getCFHTML(IDataObject* data) | |
| 590 { | |
| 591 String cfhtml = getFullCFHTML(data); | |
| 592 if (!cfhtml.isEmpty()) | |
| 593 return extractMarkupFromCFHTML(cfhtml); | |
| 594 return String(); | |
| 595 } | |
| 596 | |
| 597 String getCFHTML(const DragDataMap* dataMap) | |
| 598 { | |
| 599 String cfhtml; | |
| 600 getDataMapItem(dataMap, htmlFormat(), cfhtml); | |
| 601 return extractMarkupFromCFHTML(cfhtml); | |
| 602 } | |
| 603 | |
| 604 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*
) | |
| 605 { | |
| 606 // FIXME: We should be able to create fragments from files | |
| 607 return 0; | |
| 608 } | |
| 609 | |
| 610 PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*
) | |
| 611 { | |
| 612 // FIXME: We should be able to create fragments from files | |
| 613 return 0; | |
| 614 } | |
| 615 | |
| 616 bool containsFilenames(const IDataObject*) | |
| 617 { | |
| 618 // FIXME: We'll want to update this once we can produce fragments from files | |
| 619 return false; | |
| 620 } | |
| 621 | |
| 622 bool containsFilenames(const DragDataMap*) | |
| 623 { | |
| 624 // FIXME: We'll want to update this once we can produce fragments from files | |
| 625 return false; | |
| 626 } | |
| 627 | |
| 628 // Convert a String containing CF_HTML formatted text to a DocumentFragment | |
| 629 PassRefPtr<DocumentFragment> fragmentFromCFHTML(Document* doc, const String& cfh
tml) | |
| 630 { | |
| 631 // obtain baseURL if present | |
| 632 String srcURLStr("sourceURL:"); | |
| 633 String srcURL; | |
| 634 unsigned lineStart = cfhtml.find(srcURLStr, 0, false); | |
| 635 if (lineStart != -1) { | |
| 636 unsigned srcEnd = cfhtml.find("\n", lineStart, false); | |
| 637 unsigned srcStart = lineStart+srcURLStr.length(); | |
| 638 String rawSrcURL = cfhtml.substring(srcStart, srcEnd-srcStart); | |
| 639 replaceNBSPWithSpace(rawSrcURL); | |
| 640 srcURL = rawSrcURL.stripWhiteSpace(); | |
| 641 } | |
| 642 | |
| 643 String markup = extractMarkupFromCFHTML(cfhtml); | |
| 644 return createFragmentFromMarkup(doc, markup, srcURL, DisallowScriptingAndPlu
ginContent); | |
| 645 } | |
| 646 | |
| 647 PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data) | |
| 648 { | |
| 649 if (!doc || !data) | |
| 650 return 0; | |
| 651 | |
| 652 String cfhtml = getFullCFHTML(data); | |
| 653 if (!cfhtml.isEmpty()) { | |
| 654 if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(doc, cfhtml)) | |
| 655 return fragment.release(); | |
| 656 } | |
| 657 | |
| 658 String html = getTextHTML(data); | |
| 659 String srcURL; | |
| 660 if (!html.isEmpty()) | |
| 661 return createFragmentFromMarkup(doc, html, srcURL, DisallowScriptingAndP
luginContent); | |
| 662 | |
| 663 return 0; | |
| 664 } | |
| 665 | |
| 666 PassRefPtr<DocumentFragment> fragmentFromHTML(Document* document, const DragData
Map* data) | |
| 667 { | |
| 668 if (!document || !data || data->isEmpty()) | |
| 669 return 0; | |
| 670 | |
| 671 String stringData; | |
| 672 if (getDataMapItem(data, htmlFormat(), stringData)) { | |
| 673 if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(document, str
ingData)) | |
| 674 return fragment.release(); | |
| 675 } | |
| 676 | |
| 677 String srcURL; | |
| 678 if (getDataMapItem(data, texthtmlFormat(), stringData)) | |
| 679 return createFragmentFromMarkup(document, stringData, srcURL, DisallowSc
riptingAndPluginContent); | |
| 680 | |
| 681 return 0; | |
| 682 } | |
| 683 | |
| 684 bool containsHTML(IDataObject* data) | |
| 685 { | |
| 686 return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->Qu
eryGetData(htmlFormat())); | |
| 687 } | |
| 688 | |
| 689 bool containsHTML(const DragDataMap* data) | |
| 690 { | |
| 691 return data->contains(texthtmlFormat()->cfFormat) || data->contains(htmlForm
at()->cfFormat); | |
| 692 } | |
| 693 | |
| 694 typedef void (*GetStringFunction)(IDataObject*, FORMATETC*, Vector<String>&); | |
| 695 typedef void (*SetStringFunction)(IDataObject*, FORMATETC*, const Vector<String>
&); | |
| 696 | |
| 697 struct ClipboardDataItem { | |
| 698 GetStringFunction getString; | |
| 699 SetStringFunction setString; | |
| 700 FORMATETC* format; | |
| 701 | |
| 702 ClipboardDataItem(FORMATETC* format, GetStringFunction getString, SetStringF
unction setString): format(format), getString(getString), setString(setString) {
} | |
| 703 }; | |
| 704 | |
| 705 typedef HashMap<UINT, ClipboardDataItem*> ClipboardFormatMap; | |
| 706 | |
| 707 // Getter functions. | |
| 708 | |
| 709 template<typename T> void getStringData(IDataObject* data, FORMATETC* format, Ve
ctor<String>& dataStrings) | |
| 710 { | |
| 711 STGMEDIUM store; | |
| 712 if (FAILED(data->GetData(format, &store))) | |
| 713 return; | |
| 714 dataStrings.append(String(static_cast<T*>(GlobalLock(store.hGlobal)), ::Glob
alSize(store.hGlobal) / sizeof(T))); | |
| 715 GlobalUnlock(store.hGlobal); | |
| 716 ReleaseStgMedium(&store); | |
| 717 } | |
| 718 | |
| 719 void getUtf8Data(IDataObject* data, FORMATETC* format, Vector<String>& dataStrin
gs) | |
| 720 { | |
| 721 STGMEDIUM store; | |
| 722 if (FAILED(data->GetData(format, &store))) | |
| 723 return; | |
| 724 dataStrings.append(String(UTF8Encoding().decode(static_cast<char*>(GlobalLoc
k(store.hGlobal)), GlobalSize(store.hGlobal)))); | |
| 725 GlobalUnlock(store.hGlobal); | |
| 726 ReleaseStgMedium(&store); | |
| 727 } | |
| 728 | |
| 729 #if USE(CF) | |
| 730 void getCFData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings
) | |
| 731 { | |
| 732 STGMEDIUM store; | |
| 733 if (FAILED(data->GetData(format, &store))) | |
| 734 return; | |
| 735 | |
| 736 HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(store.hGlobal)); | |
| 737 if (!hdrop) | |
| 738 return; | |
| 739 | |
| 740 WCHAR filename[MAX_PATH]; | |
| 741 UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); | |
| 742 for (UINT i = 0; i < fileCount; i++) { | |
| 743 if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename))) | |
| 744 continue; | |
| 745 dataStrings.append(static_cast<UChar*>(filename)); | |
| 746 } | |
| 747 | |
| 748 GlobalUnlock(store.hGlobal); | |
| 749 ReleaseStgMedium(&store); | |
| 750 } | |
| 751 #endif | |
| 752 | |
| 753 // Setter functions. | |
| 754 | |
| 755 void setUCharData(IDataObject* data, FORMATETC* format, const Vector<String>& da
taStrings) | |
| 756 { | |
| 757 STGMEDIUM medium = {0}; | |
| 758 medium.tymed = TYMED_HGLOBAL; | |
| 759 | |
| 760 medium.hGlobal = createGlobalData(dataStrings.first()); | |
| 761 if (!medium.hGlobal) | |
| 762 return; | |
| 763 data->SetData(format, &medium, FALSE); | |
| 764 ::GlobalFree(medium.hGlobal); | |
| 765 } | |
| 766 | |
| 767 void setUtf8Data(IDataObject* data, FORMATETC* format, const Vector<String>& dat
aStrings) | |
| 768 { | |
| 769 STGMEDIUM medium = {0}; | |
| 770 medium.tymed = TYMED_HGLOBAL; | |
| 771 | |
| 772 CString charString = dataStrings.first().utf8(); | |
| 773 size_t stringLength = charString.length(); | |
| 774 medium.hGlobal = ::GlobalAlloc(GPTR, stringLength + 1); | |
| 775 if (!medium.hGlobal) | |
| 776 return; | |
| 777 char* buffer = static_cast<char*>(GlobalLock(medium.hGlobal)); | |
| 778 memcpy(buffer, charString.data(), stringLength); | |
| 779 buffer[stringLength] = 0; | |
| 780 GlobalUnlock(medium.hGlobal); | |
| 781 data->SetData(format, &medium, FALSE); | |
| 782 ::GlobalFree(medium.hGlobal); | |
| 783 } | |
| 784 | |
| 785 #if USE(CF) | |
| 786 void setCFData(IDataObject* data, FORMATETC* format, const Vector<String>& dataS
trings) | |
| 787 { | |
| 788 STGMEDIUM medium = {0}; | |
| 789 medium.tymed = TYMED_HGLOBAL; | |
| 790 | |
| 791 SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (dataStrings.fir
st().length() + 2)); | |
| 792 medium.hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize); | |
| 793 if (!medium.hGlobal) | |
| 794 return; | |
| 795 | |
| 796 DROPFILES* dropFiles = reinterpret_cast<DROPFILES *>(GlobalLock(medium.hGlob
al)); | |
| 797 dropFiles->pFiles = sizeof(DROPFILES); | |
| 798 dropFiles->fWide = TRUE; | |
| 799 String filename = dataStrings.first(); | |
| 800 wcscpy(reinterpret_cast<LPWSTR>(dropFiles + 1), filename.charactersWithNullT
ermination()); | |
| 801 GlobalUnlock(medium.hGlobal); | |
| 802 data->SetData(format, &medium, FALSE); | |
| 803 ::GlobalFree(medium.hGlobal); | |
| 804 } | |
| 805 #endif | |
| 806 | |
| 807 static const ClipboardFormatMap& getClipboardMap() | |
| 808 { | |
| 809 static ClipboardFormatMap formatMap; | |
| 810 if (formatMap.isEmpty()) { | |
| 811 formatMap.add(htmlFormat()->cfFormat, new ClipboardDataItem(htmlFormat()
, getUtf8Data, setUtf8Data)); | |
| 812 formatMap.add(texthtmlFormat()->cfFormat, new ClipboardDataItem(texthtml
Format(), getStringData<UChar>, setUCharData)); | |
| 813 formatMap.add(plainTextFormat()->cfFormat, new ClipboardDataItem(plainT
extFormat(), getStringData<char>, setUtf8Data)); | |
| 814 formatMap.add(plainTextWFormat()->cfFormat, new ClipboardDataItem(plain
TextWFormat(), getStringData<UChar>, setUCharData)); | |
| 815 #if USE(CF) | |
| 816 formatMap.add(cfHDropFormat()->cfFormat, new ClipboardDataItem(cfHDropF
ormat(), getCFData, setCFData)); | |
| 817 #endif | |
| 818 formatMap.add(filenameFormat()->cfFormat, new ClipboardDataItem(filenam
eFormat(), getStringData<char>, setUtf8Data)); | |
| 819 formatMap.add(filenameWFormat()->cfFormat, new ClipboardDataItem(filena
meWFormat(), getStringData<UChar>, setUCharData)); | |
| 820 formatMap.add(urlFormat()->cfFormat, new ClipboardDataItem(urlFormat(),
getStringData<char>, setUtf8Data)); | |
| 821 formatMap.add(urlWFormat()->cfFormat, new ClipboardDataItem(urlWFormat(
), getStringData<UChar>, setUCharData)); | |
| 822 } | |
| 823 return formatMap; | |
| 824 } | |
| 825 | |
| 826 void getClipboardData(IDataObject* dataObject, FORMATETC* format, Vector<String>
& dataStrings) | |
| 827 { | |
| 828 const ClipboardFormatMap& formatMap = getClipboardMap(); | |
| 829 ClipboardFormatMap::const_iterator found = formatMap.find(format->cfFormat); | |
| 830 if (found == formatMap.end()) | |
| 831 return; | |
| 832 found->value->getString(dataObject, found->value->format, dataStrings); | |
| 833 } | |
| 834 | |
| 835 void setClipboardData(IDataObject* dataObject, UINT format, const Vector<String>
& dataStrings) | |
| 836 { | |
| 837 const ClipboardFormatMap& formatMap = getClipboardMap(); | |
| 838 ClipboardFormatMap::const_iterator found = formatMap.find(format); | |
| 839 if (found == formatMap.end()) | |
| 840 return; | |
| 841 found->value->setString(dataObject, found->value->format, dataStrings); | |
| 842 } | |
| 843 | |
| 844 } // namespace WebCore | |
| OLD | NEW |