| /* |
| * Copyright (C) 2007, 2008, 2012, 2013 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| #import "Pasteboard.h" |
| |
| #import "CachedImage.h" |
| #import "DOMRangeInternal.h" |
| #import "Document.h" |
| #import "DocumentFragment.h" |
| #import "DocumentLoader.h" |
| #import "Editor.h" |
| #import "EditorClient.h" |
| #import "Frame.h" |
| #import "FrameLoader.h" |
| #import "FrameLoaderClient.h" |
| #import "HTMLElement.h" |
| #import "HTMLNames.h" |
| #import "HTMLParserIdioms.h" |
| #import "Image.h" |
| #import "LegacyWebArchive.h" |
| #import "Page.h" |
| #import "PasteboardStrategy.h" |
| #import "PlatformStrategies.h" |
| #import "RenderImage.h" |
| #import "RuntimeApplicationChecksIOS.h" |
| #import "SharedBuffer.h" |
| #import "SoftLinking.h" |
| #import "Text.h" |
| #import "URL.h" |
| #import "WebNSAttributedStringExtras.h" |
| #import "htmlediting.h" |
| #import "markup.h" |
| #import <MobileCoreServices/MobileCoreServices.h> |
| |
| @interface NSAttributedString (NSAttributedStringKitAdditions) |
| - (id)initWithRTF:(NSData *)data documentAttributes:(NSDictionary **)dict; |
| - (id)initWithRTFD:(NSData *)data documentAttributes:(NSDictionary **)dict; |
| - (NSData *)RTFFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict; |
| - (NSData *)RTFDFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict; |
| - (BOOL)containsAttachments; |
| @end |
| |
| // FIXME: The following soft linking and #define needs to be shared with PlatformPasteboardIOS.mm and EditorIOS.mm |
| |
| SOFT_LINK_FRAMEWORK(MobileCoreServices) |
| |
| SOFT_LINK(MobileCoreServices, UTTypeCreatePreferredIdentifierForTag, CFStringRef, (CFStringRef inTagClass, CFStringRef inTag, CFStringRef inConformingToUTI), (inTagClass, inTag, inConformingToUTI)) |
| SOFT_LINK(MobileCoreServices, UTTypeCopyPreferredTagWithClass, CFStringRef, (CFStringRef inUTI, CFStringRef inTagClass), (inUTI, inTagClass)) |
| |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeText, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypePNG, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeJPEG, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeURL, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeTIFF, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeGIF, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTagClassMIMEType, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTagClassFilenameExtension, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeHTML, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeRTFD, CFStringRef) |
| SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeRTF, CFStringRef) |
| |
| #define kUTTypeText getkUTTypeText() |
| #define kUTTypePNG getkUTTypePNG() |
| #define kUTTypeJPEG getkUTTypeJPEG() |
| #define kUTTypeURL getkUTTypeURL() |
| #define kUTTypeTIFF getkUTTypeTIFF() |
| #define kUTTypeGIF getkUTTypeGIF() |
| #define kUTTagClassMIMEType getkUTTagClassMIMEType() |
| #define kUTTagClassFilenameExtension getkUTTagClassFilenameExtension() |
| #define kUTTypeHTML getkUTTypeHTML() |
| #define kUTTypeRTFD getkUTTypeRTFD() |
| #define kUTTypeRTF getkUTTypeRTF() |
| |
| namespace WebCore { |
| |
| // FIXME: Does this need to be declared in the header file? |
| WEBCORE_EXPORT NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type"; |
| |
| // Making this non-inline so that WebKit 2's decoding doesn't have to include SharedBuffer.h. |
| PasteboardWebContent::PasteboardWebContent() |
| { |
| } |
| |
| PasteboardWebContent::~PasteboardWebContent() |
| { |
| } |
| |
| // Making this non-inline so that WebKit 2's decoding doesn't have to include Image.h. |
| PasteboardImage::PasteboardImage() |
| { |
| } |
| |
| PasteboardImage::~PasteboardImage() |
| { |
| } |
| |
| Pasteboard::Pasteboard() |
| : m_changeCount(platformStrategies()->pasteboardStrategy()->changeCount()) |
| { |
| } |
| |
| std::unique_ptr<Pasteboard> Pasteboard::createForCopyAndPaste() |
| { |
| return std::make_unique<Pasteboard>(); |
| } |
| |
| std::unique_ptr<Pasteboard> Pasteboard::createPrivate() |
| { |
| return std::make_unique<Pasteboard>(); |
| } |
| |
| void Pasteboard::write(const PasteboardWebContent& content) |
| { |
| platformStrategies()->pasteboardStrategy()->writeToPasteboard(content); |
| } |
| |
| String Pasteboard::resourceMIMEType(const NSString *mimeType) |
| { |
| return String(adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)mimeType, NULL)).get()); |
| } |
| |
| void Pasteboard::write(const PasteboardImage& pasteboardImage) |
| { |
| platformStrategies()->pasteboardStrategy()->writeToPasteboard(pasteboardImage); |
| } |
| |
| void Pasteboard::writePlainText(const String& text, SmartReplaceOption) |
| { |
| platformStrategies()->pasteboardStrategy()->writeToPasteboard(kUTTypeText, text); |
| } |
| |
| void Pasteboard::write(const PasteboardURL& pasteboardURL) |
| { |
| platformStrategies()->pasteboardStrategy()->writeToPasteboard(kUTTypeURL, pasteboardURL.url.string()); |
| } |
| |
| void Pasteboard::writePasteboard(const Pasteboard&) |
| { |
| } |
| |
| bool Pasteboard::canSmartReplace() |
| { |
| return false; |
| } |
| |
| void Pasteboard::read(PasteboardPlainText& text) |
| { |
| PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); |
| text.text = strategy.readStringFromPasteboard(0, kUTTypeText); |
| if (text.text.isEmpty()) |
| text.text = strategy.readStringFromPasteboard(0, kUTTypeURL); |
| } |
| |
| static NSArray* supportedImageTypes() |
| { |
| return @[(id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF]; |
| } |
| |
| void Pasteboard::read(PasteboardWebContentReader& reader) |
| { |
| PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); |
| |
| int numberOfItems = strategy.getPasteboardItemsCount(); |
| |
| if (!numberOfItems) |
| return; |
| |
| NSArray *types = supportedPasteboardTypes(); |
| int numberOfTypes = [types count]; |
| |
| for (int i = 0; i < numberOfItems; i++) { |
| for (int typeIndex = 0; typeIndex < numberOfTypes; typeIndex++) { |
| NSString *type = [types objectAtIndex:typeIndex]; |
| |
| if ([type isEqualToString:WebArchivePboardType]) { |
| if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, WebArchivePboardType)) { |
| if (reader.readWebArchive(buffer.release())) |
| break; |
| } |
| } |
| |
| if ([type isEqualToString:(NSString *)kUTTypeHTML]) { |
| String htmlString = strategy.readStringFromPasteboard(i, kUTTypeHTML); |
| if (!htmlString.isNull() && reader.readHTML(htmlString)) |
| break; |
| } |
| |
| if ([type isEqualToString:(NSString *)kUTTypeRTFD]) { |
| if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, kUTTypeRTFD)) { |
| if (reader.readRTFD(buffer.release())) |
| break; |
| } |
| } |
| |
| if ([type isEqualToString:(NSString *)kUTTypeRTF]) { |
| if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, kUTTypeRTF)) { |
| if (reader.readRTF(buffer.release())) |
| break; |
| } |
| } |
| |
| if ([supportedImageTypes() containsObject:type]) { |
| if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, type)) { |
| if (reader.readImage(buffer.release(), type)) |
| break; |
| } |
| } |
| |
| if ([type isEqualToString:(NSString *)kUTTypeURL]) { |
| URL url = strategy.readURLFromPasteboard(i, kUTTypeURL); |
| if (!url.isNull() && reader.readURL(url, String())) |
| break; |
| } |
| |
| if ([type isEqualToString:(NSString *)kUTTypeText]) { |
| String string = strategy.readStringFromPasteboard(i, kUTTypeText); |
| if (!string.isNull() && reader.readPlainText(string)) |
| break; |
| } |
| |
| } |
| } |
| } |
| |
| NSArray* Pasteboard::supportedPasteboardTypes() |
| { |
| return @[(id)WebArchivePboardType, (id)kUTTypeRTFD, (id)kUTTypeRTF, (id)kUTTypeHTML, (id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF, (id)kUTTypeURL, (id)kUTTypeText]; |
| } |
| |
| bool Pasteboard::hasData() |
| { |
| return platformStrategies()->pasteboardStrategy()->getPasteboardItemsCount() != 0; |
| } |
| |
| static String utiTypeFromCocoaType(NSString *type) |
| { |
| RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)type, NULL)); |
| if (!utiType) |
| return String(); |
| return String(adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassMIMEType)).get()); |
| } |
| |
| static RetainPtr<NSString> cocoaTypeFromHTMLClipboardType(const String& type) |
| { |
| String strippedType = type.stripWhiteSpace(); |
| |
| if (strippedType == "Text") |
| return (NSString *)kUTTypeText; |
| if (strippedType == "URL") |
| return (NSString *)kUTTypeURL; |
| |
| // Ignore any trailing charset - JS strings are Unicode, which encapsulates the charset issue. |
| if (strippedType.startsWith("text/plain")) |
| return (NSString *)kUTTypeText; |
| |
| // Special case because UTI doesn't work with Cocoa's URL type. |
| if (strippedType == "text/uri-list") |
| return (NSString *)kUTTypeURL; |
| |
| // Try UTI now. |
| if (NSString *utiType = utiTypeFromCocoaType(strippedType)) |
| return utiType; |
| |
| // No mapping, just pass the whole string though. |
| return (NSString *)strippedType; |
| } |
| |
| void Pasteboard::clear(const String& type) |
| { |
| // Since UIPasteboard enforces changeCount itself on writing, we don't check it here. |
| |
| RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); |
| if (!cocoaType) |
| return; |
| |
| platformStrategies()->pasteboardStrategy()->writeToPasteboard(cocoaType.get(), String()); |
| } |
| |
| void Pasteboard::clear() |
| { |
| platformStrategies()->pasteboardStrategy()->writeToPasteboard(String(), String()); |
| } |
| |
| String Pasteboard::readString(const String& type) |
| { |
| PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy(); |
| |
| int numberOfItems = strategy.getPasteboardItemsCount(); |
| |
| if (!numberOfItems) |
| return String(); |
| |
| // Grab the value off the pasteboard corresponding to the cocoaType. |
| RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); |
| |
| NSString *cocoaValue = nil; |
| |
| if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) { |
| URL url = strategy.readURLFromPasteboard(0, kUTTypeURL); |
| if (!url.isNull()) |
| cocoaValue = [(NSURL *)url absoluteString]; |
| } else if ([cocoaType isEqualToString:(NSString *)kUTTypeText]) { |
| String value = strategy.readStringFromPasteboard(0, kUTTypeText); |
| if (!value.isNull()) |
| cocoaValue = [(NSString *)value precomposedStringWithCanonicalMapping]; |
| } else if (cocoaType) { |
| if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(0, cocoaType.get())) |
| cocoaValue = [[[NSString alloc] initWithData:buffer->createNSData().get() encoding:NSUTF8StringEncoding] autorelease]; |
| } |
| |
| // Enforce changeCount ourselves for security. We check after reading instead of before to be |
| // sure it doesn't change between our testing the change count and accessing the data. |
| if (cocoaValue && m_changeCount == platformStrategies()->pasteboardStrategy()->changeCount()) |
| return cocoaValue; |
| |
| return String(); |
| } |
| |
| static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, NSString *cocoaType) |
| { |
| // UTI may not do these right, so make sure we get the right, predictable result. |
| if ([cocoaType isEqualToString:(NSString *)kUTTypeText]) { |
| resultTypes.add(ASCIILiteral("text/plain")); |
| return; |
| } |
| if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) { |
| resultTypes.add(ASCIILiteral("text/uri-list")); |
| return; |
| } |
| String utiType = utiTypeFromCocoaType(cocoaType); |
| if (!utiType.isEmpty()) { |
| resultTypes.add(utiType); |
| return; |
| } |
| // No mapping, just pass the whole string though. |
| resultTypes.add(cocoaType); |
| } |
| |
| void Pasteboard::writeString(const String& type, const String& data) |
| { |
| RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); |
| if (!cocoaType) |
| return; |
| |
| platformStrategies()->pasteboardStrategy()->writeToPasteboard(type, data); |
| } |
| |
| Vector<String> Pasteboard::types() |
| { |
| NSArray* types = supportedPasteboardTypes(); |
| |
| // Enforce changeCount ourselves for security. We check after reading instead of before to be |
| // sure it doesn't change between our testing the change count and accessing the data. |
| if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount()) |
| return Vector<String>(); |
| |
| ListHashSet<String> result; |
| NSUInteger count = [types count]; |
| for (NSUInteger i = 0; i < count; i++) { |
| NSString *type = [types objectAtIndex:i]; |
| addHTMLClipboardTypesForCocoaType(result, type); |
| } |
| |
| Vector<String> vector; |
| copyToVector(result, vector); |
| return vector; |
| } |
| |
| Vector<String> Pasteboard::readFilenames() |
| { |
| return Vector<String>(); |
| } |
| |
| } |