Allow double tap to zoom in fast-click pages
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 17 Feb 2016 03:27:05 +0000 (03:27 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 17 Feb 2016 03:27:05 +0000 (03:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154318
<rdar://problem/24223767>

Reviewed by Simon Fraser and Benjamin Poulain.

Most of the patch comes from Jon Lee.

Our fast-click algorithm exposed a number of cases where
people missed the double-tap-to-zoom behaviour. In particular,
when you double tap on a large body of text, typical in
blogs and articles.

This patch enhances the algorithm to have a parallel
double-tap gesture recognizer in the situations where
fast-click is enabled. This new gesture recongizer does
not cause the single tap to block for 350ms, so clicks
are still dispatched fast. If it fires, we already have
some information about whether we have a pending double
tap, based on the first tap.

* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::disableDoubleTapGesturesDuringTapIfNecessary): Remove the optimization
that only told the content view to disable on pages that allowed double taps. We now allow
them even on fast click pages.

* UIProcess/ios/WKContentViewInteraction.h: Add the new UITapGestureRecognizer for double taps
in fast click pages. This is called nonBlockingDoubleTapGestureRecognizer because, unlike
the existing DoubleTapGestureRecognizer, this one does not force the singleTapRecognizer
to wait.
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]): Set up the new UITapGestureRecognizer.
(-[WKContentView cleanupInteraction]): And remove it when we're done.
(-[WKContentView _removeDefaultGestureRecognizers]): Ditto.
(-[WKContentView _addDefaultGestureRecognizers]):
(-[WKContentView _inspectorNodeSearchRecognized:]): Something happened, we are no longer in
a potential double tap situation.
(-[WKContentView _disableDoubleTapGesturesDuringTapIfNecessary:]): Remove the check
for potential tap in progress.
(-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
(-[WKContentView gestureRecognizerShouldBegin:]):
(-[WKContentView _highlightLongPressRecognized:]): Again, something happened, so we are
no longer in a double tap situation.
(-[WKContentView _longPressRecognized:]): Ditto.
(-[WKContentView _singleTapRecognized:]): Ditto.
(-[WKContentView _doubleTapRecognized:]): Ditto.
(-[WKContentView _resetIsDoubleTapPending]):
(-[WKContentView _fastDoubleTapRecognized:]): We're now pending a double tap.
(-[WKContentView _twoFingerDoubleTapRecognized:]):
(-[WKContentView _didNotHandleTapAsClick:]): If we get here and we have a pending
double tap, then trigger a zoom operation.
(-[WKContentView _setDoubleTapGesturesEnabled:]):

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

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit2/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit2/UIProcess/ios/WKContentViewInteraction.mm

index 3467635..60189e5 100644 (file)
@@ -1,3 +1,58 @@
+2016-02-16  Dean Jackson  <dino@apple.com>
+
+        Allow double tap to zoom in fast-click pages
+        https://bugs.webkit.org/show_bug.cgi?id=154318
+        <rdar://problem/24223767>
+
+        Reviewed by Simon Fraser and Benjamin Poulain.
+        
+        Most of the patch comes from Jon Lee.
+
+        Our fast-click algorithm exposed a number of cases where
+        people missed the double-tap-to-zoom behaviour. In particular,
+        when you double tap on a large body of text, typical in
+        blogs and articles.
+
+        This patch enhances the algorithm to have a parallel
+        double-tap gesture recognizer in the situations where
+        fast-click is enabled. This new gesture recongizer does
+        not cause the single tap to block for 350ms, so clicks
+        are still dispatched fast. If it fires, we already have
+        some information about whether we have a pending double
+        tap, based on the first tap.
+
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::disableDoubleTapGesturesDuringTapIfNecessary): Remove the optimization
+        that only told the content view to disable on pages that allowed double taps. We now allow
+        them even on fast click pages.
+
+        * UIProcess/ios/WKContentViewInteraction.h: Add the new UITapGestureRecognizer for double taps
+        in fast click pages. This is called nonBlockingDoubleTapGestureRecognizer because, unlike
+        the existing DoubleTapGestureRecognizer, this one does not force the singleTapRecognizer
+        to wait.
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setupInteraction]): Set up the new UITapGestureRecognizer.
+        (-[WKContentView cleanupInteraction]): And remove it when we're done.
+        (-[WKContentView _removeDefaultGestureRecognizers]): Ditto.
+        (-[WKContentView _addDefaultGestureRecognizers]):
+        (-[WKContentView _inspectorNodeSearchRecognized:]): Something happened, we are no longer in
+        a potential double tap situation.
+        (-[WKContentView _disableDoubleTapGesturesDuringTapIfNecessary:]): Remove the check
+        for potential tap in progress.
+        (-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
+        (-[WKContentView gestureRecognizerShouldBegin:]):
+        (-[WKContentView _highlightLongPressRecognized:]): Again, something happened, so we are
+        no longer in a double tap situation.
+        (-[WKContentView _longPressRecognized:]): Ditto.
+        (-[WKContentView _singleTapRecognized:]): Ditto.
+        (-[WKContentView _doubleTapRecognized:]): Ditto.
+        (-[WKContentView _resetIsDoubleTapPending]):
+        (-[WKContentView _fastDoubleTapRecognized:]): We're now pending a double tap.
+        (-[WKContentView _twoFingerDoubleTapRecognized:]):
+        (-[WKContentView _didNotHandleTapAsClick:]): If we get here and we have a pending
+        double tap, then trigger a zoom operation.
+        (-[WKContentView _setDoubleTapGesturesEnabled:]):
+
 2016-02-16  Alex Christensen  <achristensen@webkit.org>
 
         CMake build fix.
index 475b1aa..a3c3297 100644 (file)
@@ -261,9 +261,6 @@ void PageClientImpl::didChangeContentSize(const WebCore::IntSize&)
 
 void PageClientImpl::disableDoubleTapGesturesDuringTapIfNecessary(uint64_t requestID)
 {
-    if (!m_webView._allowsDoubleTapGestures)
-        return;
-
     [m_contentView _disableDoubleTapGesturesDuringTapIfNecessary:requestID];
 }
 
index 877e4ae..fc314bc 100644 (file)
@@ -102,6 +102,7 @@ struct WKAutoCorrectionData {
     RetainPtr<_UIWebHighlightLongPressGestureRecognizer> _highlightLongPressGestureRecognizer;
     RetainPtr<UILongPressGestureRecognizer> _longPressGestureRecognizer;
     RetainPtr<UITapGestureRecognizer> _doubleTapGestureRecognizer;
+    RetainPtr<UITapGestureRecognizer> _nonBlockingDoubleTapGestureRecognizer;
     RetainPtr<UITapGestureRecognizer> _twoFingerDoubleTapGestureRecognizer;
     RetainPtr<WKInspectorNodeSearchGestureRecognizer> _inspectorNodeSearchGestureRecognizer;
 
@@ -154,6 +155,7 @@ struct WKAutoCorrectionData {
     BOOL _hasValidPositionInformation;
     BOOL _isTapHighlightIDValid;
     BOOL _potentialTapInProgress;
+    BOOL _isDoubleTapPending;
     BOOL _highlightLongPressCanClick;
     BOOL _hasTapHighlightForPotentialTap;
     BOOL _selectionNeedsUpdate;
index 9afdd03..92c698f 100644 (file)
@@ -461,6 +461,12 @@ static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularit
     [_singleTapGestureRecognizer setResetTarget:self action:@selector(_singleTapDidReset:)];
     [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
 
+    _nonBlockingDoubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_nonBlockingDoubleTapRecognized:)]);
+    [_nonBlockingDoubleTapGestureRecognizer setNumberOfTapsRequired:2];
+    [_nonBlockingDoubleTapGestureRecognizer setDelegate:self];
+    [_nonBlockingDoubleTapGestureRecognizer setEnabled:NO];
+    [self addGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
+
     [self _createAndConfigureDoubleTapGestureRecognizer];
 
     _twoFingerDoubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_twoFingerDoubleTapRecognized:)]);
