[iOS] Implement a faster click detection that intercepts double-tap-to-zoom if possible
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2019 23:43:04 +0000 (23:43 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2019 23:43:04 +0000 (23:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195473
<rdar://problem/48718396>

Reviewed by Wenson Hsieh (with some help from Dan Bates).

Source/WebKit:

Adds a new algorithm, behind a flag FasterClicksEnabled, that can trigger a click
event without waiting to see if a double tap will occur. It does this by examining
the amount of zoom that would be triggered if it was a double tap, and if that value
doesn't exceed a set threshold, commits to the click event instead.

This is implemented by having the Web Process respond to the potential click with
some geometry information. If the UI Process receives the information before the
second tap in a double tap, it can decide to trigger a click.

* Shared/WebPreferences.yaml: New internal feature so this can be toggled in
    a UI for testing.

* SourcesCocoa.txt: Renamed WKSyntheticTapGestureRecognizer.
* WebKit.xcodeproj/project.pbxproj: Ditto.

* UIProcess/ios/WKSyntheticTapGestureRecognizer.h:
* UIProcess/ios/WKSyntheticTapGestureRecognizer.m:
(-[WKSyntheticTapGestureRecognizer setGestureIdentifiedTarget:action:]):
(-[WKSyntheticTapGestureRecognizer setGestureFailedTarget:action:]):
(-[WKSyntheticTapGestureRecognizer setResetTarget:action:]):
(-[WKSyntheticTapGestureRecognizer setState:]):
(-[WKSyntheticTapGestureRecognizer reset]):  Renamed WKSyntheticClickTapGestureRecognizer to
    WKSyntheticTapGestureRecognizer, changed the signature of the main function to be a bit
    more clear about what it does, and added a gesture failed target.

* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _initialScaleFactor]):
(-[WKWebView _contentZoomScale]):
(-[WKWebView _targetContentZoomScaleForRect:currentScale:fitEntireRect:minimumScale:maximumScale:]):
    Exposed the initial content scale, the current scale and added a declaration that
    was missing from the .h.

* UIProcess/WebPageProxy.messages.in: Add a new message,
    HandleSmartMagnificationInformationForPotentialTap, to
    communicate the geometry of the clicked node to the UI Process.

* UIProcess/PageClient.h: Pure virtual function for the geometry message response.
* UIProcess/WebPageProxy.h: Ditto.

* UIProcess/ios/PageClientImplIOS.h: Calls into the WKContentView.
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::handleSmartMagnificationInformationForPotentialTap):

* UIProcess/ios/SmartMagnificationController.h:
* UIProcess/ios/SmartMagnificationController.mm:
(WebKit::SmartMagnificationController::calculatePotentialZoomParameters): A new method that
    asks the WKContentView to work out what the zoom factor will be for a potential double
    tap at a location.
(WebKit::SmartMagnificationController::smartMagnificationTargetRectAndZoomScales): New implementation
    of this function to avoid multiple out-arguments.

* UIProcess/ios/WKContentView.h:
* UIProcess/ios/WKContentView.mm:
(-[WKContentView _initialScaleFactor]):
(-[WKContentView _contentZoomScale]):
(-[WKContentView _targetContentZoomScaleForRect:currentScale:fitEntireRect:minimumScale:maximumScale:]):
    Exposed the initial content scale, the current scale and the target zoom scale. These
    all just call into the WKWebView implementation.

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _createAndConfigureDoubleTapGestureRecognizer]): Use a WKSyntheticTapGestureRecognizer instead
    of a generic one, so we can capture the failure.
(-[WKContentView setupInteraction]):
(-[WKContentView cleanupInteraction]):
(-[WKContentView _handleSmartMagnificationInformationForPotentialTap:origin:renderRect:fitEntireRect:viewportMinimumScale:viewportMaximumScale:]):
    New method that responds to the incoming Web Process message, and decides if any
    potential zoom would be "significant".
(-[WKContentView _singleTapIdentified:]):
(-[WKContentView _doubleTapDidFail:]):
(-[WKContentView _didCompleteSyntheticClick]):
(-[WKContentView _singleTapRecognized:]):
(-[WKContentView _doubleTapRecognized:]):
    Add some release logging.
(-[WKContentView _singleTapCommited:]): Deleted.

* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::potentialTapAtPosition):
(WebKit::WebPageProxy::handleSmartMagnificationInformationForPotentialTap):
* WebProcess/WebPage/ViewGestureGeometryCollector.h:
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:
    Removed an unused parameter from the existing message.

* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::potentialTapAtPosition): Calculates the geometry of the element
if requested, and sends it to the UIProcess.

LayoutTests:

Implement a test (iPad only) that sets up a page with zoomable content
but not quite at a significant scale, meaning we should dispatch a click
event rather than Double Tap To Zoom.

In order to do this, a humanSpeedDoubleTapAt() method was added to
UIHelper that sleeps a bit between taps, otherwise the double tap
gesture is recognized before the Web Process has had a chance to
evaluate the potential click.

* fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom-expected.txt: Added.
* fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom.html: Added.
* platform/ios/TestExpectations:
* platform/ipad/TestExpectations:
* resources/ui-helper.js:
(window.UIHelper.humanSpeedDoubleTapAt):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@242757 268f45cc-cd09-0410-ab3c-d52691b4dbfc

29 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom.html [new file with mode: 0644]
LayoutTests/platform/ios/TestExpectations
LayoutTests/resources/ui-helper.js
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/SourcesCocoa.txt
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h
Source/WebKit/UIProcess/PageClient.h
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebPageProxy.messages.in
Source/WebKit/UIProcess/ios/PageClientImplIOS.h
Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit/UIProcess/ios/SmartMagnificationController.h
Source/WebKit/UIProcess/ios/SmartMagnificationController.mm
Source/WebKit/UIProcess/ios/WKContentView.h
Source/WebKit/UIProcess/ios/WKContentView.mm
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/UIProcess/ios/WKSyntheticTapGestureRecognizer.h [moved from Source/WebKit/UIProcess/ios/WKSyntheticClickTapGestureRecognizer.h with 73% similarity]
Source/WebKit/UIProcess/ios/WKSyntheticTapGestureRecognizer.m [moved from Source/WebKit/UIProcess/ios/WKSyntheticClickTapGestureRecognizer.m with 69% similarity]
Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/WebPage/ViewGestureGeometryCollector.h
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

