1/* 2 * Copyright (C) 2007, 2008, 2012, 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. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#import "config.h" 26#import "Pasteboard.h" 27 28#import "CachedImage.h" 29#import "DOMRangeInternal.h" 30#import "Document.h" 31#import "DocumentFragment.h" 32#import "DocumentLoader.h" 33#import "Editor.h" 34#import "EditorClient.h" 35#import "Frame.h" 36#import "FrameLoader.h" 37#import "FrameLoaderClient.h" 38#import "HTMLElement.h" 39#import "HTMLNames.h" 40#import "HTMLParserIdioms.h" 41#import "Image.h" 42#import "LegacyWebArchive.h" 43#import "Page.h" 44#import "PasteboardStrategy.h" 45#import "PlatformStrategies.h" 46#import "RenderImage.h" 47#import "RuntimeApplicationChecksIOS.h" 48#import "SharedBuffer.h" 49#import "SoftLinking.h" 50#import "Text.h" 51#import "URL.h" 52#import "WebNSAttributedStringExtras.h" 53#import "htmlediting.h" 54#import "markup.h" 55#import <MobileCoreServices/MobileCoreServices.h> 56 57@interface NSAttributedString (NSAttributedStringKitAdditions) 58- (id)initWithRTF:(NSData *)data documentAttributes:(NSDictionary **)dict; 59- (id)initWithRTFD:(NSData *)data documentAttributes:(NSDictionary **)dict; 60- (NSData *)RTFFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict; 61- (NSData *)RTFDFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict; 62- (BOOL)containsAttachments; 63@end 64 65// FIXME: The following soft linking and #define needs to be shared with PlatformPasteboardIOS.mm and EditorIOS.mm 66 67SOFT_LINK_FRAMEWORK(MobileCoreServices) 68 69SOFT_LINK(MobileCoreServices, UTTypeCreatePreferredIdentifierForTag, CFStringRef, (CFStringRef inTagClass, CFStringRef inTag, CFStringRef inConformingToUTI), (inTagClass, inTag, inConformingToUTI)) 70SOFT_LINK(MobileCoreServices, UTTypeCopyPreferredTagWithClass, CFStringRef, (CFStringRef inUTI, CFStringRef inTagClass), (inUTI, inTagClass)) 71 72SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeText, CFStringRef) 73SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypePNG, CFStringRef) 74SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeJPEG, CFStringRef) 75SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeURL, CFStringRef) 76SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeTIFF, CFStringRef) 77SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeGIF, CFStringRef) 78SOFT_LINK_CONSTANT(MobileCoreServices, kUTTagClassMIMEType, CFStringRef) 79SOFT_LINK_CONSTANT(MobileCoreServices, kUTTagClassFilenameExtension, CFStringRef) 80SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeHTML, CFStringRef) 81SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeRTFD, CFStringRef) 82SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeRTF, CFStringRef) 83 84#define kUTTypeText getkUTTypeText() 85#define kUTTypePNG getkUTTypePNG() 86#define kUTTypeJPEG getkUTTypeJPEG() 87#define kUTTypeURL getkUTTypeURL() 88#define kUTTypeTIFF getkUTTypeTIFF() 89#define kUTTypeGIF getkUTTypeGIF() 90#define kUTTagClassMIMEType getkUTTagClassMIMEType() 91#define kUTTagClassFilenameExtension getkUTTagClassFilenameExtension() 92#define kUTTypeHTML getkUTTypeHTML() 93#define kUTTypeRTFD getkUTTypeRTFD() 94#define kUTTypeRTF getkUTTypeRTF() 95 96namespace WebCore { 97 98// FIXME: Does this need to be declared in the header file? 99NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type"; 100 101// Making this non-inline so that WebKit 2's decoding doesn't have to include SharedBuffer.h. 102PasteboardWebContent::PasteboardWebContent() 103{ 104} 105 106PasteboardWebContent::~PasteboardWebContent() 107{ 108} 109 110// Making this non-inline so that WebKit 2's decoding doesn't have to include Image.h. 111PasteboardImage::PasteboardImage() 112{ 113} 114 115PasteboardImage::~PasteboardImage() 116{ 117} 118 119Pasteboard::Pasteboard() 120 : m_changeCount(platformStrategies()->pasteboardStrategy()->changeCount()) 121{ 122} 123 124PassOwnPtr<Pasteboard> Pasteboard::createForCopyAndPaste() 125{ 126 return adoptPtr(new Pasteboard); 127} 128 129PassOwnPtr<Pasteboard> Pasteboard::createPrivate() 130{ 131 return adoptPtr(new Pasteboard); 132} 133 134void Pasteboard::write(const PasteboardWebContent& content) 135{ 136 platformStrategies()->pasteboardStrategy()->writeToPasteboard(content); 137} 138 139String Pasteboard::resourceMIMEType(const NSString *mimeType) 140{ 141 return String(adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)mimeType, NULL)).get()); 142} 143 144void Pasteboard::write(const PasteboardImage& pasteboardImage) 145{ 146 platformStrategies()->pasteboardStrategy()->writeToPasteboard(pasteboardImage); 147} 148 149void Pasteboard::writePlainText(const String& text, SmartReplaceOption) 150{ 151 platformStrategies()->pasteboardStrategy()->writeToPasteboard(kUTTypeText, text); 152} 153 154void Pasteboard::write(const PasteboardURL& pasteboardURL) 155{ 156 platformStrategies()->pasteboardStrategy()->writeToPasteboard(kUTTypeURL, pasteboardURL.url.string()); 157} 158 159void Pasteboard::writePasteboard(const Pasteboard&) 160{ 161} 162 163bool Pasteboard::canSmartReplace() 164{ 165 return false; 166} 167 168void Pasteboard::read(PasteboardPlainText& text) 169{ 170 PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); 171 text.text = strategy.readStringFromPasteboard(0, kUTTypeText); 172} 173 174static NSArray* supportedImageTypes() 175{ 176 return @[(id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF]; 177} 178 179void Pasteboard::read(PasteboardWebContentReader& reader) 180{ 181 PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); 182 183 int numberOfItems = strategy.getPasteboardItemsCount(); 184 185 if (!numberOfItems) 186 return; 187 188 NSArray *types = supportedPasteboardTypes(); 189 int numberOfTypes = [types count]; 190 191 for (int i = 0; i < numberOfItems; i++) { 192 for (int typeIndex = 0; typeIndex < numberOfTypes; typeIndex++) { 193 NSString *type = [types objectAtIndex:typeIndex]; 194 195 if ([type isEqualToString:WebArchivePboardType]) { 196 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, WebArchivePboardType)) { 197 if (reader.readWebArchive(buffer.release())) 198 break; 199 } 200 } 201 202 if ([type isEqualToString:(NSString *)kUTTypeHTML]) { 203 String htmlString = strategy.readStringFromPasteboard(i, kUTTypeHTML); 204 if (!htmlString.isNull() && reader.readHTML(htmlString)) 205 break; 206 } 207 208 if ([type isEqualToString:(NSString *)kUTTypeRTFD]) { 209 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, kUTTypeRTFD)) { 210 if (reader.readRTFD(buffer.release())) 211 break; 212 } 213 } 214 215 if ([type isEqualToString:(NSString *)kUTTypeRTF]) { 216 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, kUTTypeRTF)) { 217 if (reader.readRTF(buffer.release())) 218 break; 219 } 220 } 221 222 if ([supportedImageTypes() containsObject:type]) { 223 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, type)) { 224 if (reader.readImage(buffer.release(), type)) 225 break; 226 } 227 } 228 229 if ([type isEqualToString:(NSString *)kUTTypeURL]) { 230 URL url = strategy.readURLFromPasteboard(i, kUTTypeURL); 231 if (!url.isNull() && reader.readURL(url, String())) 232 break; 233 } 234 235 if ([type isEqualToString:(NSString *)kUTTypeText]) { 236 String string = strategy.readStringFromPasteboard(i, kUTTypeText); 237 if (!string.isNull() && reader.readPlainText(string)) 238 break; 239 } 240 241 } 242 } 243} 244 245NSArray* Pasteboard::supportedPasteboardTypes() 246{ 247 return @[(id)WebArchivePboardType, (id)kUTTypeHTML, (id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF, (id)kUTTypeURL, (id)kUTTypeText, (id)kUTTypeRTFD, (id)kUTTypeRTF]; 248} 249 250bool Pasteboard::hasData() 251{ 252 return platformStrategies()->pasteboardStrategy()->getPasteboardItemsCount() != 0; 253} 254 255static String utiTypeFromCocoaType(NSString *type) 256{ 257 RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)type, NULL)); 258 if (!utiType) 259 return String(); 260 return String(adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassMIMEType)).get()); 261} 262 263static RetainPtr<NSString> cocoaTypeFromHTMLClipboardType(const String& type) 264{ 265 String strippedType = type.stripWhiteSpace(); 266 267 if (strippedType == "Text") 268 return (NSString *)kUTTypeText; 269 if (strippedType == "URL") 270 return (NSString *)kUTTypeURL; 271 272 // Ignore any trailing charset - JS strings are Unicode, which encapsulates the charset issue. 273 if (strippedType.startsWith("text/plain")) 274 return (NSString *)kUTTypeText; 275 276 // Special case because UTI doesn't work with Cocoa's URL type. 277 if (strippedType == "text/uri-list") 278 return (NSString *)kUTTypeURL; 279 280 // Try UTI now. 281 if (NSString *utiType = utiTypeFromCocoaType(strippedType)) 282 return utiType; 283 284 // No mapping, just pass the whole string though. 285 return (NSString *)strippedType; 286} 287 288void Pasteboard::clear(const String& type) 289{ 290 // Since UIPasteboard enforces changeCount itself on writing, we don't check it here. 291 292 RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); 293 if (!cocoaType) 294 return; 295 296 platformStrategies()->pasteboardStrategy()->writeToPasteboard(cocoaType.get(), String()); 297} 298 299void Pasteboard::clear() 300{ 301 platformStrategies()->pasteboardStrategy()->writeToPasteboard(String(), String()); 302} 303 304String Pasteboard::readString(const String& type) 305{ 306 PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); 307 308 int numberOfItems = strategy.getPasteboardItemsCount(); 309 310 if (!numberOfItems) 311 return String(); 312 313 // Grab the value off the pasteboard corresponding to the cocoaType. 314 RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); 315 316 NSString *cocoaValue = nil; 317 318 if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) { 319 URL url = strategy.readURLFromPasteboard(0, kUTTypeURL); 320 if (!url.isNull()) 321 cocoaValue = [(NSURL *)url absoluteString]; 322 } else if ([cocoaType isEqualToString:(NSString *)kUTTypeText]) { 323 String value = strategy.readStringFromPasteboard(0, kUTTypeText); 324 if (!value.isNull()) 325 cocoaValue = [(NSString *)value precomposedStringWithCanonicalMapping];; 326 } else if (cocoaType) { 327 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(0, cocoaType.get())) 328 cocoaValue = [[[NSString alloc] initWithData:buffer->createNSData().get() encoding:NSUTF8StringEncoding] autorelease]; 329 } 330 331 // Enforce changeCount ourselves for security. We check after reading instead of before to be 332 // sure it doesn't change between our testing the change count and accessing the data. 333 if (cocoaValue && m_changeCount == platformStrategies()->pasteboardStrategy()->changeCount()) 334 return cocoaValue; 335 336 return String(); 337} 338 339static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, NSString *cocoaType) 340{ 341 // UTI may not do these right, so make sure we get the right, predictable result. 342 if ([cocoaType isEqualToString:(NSString *)kUTTypeText]) { 343 resultTypes.add(ASCIILiteral("text/plain")); 344 return; 345 } 346 if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) { 347 resultTypes.add(ASCIILiteral("text/uri-list")); 348 return; 349 } 350 String utiType = utiTypeFromCocoaType(cocoaType); 351 if (!utiType.isEmpty()) { 352 resultTypes.add(utiType); 353 return; 354 } 355 // No mapping, just pass the whole string though. 356 resultTypes.add(cocoaType); 357} 358 359void Pasteboard::writeString(const String& type, const String& data) 360{ 361 RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); 362 if (!cocoaType) 363 return; 364 365 platformStrategies()->pasteboardStrategy()->writeToPasteboard(type, data); 366} 367 368Vector<String> Pasteboard::types() 369{ 370 NSArray* types = supportedPasteboardTypes(); 371 372 // Enforce changeCount ourselves for security. We check after reading instead of before to be 373 // sure it doesn't change between our testing the change count and accessing the data. 374 if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount()) 375 return Vector<String>(); 376 377 ListHashSet<String> result; 378 NSUInteger count = [types count]; 379 for (NSUInteger i = 0; i < count; i++) { 380 NSString *type = [types objectAtIndex:i]; 381 addHTMLClipboardTypesForCocoaType(result, type); 382 } 383 384 Vector<String> vector; 385 copyToVector(result, vector); 386 return vector; 387} 388 389Vector<String> Pasteboard::readFilenames() 390{ 391 return Vector<String>(); 392} 393 394} 395