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