81601204a655e2575fe97e57914b139adfa02f16
[WebKit-https.git] / Source / WebCore / platform / ios / WebItemProviderPasteboard.mm
1 /*
2  * Copyright (C) 2017 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #import "WebItemProviderPasteboard.h"
28
29 #if ENABLE(DATA_INTERACTION)
30
31 #import "SoftLinking.h"
32 #import "UIKitSPI.h"
33 #import <Foundation/NSProgress.h>
34 #import <MobileCoreServices/MobileCoreServices.h>
35 #import <UIKit/UIColor.h>
36 #import <UIKit/UIImage.h>
37
38 SOFT_LINK_FRAMEWORK(UIKit)
39 SOFT_LINK_CLASS(UIKit, UIColor)
40 SOFT_LINK_CLASS(UIKit, UIImage)
41 SOFT_LINK_CLASS(UIKit, UIItemProvider)
42
43 #define MATCHES_UTI_TYPE(type, suffix) [type isEqualToString:(__bridge NSString *)kUTType ## suffix]
44 #define MATCHES_UIKIT_TYPE(type, suffix) [type isEqualToString:@"com.apple.uikit. ## suffix ##"]
45
46 static BOOL isRichTextType(NSString *type)
47 {
48     return MATCHES_UTI_TYPE(type, RTF) || MATCHES_UTI_TYPE(type, RTFD) || MATCHES_UTI_TYPE(type, HTML);
49 }
50
51 static BOOL isStringType(NSString *type)
52 {
53     return MATCHES_UTI_TYPE(type, Text) || MATCHES_UTI_TYPE(type, UTF8PlainText) || MATCHES_UTI_TYPE(type, UTF16PlainText);
54 }
55
56 static BOOL isURLType(NSString *type)
57 {
58     return MATCHES_UTI_TYPE(type, URL);
59 }
60
61 static BOOL isColorType(NSString *type)
62 {
63     return MATCHES_UIKIT_TYPE(type, color);
64 }
65
66 static BOOL isImageType(NSString *type)
67 {
68     return MATCHES_UTI_TYPE(type, PNG) || MATCHES_UTI_TYPE(type, JPEG) || MATCHES_UTI_TYPE(type, GIF) || MATCHES_UIKIT_TYPE(type, image);
69 }
70
71 @interface WebItemProviderPasteboard ()
72
73 @property (nonatomic) NSInteger numberOfItems;
74 @property (nonatomic) NSInteger changeCount;
75 @property (nonatomic) NSInteger pendingOperationCount;
76
77 @end
78
79 @implementation WebItemProviderPasteboard
80
81 + (instancetype)sharedInstance
82 {
83     static WebItemProviderPasteboard *sharedPasteboard = nil;
84     static dispatch_once_t onceToken;
85     dispatch_once(&onceToken, ^() {
86         sharedPasteboard = [[WebItemProviderPasteboard alloc] init];
87     });
88     return sharedPasteboard;
89 }
90
91 - (instancetype)init
92 {
93     if (self = [super init]) {
94         _itemProviders = [[NSArray alloc] init];
95         _changeCount = 0;
96         _pendingOperationCount = 0;
97     }
98     return self;
99 }
100
101 - (void)dealloc
102 {
103     [super dealloc];
104     [_itemProviders release];
105 }
106
107 - (NSArray<NSString *> *)pasteboardTypes
108 {
109     NSMutableSet<NSString *> *allTypes = [NSMutableSet set];
110     for (UIItemProvider *provider in _itemProviders)
111         [allTypes addObjectsFromArray:provider.registeredTypeIdentifiers];
112     return allTypes.allObjects;
113 }
114
115 - (void)setItemProviders:(NSArray<UIItemProvider *> *)itemProviders
116 {
117     itemProviders = itemProviders ?: [NSArray array];
118     if (_itemProviders == itemProviders || [_itemProviders isEqualToArray:itemProviders])
119         return;
120     _itemProviders = [itemProviders copy];
121     _changeCount++;
122 }
123
124 - (NSInteger)numberOfItems
125 {
126     return _itemProviders.count;
127 }
128
129 - (void)setItems:(NSArray *)items
130 {
131     NSMutableArray *providers = [[NSMutableArray alloc] init];
132     for (NSDictionary *item in items) {
133         if (!item.count)
134             continue;
135         UIItemProvider *itemProvider = [[getUIItemProviderClass() alloc] init];
136         for (NSString *typeIdentifier in item) {
137             [itemProvider registerDataRepresentationForTypeIdentifier:typeIdentifier options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
138             {
139                 completionBlock(item[typeIdentifier], nil);
140                 return [NSProgress discreteProgressWithTotalUnitCount:100];
141             }];
142         }
143         [providers addObject:itemProvider];
144     }
145     _changeCount++;
146     _itemProviders = providers;
147 }
148
149 - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
150 {
151     __block NSMutableArray *values = [NSMutableArray array];
152     [itemSet enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *) {
153         UIItemProvider *provider = [self itemProviderAtIndex:index];
154         if (!provider)
155             return;
156
157         NSData *data = [provider copyDataRepresentationForTypeIdentifier:pasteboardType error:nil];
158         if (data)
159             [values addObject:data];
160     }];
161
162     return values;
163 }
164
165 - (NSArray *)valuesForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
166 {
167     __block NSMutableArray *values = [NSMutableArray array];
168     [itemSet enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *) {
169         UIItemProvider *provider = [self itemProviderAtIndex:index];
170         if (!provider)
171             return;
172
173         // FIXME: These should be refactored to use asynchronous calls.
174         if (isColorType(pasteboardType) && [self _tryToCreateAndAppendObjectOfClass:[getUIColorClass() class] toArray:values usingProvider:provider])
175             return;
176
177         if (isImageType(pasteboardType) && [self _tryToCreateAndAppendObjectOfClass:[NSString class] toArray:values usingProvider:provider])
178             return;
179
180         if (isURLType(pasteboardType) && [self _tryToCreateAndAppendObjectOfClass:[NSURL class] toArray:values usingProvider:provider])
181             return;
182
183         if (isRichTextType(pasteboardType) && [self _tryToCreateAndAppendObjectOfClass:[NSAttributedString class] toArray:values usingProvider:provider])
184             return;
185
186         if (isStringType(pasteboardType) && [self _tryToCreateAndAppendObjectOfClass:[NSString class] toArray:values usingProvider:provider])
187             return;
188
189         WTFLogAlways("Failed to instantiate object for type: '%s' at index: %tu", pasteboardType.UTF8String, index);
190     }];
191     return values;
192 }
193
194 - (BOOL)_tryToCreateAndAppendObjectOfClass:(Class)objectClass toArray:(NSMutableArray *)array usingProvider:(UIItemProvider *)provider
195 {
196     if (![provider canCreateObjectOfClass:objectClass])
197         return NO;
198
199     id object = [provider createObjectOfClass:objectClass error:nil];
200     if (object)
201         [array addObject:object];
202
203     return !!object;
204 }
205
206 - (NSInteger)changeCount
207 {
208     return _changeCount;
209 }
210
211 - (UIItemProvider *)itemProviderAtIndex:(NSInteger)index
212 {
213     return 0 <= index && index < (NSInteger)_itemProviders.count ? _itemProviders[index] : nil;
214 }
215
216 - (BOOL)hasPendingOperation
217 {
218     return _pendingOperationCount;
219 }
220
221 - (void)incrementPendingOperationCount
222 {
223     _pendingOperationCount++;
224 }
225
226 - (void)decrementPendingOperationCount
227 {
228     _pendingOperationCount--;
229 }
230
231 @end
232
233 #endif // ENABLE(DATA_INTERACTION)