[iOS] Remove soft linking of MobileCoreServices.framework
[WebKit-https.git] / Source / WebCore / platform / ios / PasteboardIOS.mm
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 "Document.h"
30 #import "DocumentFragment.h"
31 #import "DocumentLoader.h"
32 #import "Editor.h"
33 #import "EditorClient.h"
34 #import "Frame.h"
35 #import "FrameLoader.h"
36 #import "FrameLoaderClient.h"
37 #import "HTMLElement.h"
38 #import "HTMLNames.h"
39 #import "HTMLParserIdioms.h"
40 #import "Image.h"
41 #import "LegacyWebArchive.h"
42 #import "Page.h"
43 #import "PasteboardStrategy.h"
44 #import "PlatformStrategies.h"
45 #import "RenderImage.h"
46 #import "RuntimeApplicationChecks.h"
47 #import "SharedBuffer.h"
48 #import "Text.h"
49 #import "URL.h"
50 #import "WebNSAttributedStringExtras.h"
51 #import "htmlediting.h"
52 #import "markup.h"
53 #import <MobileCoreServices/MobileCoreServices.h>
54
55 @interface NSAttributedString (NSAttributedStringKitAdditions)
56 - (id)initWithRTF:(NSData *)data documentAttributes:(NSDictionary **)dict;
57 - (id)initWithRTFD:(NSData *)data documentAttributes:(NSDictionary **)dict;
58 - (NSData *)RTFFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict;
59 - (NSData *)RTFDFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict;
60 - (BOOL)containsAttachments;
61 @end
62
63 namespace WebCore {
64
65 // FIXME: Does this need to be declared in the header file?
66 WEBCORE_EXPORT NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type";
67
68 // Making this non-inline so that WebKit 2's decoding doesn't have to include SharedBuffer.h.
69 PasteboardWebContent::PasteboardWebContent()
70 {
71 }
72
73 PasteboardWebContent::~PasteboardWebContent()
74 {
75 }
76     
77 // Making this non-inline so that WebKit 2's decoding doesn't have to include Image.h.
78 PasteboardImage::PasteboardImage()
79 {
80 }
81
82 PasteboardImage::~PasteboardImage()
83 {
84 }
85
86 Pasteboard::Pasteboard()
87     : m_changeCount(platformStrategies()->pasteboardStrategy()->changeCount())
88 {
89 }
90
91 std::unique_ptr<Pasteboard> Pasteboard::createForCopyAndPaste()
92 {
93     return std::make_unique<Pasteboard>();
94 }
95
96 std::unique_ptr<Pasteboard> Pasteboard::createPrivate()
97 {
98     return std::make_unique<Pasteboard>();
99 }
100
101 void Pasteboard::write(const PasteboardWebContent& content)
102 {
103     platformStrategies()->pasteboardStrategy()->writeToPasteboard(content);
104 }
105
106 String Pasteboard::resourceMIMEType(const NSString *mimeType)
107 {
108     return String(adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)mimeType, NULL)).get());
109 }
110
111 void Pasteboard::write(const PasteboardImage& pasteboardImage)
112 {
113     platformStrategies()->pasteboardStrategy()->writeToPasteboard(pasteboardImage);
114 }
115
116 void Pasteboard::writePlainText(const String& text, SmartReplaceOption)
117 {
118     platformStrategies()->pasteboardStrategy()->writeToPasteboard(kUTTypeText, text);
119 }
120
121 void Pasteboard::write(const PasteboardURL& pasteboardURL)
122 {
123     platformStrategies()->pasteboardStrategy()->writeToPasteboard(kUTTypeURL, pasteboardURL.url.string());
124 }
125
126 void Pasteboard::writePasteboard(const Pasteboard&)
127 {
128 }
129
130 bool Pasteboard::canSmartReplace()
131 {
132     return false;
133 }
134
135 void Pasteboard::read(PasteboardPlainText& text)
136 {
137     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
138     text.text = strategy.readStringFromPasteboard(0, kUTTypeText);
139     if (text.text.isEmpty())
140         text.text = strategy.readStringFromPasteboard(0, kUTTypeURL);
141 }
142
143 static NSArray* supportedImageTypes()
144 {
145     return @[(id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF];
146 }
147
148 void Pasteboard::read(PasteboardWebContentReader& reader)
149 {
150     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
151
152     int numberOfItems = strategy.getPasteboardItemsCount();
153
154     if (!numberOfItems)
155         return;
156
157     NSArray *types = supportedPasteboardTypes();
158     int numberOfTypes = [types count];
159
160     for (int i = 0; i < numberOfItems; i++) {
161         for (int typeIndex = 0; typeIndex < numberOfTypes; typeIndex++) {
162             NSString *type = [types objectAtIndex:typeIndex];
163
164             if ([type isEqualToString:WebArchivePboardType]) {
165                 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, WebArchivePboardType)) {
166                     if (reader.readWebArchive(buffer.get()))
167                         break;
168                 }
169             }
170
171             if ([type isEqualToString:(NSString *)kUTTypeHTML]) {
172                 String htmlString = strategy.readStringFromPasteboard(i, kUTTypeHTML);
173                 if (!htmlString.isNull() && reader.readHTML(htmlString))
174                     break;
175             }
176
177             if ([type isEqualToString:(NSString *)kUTTypeFlatRTFD]) {
178                 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, kUTTypeFlatRTFD)) {
179                     if (reader.readRTFD(*buffer))
180                         break;
181                 }
182             }
183
184             if ([type isEqualToString:(NSString *)kUTTypeRTF]) {
185                 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, kUTTypeRTF)) {
186                     if (reader.readRTF(*buffer))
187                         break;
188                 }
189             }
190
191             if ([supportedImageTypes() containsObject:type]) {
192                 if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(i, type)) {
193                     if (reader.readImage(buffer.releaseNonNull(), type))
194                         break;
195                 }
196         }
197
198             if ([type isEqualToString:(NSString *)kUTTypeURL]) {
199                 URL url = strategy.readURLFromPasteboard(i, kUTTypeURL);
200                 if (!url.isNull() && reader.readURL(url, String()))
201                     break;
202             }
203             
204             if ([type isEqualToString:(NSString *)kUTTypeText]) {
205                 String string = strategy.readStringFromPasteboard(i, kUTTypeText);
206                 if (!string.isNull() && reader.readPlainText(string))
207                     break;
208             }
209
210         }
211     }
212 }
213
214 NSArray* Pasteboard::supportedPasteboardTypes()
215 {
216     return @[(id)WebArchivePboardType, (id)kUTTypeFlatRTFD, (id)kUTTypeRTF, (id)kUTTypeHTML, (id)kUTTypePNG, (id)kUTTypeTIFF, (id)kUTTypeJPEG, (id)kUTTypeGIF, (id)kUTTypeURL, (id)kUTTypeText];
217 }
218
219 bool Pasteboard::hasData()
220 {
221     return platformStrategies()->pasteboardStrategy()->getPasteboardItemsCount() != 0;
222 }
223
224 static String utiTypeFromCocoaType(NSString *type)
225 {
226     RetainPtr<CFStringRef> utiType = adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)type, NULL));
227     if (!utiType)
228         return String();
229     return String(adoptCF(UTTypeCopyPreferredTagWithClass(utiType.get(), kUTTagClassMIMEType)).get());
230 }
231
232 static RetainPtr<NSString> cocoaTypeFromHTMLClipboardType(const String& type)
233 {
234     String strippedType = type.stripWhiteSpace();
235
236     if (strippedType == "Text")
237         return (NSString *)kUTTypeText;
238     if (strippedType == "URL")
239         return (NSString *)kUTTypeURL;
240
241     // Ignore any trailing charset - JS strings are Unicode, which encapsulates the charset issue.
242     if (strippedType.startsWith("text/plain"))
243         return (NSString *)kUTTypeText;
244
245     // Special case because UTI doesn't work with Cocoa's URL type.
246     if (strippedType == "text/uri-list")
247         return (NSString *)kUTTypeURL;
248
249     // Try UTI now.
250     if (NSString *utiType = utiTypeFromCocoaType(strippedType))
251         return utiType;
252
253     // No mapping, just pass the whole string though.
254     return (NSString *)strippedType;
255 }
256
257 void Pasteboard::clear(const String& type)
258 {
259     // Since UIPasteboard enforces changeCount itself on writing, we don't check it here.
260
261     RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type);
262     if (!cocoaType)
263         return;
264
265     platformStrategies()->pasteboardStrategy()->writeToPasteboard(cocoaType.get(), String());
266 }
267
268 void Pasteboard::clear()
269 {
270     platformStrategies()->pasteboardStrategy()->writeToPasteboard(String(), String());
271 }
272
273 String Pasteboard::readString(const String& type)
274 {
275     PasteboardStrategy& strategy = *platformStrategies()->pasteboardStrategy();
276
277     int numberOfItems = strategy.getPasteboardItemsCount();
278
279     if (!numberOfItems)
280         return String();
281
282     // Grab the value off the pasteboard corresponding to the cocoaType.
283     RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type);
284
285     NSString *cocoaValue = nil;
286
287     if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) {
288         URL url = strategy.readURLFromPasteboard(0, kUTTypeURL);
289         if (!url.isNull())
290             cocoaValue = [(NSURL *)url absoluteString];
291     } else if ([cocoaType isEqualToString:(NSString *)kUTTypeText]) {
292         String value = strategy.readStringFromPasteboard(0, kUTTypeText);
293         if (!value.isNull())
294             cocoaValue = [(NSString *)value precomposedStringWithCanonicalMapping];
295     } else if (cocoaType) {
296         if (RefPtr<SharedBuffer> buffer = strategy.readBufferFromPasteboard(0, cocoaType.get()))
297             cocoaValue = [[[NSString alloc] initWithData:buffer->createNSData().get() encoding:NSUTF8StringEncoding] autorelease];
298     }
299
300     // Enforce changeCount ourselves for security. We check after reading instead of before to be
301     // sure it doesn't change between our testing the change count and accessing the data.
302     if (cocoaValue && m_changeCount == platformStrategies()->pasteboardStrategy()->changeCount())
303         return cocoaValue;
304
305     return String();
306 }
307
308 static void addHTMLClipboardTypesForCocoaType(ListHashSet<String>& resultTypes, NSString *cocoaType)
309 {
310     // UTI may not do these right, so make sure we get the right, predictable result.
311     if ([cocoaType isEqualToString:(NSString *)kUTTypeText]) {
312         resultTypes.add(ASCIILiteral("text/plain"));
313         return;
314     }
315     if ([cocoaType isEqualToString:(NSString *)kUTTypeURL]) {
316         resultTypes.add(ASCIILiteral("text/uri-list"));
317         return;
318     }
319     String utiType = utiTypeFromCocoaType(cocoaType);
320     if (!utiType.isEmpty()) {
321         resultTypes.add(utiType);
322         return;
323     }
324     // No mapping, just pass the whole string though.
325     resultTypes.add(cocoaType);
326 }
327
328 void Pasteboard::writeString(const String& type, const String& data)
329 {
330     RetainPtr<NSString> cocoaType = cocoaTypeFromHTMLClipboardType(type);
331     if (!cocoaType)
332         return;
333
334     platformStrategies()->pasteboardStrategy()->writeToPasteboard(type, data);
335 }
336
337 Vector<String> Pasteboard::types()
338 {
339     NSArray* types = supportedPasteboardTypes();
340
341     // Enforce changeCount ourselves for security. We check after reading instead of before to be
342     // sure it doesn't change between our testing the change count and accessing the data.
343     if (m_changeCount != platformStrategies()->pasteboardStrategy()->changeCount())
344         return Vector<String>();
345
346     ListHashSet<String> result;
347     NSUInteger count = [types count];
348     for (NSUInteger i = 0; i < count; i++) {
349         NSString *type = [types objectAtIndex:i];
350         addHTMLClipboardTypesForCocoaType(result, type);
351     }
352
353     Vector<String> vector;
354     copyToVector(result, vector);
355     return vector;
356 }
357
358 Vector<String> Pasteboard::readFilenames()
359 {
360     return Vector<String>();
361 }
362
363 }