Title: [236935] trunk/Source/WebKit
Revision
236935
Author
[email protected]
Date
2018-10-08 13:18:26 -0700 (Mon, 08 Oct 2018)

Log Message

Adjust keyboard scrolling animator to springy and semiphysical
https://bugs.webkit.org/show_bug.cgi?id=190345
<rdar://problem/43494393>

Reviewed by Simon Fraser.

Introduce WKKeyboardScrollViewAnimator, which wraps WKKeyboardScrollingAnimator
and provides a reasonable set of default behaviors for UIScrollView.
This is the first step on the way to WKKeyboardScrollingAnimator
being platform- and toolkit-agnostic, and helps avoid adding a whole
bunch of code to WKContentView.

Adopt UIKit's scroll-to-top animation curve for whole-document scrolls.

Adjust WKKeyboardScrollingAnimator's physics; it now simulates a spring
attached to the page when decelerating. When a key is pressed, it applies
a constant force to the page (up to some maximum velocity). When released,
the spring causes the page to decelerate smoothly.

Add rubber-banding to keyboard scrolling, based on the same spring that
decelerates the page.

Remove the initial bounce on keydown before starting the smooth scroll.

Remove arrow key commands from WKContentView; this avoids double-processing
incoming arrow key events, makes the event stream make more sense, and was
entirely unnecessary.

* Platform/spi/ios/UIKitSPI.h:
Add a wide variety of useful SPI.

* UIProcess/API/Cocoa/WKWebView.mm:
* UIProcess/API/Cocoa/WKWebViewInternal.h:
Remove _scrollByContentOffset:animated: because it's only used by
the keyboard scrolling mechanism.

Remove _arrowKey because we're removing the arrow key commands.

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]):
Create a WKKeyboardScrollViewAnimator (which knows how to directly
manipulate a UIScrollView) instead of a WKKeyboardScrollingAnimator
(which requires its clients to implement everything in terms of an
abstract interface.

(-[WKContentView scrollViewWillStartPanOrPinchGesture]):
Inform the keyboard scrolling animator that the user is going to start
an interactive gesture that needs control of the scroll view, so it
can stop its current animated scroll.

(-[WKContentView canPerformActionForWebView:withSender:]):
(-[WKContentView keyCommands]):
(-[WKContentView _arrowKeyForWebView:]): Deleted.
Remove arrow key commands.

(-[WKContentView keyboardScrollViewAnimator:distanceForIncrement:]):
Scale from content to WKWebView coordinate space; the keyboard scrolling
animator operates in WKWebView coordinates. This was broken before.

(-[WKContentView keyboardScrollViewAnimatorWillScroll:]):
(-[WKContentView isScrollableForKeyboardScrollViewAnimator:]):
(-[WKContentView isKeyboardScrollable]): Deleted.
(-[WKContentView distanceForScrollingIncrement:]): Deleted.
(-[WKContentView scrollByContentOffset:animated:]): Deleted.
Reimplement these as keyboardScrollViewAnimator delegate methods.

* UIProcess/ios/WKKeyboardScrollingAnimator.h:
Add WKKeyboardScrollViewAnimator, hide the interface for
WKKeyboardScrollingAnimator itself in the implementation file.

* UIProcess/ios/WKKeyboardScrollingAnimator.mm:
(-[WKKeyboardScrollingAnimator initWithScrollable:]):
(-[WKKeyboardScrollingAnimator parameters]):
(-[WKKeyboardScrollingAnimator invalidate]):
(unitVector):
Return a unit vector in the given direction.

(perpendicularAbsoluteUnitVector):
Return a positive unit vector perpendicular to the axis of the given direction.
Useful for multiplying with another vector to remove the component in the
axis of the given direction.

(boxSide):
Convert ScrollingDirection to WebCore::PhysicalBoxSide, for use with RectEdges.

(-[WKKeyboardScrollingAnimator keyboardScrollForEvent:]):
Instead of just returning the desired increment, compute everything we'll need
for the lifetime of the scroll (including the force applied, maximum
velocity, etc.) and return them together.

(-[WKKeyboardScrollingAnimator beginWithEvent:]):
Use the scrollable's native scroll-to-extent animation (e.g. UIScrollView's
scroll-to-top curve) for whole-document scrolls.

Remove the first discrete scroll; start the smooth scroll immediately.

(-[WKKeyboardScrollingAnimator handleKeyEvent:]):
(farthestPointInDirection):
(-[WKKeyboardScrollingAnimator stopAnimatedScroll]):
Stop the current scroll. Let the spring coast to its natural stopping
point given the system's current energy, unless that stopping point is
less than one increment from the starting point. In that case, we attach
the spring to /that/ point and let it run.

(-[WKKeyboardScrollingAnimator startDisplayLinkIfNeeded]):
(-[WKKeyboardScrollingAnimator stopDisplayLink]):
(-[WKKeyboardScrollingAnimator willStartInteractiveScroll]):
Immediately stop all motion if the user touches the screen to scroll
or zoom with fingers.

(-[WKKeyboardScrollingAnimator displayLinkFired:]):
Update the position of the scrollable based on the applied force and spring.
See the comments in this function for more details.

(-[WKKeyboardScrollViewAnimator init]):
(-[WKKeyboardScrollViewAnimator initWithScrollView:]):
(-[WKKeyboardScrollViewAnimator dealloc]):
(-[WKKeyboardScrollViewAnimator invalidate]):
(-[WKKeyboardScrollViewAnimator setDelegate:]):
(-[WKKeyboardScrollViewAnimator willStartInteractiveScroll]):
(-[WKKeyboardScrollViewAnimator beginWithEvent:]):
(-[WKKeyboardScrollViewAnimator handleKeyEvent:]):
(-[WKKeyboardScrollViewAnimator isKeyboardScrollable]):
(-[WKKeyboardScrollViewAnimator distanceForIncrement:]):
(-[WKKeyboardScrollViewAnimator scrollToContentOffset:animated:]):
(-[WKKeyboardScrollViewAnimator scrollWithScrollToExtentAnimationTo:]):
(-[WKKeyboardScrollViewAnimator contentOffset]):
(-[WKKeyboardScrollViewAnimator boundedContentOffset:]):
(-[WKKeyboardScrollViewAnimator interactiveScrollVelocity]):
(-[WKKeyboardScrollViewAnimator scrollableDirectionsFromOffset:]):
(-[WKKeyboardScrollViewAnimator rubberbandableDirections]):
Add WKKeyboardScrollViewAnimator, which wraps WKKeyboardScrollingAnimator
and provides animated keyboard scrolling for a UIScrollView without much
additional plumbing work. It contains reasonable default behaviors,
with a few optional delegate methods for customization.

