blob: 0c637c52c5f969f9a4fff01f90c7774eb22e1322 [file] [log] [blame] [edit]
/*
* Copyright (C) 2022 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_ENHANCEMENTS)
#import "ImageAnalysisTestingUtilities.h"
#import "InstanceMethodSwizzler.h"
#import "PlatformUtilities.h"
#import "Test.h"
#import "TestWKWebView.h"
#import "WKWebViewConfigurationExtras.h"
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/_WKFullscreenDelegate.h>
#import <pal/spi/cocoa/VisionKitCoreSPI.h>
#import <wtf/BlockPtr.h>
#import <wtf/RunLoop.h>
#import <wtf/darwin/DispatchExtras.h>
#import <pal/cocoa/VisionKitCoreSoftLink.h>
#if PLATFORM(IOS_FAMILY)
static void swizzledPresentViewController(UIViewController *, SEL, UIViewController *, BOOL, dispatch_block_t completion)
{
RunLoop::mainSingleton().dispatch([completion = makeBlockPtr(completion)] {
if (completion)
completion();
});
}
#endif // PLATFORM(IOS_FAMILY)
static int32_t swizzledProcessRequest(VKCImageAnalyzer *, SEL, id request, void (^)(double progress), void (^completion)(VKImageAnalysis *, NSError *))
{
dispatch_async(mainDispatchQueueSingleton(), [completion = makeBlockPtr(completion)] {
completion(TestWebKitAPI::createImageAnalysisWithSimpleFixedResults().get(), nil);
});
return 100;
}
static void swizzledSetAnalysis(VKCImageAnalysisInteraction *, SEL, VKCImageAnalysis *)
{
}
@interface FullscreenVideoTextRecognitionWebView : TestWKWebView
@end
@implementation FullscreenVideoTextRecognitionWebView {
std::unique_ptr<InstanceMethodSwizzler> _imageAnalysisRequestSwizzler;
#if PLATFORM(IOS_FAMILY)
std::unique_ptr<InstanceMethodSwizzler> _viewControllerPresentationSwizzler;
std::unique_ptr<InstanceMethodSwizzler> _imageAnalysisInteractionSwizzler;
#else
std::unique_ptr<InstanceMethodSwizzler> _imageAnalysisOverlaySwizzler;
#endif
bool _doneEnteringFullscreen;
bool _doneExitingFullscreen;
}
+ (RetainPtr<FullscreenVideoTextRecognitionWebView>)create
{
auto configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
configuration.preferences.elementFullscreenEnabled = YES;
auto webView = adoptNS([[FullscreenVideoTextRecognitionWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 568) configuration:configuration]);
[webView synchronouslyLoadTestPageNamed:@"element-fullscreen"];
return webView;
}
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
{
if (!(self = [super initWithFrame:frame configuration:configuration]))
return nil;
_imageAnalysisRequestSwizzler = WTF::makeUnique<InstanceMethodSwizzler>(
PAL::getVKImageAnalyzerClassSingleton(),
@selector(processRequest:progressHandler:completionHandler:),
reinterpret_cast<IMP>(swizzledProcessRequest)
);
#if PLATFORM(IOS_FAMILY)
_imageAnalysisInteractionSwizzler = WTF::makeUnique<InstanceMethodSwizzler>(
PAL::getVKCImageAnalysisInteractionClassSingleton(),
@selector(setAnalysis:),
reinterpret_cast<IMP>(swizzledSetAnalysis)
);
// Work around lack of a real UIApplication in TestWebKitAPIApp on iOS. Without this,
// -presentViewController:animated:completion: never calls the completion handler,
// which means we never transition into WKFullscreenStateInFullscreen.
_viewControllerPresentationSwizzler = WTF::makeUnique<InstanceMethodSwizzler>(
UIViewController.class,
@selector(presentViewController:animated:completion:),
reinterpret_cast<IMP>(swizzledPresentViewController)
);
#else
_imageAnalysisOverlaySwizzler = WTF::makeUnique<InstanceMethodSwizzler>(
PAL::getVKCImageAnalysisOverlayViewClassSingleton(),
@selector(setAnalysis:),
reinterpret_cast<IMP>(swizzledSetAnalysis)
);
#endif
return self;
}
- (void)loadVideoSource:(NSString *)source
{
__block bool done = false;
[self callAsyncJavaScript:@"loadSource(source)" arguments:@{ @"source" : source } inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id, NSError *error) {
EXPECT_NULL(error);
done = true;
}];
TestWebKitAPI::Util::run(&done);
[self waitForNextPresentationUpdate];
}
- (void)enterFullscreen
{
_doneEnteringFullscreen = false;
[self evaluateJavaScript:@"enterFullscreen()" completionHandler:nil];
TestWebKitAPI::Util::run(&_doneEnteringFullscreen);
[self waitForNextPresentationUpdate];
}
- (void)exitFullscreen
{
_doneExitingFullscreen = false;
[self evaluateJavaScript:@"exitFullscreen()" completionHandler:nil];
TestWebKitAPI::Util::run(&_doneExitingFullscreen);
[self waitForNextPresentationUpdate];
}
- (void)didChangeValueForKey:(NSString *)key
{
[super didChangeValueForKey:key];
if (![key isEqualToString:@"fullscreenState"])
return;
auto state = self.fullscreenState;
switch (state) {
case WKFullscreenStateNotInFullscreen:
_doneExitingFullscreen = true;
break;
case WKFullscreenStateInFullscreen:
_doneEnteringFullscreen = true;
break;
default:
break;
}
}
- (void)pause
{
[self objectByEvaluatingJavaScript:@"video.pause()"];
[self waitForNextPresentationUpdate];
}
- (void)play
{
__block bool done = false;
[self callAsyncJavaScript:@"video.play()" arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(id, NSError *error) {
EXPECT_NULL(error);
done = true;
}];
TestWebKitAPI::Util::run(&done);
[self waitForNextPresentationUpdate];
}
- (double)waitForVideoFrame
{
__block double result = 0;
__block bool done = false;
[self callAsyncJavaScript:@"waitForVideoFrame()" arguments:nil inFrame:nil inContentWorld:WKContentWorld.pageWorld completionHandler:^(NSNumber *timestamp, NSError *error) {
EXPECT_NULL(error);
result = timestamp.doubleValue;
done = true;
}];
TestWebKitAPI::Util::run(&done);
return result;
}
- (BOOL)hasActiveImageAnalysis
{
#if PLATFORM(IOS_FAMILY)
for (id<UIInteraction> interaction in self.textInputContentView.interactions) {
if ([interaction isKindOfClass:PAL::getVKCImageAnalysisInteractionClassSingleton()])
return YES;
}
#else
for (NSView *subview in self.subviews) {
if ([subview isKindOfClass:PAL::getVKCImageAnalysisOverlayViewClassSingleton()])
return YES;
}
#endif
return NO;
}
- (void)waitForImageAnalysisToBegin
{
TestWebKitAPI::Util::waitForConditionWithLogging([&] {
return self.hasActiveImageAnalysis;
}, 3, @"Expected image analysis to begin.");
}
- (void)waitForImageAnalysisToEnd
{
TestWebKitAPI::Util::waitForConditionWithLogging([&] {
return !self.hasActiveImageAnalysis;
}, 3, @"Expected image analysis to end.");
}
@end
namespace TestWebKitAPI {
// FIXME: Re-enable this test for iOS once webkit.org/b/248094 is resolved
// FIXME: Re-enable this test in Sonoma once webkit.org/b/289025 is resolved.
#if PLATFORM(IOS) || PLATFORM(VISION) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 140000 && __MAC_OS_X_VERSION_MIN_REQUIRED < 150000)
TEST(FullscreenVideoTextRecognition, DISABLED_TogglePlaybackInElementFullscreen)
#else
TEST(FullscreenVideoTextRecognition, TogglePlaybackInElementFullscreen)
#endif
{
auto webView = [FullscreenVideoTextRecognitionWebView create];
[webView loadVideoSource:@"test.mp4"];
[webView enterFullscreen];
[webView pause];
[webView waitForImageAnalysisToBegin];
[webView play];
[webView waitForImageAnalysisToEnd];
}
// FIXME: re-enable for iOS when rdar://107476837 is resolved
#if PLATFORM(IOS) || PLATFORM(VISION)
TEST(FullscreenVideoTextRecognition, DISABLED_AddVideoAfterEnteringFullscreen)
#else
TEST(FullscreenVideoTextRecognition, AddVideoAfterEnteringFullscreen)
#endif
{
auto webView = [FullscreenVideoTextRecognitionWebView create];
[webView loadVideoSource:@"test.mp4"];
[webView objectByEvaluatingJavaScript:@"video.remove()"];
[webView enterFullscreen];
[webView objectByEvaluatingJavaScript:@"container.appendChild(video); 0;"];
[webView play];
[webView waitForVideoFrame];
[webView pause];
[webView waitForImageAnalysisToBegin];
}
// FIXME: Re-enable this test for iOS once webkit.org/b/248094 is resolved
// FIXME: Re-enable this test once webkit.org/b/248093 is resolved.
// FIXME: Re-enable this test in Sonoma once webkit.org/b/289025 is resolved.
#if PLATFORM(IOS) || PLATFORM(VISION) || !defined(NDEBUG) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 140000 && __MAC_OS_X_VERSION_MIN_REQUIRED < 150000)
TEST(FullscreenVideoTextRecognition, DISABLED_DoNotAnalyzeVideoAfterExitingFullscreen)
#else
TEST(FullscreenVideoTextRecognition, DoNotAnalyzeVideoAfterExitingFullscreen)
#endif
{
auto webView = [FullscreenVideoTextRecognitionWebView create];
[webView loadVideoSource:@"test.mp4"];
[webView enterFullscreen];
[webView pause];
[webView waitForImageAnalysisToBegin];
[webView exitFullscreen];
[webView waitForImageAnalysisToEnd];
[webView play];
[webView pause];
bool doneWaiting = false;
RunLoop::mainSingleton().dispatchAfter(300_ms, [&] {
EXPECT_FALSE([webView hasActiveImageAnalysis]);
doneWaiting = true;
});
Util::run(&doneWaiting);
}
} // namespace TestWebKitAPI
#endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)