macCatalyst: Cursor should send mouse events, not touch events
https://bugs.webkit.org/show_bug.cgi?id=203175
<rdar://problem/56321134>

Reviewed by Simon Fraser.

Source/WebCore:

* dom/Element.cpp:
(WebCore::shouldIgnoreMouseEvent):
(WebCore::Element::dispatchMouseEvent):
* page/PointerCaptureController.cpp:
(WebCore::PointerCaptureController::reset):
Share more code between platforms that support touch events and platforms that don't;
we want to be able to dispatch pointer events that originate from mouse events
in macCatalyst despite touch events being enabled (by virtue of being iOS WebKit),
so un-ifdef that code.

Source/WebKit:

* Platform/spi/ios/UIKitSPI.h:
* Shared/NativeWebMouseEvent.h:
* Shared/ios/NativeWebMouseEventIOS.mm:
(WebKit::NativeWebMouseEvent::NativeWebMouseEvent):
Add a NativeWebMouseEvent constructor that doesn't actually wrap a native event,
for creating totally-synthetic events.

* UIProcess/ios/WKMouseGestureRecognizer.h: Added.
* UIProcess/ios/WKMouseGestureRecognizer.mm: Added.
(webEventModifiersForUIKeyModifierFlags):
(-[WKMouseGestureRecognizer initWithTarget:action:]):
(-[WKMouseGestureRecognizer setView:]):
(-[WKMouseGestureRecognizer lastMouseEvent]):
(-[WKMouseGestureRecognizer lastMouseLocation]):
(-[WKMouseGestureRecognizer mouseTouch]):
(-[WKMouseGestureRecognizer _wantsHoverEvents]):
(-[WKMouseGestureRecognizer reset]):
(-[WKMouseGestureRecognizer _shouldReceiveTouch:forEvent:recognizerView:]):
(-[WKMouseGestureRecognizer _shouldReceivePress:]):
(-[WKMouseGestureRecognizer createMouseEventWithType:]):
(-[WKMouseGestureRecognizer touchesBegan:withEvent:]):
(-[WKMouseGestureRecognizer touchesMoved:withEvent:]):
(-[WKMouseGestureRecognizer touchesEnded:withEvent:]):
(-[WKMouseGestureRecognizer touchesCancelled:withEvent:]):
(-[WKMouseGestureRecognizer _hoverEntered:withEvent:]):
(-[WKMouseGestureRecognizer _hoverMoved:withEvent:]):
(-[WKMouseGestureRecognizer _hoverExited:withEvent:]):
(-[WKMouseGestureRecognizer _hoverCancelled:withEvent:]):
(-[WKMouseGestureRecognizer locationInView:]):
(-[WKMouseGestureRecognizer canPreventGestureRecognizer:]):
(-[WKMouseGestureRecognizer canBePreventedByGestureRecognizer:]):
Instead of just using UIHoverGestureRecognizer, introduce a new gesture
recognizer that does what it did, but also supports cases where the button
is pressed (so, mousedown and mouseup instead of just mousemove),
and synthesizes mouse events for all state transitions. These events
should look roughly identical to what a non-macCatalyst app would get
in AppKit's mouseUp/mouseDown/mouseMoved NSResponder methods.

* SourcesCocoa.txt:
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]):
(-[WKContentView cleanupInteraction]):
(-[WKContentView _removeDefaultGestureRecognizers]):
(-[WKContentView _addDefaultGestureRecognizers]):
(-[WKContentView gestureRecognizer:shouldReceiveTouch:]):
(-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
(-[WKContentView _showShareSheet:inRect:completionHandler:]):
(-[WKContentView _mouseGestureRecognizerChanged:]):
(webEventFlagsForUIKeyModifierFlags): Deleted.
(-[WKContentView _hoverGestureRecognizerChanged:]): Deleted.
* WebKit.xcodeproj/project.pbxproj:
Make use of WKMouseGestureRecognizer to plumb the synthesized events directly
to the macOS-style "handleMouseEvent" codepath, instead of going through
the touch events path (and then subsequently generating synthetic clicks).
Also, ensure that other gesture recognizers ignore the mouse "touch" entirely
by returning NO in shouldReceiveTouch.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251320 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index c4d1b64..5ff25ab 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,21 @@
+2019-10-18  Tim Horton  <timothy_horton@apple.com>
+
+        macCatalyst: Cursor should send mouse events, not touch events
+        https://bugs.webkit.org/show_bug.cgi?id=203175
+        <rdar://problem/56321134>
+
+        Reviewed by Simon Fraser.
+
+        * dom/Element.cpp:
+        (WebCore::shouldIgnoreMouseEvent):
+        (WebCore::Element::dispatchMouseEvent):
+        * page/PointerCaptureController.cpp:
+        (WebCore::PointerCaptureController::reset):
+        Share more code between platforms that support touch events and platforms that don't;
+        we want to be able to dispatch pointer events that originate from mouse events
+        in macCatalyst despite touch events being enabled (by virtue of being iOS WebKit),
+        so un-ifdef that code.
+
 2019-10-18  Simon Fraser  <simon.fraser@apple.com>
 
         Make it possible to query scrollbar pseudo-style without having a scrollbar
diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp
index 965c40e..d1b47ea 100644
--- a/Source/WebCore/dom/Element.cpp
+++ b/Source/WebCore/dom/Element.cpp
@@ -303,7 +303,7 @@
     return platformEvent.type() == PlatformEvent::MouseForceChanged || platformEvent.type() == PlatformEvent::MouseForceDown || platformEvent.type() == PlatformEvent::MouseForceUp;
 }
 
