Title: [293730] trunk/Source/WebKit
Revision
293730
Author
[email protected]
Date
2022-05-03 10:37:47 -0700 (Tue, 03 May 2022)

Log Message

[iOS] The "Copy Cropped Image" context menu action should be gated on cropped image results
https://bugs.webkit.org/show_bug.cgi?id=240013
rdar://88941787

Reviewed by Tim Horton.

Only show this item in the context menu when long pressing in the case where `requestImageAnalysisMarkup`
computes a non-null result for the given image bitmap. This gating logic runs alongside existing gating logic
for both the visual search item ("Look Up") and "Show Text" actions. See below for more details.

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _setUpImageAnalysis]):
(-[WKContentView _tearDownImageAnalysis]):

Introduce a `_croppedImageResult` ivar to cache the CGImage result after running image analysis over the image
corresponding to the element for which we're showing the context menu. This is reset in the same lifecycle as
the extant `_hasVisualSearchResults` and `_hasSelectableTextInImage` flags which are used for the same purpose.

(-[WKContentView imageAnalysisGestureDidBegin:]):
(-[WKContentView _completeImageAnalysisRequestForContextMenu:requestIdentifier:hasTextResults:]):

This is the codepath that currently prevents us from showing the context menu until we know whether or not there
are relevant visual search results, such that we can conditionally show the "Look Up" context menu item. Adjust
this so that it calls `-_invokeAllActionsToPerformAfterPendingImageAnalysis:` only after we've also determined
whether or not there is a non-null cropped image result, so that we can also conditionally show the "Copy
Cropped Image" item.

To achieve this, we move the call to `-_invokeAllActionsToPerformAfterPendingImageAnalysis:` into a
`WTF::CallbackAggregator`, and ref/deref the aggregator when invoking both of the async image analysis
operations. When the callback aggregator is destroyed (i.e., after both async image analysis operations are
complete), we proceed with showing the context menu.

(-[WKContentView imageAnalysisGestureDidTimeOut:]):

Implement similar logic as above, but for the scenario where we show the context menu after the user continues
to long press after selecting text inside of an image.

(-[WKContentView actionSheetAssistantShouldIncludeCopyCroppedImageAction:]):

Only show the item if `_croppedImageResult` is non-null.

(-[WKContentView actionSheetAssistant:copyCroppedImage:sourceMIMEType:]):

Instead of running image analysis and writing the resulting image to the clipboard, simply transcode the cached
image in `_croppedImageResult`.

Canonical link: https://commits.webkit.org/250218@main

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (293729 => 293730)