index d6e90ef..f002201 100644 (file)
@@ -1,3 +1,27 @@
+2019-03-11  Dean Jackson  <dino@apple.com>
+
+        [iOS] Implement a faster click detection that intercepts double-tap-to-zoom if possible
+        https://bugs.webkit.org/show_bug.cgi?id=195473
+        <rdar://problem/48718396>
+
+        Reviewed by Wenson Hsieh (with some help from Dan Bates).
+
+        Implement a test (iPad only) that sets up a page with zoomable content
+        but not quite at a significant scale, meaning we should dispatch a click
+        event rather than Double Tap To Zoom.
+
+        In order to do this, a humanSpeedDoubleTapAt() method was added to
+        UIHelper that sleeps a bit between taps, otherwise the double tap
+        gesture is recognized before the Web Process has had a chance to
+        evaluate the potential click.
+
+        * fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom-expected.txt: Added.
+        * fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom.html: Added.
+        * platform/ios/TestExpectations:
+        * platform/ipad/TestExpectations:
+        * resources/ui-helper.js:
+        (window.UIHelper.humanSpeedDoubleTapAt):
+
 2019-03-11  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [macOS] Dispatching reentrant "contextmenu" events may cause crashes
diff --git a/LayoutTests/fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom-expected.txt b/LayoutTests/fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom-expected.txt
new file mode 100644 (file)
index 0000000..c2ab4c2
--- /dev/null
@@ -0,0 +1,2 @@
+PASS: Click fired on element with handler.
+This document doesn't have fast clicks because it sets a viewport width. However, it doesn't have a large amount of zoom on double tap, so double tapping on the rectangle above should send a click event.
diff --git a/LayoutTests/fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom.html b/LayoutTests/fast/events/ios/ipad/fast-click-double-tap-sends-click-on-insignificant-zoom.html
new file mode 100644 (file)
index 0000000..d34c22f
--- /dev/null
@@ -0,0 +1,55 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+
+<html>
+<meta name="viewport" content="width=500">
+<head>
+    <style>
+        body {
+            font-family: system-ui;
+            line-height: 1.4;
+            padding: 10px 10px;
+            width: 500px;
+            margin: 0;
+        }
+    </style>
+    <script src="../../../resources/ui-helper.js"></script>
+    <script>
+        if (window.testRunner) {
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+        }
+
+        async function runTest()
+        {
+            document.getElementById("target").addEventListener("click", handleClick, false);
+
+            if (!window.testRunner)
+                return;
+            await UIHelper.humanSpeedDoubleTapAt(30, 30);
+        }
+
+        function handleClick(event)
+        {
+            document.getElementById("target").textContent = "PASS: Click fired on element with handler.";
+            testRunner.notifyDone();
+        }
+    </script>
+    <style>
+        body {
+            margin: 0;
+        }
+        #target {
+            height: 100px;
+            width: 100px;
+            background-color: silver;
+        }
+    </style>
+</head>
+<body onload="runTest()">
+<div id="target"></div>
+<div id="description">This document doesn't have fast clicks because
+    it sets a viewport width. However, it doesn't have a large amount of
+    zoom on double tap, so double tapping on the rectangle
+    above should send a click event.</div>
+</body>
+</html>
index d4779cc..f36495a 100644 (file)
@@ -23,6 +23,9 @@ http/tests/gzip-content-encoding [ Pass ]
 # End platform-specific directories.
 #//////////////////////////////////////////////////////////////////////////////////////////
 
+# iPad-specific tests skipped here and re-enabled in platform/ipad
+fast/events/ios/ipad [ Skip ]
+
 ###
 # Unsupported and disabled features
 ##
index 1eefa7b..9af5067 100644 (file)
@@ -74,6 +74,31 @@ window.UIHelper = class UIHelper {
         });
     }
 
+    static humanSpeedDoubleTapAt(x, y)
+    {
+        console.assert(this.isIOS());
+
+        if (!this.isWebKit2()) {
+            // FIXME: Add a sleep in here.
+            eventSender.addTouchPoint(x, y);
+            eventSender.touchStart();
+            eventSender.releaseTouchPoint(0);
+            eventSender.touchEnd();
+            eventSender.addTouchPoint(x, y);
+            eventSender.touchStart();
+            eventSender.releaseTouchPoint(0);
+            eventSender.touchEnd();
+            return Promise.resolve();
+        }
+
+        return new Promise(async (resolve) => {
+            await UIHelper.tapAt(x, y);
+            await new Promise(resolveAfterDelay => setTimeout(resolveAfterDelay, 120));
+            await UIHelper.tapAt(x, y);
+            resolve();
+        });
+    }
+
     static zoomByDoubleTappingAt(x, y)
     {
         console.assert(this.isIOS());
index 84a882c..f537086 100644 (file)
@@ -1,3 +1,100 @@
+2019-03-11  Dean Jackson  <dino@apple.com>
+
+        [iOS] Implement a faster click detection that intercepts double-tap-to-zoom if possible
+        https://bugs.webkit.org/show_bug.cgi?id=195473
+        <rdar://problem/48718396>
+
+        Reviewed by Wenson Hsieh (with some help from Dan Bates).
+
+        Adds a new algorithm, behind a flag FasterClicksEnabled, that can trigger a click
+        event without waiting to see if a double tap will occur. It does this by examining
+        the amount of zoom that would be triggered if it was a double tap, and if that value
+        doesn't exceed a set threshold, commits to the click event instead.
+
+        This is implemented by having the Web Process respond to the potential click with
+        some geometry information. If the UI Process receives the information before the
+        second tap in a double tap, it can decide to trigger a click.
+
+        * Shared/WebPreferences.yaml: New internal feature so this can be toggled in
+            a UI for testing.
+
+        * SourcesCocoa.txt: Renamed WKSyntheticTapGestureRecognizer.
+        * WebKit.xcodeproj/project.pbxproj: Ditto.
+
+        * UIProcess/ios/WKSyntheticTapGestureRecognizer.h:
+        * UIProcess/ios/WKSyntheticTapGestureRecognizer.m:
+        (-[WKSyntheticTapGestureRecognizer setGestureIdentifiedTarget:action:]):
+        (-[WKSyntheticTapGestureRecognizer setGestureFailedTarget:action:]):
+        (-[WKSyntheticTapGestureRecognizer setResetTarget:action:]):
+        (-[WKSyntheticTapGestureRecognizer setState:]):
+        (-[WKSyntheticTapGestureRecognizer reset]):  Renamed WKSyntheticClickTapGestureRecognizer to
+            WKSyntheticTapGestureRecognizer, changed the signature of the main function to be a bit
+            more clear about what it does, and added a gesture failed target.
+
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _initialScaleFactor]):
+        (-[WKWebView _contentZoomScale]):
+        (-[WKWebView _targetContentZoomScaleForRect:currentScale:fitEntireRect:minimumScale:maximumScale:]):
+            Exposed the initial content scale, the current scale and added a declaration that
+            was missing from the .h.
+
+        * UIProcess/WebPageProxy.messages.in: Add a new message,
+            HandleSmartMagnificationInformationForPotentialTap, to
+            communicate the geometry of the clicked node to the UI Process.
+
+        * UIProcess/PageClient.h: Pure virtual function for the geometry message response.
+        * UIProcess/WebPageProxy.h: Ditto.
+
+        * UIProcess/ios/PageClientImplIOS.h: Calls into the WKContentView.
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::handleSmartMagnificationInformationForPotentialTap):
+
+        * UIProcess/ios/SmartMagnificationController.h:
+        * UIProcess/ios/SmartMagnificationController.mm:
+        (WebKit::SmartMagnificationController::calculatePotentialZoomParameters): A new method that
+            asks the WKContentView to work out what the zoom factor will be for a potential double
+            tap at a location.
+        (WebKit::SmartMagnificationController::smartMagnificationTargetRectAndZoomScales): New implementation
+            of this function to avoid multiple out-arguments.
+
+        * UIProcess/ios/WKContentView.h:
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView _initialScaleFactor]):
+        (-[WKContentView _contentZoomScale]):
+        (-[WKContentView _targetContentZoomScaleForRect:currentScale:fitEntireRect:minimumScale:maximumScale:]):
+            Exposed the initial content scale, the current scale and the target zoom scale. These
+            all just call into the WKWebView implementation.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _createAndConfigureDoubleTapGestureRecognizer]): Use a WKSyntheticTapGestureRecognizer instead
+            of a generic one, so we can capture the failure.
+        (-[WKContentView setupInteraction]):
+        (-[WKContentView cleanupInteraction]):
+        (-[WKContentView _handleSmartMagnificationInformationForPotentialTap:origin:renderRect:fitEntireRect:viewportMinimumScale:viewportMaximumScale:]):
+            New method that responds to the incoming Web Process message, and decides if any
+            potential zoom would be "significant".
+        (-[WKContentView _singleTapIdentified:]):
+        (-[WKContentView _doubleTapDidFail:]):
+        (-[WKContentView _didCompleteSyntheticClick]):
+        (-[WKContentView _singleTapRecognized:]):
+        (-[WKContentView _doubleTapRecognized:]):
+            Add some release logging.
+        (-[WKContentView _singleTapCommited:]): Deleted.
+
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::potentialTapAtPosition):
+        (WebKit::WebPageProxy::handleSmartMagnificationInformationForPotentialTap):
+        * WebProcess/WebPage/ViewGestureGeometryCollector.h:
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+            Removed an unused parameter from the existing message.
+
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::potentialTapAtPosition): Calculates the geometry of the element
+        if requested, and sends it to the UIProcess.
+
 2019-03-11  Per Arne Vollan  <pvollan@apple.com>
 
         Unreviewed build fix after r242745.
