1/* 2 * Copyright (C) 2011 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 "ClipboardIOS.h" 27 28#import "DragData.h" 29#import "Editor.h" 30#import "EditorClient.h" 31#import "FileList.h" 32#import "Frame.h" 33#import "Pasteboard.h" 34 35#include "SoftLinking.h" 36#include <MobileCoreServices/MobileCoreServices.h> 37 38SOFT_LINK_FRAMEWORK(MobileCoreServices) 39 40SOFT_LINK(MobileCoreServices, UTTypeCreatePreferredIdentifierForTag, CFStringRef, (CFStringRef inTagClass, CFStringRef inTag, CFStringRef inConformingToUTI), (inTagClass, inTag, inConformingToUTI)) 41SOFT_LINK(MobileCoreServices, UTTypeCopyPreferredTagWithClass, CFStringRef, (CFStringRef inUTI, CFStringRef inTagClass), (inUTI, inTagClass)) 42 43SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeText, CFStringRef) 44SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeURL, CFStringRef) 45SOFT_LINK_CONSTANT(MobileCoreServices, kUTTagClassMIMEType, CFStringRef) 46 47#define kUTTypeText getkUTTypeText() 48#define kUTTypeURL getkUTTypeURL() 49#define kUTTypeTIFF getkUTTypeTIFF() 50#define kUTTagClassMIMEType getkUTTagClassMIMEType() 51 52namespace WebCore { 53 54PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData*, Frame* frame) 55{ 56 return ClipboardIOS::create(DragAndDrop, policy, frame); 57} 58 59ClipboardIOS::ClipboardIOS(ClipboardType clipboardType, ClipboardAccessPolicy policy, Frame* frame) 60 : Clipboard(policy, clipboardType) 61 , m_frame(frame) 62{ 63 m_changeCount = m_frame->editor().client()->pasteboardChangeCount(); 64} 65 66ClipboardIOS::~ClipboardIOS() 67{ 68} 69 70bool ClipboardIOS::hasData() 71{ 72 return m_frame->editor().client()->getPasteboardItemsCount() != 0; 73} 74 75static String utiTypeFromCocoaType(NSString* type) 76{ 77 RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)type, NULL)); 78 if (utiType) { 79 RetainPtr<CFStringRef> mimeType = adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassMIMEType)); 80 if (mimeType) 81 return String(mimeType.get()); 82 } 83 return String(); 84} 85 86static RetainPtr<NSString> cocoaTypeFromHTMLClipboardType(const String& type) 87{ 88 String qType = type.stripWhiteSpace(); 89 90 if (qType == "Text") 91 return (NSString*)kUTTypeText; 92 if (qType == "URL") 93 return (NSString*)kUTTypeURL; 94 95 // Ignore any trailing charset - JS strings are Unicode, which encapsulates the charset issue. 96 if (qType.startsWith(ASCIILiteral("text/plain"))) 97 return (NSString*)kUTTypeText; 98 if (qType == "text/uri-list") 99 // Special case because UTI doesn't work with Cocoa's URL type. 100 return (NSString*)kUTTypeURL; 101 102 // Try UTI now. 103 NSString *pbType = utiTypeFromCocoaType(qType); 104 if (pbType) 105 return pbType; 106 107 // No mapping, just pass the whole string though. 108 return (NSString*)qType; 109} 110 111static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, NSString* cocoaType) 112{ 113 // UTI may not do these right, so make sure we get the right, predictable result. 114 if ([cocoaType isEqualToString:(NSString*)kUTTypeText]) { 115 resultTypes.add(ASCIILiteral("text/plain")); 116 return; 117 } 118 if ([cocoaType isEqualToString:(NSString*)kUTTypeURL]) { 119 resultTypes.add(ASCIILiteral("text/uri-list")); 120 return; 121 } 122 String utiType = utiTypeFromCocoaType(cocoaType); 123 if (!utiType.isEmpty()) { 124 resultTypes.add(utiType); 125 return; 126 } 127 // No mapping, just pass the whole string though. 128 resultTypes.add(cocoaType); 129} 130 131void ClipboardIOS::clearData(const String& type) 132{ 133 if (!canWriteData()) 134 return; 135 136 // Note UIPasteboard enforces changeCount itself on writing - can't write if not the owner. 137 138 if (RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type)) { 139 RetainPtr<NSDictionary> representations = adoptNS([[NSMutableDictionary alloc] init]); 140 [representations.get() setValue:0 forKey:cocoaType.get()]; 141 m_frame->editor().client()->writeDataToPasteboard(representations.get()); 142 } 143} 144 145void ClipboardIOS::clearData() 146{ 147 if (!canWriteData()) 148 return; 149 150 RetainPtr<NSDictionary> representations = adoptNS([[NSMutableDictionary alloc] init]); 151 m_frame->editor().client()->writeDataToPasteboard(representations.get()); 152} 153 154String ClipboardIOS::getData(const String& type) const 155{ 156 if (!canReadData()) 157 return String(); 158 159 RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); 160 NSString *cocoaValue = nil; 161 162 // Grab the value off the pasteboard corresponding to the cocoaType. 163 RetainPtr<NSArray> pasteboardItem = m_frame->editor().client()->readDataFromPasteboard(cocoaType.get(), 0); 164 165 if ([pasteboardItem.get() count] == 0) 166 return String(); 167 168 if ([cocoaType.get() isEqualToString:(NSString*)kUTTypeURL]) { 169 id value = [pasteboardItem.get() objectAtIndex:0]; 170 if (![value isKindOfClass:[NSURL class]]) { 171 ASSERT([value isKindOfClass:[NSURL class]]); 172 return String(); 173 } 174 NSURL* absoluteURL = (NSURL*)value; 175 176 if (absoluteURL) 177 cocoaValue = [absoluteURL absoluteString]; 178 } else if ([cocoaType.get() isEqualToString:(NSString*)kUTTypeText]) { 179 id value = [pasteboardItem.get() objectAtIndex:0]; 180 if (![value isKindOfClass:[NSString class]]) { 181 ASSERT([value isKindOfClass:[NSString class]]); 182 return String(); 183 } 184 185 cocoaValue = [(NSString*)value precomposedStringWithCanonicalMapping]; 186 } else if (cocoaType) { 187 ASSERT([pasteboardItem.get() count] == 1); 188 id value = [pasteboardItem.get() objectAtIndex:0]; 189 if (![value isKindOfClass:[NSData class]]) { 190 ASSERT([value isKindOfClass:[NSData class]]); 191 return String(); 192 } 193 cocoaValue = [[[NSString alloc] initWithData:(NSData *)value encoding:NSUTF8StringEncoding] autorelease]; 194 } 195 196 // Enforce changeCount ourselves for security. We check after reading instead of before to be 197 // sure it doesn't change between our testing the change count and accessing the data. 198 if (cocoaValue && m_changeCount == m_frame->editor().client()->pasteboardChangeCount()) 199 return cocoaValue; 200 201 return String(); 202} 203 204bool ClipboardIOS::setData(const String& type, const String& data) 205{ 206 if (!canWriteData()) 207 return false; 208 209 RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type); 210 NSString *cocoaData = data; 211 212 RetainPtr<NSDictionary> representations = adoptNS([[NSMutableDictionary alloc] init]); 213 214 if ([cocoaType.get() isEqualToString:(NSString*)kUTTypeURL]) { 215 RetainPtr<NSURL> url = adoptNS([[NSURL alloc] initWithString:cocoaData]); 216 [representations.get() setValue:url.get() forKey:(NSString*)kUTTypeURL]; 217 m_frame->editor().client()->writeDataToPasteboard(representations.get()); 218 return true; 219 } 220 221 if (cocoaType) { 222 // Everything else we know of goes on the pboard as the original string 223 // we received as parameter. 224 [representations.get() setValue:cocoaData forKey:cocoaType.get()]; 225 m_frame->editor().client()->writeDataToPasteboard(representations.get()); 226 return true; 227 } 228 229 return false; 230} 231 232ListHashSet<String> ClipboardIOS::types() const 233{ 234 if (!canReadTypes()) 235 return ListHashSet<String>(); 236 237 NSArray* types = Pasteboard::supportedPasteboardTypes(); 238 239 // Enforce changeCount ourselves for security. We check after reading instead of before to be 240 // sure it doesn't change between our testing the change count and accessing the data. 241 if (m_changeCount != m_frame->editor().client()->pasteboardChangeCount()) 242 return ListHashSet<String>(); 243 244 ListHashSet<String> result; 245 NSUInteger count = [types count]; 246 for (NSUInteger i = 0; i < count; i++) { 247 NSString* pbType = [types objectAtIndex:i]; 248 addHTMLClipboardTypesForCocoaType(result, pbType); 249 } 250 251 return result; 252} 253 254PassRefPtr<FileList> ClipboardIOS::files() const 255{ 256 return 0; 257} 258 259void ClipboardIOS::writeRange(Range* range, Frame* frame) 260{ 261 ASSERT(range); 262 ASSERT(frame); 263 Pasteboard::generalPasteboard()->writeSelection(range, frame->editor().smartInsertDeleteEnabled() && frame->selection()->granularity() == WordGranularity, frame); 264} 265 266void ClipboardIOS::writePlainText(const String& text) 267{ 268 Pasteboard::generalPasteboard()->writePlainText(text, m_frame); 269} 270 271void ClipboardIOS::writeURL(const KURL&, const String&, Frame*) 272{ 273} 274 275#if ENABLE(DRAG_SUPPORT) 276void ClipboardIOS::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) 277{ 278 ASSERT(frame); 279 if (Page* page = frame->page()) 280 page->dragController()->client()->declareAndWriteDragImage(m_pasteboard.get(), kit(element), url, title, frame); 281} 282#endif // ENABLE(DRAG_SUPPORT) 283 284DragImageRef ClipboardIOS::createDragImage(IntPoint&) const 285{ 286 return 0; 287} 288 289void ClipboardIOS::setDragImage(CachedImage*, const IntPoint&) 290{ 291} 292 293void ClipboardIOS::setDragImageElement(Node *, const IntPoint&) 294{ 295} 296 297} 298