--- trunk/Source/WebKit/ChangeLog	2022-05-03 17:06:02 UTC (rev 293729)
+++ trunk/Source/WebKit/ChangeLog	2022-05-03 17:37:47 UTC (rev 293730)
@@ -1,3 +1,52 @@
+2022-05-03  Wenson Hsieh  <[email protected]>
+
+        [iOS] The "Copy Cropped Image" context menu action should be gated on cropped image results
+        https://bugs.webkit.org/show_bug.cgi?id=240013
+        rdar://88941787
+
+        Reviewed by Tim Horton.
+
+        Only show this item in the context menu when long pressing in the case where `requestImageAnalysisMarkup`
+        computes a non-null result for the given image bitmap. This gating logic runs alongside existing gating logic
+        for both the visual search item ("Look Up") and "Show Text" actions. See below for more details.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _setUpImageAnalysis]):
+        (-[WKContentView _tearDownImageAnalysis]):
+
+        Introduce a `_croppedImageResult` ivar to cache the CGImage result after running image analysis over the image
+        corresponding to the element for which we're showing the context menu. This is reset in the same lifecycle as
+        the extant `_hasVisualSearchResults` and `_hasSelectableTextInImage` flags which are used for the same purpose.
+
+        (-[WKContentView imageAnalysisGestureDidBegin:]):
+        (-[WKContentView _completeImageAnalysisRequestForContextMenu:requestIdentifier:hasTextResults:]):
+
+        This is the codepath that currently prevents us from showing the context menu until we know whether or not there
+        are relevant visual search results, such that we can conditionally show the "Look Up" context menu item. Adjust
+        this so that it calls `-_invokeAllActionsToPerformAfterPendingImageAnalysis:` only after we've also determined
+        whether or not there is a non-null cropped image result, so that we can also conditionally show the "Copy
+        Cropped Image" item.
+
+        To achieve this, we move the call to `-_invokeAllActionsToPerformAfterPendingImageAnalysis:` into a
+        `WTF::CallbackAggregator`, and ref/deref the aggregator when invoking both of the async image analysis
+        operations. When the callback aggregator is destroyed (i.e., after both async image analysis operations are
+        complete), we proceed with showing the context menu.
+
+        (-[WKContentView imageAnalysisGestureDidTimeOut:]):
+
+        Implement similar logic as above, but for the scenario where we show the context menu after the user continues
+        to long press after selecting text inside of an image.
+
+        (-[WKContentView actionSheetAssistantShouldIncludeCopyCroppedImageAction:]):
+
+        Only show the item if `_croppedImageResult` is non-null.
+
+        (-[WKContentView actionSheetAssistant:copyCroppedImage:sourceMIMEType:]):
+
+        Instead of running image analysis and writing the resulting image to the clipboard, simply transcode the cached
+        image in `_croppedImageResult`.
+
 2022-05-03  Philippe Normand  <[email protected]> and Yury Semikhatsky <[email protected]>
 
         [WK2] Add API to allow embedder to set a timezone override

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (293729 => 293730)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2022-05-03 17:06:02 UTC (rev 293729)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2022-05-03 17:37:47 UTC (rev 293730)
@@ -545,6 +545,7 @@
     RetainPtr<VKCImageAnalysisInteraction> _imageAnalysisInteraction;
     WebCore::FloatRect _imageAnalysisInteractionBounds;
     std::optional<WebKit::ImageAnalysisMarkupData> _imageAnalysisMarkupData;
+    RetainPtr<CGImageRef> _croppedImageResult;
 #endif
 }
 

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (293729 => 293730)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2022-05-03 17:06:02 UTC (rev 293729)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2022-05-03 17:37:47 UTC (rev 293730)
@@ -148,6 +148,7 @@
 #import <pal/spi/ios/ManagedConfigurationSPI.h>
 #import <wtf/BlockObjCExceptions.h>
 #import <wtf/BlockPtr.h>
+#import <wtf/CallbackAggregator.h>
 #import <wtf/Scope.h>
 #import <wtf/SetForScope.h>
 #import <wtf/WeakObjCPtr.h>
@@ -10714,6 +10715,7 @@
 #endif // USE(UICONTEXTMENU) && ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
 #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
     _imageAnalysisMarkupData = std::nullopt;
+    _croppedImageResult = nil;
 #endif
 }
 
@@ -10743,6 +10745,7 @@
 #if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
     [self uninstallImageAnalysisInteraction];
     _imageAnalysisMarkupData = std::nullopt;
+    _croppedImageResult = nil;
 #endif
 }
 
@@ -10838,6 +10841,10 @@
     _hasVisualSearchResults = NO;
 #endif // USE(QUICK_LOOK)
 
+#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
+    _croppedImageResult = nil;
+#endif
+
 #if USE(UICONTEXTMENU) && ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
     _contextMenuForMachineReadableCode.clear();
 #endif // USE(UICONTEXTMENU) && ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
@@ -10887,15 +10894,13 @@
         WebCore::ElementContext elementContext = *information.hostImageOrVideoElementContext;
 
         auto requestForTextSelection = [strongSelf createImageAnalyzerRequest:VKAnalysisTypeText image:cgImage.get()];
