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