blob: 9c817c508b0418462dae8a72afe4cd4cef1197bc [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 PLATFORM(MAC)
#import "AppKitSPI.h"
#import "InstanceMethodSwizzler.h"
#import "PasteboardUtilities.h"
#import "PlatformUtilities.h"
#import "Test.h"
#import "TestNavigationDelegate.h"
#import "TestUIDelegate.h"
#import "TestWKWebView.h"
#import "Utilities.h"
#import <WebKit/WKMenuItemIdentifiersPrivate.h>
#import <WebKit/WKUIDelegatePrivate.h>
#import <WebKit/WKWebViewConfigurationPrivate.h>
#import <WebKit/WKWebViewPrivateForTesting.h>
#import <WebKit/_WKContextMenuElementInfo.h>
#import <WebKit/_WKHitTestResult.h>
#import <wtf/BlockPtr.h>
@interface PopoverNotificationListener : NSObject
- (instancetype)initWithCallback:(Function<void(NSNotification *)>&&)callback;
@end
@implementation PopoverNotificationListener {
Function<void(NSNotification *)> _callback;
}
- (instancetype)initWithCallback:(Function<void(NSNotification *)>&&)callback
{
if (!(self = [super init]))
return nil;
auto *notificationCenter = NSNotificationCenter.defaultCenter;
[notificationCenter addObserver:self selector:@selector(handleNotification:) name:NSPopoverWillShowNotification object:nil];
[notificationCenter addObserver:self selector:@selector(handleNotification:) name:NSPopoverDidShowNotification object:nil];
[notificationCenter addObserver:self selector:@selector(handleNotification:) name:NSPopoverWillCloseNotification object:nil];
[notificationCenter addObserver:self selector:@selector(handleNotification:) name:NSPopoverDidCloseNotification object:nil];
_callback = WTF::move(callback);
return self;
}
- (void)dealloc
{
[NSNotificationCenter.defaultCenter removeObserver:self];
[super dealloc];
}
- (void)handleNotification:(NSNotification *)notification
{
if (_callback)
_callback(notification);
}
@end
@interface NSMenu (ContextMenuTests)
- (NSMenuItem *)itemWithIdentifier:(NSString *)identifier;
@end
@implementation NSMenu (ContextMenuTests)
- (NSMenuItem *)itemWithIdentifier:(NSString *)identifier
{
for (NSInteger index = 0; index < self.numberOfItems; ++index) {
NSMenuItem *item = [self itemAtIndex:index];
if ([item.identifier isEqualToString:identifier])
return item;
}
return nil;
}
@end
static RetainPtr<NSArray> lastSharingServicePickerItems;
@implementation NSSharingServicePicker (ContextMenuTests)
- (instancetype)swizzled_initWithItems:(NSArray *)items
{
lastSharingServicePickerItems = items;
return [self swizzled_initWithItems:items];
}
@end
namespace TestWebKitAPI {
TEST(ContextMenuTests, ProposedMenuContainsSpellingMenu)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block RetainPtr<NSMenu> proposedMenu;
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
proposedMenu = menu;
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView _setEditable:YES];
[webView synchronouslyLoadTestPageNamed:@"simple"];
[webView mouseDownAtPoint:NSMakePoint(10, 10) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(10, 10) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
NSMenuItem *spellingMenuItem = [proposedMenu itemWithIdentifier:_WKMenuItemIdentifierSpellingMenu];
NSMenu *spellingSubmenu = spellingMenuItem.submenu;
EXPECT_NOT_NULL(spellingMenuItem);
EXPECT_NOT_NULL([spellingSubmenu itemWithIdentifier:_WKMenuItemIdentifierShowSpellingPanel]);
EXPECT_NOT_NULL([spellingSubmenu itemWithIdentifier:_WKMenuItemIdentifierCheckSpelling]);
EXPECT_NOT_NULL([spellingSubmenu itemWithIdentifier:_WKMenuItemIdentifierCheckSpellingWhileTyping]);
EXPECT_NOT_NULL([spellingSubmenu itemWithIdentifier:_WKMenuItemIdentifierCheckGrammarWithSpelling]);
}
TEST(ContextMenuTests, NavigationTypeWhenOpeningLink)
{
auto navigationDelegate = adoptNS([[TestNavigationDelegate alloc] init]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setNavigationDelegate:navigationDelegate.get()];
[webView loadHTMLString:@"<a href='simple.html' style='font-size: 100px;'>Hello world</a>" baseURL:NSBundle.test_resourcesBundle.resourceURL];
[navigationDelegate waitForDidFinishNavigation];
__block bool didDecideNavigationPolicy = false;
[navigationDelegate setDecidePolicyForNavigationAction:^(WKNavigationAction *action, void (^decisionHandler)(WKNavigationActionPolicy)) {
EXPECT_EQ(action.navigationType, WKNavigationTypeLinkActivated);
EXPECT_WK_STREQ("simple.html", action.request.URL.lastPathComponent);
decisionHandler(WKNavigationActionPolicyAllow);
didDecideNavigationPolicy = true;
}];
[webView rightClick:NSMakePoint(50, 350) andSelectItemMatching:^BOOL(NSMenuItem *item) {
return [item.identifier isEqualToString:_WKMenuItemIdentifierOpenLink];
}];
Util::run(&didDecideNavigationPolicy);
}
static bool calledOrderFrontColorPanel = false;
static void swizzledOrderFrontColorPanel(id, SEL, id)
{
calledOrderFrontColorPanel = true;
}
TEST(ContextMenuTests, ShowColorPanel)
{
InstanceMethodSwizzler swizzler { NSApplication.class, @selector(orderFrontColorPanel:), reinterpret_cast<IMP>(swizzledOrderFrontColorPanel) };
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView _setEditable:YES];
[webView synchronouslyLoadTestPageNamed:@"simple"];
[webView rightClick:NSMakePoint(16, 16) andSelectItemMatching:^BOOL(NSMenuItem *item) {
return [item.title isEqualToString:@"Show Colors"];
}];
Util::run(&calledOrderFrontColorPanel);
}
TEST(ContextMenuTests, ShowSharePopoverForImage)
{
bool didShowPopover = false;
RetainPtr listener = adoptNS([[PopoverNotificationListener alloc] initWithCallback:[&](NSNotification *notification) {
if ([notification.name isEqualToString:NSPopoverDidShowNotification])
didShowPopover = true;
}]);
RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
auto presentingViewSwizzler = InstanceMethodSwizzler {
NSMenu.class,
@selector(_presentingView),
imp_implementationWithBlock([&](NSMenu *) {
return webView.get();
})
};
auto originalMethod = class_getInstanceMethod(NSSharingServicePicker.class, @selector(initWithItems:));
auto swizzledMethod = class_getInstanceMethod(NSSharingServicePicker.class, @selector(swizzled_initWithItems:));
auto originalImplementation = method_getImplementation(originalMethod);
auto swizzledImplementation = method_getImplementation(swizzledMethod);
class_replaceMethod(NSSharingServicePicker.class, @selector(swizzled_initWithItems:), originalImplementation, method_getTypeEncoding(originalMethod));
class_replaceMethod(NSSharingServicePicker.class, @selector(initWithItems:), swizzledImplementation, method_getTypeEncoding(swizzledMethod));
[webView synchronouslyLoadHTMLString:@"<img src='sunset-in-cupertino-600px.jpg'></img>"];
[webView waitForNextPresentationUpdate];
[webView rightClick:NSMakePoint(200, 200) andSelectItemMatching:^BOOL(NSMenuItem *item) {
return [item.title containsString:@"Share"];
}];
TestWebKitAPI::Util::run(&didShowPopover);
EXPECT_EQ([lastSharingServicePickerItems count], 1U);
RetainPtr<NSPreviewRepresentingActivityItem> activityItem = [lastSharingServicePickerItems firstObject];
EXPECT_TRUE([activityItem isKindOfClass:NSPreviewRepresentingActivityItem.class]);
RetainPtr<NSImage> image = [activityItem item];
EXPECT_TRUE([image isKindOfClass:NSImage.class]);
EXPECT_TRUE(NSEqualSizes([image size], NSMakeSize(600, 450)));
class_replaceMethod(NSSharingServicePicker.class, @selector(swizzled_initWithItems:), swizzledImplementation, method_getTypeEncoding(originalMethod));
class_replaceMethod(NSSharingServicePicker.class, @selector(initWithItems:), originalImplementation, method_getTypeEncoding(swizzledMethod));
lastSharingServicePickerItems = nil;
}
TEST(ContextMenuTests, SharePopoverDoesNotClearSelection)
{
bool didShowPopover = false;
auto listener = adoptNS([[PopoverNotificationListener alloc] initWithCallback:[&](NSNotification *notification) {
if ([notification.name isEqualToString:NSPopoverDidShowNotification])
didShowPopover = true;
}]);
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
auto presentingViewSwizzler = InstanceMethodSwizzler {
NSMenu.class,
@selector(_presentingView),
imp_implementationWithBlock([&](NSMenu *) {
return webView.get();
})
};
[webView setForceWindowToBecomeKey:YES];
[webView synchronouslyLoadHTMLString:@"<body style='font-size: 100px;'>Hello world this is a test</body>"];
[[webView window] makeFirstResponder:webView.get()];
[webView selectAll:nil];
[webView waitForNextPresentationUpdate];
[webView rightClick:NSMakePoint(100, 100) andSelectItemMatching:^BOOL(NSMenuItem *item) {
return [item.title containsString:@"Share"];
}];
TestWebKitAPI::Util::run(&didShowPopover);
EXPECT_WK_STREQ("Hello world this is a test", [webView selectedText]);
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsHitTestResult)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView _setEditable:YES];
[webView synchronouslyLoadTestPageNamed:@"simple"];
[webView mouseDownAtPoint:NSMakePoint(10, 10) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(10, 10) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultDoesNotContainEmptyURLs)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
_WKHitTestResult *hitTestResult = elementInfo.hitTestResult;
EXPECT_NOT_NULL(hitTestResult);
// simple.html does not contain any links, so every URL in _WKHitTestResult should be nil.
EXPECT_NULL(hitTestResult.absoluteImageURL);
EXPECT_NULL(hitTestResult.absolutePDFURL);
EXPECT_NULL(hitTestResult.absoluteLinkURL);
EXPECT_NULL(hitTestResult.absoluteMediaURL);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView _setEditable:YES];
[webView synchronouslyLoadTestPageNamed:@"simple"];
[webView mouseDownAtPoint:NSMakePoint(10, 10) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(10, 10) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
#if ENABLE(CONTEXT_MENU_QR_CODE_DETECTION)
static NSString *qrCodeSVGString()
{
return @"<svg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 1160 1160'><path fill='#fff' d='M0 0h1160v1160H0z'/><path d='M400 80h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M600 80h40v40h-40zm-200 40h40v40h-40z'/><path d='M440 120h40v40h-40zm80 0h40v40h-40z'/><path d='M560 120h40v40h-40z'/><path d='M600 120h40v40h-40zm120 0h40v40h-40zm-320 40h40v40h-40z'/><path d='M440 160h40v40h-40zm120 0h40v40h-40z'/><path d='M600 160h40v40h-40zm80 0h40v40h-40z'/><path d='M720 160h40v40h-40zm-280 40h40v40h-40z'/><path d='M480 200h40v40h-40z'/><path d='M520 200h40v40h-40zm80 0h40v40h-40z'/><path d='M640 200h40v40h-40z'/><path d='M680 200h40v40h-40z'/><path d='M720 200h40v40h-40zm-320 40h40v40h-40z'/><path d='M440 240h40v40h-40z'/><path d='M480 240h40v40h-40zm120 0h40v40h-40z'/><path d='M640 240h40v40h-40zm80 0h40v40h-40zm-280 40h40v40h-40z'/><path d='M480 280h40v40h-40zm120 0h40v40h-40z'/><path d='M640 280h40v40h-40zm-240 40h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M640 320h40v40h-40zm80 0h40v40h-40zm-240 40h40v40h-40z'/><path d='M520 360h40v40h-40z'/><path d='M560 360h40v40h-40z'/><path d='M600 360h40v40h-40z'/><path d='M640 360h40v40h-40z'/><path d='M680 360h40v40h-40zM80 400h40v40H80zm120 0h40v40h-40z'/><path d='M240 400h40v40h-40z'/><path d='M280 400h40v40h-40z'/><path d='M320 400h40v40h-40z'/><path d='M360 400h40v40h-40z'/><path d='M400 400h40v40h-40zm80 0h40v40h-40z'/><path d='M520 400h40v40h-40z'/><path d='M560 400h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M760 400h40v40h-40zm120 0h40v40h-40zm80 0h40v40h-40z'/><path d='M1000 400h40v40h-40z'/><path d='M1040 400h40v40h-40zM80 440h40v40H80z'/><path d='M120 440h40v40h-40zm80 0h40v40h-40zm200 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M600 440h40v40h-40z'/><path d='M640 440h40v40h-40z'/><path d='M680 440h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M880 440h40v40h-40z'/><path d='M920 440h40v40h-40z'/><path d='M960 440h40v40h-40z'/><path d='M1000 440h40v40h-40zm-720 40h40v40h-40z'/><path d='M320 480h40v40h-40z'/><path d='M360 480h40v40h-40zm160 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40zm120 0h40v40h-40z'/><path d='M840 480h40v40h-40zm80 0h40v40h-40zm120 0h40v40h-40zm-840 40h40v40h-40zm200 0h40v40h-40zm240 0h40v40h-40z'/><path d='M680 520h40v40h-40z'/><path d='M720 520h40v40h-40zm120 0h40v40h-40zm80 0h40v40h-40z'/><path d='M960 520h40v40h-40z'/><path d='M1000 520h40v40h-40z'/><path d='M1040 520h40v40h-40zm-920 40h40v40h-40zm80 0h40v40h-40zm120 0h40v40h-40zm200 0h40v40h-40zm160 0h40v40h-40z'/><path d='M720 560h40v40h-40zm80 0h40v40h-40zm240 0h40v40h-40zM80 600h40v40H80z'/><path d='M120 600h40v40h-40zm280 0h40v40h-40z'/><path d='M440 600h40v40h-40zm120 0h40v40h-40z'/><path d='M600 600h40v40h-40zm80 0h40v40h-40z'/><path d='M720 600h40v40h-40z'/><path d='M760 600h40v40h-40zm120 0h40v40h-40zm120 0h40v40h-40zM80 640h40v40H80z'/><path d='M120 640h40v40h-40zm200 0h40v40h-40zm360 0h40v40h-40zm80 0h40v40h-40z'/><path d='M800 640h40v40h-40zm80 0h40v40h-40z'/><path d='M920 640h40v40h-40z'/><path d='M960 640h40v40h-40z'/><path d='M1000 640h40v40h-40z'/><path d='M1040 640h40v40h-40zM80 680h40v40H80zm80 0h40v40h-40z'/><path d='M200 680h40v40h-40zm160 0h40v40h-40zm80 0h40v40h-40zm120 0h40v40h-40zm80 0h40v40h-40zm120 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M960 680h40v40h-40zm80 0h40v40h-40zM80 720h40v40H80zm80 0h40v40h-40zm120 0h40v40h-40z'/><path d='M320 720h40v40h-40z'/><path d='M360 720h40v40h-40zm80 0h40v40h-40z'/><path d='M480 720h40v40h-40z'/><path d='M520 720h40v40h-40z'/><path d='M560 720h40v40h-40z'/><path d='M600 720h40v40h-40zm80 0h40v40h-40z'/><path d='M720 720h40v40h-40z'/><path d='M760 720h40v40h-40z'/><path d='M800 720h40v40h-40z'/><path d='M840 720h40v40h-40z'/><path d='M880 720h40v40h-40zm80 0h40v40h-40z'/><path d='M1000 720h40v40h-40zm-600 40h40v40h-40z'/><path d='M440 760h40v40h-40zm80 0h40v40h-40zm200 0h40v40h-40zm160 0h40v40h-40zm80 0h40v40h-40z'/><path d='M1000 760h40v40h-40zm-600 40h40v40h-40zm160 0h40v40h-40zm160 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M1040 800h40v40h-40zm-640 40h40v40h-40z'/><path d='M440 840h40v40h-40z'/><path d='M480 840h40v40h-40z'/><path d='M520 840h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M720 840h40v40h-40zm160 0h40v40h-40zm-480 40h40v40h-40z'/><path d='M440 880h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40zm80 0h40v40h-40z'/><path d='M720 880h40v40h-40z'/><path d='M760 880h40v40h-40z'/><path d='M800 880h40v40h-40z'/><path d='M840 880h40v40h-40z'/><path d='M880 880h40v40h-40zm120 0h40v40h-40z'/><path d='M1040 880h40v40h-40zm-640 40h40v40h-40zm160 0h40v40h-40z'/><path d='M600 920h40v40h-40z'/><path d='M640 920h40v40h-40z'/><path d='M680 920h40v40h-40zm120 0h40v40h-40zm200 0h40v40h-40z'/><path d='M1040 920h40v40h-40zm-600 40h40v40h-40z'/><path d='M480 960h40v40h-40z'/><path d='M520 960h40v40h-40zm80 0h40v40h-40zm160 0h40v40h-40zm120 0h40v40h-40z'/><path d='M920 960h40v40h-40z'/><path d='M960 960h40v40h-40z'/><path d='M1000 960h40v40h-40z'/><path d='M1040 960h40v40h-40zm-520 40h40v40h-40z'/><path d='M560 1000h40v40h-40z'/><path d='M600 1000h40v40h-40zm80 0h40v40h-40zm160 0h40v40h-40z'/><path d='M880 1000h40v40h-40zm80 0h40v40h-40z'/><path d='M1000 1000h40v40h-40z'/><path d='M1040 1000h40v40h-40zm-640 40h40v40h-40z'/><path d='M440 1040h40v40h-40zm80 0h40v40h-40z'/><path d='M560 1040h40v40h-40z'/><path d='M600 1040h40v40h-40zm120 0h40v40h-40z'/><path d='M760 1040h40v40h-40zm160 0h40v40h-40zm120 0h40v40h-40zM318 80H122 80v42 196 42h42 196 42v-42-196-42h-42zm0 238H122V122h196v196zm720-238H842h-42v42 196 42h42 196 42v-42-196-42h-42zm0 238H842V122h196v196zM318 800H122 80v42 196 42h42 196 42v-42-196-42h-42zm0 238H122V842h196v196zM160 160h120v120H160zm720 0h120v120H880zM160 880h120v120H160z'/></svg>";
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadStringDefaultConfiguration)
{
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600)]);
[webView synchronouslyLoadHTMLString:@"<img src='qr-code.png'></img>"];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(300, 300)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadString)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:@"<img src='qr-code.png'></img>"];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(150, 150)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(300, 300)];
EXPECT_WK_STREQ(elementInfo.qrCodePayloadString, "https://www.webkit.org");
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadStringInsideLink)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:@"<a href='https://www.webkit.org'><img src='qr-code.png'></img></a>"];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(300, 300)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadStringCanvas)
{
auto testMessageHandler = adoptNS([[TestMessageHandler alloc] init]);
__block bool imageLoaded = false;
[testMessageHandler addMessage:@"imageLoaded" withHandler:^{
imageLoaded = true;
}];
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[[configuration userContentController] addScriptMessageHandler:testMessageHandler.get() name:@"testHandler"];
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:@"<canvas id='canvas' width='400' height='400'></canvas>"];
NSString *drawImageToCanvasScript = @""
"var canvas = document.getElementById('canvas');\n"
"var ctx = canvas.getContext('2d');\n"
"var img = new Image();\n"
"img.onload = function() {\n"
" ctx.drawImage(img, 0, 0);\n"
" window.webkit.messageHandlers.testHandler.postMessage('imageLoaded');\n"
"};\n"
"img.src = 'qr-code.png';\n";
[webView stringByEvaluatingJavaScript:drawImageToCanvasScript];
Util::run(&imageLoaded);
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(150, 150)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(300, 300)];
EXPECT_WK_STREQ(elementInfo.qrCodePayloadString, "https://www.webkit.org");
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadStringSVG)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:qrCodeSVGString()];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(150, 150)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(300, 300)];
EXPECT_WK_STREQ(elementInfo.qrCodePayloadString, "https://www.webkit.org");
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadStringObscuredSVG)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:[NSString stringWithFormat:@"%@<div style='width: 400px; height: 100px; background: red; position: absolute; top: 0px; left: 0px;'></div>", qrCodeSVGString()]];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(150, 550)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(300, 300)];
EXPECT_WK_STREQ(elementInfo.qrCodePayloadString, "https://www.webkit.org");
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadStringSVGInsideTransformedElement)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:[NSString stringWithFormat:@"<div style='transform: translateX(100px);'>%@</div>", qrCodeSVGString()]];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(50, 550)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(150, 550)];
EXPECT_WK_STREQ(elementInfo.qrCodePayloadString, "https://www.webkit.org");
}
TEST(ContextMenuTests, ContextMenuElementInfoContainsQRCodePayloadStringSVGPageZoom)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView setPageZoom:1.3];
[webView synchronouslyLoadHTMLString:qrCodeSVGString()];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(550, 50)];
EXPECT_NULL(elementInfo.qrCodePayloadString);
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(500, 100)];
EXPECT_WK_STREQ(elementInfo.qrCodePayloadString, "https://www.webkit.org");
}
// FIXME: Re-enable this test once webkit.org/b/266650 is resolved
#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED > 140000 && !defined(NDEBUG)
TEST(ContextMenuElementInfoWithQRCodeDetectionCrash, DISABLED_ContextMenuTests)
#else
TEST(ContextMenuTests, ContextMenuElementInfoWithQRCodeDetectionCrash)
#endif
{
auto createWebView = [] {
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 600, 600) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:@"<img src='qr-code.png'></img>"];
return webView;
};
@autoreleasepool {
auto webView = createWebView();
[webView rightClickAtPoint:CGPointMake(300, 300)];
[webView waitForNextPresentationUpdate];
[webView removeFromSuperview];
}
auto webView = createWebView();
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(300, 300)];
EXPECT_WK_STREQ(elementInfo.qrCodePayloadString, "https://www.webkit.org");
}
#endif // ENABLE(CONTEXT_MENU_QR_CODE_DETECTION)
TEST(ContextMenuTests, ContextMenuElementInfoHasEntireImage)
{
CGFloat iconWidth = 215;
CGFloat iconHeight = 174;
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, iconWidth * 2, iconHeight * 2)]);
[webView synchronouslyLoadHTMLString:@"<img src='icon.png'></img>"];
_WKContextMenuElementInfo *elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(iconWidth, iconHeight)];
EXPECT_TRUE(elementInfo.hasEntireImage);
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(10, 10)];
EXPECT_FALSE(elementInfo.hasEntireImage);
}
TEST(ContextMenuTests, HitTestResultElementTypeNone)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_EQ(elementInfo.hitTestResult.elementType, _WKHitTestResultElementTypeNone);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadTestPageNamed:@"simple"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultElementTypeVideo)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_EQ(elementInfo.hitTestResult.elementType, _WKHitTestResultElementTypeVideo);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<video src=\"video-without-audio.mp4\" controls></video>"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultWithElementSelected)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_TRUE(elementInfo.hitTestResult.isSelected);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<body style='font-size: 100px;'>This text can be selected.</body>"];
[[webView window] makeFirstResponder:webView.get()];
[webView selectAll:nil];
[webView waitForNextPresentationUpdate];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultWithNoElementSelected)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_FALSE(elementInfo.hitTestResult.isSelected);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<body style='font-size: 100px; -webkit-user-select: none;'>This text cannot be selected.</body>"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultWhenClickingInSubframe)
{
// This is the escaped version of "data:text/html,<body>Hello from a frame!</body>" since that's what -[NSURL absoluteString] returns.
NSString *subframeContentsString = @"data:text/html,%3Cbody%3EHello%20from%20a%20frame!%3C/body%3E";
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
_WKHitTestResult *hitTestResult = elementInfo.hitTestResult;
EXPECT_NOT_NULL(elementInfo.hitTestResult);
WKFrameInfo *frameInfo = hitTestResult.frameInfo;
EXPECT_NOT_NULL(frameInfo);
EXPECT_FALSE(frameInfo.isMainFrame);
EXPECT_TRUE([subframeContentsString isEqualToString:frameInfo.request.URL.absoluteString]);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:[NSString stringWithFormat:@"<iframe width='400' height='400' src='%@'</iframe>", subframeContentsString]];
[webView waitForNextPresentationUpdate];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultNonFullscreenMedia)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_FALSE(elementInfo.hitTestResult.isMediaFullscreen);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<video src=\"video-without-audio.mp4\" controls></video>"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultMediaDownloadable)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_TRUE(elementInfo.hitTestResult.isMediaDownloadable);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<video src=\"video-without-audio.mp4\" controls></video>"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultImageMIMEType)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_TRUE([elementInfo.hitTestResult.imageMIMEType isEqualToString:@"image/jpeg"]);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<img src='sunset-in-cupertino-600px.jpg'></img>"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultLinkLocalResourceResponse)
{
_WKContextMenuElementInfo *elementInfo;
NSURLResponse* linkLocalResourceResponse;
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView synchronouslyLoadHTMLString:@"<a href='sunset-in-cupertino-600px.jpg'><img src='sunset-in-cupertino-600px.jpg'></img></a>"];
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(200, 200)];
linkLocalResourceResponse = elementInfo.hitTestResult.linkLocalResourceResponse;
EXPECT_NOT_NULL(linkLocalResourceResponse);
EXPECT_WK_STREQ(linkLocalResourceResponse.suggestedFilename, "sunset-in-cupertino-600px.jpg");
EXPECT_WK_STREQ(linkLocalResourceResponse.MIMEType, "image/jpeg");
[webView synchronouslyLoadHTMLString:@"<a href='https://webkit.org/'><img src='sunset-in-cupertino-600px.jpg'></img></a>"];
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(200, 200)];
linkLocalResourceResponse = elementInfo.hitTestResult.linkLocalResourceResponse;
EXPECT_NULL(linkLocalResourceResponse);
}
TEST(ContextMenuTests, HitTestResultLinkSuggestedFilename)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_TRUE([elementInfo.hitTestResult.linkSuggestedFilename isEqualToString:@"Sunset.jpg"]);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<a href='sunset-in-cupertino-600px.jpg' download='Sunset.jpg'><img src='sunset-in-cupertino-600px.jpg'></img></a>"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultImageSuggestedFilename)
{
auto delegate = adoptNS([[TestUIDelegate alloc] init]);
__block bool gotProposedMenu = false;
[delegate setGetContextMenuFromProposedMenu:^(NSMenu *menu, _WKContextMenuElementInfo *elementInfo, id<NSSecureCoding>, void (^completion)(NSMenu *)) {
EXPECT_TRUE(elementInfo.hasEntireImage);
EXPECT_NOT_NULL(elementInfo.hitTestResult);
EXPECT_TRUE([elementInfo.hitTestResult.imageSuggestedFilename isEqualToString:@"sunset-in-cupertino-600px.jpg"]);
completion(nil);
gotProposedMenu = true;
}];
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView setUIDelegate:delegate.get()];
[webView synchronouslyLoadHTMLString:@"<img src='sunset-in-cupertino-600px.jpg'></img>"];
[webView mouseDownAtPoint:NSMakePoint(200, 200) simulatePressure:NO withFlags:0 eventType:NSEventTypeRightMouseDown];
[webView mouseUpAtPoint:NSMakePoint(200, 200) withFlags:0 eventType:NSEventTypeRightMouseUp];
Util::run(&gotProposedMenu);
}
TEST(ContextMenuTests, HitTestResultLinkWithInvalidURL)
{
RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView synchronouslyLoadHTMLString:@"<a href='https://' style='font-size: 100px;'>Link</a>"];
RetainPtr<_WKContextMenuElementInfo> elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(50, 350)];
EXPECT_NOT_NULL([elementInfo hitTestResult]);
}
TEST(ContextMenuTests, ContextMenuElementInfoAllowsFollowingImageURL)
{
_WKContextMenuElementInfo *elementInfo;
CGFloat iconWidth = 215;
CGFloat iconHeight = 174;
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, iconWidth * 2, iconHeight * 2)]);
[webView synchronouslyLoadHTMLString:@"<img src='icon.png'></img>"];
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(iconWidth, iconHeight)];
EXPECT_TRUE(elementInfo.allowsFollowingImageURL);
[webView synchronouslyLoadHTMLString:@"<img src='file:///icon.png'></img>"];
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(iconWidth, iconHeight)];
EXPECT_FALSE(elementInfo.allowsFollowingImageURL);
}
TEST(ContextMenuTests, ContextMenuElementInfoAllowsFollowingLink)
{
_WKContextMenuElementInfo *elementInfo;
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView synchronouslyLoadHTMLString:@"<a href='icon.png' style='font-size: 100px;'>Link</a>"];
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(50, 350)];
EXPECT_TRUE(elementInfo.allowsFollowingLink);
[webView synchronouslyLoadHTMLString:@"<a href='file:///simple.html' style='font-size: 100px;'>Hello world</a>"];
elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(50, 350)];
EXPECT_FALSE(elementInfo.allowsFollowingLink);
}
TEST(ContextMenuTests, ContextMenuTopLevelTextNodeInShadowDOMShouldNotCrash)
{
RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
[configuration _setContextMenuQRCodeDetectionEnabled:YES];
RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400) configuration:configuration.get()]);
[webView synchronouslyLoadHTMLString:@(R"(
<body style='margin: 0; padding: 0; font-size:50px'>
</body>
<script>
const shadowRoot = document.body.attachShadow({ mode: "open" });
const textNode = document.createTextNode("Text Node in Shadow DOM");
shadowRoot.appendChild(textNode);
</script>
)")];
RetainPtr elementInfo = [webView rightClickAtPointAndWaitForContextMenu:NSMakePoint(5, 395)];
EXPECT_NOT_NULL([elementInfo hitTestResult]);
}
TEST(ContextMenuTests, CopyLinkIncludesTitle)
{
RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView synchronouslyLoadHTMLString:@"<a href='https://webkit.org' style='position: absolute; top: 0; left: 0; display: inline-block; width: 200px; height: 200px;'>WebKit</a>"];
clearPasteboard();
[webView rightClick:NSMakePoint(10, 10) andSelectItemMatching:^BOOL(NSMenuItem *item) {
return [item.identifier isEqualToString:_WKMenuItemIdentifierCopyLink];
}];
while (!readTitleFromPasteboard())
TestWebKitAPI::Util::runFor(0.1_s);
EXPECT_WK_STREQ(@"WebKit", readTitleFromPasteboard());
}
TEST(ContextMenuTests, CopyLinkUsesFullURLAsTitleForLinkWithShortPath)
{
RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView synchronouslyLoadHTMLString:@"<a href='https://webkit.org' style='position: absolute; top: 0; left: 0; display: inline-block; width: 200px; height: 200px;'></a>"];
clearPasteboard();
[webView rightClick:NSMakePoint(10, 10) andSelectItemMatching:^BOOL(NSMenuItem *item) {
return [item.identifier isEqualToString:_WKMenuItemIdentifierCopyLink];
}];
while (!readTitleFromPasteboard())
TestWebKitAPI::Util::runFor(0.1_s);
EXPECT_WK_STREQ(@"https://webkit.org/", readTitleFromPasteboard());
}
TEST(ContextMenuTests, CopyLinkUsesPathComponentAsTitleForLinkWithPath)
{
RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
[webView synchronouslyLoadHTMLString:@"<a href='https://webkit.org/something-cool' style='position: absolute; top: 0; left: 0; display: inline-block; width: 200px; height: 200px;'></a>"];
clearPasteboard();
[webView rightClick:NSMakePoint(10, 10) andSelectItemMatching:^BOOL(NSMenuItem *item) {
return [item.identifier isEqualToString:_WKMenuItemIdentifierCopyLink];
}];
while (!readTitleFromPasteboard())
TestWebKitAPI::Util::runFor(0.1_s);
EXPECT_WK_STREQ(@"something-cool", readTitleFromPasteboard());
}
} // namespace TestWebKitAPI
#endif // PLATFORM(MAC)