@@ -494,6 +500,8 @@ static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularit
     [_actionSheetAssistant setDelegate:self];
     _smartMagnificationController = std::make_unique<SmartMagnificationController>(self);
     _isExpectingFastSingleTapCommit = NO;
+    _potentialTapInProgress = NO;
+    _isDoubleTapPending = NO;
     _showDebugTapHighlightsForFastClicking = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitShowFastClickDebugTapHighlights"];
 }
 
@@ -532,6 +540,9 @@ static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularit
     [_doubleTapGestureRecognizer setDelegate:nil];
     [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
 
+    [_nonBlockingDoubleTapGestureRecognizer setDelegate:nil];
+    [self removeGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
+
     [_twoFingerDoubleTapGestureRecognizer setDelegate:nil];
     [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
 
@@ -559,6 +570,7 @@ static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularit
     [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
     [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
     [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
+    [self removeGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
     [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
 }
 
@@ -568,6 +580,7 @@ static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularit
     [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
     [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
     [self addGestureRecognizer:_doubleTapGestureRecognizer.get()];
+    [self addGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
     [self addGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
 }
 
@@ -732,6 +745,7 @@ static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularit
 - (void)_inspectorNodeSearchRecognized:(UIGestureRecognizer *)gestureRecognizer
 {
     ASSERT(_inspectorNodeSearchEnabled);
+    [self _resetIsDoubleTapPending];
 
     CGPoint point = [gestureRecognizer locationInView:self];
 
@@ -920,7 +934,7 @@ static NSValue *nsSizeForTapHighlightBorderRadius(WebCore::IntSize borderRadius,
 
 - (void)_disableDoubleTapGesturesDuringTapIfNecessary:(uint64_t)requestID
 {
-    if (!_potentialTapInProgress || _latestTapID != requestID)
+    if (_latestTapID != requestID)
         return;
 
     [self _setDoubleTapGesturesEnabled:NO];
@@ -1049,6 +1063,12 @@ static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UI
     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _singleTapGestureRecognizer.get(), _textSelectionAssistant.get().singleTapGesture))
         return YES;
 
+    if (isSamePair(gestureRecognizer, otherGestureRecognizer, _singleTapGestureRecognizer.get(), _nonBlockingDoubleTapGestureRecognizer.get()))
+        return YES;
+
+    if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _nonBlockingDoubleTapGestureRecognizer.get()))
+        return YES;
+
     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _previewSecondaryGestureRecognizer.get()))
         return YES;
 
@@ -1112,6 +1132,7 @@ static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UI
 
     if (gestureRecognizer == _highlightLongPressGestureRecognizer
         || gestureRecognizer == _doubleTapGestureRecognizer
+        || gestureRecognizer == _nonBlockingDoubleTapGestureRecognizer
         || gestureRecognizer == _twoFingerDoubleTapGestureRecognizer
         || gestureRecognizer == _singleTapGestureRecognizer) {
 
@@ -1233,6 +1254,7 @@ static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UI
 - (void)_highlightLongPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
 {
     ASSERT(gestureRecognizer == _highlightLongPressGestureRecognizer);
+    [self _resetIsDoubleTapPending];
 
     _lastInteractionLocation = gestureRecognizer.startPoint;
 
@@ -1263,6 +1285,7 @@ static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UI
 - (void)_longPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
 {
     ASSERT(gestureRecognizer == _longPressGestureRecognizer);
+    [self _resetIsDoubleTapPending];
 
     _lastInteractionLocation = gestureRecognizer.startPoint;
 
@@ -1287,6 +1310,7 @@ static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UI
 {
     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
     ASSERT(!_potentialTapInProgress);
+    [self _resetIsDoubleTapPending];
 
     _page->potentialTapAtPosition(gestureRecognizer.location, ++_latestTapID);
     _potentialTapInProgress = YES;
@@ -1314,6 +1338,22 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
     [self _cancelInteraction];
 }
 
+- (void)_didNotHandleTapAsClick:(const WebCore::IntPoint&)point
+{
+    // FIXME: we should also take into account whether or not the UI delegate
+    // has handled this notification.
+    if (_hasValidPositionInformation && point == _positionInformation.point && _positionInformation.isDataDetectorLink) {
+        [self _showDataDetectorsSheet];
+        return;
+    }
+
+    if (!_isDoubleTapPending)
+        return;
+
+    _smartMagnificationController->handleSmartMagnificationGesture(_lastInteractionLocation);
+    _isDoubleTapPending = NO;
+}
+
 - (void)_singleTapCommited:(UITapGestureRecognizer *)gestureRecognizer
 {
     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
@@ -1351,13 +1391,26 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
 
 - (void)_doubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
 {
+    [self _resetIsDoubleTapPending];
     _lastInteractionLocation = gestureRecognizer.location;
 
     _smartMagnificationController->handleSmartMagnificationGesture(gestureRecognizer.location);
 }
 
+- (void)_resetIsDoubleTapPending
+{
+    _isDoubleTapPending = NO;
+}
+
+- (void)_nonBlockingDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
+{
+    _lastInteractionLocation = gestureRecognizer.location;
+    _isDoubleTapPending = YES;
+}
+
 - (void)_twoFingerDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
 {
+    [self _resetIsDoubleTapPending];
     _lastInteractionLocation = gestureRecognizer.location;
 
     _smartMagnificationController->handleResetMagnificationGesture(gestureRecognizer.location);
@@ -1401,14 +1454,6 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
     _page->clearSelection();
 }
 
-- (void)_didNotHandleTapAsClick:(const WebCore::IntPoint&)point
-{
-    // FIXME: we should also take into account whether or not the UI delegate
-    // has handled this notification.
-    if (_hasValidPositionInformation && point == _positionInformation.point && _positionInformation.isDataDetectorLink)
-        [self _showDataDetectorsSheet];
-}
-
 - (void)_positionInformationDidChange:(const InteractionInformationAtPosition&)info
 {
     _positionInformation = info;
@@ -2460,6 +2505,8 @@ static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoi
         _tapHighlightInformation.color = [self _tapHighlightColorForFastClick:YES];
 
     [_doubleTapGestureRecognizer setEnabled:enabled];
+    [_nonBlockingDoubleTapGestureRecognizer setEnabled:!enabled];
+    [self _resetIsDoubleTapPending];
 }
 
 - (void)accessoryAutoFill