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