[iOS] Context Menu - Add to Reading List does nothing
[WebKit-https.git] / Source / WebKit / UIProcess / API / Cocoa / _WKElementAction.mm
1 /*
2  * Copyright (C) 2014 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 #import "config.h"
27 #import "_WKElementActionInternal.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "GestureTypes.h"
32 #import "Logging.h"
33 #import "WKActionSheetAssistant.h"
34 #import "WKContentViewInteraction.h"
35 #import "_WKActivatedElementInfoInternal.h"
36 #import <WebCore/LocalizedStrings.h>
37 #import <wtf/RetainPtr.h>
38 #import <wtf/SoftLinking.h>
39 #import <wtf/WeakObjCPtr.h>
40 #import <wtf/text/WTFString.h>
41
42 #if HAVE(SAFARI_SERVICES_FRAMEWORK)
43 #import <SafariServices/SSReadingList.h>
44 SOFT_LINK_FRAMEWORK(SafariServices);
45 SOFT_LINK_CLASS(SafariServices, SSReadingList);
46 #endif
47
48 typedef void (^WKElementActionHandlerInternal)(WKActionSheetAssistant *, _WKActivatedElementInfo *);
49
50 static UIActionIdentifier const WKElementActionTypeCustomIdentifier = @"WKElementActionTypeCustom";
51 static UIActionIdentifier const WKElementActionTypeOpenIdentifier = @"WKElementActionTypeOpen";
52 static UIActionIdentifier const WKElementActionTypeCopyIdentifier = @"WKElementActionTypeCopy";
53 static UIActionIdentifier const WKElementActionTypeSaveImageIdentifier = @"WKElementActionTypeSaveImage";
54 #if !defined(TARGET_OS_IOS) || TARGET_OS_IOS
55 static UIActionIdentifier const WKElementActionTypeAddToReadingListIdentifier = @"WKElementActionTypeAddToReadingList";
56 static UIActionIdentifier const WKElementActionTypeOpenInDefaultBrowserIdentifier = @"WKElementActionTypeOpenInDefaultBrowser";
57 static UIActionIdentifier const WKElementActionTypeOpenInExternalApplicationIdentifier = @"WKElementActionTypeOpenInExternalApplication";
58 #endif
59 static UIActionIdentifier const WKElementActionTypeShareIdentifier = @"WKElementActionTypeShare";
60 static UIActionIdentifier const WKElementActionTypeOpenInNewTabIdentifier = @"WKElementActionTypeOpenInNewTab";
61 static UIActionIdentifier const WKElementActionTypeOpenInNewWindowIdentifier = @"WKElementActionTypeOpenInNewWindow";
62 static UIActionIdentifier const WKElementActionTypeDownloadIdentifier = @"WKElementActionTypeDownload";
63
64 @implementation _WKElementAction  {
65     RetainPtr<NSString> _title;
66     WKElementActionHandlerInternal _actionHandler;
67     WKElementActionDismissalHandler _dismissalHandler;
68     WeakObjCPtr<WKActionSheetAssistant> _defaultActionSheetAssistant;
69 }
70
71 - (id)_initWithTitle:(NSString *)title actionHandler:(WKElementActionHandlerInternal)handler type:(_WKElementActionType)type assistant:(WKActionSheetAssistant *)assistant
72 {
73     if (!(self = [super init]))
74         return nil;
75
76     _title = adoptNS([title copy]);
77     _type = type;
78     _actionHandler = [handler copy];
79     _defaultActionSheetAssistant = assistant;
80     return self;
81 }
82
83 - (void)dealloc
84 {
85     [_actionHandler release];
86     [_dismissalHandler release];
87
88     [super dealloc];
89 }
90
91 + (instancetype)elementActionWithTitle:(NSString *)title actionHandler:(WKElementActionHandler)handler
92 {
93     return [[[self alloc] _initWithTitle:title actionHandler:^(WKActionSheetAssistant *, _WKActivatedElementInfo *actionInfo) { handler(actionInfo); }
94         type:_WKElementActionTypeCustom assistant:nil] autorelease];
95 }
96
97 #if HAVE(SAFARI_SERVICES_FRAMEWORK)
98 static void addToReadingList(NSURL *targetURL, NSString *title)
99 {
100     if (!title || [title length] == 0)
101         title = [targetURL absoluteString];
102
103     [[getSSReadingListClass() defaultReadingList] addReadingListItemWithURL:targetURL title:title previewText:nil error:nil];
104 }
105 #endif
106
107 + (instancetype)elementActionWithType:(_WKElementActionType)type title:(NSString *)title actionHandler:(WKElementActionHandler)actionHandler
108 {
109     return [_WKElementAction _elementActionWithType:type title:title actionHandler:actionHandler];
110 }
111
112 + (instancetype)_elementActionWithType:(_WKElementActionType)type title:(NSString *)title actionHandler:(WKElementActionHandler)actionHandler
113 {
114     WKElementActionHandlerInternal handler = ^(WKActionSheetAssistant *, _WKActivatedElementInfo *actionInfo) { actionHandler(actionInfo); };
115     return [[[self alloc] _initWithTitle:title actionHandler:handler type:type assistant:nil] autorelease];
116 }
117
118 + (instancetype)_elementActionWithType:(_WKElementActionType)type customTitle:(NSString *)customTitle assistant:(WKActionSheetAssistant *)assistant
119 {
120     NSString *title;
121     WKElementActionHandlerInternal handler;
122     switch (type) {
123     case _WKElementActionTypeCopy:
124         title = WEB_UI_STRING_KEY("Copy", "Copy (ActionSheet)", "Title for Copy Link or Image action button");
125         handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) {
126             [assistant.delegate actionSheetAssistant:assistant performAction:WebKit::SheetAction::Copy];
127         };
128         break;
129     case _WKElementActionTypeOpen:
130         title = WEB_UI_STRING("Open", "Title for Open Link action button");
131         handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) {
132             [assistant.delegate actionSheetAssistant:assistant openElementAtLocation:actionInfo._interactionLocation];
133         };
134         break;
135     case _WKElementActionTypeSaveImage:
136         title = WEB_UI_STRING("Add to Photos", "Title for Add to Photos action button");
137         handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) {
138             [assistant.delegate actionSheetAssistant:assistant performAction:WebKit::SheetAction::SaveImage];
139         };
140         break;
141 #if HAVE(SAFARI_SERVICES_FRAMEWORK)
142     case _WKElementActionTypeAddToReadingList:
143         title = WEB_UI_STRING("Add to Reading List", "Title for Add to Reading List action button");
144         handler = ^(WKActionSheetAssistant *, _WKActivatedElementInfo *actionInfo) {
145             addToReadingList(actionInfo.URL, actionInfo.title);
146         };
147         break;
148 #endif
149     case _WKElementActionTypeShare:
150         title = WEB_UI_STRING("Share…", "Title for Share action button");
151         handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) {
152             [assistant.delegate actionSheetAssistant:assistant shareElementWithURL:actionInfo.URL ?: actionInfo.imageURL rect:actionInfo.boundingRect];
153         };
154         break;
155     default:
156         [NSException raise:NSInvalidArgumentException format:@"There is no standard web element action of type %ld.", (long)type];
157         return nil;
158     }
159
160     return [[[self alloc] _initWithTitle:(customTitle ? customTitle : title) actionHandler:handler type:type assistant:assistant] autorelease];
161 }
162
163 + (instancetype)_elementActionWithType:(_WKElementActionType)type assistant:(WKActionSheetAssistant *)assistant
164 {
165     return [self _elementActionWithType:type customTitle:nil assistant:assistant];
166 }
167
168 + (instancetype)elementActionWithType:(_WKElementActionType)type customTitle:(NSString *)customTitle
169 {
170     return [self _elementActionWithType:type customTitle:customTitle assistant:nil];
171 }
172
173 + (instancetype)elementActionWithType:(_WKElementActionType)type
174 {
175     return [self elementActionWithType:type customTitle:nil];
176 }
177
178 - (NSString *)title
179 {
180     return _title.get();
181 }
182
183 - (void)_runActionWithElementInfo:(_WKActivatedElementInfo *)info forActionSheetAssistant:(WKActionSheetAssistant *)assistant
184 {
185     _actionHandler(assistant, info);
186 }
187
188 - (void)runActionWithElementInfo:(_WKActivatedElementInfo *)info
189 {
190     [self _runActionWithElementInfo:info forActionSheetAssistant:_defaultActionSheetAssistant.get().get()];
191 }
192
193 #if USE(UICONTEXTMENU)
194 + (UIImage *)imageForElementActionType:(_WKElementActionType)actionType
195 {
196     switch (actionType) {
197     case _WKElementActionTypeCustom:
198         return nil;
199     case _WKElementActionTypeOpen:
200         return [UIImage systemImageNamed:@"safari"];
201     case _WKElementActionTypeCopy:
202         return [UIImage systemImageNamed:@"doc.on.doc"];
203     case _WKElementActionTypeSaveImage:
204         return [UIImage systemImageNamed:@"square.and.arrow.down"];
205     case _WKElementActionTypeAddToReadingList:
206         return [UIImage systemImageNamed:@"eyeglasses"];
207     case _WKElementActionTypeOpenInDefaultBrowser:
208         return [UIImage systemImageNamed:@"safari"];
209     case _WKElementActionTypeOpenInExternalApplication:
210         return [UIImage systemImageNamed:@"arrow.up.right.square"];
211     case _WKElementActionTypeShare:
212         return [UIImage systemImageNamed:@"square.and.arrow.up"];
213     case _WKElementActionTypeOpenInNewTab:
214         return [UIImage systemImageNamed:@"plus.square.on.square"];
215     case _WKElementActionTypeOpenInNewWindow:
216         return [UIImage systemImageNamed:@"square.grid.2x2"];
217     case _WKElementActionTypeDownload:
218         return [UIImage systemImageNamed:@"arrow.down.circle"];
219     }
220 }
221
222 static UIActionIdentifier elementActionTypeToUIActionIdentifier(_WKElementActionType actionType)
223 {
224     switch (actionType) {
225     case _WKElementActionTypeCustom:
226         return WKElementActionTypeCustomIdentifier;
227     case _WKElementActionTypeOpen:
228         return WKElementActionTypeOpenIdentifier;
229     case _WKElementActionTypeCopy:
230         return WKElementActionTypeCopyIdentifier;
231     case _WKElementActionTypeSaveImage:
232         return WKElementActionTypeSaveImageIdentifier;
233     case _WKElementActionTypeAddToReadingList:
234         return WKElementActionTypeAddToReadingListIdentifier;
235     case _WKElementActionTypeOpenInDefaultBrowser:
236         return WKElementActionTypeOpenInDefaultBrowserIdentifier;
237     case _WKElementActionTypeOpenInExternalApplication:
238         return WKElementActionTypeOpenInExternalApplicationIdentifier;
239     case _WKElementActionTypeShare:
240         return WKElementActionTypeShareIdentifier;
241     case _WKElementActionTypeOpenInNewTab:
242         return WKElementActionTypeOpenInNewTabIdentifier;
243     case _WKElementActionTypeOpenInNewWindow:
244         return WKElementActionTypeOpenInNewWindowIdentifier;
245     case _WKElementActionTypeDownload:
246         return WKElementActionTypeDownloadIdentifier;
247     }
248 }
249
250 static _WKElementActionType uiActionIdentifierToElementActionType(UIActionIdentifier identifier)
251 {
252     if ([identifier isEqualToString:WKElementActionTypeCustomIdentifier])
253         return _WKElementActionTypeCustom;
254     if ([identifier isEqualToString:WKElementActionTypeOpenIdentifier])
255         return _WKElementActionTypeOpen;
256     if ([identifier isEqualToString:WKElementActionTypeCopyIdentifier])
257         return _WKElementActionTypeCopy;
258     if ([identifier isEqualToString:WKElementActionTypeSaveImageIdentifier])
259         return _WKElementActionTypeSaveImage;
260     if ([identifier isEqualToString:WKElementActionTypeAddToReadingListIdentifier])
261         return _WKElementActionTypeAddToReadingList;
262     if ([identifier isEqualToString:WKElementActionTypeOpenInDefaultBrowserIdentifier])
263         return _WKElementActionTypeOpenInDefaultBrowser;
264     if ([identifier isEqualToString:WKElementActionTypeOpenInExternalApplicationIdentifier])
265         return _WKElementActionTypeOpenInExternalApplication;
266     if ([identifier isEqualToString:WKElementActionTypeShareIdentifier])
267         return _WKElementActionTypeShare;
268     if ([identifier isEqualToString:WKElementActionTypeOpenInNewTabIdentifier])
269         return _WKElementActionTypeOpenInNewTab;
270     if ([identifier isEqualToString:WKElementActionTypeOpenInNewWindowIdentifier])
271         return _WKElementActionTypeOpenInNewWindow;
272     if ([identifier isEqualToString:WKElementActionTypeDownloadIdentifier])
273         return _WKElementActionTypeDownload;
274
275     return _WKElementActionTypeCustom;
276 }
277
278 + (_WKElementActionType)elementActionTypeForUIActionIdentifier:(UIActionIdentifier)identifier
279 {
280     return uiActionIdentifierToElementActionType(identifier);
281 }
282
283 - (UIAction *)uiActionForElementInfo:(_WKActivatedElementInfo *)elementInfo
284 {
285     UIImage *image = [_WKElementAction imageForElementActionType:self.type];
286     UIActionIdentifier identifier = elementActionTypeToUIActionIdentifier(self.type);
287
288     return [UIAction actionWithTitle:self.title image:image identifier:identifier handler:[retainedSelf = retainPtr(self), retainedInfo = retainPtr(elementInfo)] (UIAction *) {
289         auto elementAction = retainedSelf.get();
290         RELEASE_LOG(ContextMenu, "Executing action for type: %s", elementActionTypeToUIActionIdentifier([elementAction type]).UTF8String);
291         [elementAction runActionWithElementInfo:retainedInfo.get()];
292     }];
293 }
294 #else
295 + (UIImage *)imageForElementActionType:(_WKElementActionType)actionType
296 {
297     return nil;
298 }
299
300 + (_WKElementActionType)elementActionTypeForUIActionIdentifier:(UIActionIdentifier)identifier
301 {
302     return _WKElementActionTypeCustom;
303 }
304
305 - (UIAction *)uiActionForElementInfo:(_WKActivatedElementInfo *)elementInfo
306 {
307     return nil;
308 }
309 #endif // USE(UICONTEXTMENU)
310
311 @end
312
313 #endif // PLATFORM(IOS_FAMILY)