406d45a9db89c54cc244d87cb5a25e14c647570b
[WebKit-https.git] / Source / WebCore / platform / mac / DragDataMac.mm
1 /*
2  * Copyright (C) 2007 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "DragData.h"
28
29 #if ENABLE(DRAG_SUPPORT)
30 #import "LegacyNSPasteboardTypes.h"
31 #import "MIMETypeRegistry.h"
32 #import "NotImplemented.h"
33 #import "Pasteboard.h"
34 #import "PasteboardStrategy.h"
35 #import "PlatformPasteboard.h"
36 #import "PlatformStrategies.h"
37 #import "WebCoreNSURLExtras.h"
38
39 #if PLATFORM(IOS)
40 #import <MobileCoreServices/MobileCoreServices.h>
41 #endif
42
43 namespace WebCore {
44
45 static inline String rtfPasteboardType()
46 {
47 #if PLATFORM(IOS)
48     return String(kUTTypeRTF);
49 #else
50     return String(legacyRTFPasteboardType());
51 #endif
52 }
53
54 static inline String rtfdPasteboardType()
55 {
56 #if PLATFORM(IOS)
57     return String(kUTTypeFlatRTFD);
58 #else
59     return String(legacyRTFDPasteboardType());
60 #endif
61 }
62
63 static inline String stringPasteboardType()
64 {
65 #if PLATFORM(IOS)
66     return String(kUTTypeText);
67 #else
68     return String(legacyStringPasteboardType());
69 #endif
70 }
71
72 static inline String urlPasteboardType()
73 {
74 #if PLATFORM(IOS)
75     return String(kUTTypeURL);
76 #else
77     return String(legacyURLPasteboardType());
78 #endif
79 }
80
81 static inline String htmlPasteboardType()
82 {
83 #if PLATFORM(IOS)
84     return String(kUTTypeHTML);
85 #else
86     return String(legacyHTMLPasteboardType());
87 #endif
88 }
89
90 static inline String colorPasteboardType()
91 {
92 #if PLATFORM(IOS)
93     return "com.apple.uikit.color";
94 #else
95     return String(legacyColorPasteboardType());
96 #endif
97 }
98
99 static inline String pdfPasteboardType()
100 {
101 #if PLATFORM(IOS)
102     return String(kUTTypePDF);
103 #else
104     return String(legacyPDFPasteboardType());
105 #endif
106 }
107
108 static inline String tiffPasteboardType()
109 {
110 #if PLATFORM(IOS)
111     return String(kUTTypeTIFF);
112 #else
113     return String(legacyTIFFPasteboardType());
114 #endif
115 }
116
117 DragData::DragData(DragDataRef data, const IntPoint& clientPosition, const IntPoint& globalPosition, DragOperation sourceOperationMask, DragApplicationFlags flags, DragDestinationAction destinationAction)
118     : m_clientPosition(clientPosition)
119     , m_globalPosition(globalPosition)
120     , m_platformDragData(data)
121     , m_draggingSourceOperationMask(sourceOperationMask)
122     , m_applicationFlags(flags)
123     , m_dragDestinationAction(destinationAction)
124 #if PLATFORM(MAC)
125     , m_pasteboardName([[m_platformDragData draggingPasteboard] name])
126 #else
127     , m_pasteboardName("data interaction pasteboard")
128 #endif
129 {
130 }
131
132 DragData::DragData(const String& dragStorageName, const IntPoint& clientPosition, const IntPoint& globalPosition, DragOperation sourceOperationMask, DragApplicationFlags flags, DragDestinationAction destinationAction)
133     : m_clientPosition(clientPosition)
134     , m_globalPosition(globalPosition)
135     , m_platformDragData(0)
136     , m_draggingSourceOperationMask(sourceOperationMask)
137     , m_applicationFlags(flags)
138     , m_dragDestinationAction(destinationAction)
139     , m_pasteboardName(dragStorageName)
140 {
141 }
142
143 bool DragData::containsURLTypeIdentifier() const
144 {
145     Vector<String> types;
146     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
147     return types.contains(urlPasteboardType());
148 }
149     
150 bool DragData::canSmartReplace() const
151 {
152     return Pasteboard(m_pasteboardName).canSmartReplace();
153 }
154
155 bool DragData::containsColor() const
156 {
157     Vector<String> types;
158     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
159     return types.contains(colorPasteboardType());
160 }
161
162 bool DragData::containsFiles() const
163 {
164     return numberOfFiles();
165 }
166
167 unsigned DragData::numberOfFiles() const
168 {
169     return platformStrategies()->pasteboardStrategy()->getNumberOfFiles(m_pasteboardName);
170 }
171
172 Vector<String> DragData::asFilenames() const
173 {
174 #if PLATFORM(MAC)
175     Vector<String> results;
176     platformStrategies()->pasteboardStrategy()->getPathnamesForType(results, String(legacyFilenamesPasteboardType()), m_pasteboardName);
177     if (!results.isEmpty())
178         return results;
179 #endif
180     return fileNames();
181 }
182
183 bool DragData::containsPlainText() const
184 {
185     Vector<String> types;
186     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
187
188     return types.contains(stringPasteboardType())
189         || types.contains(rtfdPasteboardType())
190         || types.contains(rtfPasteboardType())
191 #if PLATFORM(MAC)
192         || types.contains(String(legacyFilenamesPasteboardType()))
193 #endif
194         || platformStrategies()->pasteboardStrategy()->stringForType(urlPasteboardType(), m_pasteboardName).length();
195 }
196
197 String DragData::asPlainText() const
198 {
199     Pasteboard pasteboard(m_pasteboardName);
200     PasteboardPlainText text;
201     pasteboard.read(text);
202     String string = text.text;
203
204     // FIXME: It's not clear this is 100% correct since we know -[NSURL URLWithString:] does not handle
205     // all the same cases we handle well in the URL code for creating an NSURL.
206     if (text.isURL)
207         return userVisibleString([NSURL URLWithString:string]);
208
209     // FIXME: WTF should offer a non-Mac-specific way to convert string to precomposed form so we can do it for all platforms.
210     return [(NSString *)string precomposedStringWithCanonicalMapping];
211 }
212
213 Color DragData::asColor() const
214 {
215     return platformStrategies()->pasteboardStrategy()->color(m_pasteboardName);
216 }
217
218 bool DragData::containsCompatibleContent(DraggingPurpose purpose) const
219 {
220     if (purpose == DraggingPurpose::ForFileUpload)
221         return containsFiles();
222
223     Vector<String> types;
224     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
225     return types.contains(String(WebArchivePboardType))
226         || types.contains(htmlPasteboardType())
227 #if PLATFORM(MAC)
228         || types.contains(String(legacyFilenamesPasteboardType()))
229         || types.contains(String(legacyFilesPromisePasteboardType()))
230 #endif
231         || types.contains(tiffPasteboardType())
232         || types.contains(pdfPasteboardType())
233         || types.contains(urlPasteboardType())
234         || types.contains(rtfdPasteboardType())
235         || types.contains(rtfPasteboardType())
236         || types.contains(String(kUTTypeUTF8PlainText))
237         || types.contains(stringPasteboardType())
238         || types.contains(colorPasteboardType())
239         || types.contains(String(kUTTypeJPEG))
240         || types.contains(String(kUTTypePNG));
241 }
242
243 bool DragData::containsPromise() const
244 {
245     Vector<String> files;
246 #if PLATFORM(MAC)
247     platformStrategies()->pasteboardStrategy()->getPathnamesForType(files, String(legacyFilesPromisePasteboardType()), m_pasteboardName);
248 #endif
249     return files.size() == 1;
250 }
251
252 bool DragData::containsURL(FilenameConversionPolicy filenamePolicy) const
253 {
254 #if PLATFORM(IOS)
255     UNUSED_PARAM(filenamePolicy);
256     Vector<String> types;
257     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
258     if (!types.contains(urlPasteboardType()))
259         return false;
260
261     auto urlString = platformStrategies()->pasteboardStrategy()->stringForType(urlPasteboardType(), m_pasteboardName);
262     if (urlString.isEmpty()) {
263         // On iOS, we don't get access to the contents of UIItemProviders until we perform the drag operation.
264         // Thus, we consider DragData to contain an URL if it contains the `public.url` UTI type. Later down the
265         // road, when we perform the drag operation, we can then check if the URL's protocol is http or https,
266         // and if it isn't, we bail out of page navigation.
267         return true;
268     }
269
270     URL webcoreURL = [NSURL URLWithString:urlString];
271     return webcoreURL.protocolIs("http") || webcoreURL.protocolIs("https");
272 #else
273     return !asURL(filenamePolicy).isEmpty();
274 #endif
275 }
276
277 String DragData::asURL(FilenameConversionPolicy, String* title) const
278 {
279     // FIXME: Use filenamePolicy.
280
281     if (title) {
282 #if PLATFORM(MAC)
283         String URLTitleString = platformStrategies()->pasteboardStrategy()->stringForType(String(WebURLNamePboardType), m_pasteboardName);
284         if (!URLTitleString.isEmpty())
285             *title = URLTitleString;
286 #endif
287     }
288     
289     Vector<String> types;
290     platformStrategies()->pasteboardStrategy()->getTypes(types, m_pasteboardName);
291
292     if (types.contains(urlPasteboardType())) {
293         NSURL *URLFromPasteboard = [NSURL URLWithString:platformStrategies()->pasteboardStrategy()->stringForType(urlPasteboardType(), m_pasteboardName)];
294         NSString *scheme = [URLFromPasteboard scheme];
295         // Cannot drop other schemes unless <rdar://problem/10562662> and <rdar://problem/11187315> are fixed.
296         if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])
297             return [URLByCanonicalizingURL(URLFromPasteboard) absoluteString];
298     }
299     
300     if (types.contains(stringPasteboardType())) {
301         NSURL *URLFromPasteboard = [NSURL URLWithString:platformStrategies()->pasteboardStrategy()->stringForType(stringPasteboardType(), m_pasteboardName)];
302         NSString *scheme = [URLFromPasteboard scheme];
303         // Pasteboard content is not trusted, because JavaScript code can modify it. We can sanitize it for URLs and other typed content, but not for strings.
304         // The result of this function is used to initiate navigation, so we shouldn't allow arbitrary file URLs.
305         // FIXME: Should we allow only http family schemes, or anything non-local?
306         if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])
307             return [URLByCanonicalizingURL(URLFromPasteboard) absoluteString];
308     }
309     
310 #if PLATFORM(MAC)
311     if (types.contains(String(legacyFilenamesPasteboardType()))) {
312         Vector<String> files;
313         platformStrategies()->pasteboardStrategy()->getPathnamesForType(files, String(legacyFilenamesPasteboardType()), m_pasteboardName);
314         if (files.size() == 1) {
315             BOOL isDirectory;
316             if ([[NSFileManager defaultManager] fileExistsAtPath:files[0] isDirectory:&isDirectory] && isDirectory)
317                 return String();
318             return [URLByCanonicalizingURL([NSURL fileURLWithPath:files[0]]) absoluteString];
319         }
320     }
321
322     if (types.contains(String(legacyFilesPromisePasteboardType())) && fileNames().size() == 1)
323         return [URLByCanonicalizingURL([NSURL fileURLWithPath:fileNames()[0]]) absoluteString];
324 #endif
325
326     return String();        
327 }
328
329 } // namespace WebCore
330
331 #endif // ENABLE(DRAG_SUPPORT)