1/* 2 * Copyright (C) 2006, 2013 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 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 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#import "config.h" 27#import "Pasteboard.h" 28 29#import "CachedImage.h" 30#import "DOMRangeInternal.h" 31#import "Document.h" 32#import "DocumentFragment.h" 33#import "DocumentLoader.h" 34#import "DragData.h" 35#import "Editor.h" 36#import "EditorClient.h" 37#import "ExceptionCodePlaceholder.h" 38#import "Frame.h" 39#import "FrameView.h" 40#import "FrameLoaderClient.h" 41#import "HitTestResult.h" 42#import "HTMLAnchorElement.h" 43#import "htmlediting.h" 44#import "HTMLNames.h" 45#import "Image.h" 46#import "URL.h" 47#import "LegacyWebArchive.h" 48#import "LoaderNSURLExtras.h" 49#import "MIMETypeRegistry.h" 50#import "Page.h" 51#import "PasteboardStrategy.h" 52#import "PlatformStrategies.h" 53#import "RenderImage.h" 54#import "ResourceBuffer.h" 55#import "Text.h" 56#import "WebCoreNSStringExtras.h" 57#import "WebNSAttributedStringExtras.h" 58#import "markup.h" 59#import <wtf/StdLibExtras.h> 60#import <wtf/RetainPtr.h> 61#import <wtf/text/StringBuilder.h> 62#import <wtf/unicode/CharacterNames.h> 63 64namespace WebCore { 65 66const char* const WebArchivePboardType = "Apple Web Archive pasteboard type"; 67const char* const WebURLNamePboardType = "public.url-name"; 68 69const char WebSmartPastePboardType[] = "NeXT smart paste pasteboard type"; 70const char WebURLPboardType[] = "public.url"; 71const char WebURLsWithTitlesPboardType[] = "WebURLsWithTitlesPboardType"; 72 73// Making this non-inline so that WebKit 2's decoding doesn't have to include SharedBuffer.h. 74PasteboardWebContent::PasteboardWebContent() 75{ 76} 77 78PasteboardWebContent::~PasteboardWebContent() 79{ 80} 81 82// Making this non-inline so that WebKit 2's decoding doesn't have to include Image.h. 83PasteboardImage::PasteboardImage() 84{ 85} 86 87PasteboardImage::~PasteboardImage() 88{ 89} 90 91static const Vector<String> writableTypesForURL() 92{ 93 Vector<String> types; 94 95 types.append(WebURLsWithTitlesPboardType); 96 types.append(String(NSURLPboardType)); 97 types.append(WebURLPboardType); 98 types.append(WebURLNamePboardType); 99 types.append(String(NSStringPboardType)); 100 return types; 101} 102 103static inline Vector<String> createWritableTypesForImage() 104{ 105 Vector<String> types; 106 107 types.append(String(NSTIFFPboardType)); 108 types.appendVector(writableTypesForURL()); 109 types.append(String(NSRTFDPboardType)); 110 return types; 111} 112 113static Vector<String> writableTypesForImage() 114{ 115 Vector<String> types; 116 types.appendVector(createWritableTypesForImage()); 117 return types; 118} 119 120Pasteboard::Pasteboard(const String& pasteboardName) 121 : m_pasteboardName(pasteboardName) 122 , m_changeCount(platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName)) 123{ 124 ASSERT(pasteboardName); 125} 126 127PassOwnPtr<Pasteboard> Pasteboard::create(const String& pasteboardName) 128{ 129 return adoptPtr(new Pasteboard(pasteboardName)); 130} 131 132PassOwnPtr<Pasteboard> Pasteboard::createForCopyAndPaste() 133{ 134 return create(NSGeneralPboard); 135} 136 137PassOwnPtr<Pasteboard> Pasteboard::createPrivate() 138{ 139 return create(platformStrategies()->pasteboardStrategy()->uniqueName()); 140} 141 142PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop() 143{ 144 return create(NSDragPboard); 145} 146 147PassOwnPtr<Pasteboard> Pasteboard::createForDragAndDrop(const DragData& dragData) 148{ 149 return create(dragData.pasteboardName()); 150} 151 152void Pasteboard::clear() 153{ 154 m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(Vector<String>(), m_pasteboardName); 155} 156 157void Pasteboard::write(const PasteboardWebContent& content) 158{ 159 Vector<String> types; 160 161 if (content.canSmartCopyOrDelete) 162 types.append(WebSmartPastePboardType); 163 if (content.dataInWebArchiveFormat) 164 types.append(WebArchivePboardType); 165 if (content.dataInRTFDFormat) 166 types.append(String(NSRTFDPboardType)); 167 if (content.dataInRTFFormat) 168 types.append(String(NSRTFPboardType)); 169 if (!content.dataInStringFormat.isNull()) 170 types.append(String(NSStringPboardType)); 171 types.appendVector(content.clientTypes); 172 173 m_changeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName); 174 175 ASSERT(content.clientTypes.size() == content.clientData.size()); 176 for (size_t i = 0, size = content.clientTypes.size(); i < size; ++i) 177 m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.clientData[i], content.clientTypes[i], m_pasteboardName); 178 if (content.canSmartCopyOrDelete) 179 m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(0, WebSmartPastePboardType, m_pasteboardName); 180 if (content.dataInWebArchiveFormat) 181 m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInWebArchiveFormat, WebArchivePboardType, m_pasteboardName); 182 if (content.dataInRTFDFormat) 183 m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInRTFDFormat, NSRTFDPboardType, m_pasteboardName); 184 if (content.dataInRTFFormat) 185 m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(content.dataInRTFFormat, NSRTFPboardType, m_pasteboardName); 186 if (!content.dataInStringFormat.isNull()) 187 m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(content.dataInStringFormat, NSStringPboardType, m_pasteboardName); 188} 189 190void Pasteboard::writePlainText(const String& text, SmartReplaceOption smartReplaceOption) 191{ 192 Vector<String> types; 193 types.append(NSStringPboardType); 194 if (smartReplaceOption == CanSmartReplace) 195 types.append(WebSmartPastePboardType); 196 197 platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName); 198 m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(text, NSStringPboardType, m_pasteboardName); 199 if (smartReplaceOption == CanSmartReplace) 200 m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(0, WebSmartPastePboardType, m_pasteboardName); 201} 202 203static long writeURLForTypes(const Vector<String>& types, const String& pasteboardName, const PasteboardURL& pasteboardURL) 204{ 205 long newChangeCount = platformStrategies()->pasteboardStrategy()->setTypes(types, pasteboardName); 206 207 ASSERT(!pasteboardURL.url.isEmpty()); 208 209 NSURL *cocoaURL = pasteboardURL.url; 210 NSString *userVisibleString = pasteboardURL.userVisibleForm; 211 NSString *title = (NSString *)pasteboardURL.title; 212 if (![title length]) { 213 title = [[cocoaURL path] lastPathComponent]; 214 if (![title length]) 215 title = userVisibleString; 216 } 217 218 if (types.contains(WebURLsWithTitlesPboardType)) { 219 Vector<String> paths; 220 paths.append([cocoaURL absoluteString]); 221 paths.append(pasteboardURL.title.stripWhiteSpace()); 222 newChangeCount = platformStrategies()->pasteboardStrategy()->setPathnamesForType(paths, WebURLsWithTitlesPboardType, pasteboardName); 223 } 224 if (types.contains(String(NSURLPboardType))) 225 newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType([cocoaURL absoluteString], NSURLPboardType, pasteboardName); 226 if (types.contains(WebURLPboardType)) 227 newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType(userVisibleString, WebURLPboardType, pasteboardName); 228 if (types.contains(WebURLNamePboardType)) 229 newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType(title, WebURLNamePboardType, pasteboardName); 230 if (types.contains(String(NSStringPboardType))) 231 newChangeCount = platformStrategies()->pasteboardStrategy()->setStringForType(userVisibleString, NSStringPboardType, pasteboardName); 232 233 return newChangeCount; 234} 235 236void Pasteboard::write(const PasteboardURL& pasteboardURL) 237{ 238 m_changeCount = writeURLForTypes(writableTypesForURL(), m_pasteboardName, pasteboardURL); 239} 240 241static NSFileWrapper* fileWrapper(const PasteboardImage& pasteboardImage) 242{ 243 NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:pasteboardImage.resourceData->createNSData().get()] autorelease]; 244 [wrapper setPreferredFilename:suggestedFilenameWithMIMEType(pasteboardImage.url.url, pasteboardImage.resourceMIMEType)]; 245 return wrapper; 246} 247 248static void writeFileWrapperAsRTFDAttachment(NSFileWrapper *wrapper, const String& pasteboardName, long& newChangeCount) 249{ 250 NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:wrapper]; 251 NSAttributedString *string = [NSAttributedString attributedStringWithAttachment:attachment]; 252 [attachment release]; 253 254 NSData *RTFDData = [string RTFDFromRange:NSMakeRange(0, [string length]) documentAttributes:nil]; 255 if (!RTFDData) 256 return; 257 258 newChangeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData(RTFDData).get(), NSRTFDPboardType, pasteboardName); 259} 260 261void Pasteboard::write(const PasteboardImage& pasteboardImage) 262{ 263 NSData *imageData = [pasteboardImage.image->getNSImage() TIFFRepresentation]; 264 if (!imageData) 265 return; 266 267 // FIXME: Why can we assert this? It doesn't seem like it's guaranteed. 268 ASSERT(MIMETypeRegistry::isSupportedImageResourceMIMEType(pasteboardImage.resourceMIMEType)); 269 270 m_changeCount = writeURLForTypes(writableTypesForImage(), m_pasteboardName, pasteboardImage.url); 271 m_changeCount = platformStrategies()->pasteboardStrategy()->setBufferForType(SharedBuffer::wrapNSData(imageData), NSTIFFPboardType, m_pasteboardName); 272 writeFileWrapperAsRTFDAttachment(fileWrapper(pasteboardImage), m_pasteboardName, m_changeCount); 273} 274 275void Pasteboard::writePasteboard(const Pasteboard& pasteboard) 276{ 277 m_changeCount = platformStrategies()->pasteboardStrategy()->copy(pasteboard.m_pasteboardName, m_pasteboardName); 278} 279 280bool Pasteboard::canSmartReplace() 281{ 282 Vector<String> types; 283 platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName); 284 return types.contains(WebSmartPastePboardType); 285} 286 287void Pasteboard::read(PasteboardPlainText& text) 288{ 289 PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); 290 291 Vector<String> types; 292 strategy.getTypes(types, m_pasteboardName); 293 294 if (types.contains(String(NSStringPboardType))) { 295 text.text = strategy.stringForType(NSStringPboardType, m_pasteboardName); 296 text.isURL = false; 297 return; 298 } 299 300 if (types.contains(String(NSRTFDPboardType))) { 301 if (RefPtr<SharedBuffer> data = strategy.bufferForType(NSRTFDPboardType, m_pasteboardName)) { 302 if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTFD:data->createNSData().get() documentAttributes:NULL])) { 303 text.text = [attributedString string]; 304 text.isURL = false; 305 return; 306 } 307 } 308 } 309 310 if (types.contains(String(NSRTFPboardType))) { 311 if (RefPtr<SharedBuffer> data = strategy.bufferForType(NSRTFPboardType, m_pasteboardName)) { 312 if (auto attributedString = adoptNS([[NSAttributedString alloc] initWithRTF:data->createNSData().get() documentAttributes:NULL])) { 313 text.text = [attributedString string]; 314 text.isURL = false; 315 return; 316 } 317 } 318 } 319 320 if (types.contains(String(NSFilenamesPboardType))) { 321 Vector<String> pathnames; 322 strategy.getPathnamesForType(pathnames, NSFilenamesPboardType, m_pasteboardName); 323 StringBuilder builder; 324 for (size_t i = 0, size = pathnames.size(); i < size; i++) { 325 if (i) 326 builder.append('\n'); 327 builder.append(pathnames[i]); 328 } 329 text.text = builder.toString(); 330 text.isURL = false; 331 return; 332 } 333 334 // FIXME: The code above looks at the types vector first, but this just gets the string without checking. Why the difference? 335 text.text = strategy.stringForType(NSURLPboardType, m_pasteboardName); 336 text.isURL = !text.text.isNull(); 337} 338 339void Pasteboard::read(PasteboardWebContentReader& reader) 340{ 341 PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); 342 343 Vector<String> types; 344 strategy.getTypes(types, m_pasteboardName); 345 346 if (types.contains(WebArchivePboardType)) { 347 if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(WebArchivePboardType, m_pasteboardName)) { 348 if (reader.readWebArchive(buffer.release())) 349 return; 350 } 351 } 352 353 if (types.contains(String(NSFilenamesPboardType))) { 354 Vector<String> paths; 355 strategy.getPathnamesForType(paths, NSFilenamesPboardType, m_pasteboardName); 356 if (reader.readFilenames(paths)) 357 return; 358 } 359 360 if (types.contains(String(NSHTMLPboardType))) { 361 String string = strategy.stringForType(NSHTMLPboardType, m_pasteboardName); 362 if (!string.isNull() && reader.readHTML(string)) 363 return; 364 } 365 366 if (types.contains(String(NSRTFDPboardType))) { 367 if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSRTFDPboardType, m_pasteboardName)) { 368 if (reader.readRTFD(buffer.release())) 369 return; 370 } 371 } 372 373 if (types.contains(String(NSRTFPboardType))) { 374 if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSRTFPboardType, m_pasteboardName)) { 375 if (reader.readRTF(buffer.release())) 376 return; 377 } 378 } 379 380 if (types.contains(String(NSTIFFPboardType))) { 381 if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSTIFFPboardType, m_pasteboardName)) { 382 if (reader.readImage(buffer.release(), ASCIILiteral("image/tiff"))) 383 return; 384 } 385 } 386 387 if (types.contains(String(NSPDFPboardType))) { 388 if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(NSPDFPboardType, m_pasteboardName)) { 389 if (reader.readImage(buffer.release(), ASCIILiteral("application/pdf"))) 390 return; 391 } 392 } 393 394 if (types.contains(String(kUTTypePNG))) { 395 if (RefPtr<SharedBuffer> buffer = strategy.bufferForType(kUTTypePNG, m_pasteboardName)) { 396 if (reader.readImage(buffer.release(), ASCIILiteral("image/png"))) 397 return; 398 } 399 } 400 401 if (types.contains(String(NSURLPboardType))) { 402 URL url = strategy.url(m_pasteboardName); 403 String title = strategy.stringForType(WebURLNamePboardType, m_pasteboardName); 404 if (!url.isNull() && reader.readURL(url, title)) 405 return; 406 } 407 408 if (types.contains(String(NSStringPboardType))) { 409 String string = strategy.stringForType(NSStringPboardType, m_pasteboardName); 410 if (!string.isNull() && reader.readPlainText(string)) 411 return; 412 } 413} 414 415bool Pasteboard::hasData() 416{ 417 Vector<String> types; 418 platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName); 419 return !types.isEmpty(); 420} 421 422static String cocoaTypeFromHTMLClipboardType(const String& type) 423{ 424 // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dom-datatransfer-setdata 425 String qType = type.lower(); 426 427 if (qType == "text") 428 qType = ASCIILiteral("text/plain"); 429 if (qType == "url") 430 qType = ASCIILiteral("text/uri-list"); 431 432 // Ignore any trailing charset - JS strings are Unicode, which encapsulates the charset issue 433 if (qType == "text/plain" || qType.startsWith("text/plain;")) 434 return String(NSStringPboardType); 435 if (qType == "text/uri-list") 436 // special case because UTI doesn't work with Cocoa's URL type 437 return String(NSURLPboardType); // note special case in getData to read NSFilenamesType 438 439 // Blacklist types that might contain subframe information 440 if (qType == "text/rtf" || qType == "public.rtf" || qType == "com.apple.traditional-mac-plain-text") 441 return String(); 442 443 // Try UTI now 444 String mimeType = qType; 445 if (RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType.createCFString().get(), NULL))) { 446 RetainPtr<CFStringRef> pbType = adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassNSPboardType)); 447 if (pbType) 448 return pbType.get(); 449 } 450 451 // No mapping, just pass the whole string though 452 return qType; 453} 454 455void Pasteboard::clear(const String& type) 456{ 457 String cocoaType = cocoaTypeFromHTMLClipboardType(type); 458 if (cocoaType.isEmpty()) 459 return; 460 m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(emptyString(), cocoaType, m_pasteboardName); 461} 462 463static Vector<String> absoluteURLsFromPasteboardFilenames(const String& pasteboardName, bool onlyFirstURL = false) 464{ 465 Vector<String> fileList; 466 platformStrategies()->pasteboardStrategy()->getPathnamesForType(fileList, String(NSFilenamesPboardType), pasteboardName); 467 468 if (fileList.isEmpty()) 469 return fileList; 470 471 size_t count = onlyFirstURL ? 1 : fileList.size(); 472 Vector<String> urls; 473 for (size_t i = 0; i < count; i++) { 474 NSURL *url = [NSURL fileURLWithPath:fileList[i]]; 475 urls.append(String([url absoluteString])); 476 } 477 return urls; 478} 479 480static Vector<String> absoluteURLsFromPasteboard(const String& pasteboardName, bool onlyFirstURL = false) 481{ 482 // NOTE: We must always check [availableTypes containsObject:] before accessing pasteboard data 483 // or CoreFoundation will printf when there is not data of the corresponding type. 484 Vector<String> availableTypes; 485 Vector<String> absoluteURLs; 486 platformStrategies()->pasteboardStrategy()->getTypes(availableTypes, pasteboardName); 487 488 // Try NSFilenamesPboardType because it contains a list 489 if (availableTypes.contains(String(NSFilenamesPboardType))) { 490 absoluteURLs = absoluteURLsFromPasteboardFilenames(pasteboardName, onlyFirstURL); 491 if (!absoluteURLs.isEmpty()) 492 return absoluteURLs; 493 } 494 495 // Fallback to NSURLPboardType (which is a single URL) 496 if (availableTypes.contains(String(NSURLPboardType))) { 497 absoluteURLs.append(platformStrategies()->pasteboardStrategy()->stringForType(String(NSURLPboardType), pasteboardName)); 498 return absoluteURLs; 499 } 500 501 // No file paths on the pasteboard, return nil 502 return Vector<String>(); 503} 504 505String Pasteboard::readString(const String& type) 506{ 507 const String& cocoaType = cocoaTypeFromHTMLClipboardType(type); 508 String cocoaValue; 509 510 // Grab the value off the pasteboard corresponding to the cocoaType 511 if (cocoaType == String(NSURLPboardType)) { 512 // "url" and "text/url-list" both map to NSURLPboardType in cocoaTypeFromHTMLClipboardType(), "url" only wants the first URL 513 bool onlyFirstURL = (equalIgnoringCase(type, "url")); 514 Vector<String> absoluteURLs = absoluteURLsFromPasteboard(m_pasteboardName, onlyFirstURL); 515 for (size_t i = 0; i < absoluteURLs.size(); i++) 516 cocoaValue = i ? "\n" + absoluteURLs[i]: absoluteURLs[i]; 517 } else if (cocoaType == String(NSStringPboardType)) 518 cocoaValue = [platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, m_pasteboardName) precomposedStringWithCanonicalMapping]; 519 else if (!cocoaType.isEmpty()) 520 cocoaValue = platformStrategies()->pasteboardStrategy()->stringForType(cocoaType, m_pasteboardName); 521 522 // Enforce changeCount ourselves for security. We check after reading instead of before to be 523 // sure it doesn't change between our testing the change count and accessing the data. 524 if (!cocoaValue.isEmpty() && m_changeCount == platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName)) 525 return cocoaValue; 526 527 return String(); 528} 529 530static String utiTypeFromCocoaType(const String& type) 531{ 532 if (RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, type.createCFString().get(), 0))) { 533 if (RetainPtr<CFStringRef> mimeType = adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassMIMEType))) 534 return String(mimeType.get()); 535 } 536 return String(); 537} 538 539static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, const String& cocoaType, const String& pasteboardName) 540{ 541 // UTI may not do these right, so make sure we get the right, predictable result 542 if (cocoaType == String(NSStringPboardType)) { 543 resultTypes.add(ASCIILiteral("text/plain")); 544 return; 545 } 546 if (cocoaType == String(NSURLPboardType)) { 547 resultTypes.add(ASCIILiteral("text/uri-list")); 548 return; 549 } 550 if (cocoaType == String(NSFilenamesPboardType)) { 551 // If file list is empty, add nothing. 552 // Note that there is a chance that the file list count could have changed since we grabbed the types array. 553 // However, this is not really an issue for us doing a sanity check here. 554 Vector<String> fileList; 555 platformStrategies()->pasteboardStrategy()->getPathnamesForType(fileList, String(NSFilenamesPboardType), pasteboardName); 556 if (!fileList.isEmpty()) { 557 // It is unknown if NSFilenamesPboardType always implies NSURLPboardType in Cocoa, 558 // but NSFilenamesPboardType should imply both 'text/uri-list' and 'Files' 559 resultTypes.add(ASCIILiteral("text/uri-list")); 560 resultTypes.add(ASCIILiteral("Files")); 561 } 562 return; 563 } 564 String utiType = utiTypeFromCocoaType(cocoaType); 565 if (!utiType.isEmpty()) { 566 resultTypes.add(utiType); 567 return; 568 } 569 // No mapping, just pass the whole string through. 570 resultTypes.add(cocoaType); 571} 572 573void Pasteboard::writeString(const String& type, const String& data) 574{ 575 const String& cocoaType = cocoaTypeFromHTMLClipboardType(type); 576 String cocoaData = data; 577 578 if (cocoaType == String(NSURLPboardType) || cocoaType == String(kUTTypeFileURL)) { 579 NSURL *url = [NSURL URLWithString:cocoaData]; 580 if ([url isFileURL]) 581 return; 582 583 Vector<String> types; 584 types.append(cocoaType); 585 platformStrategies()->pasteboardStrategy()->setTypes(types, m_pasteboardName); 586 m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData, cocoaType, m_pasteboardName); 587 588 return; 589 } 590 591 if (!cocoaType.isEmpty()) { 592 // everything else we know of goes on the pboard as a string 593 Vector<String> types; 594 types.append(cocoaType); 595 platformStrategies()->pasteboardStrategy()->addTypes(types, m_pasteboardName); 596 m_changeCount = platformStrategies()->pasteboardStrategy()->setStringForType(cocoaData, cocoaType, m_pasteboardName); 597 } 598} 599 600Vector<String> Pasteboard::types() 601{ 602 Vector<String> types; 603 platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName); 604 605 // Enforce changeCount ourselves for security. We check after reading instead of before to be 606 // sure it doesn't change between our testing the change count and accessing the data. 607 if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName)) 608 return Vector<String>(); 609 610 ListHashSet<String> result; 611 // FIXME: This loop could be split into two stages. One which adds all the HTML5 specified types 612 // and a second which adds all the extra types from the cocoa clipboard (which is Mac-only behavior). 613 for (size_t i = 0; i < types.size(); i++) { 614 if (types[i] == "NeXT plain ascii pasteboard type") 615 continue; // skip this ancient type that gets auto-supplied by some system conversion 616 617 addHTMLClipboardTypesForCocoaType(result, types[i], m_pasteboardName); 618 } 619 620 copyToVector(result, types); 621 return types; 622} 623 624Vector<String> Pasteboard::readFilenames() 625{ 626 // FIXME: Seems silly to convert paths to URLs and then back to paths. Does that do anything helpful? 627 Vector<String> absoluteURLs = absoluteURLsFromPasteboardFilenames(m_pasteboardName); 628 Vector<String> paths; 629 paths.reserveCapacity(absoluteURLs.size()); 630 for (size_t i = 0; i < absoluteURLs.size(); i++) { 631 NSURL *absoluteURL = [NSURL URLWithString:absoluteURLs[i]]; 632 ASSERT([absoluteURL isFileURL]); 633 paths.uncheckedAppend([absoluteURL path]); 634 } 635 return paths; 636} 637 638void Pasteboard::setDragImage(DragImageRef image, const IntPoint& location) 639{ 640 // Don't allow setting the drag image if someone kept a pasteboard and is trying to set the image too late. 641 if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount(m_pasteboardName)) 642 return; 643 644 // Dashboard wants to be able to set the drag image during dragging, but Cocoa does not allow this. 645 // Instead we must drop down to the CoreGraphics API. 646 wkSetDragImage(image.get(), location); 647 648 // Hack: We must post an event to wake up the NSDragManager, which is sitting in a nextEvent call 649 // up the stack from us because the CoreFoundation drag manager does not use the run loop by itself. 650 // This is the most innocuous event to use, per Kristen Forster. 651 NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved location:NSZeroPoint 652 modifierFlags:0 timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:0 pressure:0]; 653 [NSApp postEvent:event atStart:YES]; 654} 655 656} 657