[iOS WK2] Page jumps when rubber-banding on azuremagazine.com
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Jun 2014 04:15:52 +0000 (04:15 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Jun 2014 04:15:52 +0000 (04:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=134238
<rdar://problem/16918228>

Reviewed by Benjamin Poulain.

If the scroll view is in the process of rubber-banding when -setContentSize: is called,
it clamps the scroll offsets between zero and the max value, which visibly interrupts the
rubberband. This can easily happen now that we continually send scroll events to the page
on scrolling, especially when pages like azuremagazine.com do fake sticky by toggling
in-flow elements into position:fixed.

Fix by computing the amount of rubber-band before calling -setContentSize:, and then
restoring the contentOffset with the same amount of rubber-band even when the content size
is different, for top/left rubberbands.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didCommitLayerTree:WebKit::]):
* UIProcess/ios/WKScrollView.h:
* UIProcess/ios/WKScrollView.mm:
(-[WKScrollView _currentTopLeftRubberbandAmount]):
(-[WKScrollView _restoreContentOffsetWithRubberbandAmount:]):
(-[WKScrollView _setContentSizePreservingContentOffsetDuringRubberband:]):

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

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/ios/WKScrollView.h
Source/WebKit2/UIProcess/ios/WKScrollView.mm

index 745fce200f059cd3ae98e7d45edf210cda64580b..0fc1f53787de7282f74389120c159f7b71663e6b 100644 (file)
@@ -1,3 +1,29 @@
+2014-06-25  Simon Fraser  <simon.fraser@apple.com>
+
+        [iOS WK2] Page jumps when rubber-banding on azuremagazine.com 
+        https://bugs.webkit.org/show_bug.cgi?id=134238
+        <rdar://problem/16918228>
+
+        Reviewed by Benjamin Poulain.
+        
+        If the scroll view is in the process of rubber-banding when -setContentSize: is called,
+        it clamps the scroll offsets between zero and the max value, which visibly interrupts the
+        rubberband. This can easily happen now that we continually send scroll events to the page
+        on scrolling, especially when pages like azuremagazine.com do fake sticky by toggling
+        in-flow elements into position:fixed.
+        
+        Fix by computing the amount of rubber-band before calling -setContentSize:, and then
+        restoring the contentOffset with the same amount of rubber-band even when the content size
+        is different, for top/left rubberbands.
+        
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didCommitLayerTree:WebKit::]):
+        * UIProcess/ios/WKScrollView.h:
+        * UIProcess/ios/WKScrollView.mm:
+        (-[WKScrollView _currentTopLeftRubberbandAmount]):
+        (-[WKScrollView _restoreContentOffsetWithRubberbandAmount:]):
+        (-[WKScrollView _setContentSizePreservingContentOffsetDuringRubberband:]):
+
 2014-06-25  Simon Fraser  <simon.fraser@apple.com>
 
         [iOS WK2] Fixed position elements jump around when zooming
index a2552dec674f7cacbeb249b807978977c50e6343..5177a037e331e7bb6cb4c7171b1f0092279d2f08 100644 (file)
@@ -774,7 +774,9 @@ static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, Web
         return;
     }
 
-    [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
+    CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
+    [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
+
     [_scrollView setMinimumZoomScale:layerTreeTransaction.minimumScaleFactor()];
     [_scrollView setMaximumZoomScale:layerTreeTransaction.maximumScaleFactor()];
     [_scrollView setZoomEnabled:layerTreeTransaction.allowsUserScaling()];
index a87f475e7d44d6028ccee1452c639d4c20fb3e94..cb40bd8ff5db78f9217e9a647dbd99d3f0768acc 100644 (file)
@@ -33,6 +33,8 @@
 
 @property (nonatomic, assign) WKWebView <UIScrollViewDelegate> *internalDelegate;
 
+- (void)_setContentSizePreservingContentOffsetDuringRubberband:(CGSize)contentSize;
+
 @end
 
 #endif // PLATFORM(IOS)
index 413369bcc4e8fd717b4bfaef0cfad8228f3275c0..7d5a2f25e6cc0d4bb276f686313c708f7b147e10 100644 (file)
@@ -198,6 +198,54 @@ static inline bool valuesAreWithinOnePixel(CGFloat a, CGFloat b)
     [_internalDelegate _updateVisibleContentRects];
 }
 
+// Fetch top/left rubberband amounts (as negative values).
+- (CGSize)_currentTopLeftRubberbandAmount
+{
+    UIEdgeInsets edgeInsets = [self contentInset];
+
+    CGSize rubberbandAmount = CGSizeZero;
+
+    CGPoint contentOffset = [self contentOffset];
+    if (contentOffset.x < -edgeInsets.left)
+        rubberbandAmount.width = std::min<CGFloat>(contentOffset.x + -edgeInsets.left, 0);
+
+    if (contentOffset.y < -edgeInsets.top)
+        rubberbandAmount.height = std::min<CGFloat>(contentOffset.y + edgeInsets.top, 0);
+    
+    return rubberbandAmount;
+}
+
+- (void)_restoreContentOffsetWithRubberbandAmount:(CGSize)rubberbandAmount
+{
+    UIEdgeInsets edgeInsets = [self contentInset];
+    CGPoint adjustedOffset = [self contentOffset];
+
+    if (rubberbandAmount.width < 0)
+        adjustedOffset.x = -edgeInsets.left + rubberbandAmount.width;
+
+    if (rubberbandAmount.height < 0)
+        adjustedOffset.y = -edgeInsets.top + rubberbandAmount.height;
+
+    [self setContentOffset:adjustedOffset];
+}
+
+- (void)_setContentSizePreservingContentOffsetDuringRubberband:(CGSize)contentSize
+{
+    CGSize currentContentSize = [self contentSize];
+
+    if (CGSizeEqualToSize(currentContentSize, CGSizeZero) || CGSizeEqualToSize(currentContentSize, contentSize) || self.zoomScale < self.minimumZoomScale) {
+        [self setContentSize:contentSize];
+        return;
+    }
+
+    CGSize rubberbandAmount = [self _currentTopLeftRubberbandAmount];
+
+    [self setContentSize:contentSize];
+
+    if (!CGSizeEqualToSize(rubberbandAmount, CGSizeZero))
+        [self _restoreContentOffsetWithRubberbandAmount:rubberbandAmount];
+}
+
 @end
 
 #endif // PLATFORM(IOS)