index 33afc79..c2086f9 100644 (file)
@@ -1470,6 +1470,15 @@ SelectionAcrossShadowBoundariesEnabled:
   category: internal
   webcoreName: selectionAcrossShadowBoundariesEnabled
 
+FasterClicksEnabled:
+  type: bool
+  defaultValue: true
+  condition: PLATFORM(IOS_FAMILY)
+  humanReadableName: "Faster clicks"
+  humanReadableDescription: "Support faster clicks on zoomable pages"
+  webcoreBinding: none
+  category: internal
+
 InputTypeColorEnabled:
   type: bool
   defaultValue: DEFAULT_INPUT_TYPE_COLOR_ENABLED
index bc4a6cd..53edff2 100644 (file)
@@ -411,8 +411,8 @@ UIProcess/ios/WKPasswordView.mm
 UIProcess/ios/WKPDFPageNumberIndicator.mm
 UIProcess/ios/WKPDFView.mm
 UIProcess/ios/WKScrollView.mm
-UIProcess/ios/WKSyntheticClickTapGestureRecognizer.m
 UIProcess/ios/WKSyntheticFlagsChangedWebEvent.mm
+UIProcess/ios/WKSyntheticTapGestureRecognizer.m
 UIProcess/ios/WKSystemPreviewView.mm
 UIProcess/ios/WKWebEvent.mm
 
index df63642..d2d28eb 100644 (file)
@@ -2455,7 +2455,17 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     [_scrollView _zoomToCenter:newCenter scale:scale duration:formControlZoomAnimationDuration force:YES];
 }
 
-- (CGFloat)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
+- (double)_initialScaleFactor
+{
+    return _initialScaleFactor;
+}
+
+- (double)_contentZoomScale
+{
+    return contentZoomScale(self);
+}
+
+- (double)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
 {
     WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
     double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
index 91e146a..f013d8a 100644 (file)
@@ -99,6 +99,9 @@ struct PrintInfo;
 
 - (void)_scrollToContentScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::IntPoint)scrollOrigin;
 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance;
