| /* |
| * Copyright (C) 2014 Apple Inc. All rights reserved. |
| * |
| * 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 "_WKElementActionInternal.h" |
| |
| #if PLATFORM(IOS_FAMILY) |
| |
| #import "GestureTypes.h" |
| #import "Logging.h" |
| #import "WKActionSheetAssistant.h" |
| #import "WKContentViewInteraction.h" |
| #import "_WKActivatedElementInfoInternal.h" |
| #import <WebCore/LocalizedStrings.h> |
| #import <wtf/RetainPtr.h> |
| #import <wtf/SoftLinking.h> |
| #import <wtf/WeakObjCPtr.h> |
| #import <wtf/text/WTFString.h> |
| |
| #if HAVE(SAFARI_SERVICES_FRAMEWORK) |
| #import <SafariServices/SSReadingList.h> |
| SOFT_LINK_FRAMEWORK(SafariServices); |
| SOFT_LINK_CLASS(SafariServices, SSReadingList); |
| #endif |
| |
| typedef void (^WKElementActionHandlerInternal)(WKActionSheetAssistant *, _WKActivatedElementInfo *); |
| |
| static UIActionIdentifier const WKElementActionTypeCustomIdentifier = @"WKElementActionTypeCustom"; |
| static UIActionIdentifier const WKElementActionTypeOpenIdentifier = @"WKElementActionTypeOpen"; |
| static UIActionIdentifier const WKElementActionTypeCopyIdentifier = @"WKElementActionTypeCopy"; |
| static UIActionIdentifier const WKElementActionTypeSaveImageIdentifier = @"WKElementActionTypeSaveImage"; |
| #if !defined(TARGET_OS_IOS) || TARGET_OS_IOS |
| static UIActionIdentifier const WKElementActionTypeAddToReadingListIdentifier = @"WKElementActionTypeAddToReadingList"; |
| static UIActionIdentifier const WKElementActionTypeOpenInDefaultBrowserIdentifier = @"WKElementActionTypeOpenInDefaultBrowser"; |
| static UIActionIdentifier const WKElementActionTypeOpenInExternalApplicationIdentifier = @"WKElementActionTypeOpenInExternalApplication"; |
| #endif |
| static UIActionIdentifier const WKElementActionTypeShareIdentifier = @"WKElementActionTypeShare"; |
| static UIActionIdentifier const WKElementActionTypeOpenInNewTabIdentifier = @"WKElementActionTypeOpenInNewTab"; |
| static UIActionIdentifier const WKElementActionTypeOpenInNewWindowIdentifier = @"WKElementActionTypeOpenInNewWindow"; |
| static UIActionIdentifier const WKElementActionTypeDownloadIdentifier = @"WKElementActionTypeDownload"; |
| UIActionIdentifier const WKElementActionTypeToggleShowLinkPreviewsIdentifier = @"WKElementActionTypeToggleShowLinkPreviews"; |
| |
| static NSString * const webkitShowLinkPreviewsPreferenceKey = @"WebKitShowLinkPreviews"; |
| static NSString * const webkitShowLinkPreviewsPreferenceChangedNotification = @"WebKitShowLinkPreviewsPreferenceChanged"; |
| |
| @implementation _WKElementAction { |
| RetainPtr<NSString> _title; |
| WKElementActionHandlerInternal _actionHandler; |
| WKElementActionDismissalHandler _dismissalHandler; |
| WeakObjCPtr<WKActionSheetAssistant> _defaultActionSheetAssistant; |
| } |
| |
| - (id)_initWithTitle:(NSString *)title actionHandler:(WKElementActionHandlerInternal)handler type:(_WKElementActionType)type assistant:(WKActionSheetAssistant *)assistant |
| { |
| if (!(self = [super init])) |
| return nil; |
| |
| _title = adoptNS([title copy]); |
| _type = type; |
| _actionHandler = [handler copy]; |
| _defaultActionSheetAssistant = assistant; |
| return self; |
| } |
| |
| - (void)dealloc |
| { |
| [_actionHandler release]; |
| [_dismissalHandler release]; |
| |
| [super dealloc]; |
| } |
| |
| + (instancetype)elementActionWithTitle:(NSString *)title actionHandler:(WKElementActionHandler)handler |
| { |
| return [[[self alloc] _initWithTitle:title actionHandler:^(WKActionSheetAssistant *, _WKActivatedElementInfo *actionInfo) { handler(actionInfo); } |
| type:_WKElementActionTypeCustom assistant:nil] autorelease]; |
| } |
| |
| #if HAVE(SAFARI_SERVICES_FRAMEWORK) |
| static void addToReadingList(NSURL *targetURL, NSString *title) |
| { |
| if (!title || [title length] == 0) |
| title = [targetURL absoluteString]; |
| |
| [[getSSReadingListClass() defaultReadingList] addReadingListItemWithURL:targetURL title:title previewText:nil error:nil]; |
| } |
| #endif |
| |
| + (instancetype)elementActionWithType:(_WKElementActionType)type title:(NSString *)title actionHandler:(WKElementActionHandler)actionHandler |
| { |
| return [_WKElementAction _elementActionWithType:type title:title actionHandler:actionHandler]; |
| } |
| |
| + (instancetype)_elementActionWithType:(_WKElementActionType)type title:(NSString *)title actionHandler:(WKElementActionHandler)actionHandler |
| { |
| WKElementActionHandlerInternal handler = ^(WKActionSheetAssistant *, _WKActivatedElementInfo *actionInfo) { actionHandler(actionInfo); }; |
| return [[[self alloc] _initWithTitle:title actionHandler:handler type:type assistant:nil] autorelease]; |
| } |
| |
| + (instancetype)_elementActionWithType:(_WKElementActionType)type customTitle:(NSString *)customTitle assistant:(WKActionSheetAssistant *)assistant |
| { |
| NSString *title = @""; |
| WKElementActionHandlerInternal handler = nil; |
| switch (type) { |
| case _WKElementActionTypeCopy: |
| title = WEB_UI_STRING_KEY("Copy", "Copy (ActionSheet)", "Title for Copy Link or Image action button"); |
| handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) { |
| if ([assistant.delegate respondsToSelector:@selector(actionSheetAssistant:willStartInteractionWithElement:)]) |
| [assistant.delegate actionSheetAssistant:assistant willStartInteractionWithElement:actionInfo]; |
| [assistant.delegate actionSheetAssistant:assistant performAction:WebKit::SheetAction::Copy]; |
| if ([assistant.delegate respondsToSelector:@selector(actionSheetAssistantDidStopInteraction:)]) |
| [assistant.delegate actionSheetAssistantDidStopInteraction:assistant]; |
| }; |
| break; |
| case _WKElementActionTypeOpen: |
| title = WEB_UI_STRING("Open", "Title for Open Link action button"); |
| handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) { |
| [assistant.delegate actionSheetAssistant:assistant openElementAtLocation:actionInfo._interactionLocation]; |
| }; |
| break; |
| case _WKElementActionTypeSaveImage: |
| title = WEB_UI_STRING("Add to Photos", "Title for Add to Photos action button"); |
| handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) { |
| if ([assistant.delegate respondsToSelector:@selector(actionSheetAssistant:willStartInteractionWithElement:)]) |
| [assistant.delegate actionSheetAssistant:assistant willStartInteractionWithElement:actionInfo]; |
| [assistant.delegate actionSheetAssistant:assistant performAction:WebKit::SheetAction::SaveImage]; |
| if ([assistant.delegate respondsToSelector:@selector(actionSheetAssistantDidStopInteraction:)]) |
| [assistant.delegate actionSheetAssistantDidStopInteraction:assistant]; |
| }; |
| break; |
| #if HAVE(SAFARI_SERVICES_FRAMEWORK) |
| case _WKElementActionTypeAddToReadingList: |
| title = WEB_UI_STRING("Add to Reading List", "Title for Add to Reading List action button"); |
| handler = ^(WKActionSheetAssistant *, _WKActivatedElementInfo *actionInfo) { |
| addToReadingList(actionInfo.URL, actionInfo.title); |
| }; |
| break; |
| #endif |
| case _WKElementActionTypeShare: |
| title = WEB_UI_STRING("Share…", "Title for Share action button"); |
| handler = ^(WKActionSheetAssistant *assistant, _WKActivatedElementInfo *actionInfo) { |
| [assistant.delegate actionSheetAssistant:assistant shareElementWithURL:actionInfo.URL ?: actionInfo.imageURL rect:actionInfo.boundingRect]; |
| }; |
| break; |
| case _WKElementActionToggleShowLinkPreviews: |
| // This action must still exist for compatibility, but doesn't do anything. |
| break; |
| default: |
| [NSException raise:NSInvalidArgumentException format:@"There is no standard web element action of type %ld.", (long)type]; |
| return nil; |
| } |
| |
| return [[[self alloc] _initWithTitle:(customTitle ? customTitle : title) actionHandler:handler type:type assistant:assistant] autorelease]; |
| } |
| |
| + (instancetype)_elementActionWithType:(_WKElementActionType)type assistant:(WKActionSheetAssistant *)assistant |
| { |
| return [self _elementActionWithType:type customTitle:nil assistant:assistant]; |
| } |
| |
| + (instancetype)elementActionWithType:(_WKElementActionType)type customTitle:(NSString *)customTitle |
| { |
| return [self _elementActionWithType:type customTitle:customTitle assistant:nil]; |
| } |
| |
| + (instancetype)elementActionWithType:(_WKElementActionType)type |
| { |
| return [self elementActionWithType:type customTitle:nil]; |
| } |
| |
| - (NSString *)title |
| { |
| return _title.get(); |
| } |
| |
| - (void)_runActionWithElementInfo:(_WKActivatedElementInfo *)info forActionSheetAssistant:(WKActionSheetAssistant *)assistant |
| { |
| _actionHandler(assistant, info); |
| } |
| |
| - (void)runActionWithElementInfo:(_WKActivatedElementInfo *)info |
| { |
| [self _runActionWithElementInfo:info forActionSheetAssistant:_defaultActionSheetAssistant.get().get()]; |
| } |
| |
| #if USE(UICONTEXTMENU) |
| + (UIImage *)imageForElementActionType:(_WKElementActionType)actionType |
| { |
| switch (actionType) { |
| case _WKElementActionTypeCustom: |
| return nil; |
| case _WKElementActionTypeOpen: |
| return [UIImage systemImageNamed:@"safari"]; |
| case _WKElementActionTypeCopy: |
| return [UIImage systemImageNamed:@"doc.on.doc"]; |
| case _WKElementActionTypeSaveImage: |
| return [UIImage systemImageNamed:@"square.and.arrow.down"]; |
| case _WKElementActionTypeAddToReadingList: |
| return [UIImage systemImageNamed:@"eyeglasses"]; |
| case _WKElementActionTypeOpenInDefaultBrowser: |
| return [UIImage systemImageNamed:@"safari"]; |
| case _WKElementActionTypeOpenInExternalApplication: |
| return [UIImage systemImageNamed:@"arrow.up.right.square"]; |
| case _WKElementActionTypeShare: |
| return [UIImage systemImageNamed:@"square.and.arrow.up"]; |
| case _WKElementActionTypeOpenInNewTab: |
| return [UIImage systemImageNamed:@"plus.square.on.square"]; |
| case _WKElementActionTypeOpenInNewWindow: |
| return [UIImage systemImageNamed:@"square.grid.2x2"]; |
| case _WKElementActionTypeDownload: |
| return [UIImage systemImageNamed:@"arrow.down.circle"]; |
| case _WKElementActionToggleShowLinkPreviews: |
| return nil; // Intentionally empty. |
| } |
| } |
| |
| static UIActionIdentifier elementActionTypeToUIActionIdentifier(_WKElementActionType actionType) |
| { |
| switch (actionType) { |
| case _WKElementActionTypeCustom: |
| return WKElementActionTypeCustomIdentifier; |
| case _WKElementActionTypeOpen: |
| return WKElementActionTypeOpenIdentifier; |
| case _WKElementActionTypeCopy: |
| return WKElementActionTypeCopyIdentifier; |
| case _WKElementActionTypeSaveImage: |
| return WKElementActionTypeSaveImageIdentifier; |
| case _WKElementActionTypeAddToReadingList: |
| return WKElementActionTypeAddToReadingListIdentifier; |
| case _WKElementActionTypeOpenInDefaultBrowser: |
| return WKElementActionTypeOpenInDefaultBrowserIdentifier; |
| case _WKElementActionTypeOpenInExternalApplication: |
| return WKElementActionTypeOpenInExternalApplicationIdentifier; |
| case _WKElementActionTypeShare: |
| return WKElementActionTypeShareIdentifier; |
| case _WKElementActionTypeOpenInNewTab: |
| return WKElementActionTypeOpenInNewTabIdentifier; |
| case _WKElementActionTypeOpenInNewWindow: |
| return WKElementActionTypeOpenInNewWindowIdentifier; |
| case _WKElementActionTypeDownload: |
| return WKElementActionTypeDownloadIdentifier; |
| case _WKElementActionToggleShowLinkPreviews: |
| return WKElementActionTypeToggleShowLinkPreviewsIdentifier; |
| } |
| } |
| |
| static _WKElementActionType uiActionIdentifierToElementActionType(UIActionIdentifier identifier) |
| { |
| if ([identifier isEqualToString:WKElementActionTypeCustomIdentifier]) |
| return _WKElementActionTypeCustom; |
| if ([identifier isEqualToString:WKElementActionTypeOpenIdentifier]) |
| return _WKElementActionTypeOpen; |
| if ([identifier isEqualToString:WKElementActionTypeCopyIdentifier]) |
| return _WKElementActionTypeCopy; |
| if ([identifier isEqualToString:WKElementActionTypeSaveImageIdentifier]) |
| return _WKElementActionTypeSaveImage; |
| if ([identifier isEqualToString:WKElementActionTypeAddToReadingListIdentifier]) |
| return _WKElementActionTypeAddToReadingList; |
| if ([identifier isEqualToString:WKElementActionTypeOpenInDefaultBrowserIdentifier]) |
| return _WKElementActionTypeOpenInDefaultBrowser; |
| if ([identifier isEqualToString:WKElementActionTypeOpenInExternalApplicationIdentifier]) |
| return _WKElementActionTypeOpenInExternalApplication; |
| if ([identifier isEqualToString:WKElementActionTypeShareIdentifier]) |
| return _WKElementActionTypeShare; |
| if ([identifier isEqualToString:WKElementActionTypeOpenInNewTabIdentifier]) |
| return _WKElementActionTypeOpenInNewTab; |
| if ([identifier isEqualToString:WKElementActionTypeOpenInNewWindowIdentifier]) |
| return _WKElementActionTypeOpenInNewWindow; |
| if ([identifier isEqualToString:WKElementActionTypeDownloadIdentifier]) |
| return _WKElementActionTypeDownload; |
| if ([identifier isEqualToString:WKElementActionTypeToggleShowLinkPreviewsIdentifier]) |
| return _WKElementActionToggleShowLinkPreviews; |
| |
| return _WKElementActionTypeCustom; |
| } |
| |
| + (_WKElementActionType)elementActionTypeForUIActionIdentifier:(UIActionIdentifier)identifier |
| { |
| return uiActionIdentifierToElementActionType(identifier); |
| } |
| |
| - (UIAction *)uiActionForElementInfo:(_WKActivatedElementInfo *)elementInfo |
| { |
| UIImage *image = [_WKElementAction imageForElementActionType:self.type]; |
| UIActionIdentifier identifier = elementActionTypeToUIActionIdentifier(self.type); |
| |
| return [UIAction actionWithTitle:self.title image:image identifier:identifier handler:[retainedSelf = retainPtr(self), retainedInfo = retainPtr(elementInfo)] (UIAction *) { |
| auto elementAction = retainedSelf.get(); |
| RELEASE_LOG(ContextMenu, "Executing action for type: %s", elementActionTypeToUIActionIdentifier([elementAction type]).UTF8String); |
| [elementAction runActionWithElementInfo:retainedInfo.get()]; |
| }]; |
| } |
| #else |
| + (UIImage *)imageForElementActionType:(_WKElementActionType)actionType |
| { |
| return nil; |
| } |
| |
| + (_WKElementActionType)elementActionTypeForUIActionIdentifier:(UIActionIdentifier)identifier |
| { |
| return _WKElementActionTypeCustom; |
| } |
| |
| - (UIAction *)uiActionForElementInfo:(_WKActivatedElementInfo *)elementInfo |
| { |
| return nil; |
| } |
| #endif // USE(UICONTEXTMENU) |
| |
| @end |
| |
| #endif // PLATFORM(IOS_FAMILY) |