(-[WKKeyboardScrollingAnimator _scrollOffsetForEvent:]): Deleted.
(-[WKKeyboardScrollingAnimator startAnimatedScroll]): Deleted.

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (236934 => 236935)


--- trunk/Source/WebKit/ChangeLog	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/ChangeLog	2018-10-08 20:18:26 UTC (rev 236935)
@@ -1,3 +1,145 @@
+2018-10-08  Tim Horton  <[email protected]>
+
+        Adjust keyboard scrolling animator to springy and semiphysical
+        https://bugs.webkit.org/show_bug.cgi?id=190345
+        <rdar://problem/43494393>
+
+        Reviewed by Simon Fraser.
+
+        Introduce WKKeyboardScrollViewAnimator, which wraps WKKeyboardScrollingAnimator
+        and provides a reasonable set of default behaviors for UIScrollView.
+        This is the first step on the way to WKKeyboardScrollingAnimator
+        being platform- and toolkit-agnostic, and helps avoid adding a whole
+        bunch of code to WKContentView.
+
+        Adopt UIKit's scroll-to-top animation curve for whole-document scrolls.
+
+        Adjust WKKeyboardScrollingAnimator's physics; it now simulates a spring
+        attached to the page when decelerating. When a key is pressed, it applies
+        a constant force to the page (up to some maximum velocity). When released,
+        the spring causes the page to decelerate smoothly.
+
+        Add rubber-banding to keyboard scrolling, based on the same spring that
+        decelerates the page.
+
+        Remove the initial bounce on keydown before starting the smooth scroll.
+
+        Remove arrow key commands from WKContentView; this avoids double-processing
+        incoming arrow key events, makes the event stream make more sense, and was
+        entirely unnecessary.
+
+        * Platform/spi/ios/UIKitSPI.h:
+        Add a wide variety of useful SPI.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        Remove _scrollByContentOffset:animated: because it's only used by
+        the keyboard scrolling mechanism.
+
+        Remove _arrowKey because we're removing the arrow key commands.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setupInteraction]):
+        Create a WKKeyboardScrollViewAnimator (which knows how to directly
+        manipulate a UIScrollView) instead of a WKKeyboardScrollingAnimator
+        (which requires its clients to implement everything in terms of an
+        abstract interface.
+
+        (-[WKContentView scrollViewWillStartPanOrPinchGesture]):
+        Inform the keyboard scrolling animator that the user is going to start
+        an interactive gesture that needs control of the scroll view, so it
+        can stop its current animated scroll.
+
+        (-[WKContentView canPerformActionForWebView:withSender:]):
+        (-[WKContentView keyCommands]):
+        (-[WKContentView _arrowKeyForWebView:]): Deleted.
+        Remove arrow key commands.
+
+        (-[WKContentView keyboardScrollViewAnimator:distanceForIncrement:]):
+        Scale from content to WKWebView coordinate space; the keyboard scrolling
+        animator operates in WKWebView coordinates. This was broken before.
+
+        (-[WKContentView keyboardScrollViewAnimatorWillScroll:]):
+        (-[WKContentView isScrollableForKeyboardScrollViewAnimator:]):
+        (-[WKContentView isKeyboardScrollable]): Deleted.
+        (-[WKContentView distanceForScrollingIncrement:]): Deleted.
+        (-[WKContentView scrollByContentOffset:animated:]): Deleted.
+        Reimplement these as keyboardScrollViewAnimator delegate methods.
+
+        * UIProcess/ios/WKKeyboardScrollingAnimator.h:
+        Add WKKeyboardScrollViewAnimator, hide the interface for
+        WKKeyboardScrollingAnimator itself in the implementation file.
+
+        * UIProcess/ios/WKKeyboardScrollingAnimator.mm:
+        (-[WKKeyboardScrollingAnimator initWithScrollable:]):
+        (-[WKKeyboardScrollingAnimator parameters]):
+        (-[WKKeyboardScrollingAnimator invalidate]):
+        (unitVector):
+        Return a unit vector in the given direction.
+
+        (perpendicularAbsoluteUnitVector):
+        Return a positive unit vector perpendicular to the axis of the given direction.
+        Useful for multiplying with another vector to remove the component in the
+        axis of the given direction.
+
+        (boxSide):
+        Convert ScrollingDirection to WebCore::PhysicalBoxSide, for use with RectEdges.
+
+        (-[WKKeyboardScrollingAnimator keyboardScrollForEvent:]):
+        Instead of just returning the desired increment, compute everything we'll need
+        for the lifetime of the scroll (including the force applied, maximum
+        velocity, etc.) and return them together.
+
+        (-[WKKeyboardScrollingAnimator beginWithEvent:]):
+        Use the scrollable's native scroll-to-extent animation (e.g. UIScrollView's
+        scroll-to-top curve) for whole-document scrolls.
+
+        Remove the first discrete scroll; start the smooth scroll immediately.
+
+        (-[WKKeyboardScrollingAnimator handleKeyEvent:]):
+        (farthestPointInDirection):
+        (-[WKKeyboardScrollingAnimator stopAnimatedScroll]):
+        Stop the current scroll. Let the spring coast to its natural stopping
+        point given the system's current energy, unless that stopping point is
+        less than one increment from the starting point. In that case, we attach
+        the spring to /that/ point and let it run.
+
+        (-[WKKeyboardScrollingAnimator startDisplayLinkIfNeeded]):
+        (-[WKKeyboardScrollingAnimator stopDisplayLink]):
+        (-[WKKeyboardScrollingAnimator willStartInteractiveScroll]):
+        Immediately stop all motion if the user touches the screen to scroll
+        or zoom with fingers.
+
+        (-[WKKeyboardScrollingAnimator displayLinkFired:]):
+        Update the position of the scrollable based on the applied force and spring.
+        See the comments in this function for more details.
+
+        (-[WKKeyboardScrollViewAnimator init]):
+        (-[WKKeyboardScrollViewAnimator initWithScrollView:]):
+        (-[WKKeyboardScrollViewAnimator dealloc]):
+        (-[WKKeyboardScrollViewAnimator invalidate]):
+        (-[WKKeyboardScrollViewAnimator setDelegate:]):
+        (-[WKKeyboardScrollViewAnimator willStartInteractiveScroll]):
+        (-[WKKeyboardScrollViewAnimator beginWithEvent:]):
+        (-[WKKeyboardScrollViewAnimator handleKeyEvent:]):
+        (-[WKKeyboardScrollViewAnimator isKeyboardScrollable]):
+        (-[WKKeyboardScrollViewAnimator distanceForIncrement:]):
+        (-[WKKeyboardScrollViewAnimator scrollToContentOffset:animated:]):
+        (-[WKKeyboardScrollViewAnimator scrollWithScrollToExtentAnimationTo:]):
+        (-[WKKeyboardScrollViewAnimator contentOffset]):
+        (-[WKKeyboardScrollViewAnimator boundedContentOffset:]):
+        (-[WKKeyboardScrollViewAnimator interactiveScrollVelocity]):
+        (-[WKKeyboardScrollViewAnimator scrollableDirectionsFromOffset:]):
+        (-[WKKeyboardScrollViewAnimator rubberbandableDirections]):
+        Add WKKeyboardScrollViewAnimator, which wraps WKKeyboardScrollingAnimator
+        and provides animated keyboard scrolling for a UIScrollView without much
+        additional plumbing work. It contains reasonable default behaviors,
+        with a few optional delegate methods for customization.
+
+        (-[WKKeyboardScrollingAnimator _scrollOffsetForEvent:]): Deleted.
+        (-[WKKeyboardScrollingAnimator startAnimatedScroll]): Deleted.
+
 2018-10-08  Jeremy Jones  <[email protected]>
 
         Remove dead code: VideoFullscreenModel::isVisible()

Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (236934 => 236935)


