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 #if ENABLE(SMOOTH_SCROLLING)
30 #include "ScrollAnimatorMac.h"
32 #include "FloatPoint.h"
33 #include "NSScrollerImpDetails.h"
34 #include "PlatformGestureEvent.h"
35 #include "PlatformWheelEvent.h"
36 #include "ScrollView.h"
37 #include "ScrollableArea.h"
38 #include "ScrollbarTheme.h"
39 #include "ScrollbarThemeMac.h"
40 #include "WebCoreSystemInterface.h"
41 #include <wtf/PassOwnPtr.h>
42 #include <wtf/UnusedParam.h>
44 using namespace WebCore;
47 @interface NSObject (ScrollAnimationHelperDetails)
48 - (id)initWithDelegate:(id)delegate;
51 - (NSPoint)targetOrigin;
55 @interface ScrollAnimationHelperDelegate : NSObject
57 WebCore::ScrollAnimatorMac* _animator;
59 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
62 static NSSize abs(NSSize size)
64 NSSize finalSize = size;
65 if (finalSize.width < 0)
66 finalSize.width = -finalSize.width;
67 if (finalSize.height < 0)
68 finalSize.height = -finalSize.height;
72 @implementation ScrollAnimationHelperDelegate
74 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
80 _animator = scrollAnimator;
84 - (void)scrollAnimatorDestroyed
94 WebCore::FloatPoint currentPosition = _animator->currentPosition();
95 return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
98 - (void)_immediateScrollToPoint:(NSPoint)newPosition
102 _animator->immediateScrollToPointForScrollAnimation(newPosition);
105 - (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin
110 - (NSSize)convertSizeToBase:(NSSize)size
115 - (NSSize)convertSizeFromBase:(NSSize)size
120 - (NSSize)convertSizeToBacking:(NSSize)size
125 - (NSSize)convertSizeFromBacking:(NSSize)size
145 - (void)_recursiveRecomputeToolTips
151 #if USE(SCROLLBAR_PAINTER)
153 @interface ScrollbarPainterControllerDelegate : NSObject
155 WebCore::ScrollAnimatorMac* _animator;
157 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
160 @implementation ScrollbarPainterControllerDelegate
162 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
168 _animator = scrollAnimator;
172 - (void)scrollAnimatorDestroyed
177 - (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
179 UNUSED_PARAM(scrollerImpPair);
183 WebCore::IntSize contentsSize = _animator->scrollableArea()->contentsSize();
184 return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
187 - (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
189 UNUSED_PARAM(scrollerImpPair);
193 return _animator->scrollableArea()->inLiveResize();
196 - (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
198 UNUSED_PARAM(scrollerImpPair);
202 return _animator->scrollableArea()->currentMousePosition();
205 - (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
207 UNUSED_PARAM(scrollerImpPair);
211 WebCore::Scrollbar* scrollbar = 0;
212 if ([scrollerImp isHorizontal])
213 scrollbar = _animator->scrollableArea()->horizontalScrollbar();
215 scrollbar = _animator->scrollableArea()->verticalScrollbar();
217 // It is possible to have a null scrollbar here since it is possible for this delegate
218 // method to be called between the moment when a scrollbar has been set to 0 and the
219 // moment when its destructor has been called. We should probably de-couple some
220 // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this
223 return WebCore::IntPoint();
225 return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea));
228 - (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
230 UNUSED_PARAM(scrollerImpPair);
234 - (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
239 [scrollerImpPair setScrollerStyle:newRecommendedScrollerStyle];
240 _animator->updateScrollerStyle();
245 @interface ScrollbarPartAnimation : NSAnimation
247 RetainPtr<ScrollbarPainter> _scrollerPainter;
248 WebCore::ScrollbarPart _part;
249 WebCore::ScrollAnimatorMac* _animator;
250 CGFloat _initialAlpha;
253 - (id)initWithScrollbarPainter:(ScrollbarPainter)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration;
256 @implementation ScrollbarPartAnimation
258 - (id)initWithScrollbarPainter:(ScrollbarPainter)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
260 self = [super initWithDuration:duration animationCurve:NSAnimationEaseInOut];
264 _scrollerPainter = scrollerPainter;
266 _animator = scrollAnimator;
267 _initialAlpha = _part == WebCore::ThumbPart ? [_scrollerPainter.get() knobAlpha] : [_scrollerPainter.get() trackAlpha];
268 _newAlpha = newAlpha;
273 - (void)setCurrentProgress:(NSAnimationProgress)progress
275 [super setCurrentProgress:progress];
280 CGFloat currentAlpha;
281 if (_initialAlpha > _newAlpha)
282 currentAlpha = 1 - progress;
284 currentAlpha = progress;
286 if (_part == WebCore::ThumbPart)
287 [_scrollerPainter.get() setKnobAlpha:currentAlpha];
289 [_scrollerPainter.get() setTrackAlpha:currentAlpha];
291 // Invalidate the scrollbars so that they paint the animation
292 if (WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar())
293 verticalScrollbar->invalidateRect(WebCore::IntRect(0, 0, verticalScrollbar->width(), verticalScrollbar->height()));
294 if (WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar())
295 horizontalScrollbar->invalidateRect(WebCore::IntRect(0, 0, horizontalScrollbar->width(), horizontalScrollbar->height()));
298 - (void)scrollAnimatorDestroyed
300 [self stopAnimation];
306 @interface ScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
308 WebCore::ScrollAnimatorMac* _animator;
310 RetainPtr<ScrollbarPartAnimation> _verticalKnobAnimation;
311 RetainPtr<ScrollbarPartAnimation> _horizontalKnobAnimation;
313 RetainPtr<ScrollbarPartAnimation> _verticalTrackAnimation;
314 RetainPtr<ScrollbarPartAnimation> _horizontalTrackAnimation;
316 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator;
317 - (void)cancelAnimations;
320 @implementation ScrollbarPainterDelegate
322 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator
328 _animator = scrollAnimator;
332 - (void)cancelAnimations
334 [_verticalKnobAnimation.get() stopAnimation];
335 [_horizontalKnobAnimation.get() stopAnimation];
336 [_verticalTrackAnimation.get() stopAnimation];
337 [_horizontalTrackAnimation.get() stopAnimation];
340 - (NSRect)convertRectToBacking:(NSRect)aRect
345 - (NSRect)convertRectFromBacking:(NSRect)aRect
354 if (!_animator->isDrawingIntoLayer())
357 // FIXME: This should attempt to return an actual layer.
358 static CALayer *dummyLayer = [[CALayer alloc] init];
362 - (void)setUpAnimation:(RetainPtr<ScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(ScrollbarPainter)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
364 // If the user has scrolled the page, then the scrollbars must be animated here.
365 // This overrides the early returns.
366 bool mustAnimate = _animator->haveScrolledSincePageLoad();
368 if (_animator->scrollbarPaintTimerIsActive() && !mustAnimate)
371 if (_animator->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
372 _animator->startScrollbarPaintTimer();
376 // At this point, we are definitely going to animate now, so stop the timer.
377 _animator->stopScrollbarPaintTimer();
379 // If we are currently animating, stop
380 if (scrollbarPartAnimation) {
381 [scrollbarPartAnimation.get() stopAnimation];
382 scrollbarPartAnimation = nil;
385 if (part == WebCore::ThumbPart && ![scrollerPainter isHorizontal]) {
387 IntRect thumbRect = IntRect([scrollerPainter rectForPart:NSScrollerKnob]);
388 _animator->setVisibleScrollerThumbRect(thumbRect);
390 _animator->setVisibleScrollerThumbRect(IntRect());
393 [NSAnimationContext beginGrouping];
394 [[NSAnimationContext currentContext] setDuration:duration];
395 scrollbarPartAnimation.adoptNS([[ScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter
397 scrollAnimator:_animator
398 animateAlphaTo:newAlpha
400 [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
401 [scrollbarPartAnimation.get() startAnimation];
402 [NSAnimationContext endGrouping];
405 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
410 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
411 if ([scrollerImp isHorizontal])
412 [self setUpAnimation:_horizontalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
414 [self setUpAnimation:_verticalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
417 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
422 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp;
423 if ([scrollerImp isHorizontal])
424 [self setUpAnimation:_horizontalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
426 [self setUpAnimation:_verticalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
429 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
431 UNUSED_PARAM(scrollerImp);
432 UNUSED_PARAM(newOverlayScrollerState);
435 - (void)scrollAnimatorDestroyed
438 [_verticalKnobAnimation.get() scrollAnimatorDestroyed];
439 [_horizontalKnobAnimation.get() scrollAnimatorDestroyed];
440 [_verticalTrackAnimation.get() scrollAnimatorDestroyed];
441 [_horizontalTrackAnimation.get() scrollAnimatorDestroyed];
446 #endif // USE(SCROLLBAR_PAINTER)
450 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
452 return adoptPtr(new ScrollAnimatorMac(scrollableArea));
455 ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea)
456 : ScrollAnimator(scrollableArea)
457 #if USE(SCROLLBAR_PAINTER)
458 , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired)
460 #if ENABLE(RUBBER_BANDING)
461 , m_inScrollGesture(false)
462 , m_momentumScrollInProgress(false)
463 , m_ignoreMomentumScrolls(false)
464 , m_lastMomentumScrollTimestamp(0)
466 , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired)
468 , m_drawingIntoLayer(false)
469 , m_haveScrolledSincePageLoad(false)
470 , m_needsScrollerStyleUpdate(false)
472 m_scrollAnimationHelperDelegate.adoptNS([[ScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
473 m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
475 #if USE(SCROLLBAR_PAINTER)
476 m_scrollbarPainterControllerDelegate.adoptNS([[ScrollbarPainterControllerDelegate alloc] initWithScrollAnimator:this]);
477 m_scrollbarPainterController = [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease];
478 [m_scrollbarPainterController.get() setDelegate:m_scrollbarPainterControllerDelegate.get()];
479 [m_scrollbarPainterController.get() setScrollerStyle:wkRecommendedScrollerStyle()];
481 m_scrollbarPainterDelegate.adoptNS([[ScrollbarPainterDelegate alloc] initWithScrollAnimator:this]);
485 ScrollAnimatorMac::~ScrollAnimatorMac()
487 #if USE(SCROLLBAR_PAINTER)
488 [m_scrollbarPainterControllerDelegate.get() scrollAnimatorDestroyed];
489 [m_scrollbarPainterController.get() setDelegate:nil];
490 [m_scrollbarPainterDelegate.get() scrollAnimatorDestroyed];
491 [m_scrollAnimationHelperDelegate.get() scrollAnimatorDestroyed];
495 bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
497 m_haveScrolledSincePageLoad = true;
499 if (![[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollAnimationEnabled"] || !m_scrollableArea->scrollAnimatorEnabled())
500 return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
502 if (granularity == ScrollByPixel)
503 return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
505 float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
506 float newPos = std::max<float>(std::min<float>(currentPos + (step * multiplier), static_cast<float>(m_scrollableArea->scrollSize(orientation))), 0);
507 if (currentPos == newPos)
511 if ([m_scrollAnimationHelper.get() _isAnimating]) {
512 NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
513 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
515 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
516 m_scrollableArea->didStartAnimatedScroll();
519 [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
523 void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
525 [m_scrollAnimationHelper.get() _stopRun];
526 immediateScrollToPoint(offset);
529 float ScrollAnimatorMac::adjustScrollXPositionIfNecessary(float position) const
531 if (!m_scrollableArea->constrainsScrollingToContentEdge())
534 return max<float>(min<float>(position, m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
537 float ScrollAnimatorMac::adjustScrollYPositionIfNecessary(float position) const
539 if (!m_scrollableArea->constrainsScrollingToContentEdge())
542 return max<float>(min<float>(position, m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
545 FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
547 if (!m_scrollableArea->constrainsScrollingToContentEdge())
550 float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
551 float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
553 return FloatPoint(newX, newY);
556 void ScrollAnimatorMac::immediateScrollToPoint(const FloatPoint& newPosition)
558 FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
560 if (adjustedPosition.x() == m_currentPosX && adjustedPosition.y() == m_currentPosY)
563 m_currentPosX = adjustedPosition.x();
564 m_currentPosY = adjustedPosition.y();
565 notifyPositionChanged();
568 void ScrollAnimatorMac::immediateScrollByDeltaX(float deltaX)
570 float newPosX = adjustScrollXPositionIfNecessary(m_currentPosX + deltaX);
572 if (newPosX == m_currentPosX)
575 m_currentPosX = newPosX;
576 notifyPositionChanged();
579 void ScrollAnimatorMac::immediateScrollByDeltaY(float deltaY)
581 float newPosY = adjustScrollYPositionIfNecessary(m_currentPosY + deltaY);
583 if (newPosY == m_currentPosY)
586 m_currentPosY = newPosY;
587 notifyPositionChanged();
590 void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition)
592 ASSERT(m_scrollAnimationHelper);
593 CGFloat progress = [m_scrollAnimationHelper.get() _progress];
595 immediateScrollToPoint(newPosition);
598 m_scrollableArea->didCompleteAnimatedScroll();
601 void ScrollAnimatorMac::notifyPositionChanged()
603 #if USE(SCROLLBAR_PAINTER)
604 [m_scrollbarPainterController.get() contentAreaScrolled];
606 ScrollAnimator::notifyPositionChanged();
609 void ScrollAnimatorMac::contentAreaWillPaint() const
611 #if USE(SCROLLBAR_PAINTER)
612 [m_scrollbarPainterController.get() contentAreaWillDraw];
616 void ScrollAnimatorMac::mouseEnteredContentArea() const
618 #if USE(SCROLLBAR_PAINTER)
619 [m_scrollbarPainterController.get() mouseEnteredContentArea];
623 void ScrollAnimatorMac::mouseExitedContentArea() const
625 #if USE(SCROLLBAR_PAINTER)
626 [m_scrollbarPainterController.get() mouseExitedContentArea];
630 void ScrollAnimatorMac::mouseMovedInContentArea() const
632 #if USE(SCROLLBAR_PAINTER)
633 [m_scrollbarPainterController.get() mouseMovedInContentArea];
637 void ScrollAnimatorMac::willStartLiveResize()
639 #if USE(SCROLLBAR_PAINTER)
640 [m_scrollbarPainterController.get() startLiveResize];
644 void ScrollAnimatorMac::contentsResized() const
646 #if USE(SCROLLBAR_PAINTER)
647 [m_scrollbarPainterController.get() contentAreaDidResize];
651 void ScrollAnimatorMac::willEndLiveResize()
653 #if USE(SCROLLBAR_PAINTER)
654 [m_scrollbarPainterController.get() endLiveResize];
658 void ScrollAnimatorMac::contentAreaDidShow() const
660 #if USE(SCROLLBAR_PAINTER)
661 [m_scrollbarPainterController.get() windowOrderedIn];
665 void ScrollAnimatorMac::contentAreaDidHide() const
667 #if USE(SCROLLBAR_PAINTER)
668 [m_scrollbarPainterController.get() windowOrderedOut];
672 void ScrollAnimatorMac::didBeginScrollGesture() const
674 #if USE(SCROLLBAR_PAINTER)
675 [m_scrollbarPainterController.get() beginScrollGesture];
679 void ScrollAnimatorMac::didEndScrollGesture() const
681 #if USE(SCROLLBAR_PAINTER)
682 [m_scrollbarPainterController.get() endScrollGesture];
686 void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
688 #if USE(SCROLLBAR_PAINTER)
689 ScrollbarPainter painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
690 [painter setDelegate:m_scrollbarPainterDelegate.get()];
691 [m_scrollbarPainterController.get() setVerticalScrollerImp:painter];
692 if (scrollableArea()->inLiveResize())
693 [painter setKnobAlpha:1];
695 UNUSED_PARAM(scrollbar);
699 void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
701 #if USE(SCROLLBAR_PAINTER)
702 ScrollbarPainter painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
703 [painter setDelegate:nil];
704 [m_scrollbarPainterController.get() setVerticalScrollerImp:nil];
706 UNUSED_PARAM(scrollbar);
710 void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
712 #if USE(SCROLLBAR_PAINTER)
713 ScrollbarPainter painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
714 [painter setDelegate:m_scrollbarPainterDelegate.get()];
715 [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter];
716 if (scrollableArea()->inLiveResize())
717 [painter setKnobAlpha:1];
719 UNUSED_PARAM(scrollbar);
723 void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
725 #if USE(SCROLLBAR_PAINTER)
726 ScrollbarPainter painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar);
727 [painter setDelegate:nil];
728 [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil];
730 UNUSED_PARAM(scrollbar);
734 void ScrollAnimatorMac::cancelAnimations()
736 m_haveScrolledSincePageLoad = false;
738 #if USE(SCROLLBAR_PAINTER)
739 if (scrollbarPaintTimerIsActive())
740 stopScrollbarPaintTimer();
741 [m_scrollbarPainterDelegate.get() cancelAnimations];
745 #if ENABLE(RUBBER_BANDING)
747 static const float scrollVelocityZeroingTimeout = 0.10f;
748 static const float rubberbandStiffness = 20;
749 static const float rubberbandDirectionLockStretchRatio = 1;
750 static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
751 static const float rubberbandAmplitude = 0.31f;
752 static const float rubberbandPeriod = 1.6f;
754 static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime)
756 float amplitude = rubberbandAmplitude;
757 float period = rubberbandPeriod;
758 float criticalDampeningFactor = expf((-elapsedTime * rubberbandStiffness) / period);
760 return (initialPosition + (-initialVelocity * elapsedTime * amplitude)) * criticalDampeningFactor;
763 static float elasticDeltaForReboundDelta(float delta)
765 float stiffness = std::max(rubberbandStiffness, 1.0f);
766 return delta / stiffness;
769 static float reboundDeltaForElasticDelta(float delta)
771 return delta * rubberbandStiffness;
774 static float scrollWheelMultiplier()
776 static float multiplier = -1;
777 if (multiplier < 0) {
778 multiplier = [[NSUserDefaults standardUserDefaults] floatForKey:@"NSScrollWheelMultiplier"];
785 static inline bool isScrollingLeftAndShouldNotRubberBand(PlatformWheelEvent& wheelEvent, ScrollableArea* scrollableArea)
787 return wheelEvent.deltaX() > 0 && !scrollableArea->shouldRubberBandInDirection(ScrollLeft);
790 static inline bool isScrollingRightAndShouldNotRubberBand(PlatformWheelEvent& wheelEvent, ScrollableArea* scrollableArea)
792 return wheelEvent.deltaX() < 0 && !scrollableArea->shouldRubberBandInDirection(ScrollRight);
795 void ScrollAnimatorMac::handleWheelEvent(PlatformWheelEvent& wheelEvent)
797 m_haveScrolledSincePageLoad = true;
799 if (!wheelEvent.hasPreciseScrollingDeltas()) {
800 ScrollAnimator::handleWheelEvent(wheelEvent);
804 // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
805 // up to the parent scrollable area. It takes advantage of the fact that
806 // the base class implemenatation of handleWheelEvent will not accept the
807 // wheel event if there is nowhere to scroll.
808 if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
809 if (!allowsVerticalStretching()) {
810 ScrollAnimator::handleWheelEvent(wheelEvent);
814 if (!allowsHorizontalStretching()) {
815 ScrollAnimator::handleWheelEvent(wheelEvent);
819 if (m_scrollableArea->horizontalScrollbar()) {
820 // If there is a scrollbar, we aggregate the wheel events to get an
821 // overall trend of the scroll. If the direction of the scroll is ever
822 // in the opposite direction of the pin location, then we switch the
823 // boolean, and rubber band. That is, if we were pinned to the left,
824 // and we ended up scrolling to the right, we rubber band.
825 m_cumulativeHorizontalScroll += wheelEvent.deltaX();
826 if (m_scrollerInitiallyPinnedOnLeft && m_cumulativeHorizontalScroll < 0)
827 m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = true;
828 if (m_scrollerInitiallyPinnedOnRight && m_cumulativeHorizontalScroll > 0)
829 m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = true;
832 // After a gesture begins, we go through:
833 // 1+ PlatformWheelEventPhaseNone
834 // 0+ PlatformWheelEventPhaseChanged
835 // 1 PlatformWheelEventPhaseEnded if there was at least one changed event
836 if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseNone && !m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin) {
837 if ((isScrollingLeftAndShouldNotRubberBand(wheelEvent, m_scrollableArea) &&
838 m_scrollerInitiallyPinnedOnLeft &&
839 m_scrollableArea->isHorizontalScrollerPinnedToMinimumPosition()) ||
840 (isScrollingRightAndShouldNotRubberBand(wheelEvent, m_scrollableArea) &&
841 m_scrollerInitiallyPinnedOnRight &&
842 m_scrollableArea->isHorizontalScrollerPinnedToMaximumPosition())) {
843 ScrollAnimator::handleWheelEvent(wheelEvent);
849 bool isMomentumScrollEvent = (wheelEvent.momentumPhase() != PlatformWheelEventPhaseNone);
850 if (m_ignoreMomentumScrolls && (isMomentumScrollEvent || m_snapRubberBandTimer.isActive())) {
851 if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded) {
852 m_ignoreMomentumScrolls = false;
859 smoothScrollWithEvent(wheelEvent);
862 void ScrollAnimatorMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
864 if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType)
865 beginScrollGesture();
866 else if (gestureEvent.type() == PlatformGestureEvent::ScrollEndType)
870 bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY)
872 FloatSize limitDelta;
873 if (fabsf(deltaY) >= fabsf(deltaX)) {
875 // We are trying to scroll up. Make sure we are not pinned to the top
876 limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
878 // We are trying to scroll down. Make sure we are not pinned to the bottom
879 limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
881 } else if (deltaX != 0) {
883 // We are trying to scroll left. Make sure we are not pinned to the left
884 limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
886 // We are trying to scroll right. Make sure we are not pinned to the right
887 limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
891 if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
896 bool ScrollAnimatorMac::allowsVerticalStretching() const
898 switch (m_scrollableArea->verticalScrollElasticity()) {
899 case ScrollElasticityAutomatic: {
900 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
901 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
902 return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
904 case ScrollElasticityNone:
906 case ScrollElasticityAllowed:
910 ASSERT_NOT_REACHED();
914 bool ScrollAnimatorMac::allowsHorizontalStretching() const
916 switch (m_scrollableArea->horizontalScrollElasticity()) {
917 case ScrollElasticityAutomatic: {
918 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
919 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
920 return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
922 case ScrollElasticityNone:
924 case ScrollElasticityAllowed:
928 ASSERT_NOT_REACHED();
932 void ScrollAnimatorMac::smoothScrollWithEvent(PlatformWheelEvent& wheelEvent)
934 m_haveScrolledSincePageLoad = true;
936 float deltaX = m_overflowScrollDelta.width();
937 float deltaY = m_overflowScrollDelta.height();
939 // Reset overflow values because we may decide to remove delta at various points and put it into overflow.
940 m_overflowScrollDelta = FloatSize();
942 float eventCoalescedDeltaX = -wheelEvent.deltaX();
943 float eventCoalescedDeltaY = -wheelEvent.deltaY();
945 deltaX += eventCoalescedDeltaX;
946 deltaY += eventCoalescedDeltaY;
948 // Slightly prefer scrolling vertically by applying the = case to deltaY
949 if (fabsf(deltaY) >= fabsf(deltaX))
954 bool isVerticallyStretched = false;
955 bool isHorizontallyStretched = false;
956 bool shouldStretch = false;
958 IntSize stretchAmount = m_scrollableArea->overhangAmount();
960 isHorizontallyStretched = stretchAmount.width();
961 isVerticallyStretched = stretchAmount.height();
963 PlatformWheelEventPhase phase = wheelEvent.momentumPhase();
965 // If we are starting momentum scrolling then do some setup.
966 if (!m_momentumScrollInProgress && (phase == PlatformWheelEventPhaseBegan || phase == PlatformWheelEventPhaseChanged))
967 m_momentumScrollInProgress = true;
969 CFTimeInterval timeDelta = wheelEvent.timestamp() - m_lastMomentumScrollTimestamp;
970 if (m_inScrollGesture || m_momentumScrollInProgress) {
971 if (m_lastMomentumScrollTimestamp && timeDelta > 0 && timeDelta < scrollVelocityZeroingTimeout) {
972 m_momentumVelocity.setWidth(eventCoalescedDeltaX / (float)timeDelta);
973 m_momentumVelocity.setHeight(eventCoalescedDeltaY / (float)timeDelta);
974 m_lastMomentumScrollTimestamp = wheelEvent.timestamp();
976 m_lastMomentumScrollTimestamp = wheelEvent.timestamp();
977 m_momentumVelocity = FloatSize();
980 if (isVerticallyStretched) {
981 if (!isHorizontallyStretched && pinnedInDirection(deltaX, 0)) {
982 // Stretching only in the vertical.
983 if (deltaY != 0 && (fabsf(deltaX / deltaY) < rubberbandDirectionLockStretchRatio))
985 else if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
986 m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
989 m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
991 } else if (isHorizontallyStretched) {
992 // Stretching only in the horizontal.
993 if (pinnedInDirection(0, deltaY)) {
994 if (deltaX != 0 && (fabsf(deltaY / deltaX) < rubberbandDirectionLockStretchRatio))
996 else if (fabsf(deltaY) < rubberbandMinimumRequiredDeltaBeforeStretch) {
997 m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
1000 m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
1003 // Not stretching at all yet.
1004 if (pinnedInDirection(deltaX, deltaY)) {
1005 if (fabsf(deltaY) >= fabsf(deltaX)) {
1006 if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
1007 m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
1010 m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
1012 shouldStretch = true;
1017 if (deltaX != 0 || deltaY != 0) {
1018 if (!(shouldStretch || isVerticallyStretched || isHorizontallyStretched)) {
1020 deltaY *= scrollWheelMultiplier();
1021 immediateScrollByDeltaY(deltaY);
1024 deltaX *= scrollWheelMultiplier();
1025 immediateScrollByDeltaX(deltaX);
1028 if (!allowsHorizontalStretching()) {
1030 eventCoalescedDeltaX = 0;
1031 } else if ((deltaX != 0) && !isHorizontallyStretched && !pinnedInDirection(deltaX, 0)) {
1032 deltaX *= scrollWheelMultiplier();
1034 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1035 immediateScrollByDeltaX(deltaX);
1036 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1041 if (!allowsVerticalStretching()) {
1043 eventCoalescedDeltaY = 0;
1044 } else if ((deltaY != 0) && !isVerticallyStretched && !pinnedInDirection(0, deltaY)) {
1045 deltaY *= scrollWheelMultiplier();
1047 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1048 immediateScrollByDeltaY(deltaY);
1049 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1054 IntSize stretchAmount = m_scrollableArea->overhangAmount();
1056 if (m_momentumScrollInProgress) {
1057 if ((pinnedInDirection(eventCoalescedDeltaX, eventCoalescedDeltaY) || (fabsf(eventCoalescedDeltaX) + fabsf(eventCoalescedDeltaY) <= 0)) && m_lastMomentumScrollTimestamp) {
1058 m_ignoreMomentumScrolls = true;
1059 m_momentumScrollInProgress = false;
1064 m_stretchScrollForce.setWidth(m_stretchScrollForce.width() + deltaX);
1065 m_stretchScrollForce.setHeight(m_stretchScrollForce.height() + deltaY);
1067 FloatSize dampedDelta(ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.width())), ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.height())));
1068 FloatPoint origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - stretchAmount;
1069 FloatPoint newOrigin = origOrigin + dampedDelta;
1071 if (origOrigin != newOrigin) {
1072 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1073 immediateScrollToPoint(newOrigin);
1074 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1079 if (m_momentumScrollInProgress && phase == PlatformWheelEventPhaseEnded) {
1080 m_momentumScrollInProgress = false;
1081 m_ignoreMomentumScrolls = false;
1082 m_lastMomentumScrollTimestamp = 0;
1086 void ScrollAnimatorMac::beginScrollGesture()
1088 didBeginScrollGesture();
1090 m_haveScrolledSincePageLoad = true;
1091 m_inScrollGesture = true;
1092 m_momentumScrollInProgress = false;
1093 m_ignoreMomentumScrolls = false;
1094 m_lastMomentumScrollTimestamp = 0;
1095 m_momentumVelocity = FloatSize();
1096 m_scrollerInitiallyPinnedOnLeft = m_scrollableArea->isHorizontalScrollerPinnedToMinimumPosition();
1097 m_scrollerInitiallyPinnedOnRight = m_scrollableArea->isHorizontalScrollerPinnedToMaximumPosition();
1098 m_cumulativeHorizontalScroll = 0;
1099 m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = false;
1101 IntSize stretchAmount = m_scrollableArea->overhangAmount();
1102 m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(stretchAmount.width()));
1103 m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(stretchAmount.height()));
1105 m_overflowScrollDelta = FloatSize();
1107 if (m_snapRubberBandTimer.isActive())
1108 m_snapRubberBandTimer.stop();
1111 void ScrollAnimatorMac::endScrollGesture()
1113 didEndScrollGesture();
1118 void ScrollAnimatorMac::snapRubberBand()
1120 CFTimeInterval timeDelta = [[NSProcessInfo processInfo] systemUptime] - m_lastMomentumScrollTimestamp;
1121 if (m_lastMomentumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout)
1122 m_momentumVelocity = FloatSize();
1124 m_inScrollGesture = false;
1126 if (m_snapRubberBandTimer.isActive())
1129 m_startTime = [NSDate timeIntervalSinceReferenceDate];
1130 m_startStretch = FloatSize();
1131 m_origOrigin = FloatPoint();
1132 m_origVelocity = FloatSize();
1134 m_snapRubberBandTimer.startRepeating(1.0/60.0);
1137 static inline float roundTowardZero(float num)
1139 return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f);
1142 static inline float roundToDevicePixelTowardZero(float num)
1144 float roundedNum = roundf(num);
1145 if (fabs(num - roundedNum) < 0.125)
1148 return roundTowardZero(num);
1151 void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*)
1153 if (!m_momentumScrollInProgress || m_ignoreMomentumScrolls) {
1154 CFTimeInterval timeDelta = [NSDate timeIntervalSinceReferenceDate] - m_startTime;
1156 if (m_startStretch == FloatSize()) {
1157 m_startStretch = m_scrollableArea->overhangAmount();
1158 if (m_startStretch == FloatSize()) {
1159 m_snapRubberBandTimer.stop();
1160 m_stretchScrollForce = FloatSize();
1162 m_startStretch = FloatSize();
1163 m_origOrigin = FloatPoint();
1164 m_origVelocity = FloatSize();
1169 m_scrollableArea->didStartRubberBand(roundedIntSize(m_startStretch));
1171 m_origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - m_startStretch;
1172 m_origVelocity = m_momentumVelocity;
1174 // Just like normal scrolling, prefer vertical rubberbanding
1175 if (fabsf(m_origVelocity.height()) >= fabsf(m_origVelocity.width()))
1176 m_origVelocity.setWidth(0);
1178 // Don't rubber-band horizontally if it's not possible to scroll horizontally
1179 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1180 if (!hScroller || !hScroller->enabled())
1181 m_origVelocity.setWidth(0);
1183 // Don't rubber-band vertically if it's not possible to scroll horizontally
1184 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1185 if (!vScroller || !vScroller->enabled())
1186 m_origVelocity.setHeight(0);
1189 FloatPoint delta(roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.width(), -m_origVelocity.width(), (float)timeDelta)),
1190 roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.height(), -m_origVelocity.height(), (float)timeDelta)));
1192 if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) {
1193 FloatPoint newOrigin = m_origOrigin + delta;
1195 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1196 immediateScrollToPoint(newOrigin);
1197 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1199 FloatSize newStretch = m_scrollableArea->overhangAmount();
1201 m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(newStretch.width()));
1202 m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(newStretch.height()));
1204 immediateScrollToPoint(m_origOrigin);
1206 m_scrollableArea->didCompleteRubberBand(roundedIntSize(m_startStretch));
1208 m_snapRubberBandTimer.stop();
1209 m_stretchScrollForce = FloatSize();
1212 m_startStretch = FloatSize();
1213 m_origOrigin = FloatPoint();
1214 m_origVelocity = FloatSize();
1217 m_startTime = [NSDate timeIntervalSinceReferenceDate];
1218 m_startStretch = FloatSize();
1223 void ScrollAnimatorMac::setIsActive()
1225 #if USE(SCROLLBAR_PAINTER)
1226 if (needsScrollerStyleUpdate())
1227 updateScrollerStyle();
1231 #if USE(SCROLLBAR_PAINTER)
1232 void ScrollAnimatorMac::updateScrollerStyle()
1234 if (!scrollableArea()->isOnActivePage()) {
1235 setNeedsScrollerStyleUpdate(true);
1239 ScrollbarThemeMac* macTheme = (ScrollbarThemeMac*)ScrollbarTheme::nativeTheme();
1240 NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle];
1242 if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
1243 verticalScrollbar->invalidate();
1245 ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp];
1246 ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1247 controlSize:(NSControlSize)verticalScrollbar->controlSize()
1249 replacingScrollerImp:oldVerticalPainter];
1250 macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
1251 [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter];
1253 // The different scrollbar styles have different thicknesses, so we must re-set the
1254 // frameRect to the new thickness, and the re-layout below will ensure the position
1255 // and length are properly updated.
1256 int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
1257 verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1260 if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
1261 horizontalScrollbar->invalidate();
1263 ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp];
1264 ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle
1265 controlSize:(NSControlSize)horizontalScrollbar->controlSize()
1267 replacingScrollerImp:oldHorizontalPainter];
1268 macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
1269 [m_scrollbarPainterController.get() setVerticalScrollerImp:newHorizontalPainter];
1271 // The different scrollbar styles have different thicknesses, so we must re-set the
1272 // frameRect to the new thickness, and the re-layout below will ensure the position
1273 // and length are properly updated.
1274 int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
1275 horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1278 // If needsScrollerStyleUpdate() is true, then the page is restoring from the page cache, and
1279 // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves.
1280 if (!needsScrollerStyleUpdate())
1281 scrollableArea()->scrollbarStyleChanged();
1283 setNeedsScrollerStyleUpdate(false);
1286 void ScrollAnimatorMac::startScrollbarPaintTimer()
1288 m_initialScrollbarPaintTimer.startOneShot(0.1);
1291 bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const
1293 return m_initialScrollbarPaintTimer.isActive();
1296 void ScrollAnimatorMac::stopScrollbarPaintTimer()
1298 m_initialScrollbarPaintTimer.stop();
1301 void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*)
1303 // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController
1304 // might think that the scrollbars are already showing and bail early.
1305 [m_scrollbarPainterController.get() hideOverlayScrollers];
1306 [m_scrollbarPainterController.get() flashScrollers];
1310 void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1312 IntRect rectInViewCoordinates = scrollerThumb;
1313 if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1314 rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1316 if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1319 m_scrollableArea->setVisibleScrollerThumbRect(rectInViewCoordinates);
1320 m_visibleScrollerThumbRect = rectInViewCoordinates;
1323 } // namespace WebCore
1325 #endif // ENABLE(SMOOTH_SCROLLING)