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