[iOS WK2] The WebProcess keep scrolling pages based on the last velocity after scroll...
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Feb 2015 02:07:33 +0000 (02:07 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Feb 2015 02:07:33 +0000 (02:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141933
rdar://problem/18746673
rdar://problem/19711490

Patch by Benjamin Poulain <bpoulain@apple.com> on 2015-02-23
Reviewed by Simon Fraser.

The bug happened like this:
1) The user scroll the page. WKContentView tracks the velocity and send
   the update rect + velocity to the WebProcess.
2) The user interupts the scrolling but does not commit to either scrolling
   again or cancelling the scrolling.
   Since we were not notified of this state, the WebProcess still believed
   the velocity is stable.
3) With any paint update, the WebProcess would account for the last velocity
   and try to guess the best repaint area. This would drift endlessly out
   of the view since the view is not really moving.

This patch fixes the issue by adding special handling for interrupted scrolling.

Kudos to Kurt Revis for providing us the required APIs.

* Shared/VisibleContentRectUpdateInfo.h:
(WebKit::operator==):
We can no longer filter VisibleContentRectUpdateInfo ignoring the velocity.

Typically, UIScrollView would call -scrollViewDidScroll: before being interrupted.
If we filter based on the VisibleContentRectUpdateInfo, we have two identical
states differing only by the velocity. If we filter the second update, the WebProcess
would never know the velocity should be zero.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _scrollViewDidInterruptDecelerating:]):
We get this callback when scrolling is interrupted. We just need to clear
the velocity and re-send a new update for the current state.

(-[WKWebView _updateVisibleContentRects]):
Do not consider an interrupted scroll as a stable state. We don't know if scrolling
will resume or will stop.

* UIProcess/ios/WKContentView.h:
* UIProcess/ios/WKContentView.mm:
(-[WKContentView didInterruptScrolling]):

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

Source/WebKit2/ChangeLog
Source/WebKit2/Shared/VisibleContentRectUpdateInfo.h
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/ios/WKContentView.h
Source/WebKit2/UIProcess/ios/WKContentView.mm

index f9e4304..38eb8d8 100644 (file)
@@ -1,3 +1,49 @@
+2015-02-23  Benjamin Poulain  <bpoulain@apple.com>
+
+        [iOS WK2] The WebProcess keep scrolling pages based on the last velocity after scrolling has been interrupted
+        https://bugs.webkit.org/show_bug.cgi?id=141933
+        rdar://problem/18746673
+        rdar://problem/19711490
+
+        Reviewed by Simon Fraser.
+
+        The bug happened like this:
+        1) The user scroll the page. WKContentView tracks the velocity and send
+           the update rect + velocity to the WebProcess.
+        2) The user interupts the scrolling but does not commit to either scrolling
+           again or cancelling the scrolling.
+           Since we were not notified of this state, the WebProcess still believed
+           the velocity is stable.
+        3) With any paint update, the WebProcess would account for the last velocity
+           and try to guess the best repaint area. This would drift endlessly out
+           of the view since the view is not really moving.
+
+        This patch fixes the issue by adding special handling for interrupted scrolling.
+
+        Kudos to Kurt Revis for providing us the required APIs.
+
+        * Shared/VisibleContentRectUpdateInfo.h:
+        (WebKit::operator==):
+        We can no longer filter VisibleContentRectUpdateInfo ignoring the velocity.
+
+        Typically, UIScrollView would call -scrollViewDidScroll: before being interrupted.
+        If we filter based on the VisibleContentRectUpdateInfo, we have two identical
+        states differing only by the velocity. If we filter the second update, the WebProcess
+        would never know the velocity should be zero.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _scrollViewDidInterruptDecelerating:]):
+        We get this callback when scrolling is interrupted. We just need to clear
+        the velocity and re-send a new update for the current state.
+
+        (-[WKWebView _updateVisibleContentRects]):
+        Do not consider an interrupted scroll as a stable state. We don't know if scrolling
+        will resume or will stop.
+
+        * UIProcess/ios/WKContentView.h:
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView didInterruptScrolling]):
+
 2015-02-23  Anders Carlsson  <andersca@apple.com>
 
         Add API for fetching website data records to _WKWebsiteDataStore
index bd3a3a7..ae63ea6 100644 (file)
@@ -101,6 +101,9 @@ inline bool operator==(const VisibleContentRectUpdateInfo& a, const VisibleConte
         && a.exposedRect() == b.exposedRect()
         && a.unobscuredRect() == b.unobscuredRect()
         && a.customFixedPositionRect() == b.customFixedPositionRect()
+        && a.horizontalVelocity() == b.horizontalVelocity()
+        && a.verticalVelocity() == b.verticalVelocity()
+        && a.scaleChangeRate() == b.scaleChangeRate()
         && a.inStableState() == b.inStableState();
 }
 
index 2869df7..8f6a4f9 100644 (file)
 @interface UIScrollView (UIScrollViewInternal)
 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary*)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
 - (BOOL)_isScrollingToTop;
+- (BOOL)_isInterruptingDeceleration;
 @end
 
 @interface UIPeripheralHost(UIKitInternal)
@@ -1411,6 +1412,15 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     [self _didFinishScrolling];
 }
 
+- (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
+{
+    if (![self usesStandardContentView])
+        return;
+
+    [_contentView didInterruptScrolling];
+    [self _updateVisibleContentRects];
+}
+
 - (void)_frameOrBoundsChanged
 {
     CGRect bounds = self.bounds;
@@ -1466,6 +1476,11 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     CGFloat scaleFactor = contentZoomScale(self);
 
     BOOL isStableState = !(_isChangingObscuredInsetsInteractively || [_scrollView isDragging] || [_scrollView isDecelerating] || [_scrollView isZooming] || [_scrollView isZoomBouncing] || [_scrollView _isAnimatingZoom] || [_scrollView _isScrollingToTop]);
+
+    // FIXME: this can be made static after we stop supporting iOS 8.x.
+    if (isStableState && [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
+        isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
+
     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
         unobscuredRect:unobscuredRectInContentCoordinates
         unobscuredRectInScrollViewCoordinates:unobscuredRect
index b06363b..c6bb3b7 100644 (file)
@@ -71,6 +71,7 @@ struct WebPageConfiguration;
     inStableState:(BOOL)isStableState isChangingObscuredInsetsInteractively:(BOOL)isChangingObscuredInsetsInteractively;
 
 - (void)didFinishScrolling;
+- (void)didInterruptScrolling;
 - (void)didZoomToScale:(CGFloat)scale;
 - (void)willStartZoomOrScroll;
 
index 313e2f7..c15af20 100644 (file)
@@ -381,6 +381,11 @@ private:
     [self _didEndScrollingOrZooming];
 }
 
+- (void)didInterruptScrolling
+{
+    _historicalKinematicData.clear();
+}
+
 - (void)willStartZoomOrScroll
 {
     [self _willStartScrollingOrZooming];