e79bd87fb01739ebdd0ef450e1c17cdfc093189b
[WebKit-https.git] / Tools / DumpRenderTree / mac / DumpRenderTreePasteboard.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007 Apple, Inc.  All rights reserved.
3  *           (C) 2007 Graham Dennis (graham.dennis@gmail.com)
4  *           (C) 2007 Eric Seidel <eric@webkit.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer. 
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution. 
15  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission. 
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #import "config.h"
32 #import "DumpRenderTreePasteboard.h"
33
34 #import "DumpRenderTreeMac.h"
35
36 #if !PLATFORM(IOS)
37
38 #import <WebKit/WebTypesInternal.h>
39 #import <wtf/Assertions.h>
40 #import <wtf/HashMap.h>
41 #import <wtf/ListHashSet.h>
42 #import <wtf/RetainPtr.h>
43
44 @interface LocalPasteboard : NSPasteboard {
45     RetainPtr<id> _owner;
46     RetainPtr<NSString> _pasteboardName;
47     NSInteger _changeCount;
48
49     ListHashSet<RetainPtr<NSString>, WTF::RetainPtrObjectHash<NSString>> _types;
50     HashMap<RetainPtr<NSString>, RetainPtr<NSData>, WTF::RetainPtrObjectHash<NSString>, WTF::RetainPtrObjectHashTraits<NSString>> _data;
51 }
52
53 -(id)initWithName:(NSString *)name;
54 @end
55
56 static NSMutableDictionary *localPasteboards;
57
58 @implementation DumpRenderTreePasteboard
59
60 // Return a local pasteboard so we don't disturb the real pasteboards when running tests.
61 + (NSPasteboard *)_pasteboardWithName:(NSString *)name
62 {
63     static int number = 0;
64     if (!name)
65         name = [NSString stringWithFormat:@"LocalPasteboard%d", ++number];
66     if (!localPasteboards)
67         localPasteboards = [[NSMutableDictionary alloc] init];
68     LocalPasteboard *pasteboard = [localPasteboards objectForKey:name];
69     if (pasteboard)
70         return pasteboard;
71     pasteboard = [[LocalPasteboard alloc] initWithName:name];
72     [localPasteboards setObject:pasteboard forKey:name];
73     [pasteboard release];
74     return pasteboard;
75 }
76
77 + (void)releaseLocalPasteboards
78 {
79     [localPasteboards release];
80     localPasteboards = nil;
81 }
82
83 // Convenience method for JS so that it doesn't have to try and create a NSArray on the objc side instead
84 // of the usual WebScriptObject that is passed around
85 - (NSInteger)declareType:(NSString *)type owner:(id)newOwner
86 {
87     return [self declareTypes:[NSArray arrayWithObject:type] owner:newOwner];
88 }
89
90 @end
91
92 @implementation LocalPasteboard
93
94 + (id)alloc
95 {
96     return NSAllocateObject(self, 0, 0);
97 }
98
99 - (id)initWithName:(NSString *)name
100 {
101     self = [super init];
102     if (!self)
103         return nil;
104
105     _pasteboardName = adoptNS([name copy]);
106
107     return self;
108 }
109
110 - (NSString *)name
111 {
112     return _pasteboardName.get();
113 }
114
115 - (void)releaseGlobally
116 {
117 }
118
119 - (NSInteger)declareTypes:(NSArray *)newTypes owner:(id)newOwner
120 {
121     _types.clear();
122     _data.clear();
123
124     return [self addTypes:newTypes owner:newOwner];
125 }
126
127 static bool isUTI(NSString *type)
128 {
129     return UTTypeIsDynamic((__bridge CFStringRef)type) || UTTypeIsDeclared((__bridge CFStringRef)type);
130 }
131
132 static RetainPtr<NSString> toUTI(NSString *type)
133 {
134     if (isUTI(type)) {
135         // This is already an UTI.
136         return adoptNS([type copy]);
137     }
138
139     return adoptNS((__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, (__bridge CFStringRef)type, nullptr));
140 }
141
142 - (NSInteger)addTypes:(NSArray *)newTypes owner:(id)newOwner
143 {
144     _owner = newOwner;
145     for (NSString *type in newTypes)
146         _types.add(toUTI(type));
147
148     return ++_changeCount;
149 }
150
151 - (NSInteger)changeCount
152 {
153     return _changeCount;
154 }
155
156 - (NSArray *)types
157 {
158     auto types = adoptNS([[NSMutableArray alloc] init]);
159
160     for (const auto& type : _types) {
161         [types addObject:type.get()];
162
163         // Include the pasteboard type as well.
164         if (auto pasteboardType = adoptNS((__bridge NSString *)UTTypeCopyPreferredTagWithClass((CFStringRef)type.get(), kUTTagClassNSPboardType)))
165             [types addObject:pasteboardType.get()];
166     }
167
168     return types.autorelease();
169 }
170
171 - (NSString *)availableTypeFromArray:(NSArray *)types
172 {
173     for (NSString *type in types) {
174         if (_types.contains(type))
175             return type;
176     }
177
178     return nil;
179 }
180
181 - (BOOL)setData:(NSData *)data forType:(NSString *)dataType
182 {
183     auto uti = toUTI(dataType);
184
185     if (!_types.contains(uti))
186         return NO;
187
188     _data.set(WTFMove(uti), data ? data : [NSData data]);
189     ++_changeCount;
190     return YES;
191 }
192
193 - (NSData *)dataForType:(NSString *)dataType
194 {
195     if (NSData *data = _data.get(toUTI(dataType)).get())
196         return data;
197
198     if (_owner && [_owner respondsToSelector:@selector(pasteboard:provideDataForType:)])
199         [_owner pasteboard:self provideDataForType:dataType];
200
201     return _data.get(toUTI(dataType)).get();
202 }
203
204 - (BOOL)setPropertyList:(id)propertyList forType:(NSString *)dataType
205 {
206     CFDataRef data = NULL;
207     if (propertyList)
208         data = CFPropertyListCreateXMLData(NULL, propertyList);
209     BOOL result = [self setData:(NSData *)data forType:dataType];
210     if (data)
211         CFRelease(data);
212     return result;
213 }
214
215 - (BOOL)setString:(NSString *)string forType:(NSString *)dataType
216 {
217     CFDataRef data = NULL;
218     if (string) {
219         if (!string.length)
220             data = CFDataCreate(NULL, NULL, 0);
221         else
222             data = CFStringCreateExternalRepresentation(NULL, (CFStringRef)string, kCFStringEncodingUTF8, 0);
223     }
224     BOOL result = [self setData:(NSData *)data forType:dataType];
225     if (data)
226         CFRelease(data);
227     return result;
228 }
229
230 - (BOOL)writeObjects:(NSArray<id <NSPasteboardWriting>> *)objects
231 {
232     for (id <NSPasteboardWriting> object in objects) {
233         for (NSString *type in [object writableTypesForPasteboard:self]) {
234             ASSERT(UTTypeIsDeclared((__bridge CFStringRef)type) || UTTypeIsDynamic((__bridge CFStringRef)type));
235
236             [self addTypes:@[ type ] owner:self];
237
238             id propertyList = [object pasteboardPropertyListForType:type];
239             if ([propertyList isKindOfClass:NSData.class])
240                 [self setData:propertyList forType:type];
241             else
242                 ASSERT_NOT_REACHED();
243         }
244     }
245
246     return YES;
247 }
248
249 @end
250
251 #endif // !PLATFORM(IOS)