Add delegate method to handle images with alternate data.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Feb 2017 21:28:58 +0000 (21:28 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Feb 2017 21:28:58 +0000 (21:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=168785
<rdar://problem/28776219>

Add delegate methods to WKUIDelegatePrivate so that a WebKit client can provide alternate URL or data for an image
before long-press or preview. When an image has alternate URL or data, we can change the actions accordingly.

Patch by Yongjun Zhang <yongjun_zhang@apple.com> on 2017-02-28
Reviewed by Enrica Casucci.

* UIProcess/API/Cocoa/WKUIDelegatePrivate.h: Add two delegate methods _webView:getAlternateURLFromImage:completionHandler:
    and _webView:alternateURLFromImage:userInfo;
* UIProcess/API/Cocoa/_WKActivatedElementInfo.h: Add an readonly property userInfo so that alternate data can be
    carried by _WKActivatedElementInfo and be processed by WebKit client.
* UIProcess/API/Cocoa/_WKActivatedElementInfo.mm:
(-[_WKActivatedElementInfo _initWithType:URL:location:title:ID:rect:image:]):
(-[_WKActivatedElementInfo _initWithType:URL:location:title:ID:rect:image:userInfo:]): A new initializer that also takes userInfo.
(-[_WKActivatedElementInfo userInfo]):
* UIProcess/API/Cocoa/_WKActivatedElementInfoInternal.h: A new delegate method for WKActionSheetAssistantDelegate to fetch
    alternate data before showing image sheet.
* UIProcess/ios/WKActionSheetAssistant.h:
* UIProcess/ios/WKActionSheetAssistant.mm:
(-[WKActionSheetAssistant showImageSheet]): Before showing the image sheet, if the image doesn't have linked URL (i.e. not a image
    link), try to fetch its alternate URL and data.
(-[WKActionSheetAssistant defaultActionsForImageSheet:]): Drive-by fix. We always use positionInformation's URL to populate the
    actions, however, we should use elementInfo's URL since the two URLs could mismatch.
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView actionSheetAssistant:getAlternateURLForImage:completion:]):
(-[WKContentView _presentedViewControllerForPreviewItemController:]): Before previewing an image, synchronously get alternate
    data for the image and update the actions accordingly.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@213170 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKUIDelegatePrivate.h
Source/WebKit2/UIProcess/API/Cocoa/_WKActivatedElementInfo.h
Source/WebKit2/UIProcess/API/Cocoa/_WKActivatedElementInfo.mm
Source/WebKit2/UIProcess/API/Cocoa/_WKActivatedElementInfoInternal.h
Source/WebKit2/UIProcess/ios/WKActionSheetAssistant.h
Source/WebKit2/UIProcess/ios/WKActionSheetAssistant.mm
Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm

index cf0177d..9a55eb9 100644 (file)
@@ -1,3 +1,35 @@
+2017-02-28  Yongjun Zhang  <yongjun_zhang@apple.com>
+
+        Add delegate method to handle images with alternate data.
+        https://bugs.webkit.org/show_bug.cgi?id=168785
+        <rdar://problem/28776219>
+
+        Add delegate methods to WKUIDelegatePrivate so that a WebKit client can provide alternate URL or data for an image
+        before long-press or preview. When an image has alternate URL or data, we can change the actions accordingly.
+
+        Reviewed by Enrica Casucci.
+
+        * UIProcess/API/Cocoa/WKUIDelegatePrivate.h: Add two delegate methods _webView:getAlternateURLFromImage:completionHandler:
+            and _webView:alternateURLFromImage:userInfo;
+        * UIProcess/API/Cocoa/_WKActivatedElementInfo.h: Add an readonly property userInfo so that alternate data can be
+            carried by _WKActivatedElementInfo and be processed by WebKit client.
+        * UIProcess/API/Cocoa/_WKActivatedElementInfo.mm:
+        (-[_WKActivatedElementInfo _initWithType:URL:location:title:ID:rect:image:]):
+        (-[_WKActivatedElementInfo _initWithType:URL:location:title:ID:rect:image:userInfo:]): A new initializer that also takes userInfo.
+        (-[_WKActivatedElementInfo userInfo]):
+        * UIProcess/API/Cocoa/_WKActivatedElementInfoInternal.h: A new delegate method for WKActionSheetAssistantDelegate to fetch
+            alternate data before showing image sheet.
+        * UIProcess/ios/WKActionSheetAssistant.h:
+        * UIProcess/ios/WKActionSheetAssistant.mm:
+        (-[WKActionSheetAssistant showImageSheet]): Before showing the image sheet, if the image doesn't have linked URL (i.e. not a image
+            link), try to fetch its alternate URL and data.
+        (-[WKActionSheetAssistant defaultActionsForImageSheet:]): Drive-by fix. We always use positionInformation's URL to populate the
+            actions, however, we should use elementInfo's URL since the two URLs could mismatch.
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView actionSheetAssistant:getAlternateURLForImage:completion:]):
+        (-[WKContentView _presentedViewControllerForPreviewItemController:]): Before previewing an image, synchronously get alternate
+            data for the image and update the actions accordingly.
+
 2017-02-28  Brady Eidson  <beidson@apple.com>
         
         Refactor WebViewImpl creation in preparation for supporting multiple WebsiteDataStores.
