| /* |
| * Copyright (C) 2020 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(IOS) || PLATFORM(MACCATALYST) || PLATFORM(VISION) |
| |
| #import "AdditionalButtonMasksIOS.h" |
| #import "HTTPServer.h" |
| #import "IOSMouseEventTestHarness.h" |
| #import "InstanceMethodSwizzler.h" |
| #import "MouseSupportUIDelegate.h" |
| #import "PlatformUtilities.h" |
| #import "Test.h" |
| #import "TestCocoa.h" |
| #import "TestNavigationDelegate.h" |
| #import "TestWKWebView.h" |
| #import "UIKitSPIForTesting.h" |
| #import <WebCore/PointerEventTypeNames.h> |
| #import <WebKit/WKPreferencesPrivate.h> |
| #import <WebKit/WKUIDelegatePrivate.h> |
| #import <WebKit/WKWebViewConfigurationPrivate.h> |
| #import <WebKit/WKWebViewPrivateForTesting.h> |
| #import <WebKit/WKWebpagePreferencesPrivate.h> |
| #import <WebKit/WebKit.h> |
| #import <pal/spi/cocoa/RevealSPI.h> |
| #import <wtf/BlockPtr.h> |
| #import <wtf/MonotonicTime.h> |
| #import <wtf/RetainPtr.h> |
| #import <wtf/cocoa/TypeCastsCocoa.h> |
| |
| #if HAVE(MOUSE_DEVICE_OBSERVATION) |
| |
| @interface WKMouseDeviceObserver |
| + (WKMouseDeviceObserver *)sharedInstance; |
| - (void)start; |
| - (void)_setHasMouseDeviceForTesting:(BOOL)hasMouseDevice; |
| @end |
| |
| #endif |
| |
| #if HAVE(UI_POINTER_INTERACTION) |
| |
| @interface TestPointerRegionRequest : UIPointerRegionRequest |
| - (instancetype)initWithLocation:(CGPoint)location; |
| @end |
| |
| @implementation TestPointerRegionRequest { |
| CGPoint _requestLocation; |
| } |
| |
| - (instancetype)initWithLocation:(CGPoint)location |
| { |
| if (!(self = [super init])) |
| return nil; |
| |
| _requestLocation = location; |
| return self; |
| } |
| |
| - (CGPoint)location |
| { |
| return _requestLocation; |
| } |
| |
| @end |
| |
| namespace TestWebKitAPI { |
| |
| struct PointerInfo { |
| BOOL isDefault { NO }; |
| CGRect regionRect { CGRectNull }; |
| }; |
| |
| } // namespace TestWebKitAPI |
| |
| @interface TestWKWebView (PointerInteractionTests) |
| - (TestWebKitAPI::PointerInfo)pointerInfoAtLocation:(CGPoint)location; |
| @end |
| |
| @implementation TestWKWebView (PointerInteractionTests) |
| |
| - (UIPointerInteraction *)pointerInteraction |
| { |
| for (id<UIInteraction> interaction in self.textInputContentView.interactions) { |
| if (auto result = dynamic_objc_cast<UIPointerInteraction>(interaction)) |
| return result; |
| } |
| return nil; |
| } |
| |
| - (TestWebKitAPI::PointerInfo)pointerInfoAtLocation:(CGPoint)location |
| { |
| auto contentView = (UIView<UIPointerInteractionDelegate> *)[self textInputContentView]; |
| auto interaction = [self pointerInteraction]; |
| RetainPtr request = adoptNS([[TestPointerRegionRequest alloc] initWithLocation:location]); |
| |
| RetainPtr defaultRegion = [UIPointerRegion regionWithRect:contentView.bounds identifier:nil]; |
| [contentView pointerInteraction:interaction regionForRequest:request.get() defaultRegion:defaultRegion.get()]; |
| [self waitForNextPresentationUpdate]; |
| |
| auto region = [contentView pointerInteraction:interaction regionForRequest:request.get() defaultRegion:defaultRegion.get()]; |
| auto style = [contentView pointerInteraction:interaction styleForRegion:region]; |
| return { [style isEqual:UIPointerStyle.systemPointerStyle], region.rect }; |
| } |
| |
| @end |
| |
| #endif // HAVE(UI_POINTER_INTERACTION) |
| |
| class iOSMouseSupport : public testing::Test { |
| public: |
| void SetUp() final |
| { |
| TestWebKitAPI::Util::instantiateUIApplicationIfNeeded(); |
| } |
| }; |
| |
| static constexpr NSString *fractionalCoordinatesTestPage = @(R"( |
| <!DOCTYPE html> |
| <html> |
| <body style='margin: 0; padding: 0;'> |
| <iframe id='frame' style='width: 100px; height: 100px; position: absolute; border: none;'></iframe> |
| </body> |
| <script> |
| var clientX; |
| var clientY; |
| var pageX; |
| var pageY; |
| |
| window.addEventListener('load', () => { |
| frame.srcdoc = `\<script\> |
| document.addEventListener('pointerdown', function(e) { |
| parent.clientX = e.clientX; |
| parent.clientY = e.clientY; |
| parent.pageX = e.pageX; |
| parent.pageY = e.pageY; |
| }); |
| \</script\>`; |
| }); |
| </script> |
| </html> |
| )"); |
| |
| static constexpr auto iframeContentForFractionalCoordinates = R"( |
| <!DOCTYPE html> |
| <html> |
| <body style='margin: 0; padding: 0;'> |
| <script> |
| document.addEventListener('pointerdown', function(e) { |
| parent.postMessage({ |
| type: 'pointerdown', |
| clientX: e.clientX, |
| clientY: e.clientY, |
| pageX: e.pageX, |
| pageY: e.pageY |
| }, '*'); |
| }); |
| </script> |
| </body> |
| </html> |
| )"_s; |
| |
| static constexpr auto mainHTMLForCrossOriginFractionalCoordinates = R"( |
| <!DOCTYPE html> |
| <html> |
| <body style='margin: 0; padding: 0;'> |
| <iframe id='frame' src='https://webkit.org/iframe' style='width: 100px; height: 100px; position: absolute; border: none;'></iframe> |
| <script> |
| window.coordinatesReceived = new Promise(resolve => { |
| window.addEventListener('message', function(e) { |
| if (e.data.type === 'pointerdown') { |
| window.clientX = e.data.clientX; |
| window.clientY = e.data.clientY; |
| window.pageX = e.data.pageX; |
| window.pageY = e.data.pageY; |
| resolve(); |
| } |
| }); |
| }); |
| </script> |
| </body> |
| </html> |
| )"_s; |
| |
| static const auto serverForCrossOriginFrameTests = TestWebKitAPI::HTTPServer({ |
| { "/example"_s, { mainHTMLForCrossOriginFractionalCoordinates } }, |
| { "/iframe"_s, { iframeContentForFractionalCoordinates } } |
| }, TestWebKitAPI::HTTPServer::Protocol::HttpsProxy); |
| |
| static std::pair<RetainPtr<TestWKWebView>, RetainPtr<TestNavigationDelegate>> siteIsolatedViewAndDelegate(const TestWebKitAPI::HTTPServer& server) |
| { |
| RetainPtr navigationDelegate = adoptNS([TestNavigationDelegate new]); |
| [navigationDelegate allowAnyTLSCertificate]; |
| |
| RetainPtr configuration = server.httpsProxyConfiguration(); |
| [[configuration preferences] _setSiteIsolationEnabled:YES]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView setNavigationDelegate:navigationDelegate.get()]; |
| |
| return { WTF::move(webView), WTF::move(navigationDelegate) }; |
| } |
| |
| static void testFractionalCoordinatesInIFrame(TestWKWebView *webView, double mouseX, double mouseY, NSString *expectedX, NSString *expectedY, NSString *jsTransform = nil, bool isCrossOrigin = false) |
| { |
| if (jsTransform) { |
| __block bool done = false; |
| [webView evaluateJavaScript:jsTransform completionHandler:^(id, NSError *) { |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView }; |
| testHarness.mouseMove(mouseX, mouseY); |
| testHarness.mouseDown(); |
| testHarness.mouseUp(); |
| [webView waitForPendingMouseEvents]; |
| |
| EXPECT_WK_STREQ(expectedX, [webView stringByEvaluatingJavaScript:@"window.clientX"]); |
| EXPECT_WK_STREQ(expectedY, [webView stringByEvaluatingJavaScript:@"window.clientY"]); |
| EXPECT_WK_STREQ(expectedX, [webView stringByEvaluatingJavaScript:@"window.pageX"]); |
| EXPECT_WK_STREQ(expectedY, [webView stringByEvaluatingJavaScript:@"window.pageY"]); |
| } |
| |
| TEST_F(iOSMouseSupport, DoNotChangeSelectionWithRightClick) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadTestPageNamed:@"simple"]; |
| [webView objectByEvaluatingJavaScript:@"document.body.setAttribute('contenteditable','');"]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(UIEventButtonMaskSecondary); |
| testHarness.mouseUp(); |
| |
| __block bool done = false; |
| |
| [webView _doAfterProcessingAllPendingMouseEvents:^{ |
| NSNumber *result = [webView objectByEvaluatingJavaScript:@"window.getSelection().isCollapsed"]; |
| EXPECT_TRUE([result boolValue]); |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TEST_F(iOSMouseSupport, RightClickOutsideOfTextNodeDoesNotSelect) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadTestPageNamed:@"emptyTable"]; |
| [webView stringByEvaluatingJavaScript:@"getSelection().selectAllChildren(document.getElementById('target'))"]; |
| |
| __block bool done = false; |
| [webView selectTextForContextMenuWithLocationInView:CGPointMake(100, 10) completion:^(BOOL) { |
| NSNumber *result = [webView objectByEvaluatingJavaScript:@"window.getSelection().isCollapsed"]; |
| EXPECT_FALSE([result boolValue]); |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TEST_F(iOSMouseSupport, RightClickDoesNotShowMenuIfPreventDefault) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadTestPageNamed:@"image"]; |
| [webView stringByEvaluatingJavaScript:@"window.didContextMenu = false; document.addEventListener('contextmenu', (event) => { event.preventDefault(); didContextMenu = true; })"]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(UIEventButtonMaskSecondary); |
| testHarness.mouseUp(); |
| |
| __block bool done = false; |
| [webView selectTextForContextMenuWithLocationInView:CGPointMake(10, 10) completion:^(BOOL shouldPresentMenu) { |
| EXPECT_FALSE(shouldPresentMenu); |
| |
| NSNumber *didContextMenu = [webView objectByEvaluatingJavaScript:@"window.didContextMenu"]; |
| EXPECT_TRUE([didContextMenu boolValue]); |
| |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TEST_F(iOSMouseSupport, TrackButtonMaskFromTouchStart) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadHTMLString:@"<script>" |
| "window.didReleaseRightButton = false;" |
| "document.documentElement.addEventListener('mouseup', function (e) {" |
| " if (e.button == 2)" |
| " window.didReleaseRightButton = true;" |
| "});" |
| "</script>"]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(UIEventButtonMaskSecondary); |
| testHarness.mouseUp(); |
| |
| __block bool done = false; |
| |
| [webView _doAfterProcessingAllPendingMouseEvents:^{ |
| NSNumber *result = [webView objectByEvaluatingJavaScript:@"window.didReleaseRightButton"]; |
| EXPECT_TRUE([result boolValue]); |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TEST_F(iOSMouseSupport, MouseTimestampTimebase) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadHTMLString:@"<script>" |
| "window.mouseDownTimestamp = -1;" |
| "document.documentElement.addEventListener('mousedown', function (e) {" |
| " window.mouseDownTimestamp = e.timeStamp;" |
| "});" |
| "</script>"]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(); |
| testHarness.mouseUp(); |
| |
| __block bool done = false; |
| |
| [webView _doAfterProcessingAllPendingMouseEvents:^{ |
| double mouseDownTimestamp = [[webView objectByEvaluatingJavaScript:@"window.mouseDownTimestamp"] doubleValue]; |
| // Ensure that the timestamp is not clamped to 0. |
| EXPECT_GT(mouseDownTimestamp, 0); |
| |
| // The test should always complete in 10 seconds, so ensure that |
| // the timestamp is also not overly large. |
| EXPECT_LE(mouseDownTimestamp, 10000); |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TEST_F(iOSMouseSupport, EndedTouchesTriggerClick) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadHTMLString:@"<script>" |
| "window.wasClicked = false;" |
| "document.documentElement.addEventListener('click', function (e) {" |
| " window.wasClicked = true;" |
| "});" |
| "</script>"]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(); |
| testHarness.mouseUp(); |
| |
| [webView waitForPendingMouseEvents]; |
| |
| bool wasClicked = [[webView objectByEvaluatingJavaScript:@"window.wasClicked"] boolValue]; |
| EXPECT_TRUE(wasClicked); |
| } |
| |
| TEST_F(iOSMouseSupport, CancelledTouchesDoNotTriggerClick) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadHTMLString:@"<script>" |
| "window.wasClicked = false;" |
| "document.documentElement.addEventListener('click', function (e) {" |
| " window.wasClicked = true;" |
| "});" |
| "</script>"]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(); |
| testHarness.mouseCancel(); |
| |
| [webView waitForPendingMouseEvents]; |
| |
| bool wasClicked = [[webView objectByEvaluatingJavaScript:@"window.wasClicked"] boolValue]; |
| EXPECT_FALSE(wasClicked); |
| } |
| |
| TEST_F(iOSMouseSupport, MouseDidMoveOverElement) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| RetainPtr delegate = adoptNS([MouseSupportUIDelegate new]); |
| |
| __block bool mouseDidMoveOverElement = false; |
| __block RetainPtr<_WKHitTestResult> hitTestResult; |
| [delegate setMouseDidMoveOverElementHandler:^(_WKHitTestResult *result) { |
| hitTestResult = result; |
| mouseDidMoveOverElement = true; |
| }]; |
| |
| [webView synchronouslyLoadTestPageNamed:@"simple"]; |
| [webView setUIDelegate:delegate.get()]; |
| |
| TestWebKitAPI::MouseEventTestHarness { webView.get() }.mouseMove(10, 10); |
| TestWebKitAPI::Util::run(&mouseDidMoveOverElement); |
| |
| EXPECT_TRUE(mouseDidMoveOverElement); |
| EXPECT_NOT_NULL(hitTestResult); |
| } |
| |
| TEST_F(iOSMouseSupport, FractionalCoordinatesInSimpleIFrame) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadHTMLString:fractionalCoordinatesTestPage]; |
| |
| testFractionalCoordinatesInIFrame(webView.get(), 0.5, 0.5, @"0.5", @"0.5"); |
| } |
| |
| TEST_F(iOSMouseSupport, FractionalCoordinatesInRotatedIFrame) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadHTMLString:fractionalCoordinatesTestPage]; |
| |
| testFractionalCoordinatesInIFrame(webView.get(), 1.5, 1.5, @"98.5", @"98.5", @"frame.style.rotate = \"180deg\";"); |
| } |
| |
| TEST_F(iOSMouseSupport, FractionalCoordinatesInScaledIFrame) |
| { |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadHTMLString:fractionalCoordinatesTestPage]; |
| |
| testFractionalCoordinatesInIFrame(webView.get(), 0.5, 0.5, @"0.25", @"0.25", @"frame.style.transformOrigin = \"top left\"; frame.style.scale = \"2\";"); |
| } |
| |
| TEST_F(iOSMouseSupport, FractionalCoordinatesInSimpleIFrameCrossOrigin) |
| { |
| auto [webView, navigationDelegate] = siteIsolatedViewAndDelegate(serverForCrossOriginFrameTests); |
| [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/example"]]]; |
| [navigationDelegate waitForDidFinishNavigation]; |
| testFractionalCoordinatesInIFrame(webView.get(), 0.5, 0.5, @"0.5", @"0.5", nil, true); |
| } |
| |
| TEST_F(iOSMouseSupport, FractionalCoordinatesInRotatedIFrameCrossOrigin) |
| { |
| auto [webView, navigationDelegate] = siteIsolatedViewAndDelegate(serverForCrossOriginFrameTests); |
| [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/example"]]]; |
| [navigationDelegate waitForDidFinishNavigation]; |
| testFractionalCoordinatesInIFrame(webView.get(), 1.5, 1.5, @"98.5", @"98.5", @"frame.style.rotate = \"180deg\";", true); |
| } |
| |
| TEST_F(iOSMouseSupport, FractionalCoordinatesInScaledIFrameCrossOrigin) |
| { |
| auto [webView, navigationDelegate] = siteIsolatedViewAndDelegate(serverForCrossOriginFrameTests); |
| [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/example"]]]; |
| [navigationDelegate waitForDidFinishNavigation]; |
| testFractionalCoordinatesInIFrame(webView.get(), 0.5, 0.5, @"0.25", @"0.25", @"frame.style.transformOrigin = \"top left\"; frame.style.scale = \"2\";", true); |
| } |
| |
| static bool selectionUpdated = false; |
| static void handleUpdatedSelection(id, SEL) |
| { |
| selectionUpdated = true; |
| } |
| |
| TEST_F(iOSMouseSupport, SelectionUpdatesBeforeContextMenuAppears) |
| { |
| InstanceMethodSwizzler swizzler { UIWKTextInteractionAssistant.class, @selector(selectionChanged), reinterpret_cast<IMP>(handleUpdatedSelection) }; |
| |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| [webView synchronouslyLoadTestPageNamed:@"simple"]; |
| [webView objectByEvaluatingJavaScript:@"document.body.setAttribute('contenteditable','');"]; |
| |
| [webView _simulateSelectionStart]; |
| __block bool done = false; |
| [webView selectTextForContextMenuWithLocationInView:CGPointZero completion:^(BOOL) { |
| EXPECT_TRUE(selectionUpdated); |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| constexpr auto largeResponsiveHelloMarkup = "<meta name='viewport' content='width=device-width'><span style='font-size: 400px;'>Hello</span>"; |
| |
| TEST_F(iOSMouseSupport, DisablingTextIteractionPreventsSelectionWhenShowingContextMenu) |
| { |
| RetainPtr configuration = adoptNS([WKWebViewConfiguration new]); |
| [configuration preferences].textInteractionEnabled = NO; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration.get()]); |
| [webView synchronouslyLoadHTMLString:@(largeResponsiveHelloMarkup)]; |
| |
| __block bool done = false; |
| [webView selectTextForContextMenuWithLocationInView:CGPointMake(100, 100) completion:^(BOOL) { |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]); |
| } |
| |
| TEST_F(iOSMouseSupport, ShowingContextMenuSelectsEditableText) |
| { |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]); |
| [webView _setEditable:YES]; |
| [webView synchronouslyLoadHTMLString:@(largeResponsiveHelloMarkup)]; |
| |
| __block bool done = false; |
| [webView selectTextForContextMenuWithLocationInView:CGPointMake(100, 100) completion:^(BOOL) { |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| EXPECT_WK_STREQ("Hello", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]); |
| EXPECT_FALSE(CGRectIsEmpty([webView selectionViewRectsInContentCoordinates].firstObject.CGRectValue)); |
| } |
| |
| TEST_F(iOSMouseSupport, ShowingContextMenuSelectsNonEditableText) |
| { |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]); |
| [webView synchronouslyLoadHTMLString:@(largeResponsiveHelloMarkup)]; |
| |
| __block bool done = false; |
| [webView selectTextForContextMenuWithLocationInView:CGPointMake(100, 100) completion:^(BOOL) { |
| done = true; |
| }]; |
| |
| TestWebKitAPI::Util::run(&done); |
| EXPECT_WK_STREQ("Hello", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]); |
| EXPECT_FALSE(CGRectIsEmpty([webView selectionViewRectsInContentCoordinates].firstObject.CGRectValue)); |
| } |
| |
| static void simulateEditContextMenuAppearance(TestWKWebView *webView, CGPoint location) |
| { |
| __block bool done = false; |
| [webView selectTextForContextMenuWithLocationInView:location completion:^(BOOL) { |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| } |
| |
| TEST_F(iOSMouseSupport, ContextClickAtEndOfSelection) |
| { |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 1000, 400)]); |
| [webView synchronouslyLoadTestPageNamed:@"try-text-select-with-disabled-text-interaction"]; |
| |
| __auto_type setupSelection = ^(TestWKWebView* webView) { |
| // Creates a selection of the characters "Hell" |
| NSString *modifySelectionJavascript = @"" |
| "(() => {" |
| " const node = document.getElementById('editable').firstChild;" |
| " const range = document.createRange();" |
| " range.setStart(node, 0);" |
| " range.setEnd(node, 4);" |
| " " |
| " var selection = window.getSelection();" |
| " selection.removeAllRanges();" |
| " selection.addRange(range);" |
| "})();"; |
| |
| __block bool done = false; |
| [webView evaluateJavaScript:modifySelectionJavascript completionHandler:^(id, NSError *error) { |
| EXPECT_NULL(error); |
| done = true; |
| }]; |
| TestWebKitAPI::Util::run(&done); |
| |
| [webView stringByEvaluatingJavaScript:modifySelectionJavascript]; |
| [webView waitForNextPresentationUpdate]; |
| |
| EXPECT_WK_STREQ("Hell", [webView selectedText]); |
| }; |
| |
| const CGFloat centerOfLetterO = 795; |
| |
| setupSelection(webView.get()); |
| |
| // Right click a bit to the left of the center of the "o". This should not change the selection. |
| simulateEditContextMenuAppearance(webView.get(), NSMakePoint(centerOfLetterO - 5, 200)); |
| EXPECT_WK_STREQ("Hell", [webView selectedText]); |
| |
| setupSelection(webView.get()); |
| |
| // Right click a bit to the right of the center of the "o". This should change the selection. |
| simulateEditContextMenuAppearance(webView.get(), NSMakePoint(centerOfLetterO + 5, 200)); |
| EXPECT_WK_STREQ("Hello", [webView selectedText]); |
| } |
| |
| #if ENABLE(IOS_TOUCH_EVENTS) |
| |
| TEST_F(iOSMouseSupport, WebsiteMouseEventPolicies) |
| { |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| |
| auto tapAndWait = [&] { |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(); |
| testHarness.mouseUp(); |
| [webView waitForPendingMouseEvents]; |
| }; |
| |
| // By default, a mouse should generate mouse events. |
| |
| [webView synchronouslyLoadHTMLString:@"<script>" |
| "window.lastPointerEventType = undefined;" |
| "document.documentElement.addEventListener('pointerdown', function (e) {" |
| " window.lastPointerEventType = e.pointerType;" |
| "});" |
| "</script>"]; |
| |
| tapAndWait(); |
| |
| NSString *result = [webView objectByEvaluatingJavaScript:@"window.lastPointerEventType"]; |
| EXPECT_WK_STREQ(WebCore::mousePointerEventType(), result); |
| |
| EXPECT_TRUE(testHarness.mouseInteraction().enabled); |
| |
| // If loaded with _WKWebsiteMouseEventPolicySynthesizeTouchEvents, it should send touch events instead. |
| |
| RetainPtr preferences = adoptNS([[WKWebpagePreferences alloc] init]); |
| [preferences _setMouseEventPolicy:_WKWebsiteMouseEventPolicySynthesizeTouchEvents]; |
| |
| [webView synchronouslyLoadHTMLString:@"two" preferences:preferences.get()]; |
| |
| // FIXME: Because we're directly calling mouseGestureRecognizerChanged: to emulate the gesture recognizer, |
| // we can't just tapAndWait() again and expect the fact that it's disabled to stop the mouse events. |
| // Instead, we'll just ensure that the gesture is disabled. |
| |
| EXPECT_FALSE(testHarness.mouseInteraction().enabled); |
| } |
| |
| #endif // ENABLE(IOS_TOUCH_EVENTS) |
| |
| #if HAVE(MOUSE_DEVICE_OBSERVATION) |
| |
| TEST_F(iOSMouseSupport, MouseInitiallyDisconnected) |
| { |
| WKMouseDeviceObserver *mouseDeviceObserver = [NSClassFromString(@"WKMouseDeviceObserver") sharedInstance]; |
| |
| [mouseDeviceObserver start]; |
| [mouseDeviceObserver _setHasMouseDeviceForTesting:NO]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView synchronouslyLoadHTMLString:@""]; |
| |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"hover: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover: hover"]); |
| |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-hover"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer: coarse"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: fine"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer: coarse"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: fine"]); |
| } |
| |
| TEST_F(iOSMouseSupport, MouseInitiallyConnected) |
| { |
| WKMouseDeviceObserver *mouseDeviceObserver = [NSClassFromString(@"WKMouseDeviceObserver") sharedInstance]; |
| |
| [mouseDeviceObserver start]; |
| [mouseDeviceObserver _setHasMouseDeviceForTesting:YES]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView synchronouslyLoadHTMLString:@""]; |
| |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"hover: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-hover: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer: coarse"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: fine"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer: coarse"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer: fine"]); |
| } |
| |
| TEST_F(iOSMouseSupport, MouseLaterDisconnected) |
| { |
| WKMouseDeviceObserver *mouseDeviceObserver = [NSClassFromString(@"WKMouseDeviceObserver") sharedInstance]; |
| |
| [mouseDeviceObserver start]; |
| [mouseDeviceObserver _setHasMouseDeviceForTesting:YES]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView synchronouslyLoadHTMLString:@""]; |
| |
| [mouseDeviceObserver _setHasMouseDeviceForTesting:NO]; |
| |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"hover: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover: hover"]); |
| |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-hover"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer: coarse"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: fine"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer: coarse"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: fine"]); |
| } |
| |
| TEST_F(iOSMouseSupport, MouseLaterConnected) |
| { |
| WKMouseDeviceObserver *mouseDeviceObserver = [NSClassFromString(@"WKMouseDeviceObserver") sharedInstance]; |
| |
| [mouseDeviceObserver start]; |
| [mouseDeviceObserver _setHasMouseDeviceForTesting:NO]; |
| |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView synchronouslyLoadHTMLString:@""]; |
| |
| [mouseDeviceObserver _setHasMouseDeviceForTesting:YES]; |
| |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"hover: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-hover: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer: coarse"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: fine"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer: coarse"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer: fine"]); |
| } |
| |
| #endif // HAVE(MOUSE_DEVICE_OBSERVATION) |
| |
| #if PLATFORM(MACCATALYST) |
| |
| TEST_F(iOSMouseSupport, MouseAlwaysConnected) |
| { |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]); |
| |
| [webView synchronouslyLoadHTMLString:@""]; |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"hover"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"hover: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-hover: none"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-hover: hover"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"pointer: coarse"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"pointer: fine"]); |
| |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: none"]); |
| EXPECT_FALSE([webView evaluateMediaQuery:@"any-pointer: coarse"]); |
| EXPECT_TRUE([webView evaluateMediaQuery:@"any-pointer: fine"]); |
| } |
| |
| #endif // PLATFORM(MACCATALYST) |
| |
| #if HAVE(UI_POINTER_INTERACTION) |
| |
| TEST_F(iOSMouseSupport, BasicPointerInteractionRegions) |
| { |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]); |
| [webView synchronouslyLoadTestPageNamed:@"cursor-styles"]; |
| |
| { |
| auto info = [webView pointerInfoAtLocation:[webView elementMidpointFromSelector:@"#container"]]; |
| EXPECT_TRUE(info.isDefault); |
| EXPECT_TRUE(CGRectEqualToRect(info.regionRect, [webView bounds])); |
| } |
| { |
| auto elementRect = [webView elementRectFromSelector:@"#editable-container"]; |
| auto info = [webView pointerInfoAtLocation:[webView elementMidpointFromSelector:@"#editable-container"]]; |
| EXPECT_FALSE(info.isDefault); |
| EXPECT_TRUE(CGRectContainsRect(elementRect, info.regionRect)); |
| } |
| { |
| auto elementRect = [webView elementRectFromSelector:@"#container-with-cursor-pointer"]; |
| auto info = [webView pointerInfoAtLocation:[webView elementMidpointFromSelector:@"#container-with-cursor-pointer"]]; |
| EXPECT_TRUE(info.isDefault); |
| EXPECT_TRUE(CGRectContainsRect(elementRect, info.regionRect)); |
| } |
| { |
| auto elementRect = [webView elementRectFromSelector:@"#container-with-text"]; |
| auto info = [webView pointerInfoAtLocation:[webView elementMidpointFromSelector:@"#container-with-text"]]; |
| EXPECT_FALSE(info.isDefault); |
| EXPECT_TRUE(CGRectContainsRect(elementRect, info.regionRect)); |
| } |
| { |
| auto elementRect = [webView elementRectFromSelector:@"#container-with-link"]; |
| auto info = [webView pointerInfoAtLocation:[webView elementMidpointFromSelector:@"#container-with-link"]]; |
| EXPECT_TRUE(info.isDefault); |
| EXPECT_TRUE(CGRectContainsRect(elementRect, info.regionRect)); |
| } |
| { |
| auto elementRect = [webView elementRectFromSelector:@"#container-with-cursor-text"]; |
| auto info = [webView pointerInfoAtLocation:[webView elementMidpointFromSelector:@"#container-with-cursor-text"]]; |
| EXPECT_FALSE(info.isDefault); |
| EXPECT_TRUE(CGRectContainsRect(elementRect, info.regionRect)); |
| } |
| } |
| |
| TEST_F(iOSMouseSupport, LeftClickFiresPointerAndMouseEvents) |
| { |
| // FIXME: It is not ideal that we have to wait for a static amount of time |
| // between sets of events if we want to test multiple pointerdowns. |
| |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| static const char* page = R"PAGEDATA( |
| <script> |
| window.mousedownCount = 0; |
| window.mouseupCount = 0; |
| window.pointerdownCount = 0; |
| window.pointerupCount = 0; |
| window.clickCount = 0; |
| |
| document.addEventListener('mousedown', () => { window.mousedownCount++ }); |
| document.addEventListener('mouseup', () => { window.mouseupCount++ }); |
| document.addEventListener('pointerdown', () => { window.pointerdownCount++ }); |
| document.addEventListener('pointerup', () => { window.pointerupCount++ }); |
| document.addEventListener('click', () => { window.clickCount++ }); |
| </script> |
| )PAGEDATA"; |
| |
| [webView synchronouslyLoadHTMLString:[NSString stringWithUTF8String:page]]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| testHarness.mouseDown(UIEventButtonMaskPrimary); |
| testHarness.mouseUp(); |
| |
| TestWebKitAPI::Util::runFor(0.1_s); |
| |
| testHarness.mouseDown(UIEventButtonMaskPrimary); |
| testHarness.mouseUp(); |
| [webView waitForPendingMouseEvents]; |
| |
| EXPECT_WK_STREQ("2", [webView stringByEvaluatingJavaScript:@"window.mousedownCount"]); |
| EXPECT_WK_STREQ("2", [webView stringByEvaluatingJavaScript:@"window.mouseupCount"]); |
| EXPECT_WK_STREQ("2", [webView stringByEvaluatingJavaScript:@"window.pointerdownCount"]); |
| EXPECT_WK_STREQ("2", [webView stringByEvaluatingJavaScript:@"window.pointerupCount"]); |
| EXPECT_WK_STREQ("2", [webView stringByEvaluatingJavaScript:@"window.clickCount"]); |
| } |
| |
| TEST_F(iOSMouseSupport, NonPrimaryMouseButtonsForEvents) |
| { |
| // FIXME: It is not ideal that we have to wait for a static amount of time |
| // between sets of events if we want to test multiple pointerdowns. |
| |
| RetainPtr webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]); |
| RetainPtr webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]); |
| static const char* page = R"PAGEDATA( |
| <script> |
| window.mousedownInfo = "none"; |
| window.mouseupInfo = "none"; |
| window.pointerdownInfo = "none"; |
| window.pointerupInfo = "none"; |
| window.auxclickInfo = "none"; |
| window.clickInfo = "none"; |
| |
| document.addEventListener('mousedown', (e) => { |
| window.mousedownInfo = "button: " + e.button + " buttons: " + e.buttons + " which: " + e.which; |
| }); |
| document.addEventListener('mouseup', (e) => { |
| window.mouseupInfo = "button: " + e.button + " buttons: " + e.buttons + " which: " + e.which; |
| }); |
| document.addEventListener('pointerdown', (e) => { |
| window.pointerdownInfo = "button: " + e.button + " buttons: " + e.buttons + " which: " + e.which; |
| }); |
| document.addEventListener('pointerup', (e) => { |
| window.pointerupInfo = "button: " + e.button + " buttons: " + e.buttons + " which: " + e.which; |
| }); |
| document.addEventListener('auxclick', (e) => { |
| window.auxclickInfo = "button: " + e.button + " buttons: " + e.buttons + " which: " + e.which; |
| }); |
| document.addEventListener('click', (e) => { |
| window.clickInfo = "button: " + e.button + " buttons: " + e.buttons + " which: " + e.which; |
| }); |
| </script> |
| )PAGEDATA"; |
| |
| [webView synchronouslyLoadHTMLString:[NSString stringWithUTF8String:page]]; |
| |
| TestWebKitAPI::MouseEventTestHarness testHarness { webView.get() }; |
| testHarness.mouseMove(10, 10); |
| |
| auto performMouseClick = [&](UIEventButtonMask buttonMask) { |
| TestWebKitAPI::Util::runFor(0.1_s); |
| testHarness.mouseDown(buttonMask); |
| testHarness.mouseUp(); |
| [webView waitForPendingMouseEvents]; |
| }; |
| |
| auto assertResults = [&](int button, int downButtons, int upButtons) { |
| RetainPtr downString = [[NSString alloc] initWithFormat:@"button: %d buttons: %d which: %d", button, downButtons, button + 1]; |
| RetainPtr upString = [[NSString alloc] initWithFormat:@"button: %d buttons: %d which: %d", button, upButtons, button + 1]; |
| |
| EXPECT_WK_STREQ(downString.get(), [webView stringByEvaluatingJavaScript:@"window.mousedownInfo"]); |
| EXPECT_WK_STREQ(downString.get(), [webView stringByEvaluatingJavaScript:@"window.pointerdownInfo"]); |
| EXPECT_WK_STREQ(upString.get(), [webView stringByEvaluatingJavaScript:@"window.mouseupInfo"]); |
| EXPECT_WK_STREQ(upString.get(), [webView stringByEvaluatingJavaScript:@"window.pointerupInfo"]); |
| if (button) |
| EXPECT_WK_STREQ(upString.get(), [webView stringByEvaluatingJavaScript:@"window.auxclickInfo"]); |
| else |
| EXPECT_WK_STREQ(upString.get(), [webView stringByEvaluatingJavaScript:@"window.clickInfo"]); |
| }; |
| |
| performMouseClick(UIEventButtonMaskPrimary); |
| assertResults(0, 1, 0); |
| performMouseClick(WebKit::UIEventButtonMaskTertiary); |
| assertResults(1, 4, 0); |
| performMouseClick(WebKit::UIEventButtonMaskQuaternary); |
| assertResults(3, 8, 0); |
| performMouseClick(WebKit::UIEventButtonMaskQuinary); |
| assertResults(4, 16, 0); |
| performMouseClick(UIEventButtonMaskSecondary); |
| assertResults(2, 2, 0); |
| } |
| |
| #endif // HAVE(UI_POINTER_INTERACTION) |
| |
| #endif // PLATFORM(IOS) || PLATFORM(MACCATALYST) || PLATFORM(VISION) |