--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2018-10-08 20:18:26 UTC (rev 236935)
@@ -331,6 +331,8 @@
 - (void)_stopScrollingAndZoomingAnimations;
 - (void)_zoomToCenter:(CGPoint)center scale:(CGFloat)scale duration:(CFTimeInterval)duration force:(BOOL)force;
 - (void)_zoomToCenter:(CGPoint)center scale:(CGFloat)scale duration:(CFTimeInterval)duration;
+- (double)_horizontalVelocity;
+- (double)_verticalVelocity;
 @property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
 @property (nonatomic, readonly, getter=_isAnimatingZoom) BOOL isAnimatingZoom;
 @property (nonatomic, readonly, getter=_isAnimatingScroll) BOOL isAnimatingScroll;
@@ -494,6 +496,7 @@
 @property (nonatomic, setter=_setContinuousCornerRadius:) CGFloat _continuousCornerRadius;
 - (void)insertSubview:(UIView *)view above:(UIView *)sibling;
 - (void)viewWillMoveToSuperview:(UIView *)newSuperview;
+- (CGSize)convertSize:(CGSize)size toView:(UIView *)view;
 @end
 
 @interface UIWebSelectionView : UIView
@@ -1026,6 +1029,10 @@
 - (void)_adjustForAutomaticKeyboardInfo:(NSDictionary *)info animated:(BOOL)animated lastAdjustment:(CGFloat*)lastAdjustment;
 - (BOOL)_isScrollingToTop;
 - (CGPoint)_animatedTargetOffset;
+- (BOOL)_canScrollX;
+- (BOOL)_canScrollY;
+- (void)_setContentOffsetWithDecelerationAnimation:(CGPoint)contentOffset;
+- (CGPoint)_adjustedContentOffsetForContentOffset:(CGPoint)contentOffset;
 @end
 
 @interface UIPeripheralHost (IPI)

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (236934 => 236935)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2018-10-08 20:18:26 UTC (rev 236935)
@@ -2154,24 +2154,6 @@
     return true;
 }
 