index 9b3413f..1183434 100644 (file)
@@ -84,6 +84,8 @@ struct UIEdgeInsets;
 - (UIViewController *)_webView:(WKWebView *)webView previewViewControllerForURL:(NSURL *)url defaultActions:(NSArray<_WKElementAction *> *)actions elementInfo:(_WKActivatedElementInfo *)elementInfo WK_API_AVAILABLE(ios(9.0));
 - (UIViewController *)_webView:(WKWebView *)webView previewViewControllerForAnimatedImageAtURL:(NSURL *)url defaultActions:(NSArray<_WKElementAction *> *)actions elementInfo:(_WKActivatedElementInfo *)elementInfo imageSize:(CGSize)imageSize WK_API_AVAILABLE(ios(9.0));
 - (UIViewController *)_presentingViewControllerForWebView:(WKWebView *)webView WK_API_AVAILABLE(ios(10.0));
+- (void)_webView:(WKWebView *)webView getAlternateURLFromImage:(UIImage *)image completionHandler:(void (^)(NSURL *alternateURL, NSDictionary *userInfo))completionHandler WK_API_AVAILABLE(ios(WK_IOS_TBA));
+- (NSURL *)_webView:(WKWebView *)webView alternateURLFromImage:(UIImage *)image userInfo:(NSDictionary **)userInfo WK_API_AVAILABLE(ios(WK_IOS_TBA));
 #else
 - (NSMenu *)_webView:(WKWebView *)webView contextMenu:(NSMenu *)menu forElement:(_WKContextMenuElementInfo *)element WK_API_AVAILABLE(macosx(10.12));
 - (NSMenu *)_webView:(WKWebView *)webView contextMenu:(NSMenu *)menu forElement:(_WKContextMenuElementInfo *)element userInfo:(id <NSSecureCoding>)userInfo WK_API_AVAILABLE(macosx(10.12));
index 39314c8..02e122f 100644 (file)
@@ -48,6 +48,7 @@ WK_CLASS_AVAILABLE(macosx(10.10), ios(8.0))
 @property (nonatomic, readonly) CGRect boundingRect;
 @property (nonatomic, readonly) NSString *ID WK_API_AVAILABLE(macosx(10.12), ios(10.0));
 #if TARGET_OS_IPHONE
+@property (nonatomic, readonly) NSDictionary *userInfo;
 @property (nonatomic, readonly, copy) UIImage *image;
 #else
 @property (nonatomic, readonly, copy) NSImage *image;
index ca932d3..58572e7 100644 (file)
@@ -47,6 +47,7 @@
     RefPtr<WebKit::ShareableBitmap> _image;
 #if PLATFORM(IOS)
     RetainPtr<UIImage> _uiImage;
+    RetainPtr<NSDictionary> _userInfo;
 #endif
 #if PLATFORM(MAC)
     RetainPtr<NSImage> _nsImage;
 
 - (instancetype)_initWithType:(_WKActivatedElementType)type URL:(NSURL *)url location:(CGPoint)location title:(NSString *)title ID:(NSString *)ID rect:(CGRect)rect image:(WebKit::ShareableBitmap*)image
 {
+    return [self _initWithType:type URL:url location:location title:title ID:ID rect:rect image:image userInfo:nil];
+}
+
+- (instancetype)_initWithType:(_WKActivatedElementType)type URL:(NSURL *)url location:(CGPoint)location title:(NSString *)title ID:(NSString *)ID rect:(CGRect)rect image:(WebKit::ShareableBitmap*)image userInfo:(NSDictionary *)userInfo
+{
     if (!(self = [super init]))
         return nil;
 
@@ -65,6 +71,9 @@
     _type = type;
     _image = image;
     _ID = ID;
+#if PLATFORM(IOS)
+    _userInfo = adoptNS([userInfo copy]);
+#endif
 
     return self;
 }
 }
 
 #if PLATFORM(IOS)
