[iOS] Remote scrolling tree needs to coordinate scroll snap state during resize/rotations
authorbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 16 May 2015 23:00:06 +0000 (23:00 +0000)
committerbfulgham@apple.com <bfulgham@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 16 May 2015 23:00:06 +0000 (23:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=145059
<rdar://problem/20975978>

Reviewed by Simon Fraser.

The web view needs to update its scroll snap point offsets to take into account any
adjustments to the view size caused by content insets. We also need to update the
offstes after device rotation.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Retrieve the proper
computed content inset for the view and incorporate into any scroll snap point adjustments.
(-[WKWebView _updateVisibleContentRects]): If we have active scroll snap points,
* UIProcess/Scrolling/RemoteScrollingCoordinatorProxy.h:
* UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm:
(WebKit::RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping): Update to account
for content inset.
(WebKit::RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling): Also validate that the
active index is valid.
(WebKit::RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling): Update to track
current active snap offset index.
(WebKit::RemoteScrollingCoordinatorProxy::hasActiveSnapPoint): Added.
(WebKit::RemoteScrollingCoordinatorProxy::nearestActiveSnapPoint): Added. It calculates the proper
scroll position incorporating any snap point and content insets.

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

Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/Scrolling/RemoteScrollingCoordinatorProxy.h
Source/WebKit2/UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm

index 3172df8..3560023 100644 (file)
@@ -1,3 +1,31 @@
+2015-05-16  Brent Fulgham  <bfulgham@apple.com>
+
+        [iOS] Remote scrolling tree needs to coordinate scroll snap state during resize/rotations
+        https://bugs.webkit.org/show_bug.cgi?id=145059
+        <rdar://problem/20975978>
+
+        Reviewed by Simon Fraser.
+
+        The web view needs to update its scroll snap point offsets to take into account any
+        adjustments to the view size caused by content insets. We also need to update the
+        offstes after device rotation.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView scrollViewWillEndDragging:withVelocity:targetContentOffset:]): Retrieve the proper
+        computed content inset for the view and incorporate into any scroll snap point adjustments.
+        (-[WKWebView _updateVisibleContentRects]): If we have active scroll snap points, 
+        * UIProcess/Scrolling/RemoteScrollingCoordinatorProxy.h:
+        * UIProcess/ios/RemoteScrollingCoordinatorProxyIOS.mm:
+        (WebKit::RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping): Update to account
+        for content inset.
+        (WebKit::RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling): Also validate that the
+        active index is valid.
+        (WebKit::RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling): Update to track
+        current active snap offset index.
+        (WebKit::RemoteScrollingCoordinatorProxy::hasActiveSnapPoint): Added.
+        (WebKit::RemoteScrollingCoordinatorProxy::nearestActiveSnapPoint): Added. It calculates the proper
+        scroll position incorporating any snap point and content insets.
+
 2015-05-15  Yongjun Zhang  <yongjun_zhang@apple.com>
 
         Don't reset the preview recognizer in [WKContentViewInteraction cleanupInteraction]
 2015-05-15  Yongjun Zhang  <yongjun_zhang@apple.com>
 
         Don't reset the preview recognizer in [WKContentViewInteraction cleanupInteraction]
index 3550428..544df08 100644 (file)
@@ -1413,7 +1413,11 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
     if (WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy()) {
         // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
         CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
-        coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, targetContentOffset);
+        
+        CGRect fullViewRect = self.bounds;
+        CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
+        
+        coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, targetContentOffset);
     }
 #endif
 }
     }
 #endif
 }
