blob: f214b9b9c6344c8682e0a0924ee6e932a2c249b0 [file] [log] [blame]
/*
* Copyright (C) 2021 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"
#if ENABLE(IMAGE_ANALYSIS)
#import "ImageAnalysisTestingUtilities.h"
#import "InstanceMethodSwizzler.h"
#import "PlatformUtilities.h"
#import "Test.h"
#import "TestInputDelegate.h"
#import "TestUIMenuBuilder.h"
#import "TestWKWebView.h"
#import "UIKitSPI.h"
#import "WKWebViewConfigurationExtras.h"
#import <WebCore/Color.h>
#import <WebCore/LocalizedStrings.h>
#import <WebKit/WKPreferencesPrivate.h>
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/WKWebViewPrivateForTesting.h>
#import <WebKit/_WKInternalDebugFeature.h>
#import <pal/cocoa/VisionKitCoreSoftLink.h>
#import <pal/spi/cocoa/VisionKitCoreSPI.h>
static unsigned gDidProcessRequestCount = 0;
#if PLATFORM(IOS_FAMILY)
static CGPoint gSwizzledLocationInView = CGPointZero;
static CGPoint swizzledLocationInView(id, SEL, UIView *)
{
return gSwizzledLocationInView;
}
@interface UIView (ImageAnalysisTesting)
- (void)imageAnalysisGestureDidBegin:(UIGestureRecognizer *)gestureRecognizer;
@end
#endif // PLATFORM(IOS_FAMILY)
@interface VKImageAnalyzerRequest (TestSupport)
@property (nonatomic, readonly) CGImageRef image;
@end
@interface TestWKWebView (ImageAnalysisTests)
- (void)waitForImageAnalysisRequests:(unsigned)numberOfRequests;
#if PLATFORM(IOS_FAMILY)
- (unsigned)simulateImageAnalysisGesture:(CGPoint)location;
#endif
@end
@implementation TestWKWebView (ImageAnalysisTests)
- (void)waitForImageAnalysisRequests:(unsigned)numberOfRequests
{
TestWebKitAPI::Util::waitForConditionWithLogging([&] {
return gDidProcessRequestCount == numberOfRequests;
}, 3, @"Timed out waiting for %u image analysis requests to complete.", numberOfRequests);
[self waitForNextPresentationUpdate];
EXPECT_EQ(gDidProcessRequestCount, numberOfRequests);
}
#if PLATFORM(IOS_FAMILY)
- (unsigned)simulateImageAnalysisGesture:(CGPoint)location
{
auto numberOfRequestsAtStart = gDidProcessRequestCount;
gSwizzledLocationInView = location;
InstanceMethodSwizzler gestureLocationSwizzler { UILongPressGestureRecognizer.class, @selector(locationInView:), reinterpret_cast<IMP>(swizzledLocationInView) };
[self.textInputContentView imageAnalysisGestureDidBegin:self._imageAnalysisGestureRecognizer];
// The process of image analysis involves at most 2 round trips to the web process.
[self waitForNextPresentationUpdate];
[self waitForNextPresentationUpdate];
return gDidProcessRequestCount - numberOfRequestsAtStart;
}
#endif // PLATFORM(IOS_FAMILY)
@end
namespace TestWebKitAPI {
// FIXME: We can unify most of this helper class with the logic in `TestPDFPage::colorAtPoint`, and deploy this
// helper class in several other tests that read pixel data from CGImages.
class CGImagePixelReader {
WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(CGImagePixelReader);
public:
CGImagePixelReader(CGImageRef image)
: m_width(CGImageGetWidth(image))
, m_height(CGImageGetHeight(image))
{
auto colorSpace = adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceSRGB));
auto bytesPerPixel = 4;
auto bytesPerRow = bytesPerPixel * CGImageGetWidth(image);
auto bitsPerComponent = 8;
auto bitmapInfo = kCGImageAlphaPremultipliedLast | kCGImageByteOrder32Big;
m_context = adoptCF(CGBitmapContextCreateWithData(nullptr, m_width, m_height, bitsPerComponent, bytesPerRow, colorSpace.get(), bitmapInfo, nullptr, nullptr));
CGContextDrawImage(m_context.get(), CGRectMake(0, 0, m_width, m_height), image);
}
bool isTransparentBlack(unsigned x, unsigned y) const
{
return at(x, y) == WebCore::Color::transparentBlack;
}
WebCore::Color at(unsigned x, unsigned y) const
{
auto* data = reinterpret_cast<uint8_t*>(CGBitmapContextGetData(m_context.get()));
auto offset = 4 * (width() * y + x);
return WebCore::makeFromComponentsClampingExceptAlpha<WebCore::SRGBA<uint8_t>>(data[offset], data[offset + 1], data[offset + 2], data[offset + 3]);
}
unsigned width() const { return m_width; }
unsigned height() const { return m_height; }
private:
unsigned m_width { 0 };
unsigned m_height { 0 };
RetainPtr<CGContextRef> m_context;
};
static Vector<RetainPtr<VKImageAnalyzerRequest>>& processedRequests()
{
static NeverDestroyed requests = Vector<RetainPtr<VKImageAnalyzerRequest>> { };
return requests.get();
}
static RetainPtr<TestWKWebView> createWebViewWithTextRecognitionEnhancements()
{
RetainPtr configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
for (_WKInternalDebugFeature *feature in WKPreferences._internalDebugFeatures) {
NSString *key = feature.key;
if ([key isEqualToString:@"TextRecognitionInVideosEnabled"] || [key isEqualToString:@"VisualTranslationEnabled"] || [key isEqualToString:@"RemoveBackgroundEnabled"])
[[configuration preferences] _setEnabled:YES forInternalDebugFeature:feature];
}
[configuration _setAttachmentElementEnabled:YES];
#if ENABLE(SERVICE_CONTROLS)
[configuration _setImageControlsEnabled:YES];
#endif
return adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get()]);
}
static void processRequestWithError(id, SEL, VKImageAnalyzerRequest *request, void (^)(double progress), void (^completion)(VKImageAnalysis *analysis, NSError *error))
{
gDidProcessRequestCount++;
processedRequests().append({ request });
completion(nil, [NSError errorWithDomain:NSCocoaErrorDomain code:1 userInfo:nil]);
}
static void processRequestWithResults(id, SEL, VKImageAnalyzerRequest *request, void (^)(double progress), void (^completion)(VKImageAnalysis *, NSError *))
{
gDidProcessRequestCount++;
processedRequests().append({ request });
completion(createImageAnalysisWithSimpleFixedResults().get(), nil);
}
static VKImageAnalyzerRequest *makeFakeRequest(id, SEL, CGImageRef image, VKImageOrientation orientation, VKAnalysisTypes requestTypes)
{
return createRequest(image, orientation, requestTypes).leakRef();
}
template <typename FunctionType>
std::pair<std::unique_ptr<InstanceMethodSwizzler>, std::unique_ptr<InstanceMethodSwizzler>> makeImageAnalysisRequestSwizzler(FunctionType function)
{
return std::pair {
makeUnique<InstanceMethodSwizzler>(PAL::getVKImageAnalyzerClass(), @selector(processRequest:progressHandler:completionHandler:), reinterpret_cast<IMP>(function)),
makeUnique<InstanceMethodSwizzler>(PAL::getVKImageAnalyzerRequestClass(), @selector(initWithCGImage:orientation:requestType:), reinterpret_cast<IMP>(makeFakeRequest))
};
}
#if PLATFORM(IOS_FAMILY)
TEST(ImageAnalysisTests, DoNotAnalyzeImagesInEditableContent)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithError);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
[webView _setEditable:YES];
[webView synchronouslyLoadTestPageNamed:@"image"];
EXPECT_EQ([webView simulateImageAnalysisGesture:CGPointMake(100, 100)], 0U);
}
TEST(ImageAnalysisTests, HandleImageAnalyzerErrors)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithError);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
[webView synchronouslyLoadTestPageNamed:@"image"];
EXPECT_EQ([webView simulateImageAnalysisGesture:CGPointMake(100, 100)], 2U);
}
TEST(ImageAnalysisTests, DoNotCrashWhenHitTestingOutsideOfWebView)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithError);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
[webView synchronouslyLoadTestPageNamed:@"image"];
EXPECT_EQ([webView simulateImageAnalysisGesture:CGPointMake(500, 500)], 0U);
[webView expectElementCount:1 querySelector:@"img"];
}
TEST(ImageAnalysisTests, AvoidRedundantTextRecognitionRequests)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithResults);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"image"];
EXPECT_EQ([webView simulateImageAnalysisGesture:CGPointMake(150, 100)], 1U);
// FIXME: If we cache visual look up results as well in the future, we can bring this down to 0 (that is, no new requests).
EXPECT_LT([webView simulateImageAnalysisGesture:CGPointMake(150, 250)], 2U);
}
#endif // PLATFORM(IOS_FAMILY)
TEST(ImageAnalysisTests, StartImageAnalysisWithoutIdentifier)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithResults);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"multiple-images"];
[webView _startImageAnalysis:nil target:nil];
[webView waitForImageAnalysisRequests:5];
NSArray<NSString *> *overlaysAsText = [webView objectByEvaluatingJavaScript:@"imageOverlaysAsText()"];
EXPECT_EQ(overlaysAsText.count, 5U);
for (NSString *overlayText in overlaysAsText)
EXPECT_WK_STREQ(overlayText, @"Foo bar");
}
TEST(ImageAnalysisTests, AnalyzeImagesInSubframes)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithResults);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"multiple-images"];
__block bool doneInsertingFrame = false;
[webView callAsyncJavaScript:@"appendAndLoadSubframe(source)" arguments:@{ @"source" : @"multiple-images.html" } inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id, NSError *error) {
EXPECT_NULL(error);
doneInsertingFrame = true;
}];
Util::run(&doneInsertingFrame);
[webView _startImageAnalysis:nil target:nil];
[webView waitForImageAnalysisRequests:10];
}
TEST(ImageAnalysisTests, AnalyzeImageAfterChangingSource)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithResults);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"image"];
[webView _startImageAnalysis:nil target:nil];
[webView waitForImageAnalysisRequests:1];
[webView objectByEvaluatingJavaScript:@"document.querySelector('img').src = 'icon.png'"];
[webView waitForImageAnalysisRequests:2];
}
TEST(ImageAnalysisTests, AnalyzeDynamicallyLoadedImages)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithResults);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"multiple-images"];
[webView _startImageAnalysis:nil target:nil];
[webView waitForImageAnalysisRequests:5];
[webView objectByEvaluatingJavaScript:@"appendImage('apple.gif')"];
[webView waitForImageAnalysisRequests:6];
[webView objectByEvaluatingJavaScript:@"appendImage('icon.png')"];
[webView waitForImageAnalysisRequests:7];
[webView objectByEvaluatingJavaScript:@"hideAllImages()"];
[webView waitForNextPresentationUpdate];
[webView objectByEvaluatingJavaScript:@"showAllImages()"];
[webView waitForNextPresentationUpdate];
EXPECT_EQ(gDidProcessRequestCount, 7U);
}
TEST(ImageAnalysisTests, ResetImageAnalysisAfterNavigation)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithResults);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"multiple-images"];
[webView _startImageAnalysis:nil target:nil];
[webView waitForImageAnalysisRequests:5];
[webView synchronouslyLoadTestPageNamed:@"simple"];
[webView waitForNextPresentationUpdate];
[webView synchronouslyLoadTestPageNamed:@"multiple-images"];
[webView waitForNextPresentationUpdate];
EXPECT_EQ(gDidProcessRequestCount, 5U);
}
TEST(ImageAnalysisTests, ImageAnalysisPrioritizesVisibleImages)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithResults);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"offscreen-image"];
[webView _startImageAnalysis:nil target:nil];
[webView waitForImageAnalysisRequests:2];
auto firstRequestedImage = [processedRequests().first() image];
auto lastRequestedImage = [processedRequests().last() image];
EXPECT_EQ(200U, CGImageGetWidth(firstRequestedImage));
EXPECT_EQ(150U, CGImageGetHeight(firstRequestedImage));
EXPECT_EQ(600U, CGImageGetWidth(lastRequestedImage));
EXPECT_EQ(450U, CGImageGetHeight(lastRequestedImage));
}
TEST(ImageAnalysisTests, ImageAnalysisWithTransparentImages)
{
auto requestSwizzler = makeImageAnalysisRequestSwizzler(processRequestWithError);
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView synchronouslyLoadTestPageNamed:@"fade-in-image"];
[webView _startImageAnalysis:@"en_US" target:@"zh_TW"];
[webView waitForImageAnalysisRequests:1];
CGImagePixelReader reader { [processedRequests().first() image] };
EXPECT_TRUE(reader.isTransparentBlack(1, 1));
EXPECT_TRUE(reader.isTransparentBlack(reader.width() - 1, 1));
EXPECT_TRUE(reader.isTransparentBlack(reader.width() - 1, reader.height() - 1));
EXPECT_TRUE(reader.isTransparentBlack(1, reader.height() - 1));
EXPECT_FALSE(reader.isTransparentBlack(reader.width() / 2, reader.height() / 2));
}
#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
static RetainPtr<CGImageRef> iconImage()
{
auto iconPath = [NSBundle.mainBundle pathForResource:@"icon" ofType:@"png" inDirectory:@"TestWebKitAPI.resources"];
#if PLATFORM(IOS_FAMILY)
return [UIImage imageWithContentsOfFile:iconPath].CGImage;
#else
auto image = adoptNS([[NSImage alloc] initWithContentsOfFile:iconPath]);
return [image CGImageForProposedRect:nil context:nil hints:nil];
#endif
}
#endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) && PLATFORM(IOS_FAMILY)
static void simulateCalloutBarAppearance(TestWKWebView *webView)
{
__block bool done = false;
[webView.textInputContentView requestPreferredArrowDirectionForEditMenuWithCompletionHandler:^(UIEditMenuArrowDirection) {
done = true;
}];
Util::run(&done);
}
static BOOL simulateEditContextMenuAppearance(TestWKWebView *webView, CGPoint location)
{
__block BOOL result;
__block bool done = false;
[webView.textInputContentView prepareSelectionForContextMenuWithLocationInView:location completionHandler:^(BOOL shouldPresent, RVItem *) {
result = shouldPresent;
done = true;
}];
Util::run(&done);
return result;
}
static void invokeRemoveBackgroundAction(TestWKWebView *webView)
{
simulateCalloutBarAppearance(webView);
auto menuBuilder = adoptNS([[TestUIMenuBuilder alloc] init]);
[webView buildMenuWithBuilder:menuBuilder.get()];
[[menuBuilder actionWithTitle:WebCore::contextMenuItemTitleRemoveBackground()] _performActionWithSender:nil];
[webView waitForNextPresentationUpdate];
}
TEST(ImageAnalysisTests, RemoveBackgroundUsingContextMenu)
{
RemoveBackgroundSwizzler swizzler { iconImage().autorelease(), CGRectMake(10, 10, 215, 174) };
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView _setEditable:YES];
[webView synchronouslyLoadTestPageNamed:@"image"];
[webView waitForNextPresentationUpdate];
EXPECT_TRUE(simulateEditContextMenuAppearance(webView.get(), CGPointMake(100, 100)));
auto menuBuilder = adoptNS([[TestUIMenuBuilder alloc] init]);
[webView buildMenuWithBuilder:menuBuilder.get()];
EXPECT_NOT_NULL([menuBuilder actionWithTitle:WebCore::contextMenuItemTitleRemoveBackground()]);
}
TEST(ImageAnalysisTests, MenuControllerItems)
{
RemoveBackgroundSwizzler swizzler { iconImage().autorelease(), CGRectMake(10, 10, 215, 174) };
auto webView = createWebViewWithTextRecognitionEnhancements();
auto inputDelegate = adoptNS([TestInputDelegate new]);
[inputDelegate setFocusStartsInputSessionPolicyHandler:[](WKWebView *, id <_WKFocusedElementInfo>) {
return _WKFocusStartsInputSessionPolicyAllow;
}];
[webView _setEditable:YES];
[webView _setInputDelegate:inputDelegate.get()];
[webView synchronouslyLoadTestPageNamed:@"multiple-images"];
[webView objectByEvaluatingJavaScript:@"let image = document.images[0]; getSelection().setBaseAndExtent(image, 0, image, 1);"];
[webView waitForNextPresentationUpdate];
simulateCalloutBarAppearance(webView.get());
auto menuBuilder = adoptNS([[TestUIMenuBuilder alloc] init]);
[webView buildMenuWithBuilder:menuBuilder.get()];
EXPECT_NOT_NULL([menuBuilder actionWithTitle:WebCore::contextMenuItemTitleRemoveBackground()]);
[webView selectAll:nil];
[webView waitForNextPresentationUpdate];
simulateCalloutBarAppearance(webView.get());
[menuBuilder reset];
[webView buildMenuWithBuilder:menuBuilder.get()];
EXPECT_NULL([menuBuilder actionWithTitle:WebCore::contextMenuItemTitleRemoveBackground()]);
[webView objectByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(document.body, 0, document.images[0], 1);"];
[webView waitForNextPresentationUpdate];
simulateCalloutBarAppearance(webView.get());
[menuBuilder reset];
[webView buildMenuWithBuilder:menuBuilder.get()];
EXPECT_NOT_NULL([menuBuilder actionWithTitle:WebCore::contextMenuItemTitleRemoveBackground()]);
}
static RetainPtr<TestWKWebView> runMarkupTest(NSString *testPage, NSString *scriptToSelectText, Function<void(TestWKWebView *, NSString *)>&& checkWebView = { })
{
RemoveBackgroundSwizzler swizzler { iconImage().autorelease(), CGRectMake(10, 10, 215, 174) };
auto webView = createWebViewWithTextRecognitionEnhancements();
auto inputDelegate = adoptNS([TestInputDelegate new]);
[inputDelegate setFocusStartsInputSessionPolicyHandler:[](WKWebView *, id <_WKFocusedElementInfo>) {
return _WKFocusStartsInputSessionPolicyAllow;
}];
[webView _setEditable:YES];
[webView _setInputDelegate:inputDelegate.get()];
[webView synchronouslyLoadTestPageNamed:testPage];
[webView objectByEvaluatingJavaScript:scriptToSelectText];
[webView waitForNextPresentationUpdate];
NSString *previousSelectedText = [webView selectedText];
invokeRemoveBackgroundAction(webView.get());
if (checkWebView)
checkWebView(webView.get(), previousSelectedText);
return webView;
}
TEST(ImageAnalysisTests, PerformRemoveBackground)
{
runMarkupTest(@"multiple-images", @"getSelection().setBaseAndExtent(document.body, 0, document.images[0], 1)", [](TestWKWebView *webView, NSString *previousSelectedText) {
EXPECT_WK_STREQ(previousSelectedText, webView.selectedText);
Util::waitForConditionWithLogging([&] {
return [[webView objectByEvaluatingJavaScript:@"document.images[0].getBoundingClientRect().width"] intValue] == 215;
}, 3, @"Expected bounding client rect to become 215.");
NSString *undoTitle = webView.undoManager.undoMenuItemTitle;
EXPECT_GT(undoTitle.length, 0U);
EXPECT_FALSE([undoTitle containsString:@"Paste"]);
});
}
TEST(ImageAnalysisTests, PerformRemoveBackgroundWithWebPImages)
{
runMarkupTest(@"webp-image", @"getSelection().selectAllChildren(document.body)", [](TestWKWebView *webView, NSString *) {
Util::waitForConditionWithLogging([&] {
return [[webView objectByEvaluatingJavaScript:@"document.images[0].getBoundingClientRect().width"] intValue] == 215;
}, 3, @"Expected bounding client rect to become 215.");
});
}
TEST(ImageAnalysisTests, AllowRemoveBackgroundOnce)
{
auto webView = runMarkupTest(@"image", @"getSelection().selectAllChildren(document.body)");
RemoveBackgroundSwizzler swizzler { iconImage().autorelease(), CGRectMake(10, 10, 215, 174) };
[webView objectByEvaluatingJavaScript:@"let image = document.images[0]; getSelection().setBaseAndExtent(image, 0, image, 1)"];
[webView waitForNextPresentationUpdate];
simulateCalloutBarAppearance(webView.get());
auto menuBuilder = adoptNS([TestUIMenuBuilder new]);
[webView buildMenuWithBuilder:menuBuilder.get()];
EXPECT_NULL([menuBuilder actionWithTitle:WebCore::contextMenuItemTitleRemoveBackground()]);
}
#endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) && PLATFORM(IOS_FAMILY)
#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) && ENABLE(SERVICE_CONTROLS)
TEST(ImageAnalysisTests, RemoveBackgroundItemInServicesMenu)
{
RemoveBackgroundSwizzler swizzler { iconImage().autorelease(), CGRectMake(10, 10, 215, 174) };
auto webView = createWebViewWithTextRecognitionEnhancements();
[webView _setEditable:YES];
[webView synchronouslyLoadTestPageNamed:@"image-controls"];
[[webView window] orderFrontRegardless];
[webView callAsyncJavaScript:@"waitForAndClickImageControls()" arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id, NSError *error) {
EXPECT_NULL(error);
}];
__block bool foundRemoveBackgroundItem = false;
RetainPtr timer = [NSTimer timerWithTimeInterval:0.1 repeats:YES block:^(NSTimer *) {
NSMenu *menu = [webView _activeMenu];
for (NSMenuItem *item in menu.itemArray) {
if ([item.title isEqualToString:WebCore::contextMenuItemTitleRemoveBackground()]) {
foundRemoveBackgroundItem = true;
[menu cancelTracking];
break;
}
}
}];
[NSRunLoop.mainRunLoop addTimer:timer.get() forMode:NSEventTrackingRunLoopMode];
Util::run(&foundRemoveBackgroundItem);
}
#endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS) && ENABLE(SERVICE_CONTROLS)
} // namespace TestWebKitAPI
#endif // ENABLE(IMAGE_ANALYSIS)