-- (void)_scrollByContentOffset:(WebCore::FloatPoint)contentOffsetDelta animated:(BOOL)animated
-{
-    WebCore::FloatPoint scaledOffsetDelta = contentOffsetDelta;
-    CGFloat zoomScale = contentZoomScale(self);
-    scaledOffsetDelta.scale(zoomScale);
-
-    CGPoint currentOffset = [_scrollView _isAnimatingScroll] ? [_scrollView _animatedTargetOffset] : [_scrollView contentOffset];
-    CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), currentOffset + scaledOffsetDelta);
-
-    if (CGPointEqualToPoint(boundedOffset, currentOffset))
-        return;
-    [_contentView willStartZoomOrScroll];
-
-    LOG_WITH_STREAM(VisibleRects, stream << "_scrollByContentOffset: scrolling to " << WebCore::FloatPoint(boundedOffset));
-
-    [_scrollView setContentOffset:boundedOffset animated:animated];
-}
-
 - (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
 {
     [self _zoomToPoint:origin atScale:[_scrollView minimumZoomScale] animated:animated];

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h (236934 => 236935)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h	2018-10-08 20:18:26 UTC (rev 236935)
@@ -97,7 +97,6 @@
 
 - (void)_scrollToContentScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::IntPoint)scrollOrigin;
 - (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance;
-- (void)_scrollByContentOffset:(WebCore::FloatPoint)offset animated:(BOOL)animated;
 - (void)_zoomToFocusRect:(WebCore::FloatRect)focusedElementRect selectionRect:(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;
@@ -135,7 +134,6 @@
 - (void)_didChangeEditorState;
 
 - (void)_addShortcut:(id)sender;
-- (void)_arrowKey:(id)sender;
 - (void)_define:(id)sender;
 - (void)_lookup:(id)sender;
 - (void)_share:(id)sender;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (236934 => 236935)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2018-10-08 20:18:26 UTC (rev 236935)
@@ -106,7 +106,6 @@
 
 #define FOR_EACH_WKCONTENTVIEW_ACTION(M) \
     M(_addShortcut) \
-    M(_arrowKey) \
     M(_define) \
     M(_lookup) \
     M(_promptForReplace) \
@@ -246,7 +245,7 @@
     
     std::unique_ptr<WebKit::InputViewUpdateDeferrer> _inputViewUpdateDeferrer;
 
-    RetainPtr<WKKeyboardScrollingAnimator> _keyboardScrollingAnimator;
+    RetainPtr<WKKeyboardScrollViewAnimator> _keyboardScrollingAnimator;
 
     BOOL _isEditable;
     BOOL _showingTextStyleOptions;
@@ -294,11 +293,10 @@
 
 @end
 
-@interface WKContentView (WKInteraction) <UIGestureRecognizerDelegate, UITextAutoscrolling, UITextInputMultiDocument, UITextInputPrivate, UIWebFormAccessoryDelegate, UIWebTouchEventsGestureRecognizerDelegate, UIWKInteractionViewProtocol, WKActionSheetAssistantDelegate, WKFileUploadPanelDelegate
+@interface WKContentView (WKInteraction) <UIGestureRecognizerDelegate, UITextAutoscrolling, UITextInputMultiDocument, UITextInputPrivate, UIWebFormAccessoryDelegate, UIWebTouchEventsGestureRecognizerDelegate, UIWKInteractionViewProtocol, WKActionSheetAssistantDelegate, WKFileUploadPanelDelegate, WKKeyboardScrollViewAnimatorDelegate
 #if !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
     , WKShareSheetDelegate
 #endif
-    , WKKeyboardScrollable
 #if ENABLE(DATA_INTERACTION)
     , UIDragInteractionDelegate, UIDropInteractionDelegate
 #endif

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (236934 => 236935)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2018-10-08 20:18:26 UTC (rev 236935)
@@ -122,7 +122,7 @@
 #import <WebKitAdditions/WKPlatformFileUploadPanel.mm>
 #endif
 
-@interface UIEvent(UIEventInternal)
+@interface UIEvent (UIEventInternal)
 @property (nonatomic, assign) UIKeyboardInputFlags _inputFlags;
 @end
 
@@ -654,7 +654,8 @@
         [self.superview addSubview:_interactionViewsContainerView.get()];
     }
 
-    _keyboardScrollingAnimator = adoptNS([[WKKeyboardScrollingAnimator alloc] initWithScrollable:self]);
+    _keyboardScrollingAnimator = adoptNS([[WKKeyboardScrollViewAnimator alloc] initWithScrollView:_webView.scrollView]);
+    [_keyboardScrollingAnimator setDelegate:self];
 
     [self.layer addObserver:self forKeyPath:@"transform" options:NSKeyValueObservingOptionInitial context:nil];
 
@@ -2084,6 +2085,8 @@
 {
     _page->hideValidationMessage();
 
+    [_keyboardScrollingAnimator willStartInteractiveScroll];
+
     _canSendTouchEventsAsynchronously = YES;
 }
 
@@ -2381,10 +2384,7 @@
 }
 
 - (BOOL)canPerformActionForWebView:(SEL)action withSender:(id)sender
-{
-    if (action == @selector(_arrowKey:))
-        return [self isFirstResponder];
-        
+{        
     auto editorState = _page->editorState();
     if (action == @selector(_showTextStyleOptions:))
         return editorState.isContentRichlyEditable && editorState.selectionIsRange && !_showingTextStyleOptions;
@@ -3167,46 +3167,16 @@
 
 - (NSArray *)keyCommands
 {
-    static NSArray* nonEditableKeyCommands = [@[
-       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:0 action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:0 action:@selector(_arrowKey:)],
-       
-       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
-       
-       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
-       
-       [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
-       
-       [UIKeyCommand keyCommandWithInput:@" " modifierFlags:0 action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:@" " modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
-       
-       [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
-       [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
-    ] retain];
+    if (!_page->editorState().isContentEditable)
+        return nil;
 
     static NSArray* editableKeyCommands = [@[
        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(_nextAccessoryTab:)],
        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]
     ] retain];
-    
-    return (_page->editorState().isContentEditable) ? editableKeyCommands : nonEditableKeyCommands;
+    return editableKeyCommands;
 }
 
-- (void)_arrowKeyForWebView:(id)sender
-{
-    UIKeyCommand* command = sender;
-    [self handleKeyEvent:command._triggeringEvent];
-}
-
 - (void)_nextAccessoryTab:(id)sender
 {
     [self accessoryTab:YES];
@@ -3950,7 +3920,7 @@
     return NO;
 }
 
-- (BOOL)isKeyboardScrollable
+- (BOOL)isScrollableForKeyboardScrollViewAnimator:(WKKeyboardScrollViewAnimator *)animator
 {
     if (_page->editorState().isContentEditable)
         return NO;
@@ -3961,23 +3931,23 @@
     return YES;
 }
 
-- (CGFloat)distanceForScrollingIncrement:(ScrollingIncrement)increment
+- (CGFloat)keyboardScrollViewAnimator:(WKKeyboardScrollViewAnimator *)animator distanceForIncrement:(ScrollingIncrement)increment
 {
     switch (increment) {
     case ScrollingIncrement::Document:
-        return self.bounds.size.height;
+        return [self convertRect:self.bounds toView:_webView].size.height;
     case ScrollingIncrement::Page:
-        return WebCore::Scrollbar::pageStep(_page->unobscuredContentRect().height(), self.bounds.size.height);
+        return [self convertSize:CGSizeMake(0, WebCore::Scrollbar::pageStep(_page->unobscuredContentRect().height(), self.bounds.size.height)) toView:_webView].height;
     case ScrollingIncrement::Line:
-        return WebCore::Scrollbar::pixelsPerLineStep();
+        return [self convertSize:CGSizeMake(0, WebCore::Scrollbar::pixelsPerLineStep()) toView:_webView].height;
     }
     ASSERT_NOT_REACHED();
     return 0;
 }
 
-- (void)scrollByContentOffset:(WebCore::FloatPoint)offset animated:(BOOL)animated
+- (void)keyboardScrollViewAnimatorWillScroll:(WKKeyboardScrollViewAnimator *)animator
 {
-    [_webView _scrollByContentOffset:offset animated:animated];
+    [self willStartZoomOrScroll];
 }
 
 - (void)executeEditCommandWithCallback:(NSString *)commandName

Modified: trunk/Source/WebKit/UIProcess/ios/WKKeyboardScrollingAnimator.h (236934 => 236935)


--- trunk/Source/WebKit/UIProcess/ios/WKKeyboardScrollingAnimator.h	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/UIProcess/ios/WKKeyboardScrollingAnimator.h	2018-10-08 20:18:26 UTC (rev 236935)
@@ -39,26 +39,32 @@
 
 }
 
+@class UIScrollView;
 @class WebEvent;
-@protocol WKKeyboardScrollable;
+@protocol WKKeyboardScrollViewAnimatorDelegate;
 
-@interface WKKeyboardScrollingAnimator : NSObject
+@interface WKKeyboardScrollViewAnimator : NSObject
 
 - (instancetype)init NS_UNAVAILABLE;
-- (instancetype)initWithScrollable:(id <WKKeyboardScrollable>)scrollable;
+- (instancetype)initWithScrollView:(UIScrollView *)scrollView;
 
 - (void)invalidate;
 
+- (void)willStartInteractiveScroll;
+
 - (BOOL)beginWithEvent:(::WebEvent *)event;
 - (BOOL)handleKeyEvent:(::WebEvent *)event;
 
+@property (nonatomic, weak) id <WKKeyboardScrollViewAnimatorDelegate> delegate;
+
 @end
 
-@protocol WKKeyboardScrollable <NSObject>
-@required
-- (BOOL)isKeyboardScrollable;
-- (CGFloat)distanceForScrollingIncrement:(WebKit::ScrollingIncrement)increment;
-- (void)scrollByContentOffset:(WebCore::FloatPoint)offset animated:(BOOL)animated;
+@protocol WKKeyboardScrollViewAnimatorDelegate <NSObject>
+@optional
+- (BOOL)isScrollableForKeyboardScrollViewAnimator:(WKKeyboardScrollViewAnimator *)animator;
+- (CGFloat)keyboardScrollViewAnimator:(WKKeyboardScrollViewAnimator *)animator distanceForIncrement:(WebKit::ScrollingIncrement)increment;
+- (void)keyboardScrollViewAnimatorWillScroll:(WKKeyboardScrollViewAnimator *)animator;
+
 @end
 
 #endif // PLATFORM(IOS)

Modified: trunk/Source/WebKit/UIProcess/ios/WKKeyboardScrollingAnimator.mm (236934 => 236935)


--- trunk/Source/WebKit/UIProcess/ios/WKKeyboardScrollingAnimator.mm	2018-10-08 20:15:22 UTC (rev 236934)
+++ trunk/Source/WebKit/UIProcess/ios/WKKeyboardScrollingAnimator.mm	2018-10-08 20:18:26 UTC (rev 236935)
@@ -30,27 +30,80 @@
 
 #import "UIKitSPI.h"
 #import <WebCore/FloatPoint.h>
+#import <WebCore/RectEdges.h>
 #import <WebCore/WebEvent.h>
+#import <WebKit/UIKitSPI.h>
 #import <algorithm>
-#import <pal/spi/cocoa/QuartzCoreSPI.h>
 #import <wtf/RetainPtr.h>
+#import <wtf/WeakObjCPtr.h>
 
 namespace WebKit {
 
-enum class KeyboardScrollingAnimatorState : uint8_t {
-    WaitingForFirstEvent,
-    WaitingForRepeat,
-    Animating
+enum class ScrollingDirection : uint8_t { Up, Down, Left, Right };
+
+struct KeyboardScroll {
+    WebCore::FloatSize offset; // Points per increment.
+    WebCore::FloatSize maximumVelocity; // Points per second.
+    WebCore::FloatSize force;
+
+    WebKit::ScrollingIncrement increment;
+    WebKit::ScrollingDirection direction;
 };
 
+struct KeyboardScrollParameters {
+    CGFloat springMass { 1 };
+    CGFloat springStiffness { 109 };
+    CGFloat springDamping { 20 };
+
+    CGFloat maximumVelocityMultiplier { 25 };
+    CGFloat timeToMaximumVelocity { 1 };
+
+    CGFloat rubberBandForce { 5000 };
 };
 
+}
+
+@protocol WKKeyboardScrollableInternal <NSObject>
+@required
+- (BOOL)isKeyboardScrollable;
+- (CGFloat)distanceForIncrement:(WebKit::ScrollingIncrement)increment;
+- (void)scrollToContentOffset:(WebCore::FloatPoint)offset animated:(BOOL)animated;
+- (void)scrollWithScrollToExtentAnimationTo:(CGPoint)offset;
+- (CGPoint)contentOffset;
+- (CGSize)interactiveScrollVelocity;
+- (CGPoint)boundedContentOffset:(CGPoint)offset;
+- (WebCore::RectEdges<bool>)scrollableDirectionsFromOffset:(CGPoint)offset;
+- (WebCore::RectEdges<bool>)rubberbandableDirections;
+
+@end
+
+@interface WKKeyboardScrollingAnimator : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithScrollable:(id <WKKeyboardScrollableInternal>)scrollable;
+
+- (void)invalidate;
+
+- (void)willStartInteractiveScroll;
+
+- (BOOL)beginWithEvent:(::WebEvent *)event;
+- (BOOL)handleKeyEvent:(::WebEvent *)event;
+
+@end
+
 @implementation WKKeyboardScrollingAnimator {
-    id <WKKeyboardScrollable> _scrollable;
-    CFTimeInterval _startTime;
-    WebCore::FloatPoint _scrollOffsetPerIncrement;
+    id <WKKeyboardScrollableInternal> _scrollable;
     RetainPtr<CADisplayLink> _displayLink;
-    WebKit::KeyboardScrollingAnimatorState _state;
+
+    std::optional<WebKit::KeyboardScroll> _currentScroll;
+
+    BOOL _hasPressedScrollingKey;
+
+    WebCore::FloatSize _velocity; // Points per second.
+
+    WebCore::FloatPoint _idealPosition;
+    WebCore::FloatPoint _currentPosition;
+    WebCore::FloatPoint _idealPositionForMinimumTravel;
 }
 
 - (instancetype)init
@@ -58,7 +111,7 @@
     return nil;
 }
 
-- (instancetype)initWithScrollable:(id <WKKeyboardScrollable>)scrollable
+- (instancetype)initWithScrollable:(id <WKKeyboardScrollableInternal>)scrollable
 {
     self = [super init];
     if (!self)
@@ -69,14 +122,61 @@
     return self;
 }
 
+- (const WebKit::KeyboardScrollParameters &)parameters
+{
+    static const WebKit::KeyboardScrollParameters parameters;
+    return parameters;
+}
+
 - (void)invalidate
 {
     [self stopAnimatedScroll];
+    [self stopDisplayLink];
     _scrollable = nil;
 }
 
-- (std::optional<WebCore::FloatPoint>)_scrollOffsetForEvent:(::WebEvent *)event
+static WebCore::FloatSize unitVector(WebKit::ScrollingDirection direction)
 {
+    switch (direction) {
+    case WebKit::ScrollingDirection::Up:
+        return { 0, -1 };
+    case WebKit::ScrollingDirection::Down:
+        return { 0, 1 };
+    case WebKit::ScrollingDirection::Left:
+        return { -1, 0 };
+    case WebKit::ScrollingDirection::Right:
+        return { 1, 0 };
+    }
+}
+
+static WebCore::FloatSize perpendicularAbsoluteUnitVector(WebKit::ScrollingDirection direction)
+{
+    switch (direction) {
+    case WebKit::ScrollingDirection::Up:
+    case WebKit::ScrollingDirection::Down:
+        return { 1, 0 };
+    case WebKit::ScrollingDirection::Left:
+    case WebKit::ScrollingDirection::Right:
+        return { 0, 1 };
+    }
+}
+
+static WebCore::PhysicalBoxSide boxSide(WebKit::ScrollingDirection direction)
+{
+    switch (direction) {
+    case WebKit::ScrollingDirection::Up:
+        return WebCore::PhysicalBoxSide::Top;
+    case WebKit::ScrollingDirection::Down:
+        return WebCore::PhysicalBoxSide::Bottom;
+    case WebKit::ScrollingDirection::Left:
+        return WebCore::PhysicalBoxSide::Left;
+    case WebKit::ScrollingDirection::Right:
+        return WebCore::PhysicalBoxSide::Right;
+    }
+}
+
+- (std::optional<WebKit::KeyboardScroll>)keyboardScrollForEvent:(::WebEvent *)event
+{
     static const unsigned kWebSpaceKey = 0x20;
 
     if (![_scrollable isKeyboardScrollable])
@@ -86,7 +186,6 @@
     if (!charactersIgnoringModifiers.length)
         return std::nullopt;
 
-    enum class Direction : uint8_t { Up, Down, Left, Right };
     enum class Key : uint8_t { Other, LeftArrow, RightArrow, UpArrow, DownArrow, PageUp, PageDown, Space };
     
     auto key = ^{
@@ -113,7 +212,7 @@
     BOOL shiftPressed = event.modifierFlags & WebEventFlagMaskShift;
     BOOL altPressed = event.modifierFlags & WebEventFlagMaskAlternate;
     BOOL cmdPressed = event.modifierFlags & WebEventFlagMaskCommand;
-    
+
     auto increment = ^{
         switch (key) {
         case Key::LeftArrow:
@@ -139,106 +238,389 @@
     auto direction = ^() {
         switch (key) {
         case Key::LeftArrow:
-            return Direction::Left;
+            return WebKit::ScrollingDirection::Left;
         case Key::RightArrow:
-            return Direction::Right;
+            return WebKit::ScrollingDirection::Right;
         case Key::UpArrow:
         case Key::PageUp:
-            return Direction::Up;
+            return WebKit::ScrollingDirection::Up;
         case Key::DownArrow:
         case Key::PageDown:
-            return Direction::Down;
+            return WebKit::ScrollingDirection::Down;
         case Key::Space:
-            return shiftPressed ? Direction::Up : Direction::Down;
+            return shiftPressed ? WebKit::ScrollingDirection::Up : WebKit::ScrollingDirection::Down;
         case Key::Other:
             ASSERT_NOT_REACHED();
-            return Direction::Down;
+            return WebKit::ScrollingDirection::Down;
         };
     }();
 
-    bool isHorizontal = direction == Direction::Left || direction == Direction::Right;
-    CGFloat scrollDistance = [_scrollable distanceForScrollingIncrement:increment];
+    CGFloat scrollDistance = [_scrollable distanceForIncrement:increment];
+
+    WebKit::KeyboardScroll scroll;
+    scroll.offset = unitVector(direction).scaled(scrollDistance);
+    scroll.increment = increment;
+    scroll.direction = direction;
+    scroll.maximumVelocity = scroll.offset.scaled(self.parameters.maximumVelocityMultiplier);
+
+    // Apply a constant force to achieve Vmax in timeToMaximumVelocity seconds.
+    // F_constant = m * Vmax / t
+    scroll.force = scroll.maximumVelocity.scaled(self.parameters.springMass / self.parameters.timeToMaximumVelocity);
     
-    if (direction == Direction::Up || direction == Direction::Left)
-        scrollDistance = -scrollDistance;
-    
-    return isHorizontal ? WebCore::FloatPoint(scrollDistance, 0) : WebCore::FloatPoint(0, scrollDistance);
+    return scroll;
 }
 
 - (BOOL)beginWithEvent:(::WebEvent *)event
 {
-    if (_state != WebKit::KeyboardScrollingAnimatorState::WaitingForFirstEvent)
-        return NO;
-    
     if (event.type != WebEventKeyDown)
         return NO;
 
-    auto offset = [self _scrollOffsetForEvent:event];
-    if (!offset)
+    auto scroll = [self keyboardScrollForEvent:event];
+    if (!scroll)
         return NO;
 
-    _state = WebKit::KeyboardScrollingAnimatorState::WaitingForRepeat;
-    _scrollOffsetPerIncrement = offset.value();
+    if (_hasPressedScrollingKey)
+        return NO;
 
-    // The first keyboard event that starts scrolling performs its own
-    // discrete animated scroll. Continuously animated scrolling starts
-    // when the key repeats.
-    [_scrollable scrollByContentOffset:_scrollOffsetPerIncrement animated:YES];
+    if (![_scrollable rubberbandableDirections].at(boxSide(scroll->direction)))
+        return NO;
 
+    _hasPressedScrollingKey = YES;
+    _currentScroll = scroll;
+
+    if (scroll->increment == WebKit::ScrollingIncrement::Document) {
+        _velocity = { };
+        [self stopAnimatedScroll];
+        [self stopDisplayLink];
+        [_scrollable scrollWithScrollToExtentAnimationTo:[_scrollable boundedContentOffset:_currentPosition + scroll->offset]];
+        return YES;
+    }
+
+    [self startDisplayLinkIfNeeded];
+
+    _currentPosition = WebCore::FloatPoint([_scrollable contentOffset]);
+    _velocity += WebCore::FloatSize([_scrollable interactiveScrollVelocity]);
+    _idealPositionForMinimumTravel = _currentPosition + _currentScroll->offset;
+
     return YES;
 }
 
 - (BOOL)handleKeyEvent:(::WebEvent *)event
 {
-    if (_state == WebKit::KeyboardScrollingAnimatorState::WaitingForFirstEvent)
+    if (!_hasPressedScrollingKey)
         return NO;
 
-    auto offset = [self _scrollOffsetForEvent:event];
-    if (!offset || event.type == WebEventKeyUp) {
+    auto scroll = [self keyboardScrollForEvent:event];
+    if (!scroll || event.type == WebEventKeyUp) {
         [self stopAnimatedScroll];
-        return NO;
+        _hasPressedScrollingKey = NO;
     }
 
-    if (_state == WebKit::KeyboardScrollingAnimatorState::WaitingForRepeat)
-        [self startAnimatedScroll];
+    return NO;
+}
 
-    return YES;
+static WebCore::FloatPoint farthestPointInDirection(WebCore::FloatPoint a, WebCore::FloatPoint b, WebKit::ScrollingDirection direction)
+{
+    switch (direction) {
+    case WebKit::ScrollingDirection::Up:
+        return WebCore::FloatPoint(a.x(), std::min(a.y(), b.y()));
+    case WebKit::ScrollingDirection::Down:
+        return WebCore::FloatPoint(a.x(), std::max(a.y(), b.y()));
+    case WebKit::ScrollingDirection::Left:
+        return WebCore::FloatPoint(std::min(a.x(), b.x()), a.y());
+    case WebKit::ScrollingDirection::Right:
+        return WebCore::FloatPoint(std::max(a.x(), b.x()), a.y());
+    }
+
+    ASSERT_NOT_REACHED();
+    return { };
 }
 
-- (void)startAnimatedScroll
+- (void)stopAnimatedScroll
 {
-    ASSERT(!_displayLink);
+    if (!_currentScroll)
+        return;
 
-    _state = WebKit::KeyboardScrollingAnimatorState::Animating;
-    _startTime = CACurrentMediaTime();
+    // Determine the settling position of the spring, conserving the system's current energy.
+    // Kinetic = elastic potential
+    // 1/2 * m * v^2 = 1/2 * k * x^2
+    // x = sqrt(v^2 * m / k)
+    auto displacementMagnitudeSquared = (_velocity * _velocity).scaled(self.parameters.springMass / self.parameters.springStiffness);
+    WebCore::FloatSize displacement = {
+        std::copysign(sqrt(displacementMagnitudeSquared.width()), _velocity.width()),
+        std::copysign(sqrt(displacementMagnitudeSquared.height()), _velocity.height())
+    };
+
+    // If the spring would settle before the minimum travel distance
+    // for an instantaneous tap, move the settling position of the spring
+    // out to that point.
+    _idealPosition = [_scrollable boundedContentOffset:farthestPointInDirection(_currentPosition + displacement, _idealPositionForMinimumTravel, _currentScroll->direction)];
+
+    _currentScroll = std::nullopt;
+}
+
+- (void)startDisplayLinkIfNeeded
+{
+    if (_displayLink)
+        return;
+
     _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkFired:)];
     [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
 }
 
-- (void)stopAnimatedScroll
+- (void)stopDisplayLink
 {
-    _state = WebKit::KeyboardScrollingAnimatorState::WaitingForFirstEvent;
     [_displayLink invalidate];
     _displayLink = nil;
 }
 
+- (void)willStartInteractiveScroll
+{
+    // If the user touches the screen to start an interactive scroll, stop everything.
+    _velocity = { };
+    [self stopAnimatedScroll];
+    [self stopDisplayLink];
+}
+
 - (void)displayLinkFired:(CADisplayLink *)sender
 {
-    const CFTimeInterval secondsPerScrollIncrement = 0.05; // Seconds it should take to cover one increment when at full speed.
-    const float maximumVelocity = 2000; // Maximum velocity in pixels per second. Empirically determined.
-    const CFTimeInterval accelerationDuration = 0.3; // Duration of acceleration in seconds. This matches UIScrollView.
+    WebCore::FloatSize force;
+    WebCore::FloatSize axesToApplySpring = { 1, 1 };
 
-    auto velocity = _scrollOffsetPerIncrement.scaled(1. / secondsPerScrollIncrement);
-    velocity = velocity.constrainedBetween({ -maximumVelocity, -maximumVelocity }, { maximumVelocity, maximumVelocity });
+    if (_currentScroll) {
+        auto scrollableDirections = [_scrollable scrollableDirectionsFromOffset:_currentPosition];
+        auto direction = _currentScroll->direction;
 
+        if (scrollableDirections.at(boxSide(direction))) {
+            // Apply the scrolling force. Only apply the spring in the perpendicular axis,
+            // otherwise it drags against the direction of motion.
+            axesToApplySpring = perpendicularAbsoluteUnitVector(direction);
+            force = _currentScroll->force;
+        } else {
+            // The scroll view cannot scroll in this direction, and is rubber-banding.
+            // Apply a constant and significant force; otherwise, the force for a
+            // single-line increment is not strong enough to rubber-band perceptibly.
+            force = unitVector(direction).scaled(self.parameters.rubberBandForce);
+        }
+
+        // If we've reached or exceeded the maximum velocity, stop applying any force.
+        // However, we won't let the spring snap, we'll just keep going at the same
+        // velocity until the user raises their finger or we hit an edge.
+        if (fabs(_velocity.width()) >= fabs(_currentScroll->maximumVelocity.width()))
+            force.setWidth(0);
+        if (fabs(_velocity.height()) >= fabs(_currentScroll->maximumVelocity.height()))
+            force.setHeight(0);
+    }
+
+    WebCore::FloatPoint idealPosition = [_scrollable boundedContentOffset:_currentScroll ? _currentPosition : _idealPosition];
+    WebCore::FloatSize displacement = _currentPosition - idealPosition;
+
+    // Compute the spring's force, and apply it in allowed directions.
+    // F_spring = -k * x - c * v
+    auto springForce = - displacement.scaled(self.parameters.springStiffness) - _velocity.scaled(self.parameters.springDamping);
+    force += springForce * axesToApplySpring;
+
+    // Integrate acceleration -> velocity -> position for this time step.
     CFTimeInterval frameDuration = sender.targetTimestamp - sender.timestamp;
-    CFTimeInterval timeFromAnimationStart = sender.timestamp - _startTime;
-    float accelerationFactor = [[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] _solveForInput:std::min<CFTimeInterval>(timeFromAnimationStart, accelerationDuration) / accelerationDuration];
+    WebCore::FloatSize acceleration = force.scaled(1. / self.parameters.springMass);
+    _velocity += acceleration.scaled(frameDuration);
+    _currentPosition += _velocity.scaled(frameDuration);
 
-    auto contentOffset = velocity.scaled(accelerationFactor * frameDuration);
-    [_scrollable scrollByContentOffset:contentOffset animated:NO];
+    [_scrollable scrollToContentOffset:_currentPosition animated:NO];
+
+    // If we've effectively stopped scrolling, and no key is pressed,
+    // shut down the display link.
+    if (!_hasPressedScrollingKey && _velocity.diagonalLengthSquared() < 1) {
+        [self stopDisplayLink];
+        _velocity = { };
+    }
 }
 
 @end
 
+@interface WKKeyboardScrollViewAnimator () <WKKeyboardScrollableInternal>
+@end
+
+@implementation WKKeyboardScrollViewAnimator {
+    WeakObjCPtr<UIScrollView> _scrollView;
+    RetainPtr<WKKeyboardScrollingAnimator> _animator;
+
+    BOOL _delegateRespondsToIsKeyboardScrollable;
+    BOOL _delegateRespondsToDistanceForIncrement;
+    BOOL _delegateRespondsToWillScroll;
+}
+
+- (instancetype)init
+{
+    return nil;
+}
+
+- (instancetype)initWithScrollView:(UIScrollView *)scrollView
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    _scrollView = scrollView;
+    _animator = adoptNS([[WKKeyboardScrollingAnimator alloc] initWithScrollable:self]);
+
+    return self;
+}
+
+- (void)dealloc
+{
+    [_animator invalidate];
+    [super dealloc];
+}
+
+- (void)invalidate
+{
+    _scrollView = nil;
+
+    [_animator invalidate];
+    _animator = nil;
+}
+
+- (void)setDelegate:(id <WKKeyboardScrollViewAnimatorDelegate>)delegate
+{
+    _delegate = delegate;
+
+    _delegateRespondsToIsKeyboardScrollable = [_delegate respondsToSelector:@selector(isScrollableForKeyboardScrollViewAnimator:)];
+    _delegateRespondsToDistanceForIncrement = [_delegate respondsToSelector:@selector(keyboardScrollViewAnimator:distanceForIncrement:)];
+    _delegateRespondsToWillScroll = [_delegate respondsToSelector:@selector(keyboardScrollViewAnimatorWillScroll:)];
+}
+
+- (void)willStartInteractiveScroll
+{
+    [_animator willStartInteractiveScroll];
+}
+
+- (BOOL)beginWithEvent:(::WebEvent *)event
+{
+    return [_animator beginWithEvent:event];
+}
+
+- (BOOL)handleKeyEvent:(::WebEvent *)event
+{
+    return [_animator handleKeyEvent:event];
+}
+
+- (BOOL)isKeyboardScrollable
+{
+    if (!_delegateRespondsToIsKeyboardScrollable)
+        return YES;
+    return [_delegate isScrollableForKeyboardScrollViewAnimator:self];
+}
+
+- (CGFloat)distanceForIncrement:(WebKit::ScrollingIncrement)increment
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    if (!scrollView)
+        return 0;
+
+    const CGFloat defaultPageScrollFraction = 0.8;
+    const CGFloat defaultLineScrollHeight = 40;
+
+    if (!_delegateRespondsToDistanceForIncrement) {
+        switch (increment) {
+        case WebKit::ScrollingIncrement::Document:
+            return scrollView.contentSize.height;
+        case WebKit::ScrollingIncrement::Page:
+            return scrollView.frame.size.height * defaultPageScrollFraction;
+        case WebKit::ScrollingIncrement::Line:
+            return defaultLineScrollHeight * scrollView.zoomScale;
+        }
+        ASSERT_NOT_REACHED();
+        return 0;
+    }
+
+    return [_delegate keyboardScrollViewAnimator:self distanceForIncrement:increment];
+}
+
+- (void)scrollToContentOffset:(WebCore::FloatPoint)contentOffsetDelta animated:(BOOL)animated
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    if (!scrollView)
+        return;
+    if (_delegateRespondsToWillScroll)
+        [_delegate keyboardScrollViewAnimatorWillScroll:self];
+    [scrollView setContentOffset:contentOffsetDelta animated:animated];
+}
+
+- (void)scrollWithScrollToExtentAnimationTo:(CGPoint)offset
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    [scrollView _setContentOffsetWithDecelerationAnimation:offset];
+}
+
+- (CGPoint)contentOffset
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    if (!scrollView)
+        return CGPointZero;
+
+    return [scrollView contentOffset];
+}
+
+- (CGPoint)boundedContentOffset:(CGPoint)offset
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    if (!scrollView)
+        return CGPointZero;
+
+    return [scrollView _adjustedContentOffsetForContentOffset:offset];
+}
+
+- (CGSize)interactiveScrollVelocity
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    if (!scrollView)
+        return CGSizeZero;
+
+    const NSTimeInterval millisecondsPerSecond = 1000;
+    return CGSizeMake(scrollView._horizontalVelocity * millisecondsPerSecond, scrollView._verticalVelocity * millisecondsPerSecond);
+}
+
+- (WebCore::RectEdges<bool>)scrollableDirectionsFromOffset:(CGPoint)offset
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    if (!scrollView)
+        return { };
+
+    UIEdgeInsets contentInsets = scrollView.adjustedContentInset;
+
+    CGSize contentSize = scrollView.contentSize;
+    CGSize scrollViewSize = scrollView.bounds.size;
+
+    CGPoint minimumContentOffset = CGPointMake(-contentInsets.left, -contentInsets.top);
+    CGPoint maximumContentOffset = CGPointMake(std::max(minimumContentOffset.x, contentSize.width + contentInsets.right - scrollViewSize.width), std::max(minimumContentOffset.y, contentSize.height + contentInsets.bottom - scrollViewSize.height));
+
+    WebCore::RectEdges<bool> edges;
+
+    edges.setTop(offset.y > minimumContentOffset.y);
+    edges.setBottom(offset.y < maximumContentOffset.y);
+    edges.setLeft(offset.x > minimumContentOffset.x);
+    edges.setRight(offset.x < maximumContentOffset.x);
+
+    return edges;
+}
+
+- (WebCore::RectEdges<bool>)rubberbandableDirections
+{
+    auto scrollView = _scrollView.getAutoreleased();
+    if (!scrollView)
+        return { };
+
+    WebCore::RectEdges<bool> edges;
+
+    edges.setTop(scrollView._canScrollY);
+    edges.setBottom(edges.top());
+    edges.setLeft(scrollView._canScrollX);
+    edges.setRight(edges.left());
+
+    return edges;
+}
+
+@end
+
 #endif // PLATFORM(IOS)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to