tizen beta release
[profile/ivi/webkit-efl.git] / Source / WebCore / platform / mac / ScrollAnimatorMac.mm
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 "BlockExceptions.h"
33 #include "FloatPoint.h"
34 #include "NSScrollerImpDetails.h"
35 #include "PlatformGestureEvent.h"
36 #include "PlatformWheelEvent.h"
37 #include "ScrollView.h"
38 #include "ScrollableArea.h"
39 #include "ScrollbarTheme.h"
40 #include "ScrollbarThemeMac.h"
41 #include "WebCoreSystemInterface.h"
42 #include <wtf/PassOwnPtr.h>
43 #include <wtf/UnusedParam.h>
44
45 using namespace WebCore;
46 using namespace std;
47
48 #if USE(SCROLLBAR_PAINTER)
49 static bool supportsUIStateTransitionProgress()
50 {
51     // FIXME: This is temporary until all platforms that support ScrollbarPainter support this part of the API.
52     static bool globalSupportsUIStateTransitionProgress = [NSClassFromString(@"NSScrollerImp") instancesRespondToSelector:@selector(mouseEnteredScroller)];
53     return globalSupportsUIStateTransitionProgress;
54 }
55 #endif
56
57 @interface NSObject (ScrollAnimationHelperDetails)
58 - (id)initWithDelegate:(id)delegate;
59 - (void)_stopRun;
60 - (BOOL)_isAnimating;
61 - (NSPoint)targetOrigin;
62 - (CGFloat)_progress;
63 @end
64
65 @interface WebScrollAnimationHelperDelegate : NSObject
66 {
67     WebCore::ScrollAnimatorMac* _animator;
68 }
69 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
70 @end
71
72 static NSSize abs(NSSize size)
73 {
74     NSSize finalSize = size;
75     if (finalSize.width < 0)
76         finalSize.width = -finalSize.width;
77     if (finalSize.height < 0)
78         finalSize.height = -finalSize.height;
79     return finalSize;    
80 }
81
82 @implementation WebScrollAnimationHelperDelegate
83
84 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
85 {
86     self = [super init];
87     if (!self)
88         return nil;
89
90     _animator = scrollAnimator;
91     return self;
92 }
93
94 - (void)scrollAnimatorDestroyed
95 {
96     _animator = 0;
97 }
98
99 - (NSRect)bounds
100 {
101     if (!_animator)
102         return NSZeroRect;
103
104     WebCore::FloatPoint currentPosition = _animator->currentPosition();
105     return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
106 }
107
108 - (void)_immediateScrollToPoint:(NSPoint)newPosition
109 {
110     if (!_animator)
111         return;
112     _animator->immediateScrollToPointForScrollAnimation(newPosition);
113 }
114
115 - (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin
116 {
117     return newOrigin;
118 }
119
120 - (NSSize)convertSizeToBase:(NSSize)size
121 {
122     return abs(size);
123 }
124
125 - (NSSize)convertSizeFromBase:(NSSize)size
126 {
127     return abs(size);
128 }
129
130 - (NSSize)convertSizeToBacking:(NSSize)size
131 {
132     return abs(size);
133 }
134
135 - (NSSize)convertSizeFromBacking:(NSSize)size
136 {
137     return abs(size);
138 }
139
140 - (id)superview
141 {
142     return nil;
143 }
144
145 - (id)documentView
146 {
147     return nil;
148 }
149
150 - (id)window
151 {
152     return nil;
153 }
154
155 - (void)_recursiveRecomputeToolTips
156 {
157 }
158
159 @end
160
161 #if USE(SCROLLBAR_PAINTER)
162
163 @interface WebScrollbarPainterControllerDelegate : NSObject
164 {
165     WebCore::ScrollAnimatorMac* _animator;
166 }
167 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
168 @end
169
170 @implementation WebScrollbarPainterControllerDelegate
171
172 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
173 {
174     self = [super init];
175     if (!self)
176         return nil;
177     
178     _animator = scrollAnimator;
179     return self;
180 }
181
182 - (void)scrollAnimatorDestroyed
183 {
184     _animator = 0;
185 }
186
187 - (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
188 {
189     UNUSED_PARAM(scrollerImpPair);
190     if (!_animator)
191         return NSZeroRect;
192
193     WebCore::IntSize contentsSize = _animator->scrollableArea()->contentsSize();
194     return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
195 }
196
197 - (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
198 {
199     UNUSED_PARAM(scrollerImpPair);
200     if (!_animator)
201         return NO;
202
203     return _animator->scrollableArea()->inLiveResize();
204 }
205
206 - (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
207 {
208     UNUSED_PARAM(scrollerImpPair);
209     if (!_animator)
210         return NSZeroPoint;
211
212     return _animator->scrollableArea()->currentMousePosition();
213 }
214
215 - (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
216 {
217     UNUSED_PARAM(scrollerImpPair);
218     if (!_animator)
219         return NSZeroPoint;
220
221     WebCore::Scrollbar* scrollbar = 0;
222     if ([scrollerImp isHorizontal])
223         scrollbar = _animator->scrollableArea()->horizontalScrollbar();
224     else 
225         scrollbar = _animator->scrollableArea()->verticalScrollbar();
226
227     // It is possible to have a null scrollbar here since it is possible for this delegate
228     // method to be called between the moment when a scrollbar has been set to 0 and the
229     // moment when its destructor has been called. We should probably de-couple some
230     // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this
231     // issue.
232     if (!scrollbar)
233         return WebCore::IntPoint();
234     
235     return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea));
236 }
237
238 - (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
239 {
240     UNUSED_PARAM(scrollerImpPair);
241     UNUSED_PARAM(rect);
242 }
243
244 - (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
245 {
246     if (!_animator)
247         return;
248
249     [scrollerImpPair setScrollerStyle:newRecommendedScrollerStyle];
250     _animator->updateScrollerStyle();
251 }
252
253 @end
254
255 enum FeatureToAnimate {
256     ThumbAlpha,
257     TrackAlpha,
258     UIStateTransition
259 };
260
261 @interface WebScrollbarPartAnimation : NSAnimation
262 {
263     RetainPtr<ScrollbarPainter> _scrollerPainter;
264     FeatureToAnimate _featureToAnimate;
265     ScrollAnimatorMac* _animator;
266     CGFloat _startValue;
267     CGFloat _endValue;
268 }
269 - (id)initWithScrollbarPainter:(ScrollbarPainter)scrollerPainter animate:(FeatureToAnimate)featureToAnimate scrollAnimator:(ScrollAnimatorMac*)scrollAnimator animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration;
270 @end
271
272 @implementation WebScrollbarPartAnimation
273
274 - (id)initWithScrollbarPainter:(ScrollbarPainter)scrollerPainter animate:(FeatureToAnimate)featureToAnimate scrollAnimator:(ScrollAnimatorMac*)scrollAnimator animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration
275 {
276     self = [super initWithDuration:duration animationCurve:NSAnimationEaseInOut];
277     if (!self)
278         return nil;
279     
280     _scrollerPainter = scrollerPainter;
281     _featureToAnimate = featureToAnimate;
282     _animator = scrollAnimator;
283     _startValue = startValue;
284     _endValue = endValue;
285     
286     return self;    
287 }
288
289 - (void)setScrollbarPainter:(ScrollbarPainter)scrollerPainter
290 {
291     _scrollerPainter = scrollerPainter;
292 }
293
294 - (void)setStartValue:(CGFloat)startValue
295 {
296     _startValue = startValue;
297 }
298
299 - (void)setEndValue:(CGFloat)endValue
300 {
301     _endValue = endValue;
302 }
303
304 - (void)setCurrentProgress:(NSAnimationProgress)progress
305 {
306     [super setCurrentProgress:progress];
307
308     if (!_animator)
309         return;
310
311     CGFloat currentValue;
312     if (_startValue > _endValue)
313         currentValue = 1 - progress;
314     else
315         currentValue = progress;
316
317     switch (_featureToAnimate) {
318     case ThumbAlpha:
319         [_scrollerPainter.get() setKnobAlpha:currentValue];
320         break;
321     case TrackAlpha:
322         [_scrollerPainter.get() setTrackAlpha:currentValue];
323         break;
324     case UIStateTransition:
325         [_scrollerPainter.get() setUiStateTransitionProgress:currentValue];
326         break;
327     }
328
329     // Invalidate the scrollbars so that they paint the animation
330     if (Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar())
331         verticalScrollbar->invalidate();
332     if (Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar())
333         horizontalScrollbar->invalidate();
334 }
335
336 - (void)scrollAnimatorDestroyed
337 {
338     BEGIN_BLOCK_OBJC_EXCEPTIONS;
339     [self stopAnimation];
340     END_BLOCK_OBJC_EXCEPTIONS;
341     _animator = 0;
342 }
343
344 @end
345
346 @interface WebScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
347 {
348     WebCore::ScrollAnimatorMac* _animator;
349
350     RetainPtr<WebScrollbarPartAnimation> _verticalKnobAnimation;
351     RetainPtr<WebScrollbarPartAnimation> _horizontalKnobAnimation;
352
353     RetainPtr<WebScrollbarPartAnimation> _verticalTrackAnimation;
354     RetainPtr<WebScrollbarPartAnimation> _horizontalTrackAnimation;
355
356     RetainPtr<WebScrollbarPartAnimation> _verticalUIStateTransitionAnimation;
357     RetainPtr<WebScrollbarPartAnimation> _horizontalUIStateTransitionAnimation;
358 }
359 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
360 - (void)cancelAnimations;
361 @end
362
363 @implementation WebScrollbarPainterDelegate
364
365 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
366 {
367     self = [super init];
368     if (!self)
369         return nil;
370     
371     _animator = scrollAnimator;
372     return self;
373 }
374
375 - (void)cancelAnimations
376 {
377     BEGIN_BLOCK_OBJC_EXCEPTIONS;
378     [_verticalKnobAnimation.get() stopAnimation];
379     [_horizontalKnobAnimation.get() stopAnimation];
380     [_verticalTrackAnimation.get() stopAnimation];
381     [_horizontalTrackAnimation.get() stopAnimation];
382     [_verticalUIStateTransitionAnimation.get() stopAnimation];
383     [_horizontalUIStateTransitionAnimation.get() stopAnimation];
384     END_BLOCK_OBJC_EXCEPTIONS;
385 }
386
387 - (NSRect)convertRectToBacking:(NSRect)aRect
388 {
389     return aRect;
390 }
391
392 - (NSRect)convertRectFromBacking:(NSRect)aRect
393 {
394     return aRect;
395 }
396
397 - (CALayer *)layer
398 {
399     if (!_animator)
400         return nil;
401     if (!_animator->isDrawingIntoLayer())
402         return nil;
403
404     // FIXME: This should attempt to return an actual layer.
405     static CALayer *dummyLayer = [[CALayer alloc] init];
406     return dummyLayer;
407 }
408
409 - (NSPoint)mouseLocationInScrollerForScrollerImp:(id)scrollerImp
410 {
411     if (!_animator)
412         return NSZeroPoint;
413
414     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
415     Scrollbar* scrollbar;
416     if ([scrollerPainter isHorizontal])
417         scrollbar = _animator->scrollableArea()->horizontalScrollbar();
418     else 
419         scrollbar = _animator->scrollableArea()->verticalScrollbar();
420
421     if (!scrollbar)
422         return NSZeroPoint;
423
424     return scrollbar->convertFromContainingView(_animator->scrollableArea()->currentMousePosition());
425 }
426
427 - (void)setUpAlphaAnimation:(RetainPtr<WebScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(ScrollbarPainter)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
428 {
429     // If the user has scrolled the page, then the scrollbars must be animated here. 
430     // This overrides the early returns.
431     bool mustAnimate = _animator->haveScrolledSincePageLoad();
432
433     if (_animator->scrollbarPaintTimerIsActive() && !mustAnimate)
434         return;
435
436     if (_animator->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
437         _animator->startScrollbarPaintTimer();
438         return;
439     }
440
441     // At this point, we are definitely going to animate now, so stop the timer.
442     _animator->stopScrollbarPaintTimer();
443
444     // If we are currently animating, stop
445     if (scrollbarPartAnimation) {
446         [scrollbarPartAnimation.get() stopAnimation];
447         scrollbarPartAnimation = nil;
448     }
449
450     if (part == WebCore::ThumbPart && ![scrollerPainter isHorizontal]) {
451         if (newAlpha == 1) {
452             IntRect thumbRect = IntRect([scrollerPainter rectForPart:NSScrollerKnob]);
453             _animator->setVisibleScrollerThumbRect(thumbRect);
454         } else
455             _animator->setVisibleScrollerThumbRect(IntRect());
456     }
457
458     [NSAnimationContext beginGrouping];
459     [[NSAnimationContext currentContext] setDuration:duration];
460     scrollbarPartAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter 
461                                                                     animate:part == ThumbPart ? ThumbAlpha : TrackAlpha
462                                                                     scrollAnimator:_animator
463                                                                     animateFrom:part == ThumbPart ? [scrollerPainter knobAlpha] : [scrollerPainter trackAlpha]
464                                                                     animateTo:newAlpha 
465                                                                     duration:duration]);
466     [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
467     [scrollbarPartAnimation.get() startAnimation];
468     [NSAnimationContext endGrouping];
469 }
470
471 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
472 {
473     if (!_animator)
474         return;
475
476     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
477     if ([scrollerImp isHorizontal])
478         [self setUpAlphaAnimation:_horizontalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
479     else
480         [self setUpAlphaAnimation:_verticalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
481 }
482
483 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
484 {
485     if (!_animator)
486         return;
487
488     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
489     if ([scrollerImp isHorizontal])
490         [self setUpAlphaAnimation:_horizontalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
491     else
492         [self setUpAlphaAnimation:_verticalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
493 }
494
495 - (void)scrollerImp:(id)scrollerImp animateUIStateTransitionWithDuration:(NSTimeInterval)duration
496 {
497     if (!_animator)
498         return;
499
500     if (!supportsUIStateTransitionProgress())
501         return;
502
503     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
504     RetainPtr<WebScrollbarPartAnimation> scrollbarPartAnimation = [scrollerPainter isHorizontal] ? _horizontalUIStateTransitionAnimation : _verticalUIStateTransitionAnimation;
505
506     // UIStateTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
507     [scrollerPainter setUiStateTransitionProgress:1 - [scrollerPainter uiStateTransitionProgress]];
508
509     [NSAnimationContext beginGrouping];
510     [[NSAnimationContext currentContext] setDuration:duration];
511     if (!scrollbarPartAnimation) {
512         scrollbarPartAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter 
513                                                                     animate:UIStateTransition
514                                                                     scrollAnimator:_animator
515                                                                     animateFrom:[scrollerPainter uiStateTransitionProgress]
516                                                                     animateTo:1.0 
517                                                                     duration:duration]);
518         [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
519     } else {
520         // If we don't need to initialize the animation, just reset the values in case they have changed.
521         [scrollbarPartAnimation.get() setScrollbarPainter:scrollerPainter];
522         [scrollbarPartAnimation.get() setStartValue:[scrollerPainter uiStateTransitionProgress]];
523         [scrollbarPartAnimation.get() setEndValue:1.0];
524         [scrollbarPartAnimation.get() setDuration:duration];
525     }
526     [scrollbarPartAnimation.get() startAnimation];
527     [NSAnimationContext endGrouping];
528 }
529
530 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
531 {
532     UNUSED_PARAM(scrollerImp);
533     UNUSED_PARAM(newOverlayScrollerState);
534 }
535
536 - (void)scrollAnimatorDestroyed
537 {
538     _animator = 0;
539     BEGIN_BLOCK_OBJC_EXCEPTIONS;
540     [_verticalKnobAnimation.get() scrollAnimatorDestroyed];
541     [_horizontalKnobAnimation.get() scrollAnimatorDestroyed];
542     [_verticalTrackAnimation.get() scrollAnimatorDestroyed];
543     [_horizontalTrackAnimation.get() scrollAnimatorDestroyed];
544     [_verticalUIStateTransitionAnimation.get() scrollAnimatorDestroyed];
545     [_horizontalUIStateTransitionAnimation.get() scrollAnimatorDestroyed];
546     END_BLOCK_OBJC_EXCEPTIONS;
547 }
548
549 @end
550
551 #endif // USE(SCROLLBAR_PAINTER)
552
553 namespace WebCore {
554
555 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
556 {
557     return adoptPtr(new ScrollAnimatorMac(scrollableArea));
558 }
559
560 #if USE(SCROLLBAR_PAINTER)
561 static ScrollbarThemeMac* macScrollbarTheme()
562 {
563     ScrollbarTheme* scrollbarTheme = ScrollbarTheme::theme();
564     return !scrollbarTheme->isMockTheme() ? static_cast<ScrollbarThemeMac*>(scrollbarTheme) : 0;
565 }
566 #endif
567
568 ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
569     : ScrollAnimator(scrollableArea)
570 #if USE(SCROLLBAR_PAINTER)
571     , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
572 #endif
573 #if ENABLE(RUBBER_BANDING)
574     , m_scrollElasticityController(this)
575     , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
576 #endif
577     , m_drawingIntoLayer(false)
578     , m_haveScrolledSincePageLoad(false)
579     , m_needsScrollerStyleUpdate(false)
580 {
581     m_scrollAnimationHelperDelegate.adoptNS([[WebScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
582     m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
583
584 #if USE(SCROLLBAR_PAINTER)
585     m_scrollbarPainterControllerDelegate.adoptNS([[WebScrollbarPainterControllerDelegate alloc] initWithScrollAnimator:this]);
586     m_scrollbarPainterController = [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease];
587     [m_scrollbarPainterController.get() setDelegate:m_scrollbarPainterControllerDelegate.get()];
588     [m_scrollbarPainterController.get() setScrollerStyle:wkRecommendedScrollerStyle()];
589
590     m_scrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollAnimator:this]);
591 #endif
592 }
593
594 ScrollAnimatorMac::~ScrollAnimatorMac()
595 {
596 #if USE(SCROLLBAR_PAINTER)
597     BEGIN_BLOCK_OBJC_EXCEPTIONS;
598     [m_scrollbarPainterControllerDelegate.get() scrollAnimatorDestroyed];
599     [m_scrollbarPainterController.get() setDelegate:nil];
600     [m_scrollbarPainterDelegate.get() scrollAnimatorDestroyed];
601     [m_scrollAnimationHelperDelegate.get() scrollAnimatorDestroyed];
602     END_BLOCK_OBJC_EXCEPTIONS;
603 #endif
604 }
605
606 bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
607 {
608     m_haveScrolledSincePageLoad = true;
609
610     if (![[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollAnimationEnabled"] || !m_scrollableArea->scrollAnimatorEnabled())
611         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
612
613     if (granularity == ScrollByPixel)
614         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
615
616     float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
617     float newPos = std::max<float>(std::min<float>(currentPos + (step * multiplier), static_cast<float>(m_scrollableArea->scrollSize(orientation))), 0);
618     if (currentPos == newPos)
619         return false;
620
621     NSPoint newPoint;
622     if ([m_scrollAnimationHelper.get() _isAnimating]) {
623         NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
624         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
625     } else {
626         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
627         m_scrollableArea->didStartAnimatedScroll();
628     }
629
630     [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
631     return true;
632 }
633
634 void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
635 {
636     [m_scrollAnimationHelper.get() _stopRun];
637     immediateScrollToPoint(offset);
638 }
639
640 float ScrollAnimatorMac::adjustScrollXPositionIfNecessary(float position) const
641 {
642     if (!m_scrollableArea->constrainsScrollingToContentEdge())
643         return position;
644
645     return max<float>(min<float>(position, m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
646 }
647
648 float ScrollAnimatorMac::adjustScrollYPositionIfNecessary(float position) const
649 {
650     if (!m_scrollableArea->constrainsScrollingToContentEdge())
651         return position;
652
653     return max<float>(min<float>(position, m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
654 }
655
656 FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
657 {
658     if (!m_scrollableArea->constrainsScrollingToContentEdge())
659         return position;
660
661     float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
662     float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
663
664     return FloatPoint(newX, newY);
665 }
666
667 void ScrollAnimatorMac::immediateScrollToPoint(const FloatPoint& newPosition)
668 {
669     FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
670  
671     m_currentPosX = adjustedPosition.x();
672     m_currentPosY = adjustedPosition.y();
673     notifyPositionChanged();
674 }
675
676 void ScrollAnimatorMac::immediateScrollByDeltaX(float deltaX)
677 {
678     float newPosX = adjustScrollXPositionIfNecessary(m_currentPosX + deltaX);
679     
680     if (newPosX == m_currentPosX)
681         return;
682     
683     m_currentPosX = newPosX;
684     notifyPositionChanged();
685 }
686
687 void ScrollAnimatorMac::immediateScrollByDeltaY(float deltaY)
688 {
689     float newPosY = adjustScrollYPositionIfNecessary(m_currentPosY + deltaY);
690     
691     if (newPosY == m_currentPosY)
692         return;
693     
694     m_currentPosY = newPosY;
695     notifyPositionChanged();
696 }
697
698 void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition)
699 {
700     ASSERT(m_scrollAnimationHelper);
701     CGFloat progress = [m_scrollAnimationHelper.get() _progress];
702     
703     immediateScrollToPoint(newPosition);
704
705     if (progress >= 1.0)
706         m_scrollableArea->didCompleteAnimatedScroll();
707 }
708
709 void ScrollAnimatorMac::notifyPositionChanged()
710 {
711 #if USE(SCROLLBAR_PAINTER)
712     // This function is called when a page is going into the page cache, but the page 
713     // isn't really scrolling in that case. We should only pass the message on to the
714     // ScrollbarPainterController when we're really scrolling on an active page.
715     if (scrollableArea()->isOnActivePage())
716         [m_scrollbarPainterController.get() contentAreaScrolled];
717 #endif
718     ScrollAnimator::notifyPositionChanged();
719 }
720
721 void ScrollAnimatorMac::contentAreaWillPaint() const
722 {
723     if (!scrollableArea()->isOnActivePage())
724         return;
725 #if USE(SCROLLBAR_PAINTER)
726     [m_scrollbarPainterController.get() contentAreaWillDraw];
727 #endif
728 }
729
730 void ScrollAnimatorMac::mouseEnteredContentArea() const
731 {
732     if (!scrollableArea()->isOnActivePage())
733         return;
734 #if USE(SCROLLBAR_PAINTER)
735     [m_scrollbarPainterController.get() mouseEnteredContentArea];
736 #endif
737 }
738
739 void ScrollAnimatorMac::mouseExitedContentArea() const
740 {
741     if (!scrollableArea()->isOnActivePage())
742         return;
743 #if USE(SCROLLBAR_PAINTER)
744     [m_scrollbarPainterController.get() mouseExitedContentArea];
745 #endif
746 }
747
748 void ScrollAnimatorMac::mouseMovedInContentArea() const
749 {
750     if (!scrollableArea()->isOnActivePage())
751         return;
752 #if USE(SCROLLBAR_PAINTER)
753     [m_scrollbarPainterController.get() mouseMovedInContentArea];
754 #endif
755 }
756
757 void ScrollAnimatorMac::mouseEnteredScrollbar(Scrollbar* scrollbar) const
758 {
759     if (!scrollableArea()->isOnActivePage())
760         return;
761 #if USE(SCROLLBAR_PAINTER)
762     if (!supportsUIStateTransitionProgress())
763         return;
764     if (ScrollbarThemeMac* theme = macScrollbarTheme()) {
765         ScrollbarPainter painter = theme->painterForScrollbar(scrollbar);
766         [painter mouseEnteredScroller];
767     }
768 #else
769     UNUSED_PARAM(scrollbar);
770 #endif
771 }
772
773 void ScrollAnimatorMac::mouseExitedScrollbar(Scrollbar* scrollbar) const
774 {
775     if (!scrollableArea()->isOnActivePage())
776         return;
777 #if USE(SCROLLBAR_PAINTER)
778     if (!supportsUIStateTransitionProgress())
779         return;
780     if (ScrollbarThemeMac* theme = macScrollbarTheme()) {
781         ScrollbarPainter painter = theme->painterForScrollbar(scrollbar);
782         [painter mouseExitedScroller];
783     }
784 #else
785     UNUSED_PARAM(scrollbar);
786 #endif
787 }
788
789 void ScrollAnimatorMac::willStartLiveResize()
790 {
791     if (!scrollableArea()->isOnActivePage())
792         return;
793 #if USE(SCROLLBAR_PAINTER)
794     [m_scrollbarPainterController.get() startLiveResize];
795 #endif
796 }
797
798 void ScrollAnimatorMac::contentsResized() const
799 {
800     if (!scrollableArea()->isOnActivePage())
801         return;
802 #if USE(SCROLLBAR_PAINTER)
803     [m_scrollbarPainterController.get() contentAreaDidResize];
804 #endif
805 }
806
807 void ScrollAnimatorMac::willEndLiveResize()
808 {
809     if (!scrollableArea()->isOnActivePage())
810         return;
811 #if USE(SCROLLBAR_PAINTER)
812     [m_scrollbarPainterController.get() endLiveResize];
813 #endif
814 }
815
816 void ScrollAnimatorMac::contentAreaDidShow() const
817 {
818     if (!scrollableArea()->isOnActivePage())
819         return;
820 #if USE(SCROLLBAR_PAINTER)
821     [m_scrollbarPainterController.get() windowOrderedIn];
822 #endif
823 }
824
825 void ScrollAnimatorMac::contentAreaDidHide() const
826 {
827     if (!scrollableArea()->isOnActivePage())
828         return;
829 #if USE(SCROLLBAR_PAINTER)
830     [m_scrollbarPainterController.get() windowOrderedOut];
831 #endif
832 }
833
834 void ScrollAnimatorMac::didBeginScrollGesture() const
835 {
836     if (!scrollableArea()->isOnActivePage())
837         return;
838 #if USE(SCROLLBAR_PAINTER)
839     [m_scrollbarPainterController.get() beginScrollGesture];
840 #endif
841 }
842
843 void ScrollAnimatorMac::didEndScrollGesture() const
844 {
845     if (!scrollableArea()->isOnActivePage())
846         return;
847 #if USE(SCROLLBAR_PAINTER)
848     [m_scrollbarPainterController.get() endScrollGesture];
849 #endif
850 }
851
852 void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
853 {
854 #if USE(SCROLLBAR_PAINTER)
855     if (ScrollbarThemeMac* theme = macScrollbarTheme()) {
856         ScrollbarPainter painter = theme->painterForScrollbar(scrollbar);
857         [painter setDelegate:m_scrollbarPainterDelegate.get()];
858         [m_scrollbarPainterController.get() setVerticalScrollerImp:painter];
859         if (scrollableArea()->inLiveResize())
860             [painter setKnobAlpha:1];
861     }
862 #else
863     UNUSED_PARAM(scrollbar);
864 #endif
865 }
866
867 void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
868 {
869 #if USE(SCROLLBAR_PAINTER)
870     if (ScrollbarThemeMac* theme = macScrollbarTheme()) {
871         ScrollbarPainter painter = theme->painterForScrollbar(scrollbar);
872         [painter setDelegate:nil];
873         [m_scrollbarPainterController.get() setVerticalScrollerImp:nil];
874     }
875 #else
876     UNUSED_PARAM(scrollbar);
877 #endif
878 }
879
880 void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
881 {
882 #if USE(SCROLLBAR_PAINTER)
883     if (ScrollbarThemeMac* theme = macScrollbarTheme()) {
884         ScrollbarPainter painter = theme->painterForScrollbar(scrollbar);
885         [painter setDelegate:m_scrollbarPainterDelegate.get()];
886         [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter];
887         if (scrollableArea()->inLiveResize())
888             [painter setKnobAlpha:1];
889     }
890 #else
891     UNUSED_PARAM(scrollbar);
892 #endif
893 }
894
895 void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
896 {
897 #if USE(SCROLLBAR_PAINTER)
898     if (ScrollbarThemeMac* theme = macScrollbarTheme()) {
899         ScrollbarPainter painter = theme->painterForScrollbar(scrollbar);
900         [painter setDelegate:nil];
901         [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil];
902     }
903 #else
904     UNUSED_PARAM(scrollbar);
905 #endif
906 }
907
908 void ScrollAnimatorMac::cancelAnimations()
909 {
910     m_haveScrolledSincePageLoad = false;
911
912 #if USE(SCROLLBAR_PAINTER)
913     if (scrollbarPaintTimerIsActive())
914         stopScrollbarPaintTimer();
915     [m_scrollbarPainterDelegate.get() cancelAnimations];
916 #endif
917 }
918
919 #if ENABLE(RUBBER_BANDING)
920
921 static const float scrollVelocityZeroingTimeout = 0.10f;
922 static const float rubberbandStiffness = 20;
923 static const float rubberbandDirectionLockStretchRatio = 1;
924 static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
925 static const float rubberbandAmplitude = 0.31f;
926 static const float rubberbandPeriod = 1.6f;
927
928 static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime)
929 {
930     float amplitude = rubberbandAmplitude;
931     float period = rubberbandPeriod;
932     float criticalDampeningFactor = expf((-elapsedTime * rubberbandStiffness) / period);
933              
934     return (initialPosition + (-initialVelocity * elapsedTime * amplitude)) * criticalDampeningFactor;
935 }
936
937 static float elasticDeltaForReboundDelta(float delta)
938 {
939     float stiffness = std::max(rubberbandStiffness, 1.0f);
940     return delta / stiffness;
941 }
942
943 static float reboundDeltaForElasticDelta(float delta)
944 {
945     return delta * rubberbandStiffness;
946 }
947
948 static float scrollWheelMultiplier()
949 {
950     static float multiplier = -1;
951     if (multiplier < 0) {
952         multiplier = [[NSUserDefaults standardUserDefaults] floatForKey:@"NSScrollWheelMultiplier"];
953         if (multiplier <= 0)
954             multiplier = 1;
955     }
956     return multiplier;
957 }
958
959 static inline bool isScrollingLeftAndShouldNotRubberBand(const PlatformWheelEvent& wheelEvent, ScrollableArea* scrollableArea)
960 {
961     return wheelEvent.deltaX() > 0 && !scrollableArea->shouldRubberBandInDirection(ScrollLeft);
962 }
963
964 static inline bool isScrollingRightAndShouldNotRubberBand(const PlatformWheelEvent& wheelEvent, ScrollableArea* scrollableArea)
965 {
966     return wheelEvent.deltaX() < 0 && !scrollableArea->shouldRubberBandInDirection(ScrollRight);
967 }
968
969 bool ScrollAnimatorMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
970 {
971     m_haveScrolledSincePageLoad = true;
972
973     if (!wheelEvent.hasPreciseScrollingDeltas())
974         return ScrollAnimator::handleWheelEvent(wheelEvent);
975
976     // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
977     // up to the parent scrollable area. It takes advantage of the fact that
978     // the base class implementation of handleWheelEvent will not accept the
979     // wheel event if there is nowhere to scroll.
980     if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
981         if (!allowsVerticalStretching())
982             return ScrollAnimator::handleWheelEvent(wheelEvent);
983     } else {
984         if (!allowsHorizontalStretching())
985             return ScrollAnimator::handleWheelEvent(wheelEvent);
986         
987         if (m_scrollableArea->horizontalScrollbar()) {
988             // If there is a scrollbar, we aggregate the wheel events to get an
989             // overall trend of the scroll. If the direction of the scroll is ever
990             // in the opposite direction of the pin location, then we switch the
991             // boolean, and rubber band. That is, if we were pinned to the left,
992             // and we ended up scrolling to the right, we rubber band.
993             m_scrollElasticityController.m_cumulativeHorizontalScroll += wheelEvent.deltaX();
994             if (m_scrollElasticityController.m_scrollerInitiallyPinnedOnLeft && m_scrollElasticityController.m_cumulativeHorizontalScroll < 0)
995                 m_scrollElasticityController.m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = true;
996             if (m_scrollElasticityController.m_scrollerInitiallyPinnedOnRight && m_scrollElasticityController.m_cumulativeHorizontalScroll > 0)
997                 m_scrollElasticityController.m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = true;
998         }
999
1000         // After a gesture begins, we go through:
1001         // 1+ PlatformWheelEventPhaseNone
1002         // 0+ PlatformWheelEventPhaseChanged
1003         // 1 PlatformWheelEventPhaseEnded if there was at least one changed event
1004         if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseNone && !m_scrollElasticityController.m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin) {
1005             if ((isScrollingLeftAndShouldNotRubberBand(wheelEvent, m_scrollableArea) &&
1006                 m_scrollElasticityController.m_scrollerInitiallyPinnedOnLeft &&
1007                 m_scrollableArea->isHorizontalScrollerPinnedToMinimumPosition()) ||
1008                 (isScrollingRightAndShouldNotRubberBand(wheelEvent, m_scrollableArea) &&
1009                 m_scrollElasticityController.m_scrollerInitiallyPinnedOnRight &&
1010                 m_scrollableArea->isHorizontalScrollerPinnedToMaximumPosition())) {
1011                 return ScrollAnimator::handleWheelEvent(wheelEvent);
1012             }
1013         }
1014     }
1015
1016     bool isMomentumScrollEvent = (wheelEvent.momentumPhase() != PlatformWheelEventPhaseNone);
1017     if (m_scrollElasticityController.m_ignoreMomentumScrolls && (isMomentumScrollEvent || m_scrollElasticityController.m_snapRubberbandTimerIsActive)) {
1018         if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded) {
1019             m_scrollElasticityController.m_ignoreMomentumScrolls = false;
1020             return true;
1021         }
1022         return false;
1023     }
1024
1025     smoothScrollWithEvent(wheelEvent);
1026     return true;
1027 }
1028
1029 void ScrollAnimatorMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
1030 {
1031     if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType)
1032         beginScrollGesture();
1033     else if (gestureEvent.type() == PlatformGestureEvent::ScrollEndType)
1034         endScrollGesture();
1035 }
1036
1037 bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
1038 {
1039     FloatSize limitDelta;
1040     if (fabsf(deltaY) >= fabsf(deltaX)) {
1041         if (deltaY < 0) {
1042             // We are trying to scroll up.  Make sure we are not pinned to the top
1043             limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
1044         } else {
1045             // We are trying to scroll down.  Make sure we are not pinned to the bottom
1046             limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
1047         }
1048     } else if (deltaX != 0) {
1049         if (deltaX < 0) {
1050             // We are trying to scroll left.  Make sure we are not pinned to the left
1051             limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
1052         } else {
1053             // We are trying to scroll right.  Make sure we are not pinned to the right
1054             limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
1055         }
1056     }
1057     
1058     if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
1059         return true;
1060     return false;
1061 }
1062
1063 bool ScrollAnimatorMac::isHorizontalScrollerPinnedToMinimumPosition()
1064 {
1065     return m_scrollableArea->isHorizontalScrollerPinnedToMinimumPosition();
1066 }
1067
1068 bool ScrollAnimatorMac::isHorizontalScrollerPinnedToMaximumPosition()
1069 {
1070     return m_scrollableArea->isHorizontalScrollerPinnedToMaximumPosition();
1071 }
1072
1073 IntSize ScrollAnimatorMac::stretchAmount()
1074 {
1075     return m_scrollableArea->overhangAmount();
1076 }
1077
1078 void ScrollAnimatorMac::startSnapRubberbandTimer()
1079 {
1080     m_snapRubberBandTimer.startRepeating(1.0 / 60.0);
1081 }
1082
1083 void ScrollAnimatorMac::stopSnapRubberbandTimer()
1084 {
1085     m_snapRubberBandTimer.stop();
1086 }
1087
1088 bool ScrollAnimatorMac::allowsVerticalStretching() const
1089 {
1090     switch (m_scrollableArea->verticalScrollElasticity()) {
1091     case ScrollElasticityAutomatic: {
1092         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1093         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1094         return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
1095     }
1096     case ScrollElasticityNone:
1097         return false;
1098     case ScrollElasticityAllowed:
1099         return true;
1100     }
1101
1102     ASSERT_NOT_REACHED();
1103     return false;
1104 }
1105
1106 bool ScrollAnimatorMac::allowsHorizontalStretching() const
1107 {
1108     switch (m_scrollableArea->horizontalScrollElasticity()) {
1109     case ScrollElasticityAutomatic: {
1110         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1111         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1112         return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
1113     }
1114     case ScrollElasticityNone:
1115         return false;
1116     case ScrollElasticityAllowed:
1117         return true;
1118     }
1119
1120     ASSERT_NOT_REACHED();
1121     return false;
1122 }
1123
1124 void ScrollAnimatorMac::smoothScrollWithEvent(const PlatformWheelEvent& wheelEvent)
1125 {
1126     m_haveScrolledSincePageLoad = true;
1127
1128     float deltaX = m_scrollElasticityController.m_overflowScrollDelta.width();
1129     float deltaY = m_scrollElasticityController.m_overflowScrollDelta.height();
1130
1131     // Reset overflow values because we may decide to remove delta at various points and put it into overflow.
1132     m_scrollElasticityController.m_overflowScrollDelta = FloatSize();
1133
1134     float eventCoalescedDeltaX = -wheelEvent.deltaX();
1135     float eventCoalescedDeltaY = -wheelEvent.deltaY();
1136
1137     deltaX += eventCoalescedDeltaX;
1138     deltaY += eventCoalescedDeltaY;
1139
1140     // Slightly prefer scrolling vertically by applying the = case to deltaY
1141     if (fabsf(deltaY) >= fabsf(deltaX))
1142         deltaX = 0;
1143     else
1144         deltaY = 0;
1145
1146     bool isVerticallyStretched = false;
1147     bool isHorizontallyStretched = false;
1148     bool shouldStretch = false;
1149     
1150     IntSize stretchAmount = m_scrollableArea->overhangAmount();
1151
1152     isHorizontallyStretched = stretchAmount.width();
1153     isVerticallyStretched = stretchAmount.height();
1154
1155     PlatformWheelEventPhase phase = wheelEvent.momentumPhase();
1156
1157     // If we are starting momentum scrolling then do some setup.
1158     if (!m_scrollElasticityController.m_momentumScrollInProgress && (phase == PlatformWheelEventPhaseBegan || phase == PlatformWheelEventPhaseChanged))
1159         m_scrollElasticityController.m_momentumScrollInProgress = true;
1160
1161     CFTimeInterval timeDelta = wheelEvent.timestamp() - m_scrollElasticityController.m_lastMomentumScrollTimestamp;
1162     if (m_scrollElasticityController.m_inScrollGesture || m_scrollElasticityController.m_momentumScrollInProgress) {
1163         if (m_scrollElasticityController.m_lastMomentumScrollTimestamp && timeDelta > 0 && timeDelta < scrollVelocityZeroingTimeout) {
1164             m_scrollElasticityController.m_momentumVelocity.setWidth(eventCoalescedDeltaX / (float)timeDelta);
1165             m_scrollElasticityController.m_momentumVelocity.setHeight(eventCoalescedDeltaY / (float)timeDelta);
1166             m_scrollElasticityController.m_lastMomentumScrollTimestamp = wheelEvent.timestamp();
1167         } else {
1168             m_scrollElasticityController.m_lastMomentumScrollTimestamp = wheelEvent.timestamp();
1169             m_scrollElasticityController.m_momentumVelocity = FloatSize();
1170         }
1171
1172         if (isVerticallyStretched) {
1173             if (!isHorizontallyStretched && pinnedInDirection(deltaX, 0)) {                
1174                 // Stretching only in the vertical.
1175                 if (deltaY != 0 && (fabsf(deltaX / deltaY) < rubberbandDirectionLockStretchRatio))
1176                     deltaX = 0;
1177                 else if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
1178                     m_scrollElasticityController.m_overflowScrollDelta.setWidth(m_scrollElasticityController.m_overflowScrollDelta.width() + deltaX);
1179                     deltaX = 0;
1180                 } else
1181                     m_scrollElasticityController.m_overflowScrollDelta.setWidth(m_scrollElasticityController.m_overflowScrollDelta.width() + deltaX);
1182             }
1183         } else if (isHorizontallyStretched) {
1184             // Stretching only in the horizontal.
1185             if (pinnedInDirection(0, deltaY)) {
1186                 if (deltaX != 0 && (fabsf(deltaY / deltaX) < rubberbandDirectionLockStretchRatio))
1187                     deltaY = 0;
1188                 else if (fabsf(deltaY) < rubberbandMinimumRequiredDeltaBeforeStretch) {
1189                     m_scrollElasticityController.m_overflowScrollDelta.setHeight(m_scrollElasticityController.m_overflowScrollDelta.height() + deltaY);
1190                     deltaY = 0;
1191                 } else
1192                     m_scrollElasticityController.m_overflowScrollDelta.setHeight(m_scrollElasticityController.m_overflowScrollDelta.height() + deltaY);
1193             }
1194         } else {
1195             // Not stretching at all yet.
1196             if (pinnedInDirection(deltaX, deltaY)) {
1197                 if (fabsf(deltaY) >= fabsf(deltaX)) {
1198                     if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
1199                         m_scrollElasticityController.m_overflowScrollDelta.setWidth(m_scrollElasticityController.m_overflowScrollDelta.width() + deltaX);
1200                         deltaX = 0;
1201                     } else
1202                         m_scrollElasticityController.m_overflowScrollDelta.setWidth(m_scrollElasticityController.m_overflowScrollDelta.width() + deltaX);
1203                 }
1204                 shouldStretch = true;
1205             }
1206         }
1207     }
1208
1209     if (deltaX != 0 || deltaY != 0) {
1210         if (!(shouldStretch || isVerticallyStretched || isHorizontallyStretched)) {
1211             if (deltaY != 0) {
1212                 deltaY *= scrollWheelMultiplier();
1213                 immediateScrollByDeltaY(deltaY);
1214             }
1215             if (deltaX != 0) {
1216                 deltaX *= scrollWheelMultiplier();
1217                 immediateScrollByDeltaX(deltaX);
1218             }
1219         } else {
1220             if (!allowsHorizontalStretching()) {
1221                 deltaX = 0;
1222                 eventCoalescedDeltaX = 0;
1223             } else if ((deltaX != 0) && !isHorizontallyStretched && !pinnedInDirection(deltaX, 0)) {
1224                 deltaX *= scrollWheelMultiplier();
1225
1226                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1227                 immediateScrollByDeltaX(deltaX);
1228                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1229
1230                 deltaX = 0;
1231             }
1232             
1233             if (!allowsVerticalStretching()) {
1234                 deltaY = 0;
1235                 eventCoalescedDeltaY = 0;
1236             } else if ((deltaY != 0) && !isVerticallyStretched && !pinnedInDirection(0, deltaY)) {
1237                 deltaY *= scrollWheelMultiplier();
1238
1239                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1240                 immediateScrollByDeltaY(deltaY);
1241                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1242
1243                 deltaY = 0;
1244             }
1245             
1246             IntSize stretchAmount = m_scrollableArea->overhangAmount();
1247         
1248             if (m_scrollElasticityController.m_momentumScrollInProgress) {
1249                 if ((pinnedInDirection(eventCoalescedDeltaX, eventCoalescedDeltaY) || (fabsf(eventCoalescedDeltaX) + fabsf(eventCoalescedDeltaY) <= 0)) && m_scrollElasticityController.m_lastMomentumScrollTimestamp) {
1250                     m_scrollElasticityController.m_ignoreMomentumScrolls = true;
1251                     m_scrollElasticityController.m_momentumScrollInProgress = false;
1252                     snapRubberBand();
1253                 }
1254             }
1255
1256             m_scrollElasticityController.m_stretchScrollForce.setWidth(m_scrollElasticityController.m_stretchScrollForce.width() + deltaX);
1257             m_scrollElasticityController.m_stretchScrollForce.setHeight(m_scrollElasticityController.m_stretchScrollForce.height() + deltaY);
1258
1259             FloatSize dampedDelta(ceilf(elasticDeltaForReboundDelta(m_scrollElasticityController.m_stretchScrollForce.width())), ceilf(elasticDeltaForReboundDelta(m_scrollElasticityController.m_stretchScrollForce.height())));
1260             FloatPoint origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - stretchAmount;
1261             FloatPoint newOrigin = origOrigin + dampedDelta;
1262
1263             if (origOrigin != newOrigin) {
1264                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1265                 immediateScrollToPoint(newOrigin);
1266                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1267             }
1268         }
1269     }
1270
1271     if (m_scrollElasticityController.m_momentumScrollInProgress && phase == PlatformWheelEventPhaseEnded) {
1272         m_scrollElasticityController.m_momentumScrollInProgress = false;
1273         m_scrollElasticityController.m_ignoreMomentumScrolls = false;
1274         m_scrollElasticityController.m_lastMomentumScrollTimestamp = 0;
1275     }
1276 }
1277
1278 void ScrollAnimatorMac::beginScrollGesture()
1279 {
1280     didBeginScrollGesture();
1281     m_haveScrolledSincePageLoad = true;
1282
1283     m_scrollElasticityController.beginScrollGesture();
1284 }
1285
1286 void ScrollAnimatorMac::endScrollGesture()
1287 {
1288     didEndScrollGesture();
1289
1290     snapRubberBand();
1291 }
1292
1293 void ScrollAnimatorMac::snapRubberBand()
1294 {
1295     CFTimeInterval timeDelta = [[NSProcessInfo processInfo] systemUptime] - m_scrollElasticityController.m_lastMomentumScrollTimestamp;
1296     if (m_scrollElasticityController.m_lastMomentumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout)
1297         m_scrollElasticityController.m_momentumVelocity = FloatSize();
1298
1299     m_scrollElasticityController.m_inScrollGesture = false;
1300
1301     if (m_scrollElasticityController.m_snapRubberbandTimerIsActive)
1302         return;
1303
1304     m_scrollElasticityController.m_startTime = [NSDate timeIntervalSinceReferenceDate];
1305     m_scrollElasticityController.m_startStretch = FloatSize();
1306     m_scrollElasticityController.m_origOrigin = FloatPoint();
1307     m_scrollElasticityController.m_origVelocity = FloatSize();
1308
1309     m_snapRubberBandTimer.startRepeating(1.0/60.0);
1310     m_scrollElasticityController.m_snapRubberbandTimerIsActive = true;
1311 }
1312
1313 static inline float roundTowardZero(float num)
1314 {
1315     return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f);
1316 }
1317
1318 static inline float roundToDevicePixelTowardZero(float num)
1319 {
1320     float roundedNum = roundf(num);
1321     if (fabs(num - roundedNum) < 0.125)
1322         num = roundedNum;
1323
1324     return roundTowardZero(num);
1325 }
1326
1327 void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
1328 {
1329     if (!m_scrollElasticityController.m_momentumScrollInProgress || m_scrollElasticityController.m_ignoreMomentumScrolls) {
1330         CFTimeInterval timeDelta = [NSDate timeIntervalSinceReferenceDate] - m_scrollElasticityController.m_startTime;
1331
1332         if (m_scrollElasticityController.m_startStretch == FloatSize()) {
1333             m_scrollElasticityController.m_startStretch = m_scrollableArea->overhangAmount();
1334             if (m_scrollElasticityController.m_startStretch == FloatSize()) {    
1335                 m_snapRubberBandTimer.stop();
1336
1337                 m_scrollElasticityController.m_stretchScrollForce = FloatSize();
1338                 m_scrollElasticityController.m_startTime = 0;
1339                 m_scrollElasticityController.m_startStretch = FloatSize();
1340                 m_scrollElasticityController.m_origOrigin = FloatPoint();
1341                 m_scrollElasticityController.m_origVelocity = FloatSize();
1342                 m_scrollElasticityController.m_snapRubberbandTimerIsActive = false;
1343
1344                 return;
1345             }
1346
1347             m_scrollableArea->didStartRubberBand(roundedIntSize(m_scrollElasticityController.m_startStretch));
1348             
1349             m_scrollElasticityController.m_origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - m_scrollElasticityController.m_startStretch;
1350             m_scrollElasticityController.m_origVelocity = m_scrollElasticityController.m_momentumVelocity;
1351
1352             // Just like normal scrolling, prefer vertical rubberbanding
1353             if (fabsf(m_scrollElasticityController.m_origVelocity.height()) >= fabsf(m_scrollElasticityController.m_origVelocity.width()))
1354                 m_scrollElasticityController.m_origVelocity.setWidth(0);
1355             
1356             // Don't rubber-band horizontally if it's not possible to scroll horizontally
1357             Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1358             if (!hScroller || !hScroller->enabled())
1359                 m_scrollElasticityController.m_origVelocity.setWidth(0);
1360             
1361             // Don't rubber-band vertically if it's not possible to scroll horizontally
1362             Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1363             if (!vScroller || !vScroller->enabled())
1364                 m_scrollElasticityController.m_origVelocity.setHeight(0);
1365         }
1366
1367         FloatPoint delta(roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_scrollElasticityController.m_startStretch.width(), -m_scrollElasticityController.m_origVelocity.width(), (float)timeDelta)),
1368                          roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_scrollElasticityController.m_startStretch.height(), -m_scrollElasticityController.m_origVelocity.height(), (float)timeDelta)));
1369
1370         if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) {
1371             FloatPoint newOrigin = m_scrollElasticityController.m_origOrigin + delta;
1372
1373             m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1374             immediateScrollToPoint(newOrigin);
1375             m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1376
1377             FloatSize newStretch = m_scrollableArea->overhangAmount();
1378             
1379             m_scrollElasticityController.m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(newStretch.width()));
1380             m_scrollElasticityController.m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(newStretch.height()));
1381         } else {
1382             immediateScrollToPoint(m_scrollElasticityController.m_origOrigin);
1383
1384             m_scrollableArea->didCompleteRubberBand(roundedIntSize(m_scrollElasticityController.m_startStretch));
1385
1386             m_snapRubberBandTimer.stop();
1387
1388             m_scrollElasticityController.m_stretchScrollForce = FloatSize();
1389             m_scrollElasticityController.m_startTime = 0;
1390             m_scrollElasticityController.m_startStretch = FloatSize();
1391             m_scrollElasticityController.m_origOrigin = FloatPoint();
1392             m_scrollElasticityController.m_origVelocity = FloatSize();
1393             m_scrollElasticityController.m_snapRubberbandTimerIsActive = false;
1394         }
1395     } else {
1396         m_scrollElasticityController.m_startTime = [NSDate timeIntervalSinceReferenceDate];
1397         m_scrollElasticityController.m_startStretch = FloatSize();
1398     }
1399 }
1400 #endif
1401
1402 void ScrollAnimatorMac::setIsActive()
1403 {
1404 #if USE(SCROLLBAR_PAINTER)
1405     if (needsScrollerStyleUpdate())
1406         updateScrollerStyle();
1407 #endif
1408 }
1409
1410 #if USE(SCROLLBAR_PAINTER)
1411 void ScrollAnimatorMac::updateScrollerStyle()
1412 {
1413     if (!scrollableArea()->isOnActivePage()) {
1414         setNeedsScrollerStyleUpdate(true);
1415         return;
1416     }
1417
1418     ScrollbarThemeMac* macTheme = macScrollbarTheme();
1419     if (!macTheme) {
1420         setNeedsScrollerStyleUpdate(false);
1421         return;
1422     }
1423
1424     NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle];
1425
1426     if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
1427         verticalScrollbar->invalidate();
1428
1429         ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp];
1430         ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 
1431                                                                                     controlSize:(NSControlSize)verticalScrollbar->controlSize() 
1432                                                                                     horizontal:NO 
1433                                                                                     replacingScrollerImp:oldVerticalPainter];
1434         macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
1435         [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter];
1436
1437         // The different scrollbar styles have different thicknesses, so we must re-set the 
1438         // frameRect to the new thickness, and the re-layout below will ensure the position
1439         // and length are properly updated.
1440         int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
1441         verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1442     }
1443
1444     if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
1445         horizontalScrollbar->invalidate();
1446
1447         ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp];
1448         ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 
1449                                                                                     controlSize:(NSControlSize)horizontalScrollbar->controlSize() 
1450                                                                                     horizontal:YES 
1451                                                                                     replacingScrollerImp:oldHorizontalPainter];
1452         macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
1453         [m_scrollbarPainterController.get() setVerticalScrollerImp:newHorizontalPainter];
1454
1455         // The different scrollbar styles have different thicknesses, so we must re-set the 
1456         // frameRect to the new thickness, and the re-layout below will ensure the position
1457         // and length are properly updated.
1458         int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
1459         horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1460     }
1461
1462     // If needsScrollerStyleUpdate() is true, then the page is restoring from the page cache, and 
1463     // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves.
1464     scrollableArea()->scrollbarStyleChanged(newStyle, !needsScrollerStyleUpdate());
1465
1466     setNeedsScrollerStyleUpdate(false);
1467 }
1468
1469 void ScrollAnimatorMac::startScrollbarPaintTimer()
1470 {
1471     m_initialScrollbarPaintTimer.startOneShot(0.1);
1472 }
1473
1474 bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1475 {
1476     return m_initialScrollbarPaintTimer.isActive();
1477 }
1478
1479 void ScrollAnimatorMac::stopScrollbarPaintTimer()
1480 {
1481     m_initialScrollbarPaintTimer.stop();
1482 }
1483
1484 void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1485 {
1486     // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController
1487     // might think that the scrollbars are already showing and bail early.
1488     [m_scrollbarPainterController.get() hideOverlayScrollers];
1489     [m_scrollbarPainterController.get() flashScrollers];
1490 }
1491 #endif
1492
1493 void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1494 {
1495     IntRect rectInViewCoordinates = scrollerThumb;
1496     if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1497         rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1498
1499     if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1500         return;
1501
1502     m_scrollableArea->setVisibleScrollerThumbRect(rectInViewCoordinates);
1503     m_visibleScrollerThumbRect = rectInViewCoordinates;
1504 }
1505
1506 } // namespace WebCore
1507
1508 #endif // ENABLE(SMOOTH_SCROLLING)