@@ -1533,6 +1537,26 @@ static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOff
     if (isStableState && [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
         isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
 
     if (isStableState && [_scrollView respondsToSelector:@selector(_isInterruptingDeceleration)])
         isStableState = ![_scrollView performSelector:@selector(_isInterruptingDeceleration)];
 
+#if ENABLE(CSS_SCROLL_SNAP) && ENABLE(ASYNC_SCROLLING)
+    if (isStableState) {
+        WebKit::RemoteScrollingCoordinatorProxy* coordinator = _page->scrollingCoordinatorProxy();
+        if (coordinator && coordinator->hasActiveSnapPoint()) {
+            CGRect fullViewRect = self.bounds;
+            CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedContentInset]);
+            
+            CGPoint currentPoint = [_scrollView contentOffset];
+            CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapPoint(unobscuredRect.origin.y, currentPoint);
+
+            if (!CGPointEqualToPoint(activePoint, currentPoint)) {
+                RetainPtr<WKScrollView> strongScrollView = _scrollView;
+                dispatch_async(dispatch_get_main_queue(), [strongScrollView, activePoint] {
+                    [strongScrollView setContentOffset:activePoint animated:NO];
+                });
+            }
+        }
+    }
+#endif
+    
     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
         unobscuredRect:unobscuredRectInContentCoordinates
         unobscuredRectInScrollViewCoordinates:unobscuredRect
     [_contentView didUpdateVisibleRect:visibleRectInContentCoordinates
         unobscuredRect:unobscuredRectInContentCoordinates
         unobscuredRectInScrollViewCoordinates:unobscuredRect
index 0818daf..ab7dd5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -85,7 +85,9 @@ public:
     void scrollingTreeNodeWillStartScroll();
     void scrollingTreeNodeDidEndScroll();
 #if ENABLE(CSS_SCROLL_SNAP)
     void scrollingTreeNodeWillStartScroll();
     void scrollingTreeNodeDidEndScroll();
 #if ENABLE(CSS_SCROLL_SNAP)
-    void adjustTargetContentOffsetForSnapping(CGSize maxScrollDimensions, CGPoint velocity, CGPoint* targetContentOffset) const;
+    void adjustTargetContentOffsetForSnapping(CGSize maxScrollDimensions, CGPoint velocity, CGFloat topInset, CGPoint* targetContentOffset);
+    bool hasActiveSnapPoint() const;
+    CGPoint nearestActiveContentInsetAdjustedSnapPoint(CGFloat topInset, const CGPoint&) const;
     bool shouldSetScrollViewDecelerationRateFast() const;
 #endif
 #endif
     bool shouldSetScrollViewDecelerationRateFast() const;
 #endif
 #endif
@@ -94,12 +96,16 @@ private:
     void connectStateNodeLayers(WebCore::ScrollingStateTree&, const RemoteLayerTreeHost&);
 #if ENABLE(CSS_SCROLL_SNAP)
     bool shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis) const;
     void connectStateNodeLayers(WebCore::ScrollingStateTree&, const RemoteLayerTreeHost&);
 #if ENABLE(CSS_SCROLL_SNAP)
     bool shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis) const;
-    float closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis, float scrollDestination, float velocity) const;
+    float closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis, float scrollDestination, float velocity, unsigned& closestIndex) const;
 #endif
 
     WebPageProxy& m_webPageProxy;
     RefPtr<RemoteScrollingTree> m_scrollingTree;
     RequestedScrollInfo* m_requestedScrollInfo;
 #endif
 
     WebPageProxy& m_webPageProxy;
     RefPtr<RemoteScrollingTree> m_scrollingTree;
     RequestedScrollInfo* m_requestedScrollInfo;
+#if ENABLE(CSS_SCROLL_SNAP)
+    unsigned m_currentHorizontalSnapPointIndex { 0 };
+    unsigned m_currentVerticalSnapPointIndex { 0 };
+#endif
     bool m_propagatesMainFrameScrolls;
 };
 
     bool m_propagatesMainFrameScrolls;
 };
 
index a861fec..bef55be 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -113,16 +113,17 @@ void RemoteScrollingCoordinatorProxy::scrollingTreeNodeDidEndScroll()
 }
 
 #if ENABLE(CSS_SCROLL_SNAP)
 }
 
 #if ENABLE(CSS_SCROLL_SNAP)
