Update To 11.40.268.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/Timer.h"
33 #include "platform/animation/TimingFunction.h"
34 #include "platform/geometry/FloatRect.h"
35 #include "platform/geometry/IntRect.h"
36 #include "platform/mac/BlockExceptions.h"
37 #include "platform/mac/NSScrollerImpDetails.h"
38 #include "platform/scroll/ScrollableArea.h"
39 #include "platform/scroll/ScrollbarTheme.h"
40 #include "platform/scroll/ScrollbarThemeMacCommon.h"
41 #include "platform/scroll/ScrollbarThemeMacOverlayAPI.h"
42 #include "wtf/MainThread.h"
43 #include "wtf/PassRefPtr.h"
44
45 using namespace blink;
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 @class WebScrollbarPartAnimation;
297
298 namespace blink {
299
300 // This class is used to drive the animation timer for WebScrollbarPartAnimation
301 // objects. This is used instead of NSAnimation because CoreAnimation
302 // establishes connections to the WindowServer, which should not be done in a
303 // sandboxed renderer process.
304 class WebScrollbarPartAnimationTimer {
305 public:
306     WebScrollbarPartAnimationTimer(WebScrollbarPartAnimation* animation,
307                                    CFTimeInterval duration)
308         : m_timer(this, &WebScrollbarPartAnimationTimer::timerFired)
309         , m_startTime(0.0)
310         , m_duration(duration)
311         , m_animation(animation)
312         , m_timingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut))
313     {
314     }
315
316     ~WebScrollbarPartAnimationTimer() {}
317
318     void start()
319     {
320         m_startTime = WTF::currentTime();
321         // Set the framerate of the animation. NSAnimation uses a default
322         // framerate of 60 Hz, so use that here.
323         m_timer.startRepeating(1.0 / 60.0, FROM_HERE);
324     }
325
326     void stop()
327     {
328         m_timer.stop();
329         [m_animation setCurrentProgress:1];
330     }
331
332     void setDuration(CFTimeInterval duration)
333     {
334         m_duration = duration;
335     }
336
337 private:
338     void timerFired(Timer<WebScrollbarPartAnimationTimer>*)
339     {
340         double currentTime = WTF::currentTime();
341         double delta = currentTime - m_startTime;
342
343         if (delta >= m_duration) {
344             stop();
345             return;
346         }
347
348         double fraction = delta / m_duration;
349         double progress = m_timingFunction->evaluate(fraction, 0.001);
350         [m_animation setCurrentProgress:progress];
351     }
352
353     Timer<WebScrollbarPartAnimationTimer> m_timer;
354     double m_startTime;  // In seconds.
355     double m_duration;   // In seconds.
356     WebScrollbarPartAnimation* m_animation;  // Weak, owns this.
357     RefPtr<CubicBezierTimingFunction> m_timingFunction;
358 };
359
360 }  // namespace blink
361
362 @interface WebScrollbarPartAnimation : NSObject {
363     Scrollbar* _scrollbar;
364     OwnPtr<WebScrollbarPartAnimationTimer> _timer;
365     RetainPtr<ScrollbarPainter> _scrollbarPainter;
366     FeatureToAnimate _featureToAnimate;
367     CGFloat _startValue;
368     CGFloat _endValue;
369 }
370 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration;
371 @end
372
373 @implementation WebScrollbarPartAnimation
374
375 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration
376 {
377     self = [super init];
378     if (!self)
379         return nil;
380
381     _timer = adoptPtr(new WebScrollbarPartAnimationTimer(self, duration));
382     _scrollbar = scrollbar;
383     _featureToAnimate = featureToAnimate;
384     _startValue = startValue;
385     _endValue = endValue;
386
387     return self;
388 }
389
390 - (void)startAnimation
391 {
392     ASSERT(_scrollbar);
393
394     _scrollbarPainter = scrollbarPainterForScrollbar(_scrollbar);
395     _timer->start();
396 }
397
398 - (void)stopAnimation
399 {
400     _timer->stop();
401 }
402
403 - (void)setDuration:(CFTimeInterval)duration
404 {
405     _timer->setDuration(duration);
406 }
407
408 - (void)setStartValue:(CGFloat)startValue
409 {
410     _startValue = startValue;
411 }
412
413 - (void)setEndValue:(CGFloat)endValue
414 {
415     _endValue = endValue;
416 }
417
418 - (void)setCurrentProgress:(NSAnimationProgress)progress
419 {
420     ASSERT(_scrollbar);
421
422     CGFloat currentValue;
423     if (_startValue > _endValue)
424         currentValue = 1 - progress;
425     else
426         currentValue = progress;
427
428     switch (_featureToAnimate) {
429     case ThumbAlpha:
430         [_scrollbarPainter.get() setKnobAlpha:currentValue];
431         break;
432     case TrackAlpha:
433         [_scrollbarPainter.get() setTrackAlpha:currentValue];
434         break;
435     case UIStateTransition:
436         [_scrollbarPainter.get() setUiStateTransitionProgress:currentValue];
437         break;
438     case ExpansionTransition:
439         [_scrollbarPainter.get() setExpansionTransitionProgress:currentValue];
440         break;
441     }
442
443     _scrollbar->invalidate();
444 }
445
446 - (void)invalidate
447 {
448     BEGIN_BLOCK_OBJC_EXCEPTIONS;
449     [self stopAnimation];
450     END_BLOCK_OBJC_EXCEPTIONS;
451     _scrollbar = 0;
452 }
453
454 @end
455
456 @interface WebScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
457 {
458     blink::Scrollbar* _scrollbar;
459
460     RetainPtr<WebScrollbarPartAnimation> _knobAlphaAnimation;
461     RetainPtr<WebScrollbarPartAnimation> _trackAlphaAnimation;
462     RetainPtr<WebScrollbarPartAnimation> _uiStateTransitionAnimation;
463     RetainPtr<WebScrollbarPartAnimation> _expansionTransitionAnimation;
464 }
465 - (id)initWithScrollbar:(blink::Scrollbar*)scrollbar;
466 - (void)cancelAnimations;
467 @end
468
469 @implementation WebScrollbarPainterDelegate
470
471 - (id)initWithScrollbar:(blink::Scrollbar*)scrollbar
472 {
473     self = [super init];
474     if (!self)
475         return nil;
476
477     _scrollbar = scrollbar;
478     return self;
479 }
480
481 - (void)cancelAnimations
482 {
483     BEGIN_BLOCK_OBJC_EXCEPTIONS;
484     [_knobAlphaAnimation.get() stopAnimation];
485     [_trackAlphaAnimation.get() stopAnimation];
486     [_uiStateTransitionAnimation.get() stopAnimation];
487     [_expansionTransitionAnimation.get() stopAnimation];
488     END_BLOCK_OBJC_EXCEPTIONS;
489 }
490
491 - (ScrollAnimatorMac*)scrollAnimator
492 {
493     return static_cast<ScrollAnimatorMac*>(_scrollbar->scrollableArea()->scrollAnimator());
494 }
495
496 - (NSRect)convertRectToBacking:(NSRect)aRect
497 {
498     return aRect;
499 }
500
501 - (NSRect)convertRectFromBacking:(NSRect)aRect
502 {
503     return aRect;
504 }
505
506 - (NSPoint)mouseLocationInScrollerForScrollerImp:(id)scrollerImp
507 {
508     if (!_scrollbar)
509         return NSZeroPoint;
510
511     ASSERT_UNUSED(scrollerImp, scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
512
513     return _scrollbar->convertFromContainingView(_scrollbar->scrollableArea()->lastKnownMousePosition());
514 }
515
516 - (void)setUpAlphaAnimation:(RetainPtr<WebScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(ScrollbarPainter)scrollerPainter part:(blink::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
517 {
518     // If the user has scrolled the page, then the scrollbars must be animated here.
519     // This overrides the early returns.
520     bool mustAnimate = [self scrollAnimator]->haveScrolledSincePageLoad();
521
522     if ([self scrollAnimator]->scrollbarPaintTimerIsActive() && !mustAnimate)
523         return;
524
525     if (_scrollbar->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
526         [self scrollAnimator]->startScrollbarPaintTimer();
527         return;
528     }
529
530     // At this point, we are definitely going to animate now, so stop the timer.
531     [self scrollAnimator]->stopScrollbarPaintTimer();
532
533     // If we are currently animating, stop
534     if (scrollbarPartAnimation) {
535         [scrollbarPartAnimation.get() stopAnimation];
536         scrollbarPartAnimation = nullptr;
537     }
538
539     if (part == blink::ThumbPart && _scrollbar->orientation() == VerticalScrollbar) {
540         if (newAlpha == 1) {
541             IntRect thumbRect = IntRect([scrollerPainter rectForPart:NSScrollerKnob]);
542             [self scrollAnimator]->setVisibleScrollerThumbRect(thumbRect);
543         } else
544             [self scrollAnimator]->setVisibleScrollerThumbRect(IntRect());
545     }
546
547     scrollbarPartAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
548                                                                        featureToAnimate:part == ThumbPart ? ThumbAlpha : TrackAlpha
549                                                                             animateFrom:part == ThumbPart ? [scrollerPainter knobAlpha] : [scrollerPainter trackAlpha]
550                                                                               animateTo:newAlpha
551                                                                                duration:duration]);
552     [scrollbarPartAnimation.get() startAnimation];
553 }
554
555 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
556 {
557     if (!_scrollbar)
558         return;
559
560     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
561
562     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
563     [self setUpAlphaAnimation:_knobAlphaAnimation scrollerPainter:scrollerPainter part:blink::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
564 }
565
566 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
567 {
568     if (!_scrollbar)
569         return;
570
571     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
572
573     ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
574     [self setUpAlphaAnimation:_trackAlphaAnimation scrollerPainter:scrollerPainter part:blink::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
575 }
576
577 - (void)scrollerImp:(id)scrollerImp animateUIStateTransitionWithDuration:(NSTimeInterval)duration
578 {
579     if (!_scrollbar)
580         return;
581
582     if (!supportsUIStateTransitionProgress())
583         return;
584
585     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
586
587     ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
588
589     // UIStateTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
590     [scrollbarPainter setUiStateTransitionProgress:1 - [scrollerImp uiStateTransitionProgress]];
591
592     if (!_uiStateTransitionAnimation)
593         _uiStateTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
594                                                                                 featureToAnimate:UIStateTransition
595                                                                                      animateFrom:[scrollbarPainter uiStateTransitionProgress]
596                                                                                        animateTo:1.0
597                                                                                         duration:duration]);
598     else {
599         // If we don't need to initialize the animation, just reset the values in case they have changed.
600         [_uiStateTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
601         [_uiStateTransitionAnimation.get() setEndValue:1.0];
602         [_uiStateTransitionAnimation.get() setDuration:duration];
603     }
604     [_uiStateTransitionAnimation.get() startAnimation];
605 }
606
607 - (void)scrollerImp:(id)scrollerImp animateExpansionTransitionWithDuration:(NSTimeInterval)duration
608 {
609     if (!_scrollbar)
610         return;
611
612     if (!supportsExpansionTransitionProgress())
613         return;
614
615     ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
616
617     ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
618
619     // ExpansionTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
620     [scrollbarPainter setExpansionTransitionProgress:1 - [scrollerImp expansionTransitionProgress]];
621
622     if (!_expansionTransitionAnimation) {
623         _expansionTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
624                                                                                   featureToAnimate:ExpansionTransition
625                                                                                        animateFrom:[scrollbarPainter expansionTransitionProgress]
626                                                                                          animateTo:1.0
627                                                                                           duration:duration]);
628     } else {
629         // If we don't need to initialize the animation, just reset the values in case they have changed.
630         [_expansionTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
631         [_expansionTransitionAnimation.get() setEndValue:1.0];
632         [_expansionTransitionAnimation.get() setDuration:duration];
633     }
634     [_expansionTransitionAnimation.get() startAnimation];
635 }
636
637 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
638 {
639 }
640
641 - (void)invalidate
642 {
643     _scrollbar = 0;
644     BEGIN_BLOCK_OBJC_EXCEPTIONS;
645     [_knobAlphaAnimation.get() invalidate];
646     [_trackAlphaAnimation.get() invalidate];
647     [_uiStateTransitionAnimation.get() invalidate];
648     [_expansionTransitionAnimation.get() invalidate];
649     END_BLOCK_OBJC_EXCEPTIONS;
650 }
651
652 @end
653
654 namespace blink {
655
656 PassRefPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
657 {
658     return adoptRef(new ScrollAnimatorMac(scrollableArea));
659 }
660
661 ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
662     : ScrollAnimator(scrollableArea)
663     , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
664     , m_sendContentAreaScrolledTimer(this, &ScrollAnimatorMac::sendContentAreaScrolledTimerFired)
665 #if USE(RUBBER_BANDING)
666     , m_scrollElasticityController(this)
667     , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
668 #endif
669     , m_haveScrolledSincePageLoad(false)
670     , m_needsScrollerStyleUpdate(false)
671 {
672     m_scrollAnimationHelperDelegate.adoptNS([[WebScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
673     m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
674
675     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
676         m_scrollbarPainterControllerDelegate.adoptNS([[WebScrollbarPainterControllerDelegate alloc] initWithScrollableArea:scrollableArea]);
677         m_scrollbarPainterController = [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease];
678         [m_scrollbarPainterController.get() setDelegate:m_scrollbarPainterControllerDelegate.get()];
679         [m_scrollbarPainterController.get() setScrollerStyle:ScrollbarThemeMacCommon::recommendedScrollerStyle()];
680     }
681 }
682
683 ScrollAnimatorMac::~ScrollAnimatorMac()
684 {
685     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
686         BEGIN_BLOCK_OBJC_EXCEPTIONS;
687         [m_scrollbarPainterControllerDelegate.get() invalidate];
688         [m_scrollbarPainterController.get() setDelegate:nil];
689         [m_horizontalScrollbarPainterDelegate.get() invalidate];
690         [m_verticalScrollbarPainterDelegate.get() invalidate];
691         [m_scrollAnimationHelperDelegate.get() invalidate];
692         END_BLOCK_OBJC_EXCEPTIONS;
693     }
694 }
695
696 static bool scrollAnimationEnabledForSystem()
697 {
698     NSString* scrollAnimationDefaultsKey =
699         @"AppleScrollAnimationEnabled";
700     static bool enabled = [[NSUserDefaults standardUserDefaults] boolForKey:scrollAnimationDefaultsKey];
701     return enabled;
702 }
703
704 #if USE(RUBBER_BANDING)
705 static bool rubberBandingEnabledForSystem()
706 {
707     static bool initialized = false;
708     static bool enabled = true;
709     // Caches the result, which is consistent with other apps like the Finder, which all
710     // require a restart after changing this default.
711     if (!initialized) {
712         // Uses -objectForKey: and not -boolForKey: in order to default to true if the value wasn't set.
713         id value = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSScrollViewRubberbanding"];
714         if ([value isKindOfClass:[NSNumber class]])
715             enabled = [value boolValue];
716         initialized = true;
717     }
718     return enabled;
719 }
720 #endif
721
722 bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta)
723 {
724     m_haveScrolledSincePageLoad = true;
725
726     if (!scrollAnimationEnabledForSystem() || !m_scrollableArea->scrollAnimatorEnabled())
727         return ScrollAnimator::scroll(orientation, granularity, step, delta);
728
729     if (granularity == ScrollByPixel)
730         return ScrollAnimator::scroll(orientation, granularity, step, delta);
731
732     float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
733     float newPos = std::max<float>(std::min<float>(currentPos + (step * delta), m_scrollableArea->maximumScrollPosition(orientation)), m_scrollableArea->minimumScrollPosition(orientation));
734     if (currentPos == newPos)
735         return false;
736
737     NSPoint newPoint;
738     if ([m_scrollAnimationHelper.get() _isAnimating]) {
739         NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
740         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
741     } else
742         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
743
744     [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
745     return true;
746 }
747
748 void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
749 {
750     [m_scrollAnimationHelper.get() _stopRun];
751     immediateScrollTo(offset);
752 }
753
754 FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
755 {
756     if (!m_scrollableArea->constrainsScrollingToContentEdge())
757         return position;
758
759     IntPoint minPos = m_scrollableArea->minimumScrollPosition();
760     IntPoint maxPos = m_scrollableArea->maximumScrollPosition();
761
762     float newX = std::max<float>(std::min<float>(position.x(), maxPos.x()), minPos.x());
763     float newY = std::max<float>(std::min<float>(position.y(), maxPos.y()), minPos.y());
764
765     return FloatPoint(newX, newY);
766 }
767
768 void ScrollAnimatorMac::adjustScrollPositionToBoundsIfNecessary()
769 {
770     bool currentlyConstrainsToContentEdge = m_scrollableArea->constrainsScrollingToContentEdge();
771     m_scrollableArea->setConstrainsScrollingToContentEdge(true);
772
773     IntPoint currentScrollPosition = absoluteScrollPosition();
774     FloatPoint nearestPointWithinBounds = adjustScrollPositionIfNecessary(absoluteScrollPosition());
775     immediateScrollBy(nearestPointWithinBounds - currentScrollPosition);
776
777     m_scrollableArea->setConstrainsScrollingToContentEdge(currentlyConstrainsToContentEdge);
778 }
779
780 void ScrollAnimatorMac::immediateScrollTo(const FloatPoint& newPosition)
781 {
782     FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
783
784     bool positionChanged = adjustedPosition.x() != m_currentPosX || adjustedPosition.y() != m_currentPosY;
785     if (!positionChanged && !scrollableArea()->scrollOriginChanged())
786         return;
787
788     FloatSize delta = FloatSize(adjustedPosition.x() - m_currentPosX, adjustedPosition.y() - m_currentPosY);
789
790     m_currentPosX = adjustedPosition.x();
791     m_currentPosY = adjustedPosition.y();
792     notifyContentAreaScrolled(delta);
793     notifyPositionChanged();
794 }
795
796 bool ScrollAnimatorMac::isRubberBandInProgress() const
797 {
798 #if !USE(RUBBER_BANDING)
799     return false;
800 #else
801     return m_scrollElasticityController.isRubberBandInProgress();
802 #endif
803 }
804
805 void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition)
806 {
807     ASSERT(m_scrollAnimationHelper);
808     immediateScrollTo(newPosition);
809 }
810
811 void ScrollAnimatorMac::contentAreaWillPaint() const
812 {
813     if (!scrollableArea()->scrollbarsCanBeActive())
814         return;
815     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
816         [m_scrollbarPainterController.get() contentAreaWillDraw];
817 }
818
819 void ScrollAnimatorMac::mouseEnteredContentArea() const
820 {
821     if (!scrollableArea()->scrollbarsCanBeActive())
822         return;
823     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
824         [m_scrollbarPainterController.get() mouseEnteredContentArea];
825 }
826
827 void ScrollAnimatorMac::mouseExitedContentArea() const
828 {
829     if (!scrollableArea()->scrollbarsCanBeActive())
830         return;
831     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
832         [m_scrollbarPainterController.get() mouseExitedContentArea];
833 }
834
835 void ScrollAnimatorMac::mouseMovedInContentArea() const
836 {
837     if (!scrollableArea()->scrollbarsCanBeActive())
838         return;
839     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
840         [m_scrollbarPainterController.get() mouseMovedInContentArea];
841 }
842
843 void ScrollAnimatorMac::mouseEnteredScrollbar(Scrollbar* scrollbar) const
844 {
845     // At this time, only legacy scrollbars needs to send notifications here.
846     if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy)
847         return;
848
849     if (!scrollableArea()->scrollbarsCanBeActive())
850         return;
851
852     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
853         if (!supportsUIStateTransitionProgress())
854             return;
855         if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
856             [painter mouseEnteredScroller];
857     }
858 }
859
860 void ScrollAnimatorMac::mouseExitedScrollbar(Scrollbar* scrollbar) const
861 {
862     // At this time, only legacy scrollbars needs to send notifications here.
863     if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy)
864         return;
865
866     if (!scrollableArea()->scrollbarsCanBeActive())
867         return;
868
869     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
870         if (!supportsUIStateTransitionProgress())
871             return;
872         if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
873             [painter mouseExitedScroller];
874     }
875 }
876
877 void ScrollAnimatorMac::willStartLiveResize()
878 {
879     if (!scrollableArea()->scrollbarsCanBeActive())
880         return;
881     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
882         [m_scrollbarPainterController.get() startLiveResize];
883 }
884
885 void ScrollAnimatorMac::contentsResized() const
886 {
887     if (!scrollableArea()->scrollbarsCanBeActive())
888         return;
889     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
890         [m_scrollbarPainterController.get() contentAreaDidResize];
891 }
892
893 void ScrollAnimatorMac::willEndLiveResize()
894 {
895     if (!scrollableArea()->scrollbarsCanBeActive())
896         return;
897     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
898         [m_scrollbarPainterController.get() endLiveResize];
899 }
900
901 void ScrollAnimatorMac::contentAreaDidShow() const
902 {
903     if (!scrollableArea()->scrollbarsCanBeActive())
904         return;
905     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
906         [m_scrollbarPainterController.get() windowOrderedIn];
907 }
908
909 void ScrollAnimatorMac::contentAreaDidHide() const
910 {
911     if (!scrollableArea()->scrollbarsCanBeActive())
912         return;
913     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
914         [m_scrollbarPainterController.get() windowOrderedOut];
915 }
916
917 void ScrollAnimatorMac::didBeginScrollGesture() const
918 {
919     if (!scrollableArea()->scrollbarsCanBeActive())
920         return;
921     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
922         [m_scrollbarPainterController.get() beginScrollGesture];
923 }
924
925 void ScrollAnimatorMac::didEndScrollGesture() const
926 {
927     if (!scrollableArea()->scrollbarsCanBeActive())
928         return;
929     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
930         [m_scrollbarPainterController.get() endScrollGesture];
931 }
932
933 void ScrollAnimatorMac::mayBeginScrollGesture() const
934 {
935     if (!scrollableArea()->scrollbarsCanBeActive())
936         return;
937     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
938         return;
939
940     [m_scrollbarPainterController.get() beginScrollGesture];
941     [m_scrollbarPainterController.get() contentAreaScrolled];
942 }
943
944 void ScrollAnimatorMac::finishCurrentScrollAnimations()
945 {
946     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
947         [m_scrollbarPainterController.get() hideOverlayScrollers];
948     }
949 }
950
951 void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
952 {
953     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
954         return;
955
956     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
957     if (!painter)
958         return;
959
960     ASSERT(!m_verticalScrollbarPainterDelegate);
961     m_verticalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
962
963     [painter setDelegate:m_verticalScrollbarPainterDelegate.get()];
964     [m_scrollbarPainterController.get() setVerticalScrollerImp:painter];
965     if (scrollableArea()->inLiveResize())
966         [painter setKnobAlpha:1];
967 }
968
969 void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
970 {
971     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
972         return;
973
974     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
975     if (!painter)
976         return;
977
978     ASSERT(m_verticalScrollbarPainterDelegate);
979     [m_verticalScrollbarPainterDelegate.get() invalidate];
980     m_verticalScrollbarPainterDelegate = nullptr;
981
982     [painter setDelegate:nil];
983     [m_scrollbarPainterController.get() setVerticalScrollerImp:nil];
984 }
985
986 void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
987 {
988     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
989         return;
990
991     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
992     if (!painter)
993         return;
994
995     ASSERT(!m_horizontalScrollbarPainterDelegate);
996     m_horizontalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
997
998     [painter setDelegate:m_horizontalScrollbarPainterDelegate.get()];
999     [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter];
1000     if (scrollableArea()->inLiveResize())
1001         [painter setKnobAlpha:1];
1002 }
1003
1004 void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
1005 {
1006     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1007         return;
1008
1009     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
1010     if (!painter)
1011         return;
1012
1013     ASSERT(m_horizontalScrollbarPainterDelegate);
1014     [m_horizontalScrollbarPainterDelegate.get() invalidate];
1015     m_horizontalScrollbarPainterDelegate = nullptr;
1016
1017     [painter setDelegate:nil];
1018     [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil];
1019 }
1020
1021 bool ScrollAnimatorMac::shouldScrollbarParticipateInHitTesting(Scrollbar* scrollbar)
1022 {
1023     // Non-overlay scrollbars should always participate in hit testing.
1024     if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleOverlay)
1025         return true;
1026
1027     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1028         return true;
1029
1030     if (scrollbar->isAlphaLocked())
1031         return true;
1032
1033     // Overlay scrollbars should participate in hit testing whenever they are at all visible.
1034     ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
1035     if (!painter)
1036         return false;
1037     return [painter knobAlpha] > 0;
1038 }
1039
1040 void ScrollAnimatorMac::notifyContentAreaScrolled(const FloatSize& delta)
1041 {
1042     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1043         return;
1044
1045     // This function is called when a page is going into the page cache, but the page
1046     // isn't really scrolling in that case. We should only pass the message on to the
1047     // ScrollbarPainterController when we're really scrolling on an active page.
1048     if (scrollableArea()->scrollbarsCanBeActive())
1049         sendContentAreaScrolledSoon(delta);
1050 }
1051
1052 void ScrollAnimatorMac::cancelAnimations()
1053 {
1054     m_haveScrolledSincePageLoad = false;
1055
1056     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
1057         if (scrollbarPaintTimerIsActive())
1058             stopScrollbarPaintTimer();
1059         [m_horizontalScrollbarPainterDelegate.get() cancelAnimations];
1060         [m_verticalScrollbarPainterDelegate.get() cancelAnimations];
1061     }
1062 }
1063
1064 void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase)
1065 {
1066     // This may not have been set to true yet if the wheel event was handled by the ScrollingTree,
1067     // So set it to true here.
1068     m_haveScrolledSincePageLoad = true;
1069
1070     if (phase == PlatformWheelEventPhaseBegan)
1071         didBeginScrollGesture();
1072     else if (phase == PlatformWheelEventPhaseEnded || phase == PlatformWheelEventPhaseCancelled)
1073         didEndScrollGesture();
1074     else if (phase == PlatformWheelEventPhaseMayBegin)
1075         mayBeginScrollGesture();
1076 }
1077
1078 #if USE(RUBBER_BANDING)
1079 bool ScrollAnimatorMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
1080 {
1081     m_haveScrolledSincePageLoad = true;
1082
1083     if (!wheelEvent.hasPreciseScrollingDeltas() || !rubberBandingEnabledForSystem())
1084         return ScrollAnimator::handleWheelEvent(wheelEvent);
1085
1086     // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
1087     // up to the parent scrollable area. It takes advantage of the fact that
1088     // the base class implementation of handleWheelEvent will not accept the
1089     // wheel event if there is nowhere to scroll.
1090     if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
1091         if (!allowsVerticalStretching())
1092             return ScrollAnimator::handleWheelEvent(wheelEvent);
1093     } else {
1094         if (!allowsHorizontalStretching())
1095             return ScrollAnimator::handleWheelEvent(wheelEvent);
1096     }
1097
1098     bool didHandleEvent = m_scrollElasticityController.handleWheelEvent(wheelEvent);
1099
1100     // The elasticity controller can return false on a phase end event if rubber banding wasn't in progress.
1101     // In this case, the wheel phase must still be handled so that that overlay scroll bars get hidden.
1102     if (didHandleEvent || wheelEvent.phase() == PlatformWheelEventPhaseEnded || wheelEvent.phase() == PlatformWheelEventPhaseCancelled)
1103         handleWheelEventPhase(wheelEvent.phase());
1104
1105     return didHandleEvent;
1106 }
1107
1108 bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
1109 {
1110     FloatSize limitDelta;
1111     if (fabsf(deltaY) >= fabsf(deltaX)) {
1112         if (deltaY < 0) {
1113             // We are trying to scroll up.  Make sure we are not pinned to the top
1114             limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
1115         } else {
1116             // We are trying to scroll down.  Make sure we are not pinned to the bottom
1117             limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
1118         }
1119     } else if (deltaX != 0) {
1120         if (deltaX < 0) {
1121             // We are trying to scroll left.  Make sure we are not pinned to the left
1122             limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
1123         } else {
1124             // We are trying to scroll right.  Make sure we are not pinned to the right
1125             limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
1126         }
1127     }
1128
1129     if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
1130         return true;
1131     return false;
1132 }
1133
1134 bool ScrollAnimatorMac::allowsVerticalStretching()
1135 {
1136     switch (m_scrollableArea->verticalScrollElasticity()) {
1137     case ScrollElasticityAutomatic: {
1138         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1139         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1140         return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
1141     }
1142     case ScrollElasticityNone:
1143         return false;
1144     case ScrollElasticityAllowed:
1145         return true;
1146     }
1147
1148     ASSERT_NOT_REACHED();
1149     return false;
1150 }
1151
1152 bool ScrollAnimatorMac::allowsHorizontalStretching()
1153 {
1154     switch (m_scrollableArea->horizontalScrollElasticity()) {
1155     case ScrollElasticityAutomatic: {
1156         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1157         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1158         return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
1159     }
1160     case ScrollElasticityNone:
1161         return false;
1162     case ScrollElasticityAllowed:
1163         return true;
1164     }
1165
1166     ASSERT_NOT_REACHED();
1167     return false;
1168 }
1169
1170 IntSize ScrollAnimatorMac::stretchAmount()
1171 {
1172     return m_scrollableArea->overhangAmount();
1173 }
1174
1175 bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction)
1176 {
1177     return pinnedInDirection(direction.width(), direction.height());
1178 }
1179
1180 bool ScrollAnimatorMac::canScrollHorizontally()
1181 {
1182     Scrollbar* scrollbar = m_scrollableArea->horizontalScrollbar();
1183     if (!scrollbar)
1184         return false;
1185     return scrollbar->enabled();
1186 }
1187
1188 bool ScrollAnimatorMac::canScrollVertically()
1189 {
1190     Scrollbar* scrollbar = m_scrollableArea->verticalScrollbar();
1191     if (!scrollbar)
1192         return false;
1193     return scrollbar->enabled();
1194 }
1195
1196 IntPoint ScrollAnimatorMac::absoluteScrollPosition()
1197 {
1198     return m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin();
1199 }
1200
1201 void ScrollAnimatorMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& delta)
1202 {
1203     m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1204     immediateScrollBy(delta);
1205     m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1206 }
1207
1208 void ScrollAnimatorMac::immediateScrollBy(const FloatSize& delta)
1209 {
1210     FloatPoint newPos = adjustScrollPositionIfNecessary(FloatPoint(m_currentPosX, m_currentPosY) + delta);
1211     if (newPos.x() == m_currentPosX && newPos.y() == m_currentPosY)
1212         return;
1213
1214     FloatSize adjustedDelta = FloatSize(newPos.x() - m_currentPosX, newPos.y() - m_currentPosY);
1215
1216     m_currentPosX = newPos.x();
1217     m_currentPosY = newPos.y();
1218     notifyContentAreaScrolled(adjustedDelta);
1219     notifyPositionChanged();
1220 }
1221
1222 void ScrollAnimatorMac::startSnapRubberbandTimer()
1223 {
1224     m_snapRubberBandTimer.startRepeating(1.0 / 60.0, FROM_HERE);
1225 }
1226
1227 void ScrollAnimatorMac::stopSnapRubberbandTimer()
1228 {
1229     m_snapRubberBandTimer.stop();
1230 }
1231
1232 void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
1233 {
1234     m_scrollElasticityController.snapRubberBandTimerFired();
1235 }
1236 #endif
1237
1238 void ScrollAnimatorMac::setIsActive()
1239 {
1240     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1241         return;
1242
1243     if (!m_needsScrollerStyleUpdate)
1244         return;
1245
1246     updateScrollerStyle();
1247 }
1248
1249 void ScrollAnimatorMac::updateScrollerStyle()
1250 {
1251     if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1252         return;
1253
1254     if (!scrollableArea()->scrollbarsCanBeActive()) {
1255         m_needsScrollerStyleUpdate = true;
1256         return;
1257     }
1258
1259     ScrollbarThemeMacOverlayAPI* macTheme = macOverlayScrollbarTheme();
1260     if (!macTheme) {
1261         m_needsScrollerStyleUpdate = false;
1262         return;
1263     }
1264
1265     NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle];
1266
1267     if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
1268         verticalScrollbar->invalidate();
1269
1270         ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp];
1271         ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1272                                                                                     controlSize:(NSControlSize)verticalScrollbar->controlSize()
1273                                                                                     horizontal:NO
1274                                                                                     replacingScrollerImp:oldVerticalPainter];
1275         [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter];
1276         macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
1277
1278         // The different scrollbar styles have different thicknesses, so we must re-set the
1279         // frameRect to the new thickness, and the re-layout below will ensure the position
1280         // and length are properly updated.
1281         int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
1282         verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1283     }
1284
1285     if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
1286         horizontalScrollbar->invalidate();
1287
1288         ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp];
1289         ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1290                                                                                     controlSize:(NSControlSize)horizontalScrollbar->controlSize()
1291                                                                                     horizontal:YES
1292                                                                                     replacingScrollerImp:oldHorizontalPainter];
1293         [m_scrollbarPainterController.get() setHorizontalScrollerImp:newHorizontalPainter];
1294         macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
1295
1296         // The different scrollbar styles have different thicknesses, so we must re-set the
1297         // frameRect to the new thickness, and the re-layout below will ensure the position
1298         // and length are properly updated.
1299         int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
1300         horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1301     }
1302
1303     // If m_needsScrollerStyleUpdate is true, then the page is restoring from the page cache, and
1304     // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves.
1305     if (!m_needsScrollerStyleUpdate)
1306         scrollableArea()->scrollbarStyleChanged();
1307
1308     m_needsScrollerStyleUpdate = false;
1309 }
1310
1311 void ScrollAnimatorMac::startScrollbarPaintTimer()
1312 {
1313     m_initialScrollbarPaintTimer.startOneShot(0.1, FROM_HERE);
1314 }
1315
1316 bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1317 {
1318     return m_initialScrollbarPaintTimer.isActive();
1319 }
1320
1321 void ScrollAnimatorMac::stopScrollbarPaintTimer()
1322 {
1323     m_initialScrollbarPaintTimer.stop();
1324 }
1325
1326 void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1327 {
1328     if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
1329         // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController
1330         // might think that the scrollbars are already showing and bail early.
1331         [m_scrollbarPainterController.get() hideOverlayScrollers];
1332         [m_scrollbarPainterController.get() flashScrollers];
1333     }
1334 }
1335
1336 void ScrollAnimatorMac::sendContentAreaScrolledSoon(const FloatSize& delta)
1337 {
1338     m_contentAreaScrolledTimerScrollDelta = delta;
1339
1340     if (!m_sendContentAreaScrolledTimer.isActive())
1341         m_sendContentAreaScrolledTimer.startOneShot(0, FROM_HERE);
1342 }
1343
1344 void ScrollAnimatorMac::sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*)
1345 {
1346     if (supportsContentAreaScrolledInDirection()) {
1347         [m_scrollbarPainterController.get() contentAreaScrolledInDirection:NSMakePoint(m_contentAreaScrolledTimerScrollDelta.width(), m_contentAreaScrolledTimerScrollDelta.height())];
1348         m_contentAreaScrolledTimerScrollDelta = FloatSize();
1349     } else
1350         [m_scrollbarPainterController.get() contentAreaScrolled];
1351 }
1352
1353 void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1354 {
1355     IntRect rectInViewCoordinates = scrollerThumb;
1356     if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1357         rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1358
1359     if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1360         return;
1361
1362     m_visibleScrollerThumbRect = rectInViewCoordinates;
1363 }
1364
1365 bool ScrollAnimatorMac::canUseCoordinatedScrollbar() {
1366     return ScrollbarThemeMacCommon::isOverlayAPIAvailable();
1367 }
1368
1369 } // namespace blink