Add support for recognizing data interaction gestures in WebKit2
[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
76 @end
77
78 @implementation WebItemProviderPasteboard
79
80 + (instancetype)sharedInstance
81 {
82     static WebItemProviderPasteboard *sharedPasteboard = nil;
83     static dispatch_once_t onceToken;
84     dispatch_once(&onceToken, ^() {
85         sharedPasteboard = [[WebItemProviderPasteboard alloc] init];
86     });
87     return sharedPasteboard;
88 }
89
90 - (instancetype)init
91 {
92     if (self = [super init]) {
93         _itemProviders = [[NSArray alloc] init];
94         _changeCount = 0;
95     }
96     return self;
97 }
98
99 - (void)dealloc
100 {
101     [super dealloc];
102     [_itemProviders release];
103 }
104
105 - (NSArray<NSString *> *)pasteboardTypes
106 {
107     NSMutableSet<NSString *> *allTypes = [NSMutableSet set];
108     for (UIItemProvider *provider in _itemProviders)
109         [allTypes addObjectsFromArray:provider.registeredTypeIdentifiers];
110     return allTypes.allObjects;
111 }
112
113 - (void)setItemProviders:(NSArray<UIItemProvider *> *)itemProviders
114 {
115     itemProviders = itemProviders ?: [NSArray array];
116     if (_itemProviders == itemProviders || [_itemProviders isEqualToArray:itemProviders])
117         return;
118     _itemProviders = [itemProviders copy];
119     _changeCount++;
120 }
121
122 - (NSInteger)numberOfItems
123 {
124     return _itemProviders.count;
125 }
126
127 - (void)setItems:(NSArray *)items
128 {
129     NSMutableArray *providers = [[NSMutableArray alloc] init];
130     for (NSDictionary *item in items) {
131         if (!item.count)
132             continue;
133         UIItemProvider *itemProvider = [[getUIItemProviderClass() alloc] init];
134         for (NSString *typeIdentifier in item) {
135             [itemProvider registerDataRepresentationForTypeIdentifier:typeIdentifier options:nil loadHandler:^NSProgress *(UIItemProviderDataLoadCompletionBlock completionBlock)
136             {
137                 completionBlock(item[typeIdentifier], nil);
138                 return [NSProgress discreteProgressWithTotalUnitCount:100];
139             }];
140         }
141         [providers addObject:itemProvider];
142     }
143     _changeCount++;
144     _itemProviders = providers;
145 }
146
147 - (NSArray *)dataForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
148 {
149     __block NSMutableArray *values = [NSMutableArray array];
150     [itemSet enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *) {
151         UIItemProvider *provider = [self itemProviderAtIndex:index];
152         if (!provider)
153             return;
154
155         NSData *data = [provider copyDataRepresentationForTypeIdentifier:pasteboardType error:nil];
156         if (data)
157             [values addObject:data];
158     }];
159
160     return values;
161 }
162
163 - (NSArray *)valuesForPasteboardType:(NSString *)pasteboardType inItemSet:(NSIndexSet *)itemSet
164 {
165     __block NSMutableArray *values = [NSMutableArray array];
166     [itemSet enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *) {
167         UIItemProvider *provider = [self itemProviderAtIndex:index];
168         if (!provider)
169             return;
170
171         // FIXME: These should be refactored to use asynchronous calls.
172         if (isColorType(pasteboardType) && [provider canCreateObjectOfClass:[getUIColorClass() class]]) {
173             [values addObject:[provider createObjectOfClass:[getUIColorClass() class] error:nil]];
174             return;
175         }
176
177         if (isImageType(pasteboardType) && [provider canCreateObjectOfClass:[getUIImageClass() class]]) {
178             [values addObject:[provider createObjectOfClass:[getUIImageClass() class] error:nil]];
179             return;
180         }
181
182         if (isURLType(pasteboardType) && [provider canCreateObjectOfClass:[NSURL class]]) {
183             [values addObject:[provider createObjectOfClass:[NSURL class] error:nil]];
184             return;
185         }
186
187         if (isRichTextType(pasteboardType) && [provider canCreateObjectOfClass:[NSAttributedString class]]) {
188             [values addObject:[provider createObjectOfClass:[NSAttributedString class] error:nil]];
189             return;
190         }
191
192         if (isStringType(pasteboardType) && [provider canCreateObjectOfClass:[NSString class]]) {
193             [values addObject:[provider createObjectOfClass:[NSString class] error:nil]];
194             return;
195         }
196
197         WTFLogAlways("Failed to instantiate object for type: '%s' at index: %tu", pasteboardType.UTF8String, index);
198     }];
199     return values;
200 }
201
202 - (NSInteger)changeCount
203 {
204     return _changeCount;
205 }
206
207 - (UIItemProvider *)itemProviderAtIndex:(NSInteger)index
208 {
209     return 0 <= index && index < (NSInteger)_itemProviders.count ? _itemProviders[index] : nil;
210 }
211
212 @end
213
214 #endif // ENABLE(DATA_INTERACTION)