Don't dispatch fake mousemove events when we don't know where the cursor is
[WebKit-https.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 "EmptyProtocolDefinitions.h"
34 #include "FloatPoint.h"
35 #include "NSScrollerImpDetails.h"
36 #include "PlatformGestureEvent.h"
37 #include "PlatformWheelEvent.h"
38 #include "ScrollView.h"
39 #include "ScrollableArea.h"
40 #include "ScrollbarTheme.h"
41 #include "ScrollbarThemeMac.h"
42 #include "WebCoreSystemInterface.h"
43 #include <wtf/PassOwnPtr.h>
44 #include <wtf/UnusedParam.h>
45
46 using namespace WebCore;
47 using namespace std;
48
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
56 static bool supportsExpansionTransitionProgress()
57 {
58     static bool globalSupportsExpansionTransitionProgress = [NSClassFromString(@"NSScrollerImp") instancesRespondToSelector:@selector(expansionTransitionProgress)];
59     return globalSupportsExpansionTransitionProgress;
60 }
61
62 static ScrollbarThemeMac* macScrollbarTheme()
63 {
64     ScrollbarTheme* scrollbarTheme = ScrollbarTheme::theme();
65     return !scrollbarTheme->isMockTheme() ? static_cast<ScrollbarThemeMac*>(scrollbarTheme) : 0;
66 }
67
68 static ScrollbarPainter scrollbarPainterForScrollbar(Scrollbar* scrollbar)
69 {
70     if (ScrollbarThemeMac* scrollbarTheme = macScrollbarTheme())
71         return scrollbarTheme->painterForScrollbar(scrollbar);
72
73     return nil;
74 }
75
76 @interface NSObject (ScrollAnimationHelperDetails)
77 - (id)initWithDelegate:(id)delegate;
78 - (void)_stopRun;
79 - (BOOL)_isAnimating;
80 - (NSPoint)targetOrigin;
81 - (CGFloat)_progress;
82 @end
83
84 @interface WebScrollAnimationHelperDelegate : NSObject
85 {
86     WebCore::ScrollAnimatorMac* _animator;
87 }
88 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
89 @end
90
91 static NSSize abs(NSSize size)
92 {
93     NSSize finalSize = size;
94     if (finalSize.width < 0)
95         finalSize.width = -finalSize.width;
96     if (finalSize.height < 0)
97         finalSize.height = -finalSize.height;
98     return finalSize;    
99 }
100
101 @implementation WebScrollAnimationHelperDelegate
102
103 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
104 {
105     self = [super init];
106     if (!self)
107         return nil;
108
109     _animator = scrollAnimator;
110     return self;
111 }
112
113 - (void)invalidate
114 {
115     _animator = 0;
116 }
117
118 - (NSRect)bounds
119 {
120     if (!_animator)
121         return NSZeroRect;
122
123     WebCore::FloatPoint currentPosition = _animator->currentPosition();
124     return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
125 }
126
127 - (void)_immediateScrollToPoint:(NSPoint)newPosition
128 {
129     if (!_animator)
130         return;
131     _animator->immediateScrollToPointForScrollAnimation(newPosition);
132 }
133
134 - (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin
135 {
136     return newOrigin;
137 }
138
139 - (NSSize)convertSizeToBase:(NSSize)size
140 {
141     return abs(size);
142 }
143
144 - (NSSize)convertSizeFromBase:(NSSize)size
145 {
146     return abs(size);
147 }
148
149 - (NSSize)convertSizeToBacking:(NSSize)size
150 {
151     return abs(size);
152 }
153
154 - (NSSize)convertSizeFromBacking:(NSSize)size
155 {
156     return abs(size);
157 }
158
159 - (id)superview
160 {
161     return nil;
162 }
163
164 - (id)documentView
165 {
166     return nil;
167 }
168
169 - (id)window
170 {
171     return nil;
172 }
173
174 - (void)_recursiveRecomputeToolTips
175 {
176 }
177
178 @end
179
180 @interface WebScrollbarPainterControllerDelegate : NSObject
181 {
182     ScrollableArea* _scrollableArea;
183 }
184 - (id)initWithScrollableArea:(ScrollableArea*)scrollableArea;
185 @end
186
187 @implementation WebScrollbarPainterControllerDelegate
188
189 - (id)initWithScrollableArea:(ScrollableArea*)scrollableArea
190 {
191     self = [super init];
192     if (!self)
193         return nil;
194     
195     _scrollableArea = scrollableArea;
196     return self;
197 }
198
199 - (void)invalidate
200 {
201     _scrollableArea = 0;
202 }
203
204 - (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
205 {
206     UNUSED_PARAM(scrollerImpPair);
207     if (!_scrollableArea)
208         return NSZeroRect;
209
210     WebCore::IntSize contentsSize = _scrollableArea->contentsSize();
211     return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
212 }
213
214 - (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
215 {
216     UNUSED_PARAM(scrollerImpPair);
217     if (!_scrollableArea)
218         return NO;
219
220     return _scrollableArea->inLiveResize();
221 }
222
223 - (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
224 {
225     UNUSED_PARAM(scrollerImpPair);
226     if (!_scrollableArea)
227         return NSZeroPoint;
228
229     return _scrollableArea->lastKnownMousePosition();
230 }
231
232 - (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
233 {
234     UNUSED_PARAM(scrollerImpPair);
235
236     if (!_scrollableArea || !scrollerImp)
237         return NSZeroPoint;
238
239     WebCore::Scrollbar* scrollbar = 0;
240     if ([scrollerImp isHorizontal])
241         scrollbar = _scrollableArea->horizontalScrollbar();
242     else 
243         scrollbar = _scrollableArea->verticalScrollbar();
244
245     // It is possible to have a null scrollbar here since it is possible for this delegate
246     // method to be called between the moment when a scrollbar has been set to 0 and the
247     // moment when its destructor has been called. We should probably de-couple some
248     // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this
249     // issue.
250     if (!scrollbar)
251         return NSZeroPoint;
252
253     ASSERT(scrollerImp == scrollbarPainterForScrollbar(scrollbar));
254
255     return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea));
256 }
257
258 - (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
259 {
260     UNUSED_PARAM(scrollerImpPair);
261     UNUSED_PARAM(rect);
262
263     if (!_scrollableArea)
264         return;
265
266     if (!_scrollableArea->scrollbarsCanBeActive())
267         return;
268
269     _scrollableArea->scrollAnimator()->contentAreaWillPaint();
270 }
271
272 - (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
273 {
274     if (!_scrollableArea)
275         return;
276
277     [scrollerImpPair setScrollerStyle:newRecommendedScrollerStyle];
278
279     static_cast<ScrollAnimatorMac*>(_scrollableArea->scrollAnimator())->updateScrollerStyle();
280 }
281
282 @end
283
284 enum FeatureToAnimate {
285     ThumbAlpha,
286     TrackAlpha,
287     UIStateTransition,
288     ExpansionTransition
289 };
290
291 @interface WebScrollbarPartAnimation : NSAnimation
292 {
293     Scrollbar* _scrollbar;
294     RetainPtr<ScrollbarPainter> _scrollbarPainter;
295     FeatureToAnimate _featureToAnimate;
296     CGFloat _startValue;
297     CGFloat _endValue;
298 }
299 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration;
300 @end
301
302 @implementation WebScrollbarPartAnimation
303
304 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration
305 {
306     self = [super initWithDuration:duration animationCurve:NSAnimationEaseInOut];
307     if (!self)
308         return nil;
309
310     _scrollbar = scrollbar;
311     _featureToAnimate = featureToAnimate;
312     _startValue = startValue;
313     _endValue = endValue;
314
315     [self setAnimationBlockingMode:NSAnimationNonblocking];
316
317     return self;
318 }
319
320 - (void)startAnimation
321 {
322     ASSERT(_scrollbar);
323
324     _scrollbarPainter = scrollbarPainterForScrollbar(_scrollbar);
325
326     [super startAnimation];
327 }
328
329 - (void)setStartValue:(CGFloat)startValue
330 {
331     _startValue = startValue;
332 }
333
334 - (void)setEndValue:(CGFloat)endValue
335 {
336     _endValue = endValue;
337 }
338
339 - (void)setCurrentProgress:(NSAnimationProgress)progress
340 {
341     [super setCurrentProgress:progress];
342
343     ASSERT(_scrollbar);
344
345     CGFloat currentValue;
346     if (_startValue > _endValue)
347         currentValue = 1 - progress;
348     else
349         currentValue = progress;
350
351     switch (_featureToAnimate) {
352     case ThumbAlpha:
353         [_scrollbarPainter.get() setKnobAlpha:currentValue];
354         break;
355     case TrackAlpha:
356         [_scrollbarPainter.get() setTrackAlpha:currentValue];
357         break;
358     case UIStateTransition:
359         [_scrollbarPainter.get() setUiStateTransitionProgress:currentValue];
360         break;
361     case ExpansionTransition:
362         [_scrollbarPainter.get() setExpansionTransitionProgress:currentValue];
363         break;
364     }
365
366     _scrollbar->invalidate();
367 }
368
369 - (void)invalidate
370 {
371     BEGIN_BLOCK_OBJC_EXCEPTIONS;
372     [self stopAnimation];
373     END_BLOCK_OBJC_EXCEPTIONS;
374     _scrollbar = 0;
375 }
376
377 @end
378
379 @interface WebScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
380 {
381     WebCore::Scrollbar* _scrollbar;
382
383     RetainPtr<WebScrollbarPartAnimation> _knobAlphaAnimation;
384     RetainPtr<WebScrollbarPartAnimation> _trackAlphaAnimation;
385     RetainPtr<WebScrollbarPartAnimation> _uiStateTransitionAnimation;
386     RetainPtr<WebScrollbarPartAnimation> _expansionTransitionAnimation;
387 }
388 - (id)initWithScrollbar:(WebCore::Scrollbar*)scrollbar;
389 - (void)cancelAnimations;
390 @end
391
392 @implementation WebScrollbarPainterDelegate
393
394 - (id)initWithScrollbar:(WebCore::Scrollbar*)scrollbar
395 {
396     self = [super init];
397     if (!self)
398         return nil;
399     
400     _scrollbar = scrollbar;
401     return self;
402 }
403
404 - (void)cancelAnimations
405 {
406     BEGIN_BLOCK_OBJC_EXCEPTIONS;
407     [_knobAlphaAnimation.get() stopAnimation];
408     [_trackAlphaAnimation.get() stopAnimation];
409     [_uiStateTransitionAnimation.get() stopAnimation];
410     [_expansionTransitionAnimation.get() stopAnimation];
411     END_BLOCK_OBJC_EXCEPTIONS;
412 }
413
414 - (ScrollAnimatorMac*)scrollAnimator
415 {
416     return static_cast<ScrollAnimatorMac*>(_scrollbar->scrollableArea()->scrollAnimator());
417 }
418
419 - (NSRect)convertRectToBacking:(NSRect)aRect
420 {
421     return aRect;
422 }
423
424 - (NSRect)convertRectFromBacking:(NSRect)aRect
425 {
426     return aRect;
427 }
428
429 #if !PLATFORM(CHROMIUM)
430 - (CALayer *)layer
431 {
432     if (!_scrollbar)
433         return nil;
434
435     if (!ScrollbarThemeMac::isCurrentlyDrawingIntoLayer())
436         return nil;
437
438     // FIXME: This should attempt to return an actual layer.
439     static CALayer *dummyLayer = [[CALayer alloc] init];
440     return dummyLayer;
441 }
442 #endif
443
444 - (NSPoint)mouseLocationInScrollerForScrollerImp:(id)scrollerImp
445 {
446     if (!_scrollbar)
447         return NSZeroPoint;
448
449     ASSERT_UNUSED(scrollerImp, scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
450
451     return _scrollbar->convertFromContainingView(_scrollbar->scrollableArea()->lastKnownMousePosition());
452 }
453
454 - (void)setUpAlphaAnimation:(RetainPtr<WebScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(ScrollbarPainter)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
455 {
456     // If the user has scrolled the page, then the scrollbars must be animated here. 
457     // This overrides the early returns.
458     bool mustAnimate = [self scrollAnimator]->haveScrolledSincePageLoad();
459
460     if ([self scrollAnimator]->scrollbarPaintTimerIsActive() && !mustAnimate)
461         return;
462
463     if (_scrollbar->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
464         [self scrollAnimator]->startScrollbarPaintTimer();
465         return;
466     }
467
468     // At this point, we are definitely going to animate now, so stop the timer.
469     [self scrollAnimator]->stopScrollbarPaintTimer();
470
471     // If we are currently animating, stop
472     if (scrollbarPartAnimation) {
473         [scrollbarPartAnimation.get() stopAnimation];
474         scrollbarPartAnimation = nil;
475     }
476
477     if (part == WebCore::ThumbPart && _scrollbar->orientation() == VerticalScrollbar) {
478         if (newAlpha == 1) {
479             IntRect thumbRect = IntRect([scrollerPainter rectForPart:NSScrollerKnob]);
480             [self scrollAnimator]->setVisibleScrollerThumbRect(thumbRect);
481         } else
482             [self scrollAnimator]->setVisibleScrollerThumbRect(IntRect());
483     }
484
485     scrollbarPartAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar 
486                                                                        featureToAnimate:part == ThumbPart ? ThumbAlpha : TrackAlpha
487                                                                             animateFrom:part == ThumbPart ? [scrollerPainter knobAlpha] : [scrollerPainter trackAlpha]
488                                                                               animateTo:newAlpha 
489                                                                                duration:duration]);
490     [scrollbarPartAnimation.get() startAnimation];
491 }
492
493 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
494 {
495     if (!_scrollbar)
496         return;
497
498     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
499
500     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
501     [self setUpAlphaAnimation:_knobAlphaAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
502 }
503
504 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
505 {
506     if (!_scrollbar)
507         return;
508
509     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
510     
511     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
512     [self setUpAlphaAnimation:_trackAlphaAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
513 }
514
515 - (void)scrollerImp:(id)scrollerImp animateUIStateTransitionWithDuration:(NSTimeInterval)duration
516 {
517     if (!_scrollbar)
518         return;
519
520     if (!supportsUIStateTransitionProgress())
521         return;
522
523     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
524
525     ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
526
527     // UIStateTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
528     [scrollbarPainter setUiStateTransitionProgress:1 - [scrollerImp uiStateTransitionProgress]];
529
530     if (!_uiStateTransitionAnimation)
531         _uiStateTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar 
532                                                                                 featureToAnimate:UIStateTransition
533                                                                                      animateFrom:[scrollbarPainter uiStateTransitionProgress]
534                                                                                        animateTo:1.0
535                                                                                         duration:duration]);
536     else {
537         // If we don't need to initialize the animation, just reset the values in case they have changed.
538         [_uiStateTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
539         [_uiStateTransitionAnimation.get() setEndValue:1.0];
540         [_uiStateTransitionAnimation.get() setDuration:duration];
541     }
542     [_uiStateTransitionAnimation.get() startAnimation];
543 }
544
545 - (void)scrollerImp:(id)scrollerImp animateExpansionTransitionWithDuration:(NSTimeInterval)duration
546 {
547     if (!_scrollbar)
548         return;
549
550     if (!supportsExpansionTransitionProgress())
551         return;
552
553     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
554
555     ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
556
557     // ExpansionTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
558     [scrollbarPainter setExpansionTransitionProgress:1 - [scrollerImp expansionTransitionProgress]];
559
560     if (!_expansionTransitionAnimation) {
561         _expansionTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
562                                                                                   featureToAnimate:ExpansionTransition
563                                                                                        animateFrom:[scrollbarPainter expansionTransitionProgress]
564                                                                                          animateTo:1.0
565                                                                                           duration:duration]);
566     } else {
567         // If we don't need to initialize the animation, just reset the values in case they have changed.
568         [_expansionTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
569         [_expansionTransitionAnimation.get() setEndValue:1.0];
570         [_expansionTransitionAnimation.get() setDuration:duration];
571     }
572     [_expansionTransitionAnimation.get() startAnimation];
573 }
574
575 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
576 {
577     UNUSED_PARAM(scrollerImp);
578     UNUSED_PARAM(newOverlayScrollerState);
579 }
580
581 - (void)invalidate
582 {
583     _scrollbar = 0;
584     BEGIN_BLOCK_OBJC_EXCEPTIONS;
585     [_knobAlphaAnimation.get() invalidate];
586     [_trackAlphaAnimation.get() invalidate];
587     [_uiStateTransitionAnimation.get() invalidate];
588     [_expansionTransitionAnimation.get() invalidate];
589     END_BLOCK_OBJC_EXCEPTIONS;
590 }
591
592 @end
593
594 namespace WebCore {
595
596 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
597 {
598     return adoptPtr(new ScrollAnimatorMac(scrollableArea));
599 }
600
601 ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
602     : ScrollAnimator(scrollableArea)
603     , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
604     , m_sendContentAreaScrolledTimer(this, &ScrollAnimatorMac::sendContentAreaScrolledTimerFired)
605 #if ENABLE(RUBBER_BANDING)
606     , m_scrollElasticityController(this)
607     , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
608 #endif
609     , m_haveScrolledSincePageLoad(false)
610     , m_needsScrollerStyleUpdate(false)
611 {
612     m_scrollAnimationHelperDelegate.adoptNS([[WebScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
613     m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
614
615     if (isScrollbarOverlayAPIAvailable()) {
616         m_scrollbarPainterControllerDelegate.adoptNS([[WebScrollbarPainterControllerDelegate alloc] initWithScrollableArea:scrollableArea]);
617         m_scrollbarPainterController = [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease];
618         [m_scrollbarPainterController.get() setDelegate:m_scrollbarPainterControllerDelegate.get()];
619         [m_scrollbarPainterController.get() setScrollerStyle:recommendedScrollerStyle()];
620     }
621 }
622
623 ScrollAnimatorMac::~ScrollAnimatorMac()
624 {
625     if (isScrollbarOverlayAPIAvailable()) {
626         BEGIN_BLOCK_OBJC_EXCEPTIONS;
627         [m_scrollbarPainterControllerDelegate.get() invalidate];
628         [m_scrollbarPainterController.get() setDelegate:nil];
629         [m_horizontalScrollbarPainterDelegate.get() invalidate];
630         [m_verticalScrollbarPainterDelegate.get() invalidate];
631         [m_scrollAnimationHelperDelegate.get() invalidate];
632         END_BLOCK_OBJC_EXCEPTIONS;
633     }
634 }
635
636 static bool scrollAnimationEnabledForSystem()
637 {
638     NSString* scrollAnimationDefaultsKey = 
639 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 || PLATFORM(CHROMIUM)
640         @"AppleScrollAnimationEnabled";
641 #else
642         @"NSScrollAnimationEnabled";
643 #endif
644     static bool enabled = [[NSUserDefaults standardUserDefaults] boolForKey:scrollAnimationDefaultsKey];
645     return enabled;
646 }
647
648 #if ENABLE(RUBBER_BANDING)
649 static bool rubberBandingEnabledForSystem()
650 {
651     static bool initialized = false;
652     static bool enabled = true;
653     // Caches the result, which is consistent with other apps like the Finder, which all
654     // require a restart after changing this default.
655     if (!initialized) {
656         // Uses -objectForKey: and not -boolForKey: in order to default to true if the value wasn't set.
657         id value = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSScrollViewRubberbanding"];
658         if ([value isKindOfClass:[NSNumber class]])
659             enabled = [value boolValue];
660         initialized = true;
661     }
662     return enabled;
663 }
664 #endif
665
666 bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
667 {
668     m_haveScrolledSincePageLoad = true;
669
670     if (!scrollAnimationEnabledForSystem() || !m_scrollableArea->scrollAnimatorEnabled())
671         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
672
673     if (granularity == ScrollByPixel)
674         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
675
676     float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
677     float newPos = std::max<float>(std::min<float>(currentPos + (step * multiplier), static_cast<float>(m_scrollableArea->scrollSize(orientation))), 0);
678     if (currentPos == newPos)
679         return false;
680
681     NSPoint newPoint;
682     if ([m_scrollAnimationHelper.get() _isAnimating]) {
683         NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
684         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
685     } else
686         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
687
688     [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
689     return true;
690 }
691
692 void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
693 {
694     [m_scrollAnimationHelper.get() _stopRun];
695     immediateScrollTo(offset);
696 }
697
698 FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
699 {
700     if (!m_scrollableArea->constrainsScrollingToContentEdge())
701         return position;
702
703     float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
704     float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
705
706     return FloatPoint(newX, newY);
707 }
708
709 void ScrollAnimatorMac::immediateScrollTo(const FloatPoint& newPosition)
710 {
711     FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
712  
713     bool positionChanged = adjustedPosition.x() != m_currentPosX || adjustedPosition.y() != m_currentPosY;
714     if (!positionChanged && !scrollableArea()->scrollOriginChanged())
715         return;
716
717     m_currentPosX = adjustedPosition.x();
718     m_currentPosY = adjustedPosition.y();
719     notifyPositionChanged();
720 }
721
722 bool ScrollAnimatorMac::isRubberBandInProgress() const
723 {
724 #if !ENABLE(RUBBER_BANDING)
725     return false;
726 #else
727     return m_scrollElasticityController.isRubberBandInProgress();
728 #endif
729 }
730
731 void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition)
732 {
733     ASSERT(m_scrollAnimationHelper);
734     immediateScrollTo(newPosition);
735 }
736
737 void ScrollAnimatorMac::notifyPositionChanged()
738 {
739     notifyContentAreaScrolled();
740     ScrollAnimator::notifyPositionChanged();
741 }
742
743 void ScrollAnimatorMac::contentAreaWillPaint() const
744 {
745     if (!scrollableArea()->scrollbarsCanBeActive())
746         return;
747     if (isScrollbarOverlayAPIAvailable())
748         [m_scrollbarPainterController.get() contentAreaWillDraw];
749 }
750
751 void ScrollAnimatorMac::mouseEnteredContentArea() const
752 {
753     if (!scrollableArea()->scrollbarsCanBeActive())
754         return;
755     if (isScrollbarOverlayAPIAvailable())
756         [m_scrollbarPainterController.get() mouseEnteredContentArea];
757 }
758
759 void ScrollAnimatorMac::mouseExitedContentArea() const
760 {
761     if (!scrollableArea()->scrollbarsCanBeActive())
762         return;
763     if (isScrollbarOverlayAPIAvailable())
764         [m_scrollbarPainterController.get() mouseExitedContentArea];
765 }
766
767 void ScrollAnimatorMac::mouseMovedInContentArea() const
768 {
769     if (!scrollableArea()->scrollbarsCanBeActive())
770         return;
771     if (isScrollbarOverlayAPIAvailable())
772         [m_scrollbarPainterController.get() mouseMovedInContentArea];
773 }
774
775 void ScrollAnimatorMac::mouseEnteredScrollbar(Scrollbar* scrollbar) const
776 {
777     // At this time, only legacy scrollbars needs to send notifications here.
778     if (recommendedScrollerStyle() != NSScrollerStyleLegacy)
779         return;
780
781     if (!scrollableArea()->scrollbarsCanBeActive())
782         return;
783
784     if (isScrollbarOverlayAPIAvailable()) {
785         if (!supportsUIStateTransitionProgress())
786             return;
787         if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
788             [painter mouseEnteredScroller];
789     }
790 }
791
792 void ScrollAnimatorMac::mouseExitedScrollbar(Scrollbar* scrollbar) const
793 {
794     // At this time, only legacy scrollbars needs to send notifications here.
795     if (recommendedScrollerStyle() != NSScrollerStyleLegacy)
796         return;
797
798     if (!scrollableArea()->scrollbarsCanBeActive())
799         return;
800
801     if (isScrollbarOverlayAPIAvailable()) {
802         if (!supportsUIStateTransitionProgress())
803             return;
804         if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
805             [painter mouseExitedScroller];
806     }
807 }
808
809 void ScrollAnimatorMac::willStartLiveResize()
810 {
811     if (!scrollableArea()->scrollbarsCanBeActive())
812         return;
813     if (isScrollbarOverlayAPIAvailable())
814         [m_scrollbarPainterController.get() startLiveResize];
815 }
816
817 void ScrollAnimatorMac::contentsResized() const
818 {
819     if (!scrollableArea()->scrollbarsCanBeActive())
820         return;
821     if (isScrollbarOverlayAPIAvailable())
822         [m_scrollbarPainterController.get() contentAreaDidResize];
823 }
824
825 void ScrollAnimatorMac::willEndLiveResize()
826 {
827     if (!scrollableArea()->scrollbarsCanBeActive())
828         return;
829     if (isScrollbarOverlayAPIAvailable())
830         [m_scrollbarPainterController.get() endLiveResize];
831 }
832
833 void ScrollAnimatorMac::contentAreaDidShow() const
834 {
835     if (!scrollableArea()->scrollbarsCanBeActive())
836         return;
837     if (isScrollbarOverlayAPIAvailable())
838         [m_scrollbarPainterController.get() windowOrderedIn];
839 }
840
841 void ScrollAnimatorMac::contentAreaDidHide() const
842 {
843     if (!scrollableArea()->scrollbarsCanBeActive())
844         return;
845     if (isScrollbarOverlayAPIAvailable())
846         [m_scrollbarPainterController.get() windowOrderedOut];
847 }
848
849 void ScrollAnimatorMac::didBeginScrollGesture() const
850 {
851     if (!scrollableArea()->scrollbarsCanBeActive())
852         return;
853     if (isScrollbarOverlayAPIAvailable())
854         [m_scrollbarPainterController.get() beginScrollGesture];
855 }
856
857 void ScrollAnimatorMac::didEndScrollGesture() const
858 {
859     if (!scrollableArea()->scrollbarsCanBeActive())
860         return;
861     if (isScrollbarOverlayAPIAvailable())
862         [m_scrollbarPainterController.get() endScrollGesture];
863 }
864
865 void ScrollAnimatorMac::mayBeginScrollGesture() const
866 {
867     if (!scrollableArea()->scrollbarsCanBeActive())
868         return;
869     if (!isScrollbarOverlayAPIAvailable())
870         return;
871
872     [m_scrollbarPainterController.get() beginScrollGesture];
873     [m_scrollbarPainterController.get() contentAreaScrolled];
874 }
875
876 void ScrollAnimatorMac::finishCurrentScrollAnimations()
877 {
878     if (isScrollbarOverlayAPIAvailable()) {
879         [m_scrollbarPainterController.get() hideOverlayScrollers];
880     }
881 }
882
883 void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
884 {
885     if (!isScrollbarOverlayAPIAvailable())
886         return;
887
888     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
889     if (!painter)
890         return;
891
892     ASSERT(!m_verticalScrollbarPainterDelegate);
893     m_verticalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
894
895     [painter setDelegate:m_verticalScrollbarPainterDelegate.get()];
896     [m_scrollbarPainterController.get() setVerticalScrollerImp:painter];
897     if (scrollableArea()->inLiveResize())
898         [painter setKnobAlpha:1];
899 }
900
901 void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
902 {
903     if (!isScrollbarOverlayAPIAvailable())
904         return;
905
906     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
907     if (!painter)
908         return;
909
910     ASSERT(m_verticalScrollbarPainterDelegate);
911     [m_verticalScrollbarPainterDelegate.get() invalidate];
912     m_verticalScrollbarPainterDelegate = nullptr;
913
914     [painter setDelegate:nil];
915     [m_scrollbarPainterController.get() setVerticalScrollerImp:nil];
916 }
917
918 void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
919 {
920     if (!isScrollbarOverlayAPIAvailable())
921         return;
922
923     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
924     if (!painter)
925         return;
926
927     ASSERT(!m_horizontalScrollbarPainterDelegate);
928     m_horizontalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
929
930     [painter setDelegate:m_horizontalScrollbarPainterDelegate.get()];
931     [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter];
932     if (scrollableArea()->inLiveResize())
933         [painter setKnobAlpha:1];
934 }
935
936 void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
937 {
938     if (!isScrollbarOverlayAPIAvailable())
939         return;
940
941     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
942     if (!painter)
943         return;
944
945     ASSERT(m_horizontalScrollbarPainterDelegate);
946     [m_horizontalScrollbarPainterDelegate.get() invalidate];
947     m_horizontalScrollbarPainterDelegate = nullptr;
948
949     [painter setDelegate:nil];
950     [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil];
951 }
952
953 bool ScrollAnimatorMac::shouldScrollbarParticipateInHitTesting(Scrollbar* scrollbar)
954 {
955     // Non-overlay scrollbars should always participate in hit testing.
956     if (recommendedScrollerStyle() != NSScrollerStyleOverlay)
957         return true;
958
959     if (!isScrollbarOverlayAPIAvailable())
960         return true;
961
962     if (scrollbar->isAlphaLocked())
963         return true;
964
965     // Overlay scrollbars should participate in hit testing whenever they are at all visible.
966     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
967     if (!painter)
968         return false;
969     return [painter knobAlpha] > 0;
970 }
971
972 void ScrollAnimatorMac::notifyContentAreaScrolled()
973 {
974     if (!isScrollbarOverlayAPIAvailable())
975         return;
976
977     // This function is called when a page is going into the page cache, but the page 
978     // isn't really scrolling in that case. We should only pass the message on to the
979     // ScrollbarPainterController when we're really scrolling on an active page.
980     if (scrollableArea()->scrollbarsCanBeActive())
981         sendContentAreaScrolledSoon();
982 }
983
984 void ScrollAnimatorMac::cancelAnimations()
985 {
986     m_haveScrolledSincePageLoad = false;
987
988     if (isScrollbarOverlayAPIAvailable()) {
989         if (scrollbarPaintTimerIsActive())
990             stopScrollbarPaintTimer();
991         [m_horizontalScrollbarPainterDelegate.get() cancelAnimations];
992         [m_verticalScrollbarPainterDelegate.get() cancelAnimations];
993     }
994 }
995
996 void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase)
997 {
998     // This may not have been set to true yet if the wheel event was handled by the ScrollingTree,
999     // So set it to true here.
1000     m_haveScrolledSincePageLoad = true;
1001
1002     if (phase == PlatformWheelEventPhaseBegan)
1003         didBeginScrollGesture();
1004     else if (phase == PlatformWheelEventPhaseEnded || phase == PlatformWheelEventPhaseCancelled)
1005         didEndScrollGesture();
1006     else if (phase == PlatformWheelEventPhaseMayBegin)
1007         mayBeginScrollGesture();
1008 }
1009
1010 #if ENABLE(RUBBER_BANDING)
1011 bool ScrollAnimatorMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
1012 {
1013     m_haveScrolledSincePageLoad = true;
1014
1015     if (!wheelEvent.hasPreciseScrollingDeltas() || !rubberBandingEnabledForSystem())
1016         return ScrollAnimator::handleWheelEvent(wheelEvent);
1017
1018     // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
1019     // up to the parent scrollable area. It takes advantage of the fact that
1020     // the base class implementation of handleWheelEvent will not accept the
1021     // wheel event if there is nowhere to scroll.
1022     if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
1023         if (!allowsVerticalStretching())
1024             return ScrollAnimator::handleWheelEvent(wheelEvent);
1025     } else {
1026         if (!allowsHorizontalStretching())
1027             return ScrollAnimator::handleWheelEvent(wheelEvent);
1028     }
1029
1030     bool didHandleEvent = m_scrollElasticityController.handleWheelEvent(wheelEvent);
1031
1032     if (didHandleEvent)
1033         handleWheelEventPhase(wheelEvent.phase());
1034
1035     return didHandleEvent;
1036 }
1037
1038 bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
1039 {
1040     FloatSize limitDelta;
1041     if (fabsf(deltaY) >= fabsf(deltaX)) {
1042         if (deltaY < 0) {
1043             // We are trying to scroll up.  Make sure we are not pinned to the top
1044             limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
1045         } else {
1046             // We are trying to scroll down.  Make sure we are not pinned to the bottom
1047             limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
1048         }
1049     } else if (deltaX != 0) {
1050         if (deltaX < 0) {
1051             // We are trying to scroll left.  Make sure we are not pinned to the left
1052             limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
1053         } else {
1054             // We are trying to scroll right.  Make sure we are not pinned to the right
1055             limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
1056         }
1057     }
1058     
1059     if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
1060         return true;
1061     return false;
1062 }
1063
1064 bool ScrollAnimatorMac::allowsVerticalStretching()
1065 {
1066     switch (m_scrollableArea->verticalScrollElasticity()) {
1067     case ScrollElasticityAutomatic: {
1068         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1069         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1070         return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
1071     }
1072     case ScrollElasticityNone:
1073         return false;
1074     case ScrollElasticityAllowed:
1075         return true;
1076     }
1077
1078     ASSERT_NOT_REACHED();
1079     return false;
1080 }
1081
1082 bool ScrollAnimatorMac::allowsHorizontalStretching()
1083 {
1084     switch (m_scrollableArea->horizontalScrollElasticity()) {
1085     case ScrollElasticityAutomatic: {
1086         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1087         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1088         return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
1089     }
1090     case ScrollElasticityNone:
1091         return false;
1092     case ScrollElasticityAllowed:
1093         return true;
1094     }
1095
1096     ASSERT_NOT_REACHED();
1097     return false;
1098 }
1099
1100 IntSize ScrollAnimatorMac::stretchAmount()
1101 {
1102     return m_scrollableArea->overhangAmount();
1103 }
1104
1105 bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction)
1106 {
1107     return pinnedInDirection(direction.width(), direction.height());
1108 }
1109
1110 bool ScrollAnimatorMac::canScrollHorizontally()
1111 {
1112     Scrollbar* scrollbar = m_scrollableArea->horizontalScrollbar();
1113     if (!scrollbar)
1114         return false;
1115     return scrollbar->enabled();
1116 }
1117
1118 bool ScrollAnimatorMac::canScrollVertically()
1119 {
1120     Scrollbar* scrollbar = m_scrollableArea->verticalScrollbar();
1121     if (!scrollbar)
1122         return false;
1123     return scrollbar->enabled();
1124 }
1125
1126 bool ScrollAnimatorMac::shouldRubberBandInDirection(ScrollDirection direction)
1127 {
1128     return m_scrollableArea->shouldRubberBandInDirection(direction);
1129 }
1130
1131 IntPoint ScrollAnimatorMac::absoluteScrollPosition()
1132 {
1133     return m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin();
1134 }
1135
1136 void ScrollAnimatorMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& delta)
1137 {
1138     m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1139     immediateScrollBy(delta);
1140     m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1141 }
1142
1143 void ScrollAnimatorMac::immediateScrollBy(const FloatSize& delta)
1144 {
1145     FloatPoint newPos = adjustScrollPositionIfNecessary(FloatPoint(m_currentPosX, m_currentPosY) + delta);
1146     if (newPos.x() == m_currentPosX && newPos.y() == m_currentPosY)
1147         return;
1148
1149     m_currentPosX = newPos.x();
1150     m_currentPosY = newPos.y();
1151     notifyPositionChanged();
1152 }
1153
1154 void ScrollAnimatorMac::startSnapRubberbandTimer()
1155 {
1156     m_snapRubberBandTimer.startRepeating(1.0 / 60.0);
1157 }
1158
1159 void ScrollAnimatorMac::stopSnapRubberbandTimer()
1160 {
1161     m_snapRubberBandTimer.stop();
1162 }
1163
1164 void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
1165 {
1166     m_scrollElasticityController.snapRubberBandTimerFired();
1167 }
1168 #endif
1169
1170 void ScrollAnimatorMac::setIsActive()
1171 {
1172     if (!isScrollbarOverlayAPIAvailable())
1173         return;
1174
1175     if (!m_needsScrollerStyleUpdate)
1176         return;
1177
1178     updateScrollerStyle();
1179 }
1180
1181 void ScrollAnimatorMac::updateScrollerStyle()
1182 {
1183     if (!isScrollbarOverlayAPIAvailable())
1184         return;
1185
1186     if (!scrollableArea()->scrollbarsCanBeActive()) {
1187         m_needsScrollerStyleUpdate = true;
1188         return;
1189     }
1190
1191     ScrollbarThemeMac* macTheme = macScrollbarTheme();
1192     if (!macTheme) {
1193         m_needsScrollerStyleUpdate = false;
1194         return;
1195     }
1196
1197     NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle];
1198
1199     if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
1200         verticalScrollbar->invalidate();
1201
1202         ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp];
1203         ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 
1204                                                                                     controlSize:(NSControlSize)verticalScrollbar->controlSize() 
1205                                                                                     horizontal:NO 
1206                                                                                     replacingScrollerImp:oldVerticalPainter];
1207         [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter];
1208         macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
1209
1210         // The different scrollbar styles have different thicknesses, so we must re-set the 
1211         // frameRect to the new thickness, and the re-layout below will ensure the position
1212         // and length are properly updated.
1213         int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
1214         verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1215     }
1216
1217     if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
1218         horizontalScrollbar->invalidate();
1219
1220         ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp];
1221         ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 
1222                                                                                     controlSize:(NSControlSize)horizontalScrollbar->controlSize() 
1223                                                                                     horizontal:YES 
1224                                                                                     replacingScrollerImp:oldHorizontalPainter];
1225         [m_scrollbarPainterController.get() setHorizontalScrollerImp:newHorizontalPainter];
1226         macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
1227
1228         // The different scrollbar styles have different thicknesses, so we must re-set the 
1229         // frameRect to the new thickness, and the re-layout below will ensure the position
1230         // and length are properly updated.
1231         int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
1232         horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1233     }
1234
1235     // If m_needsScrollerStyleUpdate is true, then the page is restoring from the page cache, and 
1236     // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves.
1237     scrollableArea()->scrollbarStyleChanged(newStyle, !m_needsScrollerStyleUpdate);
1238
1239     m_needsScrollerStyleUpdate = false;
1240 }
1241
1242 void ScrollAnimatorMac::startScrollbarPaintTimer()
1243 {
1244     m_initialScrollbarPaintTimer.startOneShot(0.1);
1245 }
1246
1247 bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1248 {
1249     return m_initialScrollbarPaintTimer.isActive();
1250 }
1251
1252 void ScrollAnimatorMac::stopScrollbarPaintTimer()
1253 {
1254     m_initialScrollbarPaintTimer.stop();
1255 }
1256
1257 void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1258 {
1259     if (isScrollbarOverlayAPIAvailable()) {
1260         // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController
1261         // might think that the scrollbars are already showing and bail early.
1262         [m_scrollbarPainterController.get() hideOverlayScrollers];
1263         [m_scrollbarPainterController.get() flashScrollers];
1264     }
1265 }
1266
1267 void ScrollAnimatorMac::sendContentAreaScrolledSoon()
1268 {
1269     if (!m_sendContentAreaScrolledTimer.isActive())
1270         m_sendContentAreaScrolledTimer.startOneShot(0);
1271 }
1272
1273 void ScrollAnimatorMac::sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*)
1274 {
1275     [m_scrollbarPainterController.get() contentAreaScrolled];
1276 }
1277
1278 void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1279 {
1280     IntRect rectInViewCoordinates = scrollerThumb;
1281     if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1282         rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1283
1284     if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1285         return;
1286
1287     m_scrollableArea->setVisibleScrollerThumbRect(rectInViewCoordinates);
1288     m_visibleScrollerThumbRect = rectInViewCoordinates;
1289 }
1290
1291 } // namespace WebCore
1292
1293 #endif // ENABLE(SMOOTH_SCROLLING)