2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "platform/mac/ScrollAnimatorMac.h"
30 #include "platform/PlatformGestureEvent.h"
31 #include "platform/PlatformWheelEvent.h"
32 #include "platform/Timer.h"
33 #include "platform/animation/TimingFunction.h"
34 #include "platform/geometry/FloatRect.h"
35 #include "platform/geometry/IntRect.h"
36 #include "platform/mac/BlockExceptions.h"
37 #include "platform/mac/NSScrollerImpDetails.h"
38 #include "platform/scroll/ScrollView.h"
39 #include "platform/scroll/ScrollableArea.h"
40 #include "platform/scroll/ScrollbarTheme.h"
41 #include "platform/scroll/ScrollbarThemeMacCommon.h"
42 #include "platform/scroll/ScrollbarThemeMacOverlayAPI.h"
43 #include "wtf/MainThread.h"
44 #include "wtf/PassOwnPtr.h"
46 using namespace blink;
48 static bool supportsUIStateTransitionProgress()
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;
55 static bool supportsExpansionTransitionProgress()
57 static bool globalSupportsExpansionTransitionProgress = [NSClassFromString(@"NSScrollerImp") instancesRespondToSelector:@selector(expansionTransitionProgress)];
58 return globalSupportsExpansionTransitionProgress;
61 static bool supportsContentAreaScrolledInDirection()
63 static bool globalSupportsContentAreaScrolledInDirection = [NSClassFromString(@"NSScrollerImpPair") instancesRespondToSelector:@selector(contentAreaScrolledInDirection:)];
64 return globalSupportsContentAreaScrolledInDirection;
67 static ScrollbarThemeMacOverlayAPI* macOverlayScrollbarTheme()
69 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(ScrollbarThemeMacCommon::isOverlayAPIAvailable());
70 ScrollbarTheme* scrollbarTheme = ScrollbarTheme::theme();
71 return !scrollbarTheme->isMockTheme() ? static_cast<ScrollbarThemeMacOverlayAPI*>(scrollbarTheme) : 0;
74 static ScrollbarPainter scrollbarPainterForScrollbar(Scrollbar* scrollbar)
76 if (ScrollbarThemeMacOverlayAPI* scrollbarTheme = macOverlayScrollbarTheme())
77 return scrollbarTheme->painterForScrollbar(scrollbar);
82 @interface NSObject (ScrollAnimationHelperDetails)
83 - (id)initWithDelegate:(id)delegate;
86 - (NSPoint)targetOrigin;
90 @interface WebScrollAnimationHelperDelegate : NSObject
92 blink::ScrollAnimatorMac* _animator;
94 - (id)initWithScrollAnimator:(blink::ScrollAnimatorMac*)scrollAnimator;
97 static NSSize abs(NSSize size)
99 NSSize finalSize = size;
100 if (finalSize.width < 0)
101 finalSize.width = -finalSize.width;
102 if (finalSize.height < 0)
103 finalSize.height = -finalSize.height;
107 @implementation WebScrollAnimationHelperDelegate
109 - (id)initWithScrollAnimator:(blink::ScrollAnimatorMac*)scrollAnimator
115 _animator = scrollAnimator;
129 blink::FloatPoint currentPosition = _animator->currentPosition();
130 return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
133 - (void)_immediateScrollToPoint:(NSPoint)newPosition
137 _animator->immediateScrollToPointForScrollAnimation(newPosition);
140 - (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin
145 - (NSSize)convertSizeToBase:(NSSize)size
150 - (NSSize)convertSizeFromBase:(NSSize)size
155 - (NSSize)convertSizeToBacking:(NSSize)size
160 - (NSSize)convertSizeFromBacking:(NSSize)size
180 - (void)_recursiveRecomputeToolTips
186 @interface WebScrollbarPainterControllerDelegate : NSObject
188 ScrollableArea* _scrollableArea;
190 - (id)initWithScrollableArea:(ScrollableArea*)scrollableArea;
193 @implementation WebScrollbarPainterControllerDelegate
195 - (id)initWithScrollableArea:(ScrollableArea*)scrollableArea
201 _scrollableArea = scrollableArea;
210 - (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
212 if (!_scrollableArea)
215 blink::IntSize contentsSize = _scrollableArea->contentsSize();
216 return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
219 - (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
221 if (!_scrollableArea)
224 return _scrollableArea->inLiveResize();
227 - (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
229 if (!_scrollableArea)
232 return _scrollableArea->lastKnownMousePosition();
235 - (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
238 if (!_scrollableArea || !scrollerImp)
241 blink::Scrollbar* scrollbar = 0;
242 if ([scrollerImp isHorizontal])
243 scrollbar = _scrollableArea->horizontalScrollbar();
245 scrollbar = _scrollableArea->verticalScrollbar();
247 // It is possible to have a null scrollbar here since it is possible for this delegate
248 // method to be called between the moment when a scrollbar has been set to 0 and the
249 // moment when its destructor has been called. We should probably de-couple some
250 // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this
255 ASSERT(scrollerImp == scrollbarPainterForScrollbar(scrollbar));
257 return scrollbar->convertFromContainingView(blink::IntPoint(pointInContentArea));
260 - (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
262 if (!_scrollableArea)
265 if (!_scrollableArea->scrollbarsCanBeActive())
268 _scrollableArea->scrollAnimator()->contentAreaWillPaint();
271 - (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
273 // Chrome has a single process mode which is used for testing on Mac. In that mode, WebKit runs on a thread in the
274 // browser process. This notification is called by the OS on the main thread in the browser process, and not on the
275 // the WebKit thread. Better to not update the style than crash.
276 // http://crbug.com/126514
280 if (!_scrollableArea)
283 [scrollerImpPair setScrollerStyle:newRecommendedScrollerStyle];
285 static_cast<ScrollAnimatorMac*>(_scrollableArea->scrollAnimator())->updateScrollerStyle();
290 enum FeatureToAnimate {
297 @class WebScrollbarPartAnimation;
301 // This class is used to drive the animation timer for WebScrollbarPartAnimation
302 // objects. This is used instead of NSAnimation because CoreAnimation
303 // establishes connections to the WindowServer, which should not be done in a
304 // sandboxed renderer process.
305 class WebScrollbarPartAnimationTimer {
307 WebScrollbarPartAnimationTimer(WebScrollbarPartAnimation* animation,
308 CFTimeInterval duration)
309 : m_timer(this, &WebScrollbarPartAnimationTimer::timerFired)
311 , m_duration(duration)
312 , m_animation(animation)
313 , m_timingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut))
317 ~WebScrollbarPartAnimationTimer() {}
321 m_startTime = WTF::currentTime();
322 // Set the framerate of the animation. NSAnimation uses a default
323 // framerate of 60 Hz, so use that here.
324 m_timer.startRepeating(1.0 / 60.0, FROM_HERE);
330 [m_animation setCurrentProgress:1];
333 void setDuration(CFTimeInterval duration)
335 m_duration = duration;
339 void timerFired(Timer<WebScrollbarPartAnimationTimer>*)
341 double currentTime = WTF::currentTime();
342 double delta = currentTime - m_startTime;
344 if (delta >= m_duration) {
349 double fraction = delta / m_duration;
350 double progress = m_timingFunction->evaluate(fraction, 0.001);
351 [m_animation setCurrentProgress:progress];
354 Timer<WebScrollbarPartAnimationTimer> m_timer;
355 double m_startTime; // In seconds.
356 double m_duration; // In seconds.
357 WebScrollbarPartAnimation* m_animation; // Weak, owns this.
358 RefPtr<CubicBezierTimingFunction> m_timingFunction;
363 @interface WebScrollbarPartAnimation : NSObject {
364 Scrollbar* _scrollbar;
365 OwnPtr<WebScrollbarPartAnimationTimer> _timer;
366 RetainPtr<ScrollbarPainter> _scrollbarPainter;
367 FeatureToAnimate _featureToAnimate;
371 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration;
374 @implementation WebScrollbarPartAnimation
376 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration
382 _timer = adoptPtr(new WebScrollbarPartAnimationTimer(self, duration));
383 _scrollbar = scrollbar;
384 _featureToAnimate = featureToAnimate;
385 _startValue = startValue;
386 _endValue = endValue;
391 - (void)startAnimation
395 _scrollbarPainter = scrollbarPainterForScrollbar(_scrollbar);
399 - (void)stopAnimation
404 - (void)setDuration:(CFTimeInterval)duration
406 _timer->setDuration(duration);
409 - (void)setStartValue:(CGFloat)startValue
411 _startValue = startValue;
414 - (void)setEndValue:(CGFloat)endValue
416 _endValue = endValue;
419 - (void)setCurrentProgress:(NSAnimationProgress)progress
423 CGFloat currentValue;
424 if (_startValue > _endValue)
425 currentValue = 1 - progress;
427 currentValue = progress;
429 switch (_featureToAnimate) {
431 [_scrollbarPainter.get() setKnobAlpha:currentValue];
434 [_scrollbarPainter.get() setTrackAlpha:currentValue];
436 case UIStateTransition:
437 [_scrollbarPainter.get() setUiStateTransitionProgress:currentValue];
439 case ExpansionTransition:
440 [_scrollbarPainter.get() setExpansionTransitionProgress:currentValue];
444 _scrollbar->invalidate();
449 BEGIN_BLOCK_OBJC_EXCEPTIONS;
450 [self stopAnimation];
451 END_BLOCK_OBJC_EXCEPTIONS;
457 @interface WebScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
459 blink::Scrollbar* _scrollbar;
461 RetainPtr<WebScrollbarPartAnimation> _knobAlphaAnimation;
462 RetainPtr<WebScrollbarPartAnimation> _trackAlphaAnimation;
463 RetainPtr<WebScrollbarPartAnimation> _uiStateTransitionAnimation;
464 RetainPtr<WebScrollbarPartAnimation> _expansionTransitionAnimation;
466 - (id)initWithScrollbar:(blink::Scrollbar*)scrollbar;
467 - (void)cancelAnimations;
470 @implementation WebScrollbarPainterDelegate
472 - (id)initWithScrollbar:(blink::Scrollbar*)scrollbar
478 _scrollbar = scrollbar;
482 - (void)cancelAnimations
484 BEGIN_BLOCK_OBJC_EXCEPTIONS;
485 [_knobAlphaAnimation.get() stopAnimation];
486 [_trackAlphaAnimation.get() stopAnimation];
487 [_uiStateTransitionAnimation.get() stopAnimation];
488 [_expansionTransitionAnimation.get() stopAnimation];
489 END_BLOCK_OBJC_EXCEPTIONS;
492 - (ScrollAnimatorMac*)scrollAnimator
494 return static_cast<ScrollAnimatorMac*>(_scrollbar->scrollableArea()->scrollAnimator());
497 - (NSRect)convertRectToBacking:(NSRect)aRect
502 - (NSRect)convertRectFromBacking:(NSRect)aRect
507 - (NSPoint)mouseLocationInScrollerForScrollerImp:(id)scrollerImp
512 ASSERT_UNUSED(scrollerImp, scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
514 return _scrollbar->convertFromContainingView(_scrollbar->scrollableArea()->lastKnownMousePosition());
517 - (void)setUpAlphaAnimation:(RetainPtr<WebScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(ScrollbarPainter)scrollerPainter part:(blink::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
519 // If the user has scrolled the page, then the scrollbars must be animated here.
520 // This overrides the early returns.
521 bool mustAnimate = [self scrollAnimator]->haveScrolledSincePageLoad();
523 if ([self scrollAnimator]->scrollbarPaintTimerIsActive() && !mustAnimate)
526 if (_scrollbar->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
527 [self scrollAnimator]->startScrollbarPaintTimer();
531 // At this point, we are definitely going to animate now, so stop the timer.
532 [self scrollAnimator]->stopScrollbarPaintTimer();
534 // If we are currently animating, stop
535 if (scrollbarPartAnimation) {
536 [scrollbarPartAnimation.get() stopAnimation];
537 scrollbarPartAnimation = nil;
540 if (part == blink::ThumbPart && _scrollbar->orientation() == VerticalScrollbar) {
542 IntRect thumbRect = IntRect([scrollerPainter rectForPart:NSScrollerKnob]);
543 [self scrollAnimator]->setVisibleScrollerThumbRect(thumbRect);
545 [self scrollAnimator]->setVisibleScrollerThumbRect(IntRect());
548 scrollbarPartAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
549 featureToAnimate:part == ThumbPart ? ThumbAlpha : TrackAlpha
550 animateFrom:part == ThumbPart ? [scrollerPainter knobAlpha] : [scrollerPainter trackAlpha]
553 [scrollbarPartAnimation.get() startAnimation];
556 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
561 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
563 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
564 [self setUpAlphaAnimation:_knobAlphaAnimation scrollerPainter:scrollerPainter part:blink::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
567 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
572 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
574 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
575 [self setUpAlphaAnimation:_trackAlphaAnimation scrollerPainter:scrollerPainter part:blink::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
578 - (void)scrollerImp:(id)scrollerImp animateUIStateTransitionWithDuration:(NSTimeInterval)duration
583 if (!supportsUIStateTransitionProgress())
586 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
588 ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
590 // UIStateTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
591 [scrollbarPainter setUiStateTransitionProgress:1 - [scrollerImp uiStateTransitionProgress]];
593 if (!_uiStateTransitionAnimation)
594 _uiStateTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
595 featureToAnimate:UIStateTransition
596 animateFrom:[scrollbarPainter uiStateTransitionProgress]
600 // If we don't need to initialize the animation, just reset the values in case they have changed.
601 [_uiStateTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
602 [_uiStateTransitionAnimation.get() setEndValue:1.0];
603 [_uiStateTransitionAnimation.get() setDuration:duration];
605 [_uiStateTransitionAnimation.get() startAnimation];
608 - (void)scrollerImp:(id)scrollerImp animateExpansionTransitionWithDuration:(NSTimeInterval)duration
613 if (!supportsExpansionTransitionProgress())
616 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
618 ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
620 // ExpansionTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
621 [scrollbarPainter setExpansionTransitionProgress:1 - [scrollerImp expansionTransitionProgress]];
623 if (!_expansionTransitionAnimation) {
624 _expansionTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
625 featureToAnimate:ExpansionTransition
626 animateFrom:[scrollbarPainter expansionTransitionProgress]
630 // If we don't need to initialize the animation, just reset the values in case they have changed.
631 [_expansionTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
632 [_expansionTransitionAnimation.get() setEndValue:1.0];
633 [_expansionTransitionAnimation.get() setDuration:duration];
635 [_expansionTransitionAnimation.get() startAnimation];
638 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
645 BEGIN_BLOCK_OBJC_EXCEPTIONS;
646 [_knobAlphaAnimation.get() invalidate];
647 [_trackAlphaAnimation.get() invalidate];
648 [_uiStateTransitionAnimation.get() invalidate];
649 [_expansionTransitionAnimation.get() invalidate];
650 END_BLOCK_OBJC_EXCEPTIONS;
657 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
659 return adoptPtr(new ScrollAnimatorMac(scrollableArea));
662 ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
663 : ScrollAnimator(scrollableArea)
664 , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
665 , m_sendContentAreaScrolledTimer(this, &ScrollAnimatorMac::sendContentAreaScrolledTimerFired)
666 #if USE(RUBBER_BANDING)
667 , m_scrollElasticityController(this)
668 , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
670 , m_haveScrolledSincePageLoad(false)
671 , m_needsScrollerStyleUpdate(false)
673 m_scrollAnimationHelperDelegate.adoptNS([[WebScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
674 m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
676 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
677 m_scrollbarPainterControllerDelegate.adoptNS([[WebScrollbarPainterControllerDelegate alloc] initWithScrollableArea:scrollableArea]);
678 m_scrollbarPainterController = [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease];
679 [m_scrollbarPainterController.get() setDelegate:m_scrollbarPainterControllerDelegate.get()];
680 [m_scrollbarPainterController.get() setScrollerStyle:ScrollbarThemeMacCommon::recommendedScrollerStyle()];
684 ScrollAnimatorMac::~ScrollAnimatorMac()
686 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
687 BEGIN_BLOCK_OBJC_EXCEPTIONS;
688 [m_scrollbarPainterControllerDelegate.get() invalidate];
689 [m_scrollbarPainterController.get() setDelegate:nil];
690 [m_horizontalScrollbarPainterDelegate.get() invalidate];
691 [m_verticalScrollbarPainterDelegate.get() invalidate];
692 [m_scrollAnimationHelperDelegate.get() invalidate];
693 END_BLOCK_OBJC_EXCEPTIONS;
697 static bool scrollAnimationEnabledForSystem()
699 NSString* scrollAnimationDefaultsKey =
700 @"AppleScrollAnimationEnabled";
701 static bool enabled = [[NSUserDefaults standardUserDefaults] boolForKey:scrollAnimationDefaultsKey];
705 #if USE(RUBBER_BANDING)
706 static bool rubberBandingEnabledForSystem()
708 static bool initialized = false;
709 static bool enabled = true;
710 // Caches the result, which is consistent with other apps like the Finder, which all
711 // require a restart after changing this default.
713 // Uses -objectForKey: and not -boolForKey: in order to default to true if the value wasn't set.
714 id value = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSScrollViewRubberbanding"];
715 if ([value isKindOfClass:[NSNumber class]])
716 enabled = [value boolValue];
723 bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta)
725 m_haveScrolledSincePageLoad = true;
727 if (!scrollAnimationEnabledForSystem() || !m_scrollableArea->scrollAnimatorEnabled())
728 return ScrollAnimator::scroll(orientation, granularity, step, delta);
730 if (granularity == ScrollByPixel)
731 return ScrollAnimator::scroll(orientation, granularity, step, delta);
733 float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
734 float newPos = std::max<float>(std::min<float>(currentPos + (step * delta), m_scrollableArea->maximumScrollPosition(orientation)), m_scrollableArea->minimumScrollPosition(orientation));
735 if (currentPos == newPos)
739 if ([m_scrollAnimationHelper.get() _isAnimating]) {
740 NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
741 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
743 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
745 [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
749 void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
751 [m_scrollAnimationHelper.get() _stopRun];
752 immediateScrollTo(offset);
755 FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
757 if (!m_scrollableArea->constrainsScrollingToContentEdge())
760 IntPoint minPos = m_scrollableArea->minimumScrollPosition();
761 IntPoint maxPos = m_scrollableArea->maximumScrollPosition();
763 float newX = std::max<float>(std::min<float>(position.x(), maxPos.x()), minPos.x());
764 float newY = std::max<float>(std::min<float>(position.y(), maxPos.y()), minPos.y());
766 return FloatPoint(newX, newY);
769 void ScrollAnimatorMac::adjustScrollPositionToBoundsIfNecessary()
771 bool currentlyConstrainsToContentEdge = m_scrollableArea->constrainsScrollingToContentEdge();
772 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
774 IntPoint currentScrollPosition = absoluteScrollPosition();
775 FloatPoint nearestPointWithinBounds = adjustScrollPositionIfNecessary(absoluteScrollPosition());
776 immediateScrollBy(nearestPointWithinBounds - currentScrollPosition);
778 m_scrollableArea->setConstrainsScrollingToContentEdge(currentlyConstrainsToContentEdge);
781 void ScrollAnimatorMac::immediateScrollTo(const FloatPoint& newPosition)
783 FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
785 bool positionChanged = adjustedPosition.x() != m_currentPosX || adjustedPosition.y() != m_currentPosY;
786 if (!positionChanged && !scrollableArea()->scrollOriginChanged())
789 FloatSize delta = FloatSize(adjustedPosition.x() - m_currentPosX, adjustedPosition.y() - m_currentPosY);
791 m_currentPosX = adjustedPosition.x();
792 m_currentPosY = adjustedPosition.y();
793 notifyContentAreaScrolled(delta);
794 notifyPositionChanged();
797 bool ScrollAnimatorMac::isRubberBandInProgress() const
799 #if !USE(RUBBER_BANDING)
802 return m_scrollElasticityController.isRubberBandInProgress();
806 void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition)
808 ASSERT(m_scrollAnimationHelper);
809 immediateScrollTo(newPosition);
812 void ScrollAnimatorMac::contentAreaWillPaint() const
814 if (!scrollableArea()->scrollbarsCanBeActive())
816 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
817 [m_scrollbarPainterController.get() contentAreaWillDraw];
820 void ScrollAnimatorMac::mouseEnteredContentArea() const
822 if (!scrollableArea()->scrollbarsCanBeActive())
824 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
825 [m_scrollbarPainterController.get() mouseEnteredContentArea];
828 void ScrollAnimatorMac::mouseExitedContentArea() const
830 if (!scrollableArea()->scrollbarsCanBeActive())
832 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
833 [m_scrollbarPainterController.get() mouseExitedContentArea];
836 void ScrollAnimatorMac::mouseMovedInContentArea() const
838 if (!scrollableArea()->scrollbarsCanBeActive())
840 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
841 [m_scrollbarPainterController.get() mouseMovedInContentArea];
844 void ScrollAnimatorMac::mouseEnteredScrollbar(Scrollbar* scrollbar) const
846 // At this time, only legacy scrollbars needs to send notifications here.
847 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy)
850 if (!scrollableArea()->scrollbarsCanBeActive())
853 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
854 if (!supportsUIStateTransitionProgress())
856 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
857 [painter mouseEnteredScroller];
861 void ScrollAnimatorMac::mouseExitedScrollbar(Scrollbar* scrollbar) const
863 // At this time, only legacy scrollbars needs to send notifications here.
864 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy)
867 if (!scrollableArea()->scrollbarsCanBeActive())
870 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
871 if (!supportsUIStateTransitionProgress())
873 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
874 [painter mouseExitedScroller];
878 void ScrollAnimatorMac::willStartLiveResize()
880 if (!scrollableArea()->scrollbarsCanBeActive())
882 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
883 [m_scrollbarPainterController.get() startLiveResize];
886 void ScrollAnimatorMac::contentsResized() const
888 if (!scrollableArea()->scrollbarsCanBeActive())
890 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
891 [m_scrollbarPainterController.get() contentAreaDidResize];
894 void ScrollAnimatorMac::willEndLiveResize()
896 if (!scrollableArea()->scrollbarsCanBeActive())
898 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
899 [m_scrollbarPainterController.get() endLiveResize];
902 void ScrollAnimatorMac::contentAreaDidShow() const
904 if (!scrollableArea()->scrollbarsCanBeActive())
906 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
907 [m_scrollbarPainterController.get() windowOrderedIn];
910 void ScrollAnimatorMac::contentAreaDidHide() const
912 if (!scrollableArea()->scrollbarsCanBeActive())
914 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
915 [m_scrollbarPainterController.get() windowOrderedOut];
918 void ScrollAnimatorMac::didBeginScrollGesture() const
920 if (!scrollableArea()->scrollbarsCanBeActive())
922 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
923 [m_scrollbarPainterController.get() beginScrollGesture];
926 void ScrollAnimatorMac::didEndScrollGesture() const
928 if (!scrollableArea()->scrollbarsCanBeActive())
930 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
931 [m_scrollbarPainterController.get() endScrollGesture];
934 void ScrollAnimatorMac::mayBeginScrollGesture() const
936 if (!scrollableArea()->scrollbarsCanBeActive())
938 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
941 [m_scrollbarPainterController.get() beginScrollGesture];
942 [m_scrollbarPainterController.get() contentAreaScrolled];
945 void ScrollAnimatorMac::finishCurrentScrollAnimations()
947 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
948 [m_scrollbarPainterController.get() hideOverlayScrollers];
952 void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
954 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
957 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
961 ASSERT(!m_verticalScrollbarPainterDelegate);
962 m_verticalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
964 [painter setDelegate:m_verticalScrollbarPainterDelegate.get()];
965 [m_scrollbarPainterController.get() setVerticalScrollerImp:painter];
966 if (scrollableArea()->inLiveResize())
967 [painter setKnobAlpha:1];
970 void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
972 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
975 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
979 ASSERT(m_verticalScrollbarPainterDelegate);
980 [m_verticalScrollbarPainterDelegate.get() invalidate];
981 m_verticalScrollbarPainterDelegate = nullptr;
983 [painter setDelegate:nil];
984 [m_scrollbarPainterController.get() setVerticalScrollerImp:nil];
987 void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
989 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
992 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
996 ASSERT(!m_horizontalScrollbarPainterDelegate);
997 m_horizontalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
999 [painter setDelegate:m_horizontalScrollbarPainterDelegate.get()];
1000 [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter];
1001 if (scrollableArea()->inLiveResize())
1002 [painter setKnobAlpha:1];
1005 void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
1007 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1010 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
1014 ASSERT(m_horizontalScrollbarPainterDelegate);
1015 [m_horizontalScrollbarPainterDelegate.get() invalidate];
1016 m_horizontalScrollbarPainterDelegate = nullptr;
1018 [painter setDelegate:nil];
1019 [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil];
1022 bool ScrollAnimatorMac::shouldScrollbarParticipateInHitTesting(Scrollbar* scrollbar)
1024 // Non-overlay scrollbars should always participate in hit testing.
1025 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleOverlay)
1028 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1031 if (scrollbar->isAlphaLocked())
1034 // Overlay scrollbars should participate in hit testing whenever they are at all visible.
1035 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
1038 return [painter knobAlpha] > 0;
1041 void ScrollAnimatorMac::notifyContentAreaScrolled(const FloatSize& delta)
1043 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1046 // This function is called when a page is going into the page cache, but the page
1047 // isn't really scrolling in that case. We should only pass the message on to the
1048 // ScrollbarPainterController when we're really scrolling on an active page.
1049 if (scrollableArea()->scrollbarsCanBeActive())
1050 sendContentAreaScrolledSoon(delta);
1053 void ScrollAnimatorMac::cancelAnimations()
1055 m_haveScrolledSincePageLoad = false;
1057 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
1058 if (scrollbarPaintTimerIsActive())
1059 stopScrollbarPaintTimer();
1060 [m_horizontalScrollbarPainterDelegate.get() cancelAnimations];
1061 [m_verticalScrollbarPainterDelegate.get() cancelAnimations];
1065 void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase)
1067 // This may not have been set to true yet if the wheel event was handled by the ScrollingTree,
1068 // So set it to true here.
1069 m_haveScrolledSincePageLoad = true;
1071 if (phase == PlatformWheelEventPhaseBegan)
1072 didBeginScrollGesture();
1073 else if (phase == PlatformWheelEventPhaseEnded || phase == PlatformWheelEventPhaseCancelled)
1074 didEndScrollGesture();
1075 else if (phase == PlatformWheelEventPhaseMayBegin)
1076 mayBeginScrollGesture();
1079 #if USE(RUBBER_BANDING)
1080 bool ScrollAnimatorMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
1082 m_haveScrolledSincePageLoad = true;
1084 if (!wheelEvent.hasPreciseScrollingDeltas() || !rubberBandingEnabledForSystem())
1085 return ScrollAnimator::handleWheelEvent(wheelEvent);
1087 // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
1088 // up to the parent scrollable area. It takes advantage of the fact that
1089 // the base class implementation of handleWheelEvent will not accept the
1090 // wheel event if there is nowhere to scroll.
1091 if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
1092 if (!allowsVerticalStretching())
1093 return ScrollAnimator::handleWheelEvent(wheelEvent);
1095 if (!allowsHorizontalStretching())
1096 return ScrollAnimator::handleWheelEvent(wheelEvent);
1099 bool didHandleEvent = m_scrollElasticityController.handleWheelEvent(wheelEvent);
1101 // The elasticity controller can return false on a phase end event if rubber banding wasn't in progress.
1102 // In this case, the wheel phase must still be handled so that that overlay scroll bars get hidden.
1103 if (didHandleEvent || wheelEvent.phase() == PlatformWheelEventPhaseEnded || wheelEvent.phase() == PlatformWheelEventPhaseCancelled)
1104 handleWheelEventPhase(wheelEvent.phase());
1106 return didHandleEvent;
1109 bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
1111 FloatSize limitDelta;
1112 if (fabsf(deltaY) >= fabsf(deltaX)) {
1114 // We are trying to scroll up. Make sure we are not pinned to the top
1115 limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
1117 // We are trying to scroll down. Make sure we are not pinned to the bottom
1118 limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
1120 } else if (deltaX != 0) {
1122 // We are trying to scroll left. Make sure we are not pinned to the left
1123 limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
1125 // We are trying to scroll right. Make sure we are not pinned to the right
1126 limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
1130 if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
1135 bool ScrollAnimatorMac::allowsVerticalStretching()
1137 switch (m_scrollableArea->verticalScrollElasticity()) {
1138 case ScrollElasticityAutomatic: {
1139 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1140 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1141 return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
1143 case ScrollElasticityNone:
1145 case ScrollElasticityAllowed:
1149 ASSERT_NOT_REACHED();
1153 bool ScrollAnimatorMac::allowsHorizontalStretching()
1155 switch (m_scrollableArea->horizontalScrollElasticity()) {
1156 case ScrollElasticityAutomatic: {
1157 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1158 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1159 return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
1161 case ScrollElasticityNone:
1163 case ScrollElasticityAllowed:
1167 ASSERT_NOT_REACHED();
1171 IntSize ScrollAnimatorMac::stretchAmount()
1173 return m_scrollableArea->overhangAmount();
1176 bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction)
1178 return pinnedInDirection(direction.width(), direction.height());
1181 bool ScrollAnimatorMac::canScrollHorizontally()
1183 Scrollbar* scrollbar = m_scrollableArea->horizontalScrollbar();
1186 return scrollbar->enabled();
1189 bool ScrollAnimatorMac::canScrollVertically()
1191 Scrollbar* scrollbar = m_scrollableArea->verticalScrollbar();
1194 return scrollbar->enabled();
1197 IntPoint ScrollAnimatorMac::absoluteScrollPosition()
1199 return m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin();
1202 void ScrollAnimatorMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& delta)
1204 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1205 immediateScrollBy(delta);
1206 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1209 void ScrollAnimatorMac::immediateScrollBy(const FloatSize& delta)
1211 FloatPoint newPos = adjustScrollPositionIfNecessary(FloatPoint(m_currentPosX, m_currentPosY) + delta);
1212 if (newPos.x() == m_currentPosX && newPos.y() == m_currentPosY)
1215 FloatSize adjustedDelta = FloatSize(newPos.x() - m_currentPosX, newPos.y() - m_currentPosY);
1217 m_currentPosX = newPos.x();
1218 m_currentPosY = newPos.y();
1219 notifyContentAreaScrolled(adjustedDelta);
1220 notifyPositionChanged();
1223 void ScrollAnimatorMac::startSnapRubberbandTimer()
1225 m_snapRubberBandTimer.startRepeating(1.0 / 60.0, FROM_HERE);
1228 void ScrollAnimatorMac::stopSnapRubberbandTimer()
1230 m_snapRubberBandTimer.stop();
1233 void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
1235 m_scrollElasticityController.snapRubberBandTimerFired();
1239 void ScrollAnimatorMac::setIsActive()
1241 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1244 if (!m_needsScrollerStyleUpdate)
1247 updateScrollerStyle();
1250 void ScrollAnimatorMac::updateScrollerStyle()
1252 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1255 if (!scrollableArea()->scrollbarsCanBeActive()) {
1256 m_needsScrollerStyleUpdate = true;
1260 ScrollbarThemeMacOverlayAPI* macTheme = macOverlayScrollbarTheme();
1262 m_needsScrollerStyleUpdate = false;
1266 NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle];
1268 if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
1269 verticalScrollbar->invalidate();
1271 ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp];
1272 ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1273 controlSize:(NSControlSize)verticalScrollbar->controlSize()
1275 replacingScrollerImp:oldVerticalPainter];
1276 [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter];
1277 macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
1279 // The different scrollbar styles have different thicknesses, so we must re-set the
1280 // frameRect to the new thickness, and the re-layout below will ensure the position
1281 // and length are properly updated.
1282 int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
1283 verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1286 if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
1287 horizontalScrollbar->invalidate();
1289 ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp];
1290 ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1291 controlSize:(NSControlSize)horizontalScrollbar->controlSize()
1293 replacingScrollerImp:oldHorizontalPainter];
1294 [m_scrollbarPainterController.get() setHorizontalScrollerImp:newHorizontalPainter];
1295 macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
1297 // The different scrollbar styles have different thicknesses, so we must re-set the
1298 // frameRect to the new thickness, and the re-layout below will ensure the position
1299 // and length are properly updated.
1300 int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
1301 horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1304 // If m_needsScrollerStyleUpdate is true, then the page is restoring from the page cache, and
1305 // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves.
1306 if (!m_needsScrollerStyleUpdate)
1307 scrollableArea()->scrollbarStyleChanged();
1309 m_needsScrollerStyleUpdate = false;
1312 void ScrollAnimatorMac::startScrollbarPaintTimer()
1314 m_initialScrollbarPaintTimer.startOneShot(0.1, FROM_HERE);
1317 bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1319 return m_initialScrollbarPaintTimer.isActive();
1322 void ScrollAnimatorMac::stopScrollbarPaintTimer()
1324 m_initialScrollbarPaintTimer.stop();
1327 void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1329 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
1330 // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController
1331 // might think that the scrollbars are already showing and bail early.
1332 [m_scrollbarPainterController.get() hideOverlayScrollers];
1333 [m_scrollbarPainterController.get() flashScrollers];
1337 void ScrollAnimatorMac::sendContentAreaScrolledSoon(const FloatSize& delta)
1339 m_contentAreaScrolledTimerScrollDelta = delta;
1341 if (!m_sendContentAreaScrolledTimer.isActive())
1342 m_sendContentAreaScrolledTimer.startOneShot(0, FROM_HERE);
1345 void ScrollAnimatorMac::sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*)
1347 if (supportsContentAreaScrolledInDirection()) {
1348 [m_scrollbarPainterController.get() contentAreaScrolledInDirection:NSMakePoint(m_contentAreaScrolledTimerScrollDelta.width(), m_contentAreaScrolledTimerScrollDelta.height())];
1349 m_contentAreaScrolledTimerScrollDelta = FloatSize();
1351 [m_scrollbarPainterController.get() contentAreaScrolled];
1354 void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1356 IntRect rectInViewCoordinates = scrollerThumb;
1357 if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1358 rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1360 if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1363 m_visibleScrollerThumbRect = rectInViewCoordinates;
1366 bool ScrollAnimatorMac::canUseCoordinatedScrollbar() {
1367 return ScrollbarThemeMacCommon::isOverlayAPIAvailable();
1370 } // namespace blink