-        auto requestForContextMenu = [strongSelf createImageAnalyzerRequest:VKAnalysisTypeVisualSearch | VKAnalysisTypeMachineReadableCode | VKAnalysisTypeAppClip image:cgImage.get()];
-
         if (information.elementContainsImageOverlay) {
-            [strongSelf _completeImageAnalysisRequestForContextMenu:requestForContextMenu.get() requestIdentifier:requestIdentifier hasTextResults:YES];
+            [strongSelf _completeImageAnalysisRequestForContextMenu:cgImage.get() requestIdentifier:requestIdentifier hasTextResults:YES];
             return;
         }
 
         auto textAnalysisStartTime = MonotonicTime::now();
-        [[strongSelf imageAnalyzer] processRequest:requestForTextSelection.get() progressHandler:nil completionHandler:[requestIdentifier = WTFMove(requestIdentifier), weakSelf, elementContext, requestLocation, requestForContextMenu, gestureDeferralToken, textAnalysisStartTime] (CocoaImageAnalysis *result, NSError *error) mutable {
+        [[strongSelf imageAnalyzer] processRequest:requestForTextSelection.get() progressHandler:nil completionHandler:[requestIdentifier = WTFMove(requestIdentifier), weakSelf, elementContext, requestLocation, cgImage, gestureDeferralToken, textAnalysisStartTime] (CocoaImageAnalysis *result, NSError *error) mutable {
             auto strongSelf = weakSelf.get();
             if (![strongSelf validateImageAnalysisRequestIdentifier:requestIdentifier])
                 return;
@@ -10903,7 +10908,7 @@
             BOOL hasTextResults = [result hasResultsForAnalysisTypes:VKAnalysisTypeText];
             RELEASE_LOG(Images, "Image analysis completed in %.0f ms (request %" PRIu64 "; found text? %d)", (MonotonicTime::now() - textAnalysisStartTime).milliseconds(), requestIdentifier.toUInt64(), hasTextResults);
 
-            strongSelf->_page->updateWithTextRecognitionResult(WebKit::makeTextRecognitionResult(result), elementContext, requestLocation, [requestIdentifier = WTFMove(requestIdentifier), weakSelf, hasTextResults, requestForContextMenu, gestureDeferralToken] (WebKit::TextRecognitionUpdateResult updateResult) mutable {
+            strongSelf->_page->updateWithTextRecognitionResult(WebKit::makeTextRecognitionResult(result), elementContext, requestLocation, [requestIdentifier = WTFMove(requestIdentifier), weakSelf, hasTextResults, cgImage, gestureDeferralToken] (WebKit::TextRecognitionUpdateResult updateResult) mutable {
                 auto strongSelf = weakSelf.get();
                 if (![strongSelf validateImageAnalysisRequestIdentifier:requestIdentifier])
                     return;
@@ -10921,16 +10926,27 @@
                     return;
                 }
 
-                [strongSelf _completeImageAnalysisRequestForContextMenu:requestForContextMenu.get() requestIdentifier:requestIdentifier hasTextResults:hasTextResults];
+                [strongSelf _completeImageAnalysisRequestForContextMenu:cgImage.get() requestIdentifier:requestIdentifier hasTextResults:hasTextResults];
             });
         }];
     } forRequest:request];
 }
 
