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#include "platform/mac/ScrollAnimatorMac.h" 29 30#include "platform/PlatformGestureEvent.h" 31#include "platform/PlatformWheelEvent.h" 32#include "platform/geometry/FloatRect.h" 33#include "platform/geometry/IntRect.h" 34#include "platform/mac/BlockExceptions.h" 35#include "platform/mac/NSScrollerImpDetails.h" 36#include "platform/scroll/ScrollView.h" 37#include "platform/scroll/ScrollableArea.h" 38#include "platform/scroll/ScrollbarTheme.h" 39#include "platform/scroll/ScrollbarThemeMacCommon.h" 40#include "platform/scroll/ScrollbarThemeMacOverlayAPI.h" 41#include "wtf/MainThread.h" 42#include "wtf/PassOwnPtr.h" 43 44using namespace WebCore; 45using namespace std; 46 47static bool supportsUIStateTransitionProgress() 48{ 49 // FIXME: This is temporary until all platforms that support ScrollbarPainter support this part of the API. 50 static bool globalSupportsUIStateTransitionProgress = [NSClassFromString(@"NSScrollerImp") instancesRespondToSelector:@selector(mouseEnteredScroller)]; 51 return globalSupportsUIStateTransitionProgress; 52} 53 54static bool supportsExpansionTransitionProgress() 55{ 56 static bool globalSupportsExpansionTransitionProgress = [NSClassFromString(@"NSScrollerImp") instancesRespondToSelector:@selector(expansionTransitionProgress)]; 57 return globalSupportsExpansionTransitionProgress; 58} 59 60static bool supportsContentAreaScrolledInDirection() 61{ 62 static bool globalSupportsContentAreaScrolledInDirection = [NSClassFromString(@"NSScrollerImpPair") instancesRespondToSelector:@selector(contentAreaScrolledInDirection:)]; 63 return globalSupportsContentAreaScrolledInDirection; 64} 65 66static ScrollbarThemeMacOverlayAPI* macOverlayScrollbarTheme() 67{ 68 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(ScrollbarThemeMacCommon::isOverlayAPIAvailable()); 69 ScrollbarTheme* scrollbarTheme = ScrollbarTheme::theme(); 70 return !scrollbarTheme->isMockTheme() ? static_cast<ScrollbarThemeMacOverlayAPI*>(scrollbarTheme) : 0; 71} 72 73static ScrollbarPainter scrollbarPainterForScrollbar(Scrollbar* scrollbar) 74{ 75 if (ScrollbarThemeMacOverlayAPI* scrollbarTheme = macOverlayScrollbarTheme()) 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 if (!_scrollableArea) 212 return NSZeroRect; 213 214 WebCore::IntSize contentsSize = _scrollableArea->contentsSize(); 215 return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height()); 216} 217 218- (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair 219{ 220 if (!_scrollableArea) 221 return NO; 222 223 return _scrollableArea->inLiveResize(); 224} 225 226- (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair 227{ 228 if (!_scrollableArea) 229 return NSZeroPoint; 230 231 return _scrollableArea->lastKnownMousePosition(); 232} 233 234- (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp 235{ 236 237 if (!_scrollableArea || !scrollerImp) 238 return NSZeroPoint; 239 240 WebCore::Scrollbar* scrollbar = 0; 241 if ([scrollerImp isHorizontal]) 242 scrollbar = _scrollableArea->horizontalScrollbar(); 243 else 244 scrollbar = _scrollableArea->verticalScrollbar(); 245 246 // It is possible to have a null scrollbar here since it is possible for this delegate 247 // method to be called between the moment when a scrollbar has been set to 0 and the 248 // moment when its destructor has been called. We should probably de-couple some 249 // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this 250 // issue. 251 if (!scrollbar) 252 return NSZeroPoint; 253 254 ASSERT(scrollerImp == scrollbarPainterForScrollbar(scrollbar)); 255 256 return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea)); 257} 258 259- (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect 260{ 261 if (!_scrollableArea) 262 return; 263 264 if (!_scrollableArea->scrollbarsCanBeActive()) 265 return; 266 267 _scrollableArea->scrollAnimator()->contentAreaWillPaint(); 268} 269 270- (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle 271{ 272 // Chrome has a single process mode which is used for testing on Mac. In that mode, WebKit runs on a thread in the 273 // browser process. This notification is called by the OS on the main thread in the browser process, and not on the 274 // the WebKit thread. Better to not update the style than crash. 275 // http://crbug.com/126514 276 if (!isMainThread()) 277 return; 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- (NSPoint)mouseLocationInScrollerForScrollerImp:(id)scrollerImp 435{ 436 if (!_scrollbar) 437 return NSZeroPoint; 438 439 ASSERT_UNUSED(scrollerImp, scrollerImp == scrollbarPainterForScrollbar(_scrollbar)); 440 441 return _scrollbar->convertFromContainingView(_scrollbar->scrollableArea()->lastKnownMousePosition()); 442} 443 444- (void)setUpAlphaAnimation:(RetainPtr<WebScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(ScrollbarPainter)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration 445{ 446 // If the user has scrolled the page, then the scrollbars must be animated here. 447 // This overrides the early returns. 448 bool mustAnimate = [self scrollAnimator]->haveScrolledSincePageLoad(); 449 450 if ([self scrollAnimator]->scrollbarPaintTimerIsActive() && !mustAnimate) 451 return; 452 453 if (_scrollbar->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) { 454 [self scrollAnimator]->startScrollbarPaintTimer(); 455 return; 456 } 457 458 // At this point, we are definitely going to animate now, so stop the timer. 459 [self scrollAnimator]->stopScrollbarPaintTimer(); 460 461 // If we are currently animating, stop 462 if (scrollbarPartAnimation) { 463 [scrollbarPartAnimation.get() stopAnimation]; 464 scrollbarPartAnimation = nil; 465 } 466 467 if (part == WebCore::ThumbPart && _scrollbar->orientation() == VerticalScrollbar) { 468 if (newAlpha == 1) { 469 IntRect thumbRect = IntRect([scrollerPainter rectForPart:NSScrollerKnob]); 470 [self scrollAnimator]->setVisibleScrollerThumbRect(thumbRect); 471 } else 472 [self scrollAnimator]->setVisibleScrollerThumbRect(IntRect()); 473 } 474 475 scrollbarPartAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar 476 featureToAnimate:part == ThumbPart ? ThumbAlpha : TrackAlpha 477 animateFrom:part == ThumbPart ? [scrollerPainter knobAlpha] : [scrollerPainter trackAlpha] 478 animateTo:newAlpha 479 duration:duration]); 480 [scrollbarPartAnimation.get() startAnimation]; 481} 482 483- (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration 484{ 485 if (!_scrollbar) 486 return; 487 488 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar)); 489 490 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp; 491 [self setUpAlphaAnimation:_knobAlphaAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration]; 492} 493 494- (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration 495{ 496 if (!_scrollbar) 497 return; 498 499 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar)); 500 501 ScrollbarPainter scrollerPainter = (ScrollbarPainter)scrollerImp; 502 [self setUpAlphaAnimation:_trackAlphaAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration]; 503} 504 505- (void)scrollerImp:(id)scrollerImp animateUIStateTransitionWithDuration:(NSTimeInterval)duration 506{ 507 if (!_scrollbar) 508 return; 509 510 if (!supportsUIStateTransitionProgress()) 511 return; 512 513 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar)); 514 515 ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp; 516 517 // UIStateTransition always animates to 1. In case an animation is in progress this avoids a hard transition. 518 [scrollbarPainter setUiStateTransitionProgress:1 - [scrollerImp uiStateTransitionProgress]]; 519 520 if (!_uiStateTransitionAnimation) 521 _uiStateTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar 522 featureToAnimate:UIStateTransition 523 animateFrom:[scrollbarPainter uiStateTransitionProgress] 524 animateTo:1.0 525 duration:duration]); 526 else { 527 // If we don't need to initialize the animation, just reset the values in case they have changed. 528 [_uiStateTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]]; 529 [_uiStateTransitionAnimation.get() setEndValue:1.0]; 530 [_uiStateTransitionAnimation.get() setDuration:duration]; 531 } 532 [_uiStateTransitionAnimation.get() startAnimation]; 533} 534 535- (void)scrollerImp:(id)scrollerImp animateExpansionTransitionWithDuration:(NSTimeInterval)duration 536{ 537 if (!_scrollbar) 538 return; 539 540 if (!supportsExpansionTransitionProgress()) 541 return; 542 543 ASSERT(scrollerImp == scrollbarPainterForScrollbar(_scrollbar)); 544 545 ScrollbarPainter scrollbarPainter = (ScrollbarPainter)scrollerImp; 546 547 // ExpansionTransition always animates to 1. In case an animation is in progress this avoids a hard transition. 548 [scrollbarPainter setExpansionTransitionProgress:1 - [scrollerImp expansionTransitionProgress]]; 549 550 if (!_expansionTransitionAnimation) { 551 _expansionTransitionAnimation.adoptNS([[WebScrollbarPartAnimation alloc] initWithScrollbar:_scrollbar 552 featureToAnimate:ExpansionTransition 553 animateFrom:[scrollbarPainter expansionTransitionProgress] 554 animateTo:1.0 555 duration:duration]); 556 } else { 557 // If we don't need to initialize the animation, just reset the values in case they have changed. 558 [_expansionTransitionAnimation.get() setStartValue:[scrollbarPainter uiStateTransitionProgress]]; 559 [_expansionTransitionAnimation.get() setEndValue:1.0]; 560 [_expansionTransitionAnimation.get() setDuration:duration]; 561 } 562 [_expansionTransitionAnimation.get() startAnimation]; 563} 564 565- (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState 566{ 567} 568 569- (void)invalidate 570{ 571 _scrollbar = 0; 572 BEGIN_BLOCK_OBJC_EXCEPTIONS; 573 [_knobAlphaAnimation.get() invalidate]; 574 [_trackAlphaAnimation.get() invalidate]; 575 [_uiStateTransitionAnimation.get() invalidate]; 576 [_expansionTransitionAnimation.get() invalidate]; 577 END_BLOCK_OBJC_EXCEPTIONS; 578} 579 580@end 581 582namespace WebCore { 583 584PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea) 585{ 586 return adoptPtr(new ScrollAnimatorMac(scrollableArea)); 587} 588 589ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea) 590 : ScrollAnimator(scrollableArea) 591 , m_initialScrollbarPaintTimer(this, &ScrollAnimatorMac::initialScrollbarPaintTimerFired) 592 , m_sendContentAreaScrolledTimer(this, &ScrollAnimatorMac::sendContentAreaScrolledTimerFired) 593#if USE(RUBBER_BANDING) 594 , m_scrollElasticityController(this) 595 , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired) 596#endif 597 , m_haveScrolledSincePageLoad(false) 598 , m_needsScrollerStyleUpdate(false) 599{ 600 m_scrollAnimationHelperDelegate.adoptNS([[WebScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]); 601 m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]); 602 603 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { 604 m_scrollbarPainterControllerDelegate.adoptNS([[WebScrollbarPainterControllerDelegate alloc] initWithScrollableArea:scrollableArea]); 605 m_scrollbarPainterController = [[[NSClassFromString(@"NSScrollerImpPair") alloc] init] autorelease]; 606 [m_scrollbarPainterController.get() setDelegate:m_scrollbarPainterControllerDelegate.get()]; 607 [m_scrollbarPainterController.get() setScrollerStyle:ScrollbarThemeMacCommon::recommendedScrollerStyle()]; 608 } 609} 610 611ScrollAnimatorMac::~ScrollAnimatorMac() 612{ 613 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { 614 BEGIN_BLOCK_OBJC_EXCEPTIONS; 615 [m_scrollbarPainterControllerDelegate.get() invalidate]; 616 [m_scrollbarPainterController.get() setDelegate:nil]; 617 [m_horizontalScrollbarPainterDelegate.get() invalidate]; 618 [m_verticalScrollbarPainterDelegate.get() invalidate]; 619 [m_scrollAnimationHelperDelegate.get() invalidate]; 620 END_BLOCK_OBJC_EXCEPTIONS; 621 } 622} 623 624static bool scrollAnimationEnabledForSystem() 625{ 626 NSString* scrollAnimationDefaultsKey = 627 @"AppleScrollAnimationEnabled"; 628 static bool enabled = [[NSUserDefaults standardUserDefaults] boolForKey:scrollAnimationDefaultsKey]; 629 return enabled; 630} 631 632#if USE(RUBBER_BANDING) 633static bool rubberBandingEnabledForSystem() 634{ 635 static bool initialized = false; 636 static bool enabled = true; 637 // Caches the result, which is consistent with other apps like the Finder, which all 638 // require a restart after changing this default. 639 if (!initialized) { 640 // Uses -objectForKey: and not -boolForKey: in order to default to true if the value wasn't set. 641 id value = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSScrollViewRubberbanding"]; 642 if ([value isKindOfClass:[NSNumber class]]) 643 enabled = [value boolValue]; 644 initialized = true; 645 } 646 return enabled; 647} 648#endif 649 650bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta) 651{ 652 m_haveScrolledSincePageLoad = true; 653 654 if (!scrollAnimationEnabledForSystem() || !m_scrollableArea->scrollAnimatorEnabled()) 655 return ScrollAnimator::scroll(orientation, granularity, step, delta); 656 657 if (granularity == ScrollByPixel) 658 return ScrollAnimator::scroll(orientation, granularity, step, delta); 659 660 float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY; 661 float newPos = std::max<float>(std::min<float>(currentPos + (step * delta), m_scrollableArea->maximumScrollPosition(orientation)), m_scrollableArea->minimumScrollPosition(orientation)); 662 if (currentPos == newPos) 663 return false; 664 665 NSPoint newPoint; 666 if ([m_scrollAnimationHelper.get() _isAnimating]) { 667 NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin]; 668 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos); 669 } else 670 newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos); 671 672 [m_scrollAnimationHelper.get() scrollToPoint:newPoint]; 673 return true; 674} 675 676void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset) 677{ 678 [m_scrollAnimationHelper.get() _stopRun]; 679 immediateScrollTo(offset); 680} 681 682FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const 683{ 684 if (!m_scrollableArea->constrainsScrollingToContentEdge()) 685 return position; 686 687 IntPoint minPos = m_scrollableArea->minimumScrollPosition(); 688 IntPoint maxPos = m_scrollableArea->maximumScrollPosition(); 689 690 float newX = max<float>(min<float>(position.x(), maxPos.x()), minPos.x()); 691 float newY = max<float>(min<float>(position.y(), maxPos.y()), minPos.y()); 692 693 return FloatPoint(newX, newY); 694} 695 696void ScrollAnimatorMac::adjustScrollPositionToBoundsIfNecessary() 697{ 698 bool currentlyConstrainsToContentEdge = m_scrollableArea->constrainsScrollingToContentEdge(); 699 m_scrollableArea->setConstrainsScrollingToContentEdge(true); 700 701 IntPoint currentScrollPosition = absoluteScrollPosition(); 702 FloatPoint nearestPointWithinBounds = adjustScrollPositionIfNecessary(absoluteScrollPosition()); 703 immediateScrollBy(nearestPointWithinBounds - currentScrollPosition); 704 705 m_scrollableArea->setConstrainsScrollingToContentEdge(currentlyConstrainsToContentEdge); 706} 707 708void ScrollAnimatorMac::immediateScrollTo(const FloatPoint& newPosition) 709{ 710 FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition); 711 712 bool positionChanged = adjustedPosition.x() != m_currentPosX || adjustedPosition.y() != m_currentPosY; 713 if (!positionChanged && !scrollableArea()->scrollOriginChanged()) 714 return; 715 716 FloatSize delta = FloatSize(adjustedPosition.x() - m_currentPosX, adjustedPosition.y() - m_currentPosY); 717 718 m_currentPosX = adjustedPosition.x(); 719 m_currentPosY = adjustedPosition.y(); 720 notifyContentAreaScrolled(delta); 721 notifyPositionChanged(); 722} 723 724bool ScrollAnimatorMac::isRubberBandInProgress() const 725{ 726#if !USE(RUBBER_BANDING) 727 return false; 728#else 729 return m_scrollElasticityController.isRubberBandInProgress(); 730#endif 731} 732 733void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition) 734{ 735 ASSERT(m_scrollAnimationHelper); 736 immediateScrollTo(newPosition); 737} 738 739void ScrollAnimatorMac::contentAreaWillPaint() const 740{ 741 if (!scrollableArea()->scrollbarsCanBeActive()) 742 return; 743 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 744 [m_scrollbarPainterController.get() contentAreaWillDraw]; 745} 746 747void ScrollAnimatorMac::mouseEnteredContentArea() const 748{ 749 if (!scrollableArea()->scrollbarsCanBeActive()) 750 return; 751 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 752 [m_scrollbarPainterController.get() mouseEnteredContentArea]; 753} 754 755void ScrollAnimatorMac::mouseExitedContentArea() const 756{ 757 if (!scrollableArea()->scrollbarsCanBeActive()) 758 return; 759 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 760 [m_scrollbarPainterController.get() mouseExitedContentArea]; 761} 762 763void ScrollAnimatorMac::mouseMovedInContentArea() const 764{ 765 if (!scrollableArea()->scrollbarsCanBeActive()) 766 return; 767 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 768 [m_scrollbarPainterController.get() mouseMovedInContentArea]; 769} 770 771void ScrollAnimatorMac::mouseEnteredScrollbar(Scrollbar* scrollbar) const 772{ 773 // At this time, only legacy scrollbars needs to send notifications here. 774 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy) 775 return; 776 777 if (!scrollableArea()->scrollbarsCanBeActive()) 778 return; 779 780 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { 781 if (!supportsUIStateTransitionProgress()) 782 return; 783 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar)) 784 [painter mouseEnteredScroller]; 785 } 786} 787 788void ScrollAnimatorMac::mouseExitedScrollbar(Scrollbar* scrollbar) const 789{ 790 // At this time, only legacy scrollbars needs to send notifications here. 791 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleLegacy) 792 return; 793 794 if (!scrollableArea()->scrollbarsCanBeActive()) 795 return; 796 797 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { 798 if (!supportsUIStateTransitionProgress()) 799 return; 800 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar)) 801 [painter mouseExitedScroller]; 802 } 803} 804 805void ScrollAnimatorMac::willStartLiveResize() 806{ 807 if (!scrollableArea()->scrollbarsCanBeActive()) 808 return; 809 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 810 [m_scrollbarPainterController.get() startLiveResize]; 811} 812 813void ScrollAnimatorMac::contentsResized() const 814{ 815 if (!scrollableArea()->scrollbarsCanBeActive()) 816 return; 817 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 818 [m_scrollbarPainterController.get() contentAreaDidResize]; 819} 820 821void ScrollAnimatorMac::willEndLiveResize() 822{ 823 if (!scrollableArea()->scrollbarsCanBeActive()) 824 return; 825 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 826 [m_scrollbarPainterController.get() endLiveResize]; 827} 828 829void ScrollAnimatorMac::contentAreaDidShow() const 830{ 831 if (!scrollableArea()->scrollbarsCanBeActive()) 832 return; 833 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 834 [m_scrollbarPainterController.get() windowOrderedIn]; 835} 836 837void ScrollAnimatorMac::contentAreaDidHide() const 838{ 839 if (!scrollableArea()->scrollbarsCanBeActive()) 840 return; 841 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 842 [m_scrollbarPainterController.get() windowOrderedOut]; 843} 844 845void ScrollAnimatorMac::didBeginScrollGesture() const 846{ 847 if (!scrollableArea()->scrollbarsCanBeActive()) 848 return; 849 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 850 [m_scrollbarPainterController.get() beginScrollGesture]; 851} 852 853void ScrollAnimatorMac::didEndScrollGesture() const 854{ 855 if (!scrollableArea()->scrollbarsCanBeActive()) 856 return; 857 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 858 [m_scrollbarPainterController.get() endScrollGesture]; 859} 860 861void ScrollAnimatorMac::mayBeginScrollGesture() const 862{ 863 if (!scrollableArea()->scrollbarsCanBeActive()) 864 return; 865 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 866 return; 867 868 [m_scrollbarPainterController.get() beginScrollGesture]; 869 [m_scrollbarPainterController.get() contentAreaScrolled]; 870} 871 872void ScrollAnimatorMac::finishCurrentScrollAnimations() 873{ 874 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { 875 [m_scrollbarPainterController.get() hideOverlayScrollers]; 876 } 877} 878 879void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar) 880{ 881 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 882 return; 883 884 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 885 if (!painter) 886 return; 887 888 ASSERT(!m_verticalScrollbarPainterDelegate); 889 m_verticalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]); 890 891 [painter setDelegate:m_verticalScrollbarPainterDelegate.get()]; 892 [m_scrollbarPainterController.get() setVerticalScrollerImp:painter]; 893 if (scrollableArea()->inLiveResize()) 894 [painter setKnobAlpha:1]; 895} 896 897void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar) 898{ 899 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 900 return; 901 902 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 903 if (!painter) 904 return; 905 906 ASSERT(m_verticalScrollbarPainterDelegate); 907 [m_verticalScrollbarPainterDelegate.get() invalidate]; 908 m_verticalScrollbarPainterDelegate = nullptr; 909 910 [painter setDelegate:nil]; 911 [m_scrollbarPainterController.get() setVerticalScrollerImp:nil]; 912} 913 914void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar) 915{ 916 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 917 return; 918 919 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 920 if (!painter) 921 return; 922 923 ASSERT(!m_horizontalScrollbarPainterDelegate); 924 m_horizontalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]); 925 926 [painter setDelegate:m_horizontalScrollbarPainterDelegate.get()]; 927 [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter]; 928 if (scrollableArea()->inLiveResize()) 929 [painter setKnobAlpha:1]; 930} 931 932void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar) 933{ 934 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 935 return; 936 937 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 938 if (!painter) 939 return; 940 941 ASSERT(m_horizontalScrollbarPainterDelegate); 942 [m_horizontalScrollbarPainterDelegate.get() invalidate]; 943 m_horizontalScrollbarPainterDelegate = nullptr; 944 945 [painter setDelegate:nil]; 946 [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil]; 947} 948 949bool ScrollAnimatorMac::shouldScrollbarParticipateInHitTesting(Scrollbar* scrollbar) 950{ 951 // Non-overlay scrollbars should always participate in hit testing. 952 if (ScrollbarThemeMacCommon::recommendedScrollerStyle() != NSScrollerStyleOverlay) 953 return true; 954 955 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 956 return true; 957 958 if (scrollbar->isAlphaLocked()) 959 return true; 960 961 // Overlay scrollbars should participate in hit testing whenever they are at all visible. 962 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 963 if (!painter) 964 return false; 965 return [painter knobAlpha] > 0; 966} 967 968void ScrollAnimatorMac::notifyContentAreaScrolled(const FloatSize& delta) 969{ 970 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 971 return; 972 973 // This function is called when a page is going into the page cache, but the page 974 // isn't really scrolling in that case. We should only pass the message on to the 975 // ScrollbarPainterController when we're really scrolling on an active page. 976 if (scrollableArea()->scrollbarsCanBeActive()) 977 sendContentAreaScrolledSoon(delta); 978} 979 980void ScrollAnimatorMac::cancelAnimations() 981{ 982 m_haveScrolledSincePageLoad = false; 983 984 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { 985 if (scrollbarPaintTimerIsActive()) 986 stopScrollbarPaintTimer(); 987 [m_horizontalScrollbarPainterDelegate.get() cancelAnimations]; 988 [m_verticalScrollbarPainterDelegate.get() cancelAnimations]; 989 } 990} 991 992void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase) 993{ 994 // This may not have been set to true yet if the wheel event was handled by the ScrollingTree, 995 // So set it to true here. 996 m_haveScrolledSincePageLoad = true; 997 998 if (phase == PlatformWheelEventPhaseBegan) 999 didBeginScrollGesture(); 1000 else if (phase == PlatformWheelEventPhaseEnded || phase == PlatformWheelEventPhaseCancelled) 1001 didEndScrollGesture(); 1002 else if (phase == PlatformWheelEventPhaseMayBegin) 1003 mayBeginScrollGesture(); 1004} 1005 1006#if USE(RUBBER_BANDING) 1007bool ScrollAnimatorMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent) 1008{ 1009 m_haveScrolledSincePageLoad = true; 1010 1011 if (!wheelEvent.hasPreciseScrollingDeltas() || !rubberBandingEnabledForSystem()) 1012 return ScrollAnimator::handleWheelEvent(wheelEvent); 1013 1014 // FIXME: This is somewhat roundabout hack to allow forwarding wheel events 1015 // up to the parent scrollable area. It takes advantage of the fact that 1016 // the base class implementation of handleWheelEvent will not accept the 1017 // wheel event if there is nowhere to scroll. 1018 if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) { 1019 if (!allowsVerticalStretching()) 1020 return ScrollAnimator::handleWheelEvent(wheelEvent); 1021 } else { 1022 if (!allowsHorizontalStretching()) 1023 return ScrollAnimator::handleWheelEvent(wheelEvent); 1024 } 1025 1026 bool didHandleEvent = m_scrollElasticityController.handleWheelEvent(wheelEvent); 1027 1028 // The elasticity controller can return false on a phase end event if rubber banding wasn't in progress. 1029 // In this case, the wheel phase must still be handled so that that overlay scroll bars get hidden. 1030 if (didHandleEvent || wheelEvent.phase() == PlatformWheelEventPhaseEnded || wheelEvent.phase() == PlatformWheelEventPhaseCancelled) 1031 handleWheelEventPhase(wheelEvent.phase()); 1032 1033 return didHandleEvent; 1034} 1035 1036bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY) 1037{ 1038 FloatSize limitDelta; 1039 if (fabsf(deltaY) >= fabsf(deltaX)) { 1040 if (deltaY < 0) { 1041 // We are trying to scroll up. Make sure we are not pinned to the top 1042 limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y()); 1043 } else { 1044 // We are trying to scroll down. Make sure we are not pinned to the bottom 1045 limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y())); 1046 } 1047 } else if (deltaX != 0) { 1048 if (deltaX < 0) { 1049 // We are trying to scroll left. Make sure we are not pinned to the left 1050 limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x()); 1051 } else { 1052 // We are trying to scroll right. Make sure we are not pinned to the right 1053 limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x())); 1054 } 1055 } 1056 1057 if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1)) 1058 return true; 1059 return false; 1060} 1061 1062bool ScrollAnimatorMac::allowsVerticalStretching() 1063{ 1064 switch (m_scrollableArea->verticalScrollElasticity()) { 1065 case ScrollElasticityAutomatic: { 1066 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar(); 1067 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar(); 1068 return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled()))); 1069 } 1070 case ScrollElasticityNone: 1071 return false; 1072 case ScrollElasticityAllowed: 1073 return true; 1074 } 1075 1076 ASSERT_NOT_REACHED(); 1077 return false; 1078} 1079 1080bool ScrollAnimatorMac::allowsHorizontalStretching() 1081{ 1082 switch (m_scrollableArea->horizontalScrollElasticity()) { 1083 case ScrollElasticityAutomatic: { 1084 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar(); 1085 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar(); 1086 return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled()))); 1087 } 1088 case ScrollElasticityNone: 1089 return false; 1090 case ScrollElasticityAllowed: 1091 return true; 1092 } 1093 1094 ASSERT_NOT_REACHED(); 1095 return false; 1096} 1097 1098IntSize ScrollAnimatorMac::stretchAmount() 1099{ 1100 return m_scrollableArea->overhangAmount(); 1101} 1102 1103bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction) 1104{ 1105 return pinnedInDirection(direction.width(), direction.height()); 1106} 1107 1108bool ScrollAnimatorMac::canScrollHorizontally() 1109{ 1110 Scrollbar* scrollbar = m_scrollableArea->horizontalScrollbar(); 1111 if (!scrollbar) 1112 return false; 1113 return scrollbar->enabled(); 1114} 1115 1116bool ScrollAnimatorMac::canScrollVertically() 1117{ 1118 Scrollbar* scrollbar = m_scrollableArea->verticalScrollbar(); 1119 if (!scrollbar) 1120 return false; 1121 return scrollbar->enabled(); 1122} 1123 1124IntPoint ScrollAnimatorMac::absoluteScrollPosition() 1125{ 1126 return m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin(); 1127} 1128 1129void ScrollAnimatorMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& delta) 1130{ 1131 m_scrollableArea->setConstrainsScrollingToContentEdge(false); 1132 immediateScrollBy(delta); 1133 m_scrollableArea->setConstrainsScrollingToContentEdge(true); 1134} 1135 1136void ScrollAnimatorMac::immediateScrollBy(const FloatSize& delta) 1137{ 1138 FloatPoint newPos = adjustScrollPositionIfNecessary(FloatPoint(m_currentPosX, m_currentPosY) + delta); 1139 if (newPos.x() == m_currentPosX && newPos.y() == m_currentPosY) 1140 return; 1141 1142 FloatSize adjustedDelta = FloatSize(newPos.x() - m_currentPosX, newPos.y() - m_currentPosY); 1143 1144 m_currentPosX = newPos.x(); 1145 m_currentPosY = newPos.y(); 1146 notifyContentAreaScrolled(adjustedDelta); 1147 notifyPositionChanged(); 1148} 1149 1150void ScrollAnimatorMac::startSnapRubberbandTimer() 1151{ 1152 m_snapRubberBandTimer.startRepeating(1.0 / 60.0, FROM_HERE); 1153} 1154 1155void ScrollAnimatorMac::stopSnapRubberbandTimer() 1156{ 1157 m_snapRubberBandTimer.stop(); 1158} 1159 1160void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*) 1161{ 1162 m_scrollElasticityController.snapRubberBandTimerFired(); 1163} 1164#endif 1165 1166void ScrollAnimatorMac::setIsActive() 1167{ 1168 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 1169 return; 1170 1171 if (!m_needsScrollerStyleUpdate) 1172 return; 1173 1174 updateScrollerStyle(); 1175} 1176 1177void ScrollAnimatorMac::updateScrollerStyle() 1178{ 1179 if (!ScrollbarThemeMacCommon::isOverlayAPIAvailable()) 1180 return; 1181 1182 if (!scrollableArea()->scrollbarsCanBeActive()) { 1183 m_needsScrollerStyleUpdate = true; 1184 return; 1185 } 1186 1187 ScrollbarThemeMacOverlayAPI* macTheme = macOverlayScrollbarTheme(); 1188 if (!macTheme) { 1189 m_needsScrollerStyleUpdate = false; 1190 return; 1191 } 1192 1193 NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle]; 1194 1195 if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) { 1196 verticalScrollbar->invalidate(); 1197 1198 ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp]; 1199 ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 1200 controlSize:(NSControlSize)verticalScrollbar->controlSize() 1201 horizontal:NO 1202 replacingScrollerImp:oldVerticalPainter]; 1203 [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter]; 1204 macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter); 1205 1206 // The different scrollbar styles have different thicknesses, so we must re-set the 1207 // frameRect to the new thickness, and the re-layout below will ensure the position 1208 // and length are properly updated. 1209 int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize()); 1210 verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness)); 1211 } 1212 1213 if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) { 1214 horizontalScrollbar->invalidate(); 1215 1216 ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp]; 1217 ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 1218 controlSize:(NSControlSize)horizontalScrollbar->controlSize() 1219 horizontal:YES 1220 replacingScrollerImp:oldHorizontalPainter]; 1221 [m_scrollbarPainterController.get() setHorizontalScrollerImp:newHorizontalPainter]; 1222 macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter); 1223 1224 // The different scrollbar styles have different thicknesses, so we must re-set the 1225 // frameRect to the new thickness, and the re-layout below will ensure the position 1226 // and length are properly updated. 1227 int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize()); 1228 horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness)); 1229 } 1230 1231 // If m_needsScrollerStyleUpdate is true, then the page is restoring from the page cache, and 1232 // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves. 1233 if (!m_needsScrollerStyleUpdate) 1234 scrollableArea()->scrollbarStyleChanged(); 1235 1236 m_needsScrollerStyleUpdate = false; 1237} 1238 1239void ScrollAnimatorMac::startScrollbarPaintTimer() 1240{ 1241 m_initialScrollbarPaintTimer.startOneShot(0.1, FROM_HERE); 1242} 1243 1244bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const 1245{ 1246 return m_initialScrollbarPaintTimer.isActive(); 1247} 1248 1249void ScrollAnimatorMac::stopScrollbarPaintTimer() 1250{ 1251 m_initialScrollbarPaintTimer.stop(); 1252} 1253 1254void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*) 1255{ 1256 if (ScrollbarThemeMacCommon::isOverlayAPIAvailable()) { 1257 // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController 1258 // might think that the scrollbars are already showing and bail early. 1259 [m_scrollbarPainterController.get() hideOverlayScrollers]; 1260 [m_scrollbarPainterController.get() flashScrollers]; 1261 } 1262} 1263 1264void ScrollAnimatorMac::sendContentAreaScrolledSoon(const FloatSize& delta) 1265{ 1266 m_contentAreaScrolledTimerScrollDelta = delta; 1267 1268 if (!m_sendContentAreaScrolledTimer.isActive()) 1269 m_sendContentAreaScrolledTimer.startOneShot(0, FROM_HERE); 1270} 1271 1272void ScrollAnimatorMac::sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*) 1273{ 1274 if (supportsContentAreaScrolledInDirection()) { 1275 [m_scrollbarPainterController.get() contentAreaScrolledInDirection:NSMakePoint(m_contentAreaScrolledTimerScrollDelta.width(), m_contentAreaScrolledTimerScrollDelta.height())]; 1276 m_contentAreaScrolledTimerScrollDelta = FloatSize(); 1277 } else 1278 [m_scrollbarPainterController.get() contentAreaScrolled]; 1279} 1280 1281void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb) 1282{ 1283 IntRect rectInViewCoordinates = scrollerThumb; 1284 if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar()) 1285 rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb); 1286 1287 if (rectInViewCoordinates == m_visibleScrollerThumbRect) 1288 return; 1289 1290 m_visibleScrollerThumbRect = rectInViewCoordinates; 1291} 1292 1293bool ScrollAnimatorMac::canUseCoordinatedScrollbar() { 1294 return ScrollbarThemeMacCommon::isOverlayAPIAvailable(); 1295} 1296 1297} // namespace WebCore 1298