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/ScrollableArea.h"
39 #include "platform/scroll/ScrollbarTheme.h"
40 #include "platform/scroll/ScrollbarThemeMacCommon.h"
41 #include "platform/scroll/ScrollbarThemeMacOverlayAPI.h"
42 #include "wtf/MainThread.h"
43 #include "wtf/PassRefPtr.h"
45 using namespace blink;
47 static bool supportsUIStateTransitionProgress()
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;
54 static bool supportsExpansionTransitionProgress()
56 static bool globalSupportsExpansionTransitionProgress = [NSClassFromString(@"NSScrollerImp") instancesRespondToSelector:@selector(expansionTransitionProgress)];
57 return globalSupportsExpansionTransitionProgress;
60 static bool supportsContentAreaScrolledInDirection()
62 static bool globalSupportsContentAreaScrolledInDirection = [NSClassFromString(@"NSScrollerImpPair") instancesRespondToSelector:@selector(contentAreaScrolledInDirection:)];
63 return globalSupportsContentAreaScrolledInDirection;
66 static ScrollbarThemeMacOverlayAPI* macOverlayScrollbarTheme()
68 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(ScrollbarThemeMacCommon::isOverlayAPIAvailable());
69 ScrollbarTheme* scrollbarTheme = ScrollbarTheme::theme();
70 return !scrollbarTheme->isMockTheme() ? static_cast<ScrollbarThemeMacOverlayAPI*>(scrollbarTheme) : 0;
73 static ScrollbarPainter scrollbarPainterForScrollbar(Scrollbar* scrollbar)
75 if (ScrollbarThemeMacOverlayAPI* scrollbarTheme = macOverlayScrollbarTheme())
76 return scrollbarTheme->painterForScrollbar(scrollbar);
81 @interface NSObject (ScrollAnimationHelperDetails)
82 - (id)initWithDelegate:(id)delegate;
85 - (NSPoint)targetOrigin;
89 @interface WebScrollAnimationHelperDelegate : NSObject
91 blink::ScrollAnimatorMac* _animator;
93 - (id)initWithScrollAnimator:(blink::ScrollAnimatorMac*)scrollAnimator;
96 static NSSize abs(NSSize size)
98 NSSize finalSize = size;
99 if (finalSize.width < 0)
100 finalSize.width = -finalSize.width;
101 if (finalSize.height < 0)
102 finalSize.height = -finalSize.height;
106 @implementation WebScrollAnimationHelperDelegate
108 - (id)initWithScrollAnimator:(blink::ScrollAnimatorMac*)scrollAnimator
114 _animator = scrollAnimator;
128 blink::FloatPoint currentPosition = _animator->currentPosition();
129 return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
132 - (void)_immediateScrollToPoint:(NSPoint)newPosition
136 _animator->immediateScrollToPointForScrollAnimation(newPosition);
139 - (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin
144 - (NSSize)convertSizeToBase:(NSSize)size
149 - (NSSize)convertSizeFromBase:(NSSize)size
154 - (NSSize)convertSizeToBacking:(NSSize)size
159 - (NSSize)convertSizeFromBacking:(NSSize)size
179 - (void)_recursiveRecomputeToolTips
185 @interface WebScrollbarPainterControllerDelegate : NSObject
187 ScrollableArea* _scrollableArea;
189 - (id)initWithScrollableArea:(ScrollableArea*)scrollableArea;
192 @implementation WebScrollbarPainterControllerDelegate
194 - (id)initWithScrollableArea:(ScrollableArea*)scrollableArea
200 _scrollableArea = scrollableArea;
209 - (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
211 if (!_scrollableArea)
214 blink::IntSize contentsSize = _scrollableArea->contentsSize();
215 return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
218 - (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
220 if (!_scrollableArea)
223 return _scrollableArea->inLiveResize();
226 - (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
228 if (!_scrollableArea)
231 return _scrollableArea->lastKnownMousePosition();
234 - (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
237 if (!_scrollableArea || !scrollerImp)
240 blink::Scrollbar* scrollbar = 0;
241 if ([scrollerImp isHorizontal])
242 scrollbar = _scrollableArea->horizontalScrollbar();
244 scrollbar = _scrollableArea->verticalScrollbar();
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
254 ASSERT(scrollerImp == scrollbarPainterForScrollbar(scrollbar));
256 return scrollbar->convertFromContainingView(blink::IntPoint(pointInContentArea));
259 - (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
261 if (!_scrollableArea)
264 if (!_scrollableArea->scrollbarsCanBeActive())
267 _scrollableArea->scrollAnimator()->contentAreaWillPaint();
270 - (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
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
279 if (!_scrollableArea)
282 [scrollerImpPair setScrollerStyle:newRecommendedScrollerStyle];
284 static_cast<ScrollAnimatorMac*>(_scrollableArea->scrollAnimator())->updateScrollerStyle();
289 enum FeatureToAnimate {
296 @class WebScrollbarPartAnimation;
300 // This class is used to drive the animation timer for WebScrollbarPartAnimation
301 // objects. This is used instead of NSAnimation because CoreAnimation
302 // establishes connections to the WindowServer, which should not be done in a
303 // sandboxed renderer process.
304 class WebScrollbarPartAnimationTimer {
306 WebScrollbarPartAnimationTimer(WebScrollbarPartAnimation* animation,
307 CFTimeInterval duration)
308 : m_timer(this, &WebScrollbarPartAnimationTimer::timerFired)
310 , m_duration(duration)
311 , m_animation(animation)
312 , m_timingFunction(CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseInOut))
316 ~WebScrollbarPartAnimationTimer() {}
320 m_startTime = WTF::currentTime();
321 // Set the framerate of the animation. NSAnimation uses a default
322 // framerate of 60 Hz, so use that here.
323 m_timer.startRepeating(1.0 / 60.0, FROM_HERE);
329 [m_animation setCurrentProgress:1];
332 void setDuration(CFTimeInterval duration)
334 m_duration = duration;
338 void timerFired(Timer<WebScrollbarPartAnimationTimer>*)
340 double currentTime = WTF::currentTime();
341 double delta = currentTime - m_startTime;
343 if (delta >= m_duration) {
348 double fraction = delta / m_duration;
349 double progress = m_timingFunction->evaluate(fraction, 0.001);
350 [m_animation setCurrentProgress:progress];
353 Timer<WebScrollbarPartAnimationTimer> m_timer;
354 double m_startTime; // In seconds.
355 double m_duration; // In seconds.
356 WebScrollbarPartAnimation* m_animation; // Weak, owns this.
357 RefPtr<CubicBezierTimingFunction> m_timingFunction;
362 @interface WebScrollbarPartAnimation : NSObject {
363 Scrollbar* _scrollbar;
364 OwnPtr<WebScrollbarPartAnimationTimer> _timer;
365 RetainPtr<ScrollbarPainter> _scrollbarPainter;
366 FeatureToAnimate _featureToAnimate;
370 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration;
373 @implementation WebScrollbarPartAnimation
375 - (id)initWithScrollbar:(Scrollbar*)scrollbar featureToAnimate:(FeatureToAnimate)featureToAnimate animateFrom:(CGFloat)startValue animateTo:(CGFloat)endValue duration:(NSTimeInterval)duration
381 _timer = adoptPtr(new WebScrollbarPartAnimationTimer(self, duration));
382 _scrollbar = scrollbar;
383 _featureToAnimate = featureToAnimate;
384 _startValue = startValue;
385 _endValue = endValue;
390 - (void)startAnimation
394 _scrollbarPainter = scrollbarPainterForScrollbar(_scrollbar);
398 - (void)stopAnimation
403 - (void)setDuration:(CFTimeInterval)duration
405 _timer->setDuration(duration);
408 - (void)setStartValue:(CGFloat)startValue
410 _startValue = startValue;
413 - (void)setEndValue:(CGFloat)endValue
415 _endValue = endValue;
418 - (void)setCurrentProgress:(NSAnimationProgress)progress
422 CGFloat currentValue;
423 if (_startValue > _endValue)
424 currentValue = 1 - progress;
426 currentValue = progress;
428 switch (_featureToAnimate) {
430 [_scrollbarPainter.get() setKnobAlpha:currentValue];
433 [_scrollbarPainter.get() setTrackAlpha:currentValue];
435 case UIStateTransition:
436 [_scrollbarPainter.get() setUiStateTransitionProgress:currentValue];
438 case ExpansionTransition:
439 [_scrollbarPainter.get() setExpansionTransitionProgress:currentValue];
443 _scrollbar->invalidate();
448 BEGIN_BLOCK_OBJC_EXCEPTIONS;
449 [self stopAnimation];
450 END_BLOCK_OBJC_EXCEPTIONS;
456 @interface WebScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
458 blink::Scrollbar* _scrollbar;
460 RetainPtr<WebScrollbarPartAnimation> _knobAlphaAnimation;
461 RetainPtr<WebScrollbarPartAnimation> _trackAlphaAnimation;
462 RetainPtr<WebScrollbarPartAnimation> _uiStateTransitionAnimation;
463 RetainPtr<WebScrollbarPartAnimation> _expansionTransitionAnimation;
465 - (id)initWithScrollbar:(blink::Scrollbar*)scrollbar;
466 - (void)cancelAnimations;
469 @implementation WebScrollbarPainterDelegate
471 - (id)initWithScrollbar:(blink::Scrollbar*)scrollbar
477 _scrollbar = scrollbar;
481 - (void)cancelAnimations
483 BEGIN_BLOCK_OBJC_EXCEPTIONS;
484 [_knobAlphaAnimation.get() stopAnimation];
485 [_trackAlphaAnimation.get() stopAnimation];
486 [_uiStateTransitionAnimation.get() stopAnimation];
487 [_expansionTransitionAnimation.get() stopAnimation];
488 END_BLOCK_OBJC_EXCEPTIONS;
491 - (ScrollAnimatorMac*)scrollAnimator
493 return static_cast<ScrollAnimatorMac*>(_scrollbar->scrollableArea()->scrollAnimator());
496 - (NSRect)convertRectToBacking:(NSRect)aRect
501 - (NSRect)convertRectFromBacking:(NSRect)aRect
506 - (NSPoint)mouseLocationInScrollerForScrollerImp:(id)scrollerImp
511 ASSERT_UNUSED(scrollerImp, scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
513 return _scrollbar->convertFromContainingView(_scrollbar->scrollableArea()->lastKnownMousePosition());
516 - (void)setUpAlphaAnimation:(RetainPtr<WebScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(ScrollbarPainter)scrollerPainter part:(blink::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
518 // If the user has scrolled the page, then the scrollbars must be animated here.
519 // This overrides the early returns.
520 bool mustAnimate = [self scrollAnimator]->haveScrolledSincePageLoad();
522 if ([self scrollAnimator]->scrollbarPaintTimerIsActive() && !mustAnimate)
525 if (_scrollbar->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
526 [self scrollAnimator]->startScrollbarPaintTimer();
530 // At this point, we are definitely going to animate now, so stop the timer.
531 [self scrollAnimator]->stopScrollbarPaintTimer();
533 // If we are currently animating, stop
534 if (scrollbarPartAnimation) {
535 [scrollbarPartAnimation.get() stopAnimation];
536 scrollbarPartAnimation = nullptr;
539 if (part == blink::ThumbPart && _scrollbar->orientation() == VerticalScrollbar) {
541 IntRect thumbRect = IntRect([scrollerPainter rectForPart:NSScrollerKnob]);
542 [self scrollAnimator]->setVisibleScrollerThumbRect(thumbRect);
544 [self scrollAnimator]->setVisibleScrollerThumbRect(IntRect());
547 scrollbarPartAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
548 featureToAnimate:part == ThumbPart ? ThumbAlpha : TrackAlpha
549 animateFrom:part == ThumbPart ? [scrollerPainter knobAlpha] : [scrollerPainter trackAlpha]
552 [scrollbarPartAnimation.get() startAnimation];
555 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
560 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
562 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
563 [self setUpAlphaAnimation:_knobAlphaAnimation scrollerPainter:scrollerPainter part:blink::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
566 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
571 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
573 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
574 [self setUpAlphaAnimation:_trackAlphaAnimation scrollerPainter:scrollerPainter part:blink::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
577 - (void)scrollerImp:(id)scrollerImp animateUIStateTransitionWithDuration:(NSTimeInterval)duration
582 if (!supportsUIStateTransitionProgress())
585 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
587 ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
589 // UIStateTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
590 [scrollbarPainter setUiStateTransitionProgress:1 - [scrollerImp uiStateTransitionProgress]];
592 if (!_uiStateTransitionAnimation)
593 _uiStateTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
594 featureToAnimate:UIStateTransition
595 animateFrom:[scrollbarPainter uiStateTransitionProgress]
599 // If we don't need to initialize the animation, just reset the values in case they have changed.
600 [_uiStateTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
601 [_uiStateTransitionAnimation.get() setEndValue:1.0];
602 [_uiStateTransitionAnimation.get() setDuration:duration];
604 [_uiStateTransitionAnimation.get() startAnimation];
607 - (void)scrollerImp:(id)scrollerImp animateExpansionTransitionWithDuration:(NSTimeInterval)duration
612 if (!supportsExpansionTransitionProgress())
615 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar));
617 ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp;
619 // ExpansionTransition always animates to 1. In case an animation is in progress this avoids a hard transition.
620 [scrollbarPainter setExpansionTransitionProgress:1 - [scrollerImp expansionTransitionProgress]];
622 if (!_expansionTransitionAnimation) {
623 _expansionTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar
624 featureToAnimate:ExpansionTransition
625 animateFrom:[scrollbarPainter expansionTransitionProgress]
629 // If we don't need to initialize the animation, just reset the values in case they have changed.
630 [_expansionTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]];
631 [_expansionTransitionAnimation.get() setEndValue:1.0];
632 [_expansionTransitionAnimation.get() setDuration:duration];
634 [_expansionTransitionAnimation.get() startAnimation];
637 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
644 BEGIN_BLOCK_OBJC_EXCEPTIONS;
645 [_knobAlphaAnimation.get() invalidate];
646 [_trackAlphaAnimation.get() invalidate];
647 [_uiStateTransitionAnimation.get() invalidate];
648 [_expansionTransitionAnimation.get() invalidate];
649 END_BLOCK_OBJC_EXCEPTIONS;
656 PassRefPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
658 return adoptRef(new ScrollAnimatorMac(scrollableArea));
661 ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
662 : ScrollAnimator(scrollableArea)
663 , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
664 , m_sendContentAreaScrolledTimer(this, &ScrollAnimatorMac::sendContentAreaScrolledTimerFired)
665 #if USE(RUBBER_BANDING)
666 , m_scrollElasticityController(this)
667 , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
669 , m_haveScrolledSincePageLoad(false)
670 , m_needsScrollerStyleUpdate(false)
672 m_scrollAnimationHelperDelegate.adoptNS([[WebScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
673 m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
675 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
676 m_scrollbarPainterControllerDelegate.adoptNS([[WebScrollbarPainterControllerDelegate alloc] initWithScrollableArea:scrollableArea]);
677 m_scrollbarPainterController = [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease];
678 [m_scrollbarPainterController.get() setDelegate:m_scrollbarPainterControllerDelegate.get()];
679 [m_scrollbarPainterController.get() setScrollerStyle:ScrollbarThemeMacCommon::recommendedScrollerStyle()];
683 ScrollAnimatorMac::~ScrollAnimatorMac()
685 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
686 BEGIN_BLOCK_OBJC_EXCEPTIONS;
687 [m_scrollbarPainterControllerDelegate.get() invalidate];
688 [m_scrollbarPainterController.get() setDelegate:nil];
689 [m_horizontalScrollbarPainterDelegate.get() invalidate];
690 [m_verticalScrollbarPainterDelegate.get() invalidate];
691 [m_scrollAnimationHelperDelegate.get() invalidate];
692 END_BLOCK_OBJC_EXCEPTIONS;
696 static bool scrollAnimationEnabledForSystem()
698 NSString* scrollAnimationDefaultsKey =
699 @"AppleScrollAnimationEnabled";
700 static bool enabled = [[NSUserDefaults standardUserDefaults] boolForKey:scrollAnimationDefaultsKey];
704 #if USE(RUBBER_BANDING)
705 static bool rubberBandingEnabledForSystem()
707 static bool initialized = false;
708 static bool enabled = true;
709 // Caches the result, which is consistent with other apps like the Finder, which all
710 // require a restart after changing this default.
712 // Uses -objectForKey: and not -boolForKey: in order to default to true if the value wasn't set.
713 id value = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSScrollViewRubberbanding"];
714 if ([value isKindOfClass:[NSNumber class]])
715 enabled = [value boolValue];
722 bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta)
724 m_haveScrolledSincePageLoad = true;
726 if (!scrollAnimationEnabledForSystem() || !m_scrollableArea->scrollAnimatorEnabled())
727 return ScrollAnimator::scroll(orientation, granularity, step, delta);
729 if (granularity == ScrollByPixel)
730 return ScrollAnimator::scroll(orientation, granularity, step, delta);
732 float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
733 float newPos = std::max<float>(std::min<float>(currentPos + (step * delta), m_scrollableArea->maximumScrollPosition(orientation)), m_scrollableArea->minimumScrollPosition(orientation));
734 if (currentPos == newPos)
738 if ([m_scrollAnimationHelper.get() _isAnimating]) {
739 NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
740 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
742 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
744 [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
748 void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
750 [m_scrollAnimationHelper.get() _stopRun];
751 immediateScrollTo(offset);
754 FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
756 if (!m_scrollableArea->constrainsScrollingToContentEdge())
759 IntPoint minPos = m_scrollableArea->minimumScrollPosition();
760 IntPoint maxPos = m_scrollableArea->maximumScrollPosition();
762 float newX = std::max<float>(std::min<float>(position.x(), maxPos.x()), minPos.x());
763 float newY = std::max<float>(std::min<float>(position.y(), maxPos.y()), minPos.y());
765 return FloatPoint(newX, newY);
768 void ScrollAnimatorMac::adjustScrollPositionToBoundsIfNecessary()
770 bool currentlyConstrainsToContentEdge = m_scrollableArea->constrainsScrollingToContentEdge();
771 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
773 IntPoint currentScrollPosition = absoluteScrollPosition();
774 FloatPoint nearestPointWithinBounds = adjustScrollPositionIfNecessary(absoluteScrollPosition());
775 immediateScrollBy(nearestPointWithinBounds - currentScrollPosition);
777 m_scrollableArea->setConstrainsScrollingToContentEdge(currentlyConstrainsToContentEdge);
780 void ScrollAnimatorMac::immediateScrollTo(const FloatPoint& newPosition)
782 FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
784 bool positionChanged = adjustedPosition.x() != m_currentPosX || adjustedPosition.y() != m_currentPosY;
785 if (!positionChanged && !scrollableArea()->scrollOriginChanged())
788 FloatSize delta = FloatSize(adjustedPosition.x() - m_currentPosX, adjustedPosition.y() - m_currentPosY);
790 m_currentPosX = adjustedPosition.x();
791 m_currentPosY = adjustedPosition.y();
792 notifyContentAreaScrolled(delta);
793 notifyPositionChanged();
796 bool ScrollAnimatorMac::isRubberBandInProgress() const
798 #if !USE(RUBBER_BANDING)
801 return m_scrollElasticityController.isRubberBandInProgress();
805 void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition)
807 ASSERT(m_scrollAnimationHelper);
808 immediateScrollTo(newPosition);
811 void ScrollAnimatorMac::contentAreaWillPaint() const
813 if (!scrollableArea()->scrollbarsCanBeActive())
815 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
816 [m_scrollbarPainterController.get() contentAreaWillDraw];
819 void ScrollAnimatorMac::mouseEnteredContentArea() const
821 if (!scrollableArea()->scrollbarsCanBeActive())
823 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
824 [m_scrollbarPainterController.get() mouseEnteredContentArea];
827 void ScrollAnimatorMac::mouseExitedContentArea() const
829 if (!scrollableArea()->scrollbarsCanBeActive())
831 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
832 [m_scrollbarPainterController.get() mouseExitedContentArea];
835 void ScrollAnimatorMac::mouseMovedInContentArea() const
837 if (!scrollableArea()->scrollbarsCanBeActive())
839 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
840 [m_scrollbarPainterController.get() mouseMovedInContentArea];
843 void ScrollAnimatorMac::mouseEnteredScrollbar(Scrollbar* scrollbar) const
845 // At this time, only legacy scrollbars needs to send notifications here.
846 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy)
849 if (!scrollableArea()->scrollbarsCanBeActive())
852 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
853 if (!supportsUIStateTransitionProgress())
855 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
856 [painter mouseEnteredScroller];
860 void ScrollAnimatorMac::mouseExitedScrollbar(Scrollbar* scrollbar) const
862 // At this time, only legacy scrollbars needs to send notifications here.
863 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy)
866 if (!scrollableArea()->scrollbarsCanBeActive())
869 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
870 if (!supportsUIStateTransitionProgress())
872 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar))
873 [painter mouseExitedScroller];
877 void ScrollAnimatorMac::willStartLiveResize()
879 if (!scrollableArea()->scrollbarsCanBeActive())
881 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
882 [m_scrollbarPainterController.get() startLiveResize];
885 void ScrollAnimatorMac::contentsResized() const
887 if (!scrollableArea()->scrollbarsCanBeActive())
889 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
890 [m_scrollbarPainterController.get() contentAreaDidResize];
893 void ScrollAnimatorMac::willEndLiveResize()
895 if (!scrollableArea()->scrollbarsCanBeActive())
897 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
898 [m_scrollbarPainterController.get() endLiveResize];
901 void ScrollAnimatorMac::contentAreaDidShow() const
903 if (!scrollableArea()->scrollbarsCanBeActive())
905 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
906 [m_scrollbarPainterController.get() windowOrderedIn];
909 void ScrollAnimatorMac::contentAreaDidHide() const
911 if (!scrollableArea()->scrollbarsCanBeActive())
913 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
914 [m_scrollbarPainterController.get() windowOrderedOut];
917 void ScrollAnimatorMac::didBeginScrollGesture() const
919 if (!scrollableArea()->scrollbarsCanBeActive())
921 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
922 [m_scrollbarPainterController.get() beginScrollGesture];
925 void ScrollAnimatorMac::didEndScrollGesture() const
927 if (!scrollableArea()->scrollbarsCanBeActive())
929 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable())
930 [m_scrollbarPainterController.get() endScrollGesture];
933 void ScrollAnimatorMac::mayBeginScrollGesture() const
935 if (!scrollableArea()->scrollbarsCanBeActive())
937 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
940 [m_scrollbarPainterController.get() beginScrollGesture];
941 [m_scrollbarPainterController.get() contentAreaScrolled];
944 void ScrollAnimatorMac::finishCurrentScrollAnimations()
946 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
947 [m_scrollbarPainterController.get() hideOverlayScrollers];
951 void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
953 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
956 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
960 ASSERT(!m_verticalScrollbarPainterDelegate);
961 m_verticalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
963 [painter setDelegate:m_verticalScrollbarPainterDelegate.get()];
964 [m_scrollbarPainterController.get() setVerticalScrollerImp:painter];
965 if (scrollableArea()->inLiveResize())
966 [painter setKnobAlpha:1];
969 void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
971 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
974 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
978 ASSERT(m_verticalScrollbarPainterDelegate);
979 [m_verticalScrollbarPainterDelegate.get() invalidate];
980 m_verticalScrollbarPainterDelegate = nullptr;
982 [painter setDelegate:nil];
983 [m_scrollbarPainterController.get() setVerticalScrollerImp:nil];
986 void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
988 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
991 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
995 ASSERT(!m_horizontalScrollbarPainterDelegate);
996 m_horizontalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]);
998 [painter setDelegate:m_horizontalScrollbarPainterDelegate.get()];
999 [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter];
1000 if (scrollableArea()->inLiveResize())
1001 [painter setKnobAlpha:1];
1004 void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
1006 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1009 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
1013 ASSERT(m_horizontalScrollbarPainterDelegate);
1014 [m_horizontalScrollbarPainterDelegate.get() invalidate];
1015 m_horizontalScrollbarPainterDelegate = nullptr;
1017 [painter setDelegate:nil];
1018 [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil];
1021 bool ScrollAnimatorMac::shouldScrollbarParticipateInHitTesting(Scrollbar* scrollbar)
1023 // Non-overlay scrollbars should always participate in hit testing.
1024 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleOverlay)
1027 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1030 if (scrollbar->isAlphaLocked())
1033 // Overlay scrollbars should participate in hit testing whenever they are at all visible.
1034 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar);
1037 return [painter knobAlpha] > 0;
1040 void ScrollAnimatorMac::notifyContentAreaScrolled(const FloatSize& delta)
1042 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1045 // This function is called when a page is going into the page cache, but the page
1046 // isn't really scrolling in that case. We should only pass the message on to the
1047 // ScrollbarPainterController when we're really scrolling on an active page.
1048 if (scrollableArea()->scrollbarsCanBeActive())
1049 sendContentAreaScrolledSoon(delta);
1052 void ScrollAnimatorMac::cancelAnimations()
1054 m_haveScrolledSincePageLoad = false;
1056 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
1057 if (scrollbarPaintTimerIsActive())
1058 stopScrollbarPaintTimer();
1059 [m_horizontalScrollbarPainterDelegate.get() cancelAnimations];
1060 [m_verticalScrollbarPainterDelegate.get() cancelAnimations];
1064 void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase)
1066 // This may not have been set to true yet if the wheel event was handled by the ScrollingTree,
1067 // So set it to true here.
1068 m_haveScrolledSincePageLoad = true;
1070 if (phase == PlatformWheelEventPhaseBegan)
1071 didBeginScrollGesture();
1072 else if (phase == PlatformWheelEventPhaseEnded || phase == PlatformWheelEventPhaseCancelled)
1073 didEndScrollGesture();
1074 else if (phase == PlatformWheelEventPhaseMayBegin)
1075 mayBeginScrollGesture();
1078 #if USE(RUBBER_BANDING)
1079 bool ScrollAnimatorMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
1081 m_haveScrolledSincePageLoad = true;
1083 if (!wheelEvent.hasPreciseScrollingDeltas() || !rubberBandingEnabledForSystem())
1084 return ScrollAnimator::handleWheelEvent(wheelEvent);
1086 // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
1087 // up to the parent scrollable area. It takes advantage of the fact that
1088 // the base class implementation of handleWheelEvent will not accept the
1089 // wheel event if there is nowhere to scroll.
1090 if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
1091 if (!allowsVerticalStretching())
1092 return ScrollAnimator::handleWheelEvent(wheelEvent);
1094 if (!allowsHorizontalStretching())
1095 return ScrollAnimator::handleWheelEvent(wheelEvent);
1098 bool didHandleEvent = m_scrollElasticityController.handleWheelEvent(wheelEvent);
1100 // The elasticity controller can return false on a phase end event if rubber banding wasn't in progress.
1101 // In this case, the wheel phase must still be handled so that that overlay scroll bars get hidden.
1102 if (didHandleEvent || wheelEvent.phase() == PlatformWheelEventPhaseEnded || wheelEvent.phase() == PlatformWheelEventPhaseCancelled)
1103 handleWheelEventPhase(wheelEvent.phase());
1105 return didHandleEvent;
1108 bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
1110 FloatSize limitDelta;
1111 if (fabsf(deltaY) >= fabsf(deltaX)) {
1113 // We are trying to scroll up. Make sure we are not pinned to the top
1114 limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
1116 // We are trying to scroll down. Make sure we are not pinned to the bottom
1117 limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
1119 } else if (deltaX != 0) {
1121 // We are trying to scroll left. Make sure we are not pinned to the left
1122 limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
1124 // We are trying to scroll right. Make sure we are not pinned to the right
1125 limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
1129 if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
1134 bool ScrollAnimatorMac::allowsVerticalStretching()
1136 switch (m_scrollableArea->verticalScrollElasticity()) {
1137 case ScrollElasticityAutomatic: {
1138 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1139 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1140 return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
1142 case ScrollElasticityNone:
1144 case ScrollElasticityAllowed:
1148 ASSERT_NOT_REACHED();
1152 bool ScrollAnimatorMac::allowsHorizontalStretching()
1154 switch (m_scrollableArea->horizontalScrollElasticity()) {
1155 case ScrollElasticityAutomatic: {
1156 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1157 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1158 return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
1160 case ScrollElasticityNone:
1162 case ScrollElasticityAllowed:
1166 ASSERT_NOT_REACHED();
1170 IntSize ScrollAnimatorMac::stretchAmount()
1172 return m_scrollableArea->overhangAmount();
1175 bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction)
1177 return pinnedInDirection(direction.width(), direction.height());
1180 bool ScrollAnimatorMac::canScrollHorizontally()
1182 Scrollbar* scrollbar = m_scrollableArea->horizontalScrollbar();
1185 return scrollbar->enabled();
1188 bool ScrollAnimatorMac::canScrollVertically()
1190 Scrollbar* scrollbar = m_scrollableArea->verticalScrollbar();
1193 return scrollbar->enabled();
1196 IntPoint ScrollAnimatorMac::absoluteScrollPosition()
1198 return m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin();
1201 void ScrollAnimatorMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& delta)
1203 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1204 immediateScrollBy(delta);
1205 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1208 void ScrollAnimatorMac::immediateScrollBy(const FloatSize& delta)
1210 FloatPoint newPos = adjustScrollPositionIfNecessary(FloatPoint(m_currentPosX, m_currentPosY) + delta);
1211 if (newPos.x() == m_currentPosX && newPos.y() == m_currentPosY)
1214 FloatSize adjustedDelta = FloatSize(newPos.x() - m_currentPosX, newPos.y() - m_currentPosY);
1216 m_currentPosX = newPos.x();
1217 m_currentPosY = newPos.y();
1218 notifyContentAreaScrolled(adjustedDelta);
1219 notifyPositionChanged();
1222 void ScrollAnimatorMac::startSnapRubberbandTimer()
1224 m_snapRubberBandTimer.startRepeating(1.0 / 60.0, FROM_HERE);
1227 void ScrollAnimatorMac::stopSnapRubberbandTimer()
1229 m_snapRubberBandTimer.stop();
1232 void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
1234 m_scrollElasticityController.snapRubberBandTimerFired();
1238 void ScrollAnimatorMac::setIsActive()
1240 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1243 if (!m_needsScrollerStyleUpdate)
1246 updateScrollerStyle();
1249 void ScrollAnimatorMac::updateScrollerStyle()
1251 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable())
1254 if (!scrollableArea()->scrollbarsCanBeActive()) {
1255 m_needsScrollerStyleUpdate = true;
1259 ScrollbarThemeMacOverlayAPI* macTheme = macOverlayScrollbarTheme();
1261 m_needsScrollerStyleUpdate = false;
1265 NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle];
1267 if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
1268 verticalScrollbar->invalidate();
1270 ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp];
1271 ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1272 controlSize:(NSControlSize)verticalScrollbar->controlSize()
1274 replacingScrollerImp:oldVerticalPainter];
1275 [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter];
1276 macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
1278 // The different scrollbar styles have different thicknesses, so we must re-set the
1279 // frameRect to the new thickness, and the re-layout below will ensure the position
1280 // and length are properly updated.
1281 int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
1282 verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1285 if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
1286 horizontalScrollbar->invalidate();
1288 ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp];
1289 ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1290 controlSize:(NSControlSize)horizontalScrollbar->controlSize()
1292 replacingScrollerImp:oldHorizontalPainter];
1293 [m_scrollbarPainterController.get() setHorizontalScrollerImp:newHorizontalPainter];
1294 macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
1296 // The different scrollbar styles have different thicknesses, so we must re-set the
1297 // frameRect to the new thickness, and the re-layout below will ensure the position
1298 // and length are properly updated.
1299 int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
1300 horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1303 // If m_needsScrollerStyleUpdate is true, then the page is restoring from the page cache, and
1304 // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves.
1305 if (!m_needsScrollerStyleUpdate)
1306 scrollableArea()->scrollbarStyleChanged();
1308 m_needsScrollerStyleUpdate = false;
1311 void ScrollAnimatorMac::startScrollbarPaintTimer()
1313 m_initialScrollbarPaintTimer.startOneShot(0.1, FROM_HERE);
1316 bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1318 return m_initialScrollbarPaintTimer.isActive();
1321 void ScrollAnimatorMac::stopScrollbarPaintTimer()
1323 m_initialScrollbarPaintTimer.stop();
1326 void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1328 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) {
1329 // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController
1330 // might think that the scrollbars are already showing and bail early.
1331 [m_scrollbarPainterController.get() hideOverlayScrollers];
1332 [m_scrollbarPainterController.get() flashScrollers];
1336 void ScrollAnimatorMac::sendContentAreaScrolledSoon(const FloatSize& delta)
1338 m_contentAreaScrolledTimerScrollDelta = delta;
1340 if (!m_sendContentAreaScrolledTimer.isActive())
1341 m_sendContentAreaScrolledTimer.startOneShot(0, FROM_HERE);
1344 void ScrollAnimatorMac::sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*)
1346 if (supportsContentAreaScrolledInDirection()) {
1347 [m_scrollbarPainterController.get() contentAreaScrolledInDirection:NSMakePoint(m_contentAreaScrolledTimerScrollDelta.width(), m_contentAreaScrolledTimerScrollDelta.height())];
1348 m_contentAreaScrolledTimerScrollDelta = FloatSize();
1350 [m_scrollbarPainterController.get() contentAreaScrolled];
1353 void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1355 IntRect rectInViewCoordinates = scrollerThumb;
1356 if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1357 rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1359 if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1362 m_visibleScrollerThumbRect = rectInViewCoordinates;
1365 bool ScrollAnimatorMac::canUseCoordinatedScrollbar() {
1366 return ScrollbarThemeMacCommon::isOverlayAPIAvailable();
1369 } // namespace blink