[Cocoa] More tweaks and refactoring to prepare for ARC
[WebKit.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 #if PLATFORM(MAC)
35
36 #import "DumpRenderTreeMac.h"
37 #import <WebKit/WebTypesInternal.h>
38 #import <objc/runtime.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<CFStringRef>, WTF::RetainPtrObjectHash<CFStringRef>> _types;
50     HashMap<RetainPtr<CFStringRef>, RetainPtr<CFDataRef>, WTF::RetainPtrObjectHash<CFStringRef>, WTF::RetainPtrObjectHashTraits<CFStringRef>> _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     // Need to skip NSPasteboard's alloc, which does not allocate an object.
97     return class_createInstance(self, 0);
98 }
99
100 - (id)initWithName:(NSString *)name
101 {
102     self = [super init];
103     if (!self)
104         return nil;
105
106     _pasteboardName = adoptNS([name copy]);
107
108     return self;
109 }
110
111 - (NSString *)name
112 {
113     return _pasteboardName.get();
114 }
115
116 - (void)releaseGlobally
117 {
118 }
119
120 - (NSInteger)declareTypes:(NSArray *)newTypes owner:(id)newOwner
121 {
122     _types.clear();
123     _data.clear();
124
125     return [self addTypes:newTypes owner:newOwner];
126 }
127
128 static bool isUTI(NSString *type)
129 {
130     return UTTypeIsDynamic((__bridge CFStringRef)type) || UTTypeIsDeclared((__bridge CFStringRef)type);
131 }
132
133 static RetainPtr<CFStringRef> toUTI(NSString *type)
134 {
135     if (isUTI(type)) {
136         // This is already a UTI.
137         return adoptCF(CFStringCreateCopy(nullptr, (__bridge CFStringRef)type));
138     }
139
140     return adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, (__bridge CFStringRef)type, nullptr));
141 }
142
143 - (NSInteger)addTypes:(NSArray *)newTypes owner:(id)newOwner
144 {
145     if (_owner != newOwner) {
146         _owner = newOwner;
147         ++_changeCount;
148     }
149
150     for (NSString *type in newTypes)
151         _types.add(toUTI(type));
152
153     return _changeCount;
154 }
155
156 - (NSInteger)changeCount
157 {
158     return _changeCount;
159 }
160
161 - (NSArray *)types
162 {
163     auto types = adoptNS([[NSMutableArray alloc] init]);
164
165     for (const auto& type : _types) {
166         [types addObject:(__bridge NSString *)type.get()];
167
168         // Include the pasteboard type as well.
169         if (auto pasteboardType = adoptNS((__bridge NSString *)UTTypeCopyPreferredTagWithClass(type.get(), kUTTagClassNSPboardType)))
170             [types addObject:pasteboardType.get()];
171     }
172
173     return types.autorelease();
174 }
175
176 - (NSString *)availableTypeFromArray:(NSArray *)types
177 {
178     for (NSString *type in types) {
179         if (_types.contains((__bridge CFStringRef)type))
180             return type;
181     }
182
183     return nil;
184 }
185
186 - (BOOL)setData:(NSData *)data forType:(NSString *)dataType
187 {
188     auto uti = toUTI(dataType);
189
190     if (!_types.contains(uti))
191         return NO;
192
193     _data.set(WTFMove(uti), (__bridge CFDataRef)(data ?: [NSData data]));
194     return YES;
195 }
196
197 - (NSData *)dataForType:(NSString *)dataType
198 {
199     if (NSData *data = (__bridge NSData *)_data.get(toUTI(dataType).get()).get())
200         return data;
201
202     if (_owner && [_owner respondsToSelector:@selector(pasteboard:provideDataForType:)])
203         [_owner pasteboard:self provideDataForType:dataType];
204
205     return (__bridge NSData *)_data.get(toUTI(dataType).get()).get();
206 }
207
208 - (BOOL)setPropertyList:(id)propertyList forType:(NSString *)dataType
209 {
210     NSData *data = nil;
211     if (propertyList)
212         data = [NSPropertyListSerialization dataWithPropertyList:propertyList format:NSPropertyListXMLFormat_v1_0 options:0 error:nullptr];
213     return [self setData:data forType:dataType];
214 }
215
216 - (BOOL)setString:(NSString *)string forType:(NSString *)dataType
217 {
218     return [self setData:[string dataUsingEncoding:NSUTF8StringEncoding] forType:dataType];
219 }
220
221 - (BOOL)writeObjects:(NSArray<id <NSPasteboardWriting>> *)objects
222 {
223     for (id <NSPasteboardWriting> object in objects) {
224         for (NSString *type in [object writableTypesForPasteboard:self]) {
225             ASSERT(UTTypeIsDeclared((__bridge CFStringRef)type) || UTTypeIsDynamic((__bridge CFStringRef)type));
226
227             [self addTypes:@[ type ] owner:self];
228
229             id propertyList = [object pasteboardPropertyListForType:type];
230             if ([propertyList isKindOfClass:NSData.class])
231                 [self setData:propertyList forType:type];
232             else
233                 ASSERT_NOT_REACHED();
234         }
235     }
236
237     return YES;
238 }
239
240 @end
241
242 #endif // PLATFORM(MAC)