-#if ENABLE(POINTER_EVENTS) && !ENABLE(TOUCH_EVENTS)
+#if ENABLE(POINTER_EVENTS)
 static bool isCompatibilityMouseEvent(const MouseEvent& mouseEvent)
 {
     // https://www.w3.org/TR/pointerevents/#compatibility-mapping-with-mouse-events
@@ -312,6 +312,35 @@
 }
 #endif
 
+static bool shouldIgnoreMouseEvent(Element& element, const MouseEvent& mouseEvent, const PlatformMouseEvent& platformEvent, bool& didNotSwallowEvent)
+{
+#if ENABLE(POINTER_EVENTS)
+    if (RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled()) {
+        if (auto* page = element.document().page()) {
+            auto& pointerCaptureController = page->pointerCaptureController();
+#if ENABLE(TOUCH_EVENTS)
+            if (platformEvent.pointerId() != mousePointerID && mouseEvent.type() != eventNames().clickEvent && pointerCaptureController.preventsCompatibilityMouseEventsForIdentifier(platformEvent.pointerId()))
+                return true;
+#else
+            UNUSED_PARAM(platformEvent);
+#endif
+            if (auto pointerEvent = pointerCaptureController.pointerEventForMouseEvent(mouseEvent)) {
+                pointerCaptureController.dispatchEvent(*pointerEvent, &element);
+                if (isCompatibilityMouseEvent(mouseEvent) && pointerCaptureController.preventsCompatibilityMouseEventsForIdentifier(pointerEvent->pointerId()))
+                    return true;
+                if (pointerEvent->defaultPrevented() || pointerEvent->defaultHandled()) {
+                    didNotSwallowEvent = false;
+                    if (pointerEvent->type() == eventNames().pointerdownEvent)
+                        return true;
+                }
+            }
+        }
+    }
+#endif
+
+    return false;
+}
+
 bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const AtomString& eventType, int detail, Element* relatedTarget)
 {
     if (isDisabledFormControl())
@@ -327,28 +356,8 @@
 
     bool didNotSwallowEvent = true;
 
-#if ENABLE(POINTER_EVENTS)
-    if (RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled()) {
-        if (auto* page = document().page()) {
-            auto& pointerCaptureController = page->pointerCaptureController();
-#if ENABLE(TOUCH_EVENTS)
-            if (mouseEvent->type() != eventNames().clickEvent && pointerCaptureController.preventsCompatibilityMouseEventsForIdentifier(platformEvent.pointerId()))
-                return false;
-#else
-            if (auto pointerEvent = pointerCaptureController.pointerEventForMouseEvent(mouseEvent)) {
-                pointerCaptureController.dispatchEvent(*pointerEvent, this);
-                if (isCompatibilityMouseEvent(mouseEvent) && pointerCaptureController.preventsCompatibilityMouseEventsForIdentifier(pointerEvent->pointerId()))
-                    return false;
-                if (pointerEvent->defaultPrevented() || pointerEvent->defaultHandled()) {
-                    didNotSwallowEvent = false;
-                    if (pointerEvent->type() == eventNames().pointerdownEvent)
-                        return false;
-                }
-            }
-#endif
-        }
-    }
-#endif
+    if (shouldIgnoreMouseEvent(*this, mouseEvent.get(), platformEvent, didNotSwallowEvent))
+        return false;
 
     ASSERT(!mouseEvent->target() || mouseEvent->target() != relatedTarget);
     dispatchEvent(mouseEvent);
diff --git a/Source/WebCore/page/PointerCaptureController.cpp b/Source/WebCore/page/PointerCaptureController.cpp
index fda80d5..16f432a 100644
--- a/Source/WebCore/page/PointerCaptureController.cpp
+++ b/Source/WebCore/page/PointerCaptureController.cpp
@@ -159,11 +159,10 @@
 void PointerCaptureController::reset()
 {
     m_activePointerIdsToCapturingData.clear();
-#if !ENABLE(TOUCH_EVENTS)
+
     CapturingData capturingData;
     capturingData.pointerType = PointerEvent::mousePointerType();
     m_activePointerIdsToCapturingData.add(mousePointerID, capturingData);
-#endif
 }
 
 void PointerCaptureController::touchWithIdentifierWasRemoved(PointerID pointerId)
diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog
index 707dc4f..a32db8e 100644
--- a/Source/WebKit/ChangeLog
+++ b/Source/WebKit/ChangeLog
@@ -1,3 +1,69 @@
+2019-10-18  Tim Horton  <timothy_horton@apple.com>
+
+        macCatalyst: Cursor should send mouse events, not touch events
+        https://bugs.webkit.org/show_bug.cgi?id=203175
+        <rdar://problem/56321134>
+
+        Reviewed by Simon Fraser.
+
+        * Platform/spi/ios/UIKitSPI.h:
+        * Shared/NativeWebMouseEvent.h:
+        * Shared/ios/NativeWebMouseEventIOS.mm:
+        (WebKit::NativeWebMouseEvent::NativeWebMouseEvent):
+        Add a NativeWebMouseEvent constructor that doesn't actually wrap a native event,
+        for creating totally-synthetic events.
+
+        * UIProcess/ios/WKMouseGestureRecognizer.h: Added.
+        * UIProcess/ios/WKMouseGestureRecognizer.mm: Added.
+        (webEventModifiersForUIKeyModifierFlags):
+        (-[WKMouseGestureRecognizer initWithTarget:action:]):
+        (-[WKMouseGestureRecognizer setView:]):
+        (-[WKMouseGestureRecognizer lastMouseEvent]):
+        (-[WKMouseGestureRecognizer lastMouseLocation]):
+        (-[WKMouseGestureRecognizer mouseTouch]):
+        (-[WKMouseGestureRecognizer _wantsHoverEvents]):
+        (-[WKMouseGestureRecognizer reset]):
+        (-[WKMouseGestureRecognizer _shouldReceiveTouch:forEvent:recognizerView:]):
+        (-[WKMouseGestureRecognizer _shouldReceivePress:]):
+        (-[WKMouseGestureRecognizer createMouseEventWithType:]):
+        (-[WKMouseGestureRecognizer touchesBegan:withEvent:]):
+        (-[WKMouseGestureRecognizer touchesMoved:withEvent:]):
+        (-[WKMouseGestureRecognizer touchesEnded:withEvent:]):
+        (-[WKMouseGestureRecognizer touchesCancelled:withEvent:]):
+        (-[WKMouseGestureRecognizer _hoverEntered:withEvent:]):
+        (-[WKMouseGestureRecognizer _hoverMoved:withEvent:]):
+        (-[WKMouseGestureRecognizer _hoverExited:withEvent:]):
+        (-[WKMouseGestureRecognizer _hoverCancelled:withEvent:]):
+        (-[WKMouseGestureRecognizer locationInView:]):
+        (-[WKMouseGestureRecognizer canPreventGestureRecognizer:]):
+        (-[WKMouseGestureRecognizer canBePreventedByGestureRecognizer:]):
+        Instead of just using UIHoverGestureRecognizer, introduce a new gesture
+        recognizer that does what it did, but also supports cases where the button
+        is pressed (so, mousedown and mouseup instead of just mousemove),
+        and synthesizes mouse events for all state transitions. These events
+        should look roughly identical to what a non-macCatalyst app would get
+        in AppKit's mouseUp/mouseDown/mouseMoved NSResponder methods.
+
+        * SourcesCocoa.txt:
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setupInteraction]):
+        (-[WKContentView cleanupInteraction]):
+        (-[WKContentView _removeDefaultGestureRecognizers]):
+        (-[WKContentView _addDefaultGestureRecognizers]):
+        (-[WKContentView gestureRecognizer:shouldReceiveTouch:]):
+        (-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
+        (-[WKContentView _showShareSheet:inRect:completionHandler:]):
+        (-[WKContentView _mouseGestureRecognizerChanged:]):
+        (webEventFlagsForUIKeyModifierFlags): Deleted.
+        (-[WKContentView _hoverGestureRecognizerChanged:]): Deleted.
+        * WebKit.xcodeproj/project.pbxproj:
+        Make use of WKMouseGestureRecognizer to plumb the synthesized events directly
+        to the macOS-style "handleMouseEvent" codepath, instead of going through
+        the touch events path (and then subsequently generating synthetic clicks).
+        Also, ensure that other gesture recognizers ignore the mouse "touch" entirely
+        by returning NO in shouldReceiveTouch.
+
 2019-10-18  Jiewen Tan  <jiewen_tan@apple.com>
 
         [WebAuthn] Warn users when no credentials are found
diff --git a/Source/WebKit/Platform/spi/ios/UIKitSPI.h b/Source/WebKit/Platform/spi/ios/UIKitSPI.h
index 3eaf460..f086d4da3 100644
--- a/Source/WebKit/Platform/spi/ios/UIKitSPI.h
+++ b/Source/WebKit/Platform/spi/ios/UIKitSPI.h
@@ -1116,6 +1116,20 @@
 - (void)_updateSafeAreaInsets;
 @end
 
+@interface UIGestureRecognizer (IPI)
+- (BOOL)_paused;
+@property (nonatomic) UIView *view;
+@property (nonatomic, assign, getter=_acceptsFailureRequirements, setter=_setAcceptsFailureRequiments:) BOOL _acceptsFailureRequirements;
+@end
+
+@interface UIHoverEvent : UIEvent
+- (void)setNeedsHitTestReset;
+@end
+
+@interface UIApplication (IPI)
+- (UIHoverEvent *)_hoverEventForWindow:(UIWindow *)window;
+@end
+
 @interface UIScrollView (IPI)
 - (CGFloat)_rubberBandOffsetForOffset:(CGFloat)newOffset maxOffset:(CGFloat)maxOffset minOffset:(CGFloat)minOffset range:(CGFloat)range outside:(BOOL *)outside;
 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary *)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