-- (void)_completeImageAnalysisRequestForContextMenu:(CocoaImageAnalyzerRequest *)requestForContextMenu requestIdentifier:(WebKit::ImageAnalysisRequestIdentifier)requestIdentifier hasTextResults:(BOOL)hasTextResults
+- (void)_completeImageAnalysisRequestForContextMenu:(CGImageRef)image requestIdentifier:(WebKit::ImageAnalysisRequestIdentifier)requestIdentifier hasTextResults:(BOOL)hasTextResults
 {
+#if USE(QUICK_LOOK)
+    _hasSelectableTextInImage = hasTextResults;
+#endif
+
+    auto weakSelf = WeakObjCPtr<WKContentView>(self);
+    auto aggregator = CallbackAggregator::create([weakSelf, requestIdentifier]() mutable {
+        if (auto strongSelf = weakSelf.get(); [strongSelf validateImageAnalysisRequestIdentifier:requestIdentifier])
+            [strongSelf _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No];
+    });
+
+    auto request = [self createImageAnalyzerRequest:VKAnalysisTypeVisualSearch | VKAnalysisTypeMachineReadableCode | VKAnalysisTypeAppClip image:image];
     auto visualSearchAnalysisStartTime = MonotonicTime::now();
-    [self.imageAnalyzer processRequest:requestForContextMenu progressHandler:nil completionHandler:[requestIdentifier = WTFMove(requestIdentifier), weakSelf = WeakObjCPtr<WKContentView>(self), hasTextResults, visualSearchAnalysisStartTime] (CocoaImageAnalysis *result, NSError *error) mutable {
+    [self.imageAnalyzer processRequest:request.get() progressHandler:nil completionHandler:[requestIdentifier = WTFMove(requestIdentifier), weakSelf, visualSearchAnalysisStartTime, aggregator = aggregator.copyRef()] (CocoaImageAnalysis *result, NSError *error) mutable {
         auto strongSelf = weakSelf.get();
         if (![strongSelf validateImageAnalysisRequestIdentifier:requestIdentifier])
             return;
@@ -10941,22 +10957,25 @@
 #else
         UNUSED_PARAM(visualSearchAnalysisStartTime);
 #endif
-        if (!result || error) {
-            [strongSelf _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No];
+        if (!result || error)
             return;
-        }
 
 #if USE(QUICK_LOOK)
-        strongSelf->_hasSelectableTextInImage = hasTextResults;
         strongSelf->_hasVisualSearchResults = hasVisualSearchResults;
-#else
-        UNUSED_PARAM(hasTextResults);
 #endif
 #if USE(UICONTEXTMENU) && ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
         [strongSelf _updateContextMenuForMachineReadableCodeForImageAnalysis:result];
-#endif // USE(UICONTEXTMENU) && ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
-        [strongSelf _invokeAllActionsToPerformAfterPendingImageAnalysis:WebKit::ProceedWithTextSelectionInImage::No];
+#endif
     }];
+
+#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
+    if (_page->preferences().imageAnalysisMarkupEnabled()) {
+        WebKit::requestImageAnalysisMarkup(image, [weakSelf = WeakObjCPtr<WKContentView>(self), aggregator = aggregator.copyRef()](CGImageRef result, CGRect) mutable {
+            if (auto strongSelf = weakSelf.get())
+                strongSelf->_croppedImageResult = result;
+        });
+    }
+#endif
 }
 
 - (void)imageAnalysisGestureDidFail:(WKImageAnalysisGestureRecognizer *)gestureRecognizer
@@ -10997,9 +11016,18 @@
         // FIXME: We need to implement some way to cache image analysis results per element, so that we don't end up
         // making redundant image analysis requests for the same image data.
 
+        auto aggregator = CallbackAggregator::create([weakSelf, location]() mutable {
+            auto strongSelf = weakSelf.get();
+            if (!strongSelf)
+                return;
+
+            strongSelf->_contextMenuWasTriggeredByImageAnalysisTimeout = YES;
+            [strongSelf presentContextMenu:strongSelf->_contextMenuInteraction.get() atLocation:location];
+        });
+
         auto visualSearchAnalysisStartTime = MonotonicTime::now();
         auto requestForContextMenu = [strongSelf createImageAnalyzerRequest:VKAnalysisTypeVisualSearch | VKAnalysisTypeMachineReadableCode | VKAnalysisTypeAppClip image:cgImage.get()];
-        [[strongSelf imageAnalyzer] processRequest:requestForContextMenu.get() progressHandler:nil completionHandler:[weakSelf, location, visualSearchAnalysisStartTime] (CocoaImageAnalysis *result, NSError *error) {
+        [[strongSelf imageAnalyzer] processRequest:requestForContextMenu.get() progressHandler:nil completionHandler:[weakSelf, visualSearchAnalysisStartTime, aggregator = aggregator.copyRef()] (CocoaImageAnalysis *result, NSError *error) {
             auto strongSelf = weakSelf.get();
             if (!strongSelf)
                 return;
@@ -11013,16 +11041,19 @@
             UNUSED_PARAM(visualSearchAnalysisStartTime);
 #endif
 
-#if USE(UICONTEXTMENU)
-#if ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
+#if USE(UICONTEXTMENU) && ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
             [strongSelf _updateContextMenuForMachineReadableCodeForImageAnalysis:result];
-#endif // ENABLE(IMAGE_ANALYSIS_FOR_MACHINE_READABLE_CODES)
-            strongSelf->_contextMenuWasTriggeredByImageAnalysisTimeout = YES;
-            [strongSelf presentContextMenu:strongSelf->_contextMenuInteraction.get() atLocation:location];
-#else
-            UNUSED_PARAM(location);
-#endif // USE(UICONTEXTMENU)
+#endif
         }];
+
+#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
+        if (strongSelf->_page->preferences().imageAnalysisMarkupEnabled()) {
+            WebKit::requestImageAnalysisMarkup(cgImage.get(), [weakSelf, aggregator = aggregator.copyRef()](CGImageRef result, CGRect) mutable {
+                if (auto strongSelf = weakSelf.get())
+                    strongSelf->_croppedImageResult = result;
+            });
+        }
+#endif
     } forRequest:request];
 }
 
@@ -11037,30 +11068,21 @@
 
 - (BOOL)actionSheetAssistantShouldIncludeCopyCroppedImageAction:(WKActionSheetAssistant *)assistant
 {
-    return _page->preferences().imageAnalysisMarkupEnabled();
+    return !!_croppedImageResult;
 }
 
 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant copyCroppedImage:(UIImage *)image sourceMIMEType:(NSString *)sourceMIMEType
 {
-    auto changeCount = UIPasteboard.generalPasteboard.changeCount;
-    WebKit::requestImageAnalysisMarkup(image.CGImage, [changeCount, weakSelf = WeakObjCPtr<WKContentView>(self), originalImage = RetainPtr { image }, sourceMIMEType = RetainPtr { sourceMIMEType }](CGImageRef result, CGRect) mutable {
-        if (!result)
-            return;
+    if (!_croppedImageResult)
+        return;
 
-        auto strongSelf = weakSelf.get();
-        if (!strongSelf)
-            return;
+    auto [data, type] = WebKit::imageDataForCroppedImageResult(_croppedImageResult.get(), (__bridge CFStringRef)sourceMIMEType);
+    if (!data)
+        return;
 
-        auto [data, type] = WebKit::imageDataForCroppedImageResult(result, (__bridge CFStringRef)sourceMIMEType.get());
-        if (!data)
-            return;
-
-        [UIPasteboard _performAsDataOwner:[strongSelf _dataOwnerForCopy] block:[data = "" type = WTFMove(type), changeCount] {
-            auto pasteboard = UIPasteboard.generalPasteboard;
-            if (changeCount == pasteboard.changeCount)
-                [pasteboard setData:data.get() forPasteboardType:(__bridge NSString *)type.get()];
-        }];
-    });
+    [UIPasteboard _performAsDataOwner:self._dataOwnerForCopy block:[data = "" type = WTFMove(type)] {
+        [UIPasteboard.generalPasteboard setData:data.get() forPasteboardType:(__bridge NSString *)type.get()];
+    }];
 }
 
 - (void)installImageAnalysisInteraction:(VKCImageAnalysis *)analysis
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to