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