diff --git a/Source/WebKit/Shared/NativeWebMouseEvent.h b/Source/WebKit/Shared/NativeWebMouseEvent.h
index bbddd948..ba7f93c 100644
--- a/Source/WebKit/Shared/NativeWebMouseEvent.h
+++ b/Source/WebKit/Shared/NativeWebMouseEvent.h
@@ -63,6 +63,7 @@
     NativeWebMouseEvent(GdkEvent*, int);
 #elif PLATFORM(IOS_FAMILY)
     NativeWebMouseEvent(::WebEvent *);
+    NativeWebMouseEvent(Type, Button, unsigned short buttons, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, float deltaX, float deltaY, float deltaZ, int clickCount, OptionSet<Modifier>, WallTime timestamp, double force);
 #elif USE(LIBWPE)
     NativeWebMouseEvent(struct wpe_input_pointer_event*, float deviceScaleFactor);
 #elif PLATFORM(WIN)
diff --git a/Source/WebKit/Shared/ios/NativeWebMouseEventIOS.mm b/Source/WebKit/Shared/ios/NativeWebMouseEventIOS.mm
index b9bcf1b..7db00f1 100644
--- a/Source/WebKit/Shared/ios/NativeWebMouseEventIOS.mm
+++ b/Source/WebKit/Shared/ios/NativeWebMouseEventIOS.mm
@@ -38,6 +38,11 @@
 {
 }
 
