Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #if ENABLE(SMOOTH_SCROLLING)
     29 
     30 #include "ScrollAnimatorMac.h"
     31 
     32 #include "FloatPoint.h"
     33 #include "IntRect.h"
     34 #include "PlatformGestureEvent.h"
     35 #include "PlatformWheelEvent.h"
     36 #include "ScrollView.h"
     37 #include "ScrollableArea.h"
     38 #include "ScrollbarTheme.h"
     39 #include "ScrollbarThemeMac.h"
     40 #include <wtf/PassOwnPtr.h>
     41 #include <wtf/UnusedParam.h>
     42 
     43 using namespace WebCore;
     44 using namespace std;
     45 
     46 @interface NSObject (ScrollAnimationHelperDetails)
     47 - (id)initWithDelegate:(id)delegate;
     48 - (void)_stopRun;
     49 - (BOOL)_isAnimating;
     50 - (NSPoint)targetOrigin;
     51 @end
     52 
     53 @interface ScrollAnimationHelperDelegate : NSObject
     54 {
     55     WebCore::ScrollAnimatorMac* _animator;
     56 }
     57 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
     58 @end
     59 
     60 static NSSize abs(NSSize size)
     61 {
     62     NSSize finalSize = size;
     63     if (finalSize.width < 0)
     64         finalSize.width = -finalSize.width;
     65     if (finalSize.height < 0)
     66         finalSize.height = -finalSize.height;
     67     return finalSize;
     68 }
     69 
     70 @implementation ScrollAnimationHelperDelegate
     71 
     72 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
     73 {
     74     self = [super init];
     75     if (!self)
     76         return nil;
     77 
     78     _animator = scrollAnimator;
     79     return self;
     80 }
     81 
     82 - (void)scrollAnimatorDestroyed
     83 {
     84     _animator = 0;
     85 }
     86 
     87 - (NSRect)bounds
     88 {
     89     if (!_animator)
     90         return NSZeroRect;
     91 
     92     WebCore::FloatPoint currentPosition = _animator->currentPosition();
     93     return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
     94 }
     95 
     96 - (void)_immediateScrollToPoint:(NSPoint)newPosition
     97 {
     98     if (!_animator)
     99         return;
    100     _animator->immediateScrollToPoint(newPosition);
    101 }
    102 
    103 - (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin
    104 {
    105     return newOrigin;
    106 }
    107 
    108 - (NSSize)convertSizeToBase:(NSSize)size
    109 {
    110     return abs(size);
    111 }
    112 
    113 - (NSSize)convertSizeFromBase:(NSSize)size
    114 {
    115     return abs(size);
    116 }
    117 
    118 - (NSSize)convertSizeToBacking:(NSSize)size
    119 {
    120     return abs(size);
    121 }
    122 
    123 - (NSSize)convertSizeFromBacking:(NSSize)size
    124 {
    125     return abs(size);
    126 }
    127 
    128 - (id)superview
    129 {
    130     return nil;
    131 }
    132 
    133 - (id)documentView
    134 {
    135     return nil;
    136 }
    137 
    138 - (id)window
    139 {
    140     return nil;
    141 }
    142 
    143 - (void)_recursiveRecomputeToolTips
    144 {
    145 }
    146 
    147 @end
    148 
    149 #if USE(WK_SCROLLBAR_PAINTER)
    150 
    151 @interface ScrollbarPainterControllerDelegate : NSObject
    152 {
    153     WebCore::ScrollAnimatorMac* _animator;
    154 }
    155 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
    156 @end
    157 
    158 @implementation ScrollbarPainterControllerDelegate
    159 
    160 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
    161 {
    162     self = [super init];
    163     if (!self)
    164         return nil;
    165 
    166     _animator = scrollAnimator;
    167     return self;
    168 }
    169 
    170 - (void)scrollAnimatorDestroyed
    171 {
    172     _animator = 0;
    173 }
    174 
    175 - (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
    176 {
    177     UNUSED_PARAM(scrollerImpPair);
    178     if (!_animator)
    179         return NSZeroRect;
    180 
    181     WebCore::IntSize contentsSize = _animator->scrollableArea()->contentsSize();
    182     return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
    183 }
    184 
    185 - (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
    186 {
    187     UNUSED_PARAM(scrollerImpPair);
    188     if (!_animator)
    189         return NO;
    190 
    191     return _animator->scrollableArea()->inLiveResize();
    192 }
    193 
    194 - (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
    195 {
    196     UNUSED_PARAM(scrollerImpPair);
    197     if (!_animator)
    198         return NSZeroPoint;
    199 
    200     return _animator->scrollableArea()->currentMousePosition();
    201 }
    202 
    203 - (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
    204 {
    205     UNUSED_PARAM(scrollerImpPair);
    206     if (!_animator)
    207         return NSZeroPoint;
    208 
    209     WebCore::Scrollbar* scrollbar = 0;
    210     if (wkScrollbarPainterIsHorizontal((WKScrollbarPainterRef)scrollerImp))
    211         scrollbar = _animator->scrollableArea()->horizontalScrollbar();
    212     else
    213         scrollbar = _animator->scrollableArea()->verticalScrollbar();
    214 
    215     // It is possible to have a null scrollbar here since it is possible for this delegate
    216     // method to be called between the moment when a scrollbar has been set to 0 and the
    217     // moment when its destructor has been called. We should probably de-couple some
    218     // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this
    219     // issue.
    220     if (!scrollbar)
    221         return WebCore::IntPoint();
    222 
    223     return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea));
    224 }
    225 
    226 - (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
    227 {
    228     UNUSED_PARAM(scrollerImpPair);
    229     UNUSED_PARAM(rect);
    230 }
    231 
    232 - (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
    233 {
    234     if (!_animator)
    235         return;
    236 
    237     WKScrollbarPainterControllerRef painterController = (WKScrollbarPainterControllerRef)scrollerImpPair;
    238     WebCore::ScrollbarThemeMac* macTheme = (WebCore::ScrollbarThemeMac*)WebCore::ScrollbarTheme::nativeTheme();
    239 
    240     WKScrollbarPainterRef oldVerticalPainter = wkVerticalScrollbarPainterForController(painterController);
    241     if (oldVerticalPainter) {
    242         WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar();
    243         WKScrollbarPainterRef newVerticalPainter = wkMakeScrollbarReplacementPainter(oldVerticalPainter,
    244                                                                                      newRecommendedScrollerStyle,
    245                                                                                      verticalScrollbar->controlSize(),
    246                                                                                      false);
    247         macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
    248         wkSetPainterForPainterController(painterController, newVerticalPainter, false);
    249 
    250         // The different scrollbar styles have different thicknesses, so we must re-set the
    251         // frameRect to the new thickness, and the re-layout below will ensure the position
    252         // and length are properly updated.
    253         int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
    254         verticalScrollbar->setFrameRect(WebCore::IntRect(0, 0, thickness, thickness));
    255     }
    256 
    257     WKScrollbarPainterRef oldHorizontalPainter = wkHorizontalScrollbarPainterForController(painterController);
    258     if (oldHorizontalPainter) {
    259         WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar();
    260         WKScrollbarPainterRef newHorizontalPainter = wkMakeScrollbarReplacementPainter(oldHorizontalPainter,
    261                                                                                        newRecommendedScrollerStyle,
    262                                                                                        horizontalScrollbar->controlSize(),
    263                                                                                        true);
    264         macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
    265         wkSetPainterForPainterController(painterController, newHorizontalPainter, true);
    266 
    267         // The different scrollbar styles have different thicknesses, so we must re-set the
    268         // frameRect to the new thickness, and the re-layout below will ensure the position
    269         // and length are properly updated.
    270         int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
    271         horizontalScrollbar->setFrameRect(WebCore::IntRect(0, 0, thickness, thickness));
    272     }
    273 
    274     wkSetScrollbarPainterControllerStyle(painterController, newRecommendedScrollerStyle);
    275 
    276     // The different scrollbar styles affect layout, so we must re-layout everything.
    277     _animator->scrollableArea()->scrollbarStyleChanged();
    278 }
    279 
    280 @end
    281 
    282 @interface ScrollbarPartAnimation : NSAnimation
    283 {
    284     RetainPtr<WKScrollbarPainterRef> _scrollerPainter;
    285     WebCore::ScrollbarPart _part;
    286     WebCore::ScrollAnimatorMac* _animator;
    287     CGFloat _initialAlpha;
    288     CGFloat _newAlpha;
    289 }
    290 - (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration;
    291 @end
    292 
    293 @implementation ScrollbarPartAnimation
    294 
    295 - (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
    296 {
    297     self = [super initWithDuration:duration animationCurve:NSAnimationEaseInOut];
    298     if (!self)
    299         return nil;
    300 
    301     _scrollerPainter = scrollerPainter;
    302     _part = part;
    303     _animator = scrollAnimator;
    304     _initialAlpha = _part == WebCore::ThumbPart ? wkScrollbarPainterKnobAlpha(_scrollerPainter.get()) : wkScrollbarPainterTrackAlpha(_scrollerPainter.get());
    305     _newAlpha = newAlpha;
    306 
    307     return self;
    308 }
    309 
    310 - (void)setCurrentProgress:(NSAnimationProgress)progress
    311 {
    312     [super setCurrentProgress:progress];
    313 
    314     if (!_animator)
    315         return;
    316 
    317     CGFloat currentAlpha;
    318     if (_initialAlpha > _newAlpha)
    319         currentAlpha = 1 - progress;
    320     else
    321         currentAlpha = progress;
    322 
    323     if (_part == WebCore::ThumbPart)
    324         wkSetScrollbarPainterKnobAlpha(_scrollerPainter.get(), currentAlpha);
    325     else
    326         wkSetScrollbarPainterTrackAlpha(_scrollerPainter.get(), currentAlpha);
    327 
    328     // Invalidate the scrollbars so that they paint the animation
    329     if (WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar())
    330         verticalScrollbar->invalidateRect(WebCore::IntRect(0, 0, verticalScrollbar->width(), verticalScrollbar->height()));
    331     if (WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar())
    332         horizontalScrollbar->invalidateRect(WebCore::IntRect(0, 0, horizontalScrollbar->width(), horizontalScrollbar->height()));
    333 }
    334 
    335 - (void)scrollAnimatorDestroyed
    336 {
    337     [self stopAnimation];
    338     _animator = 0;
    339 }
    340 
    341 @end
    342 
    343 @interface ScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
    344 {
    345     WebCore::ScrollAnimatorMac* _animator;
    346 
    347     RetainPtr<ScrollbarPartAnimation> _verticalKnobAnimation;
    348     RetainPtr<ScrollbarPartAnimation> _horizontalKnobAnimation;
    349 
    350     RetainPtr<ScrollbarPartAnimation> _verticalTrackAnimation;
    351     RetainPtr<ScrollbarPartAnimation> _horizontalTrackAnimation;
    352 }
    353 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
    354 - (void)cancelAnimations;
    355 @end
    356 
    357 @implementation ScrollbarPainterDelegate
    358 
    359 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
    360 {
    361     self = [super init];
    362     if (!self)
    363         return nil;
    364 
    365     _animator = scrollAnimator;
    366     return self;
    367 }
    368 
    369 - (void)cancelAnimations
    370 {
    371     [_verticalKnobAnimation.get() stopAnimation];
    372     [_horizontalKnobAnimation.get() stopAnimation];
    373     [_verticalTrackAnimation.get() stopAnimation];
    374     [_horizontalTrackAnimation.get() stopAnimation];
    375 }
    376 
    377 - (NSRect)convertRectToBacking:(NSRect)aRect
    378 {
    379     return aRect;
    380 }
    381 
    382 - (NSRect)convertRectFromBacking:(NSRect)aRect
    383 {
    384     return aRect;
    385 }
    386 
    387 - (CALayer *)layer
    388 {
    389     if (!_animator)
    390         return nil;
    391     if (!_animator->isDrawingIntoLayer())
    392         return nil;
    393 
    394     // FIXME: This should attempt to return an actual layer.
    395     static CALayer *dummyLayer = [[CALayer alloc] init];
    396     return dummyLayer;
    397 }
    398 
    399 - (void)setUpAnimation:(RetainPtr<ScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
    400 {
    401     // If the user has scrolled the page, then the scrollbars must be animated here.
    402     // This overrides the early returns.
    403     bool mustAnimate = _animator->haveScrolledSincePageLoad();
    404 
    405     if (_animator->scrollbarPaintTimerIsActive() && !mustAnimate)
    406         return;
    407 
    408     if (_animator->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
    409         _animator->startScrollbarPaintTimer();
    410         return;
    411     }
    412 
    413     // At this point, we are definitely going to animate now, so stop the timer.
    414     _animator->stopScrollbarPaintTimer();
    415 
    416     // If we are currently animating, stop
    417     if (scrollbarPartAnimation) {
    418         [scrollbarPartAnimation.get() stopAnimation];
    419         scrollbarPartAnimation = nil;
    420     }
    421 
    422     [NSAnimationContext beginGrouping];
    423     [[NSAnimationContext currentContext] setDuration:duration];
    424     scrollbarPartAnimation.adoptNS([[ScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter
    425                                                                     part:part
    426                                                                     scrollAnimator:_animator
    427                                                                     animateAlphaTo:newAlpha
    428                                                                     duration:duration]);
    429     [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
    430     [scrollbarPartAnimation.get() startAnimation];
    431     [NSAnimationContext endGrouping];
    432 }
    433 
    434 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
    435 {
    436     if (!_animator)
    437         return;
    438 
    439     WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
    440     if (wkScrollbarPainterIsHorizontal(scrollerPainter))
    441         [self setUpAnimation:_horizontalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
    442     else
    443         [self setUpAnimation:_verticalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
    444 }
    445 
    446 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
    447 {
    448     if (!_animator)
    449         return;
    450 
    451     WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
    452     if (wkScrollbarPainterIsHorizontal(scrollerPainter))
    453         [self setUpAnimation:_horizontalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
    454     else
    455         [self setUpAnimation:_verticalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
    456 }
    457 
    458 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
    459 {
    460     UNUSED_PARAM(scrollerImp);
    461     UNUSED_PARAM(newOverlayScrollerState);
    462 }
    463 
    464 - (void)scrollAnimatorDestroyed
    465 {
    466     _animator = 0;
    467     [_verticalKnobAnimation.get() scrollAnimatorDestroyed];
    468     [_horizontalKnobAnimation.get() scrollAnimatorDestroyed];
    469     [_verticalTrackAnimation.get() scrollAnimatorDestroyed];
    470     [_horizontalTrackAnimation.get() scrollAnimatorDestroyed];
    471 }
    472 
    473 @end
    474 
    475 #endif // USE(WK_SCROLLBAR_PAINTER)
    476 
    477 namespace WebCore {
    478 
    479 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
    480 {
    481     return adoptPtr(new ScrollAnimatorMac(scrollableArea));
    482 }
    483 
    484 ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
    485     : ScrollAnimator(scrollableArea)
    486 #if USE(WK_SCROLLBAR_PAINTER)
    487     , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
    488 #endif
    489 #if ENABLE(RUBBER_BANDING)
    490     , m_inScrollGesture(false)
    491     , m_momentumScrollInProgress(false)
    492     , m_ignoreMomentumScrolls(false)
    493     , m_lastMomemtumScrollTimestamp(0)
    494     , m_startTime(0)
    495     , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
    496 #endif
    497     , m_drawingIntoLayer(false)
    498     , m_haveScrolledSincePageLoad(false)
    499 {
    500     m_scrollAnimationHelperDelegate.adoptNS([[ScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
    501     m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
    502 
    503 #if USE(WK_SCROLLBAR_PAINTER)
    504     m_scrollbarPainterControllerDelegate.adoptNS([[ScrollbarPainterControllerDelegate alloc] initWithScrollAnimator:this]);
    505     m_scrollbarPainterController = wkMakeScrollbarPainterController(m_scrollbarPainterControllerDelegate.get());
    506     m_scrollbarPainterDelegate.adoptNS([[ScrollbarPainterDelegate alloc] initWithScrollAnimator:this]);
    507 #endif
    508 }
    509 
    510 ScrollAnimatorMac::~ScrollAnimatorMac()
    511 {
    512 #if USE(WK_SCROLLBAR_PAINTER)
    513     [m_scrollbarPainterControllerDelegate.get() scrollAnimatorDestroyed];
    514     [(id)m_scrollbarPainterController.get() setDelegate:nil];
    515     [m_scrollbarPainterDelegate.get() scrollAnimatorDestroyed];
    516     [m_scrollAnimationHelperDelegate.get() scrollAnimatorDestroyed];
    517 #endif
    518 }
    519 
    520 bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
    521 {
    522     m_haveScrolledSincePageLoad = true;
    523 
    524     if (![[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollAnimationEnabled"])
    525         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
    526 
    527     if (granularity == ScrollByPixel)
    528         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
    529 
    530     float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
    531     float newPos = std::max<float>(std::min<float>(currentPos + (step * multiplier), static_cast<float>(m_scrollableArea->scrollSize(orientation))), 0);
    532     if (currentPos == newPos)
    533         return false;
    534 
    535     NSPoint newPoint;
    536     if ([m_scrollAnimationHelper.get() _isAnimating]) {
    537         NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
    538         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
    539     } else
    540         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
    541 
    542     [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
    543     return true;
    544 }
    545 
    546 void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
    547 {
    548     [m_scrollAnimationHelper.get() _stopRun];
    549     immediateScrollToPoint(offset);
    550 }
    551 
    552 float ScrollAnimatorMac::adjustScrollXPositionIfNecessary(float position) const
    553 {
    554     if (!m_scrollableArea->constrainsScrollingToContentEdge())
    555         return position;
    556 
    557     return max<float>(min<float>(position, m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
    558 }
    559 
    560 float ScrollAnimatorMac::adjustScrollYPositionIfNecessary(float position) const
    561 {
    562     if (!m_scrollableArea->constrainsScrollingToContentEdge())
    563         return position;
    564 
    565     return max<float>(min<float>(position, m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
    566 }
    567 
    568 FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
    569 {
    570     if (!m_scrollableArea->constrainsScrollingToContentEdge())
    571         return position;
    572 
    573     float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
    574     float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
    575 
    576     return FloatPoint(newX, newY);
    577 }
    578 
    579 void ScrollAnimatorMac::immediateScrollToPoint(const FloatPoint& newPosition)
    580 {
    581     FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
    582 
    583     if (adjustedPosition.x() == m_currentPosX && adjustedPosition.y() == m_currentPosY)
    584         return;
    585 
    586     m_currentPosX = adjustedPosition.x();
    587     m_currentPosY = adjustedPosition.y();
    588     notityPositionChanged();
    589 }
    590 
    591 void ScrollAnimatorMac::immediateScrollByDeltaX(float deltaX)
    592 {
    593     float newPosX = adjustScrollXPositionIfNecessary(m_currentPosX + deltaX);
    594 
    595     if (newPosX == m_currentPosX)
    596         return;
    597 
    598     m_currentPosX = newPosX;
    599     notityPositionChanged();
    600 }
    601 
    602 void ScrollAnimatorMac::immediateScrollByDeltaY(float deltaY)
    603 {
    604     float newPosY = adjustScrollYPositionIfNecessary(m_currentPosY + deltaY);
    605 
    606     if (newPosY == m_currentPosY)
    607         return;
    608 
    609     m_currentPosY = newPosY;
    610     notityPositionChanged();
    611 }
    612 
    613 void ScrollAnimatorMac::notityPositionChanged()
    614 {
    615 #if USE(WK_SCROLLBAR_PAINTER)
    616     wkContentAreaScrolled(m_scrollbarPainterController.get());
    617 #endif
    618     ScrollAnimator::notityPositionChanged();
    619 }
    620 
    621 void ScrollAnimatorMac::contentAreaWillPaint() const
    622 {
    623 #if USE(WK_SCROLLBAR_PAINTER)
    624     wkContentAreaWillPaint(m_scrollbarPainterController.get());
    625 #endif
    626 }
    627 
    628 void ScrollAnimatorMac::mouseEnteredContentArea() const
    629 {
    630 #if USE(WK_SCROLLBAR_PAINTER)
    631     wkMouseEnteredContentArea(m_scrollbarPainterController.get());
    632 #endif
    633 }
    634 
    635 void ScrollAnimatorMac::mouseExitedContentArea() const
    636 {
    637 #if USE(WK_SCROLLBAR_PAINTER)
    638     wkMouseExitedContentArea(m_scrollbarPainterController.get());
    639 #endif
    640 }
    641 
    642 void ScrollAnimatorMac::mouseMovedInContentArea() const
    643 {
    644 #if USE(WK_SCROLLBAR_PAINTER)
    645     wkMouseMovedInContentArea(m_scrollbarPainterController.get());
    646 #endif
    647 }
    648 
    649 void ScrollAnimatorMac::willStartLiveResize()
    650 {
    651 #if USE(WK_SCROLLBAR_PAINTER)
    652     wkWillStartLiveResize(m_scrollbarPainterController.get());
    653 #endif
    654 }
    655 
    656 void ScrollAnimatorMac::contentsResized() const
    657 {
    658 #if USE(WK_SCROLLBAR_PAINTER)
    659     wkContentAreaResized(m_scrollbarPainterController.get());
    660 #endif
    661 }
    662 
    663 void ScrollAnimatorMac::willEndLiveResize()
    664 {
    665 #if USE(WK_SCROLLBAR_PAINTER)
    666     wkWillEndLiveResize(m_scrollbarPainterController.get());
    667 #endif
    668 }
    669 
    670 void ScrollAnimatorMac::contentAreaDidShow() const
    671 {
    672 #if USE(WK_SCROLLBAR_PAINTER)
    673     wkContentAreaDidShow(m_scrollbarPainterController.get());
    674 #endif
    675 }
    676 
    677 void ScrollAnimatorMac::contentAreaDidHide() const
    678 {
    679 #if USE(WK_SCROLLBAR_PAINTER)
    680     wkContentAreaDidHide(m_scrollbarPainterController.get());
    681 #endif
    682 }
    683 
    684 void ScrollAnimatorMac::didBeginScrollGesture() const
    685 {
    686 #if USE(WK_SCROLLBAR_PAINTER)
    687     wkDidBeginScrollGesture(m_scrollbarPainterController.get());
    688 #endif
    689 }
    690 
    691 void ScrollAnimatorMac::didEndScrollGesture() const
    692 {
    693 #if USE(WK_SCROLLBAR_PAINTER)
    694     wkDidEndScrollGesture(m_scrollbarPainterController.get());
    695 #endif
    696 }
    697 
    698 void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
    699 {
    700 #if USE(WK_SCROLLBAR_PAINTER)
    701     WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
    702     wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
    703     wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, false);
    704     if (scrollableArea()->inLiveResize())
    705         wkSetScrollbarPainterKnobAlpha(painter, 1);
    706 #else
    707     UNUSED_PARAM(scrollbar);
    708 #endif
    709 }
    710 
    711 void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
    712 {
    713 #if USE(WK_SCROLLBAR_PAINTER)
    714     WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
    715     wkScrollbarPainterSetDelegate(painter, nil);
    716     wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, false);
    717 #else
    718     UNUSED_PARAM(scrollbar);
    719 #endif
    720 }
    721 
    722 void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
    723 {
    724 #if USE(WK_SCROLLBAR_PAINTER)
    725     WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
    726     wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
    727     wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, true);
    728     if (scrollableArea()->inLiveResize())
    729         wkSetScrollbarPainterKnobAlpha(painter, 1);
    730 #else
    731     UNUSED_PARAM(scrollbar);
    732 #endif
    733 }
    734 
    735 void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
    736 {
    737 #if USE(WK_SCROLLBAR_PAINTER)
    738     WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
    739     wkScrollbarPainterSetDelegate(painter, nil);
    740     wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, true);
    741 #else
    742     UNUSED_PARAM(scrollbar);
    743 #endif
    744 }
    745 
    746 void ScrollAnimatorMac::cancelAnimations()
    747 {
    748     m_haveScrolledSincePageLoad = false;
    749 
    750 #if USE(WK_SCROLLBAR_PAINTER)
    751     if (scrollbarPaintTimerIsActive())
    752         stopScrollbarPaintTimer();
    753     [m_scrollbarPainterDelegate.get() cancelAnimations];
    754 #endif
    755 }
    756 
    757 #if ENABLE(RUBBER_BANDING)
    758 
    759 static const float scrollVelocityZeroingTimeout = 0.10f;
    760 static const float rubberbandStiffness = 20;
    761 static const float rubberbandDirectionLockStretchRatio = 1;
    762 static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
    763 static const float rubberbandAmplitude = 0.31f;
    764 static const float rubberbandPeriod = 1.6f;
    765 
    766 static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime)
    767 {
    768     float amplitude = rubberbandAmplitude;
    769     float period = rubberbandPeriod;
    770     float criticalDampeningFactor = expf((-elapsedTime * rubberbandStiffness) / period);
    771 
    772     return (initialPosition + (-initialVelocity * elapsedTime * amplitude)) * criticalDampeningFactor;
    773 }
    774 
    775 static float elasticDeltaForReboundDelta(float delta)
    776 {
    777     float stiffness = std::max(rubberbandStiffness, 1.0f);
    778     return delta / stiffness;
    779 }
    780 
    781 static float reboundDeltaForElasticDelta(float delta)
    782 {
    783     return delta * rubberbandStiffness;
    784 }
    785 
    786 static float scrollWheelMultiplier()
    787 {
    788     static float multiplier = -1;
    789     if (multiplier < 0) {
    790         multiplier = [[NSUserDefaults standardUserDefaults] floatForKey:@"NSScrollWheelMultiplier"];
    791         if (multiplier <= 0)
    792             multiplier = 1;
    793     }
    794     return multiplier;
    795 }
    796 
    797 void ScrollAnimatorMac::handleWheelEvent(PlatformWheelEvent& wheelEvent)
    798 {
    799     m_haveScrolledSincePageLoad = true;
    800 
    801     if (!wheelEvent.hasPreciseScrollingDeltas()) {
    802         ScrollAnimator::handleWheelEvent(wheelEvent);
    803         return;
    804     }
    805 
    806     // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
    807     // up to the parent scrollable area. It takes advantage of the fact that
    808     // the base class implemenatation of handleWheelEvent will not accept the
    809     // wheel event if there is nowhere to scroll.
    810     if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
    811         if (!allowsVerticalStretching()) {
    812             ScrollAnimator::handleWheelEvent(wheelEvent);
    813             return;
    814         }
    815     } else {
    816         if (!allowsHorizontalStretching()) {
    817             ScrollAnimator::handleWheelEvent(wheelEvent);
    818             return;
    819         }
    820     }
    821 
    822     wheelEvent.accept();
    823 
    824     bool isMometumScrollEvent = (wheelEvent.momentumPhase() != PlatformWheelEventPhaseNone);
    825     if (m_ignoreMomentumScrolls && (isMometumScrollEvent || m_snapRubberBandTimer.isActive())) {
    826         if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded)
    827             m_ignoreMomentumScrolls = false;
    828         return;
    829     }
    830 
    831     smoothScrollWithEvent(wheelEvent);
    832 }
    833 
    834 void ScrollAnimatorMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
    835 {
    836     if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType)
    837         beginScrollGesture();
    838     else
    839         endScrollGesture();
    840 }
    841 
    842 bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
    843 {
    844     FloatSize limitDelta;
    845     if (fabsf(deltaY) >= fabsf(deltaX)) {
    846         if (deltaY < 0) {
    847             // We are trying to scroll up.  Make sure we are not pinned to the top
    848             limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
    849         } else {
    850             // We are trying to scroll down.  Make sure we are not pinned to the bottom
    851             limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
    852         }
    853     } else if (deltaX != 0) {
    854         if (deltaX < 0) {
    855             // We are trying to scroll left.  Make sure we are not pinned to the left
    856             limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
    857         } else {
    858             // We are trying to scroll right.  Make sure we are not pinned to the right
    859             limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
    860         }
    861     }
    862 
    863     if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
    864         return true;
    865     return false;
    866 }
    867 
    868 bool ScrollAnimatorMac::allowsVerticalStretching() const
    869 {
    870     switch (m_scrollableArea->verticalScrollElasticity()) {
    871     case ScrollElasticityAutomatic: {
    872         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
    873         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
    874         return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
    875     }
    876     case ScrollElasticityNone:
    877         return false;
    878     case ScrollElasticityAllowed:
    879         return true;
    880     }
    881 
    882     ASSERT_NOT_REACHED();
    883     return false;
    884 }
    885 
    886 bool ScrollAnimatorMac::allowsHorizontalStretching() const
    887 {
    888     switch (m_scrollableArea->horizontalScrollElasticity()) {
    889     case ScrollElasticityAutomatic: {
    890         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
    891         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
    892         return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
    893     }
    894     case ScrollElasticityNone:
    895         return false;
    896     case ScrollElasticityAllowed:
    897         return true;
    898     }
    899 
    900     ASSERT_NOT_REACHED();
    901     return false;
    902 }
    903 
    904 void ScrollAnimatorMac::smoothScrollWithEvent(PlatformWheelEvent& wheelEvent)
    905 {
    906     m_haveScrolledSincePageLoad = true;
    907 
    908     float deltaX = m_overflowScrollDelta.width();
    909     float deltaY = m_overflowScrollDelta.height();
    910 
    911     // Reset overflow values because we may decide to remove delta at various points and put it into overflow.
    912     m_overflowScrollDelta = FloatSize();
    913 
    914     float eventCoallescedDeltaX = -wheelEvent.deltaX();
    915     float eventCoallescedDeltaY = -wheelEvent.deltaY();
    916 
    917     deltaX += eventCoallescedDeltaX;
    918     deltaY += eventCoallescedDeltaY;
    919 
    920     // Slightly prefer scrolling vertically by applying the = case to deltaY
    921     if (fabsf(deltaY) >= fabsf(deltaX))
    922         deltaX = 0;
    923     else
    924         deltaY = 0;
    925 
    926     bool isVerticallyStretched = false;
    927     bool isHorizontallyStretched = false;
    928     bool shouldStretch = false;
    929 
    930     IntSize stretchAmount = m_scrollableArea->overhangAmount();
    931 
    932     isHorizontallyStretched = stretchAmount.width();
    933     isVerticallyStretched = stretchAmount.height();
    934 
    935     PlatformWheelEventPhase phase = wheelEvent.momentumPhase();
    936 
    937     // If we are starting momentum scrolling then do some setup.
    938     if (!m_momentumScrollInProgress && (phase == PlatformWheelEventPhaseBegan || phase == PlatformWheelEventPhaseChanged))
    939         m_momentumScrollInProgress = true;
    940 
    941     CFTimeInterval timeDelta = wheelEvent.timestamp() - m_lastMomemtumScrollTimestamp;
    942     if (m_inScrollGesture || m_momentumScrollInProgress) {
    943         if (m_lastMomemtumScrollTimestamp && timeDelta > 0 && timeDelta < scrollVelocityZeroingTimeout) {
    944             m_momentumVelocity.setWidth(eventCoallescedDeltaX / (float)timeDelta);
    945             m_momentumVelocity.setHeight(eventCoallescedDeltaY / (float)timeDelta);
    946             m_lastMomemtumScrollTimestamp = wheelEvent.timestamp();
    947         } else {
    948             m_lastMomemtumScrollTimestamp = wheelEvent.timestamp();
    949             m_momentumVelocity = FloatSize();
    950         }
    951 
    952         if (isVerticallyStretched) {
    953             if (!isHorizontallyStretched && pinnedInDirection(deltaX, 0)) {
    954                 // Stretching only in the vertical.
    955                 if (deltaY != 0 && (fabsf(deltaX / deltaY) < rubberbandDirectionLockStretchRatio))
    956                     deltaX = 0;
    957                 else if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
    958                     m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
    959                     deltaX = 0;
    960                 } else
    961                     m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
    962             }
    963         } else if (isHorizontallyStretched) {
    964             // Stretching only in the horizontal.
    965             if (pinnedInDirection(0, deltaY)) {
    966                 if (deltaX != 0 && (fabsf(deltaY / deltaX) < rubberbandDirectionLockStretchRatio))
    967                     deltaY = 0;
    968                 else if (fabsf(deltaY) < rubberbandMinimumRequiredDeltaBeforeStretch) {
    969                     m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
    970                     deltaY = 0;
    971                 } else
    972                     m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
    973             }
    974         } else {
    975             // Not stretching at all yet.
    976             if (pinnedInDirection(deltaX, deltaY)) {
    977                 if (fabsf(deltaY) >= fabsf(deltaX)) {
    978                     if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
    979                         m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
    980                         deltaX = 0;
    981                     } else
    982                         m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
    983                 }
    984                 shouldStretch = true;
    985             }
    986         }
    987     }
    988 
    989     if (deltaX != 0 || deltaY != 0) {
    990         if (!(shouldStretch || isVerticallyStretched || isHorizontallyStretched)) {
    991             if (deltaY != 0) {
    992                 deltaY *= scrollWheelMultiplier();
    993                 immediateScrollByDeltaY(deltaY);
    994             }
    995             if (deltaX != 0) {
    996                 deltaX *= scrollWheelMultiplier();
    997                 immediateScrollByDeltaX(deltaX);
    998             }
    999         } else {
   1000             if (!allowsHorizontalStretching()) {
   1001                 deltaX = 0;
   1002                 eventCoallescedDeltaX = 0;
   1003             } else if ((deltaX != 0) && !isHorizontallyStretched && !pinnedInDirection(deltaX, 0)) {
   1004                 deltaX *= scrollWheelMultiplier();
   1005 
   1006                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
   1007                 immediateScrollByDeltaX(deltaX);
   1008                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
   1009 
   1010                 deltaX = 0;
   1011             }
   1012 
   1013             if (!allowsVerticalStretching()) {
   1014                 deltaY = 0;
   1015                 eventCoallescedDeltaY = 0;
   1016             } else if ((deltaY != 0) && !isVerticallyStretched && !pinnedInDirection(0, deltaY)) {
   1017                 deltaY *= scrollWheelMultiplier();
   1018 
   1019                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
   1020                 immediateScrollByDeltaY(deltaY);
   1021                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
   1022 
   1023                 deltaY = 0;
   1024             }
   1025 
   1026             IntSize stretchAmount = m_scrollableArea->overhangAmount();
   1027 
   1028             if (m_momentumScrollInProgress) {
   1029                 if ((pinnedInDirection(eventCoallescedDeltaX, eventCoallescedDeltaY) || (fabsf(eventCoallescedDeltaX) + fabsf(eventCoallescedDeltaY) <= 0)) && m_lastMomemtumScrollTimestamp) {
   1030                     m_ignoreMomentumScrolls = true;
   1031                     m_momentumScrollInProgress = false;
   1032                     snapRubberBand();
   1033                 }
   1034             }
   1035 
   1036             m_stretchScrollForce.setWidth(m_stretchScrollForce.width() + deltaX);
   1037             m_stretchScrollForce.setHeight(m_stretchScrollForce.height() + deltaY);
   1038 
   1039             FloatSize dampedDelta(ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.width())), ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.height())));
   1040             FloatPoint origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - stretchAmount;
   1041             FloatPoint newOrigin = origOrigin + dampedDelta;
   1042 
   1043             if (origOrigin != newOrigin) {
   1044                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
   1045                 immediateScrollToPoint(newOrigin);
   1046                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
   1047             }
   1048         }
   1049     }
   1050 
   1051     if (m_momentumScrollInProgress && phase == PlatformWheelEventPhaseEnded) {
   1052         m_momentumScrollInProgress = false;
   1053         m_ignoreMomentumScrolls = false;
   1054         m_lastMomemtumScrollTimestamp = 0;
   1055     }
   1056 }
   1057 
   1058 void ScrollAnimatorMac::beginScrollGesture()
   1059 {
   1060     didBeginScrollGesture();
   1061 
   1062     m_haveScrolledSincePageLoad = true;
   1063     m_inScrollGesture = true;
   1064     m_momentumScrollInProgress = false;
   1065     m_ignoreMomentumScrolls = false;
   1066     m_lastMomemtumScrollTimestamp = 0;
   1067     m_momentumVelocity = FloatSize();
   1068 
   1069     IntSize stretchAmount = m_scrollableArea->overhangAmount();
   1070     m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(stretchAmount.width()));
   1071     m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(stretchAmount.height()));
   1072 
   1073     m_overflowScrollDelta = FloatSize();
   1074 
   1075     if (m_snapRubberBandTimer.isActive())
   1076         m_snapRubberBandTimer.stop();
   1077 }
   1078 
   1079 void ScrollAnimatorMac::endScrollGesture()
   1080 {
   1081     didEndScrollGesture();
   1082 
   1083     snapRubberBand();
   1084 }
   1085 
   1086 void ScrollAnimatorMac::snapRubberBand()
   1087 {
   1088     CFTimeInterval timeDelta = [[NSProcessInfo processInfo] systemUptime] - m_lastMomemtumScrollTimestamp;
   1089     if (m_lastMomemtumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout)
   1090         m_momentumVelocity = FloatSize();
   1091 
   1092     m_inScrollGesture = false;
   1093 
   1094     if (m_snapRubberBandTimer.isActive())
   1095         return;
   1096 
   1097     m_startTime = [NSDate timeIntervalSinceReferenceDate];
   1098     m_startStretch = FloatSize();
   1099     m_origOrigin = FloatPoint();
   1100     m_origVelocity = FloatSize();
   1101 
   1102     m_snapRubberBandTimer.startRepeating(1.0/60.0);
   1103 }
   1104 
   1105 static inline float roundTowardZero(float num)
   1106 {
   1107     return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f);
   1108 }
   1109 
   1110 static inline float roundToDevicePixelTowardZero(float num)
   1111 {
   1112     float roundedNum = roundf(num);
   1113     if (fabs(num - roundedNum) < 0.125)
   1114         num = roundedNum;
   1115 
   1116     return roundTowardZero(num);
   1117 }
   1118 
   1119 void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
   1120 {
   1121     if (!m_momentumScrollInProgress || m_ignoreMomentumScrolls) {
   1122         CFTimeInterval timeDelta = [NSDate timeIntervalSinceReferenceDate] - m_startTime;
   1123 
   1124         if (m_startStretch == FloatSize()) {
   1125             m_startStretch = m_scrollableArea->overhangAmount();
   1126             if (m_startStretch == FloatSize()) {
   1127                 m_snapRubberBandTimer.stop();
   1128                 m_stretchScrollForce = FloatSize();
   1129                 m_startTime = 0;
   1130                 m_startStretch = FloatSize();
   1131                 m_origOrigin = FloatPoint();
   1132                 m_origVelocity = FloatSize();
   1133 
   1134                 return;
   1135             }
   1136 
   1137             m_origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - m_startStretch;
   1138             m_origVelocity = m_momentumVelocity;
   1139 
   1140             // Just like normal scrolling, prefer vertical rubberbanding
   1141             if (fabsf(m_origVelocity.height()) >= fabsf(m_origVelocity.width()))
   1142                 m_origVelocity.setWidth(0);
   1143 
   1144             // Don't rubber-band horizontally if it's not possible to scroll horizontally
   1145             Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
   1146             if (!hScroller || !hScroller->enabled())
   1147                 m_origVelocity.setWidth(0);
   1148 
   1149             // Don't rubber-band vertically if it's not possible to scroll horizontally
   1150             Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
   1151             if (!vScroller || !vScroller->enabled())
   1152                 m_origVelocity.setHeight(0);
   1153         }
   1154 
   1155         FloatPoint delta(roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.width(), -m_origVelocity.width(), (float)timeDelta)),
   1156                          roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.height(), -m_origVelocity.height(), (float)timeDelta)));
   1157 
   1158         if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) {
   1159             FloatPoint newOrigin = m_origOrigin + delta;
   1160 
   1161             m_scrollableArea->setConstrainsScrollingToContentEdge(false);
   1162             immediateScrollToPoint(newOrigin);
   1163             m_scrollableArea->setConstrainsScrollingToContentEdge(true);
   1164 
   1165             FloatSize newStretch = m_scrollableArea->overhangAmount();
   1166 
   1167             m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(newStretch.width()));
   1168             m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(newStretch.height()));
   1169         } else {
   1170             immediateScrollToPoint(m_origOrigin);
   1171 
   1172             m_scrollableArea->didCompleteRubberBand(roundedIntSize(m_startStretch));
   1173 
   1174             m_snapRubberBandTimer.stop();
   1175             m_stretchScrollForce = FloatSize();
   1176 
   1177             m_startTime = 0;
   1178             m_startStretch = FloatSize();
   1179             m_origOrigin = FloatPoint();
   1180             m_origVelocity = FloatSize();
   1181         }
   1182     } else {
   1183         m_startTime = [NSDate timeIntervalSinceReferenceDate];
   1184         m_startStretch = FloatSize();
   1185     }
   1186 }
   1187 #endif
   1188 
   1189 #if USE(WK_SCROLLBAR_PAINTER)
   1190 void ScrollAnimatorMac::startScrollbarPaintTimer()
   1191 {
   1192     m_initialScrollbarPaintTimer.startOneShot(0.1);
   1193 }
   1194 
   1195 bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
   1196 {
   1197     return m_initialScrollbarPaintTimer.isActive();
   1198 }
   1199 
   1200 void ScrollAnimatorMac::stopScrollbarPaintTimer()
   1201 {
   1202     m_initialScrollbarPaintTimer.stop();
   1203 }
   1204 
   1205 void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
   1206 {
   1207     wkScrollbarPainterForceFlashScrollers(m_scrollbarPainterController.get());
   1208 }
   1209 #endif
   1210 
   1211 } // namespace WebCore
   1212 
   1213 #endif // ENABLE(SMOOTH_SCROLLING)
   1214