-void RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping(CGSize maxScrollOffsets, CGPoint velocity, CGPoint* targetContentOffset) const
+void RemoteScrollingCoordinatorProxy::adjustTargetContentOffsetForSnapping(CGSize maxScrollOffsets, CGPoint velocity, CGFloat topInset, CGPoint* targetContentOffset)
 {
     // The bounds checking with maxScrollOffsets is to ensure that we won't interfere with rubber-banding when scrolling to the edge of the page.
     if (shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis::Horizontal) && targetContentOffset->x > 0 && targetContentOffset->x < maxScrollOffsets.width) {
 {
     // The bounds checking with maxScrollOffsets is to ensure that we won't interfere with rubber-banding when scrolling to the edge of the page.
     if (shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis::Horizontal) && targetContentOffset->x > 0 && targetContentOffset->x < maxScrollOffsets.width) {
-        float potentialSnapPosition = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Horizontal, targetContentOffset->x, velocity.x);
+        float potentialSnapPosition = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Horizontal, targetContentOffset->x, velocity.x, m_currentHorizontalSnapPointIndex);
         targetContentOffset->x = std::min<float>(maxScrollOffsets.width, potentialSnapPosition);
     }
         targetContentOffset->x = std::min<float>(maxScrollOffsets.width, potentialSnapPosition);
     }
-    // FIXME: We need to account for how the top navigation bar changes in size.
+
     if (shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical) && targetContentOffset->y > 0 && targetContentOffset->y < maxScrollOffsets.height) {
     if (shouldSnapForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical) && targetContentOffset->y > 0 && targetContentOffset->y < maxScrollOffsets.height) {
-        float potentialSnapPosition = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical, targetContentOffset->y, velocity.y);
+        float potentialSnapPosition = closestSnapOffsetForMainFrameScrolling(WebCore::ScrollEventAxis::Vertical, targetContentOffset->y, velocity.y, m_currentVerticalSnapPointIndex);
+        potentialSnapPosition -= topInset;
         targetContentOffset->y = std::min<float>(maxScrollOffsets.height, potentialSnapPosition);
     }
 }
         targetContentOffset->y = std::min<float>(maxScrollOffsets.height, potentialSnapPosition);
     }
 }
@@ -138,23 +139,71 @@ bool RemoteScrollingCoordinatorProxy::shouldSnapForMainFrameScrolling(ScrollEven
     if (root && root->isFrameScrollingNode()) {
         ScrollingTreeFrameScrollingNode* rootFrame = static_cast<ScrollingTreeFrameScrollingNode*>(root);
         const Vector<float>& snapOffsets = axis == ScrollEventAxis::Horizontal ? rootFrame->horizontalSnapOffsets() : rootFrame->verticalSnapOffsets();
     if (root && root->isFrameScrollingNode()) {
         ScrollingTreeFrameScrollingNode* rootFrame = static_cast<ScrollingTreeFrameScrollingNode*>(root);
         const Vector<float>& snapOffsets = axis == ScrollEventAxis::Horizontal ? rootFrame->horizontalSnapOffsets() : rootFrame->verticalSnapOffsets();
-        return snapOffsets.size() > 0;
+        unsigned currentIndex = axis == ScrollEventAxis::Horizontal ? m_currentHorizontalSnapPointIndex : m_currentVerticalSnapPointIndex;
+        return (snapOffsets.size() > 0) && (currentIndex < snapOffsets.size());
     }
     return false;
 }
 
     }
     return false;
 }
 
