246 lines
7.8 KiB
Plaintext
246 lines
7.8 KiB
Plaintext
/*
|
|
* Copyright (C) 2005-2018 Apple, Inc. All rights reserved.
|
|
* Copyright (C) 2007 Graham Dennis (graham.dennis@gmail.com)
|
|
* Copyright (C) 2007 Eric Seidel <eric@webkit.org>
|
|
*
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "WebKitTestRunnerPasteboard.h"
|
|
|
|
#import "NSPasteboardAdditions.h"
|
|
#import <objc/runtime.h>
|
|
#import <wtf/RetainPtr.h>
|
|
|
|
@interface LocalPasteboard : NSPasteboard
|
|
{
|
|
RetainPtr<NSMutableArray> _typesArray;
|
|
RetainPtr<NSMutableSet> _typesSet;
|
|
RetainPtr<NSMutableArray<NSPasteboardItem *>> _writtenPasteboardItems;
|
|
RetainPtr<NSMutableDictionary> _dataByType;
|
|
NSInteger _changeCount;
|
|
RetainPtr<NSString> _pasteboardName;
|
|
}
|
|
|
|
-(id)initWithName:(NSString *)name;
|
|
@end
|
|
|
|
static RetainPtr<NSMutableDictionary> localPasteboards;
|
|
|
|
@implementation WebKitTestRunnerPasteboard
|
|
|
|
// 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 = adoptNS([[NSMutableDictionary alloc] init]);
|
|
if (LocalPasteboard *pasteboard = [localPasteboards objectForKey:name])
|
|
return pasteboard;
|
|
auto pasteboard = adoptNS([[LocalPasteboard alloc] initWithName:name]);
|
|
[localPasteboards setObject:pasteboard.get() forKey:name];
|
|
return pasteboard.autorelease();
|
|
}
|
|
|
|
// This method crashes when called on LocalPasteboard.
|
|
// This happens during dragging, so overriding it may become unnecessary once we use mock dragging, like DumpRenderTree does.
|
|
- (void)_updateTypeCacheIfNeeded
|
|
{
|
|
}
|
|
|
|
+ (void)releaseLocalPasteboards
|
|
{
|
|
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:@[type] owner:newOwner];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation LocalPasteboard
|
|
|
|
+ (id)alloc
|
|
{
|
|
// Need to skip over [NSPasteboard alloc], which won't allocate a new object.
|
|
return class_createInstance(self, 0);
|
|
}
|
|
|
|
- (id)initWithName:(NSString *)name
|
|
{
|
|
self = [super init];
|
|
if (!self)
|
|
return nil;
|
|
_typesArray = adoptNS([[NSMutableArray alloc] init]);
|
|
_typesSet = adoptNS([[NSMutableSet alloc] init]);
|
|
_dataByType = adoptNS([[NSMutableDictionary alloc] init]);
|
|
_pasteboardName = adoptNS([name copy]);
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)name
|
|
{
|
|
return _pasteboardName.get();
|
|
}
|
|
|
|
- (void)releaseGlobally
|
|
{
|
|
}
|
|
|
|
- (void)_clearContentsWithoutUpdatingChangeCount
|
|
{
|
|
_writtenPasteboardItems = nil;
|
|
[_typesArray removeAllObjects];
|
|
[_typesSet removeAllObjects];
|
|
[_dataByType removeAllObjects];
|
|
}
|
|
|
|
- (NSInteger)clearContents
|
|
{
|
|
[self _clearContentsWithoutUpdatingChangeCount];
|
|
return ++_changeCount;
|
|
}
|
|
|
|
- (NSInteger)declareTypes:(NSArray *)newTypes owner:(id)newOwner
|
|
{
|
|
[self _clearContentsWithoutUpdatingChangeCount];
|
|
[self _addTypesWithoutUpdatingChangeCount:newTypes owner:newOwner];
|
|
return ++_changeCount;
|
|
}
|
|
|
|
- (NSInteger)addTypes:(NSArray<NSPasteboardType> *)newTypes owner:(id)newOwner
|
|
{
|
|
[self _addTypesWithoutUpdatingChangeCount:newTypes owner:newOwner];
|
|
// FIXME: Ideally, we would keep track of the current owner and only bump the change
|
|
// count if the new owner is different.
|
|
return ++_changeCount;
|
|
}
|
|
|
|
- (void)_addTypesWithoutUpdatingChangeCount:(NSArray *)newTypes owner:(id)newOwner
|
|
{
|
|
unsigned count = [newTypes count];
|
|
unsigned i;
|
|
for (i = 0; i < count; ++i) {
|
|
NSString *type = [newTypes objectAtIndex:i];
|
|
RetainPtr<NSString> setType = [_typesSet member:type];
|
|
if (!setType) {
|
|
setType = adoptNS([type copy]);
|
|
[_typesArray addObject:setType.get()];
|
|
[_typesSet addObject:setType.get()];
|
|
}
|
|
if (newOwner && [newOwner respondsToSelector:@selector(pasteboard:provideDataForType:)])
|
|
[newOwner pasteboard:self provideDataForType:setType.get()];
|
|
}
|
|
}
|
|
|
|
- (NSInteger)changeCount
|
|
{
|
|
return _changeCount;
|
|
}
|
|
|
|
- (NSArray *)types
|
|
{
|
|
return _typesArray.get();
|
|
}
|
|
|
|
- (NSString *)availableTypeFromArray:(NSArray *)types
|
|
{
|
|
for (NSString *type in types) {
|
|
if (NSString *setType = [_typesSet member:type])
|
|
return setType;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL)setData:(NSData *)data forType:(NSString *)dataType
|
|
{
|
|
if (![_typesSet containsObject:dataType])
|
|
return NO;
|
|
if (!data)
|
|
data = [NSData data];
|
|
[_dataByType setObject:data forKey:dataType];
|
|
++_changeCount;
|
|
return YES;
|
|
}
|
|
|
|
- (NSData *)dataForType:(NSString *)dataType
|
|
{
|
|
return [_dataByType objectForKey:dataType];
|
|
}
|
|
|
|
- (BOOL)setPropertyList:(id)propertyList forType:(NSString *)dataType
|
|
{
|
|
NSData *data = nil;
|
|
if (propertyList)
|
|
data = [NSPropertyListSerialization dataWithPropertyList:propertyList format:NSPropertyListXMLFormat_v1_0 options:0 error:nullptr];
|
|
return [self setData:data forType:dataType];
|
|
}
|
|
|
|
- (BOOL)setString:(NSString *)string forType:(NSString *)dataType
|
|
{
|
|
return [self setData:[string dataUsingEncoding:NSUTF8StringEncoding] forType:dataType];
|
|
}
|
|
|
|
- (NSArray<NSPasteboardItem *> *)pasteboardItems
|
|
{
|
|
if (_writtenPasteboardItems)
|
|
return _writtenPasteboardItems.get();
|
|
|
|
auto item = adoptNS([[NSPasteboardItem alloc] init]);
|
|
for (NSString *type in _typesArray.get()) {
|
|
NSPasteboardType modernPasteboardType = [NSPasteboard _modernPasteboardType:type];
|
|
if (NSData *dataForType = [_dataByType objectForKey:type] ?: [_dataByType objectForKey:modernPasteboardType])
|
|
[item setData:dataForType forType:modernPasteboardType];
|
|
}
|
|
return @[ item.get() ];
|
|
}
|
|
|
|
- (BOOL)writeObjects:(NSArray<id <NSPasteboardWriting>> *)objects
|
|
{
|
|
_writtenPasteboardItems = adoptNS([[NSMutableArray<NSPasteboardItem *> alloc] initWithCapacity:objects.count]);
|
|
for (id <NSPasteboardWriting> object in objects) {
|
|
ASSERT([object isKindOfClass:NSPasteboardItem.class]);
|
|
[_writtenPasteboardItems addObject:(NSPasteboardItem *)object];
|
|
NSArray<NSPasteboardType> *writableTypes = [object writableTypesForPasteboard:self];
|
|
for (NSString *type in writableTypes) {
|
|
[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
|