/* * Copyright (C) 2005, 2006, 2007 Apple, Inc. All rights reserved. * (C) 2007 Graham Dennis (graham.dennis@gmail.com) * (C) 2007 Eric Seidel * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "DumpRenderTreePasteboard.h" #import "DumpRenderTreeMac.h" #if !PLATFORM(IOS) #import #import #import #import #import @interface LocalPasteboard : NSPasteboard { RetainPtr _owner; RetainPtr _pasteboardName; NSInteger _changeCount; ListHashSet, WTF::RetainPtrObjectHash> _types; HashMap, RetainPtr, WTF::RetainPtrObjectHash, WTF::RetainPtrObjectHashTraits> _data; } -(id)initWithName:(NSString *)name; @end static NSMutableDictionary *localPasteboards; @implementation DumpRenderTreePasteboard // Return a local pasteboard so we don't disturb the real pasteboards when running tests. + (NSPasteboard *)_pasteboardWithName:(NSString *)name { static int number = 0; if (!name) name = [NSString stringWithFormat:@"LocalPasteboard%d", ++number]; if (!localPasteboards) localPasteboards = [[NSMutableDictionary alloc] init]; LocalPasteboard *pasteboard = [localPasteboards objectForKey:name]; if (pasteboard) return pasteboard; pasteboard = [[LocalPasteboard alloc] initWithName:name]; [localPasteboards setObject:pasteboard forKey:name]; [pasteboard release]; return pasteboard; } + (void)releaseLocalPasteboards { [localPasteboards release]; localPasteboards = nil; } // Convenience method for JS so that it doesn't have to try and create a NSArray on the objc side instead // of the usual WebScriptObject that is passed around - (NSInteger)declareType:(NSString *)type owner:(id)newOwner { return [self declareTypes:[NSArray arrayWithObject:type] owner:newOwner]; } @end @implementation LocalPasteboard + (id)alloc { return NSAllocateObject(self, 0, 0); } - (id)initWithName:(NSString *)name { self = [super init]; if (!self) return nil; _pasteboardName = adoptNS([name copy]); return self; } - (NSString *)name { return _pasteboardName.get(); } - (void)releaseGlobally { } - (NSInteger)declareTypes:(NSArray *)newTypes owner:(id)newOwner { _types.clear(); _data.clear(); return [self addTypes:newTypes owner:newOwner]; } static bool isUTI(NSString *type) { return UTTypeIsDynamic((__bridge CFStringRef)type) || UTTypeIsDeclared((__bridge CFStringRef)type); } static RetainPtr toUTI(NSString *type) { if (isUTI(type)) { // This is already an UTI. return adoptNS([type copy]); } return adoptNS((__bridge NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassNSPboardType, (__bridge CFStringRef)type, nullptr)); } - (NSInteger)addTypes:(NSArray *)newTypes owner:(id)newOwner { if (_owner != newOwner) { _owner = newOwner; ++_changeCount; } for (NSString *type in newTypes) _types.add(toUTI(type)); return _changeCount; } - (NSInteger)changeCount { return _changeCount; } - (NSArray *)types { auto types = adoptNS([[NSMutableArray alloc] init]); for (const auto& type : _types) { [types addObject:type.get()]; // Include the pasteboard type as well. if (auto pasteboardType = adoptNS((__bridge NSString *)UTTypeCopyPreferredTagWithClass((CFStringRef)type.get(), kUTTagClassNSPboardType))) [types addObject:pasteboardType.get()]; } return types.autorelease(); } - (NSString *)availableTypeFromArray:(NSArray *)types { for (NSString *type in types) { if (_types.contains(type)) return type; } return nil; } - (BOOL)setData:(NSData *)data forType:(NSString *)dataType { auto uti = toUTI(dataType); if (!_types.contains(uti)) return NO; _data.set(WTFMove(uti), data ? data : [NSData data]); return YES; } - (NSData *)dataForType:(NSString *)dataType { if (NSData *data = _data.get(toUTI(dataType)).get()) return data; if (_owner && [_owner respondsToSelector:@selector(pasteboard:provideDataForType:)]) [_owner pasteboard:self provideDataForType:dataType]; return _data.get(toUTI(dataType)).get(); } - (BOOL)setPropertyList:(id)propertyList forType:(NSString *)dataType { CFDataRef data = NULL; if (propertyList) data = CFPropertyListCreateXMLData(NULL, propertyList); BOOL result = [self setData:(NSData *)data forType:dataType]; if (data) CFRelease(data); return result; } - (BOOL)setString:(NSString *)string forType:(NSString *)dataType { CFDataRef data = NULL; if (string) { if (!string.length) data = CFDataCreate(NULL, NULL, 0); else data = CFStringCreateExternalRepresentation(NULL, (CFStringRef)string, kCFStringEncodingUTF8, 0); } BOOL result = [self setData:(NSData *)data forType:dataType]; if (data) CFRelease(data); return result; } - (BOOL)writeObjects:(NSArray> *)objects { for (id object in objects) { for (NSString *type in [object writableTypesForPasteboard:self]) { ASSERT(UTTypeIsDeclared((__bridge CFStringRef)type) || UTTypeIsDynamic((__bridge CFStringRef)type)); [self addTypes:@[ type ] owner:self]; id propertyList = [object pasteboardPropertyListForType:type]; if ([propertyList isKindOfClass:NSData.class]) [self setData:propertyList forType:type]; else ASSERT_NOT_REACHED(); } } return YES; } @end #endif // !PLATFORM(IOS)