-float RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling(ScrollEventAxis axis, float scrollDestination, float velocity) const
+float RemoteScrollingCoordinatorProxy::closestSnapOffsetForMainFrameScrolling(ScrollEventAxis axis, float scrollDestination, float velocity, unsigned& currentIndex) const
 {
     ScrollingTreeNode* root = m_scrollingTree->rootNode();
     ASSERT(root && root->isFrameScrollingNode());
     ScrollingTreeFrameScrollingNode* rootFrame = static_cast<ScrollingTreeFrameScrollingNode*>(root);
     const Vector<float>& snapOffsets = axis == ScrollEventAxis::Horizontal ? rootFrame->horizontalSnapOffsets() : rootFrame->verticalSnapOffsets();
 
 {
     ScrollingTreeNode* root = m_scrollingTree->rootNode();
     ASSERT(root && root->isFrameScrollingNode());
     ScrollingTreeFrameScrollingNode* rootFrame = static_cast<ScrollingTreeFrameScrollingNode*>(root);
     const Vector<float>& snapOffsets = axis == ScrollEventAxis::Horizontal ? rootFrame->horizontalSnapOffsets() : rootFrame->verticalSnapOffsets();
 
-    unsigned ignore = 0;
     float scaledScrollDestination = scrollDestination / m_webPageProxy.displayedContentScale();
     float scaledScrollDestination = scrollDestination / m_webPageProxy.displayedContentScale();
-    float rawClosestSnapOffset = closestSnapOffset<float, float>(snapOffsets, scaledScrollDestination, velocity, ignore);
+    float rawClosestSnapOffset = closestSnapOffset<float, float>(snapOffsets, scaledScrollDestination, velocity, currentIndex);
     return rawClosestSnapOffset * m_webPageProxy.displayedContentScale();
 }
     return rawClosestSnapOffset * m_webPageProxy.displayedContentScale();
 }
+
+bool RemoteScrollingCoordinatorProxy::hasActiveSnapPoint() const
+{
+    ScrollingTreeNode* root = m_scrollingTree->rootNode();
+    if (!root)
+        return false;
+
+    if (!is<ScrollingTreeFrameScrollingNode>(root))
+        return false;
+
+    ScrollingTreeFrameScrollingNode& rootFrame = downcast<ScrollingTreeFrameScrollingNode>(*root);
+    const Vector<float>& horizontal = rootFrame.horizontalSnapOffsets();
+    const Vector<float>& vertical = rootFrame.verticalSnapOffsets();
+
+    if (horizontal.isEmpty() && vertical.isEmpty())
+        return false;
+
+    if ((!horizontal.isEmpty() && m_currentHorizontalSnapPointIndex >= horizontal.size())
+        || (!vertical.isEmpty() && m_currentVerticalSnapPointIndex >= vertical.size())) {
+        return false;
+    }
+    
+    return true;
+}
+    
+CGPoint RemoteScrollingCoordinatorProxy::nearestActiveContentInsetAdjustedSnapPoint(CGFloat topInset, const CGPoint& currentPoint) const
+{
+    CGPoint activePoint = currentPoint;
+
+    ScrollingTreeNode* root = m_scrollingTree->rootNode();
+    ASSERT(root && is<ScrollingTreeFrameScrollingNode>(root));
+    ScrollingTreeFrameScrollingNode& rootFrame = downcast<ScrollingTreeFrameScrollingNode>(*root);
+    const Vector<float>& horizontal = rootFrame.horizontalSnapOffsets();
+    const Vector<float>& vertical = rootFrame.verticalSnapOffsets();
+
+    // The bounds checking with maxScrollOffsets is to ensure that we won't interfere with rubber-banding when scrolling to the edge of the page.
+    if (!horizontal.isEmpty() && m_currentHorizontalSnapPointIndex < horizontal.size())
+        activePoint.x = horizontal[m_currentHorizontalSnapPointIndex] * m_webPageProxy.displayedContentScale();
+
+    if (!vertical.isEmpty() && m_currentVerticalSnapPointIndex < vertical.size()) {
+        float potentialSnapPosition = vertical[m_currentVerticalSnapPointIndex] * m_webPageProxy.displayedContentScale();
+        potentialSnapPosition -= topInset;
+        activePoint.y = potentialSnapPosition;
+    }
+
+    return activePoint;
+}
+
 #endif
 
 } // namespace WebKit
 #endif
 
 } // namespace WebKit