+- (NSDictionary *)userInfo
+{
+    return _userInfo.get();
+}
+
 - (UIImage *)image
 {
     if (_uiImage)
index cde75be..e579df4 100644 (file)
@@ -34,6 +34,7 @@ namespace WebKit {
 @interface _WKActivatedElementInfo ()
 
 - (instancetype)_initWithType:(_WKActivatedElementType)type URL:(NSURL *)url location:(CGPoint)location title:(NSString *)title ID:(NSString *)ID rect:(CGRect)rect image:(WebKit::ShareableBitmap*)image;
+- (instancetype)_initWithType:(_WKActivatedElementType)type URL:(NSURL *)url location:(CGPoint)location title:(NSString *)title ID:(NSString *)ID rect:(CGRect)rect image:(WebKit::ShareableBitmap*)image userInfo:(NSDictionary *)userInfo;
 
 @property (nonatomic, readonly) CGPoint _interactionLocation;
 
index 3d5a208..efb91ee 100644 (file)
@@ -58,6 +58,7 @@ struct InteractionInformationAtPosition;
 - (void)actionSheetAssistantDidStopInteraction:(WKActionSheetAssistant *)assistant;
 - (NSDictionary *)dataDetectionContextForActionSheetAssistant:(WKActionSheetAssistant *)assistant;
 - (NSString *)selectedTextForActionSheetAssistant:(WKActionSheetAssistant *)assistant;
+- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant getAlternateURLForImage:(UIImage *)image completion:(void (^)(NSURL *alternateURL, NSDictionary *userInfo))completion;
 
 @end
 
index 1189ddd..e98f7d4 100644 (file)
@@ -334,26 +334,44 @@ static const CGFloat presentationElementRectPadding = 15;
 
     const auto& positionInformation = [delegate positionInformationForActionSheetAssistant:self];
 
-    NSURL *targetURL = [NSURL _web_URLWithWTFString:positionInformation.url];
-    auto elementBounds = positionInformation.bounds;
-    auto elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:targetURL location:positionInformation.request.point title:positionInformation.title ID:positionInformation.idAttribute rect:elementBounds image:positionInformation.image.get()]);
-    if ([delegate respondsToSelector:@selector(actionSheetAssistant:showCustomSheetForElement:)] && [delegate actionSheetAssistant:self showCustomSheetForElement:elementInfo.get()])
-        return;
-    auto defaultActions = [self defaultActionsForImageSheet:elementInfo.get()];
+    void (^showImageSheetWithAlternateURLBlock)(NSURL*, NSDictionary *userInfo) = ^(NSURL *alternateURL, NSDictionary *userInfo) {
+        NSURL *targetURL = [NSURL _web_URLWithWTFString:positionInformation.url] ?: alternateURL;
+        auto elementBounds = positionInformation.bounds;
+        auto elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:targetURL location:positionInformation.request.point title:positionInformation.title ID:positionInformation.idAttribute rect:elementBounds image:positionInformation.image.get() userInfo:userInfo]);
+        if ([delegate respondsToSelector:@selector(actionSheetAssistant:showCustomSheetForElement:)] && [delegate actionSheetAssistant:self showCustomSheetForElement:elementInfo.get()])
+            return;
+        auto defaultActions = [self defaultActionsForImageSheet:elementInfo.get()];
+
+        RetainPtr<NSArray> actions = [delegate actionSheetAssistant:self decideActionsForElement:elementInfo.get() defaultActions:WTFMove(defaultActions)];
+
+        if (![actions count])
+            return;
+
+        if (!alternateURL && userInfo) {
+            [UIApp _cancelAllTouches];
+            return;
+        }
 
-    RetainPtr<NSArray> actions = [delegate actionSheetAssistant:self decideActionsForElement:elementInfo.get() defaultActions:WTFMove(defaultActions)];
+        [self _createSheetWithElementActions:actions.get() showLinkTitle:YES];
+        if (!_interactionSheet)
+            return;
 
-    if (![actions count])
-        return;
+        _elementInfo = WTFMove(elementInfo);
 
-    [self _createSheetWithElementActions:actions.get() showLinkTitle:YES];
-    if (!_interactionSheet)
-        return;
+        if (![_interactionSheet presentSheet:[self _shouldPresentAtTouchLocationForElementRect:elementBounds] ? WKActionSheetPresentAtTouchLocation : WKActionSheetPresentAtElementRect])
+            [self cleanupSheet];
+    };
 
