c293ea2b82574367526492754708cc4031313aa2
[WebKit-https.git] / Source / WebCore / platform / mac / PlatformPasteboardMac.mm
1 /*
2  * Copyright (C) 2006 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 "PlatformPasteboard.h"
28
29 #import "Color.h"
30 #import "Pasteboard.h"
31 #import "URL.h"
32 #import "SharedBuffer.h"
33 #import <wtf/HashCountedSet.h>
34 #import <wtf/ListHashSet.h>
35 #import <wtf/text/StringHash.h>
36
37 namespace WebCore {
38
39 PlatformPasteboard::PlatformPasteboard(const String& pasteboardName)
40     : m_pasteboard([NSPasteboard pasteboardWithName:pasteboardName])
41 {
42     ASSERT(pasteboardName);
43 }
44
45 void PlatformPasteboard::getTypes(Vector<String>& types)
46 {
47     NSArray *pasteboardTypes = [m_pasteboard.get() types];
48
49     for (NSUInteger i = 0; i < [pasteboardTypes count]; i++)
50         types.append([pasteboardTypes objectAtIndex:i]);
51 }
52
53 RefPtr<SharedBuffer> PlatformPasteboard::bufferForType(const String& pasteboardType)
54 {
55     NSData *data = [m_pasteboard.get() dataForType:pasteboardType];
56     if (!data)
57         return nullptr;
58     return SharedBuffer::create([[data copy] autorelease]);
59 }
60
61 int PlatformPasteboard::numberOfFiles() const
62 {
63     Vector<String> files;
64     getPathnamesForType(files, String(NSFilenamesPboardType));
65     if (!files.size())
66         getPathnamesForType(files, String(NSFilesPromisePboardType));
67     return files.size();
68 }
69
70 void PlatformPasteboard::getPathnamesForType(Vector<String>& pathnames, const String& pasteboardType) const
71 {
72     NSArray* paths = [m_pasteboard.get() propertyListForType:pasteboardType];
73     if ([paths isKindOfClass:[NSString class]]) {
74         pathnames.append((NSString *)paths);
75         return;
76     }
77     for (NSUInteger i = 0; i < [paths count]; i++)
78         pathnames.append([paths objectAtIndex:i]);
79 }
80
81 String PlatformPasteboard::stringForType(const String& pasteboardType)
82 {
83     if (pasteboardType == String(NSURLPboardType)) {
84         if (NSURL *urlFromPasteboard = [NSURL URLFromPasteboard:m_pasteboard.get()])
85             return urlFromPasteboard.absoluteString;
86
87         URL url([NSURL URLWithString:[m_pasteboard stringForType:NSURLPboardType]]);
88         if (!url.isValid())
89             return { };
90         return url.string();
91     }
92
93     return [m_pasteboard stringForType:pasteboardType];
94 }
95
96 static const char* safeTypeForDOMToReadAndWriteForPlatformType(const String& platformType)
97 {
98     if (platformType == String(NSStringPboardType) || platformType == String(NSPasteboardTypeString))
99         return ASCIILiteral("text/plain");
100
101     if (platformType == String(NSURLPboardType))
102         return ASCIILiteral("text/uri-list");
103
104     if (platformType == String(NSHTMLPboardType))
105         return ASCIILiteral("text/html");
106
107     return nullptr;
108 }
109
110 Vector<String> PlatformPasteboard::typesSafeForDOMToReadAndWrite(const String& origin) const
111 {
112     ListHashSet<String> domPasteboardTypes;
113     if (NSData *serializedCustomData = [m_pasteboard dataForType:@(PasteboardCustomData::cocoaType())]) {
114         auto data = PasteboardCustomData::fromSharedBuffer(SharedBuffer::create(serializedCustomData).get());
115         if (data.origin == origin) {
116             for (auto& type : data.orderedTypes)
117                 domPasteboardTypes.add(type);
118         }
119     }
120
121     NSArray<NSString *> *allTypes = [m_pasteboard types];
122     for (NSString *type in allTypes) {
123         if ([type isEqualToString:@(PasteboardCustomData::cocoaType())])
124             continue;
125
126         if (Pasteboard::isSafeTypeForDOMToReadAndWrite(type))
127             domPasteboardTypes.add(type);
128         else if (auto* domType = safeTypeForDOMToReadAndWriteForPlatformType(type))
129             domPasteboardTypes.add(String::fromUTF8(domType));
130     }
131
132     return copyToVector(domPasteboardTypes);
133 }
134
135 long PlatformPasteboard::write(const PasteboardCustomData& data)
136 {
137     NSMutableArray *types = [NSMutableArray array];
138     for (auto& entry : data.platformData)
139         [types addObject:platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(entry.key)];
140     if (data.sameOriginCustomData.size())
141         [types addObject:@(PasteboardCustomData::cocoaType())];
142
143     [m_pasteboard declareTypes:types owner:nil];
144
145     for (auto& entry : data.platformData) {
146         auto platformType = platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(entry.key);
147         ASSERT(!platformType.isEmpty());
148         if (!platformType.isEmpty())
149             [m_pasteboard setString:entry.value forType:platformType];
150     }
151
152     if (data.sameOriginCustomData.size()) {
153         if (auto serializedCustomData = data.createSharedBuffer()->createNSData())
154             [m_pasteboard setData:serializedCustomData.get() forType:@(PasteboardCustomData::cocoaType())];
155     }
156
157     return changeCount();
158 }
159
160 long PlatformPasteboard::changeCount() const
161 {
162     return [m_pasteboard.get() changeCount];
163 }
164
165 String PlatformPasteboard::platformPasteboardTypeForSafeTypeForDOMToReadAndWrite(const String& domType)
166 {
167     if (domType == "text/plain")
168         return NSStringPboardType;
169
170     if (domType == "text/html")
171         return NSHTMLPboardType;
172
173     if (domType == "text/uri-list")
174         return NSURLPboardType;
175
176     return { };
177 }
178
179 String PlatformPasteboard::uniqueName()
180 {
181     return [[NSPasteboard pasteboardWithUniqueName] name];
182 }
183
184 Color PlatformPasteboard::color()
185 {
186     NSColor *color = [NSColor colorFromPasteboard:m_pasteboard.get()];
187
188     // The color may not be in an RGB colorspace. This commonly occurs when a color is
189     // dragged from the NSColorPanel grayscale picker.
190     if ([[color colorSpace] colorSpaceModel] != NSRGBColorSpaceModel)
191         color = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
192
193     return makeRGBA((int)([color redComponent] * 255.0 + 0.5), (int)([color greenComponent] * 255.0 + 0.5),
194         (int)([color blueComponent] * 255.0 + 0.5), (int)([color alphaComponent] * 255.0 + 0.5));
195 }
196
197 URL PlatformPasteboard::url()
198 {
199     return [NSURL URLFromPasteboard:m_pasteboard.get()];
200 }
201
202 long PlatformPasteboard::copy(const String& fromPasteboard)
203 {
204     NSPasteboard* pasteboard = [NSPasteboard pasteboardWithName:fromPasteboard];
205     NSArray* types = [pasteboard types];
206
207     [m_pasteboard.get() addTypes:types owner:nil];
208     for (NSUInteger i = 0; i < [types count]; i++) {
209         NSString* type = [types objectAtIndex:i];
210         if (![m_pasteboard.get() setData:[pasteboard dataForType:type] forType:type])
211             return 0;
212     }
213     return changeCount();
214 }
215
216 long PlatformPasteboard::addTypes(const Vector<String>& pasteboardTypes)
217 {
218     RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] init]);
219     for (size_t i = 0; i < pasteboardTypes.size(); ++i)
220         [types.get() addObject:pasteboardTypes[i]];
221
222     return [m_pasteboard.get() addTypes:types.get() owner:nil];
223 }
224
225 long PlatformPasteboard::setTypes(const Vector<String>& pasteboardTypes)
226 {
227     if (pasteboardTypes.isEmpty())
228         return [m_pasteboard declareTypes:@[] owner:nil];
229
230     RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] init]);
231     for (size_t i = 0; i < pasteboardTypes.size(); ++i)
232         [types.get() addObject:pasteboardTypes[i]];
233
234     return [m_pasteboard.get() declareTypes:types.get() owner:nil];
235 }
236
237 long PlatformPasteboard::setBufferForType(SharedBuffer* buffer, const String& pasteboardType)
238 {
239     BOOL didWriteData = [m_pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:pasteboardType];
240     if (!didWriteData)
241         return 0;
242     return changeCount();
243 }
244
245 long PlatformPasteboard::setPathnamesForType(const Vector<String>& pathnames, const String& pasteboardType)
246 {
247     RetainPtr<NSMutableArray> paths = adoptNS([[NSMutableArray alloc] init]);
248     for (size_t i = 0; i < pathnames.size(); ++i)
249         [paths.get() addObject:[NSArray arrayWithObject:pathnames[i]]];
250     BOOL didWriteData = [m_pasteboard.get() setPropertyList:paths.get() forType:pasteboardType];
251     if (!didWriteData)
252         return 0;
253     return changeCount();
254 }
255
256 long PlatformPasteboard::setStringForType(const String& string, const String& pasteboardType)
257 {
258     BOOL didWriteData;
259
260     if (pasteboardType == String(NSURLPboardType)) {
261         // We cannot just use -NSPasteboard writeObjects:], because -declareTypes has been already called, implicitly creating an item.
262         NSURL *url = [NSURL URLWithString:string];
263         if ([[m_pasteboard.get() types] containsObject:NSURLPboardType]) {
264             NSURL *base = [url baseURL];
265             if (base)
266                 didWriteData = [m_pasteboard.get() setPropertyList:@[[url relativeString], [base absoluteString]] forType:NSURLPboardType];
267             else if (url)
268                 didWriteData = [m_pasteboard.get() setPropertyList:@[[url absoluteString], @""] forType:NSURLPboardType];
269             else
270                 didWriteData = [m_pasteboard.get() setPropertyList:@[@"", @""] forType:NSURLPboardType];
271
272             if (!didWriteData)
273                 return 0;
274         }
275
276         if ([[m_pasteboard.get() types] containsObject:(NSString *)kUTTypeURL]) {
277             didWriteData = [m_pasteboard.get() setString:[url absoluteString] forType:(NSString *)kUTTypeURL];
278             if (!didWriteData)
279                 return 0;
280         }
281
282         if ([[m_pasteboard.get() types] containsObject:(NSString *)kUTTypeFileURL] && [url isFileURL]) {
283             didWriteData = [m_pasteboard.get() setString:[url absoluteString] forType:(NSString *)kUTTypeFileURL];
284             if (!didWriteData)
285                 return 0;
286         }
287
288     } else {
289         didWriteData = [m_pasteboard.get() setString:string forType:pasteboardType];
290         if (!didWriteData)
291             return 0;
292     }
293
294     return changeCount();
295 }
296
297 }