+- (double)_initialScaleFactor;
+- (double)_contentZoomScale;
+- (double)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale;
 - (void)_zoomToFocusRect:(const WebCore::FloatRect&)focusedElementRect selectionRect:(const WebCore::FloatRect&)selectionRectInDocumentCoordinates insideFixed:(BOOL)insideFixed fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll;
 - (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance;
 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated;
index d474a49..775f507 100644 (file)
@@ -379,6 +379,7 @@ public:
     virtual void saveImageToLibrary(Ref<WebCore::SharedBuffer>&&) = 0;
     virtual void showPlaybackTargetPicker(bool hasVideo, const WebCore::IntRect& elementRect, WebCore::RouteSharingPolicy, const String&) = 0;
     virtual void disableDoubleTapGesturesDuringTapIfNecessary(uint64_t requestID) = 0;
+    virtual void handleSmartMagnificationInformationForPotentialTap(uint64_t requestID, const WebCore::FloatRect& renderRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale) = 0;
     virtual double minimumZoomScale() const = 0;
     virtual WebCore::FloatRect documentRect() const = 0;
     virtual void scrollingNodeScrollViewWillStartPanGesture() = 0;
index 1ddb2a3..44c6416 100644 (file)
@@ -678,6 +678,7 @@ public:
     void didNotHandleTapAsClick(const WebCore::IntPoint&);
     void didCompleteSyntheticClick();
     void disableDoubleTapGesturesDuringTapIfNecessary(uint64_t requestID);
+    void handleSmartMagnificationInformationForPotentialTap(uint64_t requestID, const WebCore::FloatRect& renderRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale);
     void contentSizeCategoryDidChange(const String& contentSizeCategory);
     void getSelectionContext(WTF::Function<void(const String&, const String&, const String&, CallbackBase::Error)>&&);
     void handleTwoFingerTapAtPoint(const WebCore::IntPoint&, OptionSet<WebKit::WebEvent::Modifier>, uint64_t requestID);
@@ -1178,7 +1179,7 @@ public:
 #if PLATFORM(IOS_FAMILY)
     void willStartUserTriggeredZooming();
 
-    void potentialTapAtPosition(const WebCore::FloatPoint&, uint64_t& requestID);
+    void potentialTapAtPosition(const WebCore::FloatPoint&, bool shouldRequestMagnificationInformation, uint64_t& requestID);
     void commitPotentialTap(OptionSet<WebKit::WebEvent::Modifier>, uint64_t layerTreeTransactionIdAtLastTouchStart);
     void cancelPotentialTap();
     void tapHighlightAtPosition(const WebCore::FloatPoint&, uint64_t& requestID);
index 4c7da4a..3465e76 100644 (file)
@@ -195,6 +195,7 @@ messages -> WebPageProxy {
     DidNotHandleTapAsClick(WebCore::IntPoint point)
     DidCompleteSyntheticClick()
     DisableDoubleTapGesturesDuringTapIfNecessary(uint64_t requestID)
+    HandleSmartMagnificationInformationForPotentialTap(uint64_t requestID, WebCore::FloatRect renderRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale)
     DrawToPDFCallback(IPC::DataReference pdfData, WebKit::CallbackID callbackID)
     SelectionRectsCallback(Vector<WebCore::SelectionRect> selectionRects, WebKit::CallbackID callbackID);
 #endif
index 85ecde8..6025a22 100644 (file)
@@ -161,6 +161,8 @@ private:
     bool showShareSheet(const WebCore::ShareDataWithParsedURL&, WTF::CompletionHandler<void(bool)>&&) override;
     
     void disableDoubleTapGesturesDuringTapIfNecessary(uint64_t requestID) override;
+    void handleSmartMagnificationInformationForPotentialTap(uint64_t requestID, const WebCore::FloatRect& renderRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale) override;
+
     double minimumZoomScale() const override;
     WebCore::FloatRect documentRect() const override;
 
index 73b9a93..db3f675 100644 (file)
@@ -238,6 +238,11 @@ void PageClientImpl::disableDoubleTapGesturesDuringTapIfNecessary(uint64_t reque
     [m_contentView _disableDoubleTapGesturesDuringTapIfNecessary:requestID];
 }
 
+void PageClientImpl::handleSmartMagnificationInformationForPotentialTap(uint64_t requestID, const WebCore::FloatRect& renderRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale)
+{
+    [m_contentView _handleSmartMagnificationInformationForPotentialTap:requestID renderRect:renderRect fitEntireRect:fitEntireRect viewportMinimumScale:viewportMinimumScale viewportMaximumScale:viewportMaximumScale];
+}
+
 double PageClientImpl::minimumZoomScale() const
 {
     if (UIScrollView *scroller = [m_webView scrollView])
index d18c622..7280788 100644 (file)
@@ -48,6 +48,8 @@ public:
     void handleSmartMagnificationGesture(WebCore::FloatPoint origin);
     void handleResetMagnificationGesture(WebCore::FloatPoint origin);
 
+    double zoomFactorForTargetRect(WebCore::FloatRect targetRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale);
+
 private:
     // IPC::MessageReceiver.
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
@@ -55,7 +57,7 @@ private:
     void didCollectGeometryForSmartMagnificationGesture(WebCore::FloatPoint origin, WebCore::FloatRect renderRect, WebCore::FloatRect visibleContentBounds, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale);
     void magnify(WebCore::FloatPoint origin, WebCore::FloatRect targetRect, WebCore::FloatRect visibleContentRect, double viewportMinimumScale, double viewportMaximumScale);
     void scrollToRect(WebCore::FloatPoint origin, WebCore::FloatRect targetRect);
-    void adjustSmartMagnificationTargetRectAndZoomScales(bool addMagnificationPadding, WebCore::FloatRect& targetRect, double& minimumScale, double& maximumScale);
+    std::tuple<WebCore::FloatRect, double, double> smartMagnificationTargetRectAndZoomScales(WebCore::FloatRect targetRect, double minimumScale, double maximumScale, bool addMagnificationPadding);
 
     WebPageProxy& m_webPageProxy;
     WKContentView *m_contentView;
index f88430b..6eff64b 100644 (file)
@@ -76,15 +76,39 @@ void SmartMagnificationController::handleResetMagnificationGesture(FloatPoint or
     [m_contentView _zoomOutWithOrigin:origin];
 }
 
-void SmartMagnificationController::adjustSmartMagnificationTargetRectAndZoomScales(bool addMagnificationPadding, WebCore::FloatRect& targetRect, double& minimumScale, double& maximumScale)
+std::tuple<FloatRect, double, double> SmartMagnificationController::smartMagnificationTargetRectAndZoomScales(FloatRect targetRect, double minimumScale, double maximumScale, bool addMagnificationPadding)
 {
+    FloatRect outTargetRect = targetRect;
+    double outMinimumScale = minimumScale;
+    double outMaximumScale = maximumScale;
+
     if (addMagnificationPadding) {
-        targetRect.inflateX(smartMagnificationElementPadding * targetRect.width());
-        targetRect.inflateY(smartMagnificationElementPadding * targetRect.height());
+        outTargetRect.inflateX(smartMagnificationElementPadding * outTargetRect.width());
+        outTargetRect.inflateY(smartMagnificationElementPadding * outTargetRect.height());
     }
 
-    minimumScale = std::max(minimumScale, smartMagnificationMinimumScale);
-    maximumScale = std::min(maximumScale, smartMagnificationMaximumScale);
+    outMinimumScale = std::max(outMinimumScale, smartMagnificationMinimumScale);
+    outMaximumScale = std::min(outMaximumScale, smartMagnificationMaximumScale);
+
+    return { outTargetRect, outMinimumScale, outMaximumScale };
+}
+
+double SmartMagnificationController::zoomFactorForTargetRect(FloatRect targetRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale)
+{
+    // FIXME: Share some of this code with didCollectGeometryForSmartMagnificationGesture?
+
+    FloatRect adjustedTargetRect;
+    double minimumScale = viewportMinimumScale;
+    double maximumScale = viewportMaximumScale;
+    std::tie(adjustedTargetRect, minimumScale, maximumScale) = smartMagnificationTargetRectAndZoomScales(targetRect, viewportMinimumScale, viewportMaximumScale, !fitEntireRect);
+
+    double currentScale = [m_contentView _contentZoomScale];
+    double targetScale = [m_contentView _targetContentZoomScaleForRect:adjustedTargetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
+
+    if (targetScale == currentScale)
+        targetScale = [m_contentView _initialScaleFactor];
+
+    return targetScale;
 }
 
 void SmartMagnificationController::didCollectGeometryForSmartMagnificationGesture(FloatPoint origin, FloatRect targetRect, FloatRect visibleContentRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale)
@@ -94,9 +118,10 @@ void SmartMagnificationController::didCollectGeometryForSmartMagnificationGestur
         [m_contentView _zoomToInitialScaleWithOrigin:origin];
         return;
     }
+    FloatRect adjustedTargetRect;
     double minimumScale = viewportMinimumScale;
     double maximumScale = viewportMaximumScale;
-    adjustSmartMagnificationTargetRectAndZoomScales(!fitEntireRect, targetRect, minimumScale, maximumScale);
+    std::tie(adjustedTargetRect, minimumScale, maximumScale) = smartMagnificationTargetRectAndZoomScales(targetRect, viewportMinimumScale, viewportMaximumScale, !fitEntireRect);
 
     // FIXME: Check if text selection wants to consume the double tap before we attempt magnification.
 
@@ -113,7 +138,7 @@ void SmartMagnificationController::didCollectGeometryForSmartMagnificationGestur
     // For replaced elements like images, we want to fit the whole element
     // in the view, so scale it down enough to make both dimensions fit if possible.
     // For other elements, try to fit them horizontally.
-    if ([m_contentView _zoomToRect:targetRect withOrigin:origin fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale minimumScrollDistance:minimumScrollDistance])
+    if ([m_contentView _zoomToRect:adjustedTargetRect withOrigin:origin fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale minimumScrollDistance:minimumScrollDistance])
         return;
 
     // FIXME: If we still don't zoom, send the tap along to text selection (see <rdar://problem/6810344>).
@@ -122,10 +147,12 @@ void SmartMagnificationController::didCollectGeometryForSmartMagnificationGestur
 
 void SmartMagnificationController::magnify(FloatPoint origin, FloatRect targetRect, FloatRect visibleContentRect, double viewportMinimumScale, double viewportMaximumScale)
 {
+    FloatRect adjustedTargetRect;
     double maximumScale = viewportMaximumScale;
     double minimumScale = viewportMinimumScale;
-    adjustSmartMagnificationTargetRectAndZoomScales(true, targetRect, minimumScale, maximumScale);
-    [m_contentView _zoomToRect:targetRect withOrigin:origin fitEntireRect:NO minimumScale:minimumScale maximumScale:maximumScale minimumScrollDistance:0];
+    std::tie(adjustedTargetRect, minimumScale, maximumScale) = smartMagnificationTargetRectAndZoomScales(targetRect, viewportMinimumScale, viewportMaximumScale, true);
+
+    [m_contentView _zoomToRect:adjustedTargetRect withOrigin:origin fitEntireRect:NO minimumScale:minimumScale maximumScale:maximumScale minimumScrollDistance:0];
 }
 
 void SmartMagnificationController::scrollToRect(FloatPoint origin, FloatRect targetRect)
index 522aca1..4b290a7 100644 (file)
@@ -38,6 +38,7 @@ class PageConfiguration;
 }
 
 namespace WebCore {
+class FloatRect;
 struct Highlight;
 }
 
@@ -108,5 +109,8 @@ class WebProcessPool;
 - (BOOL)_zoomToRect:(CGRect)targetRect withOrigin:(CGPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(CGFloat)minimumScrollDistance;
 - (void)_zoomOutWithOrigin:(CGPoint)origin;
 - (void)_zoomToInitialScaleWithOrigin:(CGPoint)origin;
+- (double)_initialScaleFactor;
+- (double)_contentZoomScale;
+- (double)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale;
 
 @end
index c49cbfe..ff163da 100644 (file)
@@ -641,6 +641,21 @@ static void storeAccessibilityRemoteConnectionInformation(id element, pid_t pid,
     return [_webView _zoomToInitialScaleWithOrigin:origin animated:YES];
 }
 
+- (double)_initialScaleFactor
+{
+    return [_webView _initialScaleFactor];
+}
+
+- (double)_contentZoomScale
+{
+    return [_webView _contentZoomScale];
+}
+
+- (double)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
+{
+    return [_webView _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
+}
+
 - (void)_applicationWillResignActive:(NSNotification*)notification
 {
     _page->applicationWillResignActive();
index d0da577..44a96f7 100644 (file)
@@ -43,7 +43,7 @@
 #import "WKFormPeripheral.h"
 #import "WKKeyboardScrollingAnimator.h"
 #import "WKShareSheet.h"
-#import "WKSyntheticClickTapGestureRecognizer.h"
+#import "WKSyntheticTapGestureRecognizer.h"
 #import "_WKFormInputSession.h"
 #import <UIKit/UIView.h>
 #import <WebCore/Color.h>
@@ -202,10 +202,10 @@ struct WKAutoCorrectionData {
     BOOL _preventsPanningInYAxis;
 #endif
 
-    RetainPtr<WKSyntheticClickTapGestureRecognizer> _singleTapGestureRecognizer;
+    RetainPtr<WKSyntheticTapGestureRecognizer> _singleTapGestureRecognizer;
     RetainPtr<_UIWebHighlightLongPressGestureRecognizer> _highlightLongPressGestureRecognizer;
     RetainPtr<UILongPressGestureRecognizer> _longPressGestureRecognizer;
-    RetainPtr<UITapGestureRecognizer> _doubleTapGestureRecognizer;
+    RetainPtr<WKSyntheticTapGestureRecognizer> _doubleTapGestureRecognizer;
     RetainPtr<UITapGestureRecognizer> _nonBlockingDoubleTapGestureRecognizer;
     RetainPtr<UITapGestureRecognizer> _twoFingerDoubleTapGestureRecognizer;
     RetainPtr<UITapGestureRecognizer> _twoFingerSingleTapGestureRecognizer;
@@ -405,6 +405,7 @@ FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW)
 
 - (BOOL)_mayDisableDoubleTapGesturesDuringSingleTap;
 - (void)_disableDoubleTapGesturesDuringTapIfNecessary:(uint64_t)requestID;
+- (void)_handleSmartMagnificationInformationForPotentialTap:(uint64_t)requestID renderRect:(const WebCore::FloatRect&)renderRect fitEntireRect:(BOOL)fitEntireRect viewportMinimumScale:(double)viewportMinimumScale viewportMaximumScale:(double)viewportMaximumScale;
 - (void)_elementDidFocus:(const WebKit::FocusedElementInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode changingActivityState:(BOOL)changingActivityState userObject:(NSObject <NSSecureCoding> *)userObject;
 - (void)_elementDidBlur;
 - (void)_didUpdateInputMode:(WebCore::InputMode)mode;
index 134d24b..260d491 100644 (file)
@@ -228,9 +228,10 @@ TextStream& operator<<(TextStream& stream, const WKSelectionDrawingInfo& info)
 
 } // namespace WebKit
 
-static const float highlightDelay = 0.12;
-static const float tapAndHoldDelay  = 0.75;
-const CGFloat minimumTapHighlightRadius = 2.0;
+constexpr float highlightDelay = 0.12;
+constexpr float tapAndHoldDelay = 0.75;
+constexpr CGFloat minimumTapHighlightRadius = 2.0;
+constexpr double fasterTapSignificantZoomThreshold = 0.8;
 
 @interface WKTextRange : UITextRange {
     CGRect _startRect;
@@ -645,7 +646,8 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
 
 - (void)_createAndConfigureDoubleTapGestureRecognizer
 {
-    _doubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_doubleTapRecognized:)]);
+    _doubleTapGestureRecognizer = adoptNS([[WKSyntheticTapGestureRecognizer alloc] initWithTarget:self action:@selector(_doubleTapRecognized:)]);
+    [_doubleTapGestureRecognizer setGestureFailedTarget:self action:@selector(_doubleTapDidFail:)];
     [_doubleTapGestureRecognizer setNumberOfTapsRequired:2];
     [_doubleTapGestureRecognizer setDelegate:self];
     [self addGestureRecognizer:_doubleTapGestureRecognizer.get()];
@@ -694,9 +696,9 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     
 #endif
 
-    _singleTapGestureRecognizer = adoptNS([[WKSyntheticClickTapGestureRecognizer alloc] initWithTarget:self action:@selector(_singleTapCommited:)]);
+    _singleTapGestureRecognizer = adoptNS([[WKSyntheticTapGestureRecognizer alloc] initWithTarget:self action:@selector(_singleTapRecognized:)]);
     [_singleTapGestureRecognizer setDelegate:self];
-    [_singleTapGestureRecognizer setGestureRecognizedTarget:self action:@selector(_singleTapRecognized:)];
+    [_singleTapGestureRecognizer setGestureIdentifiedTarget:self action:@selector(_singleTapIdentified:)];
     [_singleTapGestureRecognizer setResetTarget:self action:@selector(_singleTapDidReset:)];
     [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
 
@@ -813,7 +815,7 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
 #endif
 
     [_singleTapGestureRecognizer setDelegate:nil];
-    [_singleTapGestureRecognizer setGestureRecognizedTarget:nil action:nil];
+    [_singleTapGestureRecognizer setGestureIdentifiedTarget:nil action:nil];
     [_singleTapGestureRecognizer setResetTarget:nil action:nil];
     [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
 
@@ -1491,6 +1493,23 @@ static NSValue *nsSizeForTapHighlightBorderRadius(WebCore::IntSize borderRadius,
     [self _setDoubleTapGesturesEnabled:NO];
 }
 
+- (void)_handleSmartMagnificationInformationForPotentialTap:(uint64_t)requestID renderRect:(const WebCore::FloatRect&)renderRect fitEntireRect:(BOOL)fitEntireRect viewportMinimumScale:(double)viewportMinimumScale viewportMaximumScale:(double)viewportMaximumScale
+{
+    ASSERT(_page->preferences().fasterClicksEnabled());
+    if (!_potentialTapInProgress)
+        return;
+
+    auto targetScale = _smartMagnificationController->zoomFactorForTargetRect(renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale);
+
+    auto initialScale = [self _initialScaleFactor];
+    if (std::min(targetScale, initialScale) / std::max(targetScale, initialScale) > fasterTapSignificantZoomThreshold) {
+        RELEASE_LOG(ViewGestures, "Potential tap would not cause a significant zoom. Trigger click. (%p)", self);
+        [self _setDoubleTapGesturesEnabled:NO];
+        return;
+    }
+    RELEASE_LOG(ViewGestures, "Potential tap may cause significant zoom. Wait. (%p)", self);
+}
+
 - (void)_cancelLongPressGestureRecognizer
 {
     [_highlightLongPressGestureRecognizer cancel];
@@ -2176,13 +2195,17 @@ static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UI
     _potentialTapInProgress = NO;
 }
 
-- (void)_singleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
+- (void)_singleTapIdentified:(UITapGestureRecognizer *)gestureRecognizer
 {
     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
     ASSERT(!_potentialTapInProgress);
     [self _resetIsDoubleTapPending];
 
-    _page->potentialTapAtPosition(gestureRecognizer.location, ++_latestTapID);
+    bool shouldRequestMagnificationInformation = _page->preferences().fasterClicksEnabled();
+    if (shouldRequestMagnificationInformation)
+        RELEASE_LOG(ViewGestures, "Single tap identified. Request details on potential zoom. (%p)", self);
+
+    _page->potentialTapAtPosition(gestureRecognizer.location, shouldRequestMagnificationInformation, ++_latestTapID);
     _potentialTapInProgress = YES;
     _isTapHighlightIDValid = YES;
     _isExpectingFastSingleTapCommit = !_doubleTapGestureRecognizer.get().enabled;
@@ -2203,6 +2226,12 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
     cancelPotentialTapIfNecessary(self);
 }
 
+- (void)_doubleTapDidFail:(UITapGestureRecognizer *)gestureRecognizer
+{
+    RELEASE_LOG(ViewGestures, "Double tap was not recognized. (%p)", self);
+    ASSERT(gestureRecognizer == _doubleTapGestureRecognizer);
+}
+
 - (void)_commitPotentialTapFailed
 {
     [self _cancelInteraction];
@@ -2232,10 +2261,11 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
 
 - (void)_didCompleteSyntheticClick
 {
+    RELEASE_LOG(ViewGestures, "Synthetic click completed. (%p)", self);
     [self _resetInputViewDeferral];
 }
 
-- (void)_singleTapCommited:(UITapGestureRecognizer *)gestureRecognizer
+- (void)_singleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
 {
     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
 
@@ -2260,6 +2290,9 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
     }
 
     [_inputPeripheral endEditing];
+
+    RELEASE_LOG(ViewGestures, "Single tap recognized - commit potential tap (%p)", self);
+
     _page->commitPotentialTap(WebKit::webEventModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer)), _layerTreeTransactionIdAtLastTouchStart);
 
     if (!_isExpectingFastSingleTapCommit)
@@ -2268,6 +2301,8 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
 
 - (void)_doubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
 {
+    RELEASE_LOG(ViewGestures, "Identified a double tap (%p)", self);
+
     [self _resetIsDoubleTapPending];
     _lastInteractionLocation = gestureRecognizer.location;
 
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 - 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
 
 #import "UIKitSPI.h"
 
-@interface WKSyntheticClickTapGestureRecognizer : UITapGestureRecognizer
-- (void)setGestureRecognizedTarget:(id)target action:(SEL)action;
+// The purpose of this class is to call a target/action when
+// the gesture is recognized, as well as the typical time when
+// a gesture should be handled. This allows it to be used while
+// it is waiting for another gesture recognizer to fail.
+@interface WKSyntheticTapGestureRecognizer : UITapGestureRecognizer
+- (void)setGestureIdentifiedTarget:(id)target action:(SEL)action;
+- (void)setGestureFailedTarget:(id)target action:(SEL)action;
 - (void)setResetTarget:(id)target action:(SEL)action;
 @end
 
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 - 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
  */
 
 #import "config.h"
-#import "WKSyntheticClickTapGestureRecognizer.h"
+#import "WKSyntheticTapGestureRecognizer.h"
 
 #if PLATFORM(IOS_FAMILY)
 
 #import <UIKit/UIGestureRecognizerSubclass.h>
 
-@implementation WKSyntheticClickTapGestureRecognizer {
-    id _gestureRecognizedTarget;
-    SEL _gestureRecognizedAction;
+@implementation WKSyntheticTapGestureRecognizer {
+    id _gestureIdentifiedTarget;
+    SEL _gestureIdentifiedAction;
+    id _gestureFailedTarget;
+    SEL _gestureFailedAction;
     id _resetTarget;
     SEL _resetAction;
 }
 
-- (void)setGestureRecognizedTarget:(id)target action:(SEL)action
+- (void)setGestureIdentifiedTarget:(id)target action:(SEL)action
 {
-    _gestureRecognizedTarget = target;
-    _gestureRecognizedAction = action;
+    _gestureIdentifiedTarget = target;
+    _gestureIdentifiedAction = action;
+}
+
+- (void)setGestureFailedTarget:(id)target action:(SEL)action
+{
+    _gestureFailedTarget = target;
+    _gestureFailedAction = action;
 }
 
 - (void)setResetTarget:(id)target action:(SEL)action
@@ -52,7 +60,9 @@
 - (void)setState:(UIGestureRecognizerState)state
 {
     if (state == UIGestureRecognizerStateEnded)
-        [_gestureRecognizedTarget performSelector:_gestureRecognizedAction withObject:self];
+        [_gestureIdentifiedTarget performSelector:_gestureIdentifiedAction withObject:self];
+    else if (state == UIGestureRecognizerStateFailed)
+        [_gestureFailedTarget performSelector:_gestureFailedAction withObject:self];
     [super setState:state];
 }
 
index 98553ad..bc816fd 100644 (file)
@@ -812,10 +812,10 @@ void WebPageProxy::willStartUserTriggeredZooming()
     process().send(Messages::WebPage::WillStartUserTriggeredZooming(), m_pageID);
 }
 
-void WebPageProxy::potentialTapAtPosition(const WebCore::FloatPoint& position, uint64_t& requestID)
+void WebPageProxy::potentialTapAtPosition(const WebCore::FloatPoint& position, bool shouldRequestMagnificationInformation, uint64_t& requestID)
 {
     hideValidationMessage();
-    process().send(Messages::WebPage::PotentialTapAtPosition(requestID, position), m_pageID);
+    process().send(Messages::WebPage::PotentialTapAtPosition(requestID, position, shouldRequestMagnificationInformation), m_pageID);
 }
 
 void WebPageProxy::commitPotentialTap(OptionSet<WebEvent::Modifier> modifiers, uint64_t layerTreeTransactionIdAtLastTouchStart)
@@ -1032,6 +1032,11 @@ void WebPageProxy::disableDoubleTapGesturesDuringTapIfNecessary(uint64_t request
     pageClient().disableDoubleTapGesturesDuringTapIfNecessary(requestID);
 }
 
+void WebPageProxy::handleSmartMagnificationInformationForPotentialTap(uint64_t requestID, const WebCore::FloatRect& renderRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale)
+{
+    pageClient().handleSmartMagnificationInformationForPotentialTap(requestID, renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale);
+}
+
 uint32_t WebPageProxy::computePagesForPrintingAndDrawToPDF(uint64_t frameID, const PrintInfo& printInfo, DrawToPDFCallback::CallbackFunction&& callback)
 {
     if (!isValid()) {
index d308ef4..13ca4d1 100644 (file)
                2684054418B85A630022C38B /* VisibleContentRectUpdateInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 2684054218B85A630022C38B /* VisibleContentRectUpdateInfo.h */; };
                2684055218B86ED60022C38B /* ViewUpdateDispatcherMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2684055018B86ED60022C38B /* ViewUpdateDispatcherMessageReceiver.cpp */; };
                2684055318B86ED60022C38B /* ViewUpdateDispatcherMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 2684055118B86ED60022C38B /* ViewUpdateDispatcherMessages.h */; };
-               26F10BE819187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 26F10BE619187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.h */; };
-               26F10BE919187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 26F10BE719187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.m */; };
+               26F10BE819187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 26F10BE619187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.h */; };
+               26F10BE919187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 26F10BE719187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.m */; };
                26F9A83B18A3468100AEB88A /* WKWebViewPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 26F9A83A18A3463F00AEB88A /* WKWebViewPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2749F6442146561B008380BF /* InjectedBundleNodeHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC4BEEAA120A0A5F00FBA0C7 /* InjectedBundleNodeHandle.cpp */; };
                2749F6452146561E008380BF /* InjectedBundleRangeHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC33E0D012408E8600360F3F /* InjectedBundleRangeHandle.cpp */; };
                2684054A18B866FF0022C38B /* VisibleContentRectUpdateInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VisibleContentRectUpdateInfo.cpp; sourceTree = "<group>"; };
                2684055018B86ED60022C38B /* ViewUpdateDispatcherMessageReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ViewUpdateDispatcherMessageReceiver.cpp; path = DerivedSources/WebKit2/ViewUpdateDispatcherMessageReceiver.cpp; sourceTree = BUILT_PRODUCTS_DIR; };
                2684055118B86ED60022C38B /* ViewUpdateDispatcherMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ViewUpdateDispatcherMessages.h; path = DerivedSources/WebKit2/ViewUpdateDispatcherMessages.h; sourceTree = BUILT_PRODUCTS_DIR; };
-               26F10BE619187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKSyntheticClickTapGestureRecognizer.h; path = ios/WKSyntheticClickTapGestureRecognizer.h; sourceTree = "<group>"; };
-               26F10BE719187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WKSyntheticClickTapGestureRecognizer.m; path = ios/WKSyntheticClickTapGestureRecognizer.m; sourceTree = "<group>"; };
+               26F10BE619187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKSyntheticTapGestureRecognizer.h; path = ios/WKSyntheticTapGestureRecognizer.h; sourceTree = "<group>"; };
+               26F10BE719187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WKSyntheticTapGestureRecognizer.m; path = ios/WKSyntheticTapGestureRecognizer.m; sourceTree = "<group>"; };
                26F9A83A18A3463F00AEB88A /* WKWebViewPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKWebViewPrivate.h; sourceTree = "<group>"; };
                290F4271172A0C7400939FF0 /* AuxiliaryProcessSupplement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AuxiliaryProcessSupplement.h; sourceTree = "<group>"; };
                29232DF118B29D1100D0596F /* WKAccessibilityWebPageObjectMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKAccessibilityWebPageObjectMac.mm; sourceTree = "<group>"; };
                                A1046EA02079263100F0C5D8 /* WKPDFView.mm */,
                                0FCB4E4418BBE044000FCFC9 /* WKScrollView.h */,
                                0FCB4E4518BBE044000FCFC9 /* WKScrollView.mm */,
