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 "PlatformGestureEvent.h"
34#include "PlatformWheelEvent.h"
35#include "ScrollView.h"
36#include "ScrollableArea.h"
37#include "ScrollbarTheme.h"
38#include "ScrollbarThemeMac.h"
39#include "WebCoreSystemInterface.h"
40#include <wtf/PassOwnPtr.h>
41#include <wtf/UnusedParam.h>
42
43using namespace WebCore;
44using 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
60static 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    if (part == WebCore::ThumbPart && !wkScrollbarPainterIsHorizontal(scrollerPainter)) {
423        if (newAlpha == 1) {
424            IntRect thumbRect = IntRect(wkScrollbarPainterKnobRect(scrollerPainter));
425            _animator->setVisibleScrollerThumbRect(thumbRect);
426        } else
427            _animator->setVisibleScrollerThumbRect(IntRect());
428    }
429
430    [NSAnimationContext beginGrouping];
431    [[NSAnimationContext currentContext] setDuration:duration];
432    scrollbarPartAnimation.adoptNS([[ScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter
433                                                                    part:part
434                                                                    scrollAnimator:_animator
435                                                                    animateAlphaTo:newAlpha
436                                                                    duration:duration]);
437    [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
438    [scrollbarPartAnimation.get() startAnimation];
439    [NSAnimationContext endGrouping];
440}
441
442- (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
443{
444    if (!_animator)
445        return;
446
447    WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
448    if (wkScrollbarPainterIsHorizontal(scrollerPainter))
449        [self setUpAnimation:_horizontalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
450    else
451        [self setUpAnimation:_verticalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
452}
453
454- (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
455{
456    if (!_animator)
457        return;
458
459    WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
460    if (wkScrollbarPainterIsHorizontal(scrollerPainter))
461        [self setUpAnimation:_horizontalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
462    else
463        [self setUpAnimation:_verticalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
464}
465
466- (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
467{
468    UNUSED_PARAM(scrollerImp);
469    UNUSED_PARAM(newOverlayScrollerState);
470}
471
472- (void)scrollAnimatorDestroyed
473{
474    _animator = 0;
475    [_verticalKnobAnimation.get() scrollAnimatorDestroyed];
476    [_horizontalKnobAnimation.get() scrollAnimatorDestroyed];
477    [_verticalTrackAnimation.get() scrollAnimatorDestroyed];
478    [_horizontalTrackAnimation.get() scrollAnimatorDestroyed];
479}
480
481@end
482
483#endif // USE(WK_SCROLLBAR_PAINTER)
484
485namespace WebCore {
486
487PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
488{
489    return adoptPtr(new ScrollAnimatorMac(scrollableArea));
490}
491
492ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
493    : ScrollAnimator(scrollableArea)
494#if USE(WK_SCROLLBAR_PAINTER)
495    , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
496#endif
497#if ENABLE(RUBBER_BANDING)
498    , m_inScrollGesture(false)
499    , m_momentumScrollInProgress(false)
500    , m_ignoreMomentumScrolls(false)
501    , m_lastMomemtumScrollTimestamp(0)
502    , m_startTime(0)
503    , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
504#endif
505    , m_drawingIntoLayer(false)
506    , m_haveScrolledSincePageLoad(false)
507{
508    m_scrollAnimationHelperDelegate.adoptNS([[ScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
509    m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
510
511#if USE(WK_SCROLLBAR_PAINTER)
512    m_scrollbarPainterControllerDelegate.adoptNS([[ScrollbarPainterControllerDelegate alloc] initWithScrollAnimator:this]);
513    m_scrollbarPainterController = wkMakeScrollbarPainterController(m_scrollbarPainterControllerDelegate.get());
514    m_scrollbarPainterDelegate.adoptNS([[ScrollbarPainterDelegate alloc] initWithScrollAnimator:this]);
515#endif
516}
517
518ScrollAnimatorMac::~ScrollAnimatorMac()
519{
520#if USE(WK_SCROLLBAR_PAINTER)
521    [m_scrollbarPainterControllerDelegate.get() scrollAnimatorDestroyed];
522    [(id)m_scrollbarPainterController.get() setDelegate:nil];
523    [m_scrollbarPainterDelegate.get() scrollAnimatorDestroyed];
524    [m_scrollAnimationHelperDelegate.get() scrollAnimatorDestroyed];
525#endif
526}
527
528bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
529{
530    m_haveScrolledSincePageLoad = true;
531
532    if (![[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollAnimationEnabled"])
533        return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
534
535    if (granularity == ScrollByPixel)
536        return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
537
538    float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
539    float newPos = std::max<float>(std::min<float>(currentPos + (step * multiplier), static_cast<float>(m_scrollableArea->scrollSize(orientation))), 0);
540    if (currentPos == newPos)
541        return false;
542
543    NSPoint newPoint;
544    if ([m_scrollAnimationHelper.get() _isAnimating]) {
545        NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
546        newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
547    } else
548        newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
549
550    [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
551    return true;
552}
553
554void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
555{
556    [m_scrollAnimationHelper.get() _stopRun];
557    immediateScrollToPoint(offset);
558}
559
560float ScrollAnimatorMac::adjustScrollXPositionIfNecessary(float position) const
561{
562    if (!m_scrollableArea->constrainsScrollingToContentEdge())
563        return position;
564
565    return max<float>(min<float>(position, m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
566}
567
568float ScrollAnimatorMac::adjustScrollYPositionIfNecessary(float position) const
569{
570    if (!m_scrollableArea->constrainsScrollingToContentEdge())
571        return position;
572
573    return max<float>(min<float>(position, m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
574}
575
576FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
577{
578    if (!m_scrollableArea->constrainsScrollingToContentEdge())
579        return position;
580
581    float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
582    float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
583
584    return FloatPoint(newX, newY);
585}
586
587void ScrollAnimatorMac::immediateScrollToPoint(const FloatPoint& newPosition)
588{
589    FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
590
591    if (adjustedPosition.x() == m_currentPosX && adjustedPosition.y() == m_currentPosY)
592        return;
593
594    m_currentPosX = adjustedPosition.x();
595    m_currentPosY = adjustedPosition.y();
596    notityPositionChanged();
597}
598
599void ScrollAnimatorMac::immediateScrollByDeltaX(float deltaX)
600{
601    float newPosX = adjustScrollXPositionIfNecessary(m_currentPosX + deltaX);
602
603    if (newPosX == m_currentPosX)
604        return;
605
606    m_currentPosX = newPosX;
607    notityPositionChanged();
608}
609
610void ScrollAnimatorMac::immediateScrollByDeltaY(float deltaY)
611{
612    float newPosY = adjustScrollYPositionIfNecessary(m_currentPosY + deltaY);
613
614    if (newPosY == m_currentPosY)
615        return;
616
617    m_currentPosY = newPosY;
618    notityPositionChanged();
619}
620
621void ScrollAnimatorMac::notityPositionChanged()
622{
623#if USE(WK_SCROLLBAR_PAINTER)
624    wkContentAreaScrolled(m_scrollbarPainterController.get());
625#endif
626    ScrollAnimator::notityPositionChanged();
627}
628
629void ScrollAnimatorMac::contentAreaWillPaint() const
630{
631#if USE(WK_SCROLLBAR_PAINTER)
632    wkContentAreaWillPaint(m_scrollbarPainterController.get());
633#endif
634}
635
636void ScrollAnimatorMac::mouseEnteredContentArea() const
637{
638#if USE(WK_SCROLLBAR_PAINTER)
639    wkMouseEnteredContentArea(m_scrollbarPainterController.get());
640#endif
641}
642
643void ScrollAnimatorMac::mouseExitedContentArea() const
644{
645#if USE(WK_SCROLLBAR_PAINTER)
646    wkMouseExitedContentArea(m_scrollbarPainterController.get());
647#endif
648}
649
650void ScrollAnimatorMac::mouseMovedInContentArea() const
651{
652#if USE(WK_SCROLLBAR_PAINTER)
653    wkMouseMovedInContentArea(m_scrollbarPainterController.get());
654#endif
655}
656
657void ScrollAnimatorMac::willStartLiveResize()
658{
659#if USE(WK_SCROLLBAR_PAINTER)
660    wkWillStartLiveResize(m_scrollbarPainterController.get());
661#endif
662}
663
664void ScrollAnimatorMac::contentsResized() const
665{
666#if USE(WK_SCROLLBAR_PAINTER)
667    wkContentAreaResized(m_scrollbarPainterController.get());
668#endif
669}
670
671void ScrollAnimatorMac::willEndLiveResize()
672{
673#if USE(WK_SCROLLBAR_PAINTER)
674    wkWillEndLiveResize(m_scrollbarPainterController.get());
675#endif
676}
677
678void ScrollAnimatorMac::contentAreaDidShow() const
679{
680#if USE(WK_SCROLLBAR_PAINTER)
681    wkContentAreaDidShow(m_scrollbarPainterController.get());
682#endif
683}
684
685void ScrollAnimatorMac::contentAreaDidHide() const
686{
687#if USE(WK_SCROLLBAR_PAINTER)
688    wkContentAreaDidHide(m_scrollbarPainterController.get());
689#endif
690}
691
692void ScrollAnimatorMac::didBeginScrollGesture() const
693{
694#if USE(WK_SCROLLBAR_PAINTER)
695    wkDidBeginScrollGesture(m_scrollbarPainterController.get());
696#endif
697}
698
699void ScrollAnimatorMac::didEndScrollGesture() const
700{
701#if USE(WK_SCROLLBAR_PAINTER)
702    wkDidEndScrollGesture(m_scrollbarPainterController.get());
703#endif
704}
705
706void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
707{
708#if USE(WK_SCROLLBAR_PAINTER)
709    WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
710    wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
711    wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, false);
712    if (scrollableArea()->inLiveResize())
713        wkSetScrollbarPainterKnobAlpha(painter, 1);
714#else
715    UNUSED_PARAM(scrollbar);
716#endif
717}
718
719void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
720{
721#if USE(WK_SCROLLBAR_PAINTER)
722    WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
723    wkScrollbarPainterSetDelegate(painter, nil);
724    wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, false);
725#else
726    UNUSED_PARAM(scrollbar);
727#endif
728}
729
730void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
731{
732#if USE(WK_SCROLLBAR_PAINTER)
733    WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
734    wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
735    wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, true);
736    if (scrollableArea()->inLiveResize())
737        wkSetScrollbarPainterKnobAlpha(painter, 1);
738#else
739    UNUSED_PARAM(scrollbar);
740#endif
741}
742
743void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
744{
745#if USE(WK_SCROLLBAR_PAINTER)
746    WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
747    wkScrollbarPainterSetDelegate(painter, nil);
748    wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, true);
749#else
750    UNUSED_PARAM(scrollbar);
751#endif
752}
753
754void ScrollAnimatorMac::cancelAnimations()
755{
756    m_haveScrolledSincePageLoad = false;
757
758#if USE(WK_SCROLLBAR_PAINTER)
759    if (scrollbarPaintTimerIsActive())
760        stopScrollbarPaintTimer();
761    [m_scrollbarPainterDelegate.get() cancelAnimations];
762#endif
763}
764
765#if ENABLE(RUBBER_BANDING)
766
767static const float scrollVelocityZeroingTimeout = 0.10f;
768static const float rubberbandStiffness = 20;
769static const float rubberbandDirectionLockStretchRatio = 1;
770static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
771static const float rubberbandAmplitude = 0.31f;
772static const float rubberbandPeriod = 1.6f;
773
774static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime)
775{
776    float amplitude = rubberbandAmplitude;
777    float period = rubberbandPeriod;
778    float criticalDampeningFactor = expf((-elapsedTime * rubberbandStiffness) / period);
779
780    return (initialPosition + (-initialVelocity * elapsedTime * amplitude)) * criticalDampeningFactor;
781}
782
783static float elasticDeltaForReboundDelta(float delta)
784{
785    float stiffness = std::max(rubberbandStiffness, 1.0f);
786    return delta / stiffness;
787}
788
789static float reboundDeltaForElasticDelta(float delta)
790{
791    return delta * rubberbandStiffness;
792}
793
794static float scrollWheelMultiplier()
795{
796    static float multiplier = -1;
797    if (multiplier < 0) {
798        multiplier = [[NSUserDefaults standardUserDefaults] floatForKey:@"NSScrollWheelMultiplier"];
799        if (multiplier <= 0)
800            multiplier = 1;
801    }
802    return multiplier;
803}
804
805void ScrollAnimatorMac::handleWheelEvent(PlatformWheelEvent& wheelEvent)
806{
807    m_haveScrolledSincePageLoad = true;
808
809    if (!wheelEvent.hasPreciseScrollingDeltas()) {
810        ScrollAnimator::handleWheelEvent(wheelEvent);
811        return;
812    }
813
814    // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
815    // up to the parent scrollable area. It takes advantage of the fact that
816    // the base class implemenatation of handleWheelEvent will not accept the
817    // wheel event if there is nowhere to scroll.
818    if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
819        if (!allowsVerticalStretching()) {
820            ScrollAnimator::handleWheelEvent(wheelEvent);
821            return;
822        }
823    } else {
824        if (!allowsHorizontalStretching()) {
825            ScrollAnimator::handleWheelEvent(wheelEvent);
826            return;
827        }
828    }
829
830    wheelEvent.accept();
831
832    bool isMometumScrollEvent = (wheelEvent.momentumPhase() != PlatformWheelEventPhaseNone);
833    if (m_ignoreMomentumScrolls && (isMometumScrollEvent || m_snapRubberBandTimer.isActive())) {
834        if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded)
835            m_ignoreMomentumScrolls = false;
836        return;
837    }
838
839    smoothScrollWithEvent(wheelEvent);
840}
841
842void ScrollAnimatorMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
843{
844    if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType)
845        beginScrollGesture();
846    else
847        endScrollGesture();
848}
849
850bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
851{
852    FloatSize limitDelta;
853    if (fabsf(deltaY) >= fabsf(deltaX)) {
854        if (deltaY < 0) {
855            // We are trying to scroll up.  Make sure we are not pinned to the top
856            limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
857        } else {
858            // We are trying to scroll down.  Make sure we are not pinned to the bottom
859            limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
860        }
861    } else if (deltaX != 0) {
862        if (deltaX < 0) {
863            // We are trying to scroll left.  Make sure we are not pinned to the left
864            limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
865        } else {
866            // We are trying to scroll right.  Make sure we are not pinned to the right
867            limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
868        }
869    }
870
871    if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
872        return true;
873    return false;
874}
875
876bool ScrollAnimatorMac::allowsVerticalStretching() const
877{
878    switch (m_scrollableArea->verticalScrollElasticity()) {
879    case ScrollElasticityAutomatic: {
880        Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
881        Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
882        return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
883    }
884    case ScrollElasticityNone:
885        return false;
886    case ScrollElasticityAllowed:
887        return true;
888    }
889
890    ASSERT_NOT_REACHED();
891    return false;
892}
893
894bool ScrollAnimatorMac::allowsHorizontalStretching() const
895{
896    switch (m_scrollableArea->horizontalScrollElasticity()) {
897    case ScrollElasticityAutomatic: {
898        Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
899        Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
900        return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
901    }
902    case ScrollElasticityNone:
903        return false;
904    case ScrollElasticityAllowed:
905        return true;
906    }
907
908    ASSERT_NOT_REACHED();
909    return false;
910}
911
912void ScrollAnimatorMac::smoothScrollWithEvent(PlatformWheelEvent& wheelEvent)
913{
914    m_haveScrolledSincePageLoad = true;
915
916    float deltaX = m_overflowScrollDelta.width();
917    float deltaY = m_overflowScrollDelta.height();
918
919    // Reset overflow values because we may decide to remove delta at various points and put it into overflow.
920    m_overflowScrollDelta = FloatSize();
921
922    float eventCoallescedDeltaX = -wheelEvent.deltaX();
923    float eventCoallescedDeltaY = -wheelEvent.deltaY();
924
925    deltaX += eventCoallescedDeltaX;
926    deltaY += eventCoallescedDeltaY;
927
928    // Slightly prefer scrolling vertically by applying the = case to deltaY
929    if (fabsf(deltaY) >= fabsf(deltaX))
930        deltaX = 0;
931    else
932        deltaY = 0;
933
934    bool isVerticallyStretched = false;
935    bool isHorizontallyStretched = false;
936    bool shouldStretch = false;
937
938    IntSize stretchAmount = m_scrollableArea->overhangAmount();
939
940    isHorizontallyStretched = stretchAmount.width();
941    isVerticallyStretched = stretchAmount.height();
942
943    PlatformWheelEventPhase phase = wheelEvent.momentumPhase();
944
945    // If we are starting momentum scrolling then do some setup.
946    if (!m_momentumScrollInProgress && (phase == PlatformWheelEventPhaseBegan || phase == PlatformWheelEventPhaseChanged))
947        m_momentumScrollInProgress = true;
948
949    CFTimeInterval timeDelta = wheelEvent.timestamp() - m_lastMomemtumScrollTimestamp;
950    if (m_inScrollGesture || m_momentumScrollInProgress) {
951        if (m_lastMomemtumScrollTimestamp && timeDelta > 0 && timeDelta < scrollVelocityZeroingTimeout) {
952            m_momentumVelocity.setWidth(eventCoallescedDeltaX / (float)timeDelta);
953            m_momentumVelocity.setHeight(eventCoallescedDeltaY / (float)timeDelta);
954            m_lastMomemtumScrollTimestamp = wheelEvent.timestamp();
955        } else {
956            m_lastMomemtumScrollTimestamp = wheelEvent.timestamp();
957            m_momentumVelocity = FloatSize();
958        }
959
960        if (isVerticallyStretched) {
961            if (!isHorizontallyStretched && pinnedInDirection(deltaX, 0)) {
962                // Stretching only in the vertical.
963                if (deltaY != 0 && (fabsf(deltaX / deltaY) < rubberbandDirectionLockStretchRatio))
964                    deltaX = 0;
965                else if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
966                    m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
967                    deltaX = 0;
968                } else
969                    m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
970            }
971        } else if (isHorizontallyStretched) {
972            // Stretching only in the horizontal.
973            if (pinnedInDirection(0, deltaY)) {
974                if (deltaX != 0 && (fabsf(deltaY / deltaX) < rubberbandDirectionLockStretchRatio))
975                    deltaY = 0;
976                else if (fabsf(deltaY) < rubberbandMinimumRequiredDeltaBeforeStretch) {
977                    m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
978                    deltaY = 0;
979                } else
980                    m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
981            }
982        } else {
983            // Not stretching at all yet.
984            if (pinnedInDirection(deltaX, deltaY)) {
985                if (fabsf(deltaY) >= fabsf(deltaX)) {
986                    if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
987                        m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
988                        deltaX = 0;
989                    } else
990                        m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
991                }
992                shouldStretch = true;
993            }
994        }
995    }
996
997    if (deltaX != 0 || deltaY != 0) {
998        if (!(shouldStretch || isVerticallyStretched || isHorizontallyStretched)) {
999            if (deltaY != 0) {
1000                deltaY *= scrollWheelMultiplier();
1001                immediateScrollByDeltaY(deltaY);
1002            }
1003            if (deltaX != 0) {
1004                deltaX *= scrollWheelMultiplier();
1005                immediateScrollByDeltaX(deltaX);
1006            }
1007        } else {
1008            if (!allowsHorizontalStretching()) {
1009                deltaX = 0;
1010                eventCoallescedDeltaX = 0;
1011            } else if ((deltaX != 0) && !isHorizontallyStretched && !pinnedInDirection(deltaX, 0)) {
1012                deltaX *= scrollWheelMultiplier();
1013
1014                m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1015                immediateScrollByDeltaX(deltaX);
1016                m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1017
1018                deltaX = 0;
1019            }
1020
1021            if (!allowsVerticalStretching()) {
1022                deltaY = 0;
1023                eventCoallescedDeltaY = 0;
1024            } else if ((deltaY != 0) && !isVerticallyStretched && !pinnedInDirection(0, deltaY)) {
1025                deltaY *= scrollWheelMultiplier();
1026
1027                m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1028                immediateScrollByDeltaY(deltaY);
1029                m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1030
1031                deltaY = 0;
1032            }
1033
1034            IntSize stretchAmount = m_scrollableArea->overhangAmount();
1035
1036            if (m_momentumScrollInProgress) {
1037                if ((pinnedInDirection(eventCoallescedDeltaX, eventCoallescedDeltaY) || (fabsf(eventCoallescedDeltaX) + fabsf(eventCoallescedDeltaY) <= 0)) && m_lastMomemtumScrollTimestamp) {
1038                    m_ignoreMomentumScrolls = true;
1039                    m_momentumScrollInProgress = false;
1040                    snapRubberBand();
1041                }
1042            }
1043
1044            m_stretchScrollForce.setWidth(m_stretchScrollForce.width() + deltaX);
1045            m_stretchScrollForce.setHeight(m_stretchScrollForce.height() + deltaY);
1046
1047            FloatSize dampedDelta(ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.width())), ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.height())));
1048            FloatPoint origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - stretchAmount;
1049            FloatPoint newOrigin = origOrigin + dampedDelta;
1050
1051            if (origOrigin != newOrigin) {
1052                m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1053                immediateScrollToPoint(newOrigin);
1054                m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1055            }
1056        }
1057    }
1058
1059    if (m_momentumScrollInProgress && phase == PlatformWheelEventPhaseEnded) {
1060        m_momentumScrollInProgress = false;
1061        m_ignoreMomentumScrolls = false;
1062        m_lastMomemtumScrollTimestamp = 0;
1063    }
1064}
1065
1066void ScrollAnimatorMac::beginScrollGesture()
1067{
1068    didBeginScrollGesture();
1069
1070    m_haveScrolledSincePageLoad = true;
1071    m_inScrollGesture = true;
1072    m_momentumScrollInProgress = false;
1073    m_ignoreMomentumScrolls = false;
1074    m_lastMomemtumScrollTimestamp = 0;
1075    m_momentumVelocity = FloatSize();
1076
1077    IntSize stretchAmount = m_scrollableArea->overhangAmount();
1078    m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(stretchAmount.width()));
1079    m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(stretchAmount.height()));
1080
1081    m_overflowScrollDelta = FloatSize();
1082
1083    if (m_snapRubberBandTimer.isActive())
1084        m_snapRubberBandTimer.stop();
1085}
1086
1087void ScrollAnimatorMac::endScrollGesture()
1088{
1089    didEndScrollGesture();
1090
1091    snapRubberBand();
1092}
1093
1094void ScrollAnimatorMac::snapRubberBand()
1095{
1096    CFTimeInterval timeDelta = [[NSProcessInfo processInfo] systemUptime] - m_lastMomemtumScrollTimestamp;
1097    if (m_lastMomemtumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout)
1098        m_momentumVelocity = FloatSize();
1099
1100    m_inScrollGesture = false;
1101
1102    if (m_snapRubberBandTimer.isActive())
1103        return;
1104
1105    m_startTime = [NSDate timeIntervalSinceReferenceDate];
1106    m_startStretch = FloatSize();
1107    m_origOrigin = FloatPoint();
1108    m_origVelocity = FloatSize();
1109
1110    m_snapRubberBandTimer.startRepeating(1.0/60.0);
1111}
1112
1113static inline float roundTowardZero(float num)
1114{
1115    return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f);
1116}
1117
1118static inline float roundToDevicePixelTowardZero(float num)
1119{
1120    float roundedNum = roundf(num);
1121    if (fabs(num - roundedNum) < 0.125)
1122        num = roundedNum;
1123
1124    return roundTowardZero(num);
1125}
1126
1127void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
1128{
1129    if (!m_momentumScrollInProgress || m_ignoreMomentumScrolls) {
1130        CFTimeInterval timeDelta = [NSDate timeIntervalSinceReferenceDate] - m_startTime;
1131
1132        if (m_startStretch == FloatSize()) {
1133            m_startStretch = m_scrollableArea->overhangAmount();
1134            if (m_startStretch == FloatSize()) {
1135                m_snapRubberBandTimer.stop();
1136                m_stretchScrollForce = FloatSize();
1137                m_startTime = 0;
1138                m_startStretch = FloatSize();
1139                m_origOrigin = FloatPoint();
1140                m_origVelocity = FloatSize();
1141
1142                return;
1143            }
1144
1145            m_origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - m_startStretch;
1146            m_origVelocity = m_momentumVelocity;
1147
1148            // Just like normal scrolling, prefer vertical rubberbanding
1149            if (fabsf(m_origVelocity.height()) >= fabsf(m_origVelocity.width()))
1150                m_origVelocity.setWidth(0);
1151
1152            // Don't rubber-band horizontally if it's not possible to scroll horizontally
1153            Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1154            if (!hScroller || !hScroller->enabled())
1155                m_origVelocity.setWidth(0);
1156
1157            // Don't rubber-band vertically if it's not possible to scroll horizontally
1158            Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1159            if (!vScroller || !vScroller->enabled())
1160                m_origVelocity.setHeight(0);
1161        }
1162
1163        FloatPoint delta(roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.width(), -m_origVelocity.width(), (float)timeDelta)),
1164                         roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.height(), -m_origVelocity.height(), (float)timeDelta)));
1165
1166        if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) {
1167            FloatPoint newOrigin = m_origOrigin + delta;
1168
1169            m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1170            immediateScrollToPoint(newOrigin);
1171            m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1172
1173            FloatSize newStretch = m_scrollableArea->overhangAmount();
1174
1175            m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(newStretch.width()));
1176            m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(newStretch.height()));
1177        } else {
1178            immediateScrollToPoint(m_origOrigin);
1179
1180            m_scrollableArea->didCompleteRubberBand(roundedIntSize(m_startStretch));
1181
1182            m_snapRubberBandTimer.stop();
1183            m_stretchScrollForce = FloatSize();
1184
1185            m_startTime = 0;
1186            m_startStretch = FloatSize();
1187            m_origOrigin = FloatPoint();
1188            m_origVelocity = FloatSize();
1189        }
1190    } else {
1191        m_startTime = [NSDate timeIntervalSinceReferenceDate];
1192        m_startStretch = FloatSize();
1193    }
1194}
1195#endif
1196
1197#if USE(WK_SCROLLBAR_PAINTER)
1198void ScrollAnimatorMac::startScrollbarPaintTimer()
1199{
1200    m_initialScrollbarPaintTimer.startOneShot(0.1);
1201}
1202
1203bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1204{
1205    return m_initialScrollbarPaintTimer.isActive();
1206}
1207
1208void ScrollAnimatorMac::stopScrollbarPaintTimer()
1209{
1210    m_initialScrollbarPaintTimer.stop();
1211}
1212
1213void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1214{
1215    wkScrollbarPainterForceFlashScrollers(m_scrollbarPainterController.get());
1216}
1217#endif
1218
1219void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1220{
1221    IntRect rectInViewCoordinates = scrollerThumb;
1222    if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1223        rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1224
1225    if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1226        return;
1227
1228    m_scrollableArea->setVisibleScrollerThumbRect(rectInViewCoordinates);
1229    m_visibleScrollerThumbRect = rectInViewCoordinates;
1230}
1231
1232} // namespace WebCore
1233
1234#endif // ENABLE(SMOOTH_SCROLLING)
1235