+NativeWebMouseEvent::NativeWebMouseEvent(Type type, Button button, unsigned short buttons, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, float deltaX, float deltaY, float deltaZ, int clickCount, OptionSet<Modifier> modifiers, WallTime timestamp, double force)
+    : WebMouseEvent(type, button, buttons, position, globalPosition, deltaX, deltaY, deltaZ, clickCount, modifiers, timestamp, force)
+{
+}
+
 } // namespace WebKit
 
 #endif // PLATFORM(IOS_FAMILY)
diff --git a/Source/WebKit/SourcesCocoa.txt b/Source/WebKit/SourcesCocoa.txt
index 327ea4e..257e828 100644
--- a/Source/WebKit/SourcesCocoa.txt
+++ b/Source/WebKit/SourcesCocoa.txt
@@ -426,6 +426,7 @@
 UIProcess/ios/WKHighlightLongPressGestureRecognizer.mm
 UIProcess/ios/WKInspectorNodeSearchGestureRecognizer.mm
 UIProcess/ios/WKKeyboardScrollingAnimator.mm
+UIProcess/ios/WKMouseGestureRecognizer.mm
 UIProcess/ios/WKPasswordView.mm
 UIProcess/ios/WKPDFPageNumberIndicator.mm
 UIProcess/ios/WKPDFView.mm
diff --git a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
index 10fe619..1a784a6 100644
--- a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
+++ b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
@@ -99,7 +99,6 @@
 
 @class _UILookupGestureRecognizer;
 @class _UIHighlightView;
-@class UIHoverGestureRecognizer;
 @class UITargetedPreview;
 @class WebEvent;
 @class WKActionSheetAssistant;
@@ -109,6 +108,7 @@
 @class WKFormInputControl;
 @class WKFormInputSession;
 @class WKHighlightLongPressGestureRecognizer;
+@class WKMouseGestureRecognizer;
 @class WKInspectorNodeSearchGestureRecognizer;
 
 typedef void (^UIWKAutocorrectionCompletionHandler)(UIWKAutocorrectionRects *rectsForInput);
@@ -236,8 +236,7 @@
 #endif
 
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
-    RetainPtr<UIHoverGestureRecognizer> _hoverGestureRecognizer;
-    CGPoint _lastHoverLocation;
+    RetainPtr<WKMouseGestureRecognizer> _mouseGestureRecognizer;
 #endif
 
     RetainPtr<UIWKTextInteractionAssistant> _textSelectionAssistant;
diff --git a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
index 76ce342..bda6cca 100644
--- a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
+++ b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
@@ -55,6 +55,7 @@
 #import "WKHighlightLongPressGestureRecognizer.h"
 #import "WKImagePreviewViewController.h"
 #import "WKInspectorNodeSearchGestureRecognizer.h"
+#import "WKMouseGestureRecognizer.h"
 #import "WKNSURLExtras.h"
 #import "WKPreviewActionItemIdentifiers.h"
 #import "WKPreviewActionItemInternal.h"
@@ -133,11 +134,6 @@
 #import <UIKit/_UILookupGestureRecognizer.h>
 #endif
 
-#if HAVE(HOVER_GESTURE_RECOGNIZER)
-#import "NativeWebMouseEvent.h"
-#import <UIKit/UIHoverGestureRecognizer.h>
-#endif
-
 #if ENABLE(INPUT_TYPE_COLOR)
 #import "WKFormColorControl.h"
 #endif