-                               26F10BE619187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.h */,
-                               26F10BE719187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.m */,
                                CE5B4C8621B73D870022E64F /* WKSyntheticFlagsChangedWebEvent.h */,
                                CE5B4C8721B73D870022E64F /* WKSyntheticFlagsChangedWebEvent.mm */,
+                               26F10BE619187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.h */,
+                               26F10BE719187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.m */,
                                316B8B622054B55800BD4A62 /* WKSystemPreviewView.h */,
                                316B8B612054B55800BD4A62 /* WKSystemPreviewView.mm */,
                                2D1E8221216FFF5000A15265 /* WKWebEvent.h */,
                                BC407606124FF0270068F20A /* WKString.h in Headers */,
                                BC40761A124FF0370068F20A /* WKStringCF.h in Headers */,
                                BC9099801256A98200083756 /* WKStringPrivate.h in Headers */,
-                               26F10BE819187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.h in Headers */,
                                CE5B4C8821B73D870022E64F /* WKSyntheticFlagsChangedWebEvent.h in Headers */,
+                               26F10BE819187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.h in Headers */,
                                316B8B642054B55800BD4A62 /* WKSystemPreviewView.h in Headers */,
                                51F886A61F2C228100C193EF /* WKTestingSupport.h in Headers */,
                                31D755C11D91B81500843BD1 /* WKTextChecker.h in Headers */,
                                637281A321ADC744009E0DE6 /* WKDownloadProgress.mm in Sources */,
                                5CA26D83217AD1B800F97A35 /* WKSafeBrowsingWarning.mm in Sources */,
                                1DB01944211CF005009FB3E8 /* WKShareSheet.mm in Sources */,
