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