@@ -745,9 +741,9 @@
     [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
 
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
-    _hoverGestureRecognizer = adoptNS([[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(_hoverGestureRecognizerChanged:)]);
-    [_hoverGestureRecognizer setDelegate:self];
-    [self addGestureRecognizer:_hoverGestureRecognizer.get()];
+    _mouseGestureRecognizer = adoptNS([[WKMouseGestureRecognizer alloc] initWithTarget:self action:@selector(_mouseGestureRecognizerChanged:)]);
+    [_mouseGestureRecognizer setDelegate:self];
+    [self addGestureRecognizer:_mouseGestureRecognizer.get()];
 #endif
 
 #if PLATFORM(MACCATALYST)    
@@ -897,8 +893,8 @@
     [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
 
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
-    [_hoverGestureRecognizer setDelegate:nil];
-    [self removeGestureRecognizer:_hoverGestureRecognizer.get()];
+    [_mouseGestureRecognizer setDelegate:nil];
+    [self removeGestureRecognizer:_mouseGestureRecognizer.get()];
 #endif
 
 #if PLATFORM(MACCATALYST)    
@@ -1014,7 +1010,7 @@
     [self removeGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()];
     [self removeGestureRecognizer:_stylusSingleTapGestureRecognizer.get()];
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
-    [self removeGestureRecognizer:_hoverGestureRecognizer.get()];
+    [self removeGestureRecognizer:_mouseGestureRecognizer.get()];
 #endif
 #if PLATFORM(MACCATALYST)
     [self removeGestureRecognizer:_lookupGestureRecognizer.get()];
@@ -1040,7 +1036,7 @@
     [self addGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()];
     [self addGestureRecognizer:_stylusSingleTapGestureRecognizer.get()];
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
-    [self addGestureRecognizer:_hoverGestureRecognizer.get()];
+    [self addGestureRecognizer:_mouseGestureRecognizer.get()];
 #endif
 #if PLATFORM(MACCATALYST)
     [self addGestureRecognizer:_lookupGestureRecognizer.get()];
@@ -1449,17 +1445,24 @@
 
 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
 {
-    if (gestureRecognizer != _touchActionLeftSwipeGestureRecognizer && gestureRecognizer != _touchActionRightSwipeGestureRecognizer && gestureRecognizer != _touchActionUpSwipeGestureRecognizer && gestureRecognizer != _touchActionDownSwipeGestureRecognizer)
-        return true;
+#if HAVE(HOVER_GESTURE_RECOGNIZER)
+    if (gestureRecognizer != _mouseGestureRecognizer && [_mouseGestureRecognizer mouseTouch] == touch)
+        return NO;
+#endif
 
-    // We update the enabled state of the various swipe gesture recognizers such that if we have a unidirectional touch-action
-    // specified (only pan-x or only pan-y) we enable the two recognizers in the opposite axis to prevent scrolling from starting
-    // if the initial gesture is such a swipe. Since the recognizers are specified to use a single finger for recognition, we don't
-    // need to worry about the case where there may be more than a single touch for a given UIScrollView.
-    auto touchActions = WebKit::touchActionsForPoint(self, WebCore::roundedIntPoint([touch locationInView:self]));
-    if (gestureRecognizer == _touchActionLeftSwipeGestureRecognizer || gestureRecognizer == _touchActionRightSwipeGestureRecognizer)
-        return touchActions == WebCore::TouchAction::PanY;
-    return touchActions == WebCore::TouchAction::PanX;
+    if (gestureRecognizer == _touchActionLeftSwipeGestureRecognizer || gestureRecognizer == _touchActionRightSwipeGestureRecognizer || gestureRecognizer == _touchActionUpSwipeGestureRecognizer || gestureRecognizer == _touchActionDownSwipeGestureRecognizer) {
+
+        // We update the enabled state of the various swipe gesture recognizers such that if we have a unidirectional touch-action
+        // specified (only pan-x or only pan-y) we enable the two recognizers in the opposite axis to prevent scrolling from starting
+        // if the initial gesture is such a swipe. Since the recognizers are specified to use a single finger for recognition, we don't
+        // need to worry about the case where there may be more than a single touch for a given UIScrollView.
+        auto touchActions = WebKit::touchActionsForPoint(self, WebCore::roundedIntPoint([touch locationInView:self]));
+        if (gestureRecognizer == _touchActionLeftSwipeGestureRecognizer || gestureRecognizer == _touchActionRightSwipeGestureRecognizer)
+            return touchActions == WebCore::TouchAction::PanY;
+        return touchActions == WebCore::TouchAction::PanX;
+    }
+
+    return YES;
 }
 
 #pragma mark - WKTouchActionGestureRecognizerDelegate implementation
@@ -1956,7 +1959,7 @@
         return YES;
 
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
-    if ([gestureRecognizer isKindOfClass:[UIHoverGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[UIHoverGestureRecognizer class]])
+    if ([gestureRecognizer isKindOfClass:[WKMouseGestureRecognizer class]] || [otherGestureRecognizer isKindOfClass:[WKMouseGestureRecognizer class]])
         return YES;
 #endif
 
@@ -6352,8 +6355,10 @@
 
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
     if (!rect) {
-        auto hoverLocationInWebView = [self convertPoint:_lastHoverLocation toView:_webView];
-        rect = WebCore::FloatRect(hoverLocationInWebView.x, hoverLocationInWebView.y, 1, 1);
+        if (auto lastMouseLocation = [_mouseGestureRecognizer lastMouseLocation]) {
+            auto hoverLocationInWebView = [self convertPoint:*lastMouseLocation toView:_webView];
+            rect = WebCore::FloatRect(hoverLocationInWebView.x, hoverLocationInWebView.y, 1, 1);
+        }
     }
 #endif
     
@@ -7750,46 +7755,13 @@
 #endif
 
 #if HAVE(HOVER_GESTURE_RECOGNIZER)
-static WebEventFlags webEventFlagsForUIKeyModifierFlags(UIKeyModifierFlags flags)
-{
-    WebEventFlags eventFlags = 0;
-    if (flags & UIKeyModifierShift)
-        eventFlags |= WebEventFlagMaskLeftShiftKey;
-    if (flags & UIKeyModifierControl)
-        eventFlags |= WebEventFlagMaskLeftControlKey;
-    if (flags & UIKeyModifierAlternate)
-        eventFlags |= WebEventFlagMaskLeftOptionKey;
-    if (flags & UIKeyModifierCommand)
-        eventFlags |= WebEventFlagMaskLeftCommandKey;
-    if (flags & UIKeyModifierAlphaShift)
-        eventFlags |= WebEventFlagMaskLeftCapsLockKey;
-    return eventFlags;
-}
-
-- (void)_hoverGestureRecognizerChanged:(UIGestureRecognizer *)gestureRecognizer
+- (void)_mouseGestureRecognizerChanged:(WKMouseGestureRecognizer *)gestureRecognizer
 {
     if (!_page->hasRunningProcess())
         return;
 
-    // Make a timestamp that matches UITouch and UIEvent.
-    CFTimeInterval timestamp = GSCurrentEventTimestamp() / 1000000000.0;
-
-    CGPoint point;
-    switch (gestureRecognizer.state) {
-    case UIGestureRecognizerStateBegan:
-    case UIGestureRecognizerStateChanged:
-        point = [gestureRecognizer locationInView:self];
-        _lastHoverLocation = point;
-        break;
-    case UIGestureRecognizerStateEnded:
-    case UIGestureRecognizerStateCancelled:
-    default:
-        point = CGPointMake(-1, -1);
-        break;
-    }
-
-    auto event = adoptNS([[::WebEvent alloc] initWithMouseEventType:WebEventMouseMoved timeStamp:timestamp location:point modifiers:webEventFlagsForUIKeyModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer))]);
-    _page->handleMouseEvent(WebKit::NativeWebMouseEvent(event.get()));
+    if (auto event = gestureRecognizer.lastMouseEvent)
+        _page->handleMouseEvent(*event);
 }
 #endif
 
diff --git a/Source/WebKit/UIProcess/ios/WKMouseGestureRecognizer.h b/Source/WebKit/UIProcess/ios/WKMouseGestureRecognizer.h
new file mode 100644
index 0000000..5628cf3
--- /dev/null
+++ b/Source/WebKit/UIProcess/ios/WKMouseGestureRecognizer.h
@@ -0,0 +1,39 @@
+/*
+* Copyright (C) 2019 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.
+*/
+
+#if HAVE(HOVER_GESTURE_RECOGNIZER)
+
+#import "NativeWebMouseEvent.h"
+
+@interface WKMouseGestureRecognizer : UIGestureRecognizer
+
+- (WTF::Optional<CGPoint>)lastMouseLocation;
+- (WebKit::NativeWebMouseEvent *)lastMouseEvent;
+
+- (UITouch *)mouseTouch;
+
+@end
+
+#endif
diff --git a/Source/WebKit/UIProcess/ios/WKMouseGestureRecognizer.mm b/Source/WebKit/UIProcess/ios/WKMouseGestureRecognizer.mm
new file mode 100644
index 0000000..28d8b51
--- /dev/null
+++ b/Source/WebKit/UIProcess/ios/WKMouseGestureRecognizer.mm
@@ -0,0 +1,252 @@
+/*
+* Copyright (C) 2019 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"
+#import "WKMouseGestureRecognizer.h"
+
+#if HAVE(HOVER_GESTURE_RECOGNIZER)
+
+#import "NativeWebMouseEvent.h"
+#import "UIKitSPI.h"
+#import <wtf/Optional.h>
+
+static OptionSet<WebKit::WebEvent::Modifier> webEventModifiersForUIKeyModifierFlags(UIKeyModifierFlags flags)
+{
+    OptionSet<WebKit::WebEvent::Modifier> modifiers;
+    if (flags & UIKeyModifierShift)
+        modifiers.add(WebKit::WebEvent::Modifier::ShiftKey);
+    if (flags & UIKeyModifierControl)
+        modifiers.add(WebKit::WebEvent::Modifier::ControlKey);
+    if (flags & UIKeyModifierAlternate)
+        modifiers.add(WebKit::WebEvent::Modifier::AltKey);
+    if (flags & UIKeyModifierCommand)
+        modifiers.add(WebKit::WebEvent::Modifier::MetaKey);
+    if (flags & UIKeyModifierAlphaShift)
+        modifiers.add(WebKit::WebEvent::Modifier::CapsLockKey);
+    return modifiers;
+}
+
+@implementation WKMouseGestureRecognizer {
+    RetainPtr<UIHoverEvent> _currentHoverEvent;
+    RetainPtr<UITouch> _currentTouch;
+
+    BOOL _touching;
+
+    std::unique_ptr<WebKit::NativeWebMouseEvent> _lastEvent;
+    Optional<CGPoint> _lastLocation;
+}
+
+- (instancetype)initWithTarget:(id)target action:(SEL)action
+{
+    self = [super initWithTarget:target action:action];
+    if (!self)
+        return nil;
+
+    [self _setAcceptsFailureRequiments:NO];
+
+    return self;
+}
+
+- (void)setView:(UIView *)view
+{
+    if (view == self.view)
+        return;
+
+    [super setView:view];
+
+    if (view._window) {
+        UIHoverEvent *hoverEvent = [UIApp _hoverEventForWindow:view._window];
+        [hoverEvent setNeedsHitTestReset];
+    }
+}
+
+- (WebKit::NativeWebMouseEvent *)lastMouseEvent
+{
+    return _lastEvent.get();
+}
+
+- (WTF::Optional<CGPoint>)lastMouseLocation
+{
+    return _lastLocation;
+}
+
+- (UITouch *)mouseTouch
+{
+    return _currentTouch.get();
+}
+
+- (BOOL)_wantsHoverEvents
+{
+    return YES;
+}
+
+- (void)reset
+{
+    [super reset];
+    _currentHoverEvent = nil;
+    _currentTouch = nil;
+}
+
+- (BOOL)_shouldReceiveTouch:(UITouch *)touch forEvent:(UIEvent *)event recognizerView:(UIView *)recognizerView
+{
+    return touch == _currentTouch;
+}
+
+- (BOOL)_shouldReceivePress:(UIPress *)press
+{
+    return NO;
+}
+
+- (std::unique_ptr<WebKit::NativeWebMouseEvent>)createMouseEventWithType:(WebKit::WebEvent::Type)type
+{
+    auto modifiers = webEventModifiersForUIKeyModifierFlags(self.modifierFlags);
+    BOOL hasControlModifier = modifiers.contains(WebKit::WebEvent::Modifier::ControlKey);
+
+    auto button = [&] {
+        if (!_touching || type == WebKit::WebEvent::Type::MouseUp)
+            return WebKit::WebMouseEvent::NoButton;
+        if (hasControlModifier)
+            return WebKit::WebMouseEvent::RightButton;
+        return WebKit::WebMouseEvent::LeftButton;
+    }();
+
+    auto buttons = [&] {
+        if (!_touching)
+            return 0;
+        if (hasControlModifier)
+            return 2;
+        return 1;
+    }();
+
+    WebCore::IntPoint point { [self locationInView:self.view] };
+
+    auto timestamp = WallTime::fromRawSeconds(Seconds::fromNanoseconds(GSCurrentEventTimestamp()).seconds());
+
+    return WTF::makeUnique<WebKit::NativeWebMouseEvent>(type, button, buttons, point, point, 0, 0, 0, [_currentTouch tapCount], modifiers, timestamp, 0);
+}
+
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    _touching = YES;
+
+    _lastEvent = [self createMouseEventWithType:WebKit::WebEvent::MouseDown];
+    _lastLocation = [self locationInView:self.view];
+
+    self.state = UIGestureRecognizerStateChanged;
+}
+
+- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    _lastEvent = [self createMouseEventWithType:WebKit::WebEvent::MouseMove];
+    _lastLocation = [self locationInView:self.view];
+
+    self.state = UIGestureRecognizerStateChanged;
+}
+
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    _touching = NO;
+
+    _lastEvent = [self createMouseEventWithType:WebKit::WebEvent::MouseUp];
+    _lastLocation = [self locationInView:self.view];
+
+    self.state = UIGestureRecognizerStateChanged;
+}
+
+- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    [self touchesEnded:touches withEvent:event];
+}
+
+- (void)_hoverEntered:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    _lastEvent = [self createMouseEventWithType:WebKit::WebEvent::MouseMove];
+
+    if (_currentHoverEvent == nil && touches.count == 1 && [event isKindOfClass:NSClassFromString(@"UIHoverEvent")]) {
+        _currentHoverEvent = (UIHoverEvent *)event;
+        _currentTouch = touches.anyObject;
+        _lastLocation = [self locationInView:self.view];
+        self.state = UIGestureRecognizerStateBegan;
+    }
+
+    [super _hoverEntered:touches withEvent:event];
+}
+
+- (void)_hoverMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    if (_touching) {
+        _lastEvent = nullptr;
+        return;
+    }
+
+    _lastEvent = [self createMouseEventWithType:WebKit::WebEvent::MouseMove];
+    _lastLocation = [self locationInView:self.view];
+
+    if (_currentHoverEvent == event && [touches containsObject:_currentTouch.get()])
+        self.state = UIGestureRecognizerStateChanged;
+
+    [super _hoverMoved:touches withEvent:event];
+}
+
+- (void)_hoverExited:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    _lastEvent = [self createMouseEventWithType:WebKit::WebEvent::MouseMove];
+    _lastLocation = [self locationInView:self.view];
+
+    if (_currentHoverEvent == event) {
+        _currentHoverEvent = nil;
+        _currentTouch = nil;
+        self.state = UIGestureRecognizerStateEnded;
+    }
+
+    [super _hoverExited:touches withEvent:event];
+}
+
+- (void)_hoverCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    [self _hoverExited:touches withEvent:event];
+    [super _hoverCancelled:touches withEvent:event];
+}
+
+- (CGPoint)locationInView:(UIView *)view
+{
+    if (!_currentTouch)
+        return CGPointMake(-1, -1);
+    return [_currentTouch locationInView:view];
+}
+
+- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
+{
+    return NO;
+}
+
+- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer
+{
+    return NO;
+}
+
+@end
+
+#endif
diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj
index ca1bf54..140179e 100644
--- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj
+++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj
@@ -705,6 +705,7 @@
 		2DACE64E18ADBFF000E4CA76 /* _WKThumbnailViewInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DACE64D18ADBFF000E4CA76 /* _WKThumbnailViewInternal.h */; };
 		2DAF06D618BD1A470081CEB1 /* SmartMagnificationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DAF06D418BD1A470081CEB1 /* SmartMagnificationController.h */; };
 		2DB7667121B5E48A0045DDB1 /* AccessibilitySupportSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DB7667021B5E48A0045DDB1 /* AccessibilitySupportSPI.h */; };