-    _elementInfo = WTFMove(elementInfo);
+    if (positionInformation.url.isEmpty() && positionInformation.image && [delegate respondsToSelector:@selector(actionSheetAssistant:getAlternateURLForImage:completion:)]) {
+        RetainPtr<UIImage> uiImage = adoptNS([[UIImage alloc] initWithCGImage:positionInformation.image->makeCGImageCopy().get()]);
 
-    if (![_interactionSheet presentSheet:[self _shouldPresentAtTouchLocationForElementRect:elementBounds] ? WKActionSheetPresentAtTouchLocation : WKActionSheetPresentAtElementRect])
-        [self cleanupSheet];
+        [delegate actionSheetAssistant:self getAlternateURLForImage:uiImage.get() completion:^(NSURL *alternateURL, NSDictionary *userInfo) {
+            showImageSheetWithAlternateURLBlock(alternateURL, userInfo);
+        }];
+        return;
+    }
+
+    showImageSheetWithAlternateURLBlock(nil, nil);
 }
 
 - (BOOL)_shouldPresentAtTouchLocationForElementRect:(CGRect)elementRect
@@ -436,11 +454,10 @@ static const CGFloat presentationElementRectPadding = 15;
     if (!delegate)
         return nil;
 
-    const auto& positionInformation = [delegate positionInformationForActionSheetAssistant:self];
-    NSURL *targetURL = [NSURL _web_URLWithWTFString:positionInformation.url];
+    NSURL *targetURL = [elementInfo URL];
 
     auto defaultActions = adoptNS([[NSMutableArray alloc] init]);
-    if (!positionInformation.url.isEmpty()) {
+    if (targetURL) {
         [self _appendOpenActionsForURL:targetURL actions:defaultActions.get() elementInfo:elementInfo];
         [defaultActions addObject:[_WKElementAction _elementActionWithType:_WKElementActionTypeShare assistant:self]];
     }
index 4a67546..8c10ac2 100644 (file)
@@ -4012,6 +4012,17 @@ static bool isAssistableInputType(InputType type)
     return [self selectedText];
 }
 
+- (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant getAlternateURLForImage:(UIImage *)image completion:(void (^)(NSURL *alternateURL, NSDictionary *userInfo))completion
+{
+    id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
+    if ([uiDelegate respondsToSelector:@selector(_webView:getAlternateURLFromImage:completionHandler:)]) {
+        [uiDelegate _webView:_webView getAlternateURLFromImage:image completionHandler:^(NSURL *alternateURL, NSDictionary *userInfo) {
+            completion(alternateURL, userInfo);
+        }];
+    } else
+        completion(nil, nil);
+}
+
 @end
 
 @implementation WKContentView (WKTesting)
@@ -4257,12 +4268,27 @@ static NSString *previewIdentifierForElementAction(_WKElementAction *action)
         if (!isValidURLForImagePreview)
             return nil;
 
-        RetainPtr<_WKActivatedElementInfo> elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:targetURL location:_positionInformation.request.point title:_positionInformation.title ID:_positionInformation.idAttribute rect:_positionInformation.bounds image:_positionInformation.image.get()]);
+        RetainPtr<NSURL> alternateURL = targetURL;
+        RetainPtr<NSDictionary> imageInfo;
+        RetainPtr<CGImageRef> cgImage = _positionInformation.image->makeCGImageCopy();
+        RetainPtr<UIImage> uiImage = adoptNS([[UIImage alloc] initWithCGImage:cgImage.get()]);
+        if ([uiDelegate respondsToSelector:@selector(_webView:alternateURLFromImage:userInfo:)]) {
+            NSDictionary *userInfo;
+            alternateURL = [uiDelegate _webView:_webView alternateURLFromImage:uiImage.get() userInfo:&userInfo];
+            imageInfo = userInfo;
+        }
+
+        RetainPtr<_WKActivatedElementInfo> elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:alternateURL.get() location:_positionInformation.request.point title:_positionInformation.title ID:_positionInformation.idAttribute rect:_positionInformation.bounds image:_positionInformation.image.get() userInfo:imageInfo.get()]);
         _page->startInteractionWithElementAtPosition(_positionInformation.request.point);
 
         if ([uiDelegate respondsToSelector:@selector(_webView:willPreviewImageWithURL:)])
             [uiDelegate _webView:_webView willPreviewImageWithURL:targetURL];
-        return [[[WKImagePreviewViewController alloc] initWithCGImage:_positionInformation.image->makeCGImageCopy() defaultActions:[_actionSheetAssistant defaultActionsForImageSheet:elementInfo.get()] elementInfo:elementInfo] autorelease];
+
+        auto defaultActions = [_actionSheetAssistant defaultActionsForImageSheet:elementInfo.get()];
+        if (imageInfo && [uiDelegate respondsToSelector:@selector(_webView:actionsForElement:defaultActions:)])
+            defaultActions = [uiDelegate _webView:_webView actionsForElement:elementInfo.get() defaultActions:defaultActions.get()];
+
+        return [[[WKImagePreviewViewController alloc] initWithCGImage:cgImage defaultActions:defaultActions elementInfo:elementInfo] autorelease];
     }
 
     return nil;