• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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    [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
477namespace WebCore {
478
479PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
480{
481    return adoptPtr(new ScrollAnimatorMac(scrollableArea));
482}
483
484ScrollAnimatorMac::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
510ScrollAnimatorMac::~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
520bool 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
546void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
547{
548    [m_scrollAnimationHelper.get() _stopRun];
549    immediateScrollToPoint(offset);
550}
551
552float 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
560float 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
568FloatPoint 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
579void 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
591void 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
602void 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
613void ScrollAnimatorMac::notityPositionChanged()
614{
615#if USE(WK_SCROLLBAR_PAINTER)
616    wkContentAreaScrolled(m_scrollbarPainterController.get());
617#endif
618    ScrollAnimator::notityPositionChanged();
619}
620
621void ScrollAnimatorMac::contentAreaWillPaint() const
622{
623#if USE(WK_SCROLLBAR_PAINTER)
624    wkContentAreaWillPaint(m_scrollbarPainterController.get());
625#endif
626}
627
628void ScrollAnimatorMac::mouseEnteredContentArea() const
629{
630#if USE(WK_SCROLLBAR_PAINTER)
631    wkMouseEnteredContentArea(m_scrollbarPainterController.get());
632#endif
633}
634
635void ScrollAnimatorMac::mouseExitedContentArea() const
636{
637#if USE(WK_SCROLLBAR_PAINTER)
638    wkMouseExitedContentArea(m_scrollbarPainterController.get());
639#endif
640}
641
642void ScrollAnimatorMac::mouseMovedInContentArea() const
643{
644#if USE(WK_SCROLLBAR_PAINTER)
645    wkMouseMovedInContentArea(m_scrollbarPainterController.get());
646#endif
647}
648
649void ScrollAnimatorMac::willStartLiveResize()
650{
651#if USE(WK_SCROLLBAR_PAINTER)
652    wkWillStartLiveResize(m_scrollbarPainterController.get());
653#endif
654}
655
656void ScrollAnimatorMac::contentsResized() const
657{
658#if USE(WK_SCROLLBAR_PAINTER)
659    wkContentAreaResized(m_scrollbarPainterController.get());
660#endif
661}
662
663void ScrollAnimatorMac::willEndLiveResize()
664{
665#if USE(WK_SCROLLBAR_PAINTER)
666    wkWillEndLiveResize(m_scrollbarPainterController.get());
667#endif
668}
669
670void ScrollAnimatorMac::contentAreaDidShow() const
671{
672#if USE(WK_SCROLLBAR_PAINTER)
673    wkContentAreaDidShow(m_scrollbarPainterController.get());
674#endif
675}
676
677void ScrollAnimatorMac::contentAreaDidHide() const
678{
679#if USE(WK_SCROLLBAR_PAINTER)
680    wkContentAreaDidHide(m_scrollbarPainterController.get());
681#endif
682}
683
684void ScrollAnimatorMac::didBeginScrollGesture() const
685{
686#if USE(WK_SCROLLBAR_PAINTER)
687    wkDidBeginScrollGesture(m_scrollbarPainterController.get());
688#endif
689}
690
691void ScrollAnimatorMac::didEndScrollGesture() const
692{
693#if USE(WK_SCROLLBAR_PAINTER)
694    wkDidEndScrollGesture(m_scrollbarPainterController.get());
695#endif
696}
697
698void 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
711void 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
722void 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
735void 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
746void 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
759static const float scrollVelocityZeroingTimeout = 0.10f;
760static const float rubberbandStiffness = 20;
761static const float rubberbandDirectionLockStretchRatio = 1;
762static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
763static const float rubberbandAmplitude = 0.31f;
764static const float rubberbandPeriod = 1.6f;
765
766static 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
775static float elasticDeltaForReboundDelta(float delta)
776{
777    float stiffness = std::max(rubberbandStiffness, 1.0f);
778    return delta / stiffness;
779}
780
781static float reboundDeltaForElasticDelta(float delta)
782{
783    return delta * rubberbandStiffness;
784}
785
786static 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
797void 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
834void ScrollAnimatorMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
835{
836    if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType)
837        beginScrollGesture();
838    else
839        endScrollGesture();
840}
841
842bool 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
868bool 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
886bool 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
904void 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
1058void 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
1079void ScrollAnimatorMac::endScrollGesture()
1080{
1081    didEndScrollGesture();
1082
1083    snapRubberBand();
1084}
1085
1086void 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
1105static inline float roundTowardZero(float num)
1106{
1107    return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f);
1108}
1109
1110static 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
1119void 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)
1190void ScrollAnimatorMac::startScrollbarPaintTimer()
1191{
1192    m_initialScrollbarPaintTimer.startOneShot(0.1);
1193}
1194
1195bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1196{
1197    return m_initialScrollbarPaintTimer.isActive();
1198}
1199
1200void ScrollAnimatorMac::stopScrollbarPaintTimer()
1201{
1202    m_initialScrollbarPaintTimer.stop();
1203}
1204
1205void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1206{
1207    wkScrollbarPainterForceFlashScrollers(m_scrollbarPainterController.get());
1208}
1209#endif
1210
1211} // namespace WebCore
1212
1213#endif // ENABLE(SMOOTH_SCROLLING)
1214