+		2DB94299234E7A7F00E776AD /* WKMouseGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DB94297234E7A7F00E776AD /* WKMouseGestureRecognizer.h */; };
 		2DC18FAD218910490025A88D /* WKDrawingView.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC18FAB218910480025A88D /* WKDrawingView.h */; };
 		2DC18FB0218912640025A88D /* PencilKitSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC18FAF218912640025A88D /* PencilKitSPI.h */; };
 		2DC18FB3218A6E9E0025A88D /* RemoteLayerTreeViews.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC18FB1218A6E9E0025A88D /* RemoteLayerTreeViews.h */; };
@@ -2855,6 +2856,8 @@
 		2DAF06D818BD23BA0081CEB1 /* SmartMagnificationController.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; name = SmartMagnificationController.messages.in; path = ios/SmartMagnificationController.messages.in; sourceTree = "<group>"; };
 		2DAF4FFA1B636181006013D6 /* ViewGestureController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ViewGestureController.cpp; sourceTree = "<group>"; };
 		2DB7667021B5E48A0045DDB1 /* AccessibilitySupportSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccessibilitySupportSPI.h; sourceTree = "<group>"; };
+		2DB94297234E7A7F00E776AD /* WKMouseGestureRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WKMouseGestureRecognizer.h; path = ios/WKMouseGestureRecognizer.h; sourceTree = "<group>"; };
+		2DB94298234E7A7F00E776AD /* WKMouseGestureRecognizer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = WKMouseGestureRecognizer.mm; path = ios/WKMouseGestureRecognizer.mm; sourceTree = "<group>"; };
 		2DC18FAB218910480025A88D /* WKDrawingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKDrawingView.h; path = ios/WKDrawingView.h; sourceTree = "<group>"; };
 		2DC18FAC218910480025A88D /* WKDrawingView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKDrawingView.mm; path = ios/WKDrawingView.mm; sourceTree = "<group>"; };
 		2DC18FAF218912640025A88D /* PencilKitSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PencilKitSPI.h; sourceTree = "<group>"; };
@@ -6137,6 +6140,8 @@
 				A54293A3195A43C6002782C7 /* WKInspectorNodeSearchGestureRecognizer.mm */,
 				2DD5E127210ADC7A00DB6012 /* WKKeyboardScrollingAnimator.h */,
 				2DD5E126210ADC7A00DB6012 /* WKKeyboardScrollingAnimator.mm */,
+				2DB94297234E7A7F00E776AD /* WKMouseGestureRecognizer.h */,
+				2DB94298234E7A7F00E776AD /* WKMouseGestureRecognizer.mm */,
 				A15EEDE41E301CEE000069B0 /* WKPasswordView.h */,
 				A15EEDE31E301CEE000069B0 /* WKPasswordView.mm */,
 				2D6AB53F192B1C4A003A9FD1 /* WKPDFPageNumberIndicator.h */,
@@ -10258,6 +10263,7 @@
 				5CABDC8622C40FDE001EDE8E /* WKMessageListener.h in Headers */,
 				C11E1694212B87C500985FF6 /* WKMockDisplay.h in Headers */,
 				411A8DDB20DDD1AC0060D34F /* WKMockMediaDevice.h in Headers */,
+				2DB94299234E7A7F00E776AD /* WKMouseGestureRecognizer.h in Headers */,
 				BC4075FE124FF0270068F20A /* WKMutableArray.h in Headers */,
 				BC407600124FF0270068F20A /* WKMutableDictionary.h in Headers */,
 				C09AE5E9125257C20025825D /* WKNativeEvent.h in Headers */,