-                               26F10BE919187E2E001D0E68 /* WKSyntheticClickTapGestureRecognizer.m in Sources */,
+                               26F10BE919187E2E001D0E68 /* WKSyntheticTapGestureRecognizer.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index dc2b1fc..aade3d9 100644 (file)
@@ -46,6 +46,8 @@ public:
 
     void mainFrameDidLayout();
 
+    void computeZoomInformationForNode(WebCore::Node&, WebCore::FloatPoint& origin, WebCore::FloatRect& renderRect, bool& isReplaced, double& viewportMinimumScale, double& viewportMaximumScale);
+
 private:
     // IPC::MessageReceiver.
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
@@ -62,7 +64,6 @@ private:
 #endif
 
     void dispatchDidCollectGeometryForSmartMagnificationGesture(WebCore::FloatPoint origin, WebCore::FloatRect targetRect, WebCore::FloatRect visibleContentRect, bool fitEntireRect, double viewportMinimumScale, double viewportMaximumScale);
-    void computeZoomInformationForNode(WebCore::Node&, WebCore::FloatPoint& origin, WebCore::FloatRect& renderRect, bool& isReplaced, double& viewportMinimumScale, double& viewportMaximumScale);
     void computeMinimumAndMaximumViewportScales(double& viewportMinimumScale, double& viewportMaximumScale) const;
 
 #if PLATFORM(IOS_FAMILY)
index 3f9466e..9f73f83 100644 (file)
@@ -614,7 +614,7 @@ public:
     bool hasStablePageScaleFactor() const { return m_hasStablePageScaleFactor; }
 
     void handleTap(const WebCore::IntPoint&, OptionSet<WebKit::WebEvent::Modifier>, uint64_t lastLayerTreeTransactionId);
-    void potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint&);
+    void potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint&, bool shouldRequestMagnificationInformation);
     void commitPotentialTap(OptionSet<WebKit::WebEvent::Modifier>, uint64_t lastLayerTreeTransactionId);
     void commitPotentialTapFailed();
     void cancelPotentialTap();
index 912f1e4..20eb1a9 100644 (file)
@@ -52,7 +52,7 @@ messages -> WebPage LegacyReceiver {
     DynamicViewportSizeUpdate(WebCore::FloatSize viewLayoutSize, WebCore::FloatSize maximumUnobscuredSize, WebCore::FloatRect targetExposedContentRect, WebCore::FloatRect targetUnobscuredRect, WebCore::FloatRect targetUnobscuredRectInScrollViewCoordinates, WebCore::RectEdges<float> targetUnobscuredSafeAreaInsets, double scale, int32_t deviceOrientation, uint64_t dynamicViewportSizeUpdateID)
 
     HandleTap(WebCore::IntPoint point, OptionSet<WebKit::WebEvent::Modifier> modifiers, uint64_t lastLayerTreeTransactionId)
-    PotentialTapAtPosition(uint64_t requestID, WebCore::FloatPoint point)
+    PotentialTapAtPosition(uint64_t requestID, WebCore::FloatPoint point, bool shouldRequestMagnificationInformation)
     CommitPotentialTap(OptionSet<WebKit::WebEvent::Modifier> modifiers, uint64_t lastLayerTreeTransactionId)
     CancelPotentialTap()
     TapHighlightAtPosition(uint64_t requestID, WebCore::FloatPoint point)
index e50985a..db00d2f 100644 (file)
@@ -43,6 +43,7 @@
 #import "SandboxUtilities.h"
 #import "UIKitSPI.h"
 #import "UserData.h"
+#import "ViewGestureGeometryCollector.h"
 #import "VisibleContentRectUpdateInfo.h"
 #import "WKAccessibilityWebPageObjectIOS.h"
 #import "WebAutocorrectionContext.h"
@@ -808,9 +809,22 @@ void WebPage::handleStylusSingleTapAtPoint(const WebCore::IntPoint& point, uint6
     frame.document()->setFocusedElement(image.get());
 }
 
-void WebPage::potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint& position)
+void WebPage::potentialTapAtPosition(uint64_t requestID, const WebCore::FloatPoint& position, bool shouldRequestMagnificationInformation)
 {
     m_potentialTapNode = m_page->mainFrame().nodeRespondingToClickEvents(position, m_potentialTapLocation, m_potentialTapSecurityOrigin.get());
+
+    if (shouldRequestMagnificationInformation && m_potentialTapNode && m_viewGestureGeometryCollector) {
+        // FIXME: Could this be combined into tap highlight?
+        FloatPoint origin = position;
+        FloatRect renderRect;
+        bool fitEntireRect;
+        double viewportMinimumScale;
+        double viewportMaximumScale;
+
+        m_viewGestureGeometryCollector->computeZoomInformationForNode(*m_potentialTapNode, origin, renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale);
+        send(Messages::WebPageProxy::HandleSmartMagnificationInformationForPotentialTap(requestID, renderRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale));
+    }
+
     sendTapHighlightForNodeIfNecessary(requestID, m_potentialTapNode.get());
 #if ENABLE(TOUCH_EVENTS)
     if (m_potentialTapNode && !m_potentialTapNode->allowsDoubleTapGesture())