summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/mac/ScrollAnimatorMac.mm
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/mac/ScrollAnimatorMac.mm')
-rw-r--r--Source/WebCore/platform/mac/ScrollAnimatorMac.mm923
1 files changed, 907 insertions, 16 deletions
diff --git a/Source/WebCore/platform/mac/ScrollAnimatorMac.mm b/Source/WebCore/platform/mac/ScrollAnimatorMac.mm
index 59b333b..c1154d5 100644
--- a/Source/WebCore/platform/mac/ScrollAnimatorMac.mm
+++ b/Source/WebCore/platform/mac/ScrollAnimatorMac.mm
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,10 +30,20 @@
#include "ScrollAnimatorMac.h"
#include "FloatPoint.h"
+#include "IntRect.h"
+#include "PlatformGestureEvent.h"
+#include "PlatformWheelEvent.h"
+#include "ScrollView.h"
#include "ScrollableArea.h"
+#include "ScrollbarTheme.h"
+#include "ScrollbarThemeMac.h"
#include <wtf/PassOwnPtr.h>
+#include <wtf/UnusedParam.h>
-@interface NSObject (NSScrollAnimationHelperDetails)
+using namespace WebCore;
+using namespace std;
+
+@interface NSObject (ScrollAnimationHelperDetails)
- (id)initWithDelegate:(id)delegate;
- (void)_stopRun;
- (BOOL)_isAnimating;
@@ -44,18 +54,7 @@
{
WebCore::ScrollAnimatorMac* _animator;
}
-
- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
-
-- (NSRect)bounds;
-- (void)_immediateScrollToPoint:(NSPoint)newPosition;
-- (NSSize)convertSizeToBase:(NSSize)size;
-- (NSSize)convertSizeFromBase:(NSSize)size;
-
-- (id)superview; // Return nil.
-- (id)documentView; // Return nil.
-- (id)window; // Return nil.
-- (void)_recursiveRecomputeToolTips; // No-op.
@end
static NSSize abs(NSSize size)
@@ -80,14 +79,24 @@ static NSSize abs(NSSize size)
return self;
}
+- (void)scrollAnimatorDestroyed
+{
+ _animator = 0;
+}
+
- (NSRect)bounds
{
+ if (!_animator)
+ return NSZeroRect;
+
WebCore::FloatPoint currentPosition = _animator->currentPosition();
return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
}
- (void)_immediateScrollToPoint:(NSPoint)newPosition
{
+ if (!_animator)
+ return;
_animator->immediateScrollToPoint(newPosition);
}
@@ -101,6 +110,16 @@ static NSSize abs(NSSize size)
return abs(size);
}
+- (NSSize)convertSizeToBacking:(NSSize)size
+{
+ return abs(size);
+}
+
+- (NSSize)convertSizeFromBacking:(NSSize)size
+{
+ return abs(size);
+}
+
- (id)superview
{
return nil;
@@ -122,6 +141,307 @@ static NSSize abs(NSSize size)
@end
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+
+@interface ScrollbarPainterControllerDelegate : NSObject
+{
+ WebCore::ScrollAnimatorMac* _animator;
+}
+- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
+@end
+
+@implementation ScrollbarPainterControllerDelegate
+
+- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _animator = scrollAnimator;
+ return self;
+}
+
+- (void)scrollAnimatorDestroyed
+{
+ _animator = 0;
+}
+
+- (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
+{
+ UNUSED_PARAM(scrollerImpPair);
+ if (!_animator)
+ return NSZeroRect;
+
+ WebCore::IntSize contentsSize = _animator->scrollableArea()->contentsSize();
+ return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
+}
+
+- (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
+{
+ UNUSED_PARAM(scrollerImpPair);
+ if (!_animator)
+ return NO;
+
+ return _animator->scrollableArea()->inLiveResize();
+}
+
+- (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
+{
+ UNUSED_PARAM(scrollerImpPair);
+ if (!_animator)
+ return NSZeroPoint;
+
+ return _animator->scrollableArea()->currentMousePosition();
+}
+
+- (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
+{
+ UNUSED_PARAM(scrollerImpPair);
+ if (!_animator)
+ return NSZeroPoint;
+
+ WebCore::Scrollbar* scrollbar = 0;
+ if (wkScrollbarPainterIsHorizontal((WKScrollbarPainterRef)scrollerImp))
+ scrollbar = _animator->scrollableArea()->horizontalScrollbar();
+ else
+ scrollbar = _animator->scrollableArea()->verticalScrollbar();
+
+ // It is possible to have a null scrollbar here since it is possible for this delegate
+ // method to be called between the moment when a scrollbar has been set to 0 and the
+ // moment when its destructor has been called. We should probably de-couple some
+ // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this
+ // issue.
+ if (!scrollbar)
+ return WebCore::IntPoint();
+
+ return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea));
+}
+
+- (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
+{
+ UNUSED_PARAM(scrollerImpPair);
+ UNUSED_PARAM(rect);
+}
+
+- (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
+{
+ if (!_animator)
+ return;
+
+ WKScrollbarPainterControllerRef painterController = (WKScrollbarPainterControllerRef)scrollerImpPair;
+ WebCore::ScrollbarThemeMac* macTheme = (WebCore::ScrollbarThemeMac*)WebCore::ScrollbarTheme::nativeTheme();
+
+ WKScrollbarPainterRef oldVerticalPainter = wkVerticalScrollbarPainterForController(painterController);
+ if (oldVerticalPainter) {
+ WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar();
+ WKScrollbarPainterRef newVerticalPainter = wkMakeScrollbarReplacementPainter(oldVerticalPainter,
+ newRecommendedScrollerStyle,
+ verticalScrollbar->controlSize(),
+ false);
+ macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
+ }
+
+ WKScrollbarPainterRef oldHorizontalPainter = wkHorizontalScrollbarPainterForController(painterController);
+ if (oldHorizontalPainter) {
+ WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar();
+ WKScrollbarPainterRef newHorizontalPainter = wkMakeScrollbarReplacementPainter(oldHorizontalPainter,
+ newRecommendedScrollerStyle,
+ horizontalScrollbar->controlSize(),
+ true);
+ macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
+ }
+
+ wkSetScrollbarPainterControllerStyle(painterController, newRecommendedScrollerStyle);
+}
+
+@end
+
+@interface ScrollbarPartAnimation : NSAnimation
+{
+ RetainPtr<WKScrollbarPainterRef> _scrollerPainter;
+ WebCore::ScrollbarPart _part;
+ WebCore::ScrollAnimatorMac* _animator;
+ CGFloat _initialAlpha;
+ CGFloat _newAlpha;
+}
+- (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration;
+@end
+
+@implementation ScrollbarPartAnimation
+
+- (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
+{
+ self = [super initWithDuration:duration animationCurve:NSAnimationEaseInOut];
+ if (!self)
+ return nil;
+
+ _scrollerPainter = scrollerPainter;
+ _part = part;
+ _animator = scrollAnimator;
+ _initialAlpha = _part == WebCore::ThumbPart ? wkScrollbarPainterKnobAlpha(_scrollerPainter.get()) : wkScrollbarPainterTrackAlpha(_scrollerPainter.get());
+ _newAlpha = newAlpha;
+
+ return self;
+}
+
+- (void)setCurrentProgress:(NSAnimationProgress)progress
+{
+ [super setCurrentProgress:progress];
+
+ if (!_animator)
+ return;
+
+ CGFloat currentAlpha;
+ if (_initialAlpha > _newAlpha)
+ currentAlpha = 1 - progress;
+ else
+ currentAlpha = progress;
+
+ if (_part == WebCore::ThumbPart)
+ wkSetScrollbarPainterKnobAlpha(_scrollerPainter.get(), currentAlpha);
+ else
+ wkSetScrollbarPainterTrackAlpha(_scrollerPainter.get(), currentAlpha);
+
+ // Invalidate the scrollbars so that they paint the animation
+ if (WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar())
+ _animator->scrollableArea()->invalidateScrollbarRect(verticalScrollbar, WebCore::IntRect(0, 0, verticalScrollbar->width(), verticalScrollbar->height()));
+ if (WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar())
+ _animator->scrollableArea()->invalidateScrollbarRect(horizontalScrollbar, WebCore::IntRect(0, 0, horizontalScrollbar->width(), horizontalScrollbar->height()));
+}
+
+- (void)scrollAnimatorDestroyed
+{
+ [self stopAnimation];
+ _animator = 0;
+}
+
+@end
+
+@interface ScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
+{
+ WebCore::ScrollAnimatorMac* _animator;
+
+ RetainPtr<ScrollbarPartAnimation> _verticalKnobAnimation;
+ RetainPtr<ScrollbarPartAnimation> _horizontalKnobAnimation;
+
+ RetainPtr<ScrollbarPartAnimation> _verticalTrackAnimation;
+ RetainPtr<ScrollbarPartAnimation> _horizontalTrackAnimation;
+}
+- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
+@end
+
+@implementation ScrollbarPainterDelegate
+
+- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _animator = scrollAnimator;
+ return self;
+}
+
+- (NSRect)convertRectToBacking:(NSRect)aRect
+{
+ return aRect;
+}
+
+- (NSRect)convertRectFromBacking:(NSRect)aRect
+{
+ return aRect;
+}
+
+- (CALayer *)layer
+{
+ if (!_animator)
+ return nil;
+ if (!_animator->scrollableArea()->scrollbarWillRenderIntoCompositingLayer())
+ return nil;
+
+ // FIXME: This should attempt to return an actual layer.
+ static CALayer *dummyLayer = [[CALayer alloc] init];
+ return dummyLayer;
+}
+
+- (void)setUpAnimation:(RetainPtr<ScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
+{
+ // If we are currently animating, stop
+ if (scrollbarPartAnimation) {
+ [scrollbarPartAnimation.get() stopAnimation];
+ scrollbarPartAnimation = nil;
+ }
+
+ scrollbarPartAnimation.adoptNS([[ScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter
+ part:part
+ scrollAnimator:_animator
+ animateAlphaTo:newAlpha
+ duration:duration]);
+ [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
+ [scrollbarPartAnimation.get() startAnimation];
+}
+
+- (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
+{
+ if (!_animator)
+ return;
+
+ WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
+ if (newKnobAlpha == wkScrollbarPainterKnobAlpha(scrollerPainter))
+ return;
+
+ if (wkScrollbarPainterIsHorizontal(scrollerPainter))
+ [self setUpAnimation:_horizontalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
+ else
+ [self setUpAnimation:_verticalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
+}
+
+- (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
+{
+ if (!_animator)
+ return;
+
+ WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
+ if (newTrackAlpha == wkScrollbarPainterTrackAlpha(scrollerPainter))
+ return;
+
+ if (wkScrollbarPainterIsHorizontal(scrollerPainter))
+ [self setUpAnimation:_horizontalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
+ else
+ [self setUpAnimation:_verticalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
+}
+
+- (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
+{
+ if (!_animator)
+ return;
+
+ WKScrollbarPainterRef scrollbarPainter = (WKScrollbarPainterRef)scrollerImp;
+ wkScrollbarPainterSetOverlayState(scrollbarPainter, newOverlayScrollerState);
+
+ if (wkScrollbarPainterIsHorizontal(scrollbarPainter)) {
+ WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar();
+ _animator->scrollableArea()->invalidateScrollbarRect(horizontalScrollbar, WebCore::IntRect(0, 0, horizontalScrollbar->width(), horizontalScrollbar->height()));
+ } else {
+ WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar();
+ _animator->scrollableArea()->invalidateScrollbarRect(verticalScrollbar, WebCore::IntRect(0, 0, verticalScrollbar->width(), verticalScrollbar->height()));
+
+ }
+}
+
+- (void)scrollAnimatorDestroyed
+{
+ _animator = 0;
+ [_verticalKnobAnimation.get() scrollAnimatorDestroyed];
+ [_horizontalKnobAnimation.get() scrollAnimatorDestroyed];
+ [_verticalTrackAnimation.get() scrollAnimatorDestroyed];
+ [_horizontalTrackAnimation.get() scrollAnimatorDestroyed];
+}
+
+@end
+#endif // #if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+
namespace WebCore {
PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
@@ -131,13 +451,33 @@ PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea
ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
: ScrollAnimator(scrollableArea)
+#if ENABLE(RUBBER_BANDING)
+ , m_inScrollGesture(false)
+ , m_momentumScrollInProgress(false)
+ , m_ignoreMomentumScrolls(false)
+ , m_lastMomemtumScrollTimestamp(0)
+ , m_startTime(0)
+ , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
+#endif
{
m_scrollAnimationHelperDelegate.adoptNS([[ScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
+
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ m_scrollbarPainterControllerDelegate.adoptNS([[ScrollbarPainterControllerDelegate alloc] initWithScrollAnimator:this]);
+ m_scrollbarPainterController = wkMakeScrollbarPainterController(m_scrollbarPainterControllerDelegate.get());
+ m_scrollbarPainterDelegate.adoptNS([[ScrollbarPainterDelegate alloc] initWithScrollAnimator:this]);
+#endif
}
ScrollAnimatorMac::~ScrollAnimatorMac()
{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ [m_scrollbarPainterControllerDelegate.get() scrollAnimatorDestroyed];
+ [(id)m_scrollbarPainterController.get() setDelegate:nil];
+ [m_scrollbarPainterDelegate.get() scrollAnimatorDestroyed];
+ [m_scrollAnimationHelperDelegate.get() scrollAnimatorDestroyed];
+#endif
}
bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
@@ -167,16 +507,567 @@ bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranulari
void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
{
[m_scrollAnimationHelper.get() _stopRun];
- ScrollAnimator::scrollToOffsetWithoutAnimation(offset);
+ immediateScrollToPoint(offset);
+}
+
+float ScrollAnimatorMac::adjustScrollXPositionIfNecessary(float position) const
+{
+ if (!m_scrollableArea->constrainsScrollingToContentEdge())
+ return position;
+
+ return max<float>(min<float>(position, m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
+}
+
+float ScrollAnimatorMac::adjustScrollYPositionIfNecessary(float position) const
+{
+ if (!m_scrollableArea->constrainsScrollingToContentEdge())
+ return position;
+
+ return max<float>(min<float>(position, m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
+}
+
+FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
+{
+ if (!m_scrollableArea->constrainsScrollingToContentEdge())
+ return position;
+
+ float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
+ float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
+
+ return FloatPoint(newX, newY);
}
void ScrollAnimatorMac::immediateScrollToPoint(const FloatPoint& newPosition)
{
- m_currentPosX = newPosition.x();
- m_currentPosY = newPosition.y();
+ FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
+
+ m_currentPosX = adjustedPosition.x();
+ m_currentPosY = adjustedPosition.y();
notityPositionChanged();
}
+void ScrollAnimatorMac::immediateScrollByDeltaX(float deltaX)
+{
+ m_currentPosX = adjustScrollXPositionIfNecessary(m_currentPosX + deltaX);
+ notityPositionChanged();
+}
+
+void ScrollAnimatorMac::immediateScrollByDeltaY(float deltaY)
+{
+ m_currentPosY = adjustScrollYPositionIfNecessary(m_currentPosY + deltaY);
+ notityPositionChanged();
+}
+
+void ScrollAnimatorMac::notityPositionChanged()
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkContentAreaScrolled(m_scrollbarPainterController.get());
+#endif
+ ScrollAnimator::notityPositionChanged();
+}
+
+void ScrollAnimatorMac::contentAreaWillPaint() const
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkContentAreaWillPaint(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::mouseEnteredContentArea() const
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkMouseEnteredContentArea(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::mouseExitedContentArea() const
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkMouseExitedContentArea(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::mouseMovedInContentArea() const
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkMouseMovedInContentArea(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::willStartLiveResize()
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkWillStartLiveResize(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::contentsResized() const
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkContentAreaResized(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::willEndLiveResize()
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkWillEndLiveResize(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::contentAreaDidShow() const
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkContentAreaDidShow(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::contentAreaDidHide() const
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ wkContentAreaDidHide(m_scrollbarPainterController.get());
+#endif
+}
+
+void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
+ wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
+ wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, false);
+ if (scrollableArea()->inLiveResize())
+ wkSetScrollbarPainterKnobAlpha(painter, 1);
+#else
+ UNUSED_PARAM(scrollbar);
+#endif
+}
+
+void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
+ wkScrollbarPainterSetDelegate(painter, nil);
+ wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, false);
+#else
+ UNUSED_PARAM(scrollbar);
+#endif
+}
+
+void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
+ wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
+ wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, true);
+ if (scrollableArea()->inLiveResize())
+ wkSetScrollbarPainterKnobAlpha(painter, 1);
+#else
+ UNUSED_PARAM(scrollbar);
+#endif
+}
+
+void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
+{
+#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER)
+ WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
+ wkScrollbarPainterSetDelegate(painter, nil);
+ wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, true);
+#else
+ UNUSED_PARAM(scrollbar);
+#endif
+}
+
+#if ENABLE(RUBBER_BANDING)
+
+static const float scrollVelocityZeroingTimeout = 0.10f;
+static const float rubberbandStiffness = 20;
+static const float rubberbandDirectionLockStretchRatio = 1;
+static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
+static const float rubberbandAmplitude = 0.31f;
+static const float rubberbandPeriod = 1.6f;
+
+static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime)
+{
+ float amplitude = rubberbandAmplitude;
+ float period = rubberbandPeriod;
+ float criticalDampeningFactor = expf((-elapsedTime * rubberbandStiffness) / period);
+
+ return (initialPosition + (-initialVelocity * elapsedTime * amplitude)) * criticalDampeningFactor;
+}
+
+static float elasticDeltaForReboundDelta(float delta)
+{
+ float stiffness = std::max(rubberbandStiffness, 1.0f);
+ return delta / stiffness;
+}
+
+static float reboundDeltaForElasticDelta(float delta)
+{
+ return delta * rubberbandStiffness;
+}
+
+static float scrollWheelMultiplier()
+{
+ static float multiplier = -1;
+ if (multiplier < 0) {
+ multiplier = [[NSUserDefaults standardUserDefaults] floatForKey:@"NSScrollWheelMultiplier"];
+ if (multiplier <= 0)
+ multiplier = 1;
+ }
+ return multiplier;
+}
+
+void ScrollAnimatorMac::handleWheelEvent(PlatformWheelEvent& wheelEvent)
+{
+ if (!wheelEvent.hasPreciseScrollingDeltas()) {
+ ScrollAnimator::handleWheelEvent(wheelEvent);
+ return;
+ }
+
+ wheelEvent.accept();
+
+ bool isMometumScrollEvent = (wheelEvent.phase() != PlatformWheelEventPhaseNone);
+ if (m_ignoreMomentumScrolls && (isMometumScrollEvent || m_snapRubberBandTimer.isActive())) {
+ if (wheelEvent.phase() == PlatformWheelEventPhaseEnded)
+ m_ignoreMomentumScrolls = false;
+ return;
+ }
+
+ smoothScrollWithEvent(wheelEvent);
+}
+
+void ScrollAnimatorMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
+{
+ if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType)
+ beginScrollGesture();
+ else
+ endScrollGesture();
+}
+
+bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
+{
+ FloatSize limitDelta;
+ if (fabsf(deltaY) >= fabsf(deltaX)) {
+ if (deltaY < 0) {
+ // We are trying to scroll up. Make sure we are not pinned to the top
+ limitDelta.setHeight(m_scrollableArea->visibleContentRect().y());
+ } else {
+ // We are trying to scroll down. Make sure we are not pinned to the bottom
+ limitDelta.setHeight(m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleContentRect().maxY());
+ }
+ } else if (deltaX != 0) {
+ if (deltaX < 0) {
+ // We are trying to scroll left. Make sure we are not pinned to the left
+ limitDelta.setWidth(m_scrollableArea->visibleContentRect().x());
+ } else {
+ // We are trying to scroll right. Make sure we are not pinned to the right
+ limitDelta.setWidth(m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleContentRect().maxX());
+ }
+ }
+
+ if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
+ return true;
+ return false;
+}
+
+bool ScrollAnimatorMac::allowsVerticalStretching() const
+{
+ Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
+ Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
+ if (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())))
+ return true;
+
+ return false;
+}
+
+bool ScrollAnimatorMac::allowsHorizontalStretching() const
+{
+ Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
+ Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
+ if (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())))
+ return true;
+
+ return false;
+}
+
+void ScrollAnimatorMac::smoothScrollWithEvent(PlatformWheelEvent& wheelEvent)
+{
+ float deltaX = m_overflowScrollDelta.width();
+ float deltaY = m_overflowScrollDelta.height();
+
+ // Reset overflow values because we may decide to remove delta at various points and put it into overflow.
+ m_overflowScrollDelta = FloatSize();
+
+ float eventCoallescedDeltaX = -wheelEvent.deltaX();
+ float eventCoallescedDeltaY = -wheelEvent.deltaY();
+
+ deltaX += eventCoallescedDeltaX;
+ deltaY += eventCoallescedDeltaY;
+
+ // Slightly prefer scrolling vertically by applying the = case to deltaY
+ if (fabsf(deltaY) >= fabsf(deltaX))
+ deltaX = 0;
+ else
+ deltaY = 0;
+
+ bool isVerticallyStretched = false;
+ bool isHorizontallyStretched = false;
+ bool shouldStretch = false;
+
+ IntSize stretchAmount = m_scrollableArea->overhangAmount();
+
+ isHorizontallyStretched = stretchAmount.width();
+ isVerticallyStretched = stretchAmount.height();
+
+ PlatformWheelEventPhase phase = wheelEvent.phase();
+
+ // If we are starting momentum scrolling then do some setup.
+ if (!m_momentumScrollInProgress && (phase == PlatformWheelEventPhaseBegan || phase == PlatformWheelEventPhaseChanged))
+ m_momentumScrollInProgress = true;
+
+ CFTimeInterval timeDelta = wheelEvent.timestamp() - m_lastMomemtumScrollTimestamp;
+ if (m_inScrollGesture || m_momentumScrollInProgress) {
+ if (m_lastMomemtumScrollTimestamp && timeDelta > 0 && timeDelta < scrollVelocityZeroingTimeout) {
+ m_momentumVelocity.setWidth(eventCoallescedDeltaX / (float)timeDelta);
+ m_momentumVelocity.setHeight(eventCoallescedDeltaY / (float)timeDelta);
+ m_lastMomemtumScrollTimestamp = wheelEvent.timestamp();
+ } else {
+ m_lastMomemtumScrollTimestamp = wheelEvent.timestamp();
+ m_momentumVelocity = FloatSize();
+ }
+
+ if (isVerticallyStretched) {
+ if (!isHorizontallyStretched && pinnedInDirection(deltaX, 0)) {
+ // Stretching only in the vertical.
+ if (deltaY != 0 && (fabsf(deltaX / deltaY) < rubberbandDirectionLockStretchRatio))
+ deltaX = 0;
+ else if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
+ m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
+ deltaX = 0;
+ } else
+ m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
+ }
+ } else if (isHorizontallyStretched) {
+ // Stretching only in the horizontal.
+ if (pinnedInDirection(0, deltaY)) {
+ if (deltaX != 0 && (fabsf(deltaY / deltaX) < rubberbandDirectionLockStretchRatio))
+ deltaY = 0;
+ else if (fabsf(deltaY) < rubberbandMinimumRequiredDeltaBeforeStretch) {
+ m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
+ deltaY = 0;
+ } else
+ m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
+ }
+ } else {
+ // Not stretching at all yet.
+ if (pinnedInDirection(deltaX, deltaY)) {
+ if (fabsf(deltaY) >= fabsf(deltaX)) {
+ if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
+ m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
+ deltaX = 0;
+ } else
+ m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
+ }
+ shouldStretch = true;
+ }
+ }
+ }
+
+ if (deltaX != 0 || deltaY != 0) {
+ if (!(shouldStretch || isVerticallyStretched || isHorizontallyStretched)) {
+ if (deltaY != 0) {
+ deltaY *= scrollWheelMultiplier();
+ immediateScrollByDeltaY(deltaY);
+ }
+ if (deltaX != 0) {
+ deltaX *= scrollWheelMultiplier();
+ immediateScrollByDeltaX(deltaX);
+ }
+ } else {
+ if (!allowsHorizontalStretching()) {
+ deltaX = 0;
+ eventCoallescedDeltaX = 0;
+ } else if ((deltaX != 0) && !isHorizontallyStretched && !pinnedInDirection(deltaX, 0)) {
+ deltaX *= scrollWheelMultiplier();
+
+ m_scrollableArea->setConstrainsScrollingToContentEdge(false);
+ immediateScrollByDeltaX(deltaX);
+ m_scrollableArea->setConstrainsScrollingToContentEdge(true);
+
+ deltaX = 0;
+ }
+
+ if (!allowsVerticalStretching()) {
+ deltaY = 0;
+ eventCoallescedDeltaY = 0;
+ } else if ((deltaY != 0) && !isVerticallyStretched && !pinnedInDirection(0, deltaY)) {
+ deltaY *= scrollWheelMultiplier();
+
+ m_scrollableArea->setConstrainsScrollingToContentEdge(false);
+ immediateScrollByDeltaY(deltaY);
+ m_scrollableArea->setConstrainsScrollingToContentEdge(true);
+
+ deltaY = 0;
+ }
+
+ IntSize stretchAmount = m_scrollableArea->overhangAmount();
+
+ if (m_momentumScrollInProgress) {
+ if ((pinnedInDirection(eventCoallescedDeltaX, eventCoallescedDeltaY) || (fabsf(eventCoallescedDeltaX) + fabsf(eventCoallescedDeltaY) <= 0)) && m_lastMomemtumScrollTimestamp) {
+ m_ignoreMomentumScrolls = true;
+ m_momentumScrollInProgress = false;
+ snapRubberBand();
+ }
+ }
+
+ m_stretchScrollForce.setWidth(m_stretchScrollForce.width() + deltaX);
+ m_stretchScrollForce.setHeight(m_stretchScrollForce.height() + deltaY);
+
+ FloatSize dampedDelta(ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.width())), ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.height())));
+ FloatPoint origOrigin = m_scrollableArea->visibleContentRect().location() - stretchAmount;
+ FloatPoint newOrigin = origOrigin + dampedDelta;
+
+ if (origOrigin != newOrigin) {
+ m_scrollableArea->setConstrainsScrollingToContentEdge(false);
+ immediateScrollToPoint(newOrigin);
+ m_scrollableArea->setConstrainsScrollingToContentEdge(true);
+ }
+ }
+ }
+
+ if (m_momentumScrollInProgress && phase == PlatformWheelEventPhaseEnded) {
+ m_momentumScrollInProgress = false;
+ m_ignoreMomentumScrolls = false;
+ m_lastMomemtumScrollTimestamp = 0;
+ }
+}
+
+void ScrollAnimatorMac::beginScrollGesture()
+{
+ m_inScrollGesture = true;
+ m_momentumScrollInProgress = false;
+ m_ignoreMomentumScrolls = false;
+ m_lastMomemtumScrollTimestamp = 0;
+ m_momentumVelocity = FloatSize();
+
+ IntSize stretchAmount = m_scrollableArea->overhangAmount();
+ m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(stretchAmount.width()));
+ m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(stretchAmount.height()));
+
+ m_overflowScrollDelta = FloatSize();
+
+ if (m_snapRubberBandTimer.isActive())
+ m_snapRubberBandTimer.stop();
+}
+
+void ScrollAnimatorMac::endScrollGesture()
+{
+ snapRubberBand();
+}
+
+void ScrollAnimatorMac::snapRubberBand()
+{
+ CFTimeInterval timeDelta = [[NSProcessInfo processInfo] systemUptime] - m_lastMomemtumScrollTimestamp;
+ if (m_lastMomemtumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout)
+ m_momentumVelocity = FloatSize();
+
+ m_inScrollGesture = false;
+
+ if (m_snapRubberBandTimer.isActive())
+ return;
+
+ m_startTime = [NSDate timeIntervalSinceReferenceDate];
+ m_startStretch = FloatSize();
+ m_origOrigin = FloatPoint();
+ m_origVelocity = FloatSize();
+
+ m_snapRubberBandTimer.startRepeating(1.0/60.0);
+}
+
+static inline float roundTowardZero(float num)
+{
+ return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f);
+}
+
+static inline float roundToDevicePixelTowardZero(float num)
+{
+ float roundedNum = roundf(num);
+ if (fabs(num - roundedNum) < 0.125)
+ num = roundedNum;
+
+ return roundTowardZero(num);
+}
+
+void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
+{
+ if (!m_momentumScrollInProgress || m_ignoreMomentumScrolls) {
+ CFTimeInterval timeDelta = [NSDate timeIntervalSinceReferenceDate] - m_startTime;
+
+ if (m_startStretch == FloatSize()) {
+ m_startStretch = m_scrollableArea->overhangAmount();
+ if (m_startStretch == FloatSize()) {
+ m_snapRubberBandTimer.stop();
+ m_stretchScrollForce = FloatSize();
+ m_startTime = 0;
+ m_startStretch = FloatSize();
+ m_origOrigin = FloatPoint();
+ m_origVelocity = FloatSize();
+
+ return;
+ }
+
+ m_origOrigin = m_scrollableArea->visibleContentRect().location() - m_startStretch;
+ m_origVelocity = m_momentumVelocity;
+
+ // Just like normal scrolling, prefer vertical rubberbanding
+ if (fabsf(m_origVelocity.height()) >= fabsf(m_origVelocity.width()))
+ m_origVelocity.setWidth(0);
+
+ // Don't rubber-band horizontally if it's not possible to scroll horizontally
+ Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
+ if (!hScroller || !hScroller->enabled())
+ m_origVelocity.setWidth(0);
+
+ // Don't rubber-band vertically if it's not possible to scroll horizontally
+ Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
+ if (!vScroller || !vScroller->enabled())
+ m_origVelocity.setHeight(0);
+ }
+
+ FloatPoint delta(roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.width(), -m_origVelocity.width(), (float)timeDelta)),
+ roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.height(), -m_origVelocity.height(), (float)timeDelta)));
+
+ if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) {
+ FloatPoint newOrigin = m_origOrigin + delta;
+
+ m_scrollableArea->setConstrainsScrollingToContentEdge(false);
+ immediateScrollToPoint(newOrigin);
+ m_scrollableArea->setConstrainsScrollingToContentEdge(true);
+
+ FloatSize newStretch = m_scrollableArea->overhangAmount();
+
+ m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(newStretch.width()));
+ m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(newStretch.height()));
+ } else {
+ immediateScrollToPoint(m_origOrigin);
+
+ m_scrollableArea->didCompleteRubberBand(roundedIntSize(m_startStretch));
+
+ m_snapRubberBandTimer.stop();
+ m_stretchScrollForce = FloatSize();
+
+ m_startTime = 0;
+ m_startStretch = FloatSize();
+ m_origOrigin = FloatPoint();
+ m_origVelocity = FloatSize();
+ }
+ } else {
+ m_startTime = [NSDate timeIntervalSinceReferenceDate];
+ m_startStretch = FloatSize();
+ }
+}
+#endif
+
} // namespace WebCore
#endif // ENABLE(SMOOTH_SCROLLING)