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/FloatPoint.h" 33#include "platform/mac/BlockExceptions.h" 34#include "platform/mac/EmptyProtocolDefinitions.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(isScrollbarOverlayAPIAvailable()); 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 (isScrollbarOverlayAPIAvailable()) { 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:recommendedScrollerStyle()]; 608 } 609} 610 611ScrollAnimatorMac::~ScrollAnimatorMac() 612{ 613 if (isScrollbarOverlayAPIAvailable()) { 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 multiplier) 651{ 652 m_haveScrolledSincePageLoad = true; 653 654 if (!scrollAnimationEnabledForSystem() || !m_scrollableArea->scrollAnimatorEnabled()) 655 return ScrollAnimator::scroll(orientation, granularity, step, multiplier); 656 657 if (granularity == ScrollByPixel) 658 return ScrollAnimator::scroll(orientation, granularity, step, multiplier); 659 660 float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY; 661 float newPos = std::max<float>(std::min<float>(currentPos + (step * multiplier), 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::immediateScrollTo(const FloatPoint& newPosition) 697{ 698 FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition); 699 700 bool positionChanged = adjustedPosition.x() != m_currentPosX || adjustedPosition.y() != m_currentPosY; 701 if (!positionChanged && !scrollableArea()->scrollOriginChanged()) 702 return; 703 704 FloatSize delta = FloatSize(adjustedPosition.x() - m_currentPosX, adjustedPosition.y() - m_currentPosY); 705 706 m_currentPosX = adjustedPosition.x(); 707 m_currentPosY = adjustedPosition.y(); 708 notifyPositionChanged(delta); 709} 710 711bool ScrollAnimatorMac::isRubberBandInProgress() const 712{ 713#if !USE(RUBBER_BANDING) 714 return false; 715#else 716 return m_scrollElasticityController.isRubberBandInProgress(); 717#endif 718} 719 720void ScrollAnimatorMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition) 721{ 722 ASSERT(m_scrollAnimationHelper); 723 immediateScrollTo(newPosition); 724} 725 726void ScrollAnimatorMac::notifyPositionChanged(const FloatSize& delta) 727{ 728 notifyContentAreaScrolled(delta); 729 ScrollAnimator::notifyPositionChanged(delta); 730} 731 732void ScrollAnimatorMac::contentAreaWillPaint() const 733{ 734 if (!scrollableArea()->scrollbarsCanBeActive()) 735 return; 736 if (isScrollbarOverlayAPIAvailable()) 737 [m_scrollbarPainterController.get() contentAreaWillDraw]; 738} 739 740void ScrollAnimatorMac::mouseEnteredContentArea() const 741{ 742 if (!scrollableArea()->scrollbarsCanBeActive()) 743 return; 744 if (isScrollbarOverlayAPIAvailable()) 745 [m_scrollbarPainterController.get() mouseEnteredContentArea]; 746} 747 748void ScrollAnimatorMac::mouseExitedContentArea() const 749{ 750 if (!scrollableArea()->scrollbarsCanBeActive()) 751 return; 752 if (isScrollbarOverlayAPIAvailable()) 753 [m_scrollbarPainterController.get() mouseExitedContentArea]; 754} 755 756void ScrollAnimatorMac::mouseMovedInContentArea() const 757{ 758 if (!scrollableArea()->scrollbarsCanBeActive()) 759 return; 760 if (isScrollbarOverlayAPIAvailable()) 761 [m_scrollbarPainterController.get() mouseMovedInContentArea]; 762} 763 764void ScrollAnimatorMac::mouseEnteredScrollbar(Scrollbar* scrollbar) const 765{ 766 // At this time, only legacy scrollbars needs to send notifications here. 767 if (recommendedScrollerStyle() != NSScrollerStyleLegacy) 768 return; 769 770 if (!scrollableArea()->scrollbarsCanBeActive()) 771 return; 772 773 if (isScrollbarOverlayAPIAvailable()) { 774 if (!supportsUIStateTransitionProgress()) 775 return; 776 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar)) 777 [painter mouseEnteredScroller]; 778 } 779} 780 781void ScrollAnimatorMac::mouseExitedScrollbar(Scrollbar* scrollbar) const 782{ 783 // At this time, only legacy scrollbars needs to send notifications here. 784 if (recommendedScrollerStyle() != NSScrollerStyleLegacy) 785 return; 786 787 if (!scrollableArea()->scrollbarsCanBeActive()) 788 return; 789 790 if (isScrollbarOverlayAPIAvailable()) { 791 if (!supportsUIStateTransitionProgress()) 792 return; 793 if (ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar)) 794 [painter mouseExitedScroller]; 795 } 796} 797 798void ScrollAnimatorMac::willStartLiveResize() 799{ 800 if (!scrollableArea()->scrollbarsCanBeActive()) 801 return; 802 if (isScrollbarOverlayAPIAvailable()) 803 [m_scrollbarPainterController.get() startLiveResize]; 804} 805 806void ScrollAnimatorMac::contentsResized() const 807{ 808 if (!scrollableArea()->scrollbarsCanBeActive()) 809 return; 810 if (isScrollbarOverlayAPIAvailable()) 811 [m_scrollbarPainterController.get() contentAreaDidResize]; 812} 813 814void ScrollAnimatorMac::willEndLiveResize() 815{ 816 if (!scrollableArea()->scrollbarsCanBeActive()) 817 return; 818 if (isScrollbarOverlayAPIAvailable()) 819 [m_scrollbarPainterController.get() endLiveResize]; 820} 821 822void ScrollAnimatorMac::contentAreaDidShow() const 823{ 824 if (!scrollableArea()->scrollbarsCanBeActive()) 825 return; 826 if (isScrollbarOverlayAPIAvailable()) 827 [m_scrollbarPainterController.get() windowOrderedIn]; 828} 829 830void ScrollAnimatorMac::contentAreaDidHide() const 831{ 832 if (!scrollableArea()->scrollbarsCanBeActive()) 833 return; 834 if (isScrollbarOverlayAPIAvailable()) 835 [m_scrollbarPainterController.get() windowOrderedOut]; 836} 837 838void ScrollAnimatorMac::didBeginScrollGesture() const 839{ 840 if (!scrollableArea()->scrollbarsCanBeActive()) 841 return; 842 if (isScrollbarOverlayAPIAvailable()) 843 [m_scrollbarPainterController.get() beginScrollGesture]; 844} 845 846void ScrollAnimatorMac::didEndScrollGesture() const 847{ 848 if (!scrollableArea()->scrollbarsCanBeActive()) 849 return; 850 if (isScrollbarOverlayAPIAvailable()) 851 [m_scrollbarPainterController.get() endScrollGesture]; 852} 853 854void ScrollAnimatorMac::mayBeginScrollGesture() const 855{ 856 if (!scrollableArea()->scrollbarsCanBeActive()) 857 return; 858 if (!isScrollbarOverlayAPIAvailable()) 859 return; 860 861 [m_scrollbarPainterController.get() beginScrollGesture]; 862 [m_scrollbarPainterController.get() contentAreaScrolled]; 863} 864 865void ScrollAnimatorMac::finishCurrentScrollAnimations() 866{ 867 if (isScrollbarOverlayAPIAvailable()) { 868 [m_scrollbarPainterController.get() hideOverlayScrollers]; 869 } 870} 871 872void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar) 873{ 874 if (!isScrollbarOverlayAPIAvailable()) 875 return; 876 877 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 878 if (!painter) 879 return; 880 881 ASSERT(!m_verticalScrollbarPainterDelegate); 882 m_verticalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]); 883 884 [painter setDelegate:m_verticalScrollbarPainterDelegate.get()]; 885 [m_scrollbarPainterController.get() setVerticalScrollerImp:painter]; 886 if (scrollableArea()->inLiveResize()) 887 [painter setKnobAlpha:1]; 888} 889 890void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar) 891{ 892 if (!isScrollbarOverlayAPIAvailable()) 893 return; 894 895 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 896 if (!painter) 897 return; 898 899 ASSERT(m_verticalScrollbarPainterDelegate); 900 [m_verticalScrollbarPainterDelegate.get() invalidate]; 901 m_verticalScrollbarPainterDelegate = nullptr; 902 903 [painter setDelegate:nil]; 904 [m_scrollbarPainterController.get() setVerticalScrollerImp:nil]; 905} 906 907void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar) 908{ 909 if (!isScrollbarOverlayAPIAvailable()) 910 return; 911 912 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 913 if (!painter) 914 return; 915 916 ASSERT(!m_horizontalScrollbarPainterDelegate); 917 m_horizontalScrollbarPainterDelegate.adoptNS([[WebScrollbarPainterDelegate alloc] initWithScrollbar:scrollbar]); 918 919 [painter setDelegate:m_horizontalScrollbarPainterDelegate.get()]; 920 [m_scrollbarPainterController.get() setHorizontalScrollerImp:painter]; 921 if (scrollableArea()->inLiveResize()) 922 [painter setKnobAlpha:1]; 923} 924 925void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar) 926{ 927 if (!isScrollbarOverlayAPIAvailable()) 928 return; 929 930 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 931 if (!painter) 932 return; 933 934 ASSERT(m_horizontalScrollbarPainterDelegate); 935 [m_horizontalScrollbarPainterDelegate.get() invalidate]; 936 m_horizontalScrollbarPainterDelegate = nullptr; 937 938 [painter setDelegate:nil]; 939 [m_scrollbarPainterController.get() setHorizontalScrollerImp:nil]; 940} 941 942bool ScrollAnimatorMac::shouldScrollbarParticipateInHitTesting(Scrollbar* scrollbar) 943{ 944 // Non-overlay scrollbars should always participate in hit testing. 945 if (recommendedScrollerStyle() != NSScrollerStyleOverlay) 946 return true; 947 948 if (!isScrollbarOverlayAPIAvailable()) 949 return true; 950 951 if (scrollbar->isAlphaLocked()) 952 return true; 953 954 // Overlay scrollbars should participate in hit testing whenever they are at all visible. 955 ScrollbarPainter painter = scrollbarPainterForScrollbar(scrollbar); 956 if (!painter) 957 return false; 958 return [painter knobAlpha] > 0; 959} 960 961void ScrollAnimatorMac::notifyContentAreaScrolled(const FloatSize& delta) 962{ 963 if (!isScrollbarOverlayAPIAvailable()) 964 return; 965 966 // This function is called when a page is going into the page cache, but the page 967 // isn't really scrolling in that case. We should only pass the message on to the 968 // ScrollbarPainterController when we're really scrolling on an active page. 969 if (scrollableArea()->scrollbarsCanBeActive()) 970 sendContentAreaScrolledSoon(delta); 971} 972 973void ScrollAnimatorMac::cancelAnimations() 974{ 975 m_haveScrolledSincePageLoad = false; 976 977 if (isScrollbarOverlayAPIAvailable()) { 978 if (scrollbarPaintTimerIsActive()) 979 stopScrollbarPaintTimer(); 980 [m_horizontalScrollbarPainterDelegate.get() cancelAnimations]; 981 [m_verticalScrollbarPainterDelegate.get() cancelAnimations]; 982 } 983} 984 985void ScrollAnimatorMac::handleWheelEventPhase(PlatformWheelEventPhase phase) 986{ 987 // This may not have been set to true yet if the wheel event was handled by the ScrollingTree, 988 // So set it to true here. 989 m_haveScrolledSincePageLoad = true; 990 991 if (phase == PlatformWheelEventPhaseBegan) 992 didBeginScrollGesture(); 993 else if (phase == PlatformWheelEventPhaseEnded || phase == PlatformWheelEventPhaseCancelled) 994 didEndScrollGesture(); 995 else if (phase == PlatformWheelEventPhaseMayBegin) 996 mayBeginScrollGesture(); 997} 998 999#if USE(RUBBER_BANDING) 1000bool ScrollAnimatorMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent) 1001{ 1002 m_haveScrolledSincePageLoad = true; 1003 1004 if (!wheelEvent.hasPreciseScrollingDeltas() || !rubberBandingEnabledForSystem()) 1005 return ScrollAnimator::handleWheelEvent(wheelEvent); 1006 1007 // FIXME: This is somewhat roundabout hack to allow forwarding wheel events 1008 // up to the parent scrollable area. It takes advantage of the fact that 1009 // the base class implementation of handleWheelEvent will not accept the 1010 // wheel event if there is nowhere to scroll. 1011 if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) { 1012 if (!allowsVerticalStretching()) 1013 return ScrollAnimator::handleWheelEvent(wheelEvent); 1014 } else { 1015 if (!allowsHorizontalStretching()) 1016 return ScrollAnimator::handleWheelEvent(wheelEvent); 1017 } 1018 1019 bool didHandleEvent = m_scrollElasticityController.handleWheelEvent(wheelEvent); 1020 1021 // The elasticity controller can return false on a phase end event if rubber banding wasn't in progress. 1022 // In this case, the wheel phase must still be handled so that that overlay scroll bars get hidden. 1023 if (didHandleEvent || wheelEvent.phase() == PlatformWheelEventPhaseEnded || wheelEvent.phase() == PlatformWheelEventPhaseCancelled) 1024 handleWheelEventPhase(wheelEvent.phase()); 1025 1026 return didHandleEvent; 1027} 1028 1029bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY) 1030{ 1031 FloatSize limitDelta; 1032 if (fabsf(deltaY) >= fabsf(deltaX)) { 1033 if (deltaY < 0) { 1034 // We are trying to scroll up. Make sure we are not pinned to the top 1035 limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y()); 1036 } else { 1037 // We are trying to scroll down. Make sure we are not pinned to the bottom 1038 limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y())); 1039 } 1040 } else if (deltaX != 0) { 1041 if (deltaX < 0) { 1042 // We are trying to scroll left. Make sure we are not pinned to the left 1043 limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x()); 1044 } else { 1045 // We are trying to scroll right. Make sure we are not pinned to the right 1046 limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x())); 1047 } 1048 } 1049 1050 if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1)) 1051 return true; 1052 return false; 1053} 1054 1055bool ScrollAnimatorMac::allowsVerticalStretching() 1056{ 1057 switch (m_scrollableArea->verticalScrollElasticity()) { 1058 case ScrollElasticityAutomatic: { 1059 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar(); 1060 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar(); 1061 return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled()))); 1062 } 1063 case ScrollElasticityNone: 1064 return false; 1065 case ScrollElasticityAllowed: 1066 return true; 1067 } 1068 1069 ASSERT_NOT_REACHED(); 1070 return false; 1071} 1072 1073bool ScrollAnimatorMac::allowsHorizontalStretching() 1074{ 1075 switch (m_scrollableArea->horizontalScrollElasticity()) { 1076 case ScrollElasticityAutomatic: { 1077 Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar(); 1078 Scrollbar* vScroller = m_scrollableArea->verticalScrollbar(); 1079 return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled()))); 1080 } 1081 case ScrollElasticityNone: 1082 return false; 1083 case ScrollElasticityAllowed: 1084 return true; 1085 } 1086 1087 ASSERT_NOT_REACHED(); 1088 return false; 1089} 1090 1091IntSize ScrollAnimatorMac::stretchAmount() 1092{ 1093 return m_scrollableArea->overhangAmount(); 1094} 1095 1096bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction) 1097{ 1098 return pinnedInDirection(direction.width(), direction.height()); 1099} 1100 1101bool ScrollAnimatorMac::canScrollHorizontally() 1102{ 1103 Scrollbar* scrollbar = m_scrollableArea->horizontalScrollbar(); 1104 if (!scrollbar) 1105 return false; 1106 return scrollbar->enabled(); 1107} 1108 1109bool ScrollAnimatorMac::canScrollVertically() 1110{ 1111 Scrollbar* scrollbar = m_scrollableArea->verticalScrollbar(); 1112 if (!scrollbar) 1113 return false; 1114 return scrollbar->enabled(); 1115} 1116 1117bool ScrollAnimatorMac::shouldRubberBandInDirection(ScrollDirection direction) 1118{ 1119 return m_scrollableArea->shouldRubberBandInDirection(direction); 1120} 1121 1122IntPoint ScrollAnimatorMac::absoluteScrollPosition() 1123{ 1124 return m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin(); 1125} 1126 1127void ScrollAnimatorMac::immediateScrollByWithoutContentEdgeConstraints(const FloatSize& delta) 1128{ 1129 m_scrollableArea->setConstrainsScrollingToContentEdge(false); 1130 immediateScrollBy(delta); 1131 m_scrollableArea->setConstrainsScrollingToContentEdge(true); 1132} 1133 1134void ScrollAnimatorMac::immediateScrollBy(const FloatSize& delta) 1135{ 1136 FloatPoint newPos = adjustScrollPositionIfNecessary(FloatPoint(m_currentPosX, m_currentPosY) + delta); 1137 if (newPos.x() == m_currentPosX && newPos.y() == m_currentPosY) 1138 return; 1139 1140 FloatSize adjustedDelta = FloatSize(newPos.x() - m_currentPosX, newPos.y() - m_currentPosY); 1141 1142 m_currentPosX = newPos.x(); 1143 m_currentPosY = newPos.y(); 1144 notifyPositionChanged(adjustedDelta); 1145} 1146 1147void ScrollAnimatorMac::startSnapRubberbandTimer() 1148{ 1149 m_snapRubberBandTimer.startRepeating(1.0 / 60.0); 1150} 1151 1152void ScrollAnimatorMac::stopSnapRubberbandTimer() 1153{ 1154 m_snapRubberBandTimer.stop(); 1155} 1156 1157void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*) 1158{ 1159 m_scrollElasticityController.snapRubberBandTimerFired(); 1160} 1161#endif 1162 1163void ScrollAnimatorMac::setIsActive() 1164{ 1165 if (!isScrollbarOverlayAPIAvailable()) 1166 return; 1167 1168 if (!m_needsScrollerStyleUpdate) 1169 return; 1170 1171 updateScrollerStyle(); 1172} 1173 1174void ScrollAnimatorMac::updateScrollerStyle() 1175{ 1176 if (!isScrollbarOverlayAPIAvailable()) 1177 return; 1178 1179 if (!scrollableArea()->scrollbarsCanBeActive()) { 1180 m_needsScrollerStyleUpdate = true; 1181 return; 1182 } 1183 1184 ScrollbarThemeMacOverlayAPI* macTheme = macOverlayScrollbarTheme(); 1185 if (!macTheme) { 1186 m_needsScrollerStyleUpdate = false; 1187 return; 1188 } 1189 1190 NSScrollerStyle newStyle = [m_scrollbarPainterController.get() scrollerStyle]; 1191 1192 if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) { 1193 verticalScrollbar->invalidate(); 1194 1195 ScrollbarPainter oldVerticalPainter = [m_scrollbarPainterController.get() verticalScrollerImp]; 1196 ScrollbarPainter newVerticalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 1197 controlSize:(NSControlSize)verticalScrollbar->controlSize() 1198 horizontal:NO 1199 replacingScrollerImp:oldVerticalPainter]; 1200 [m_scrollbarPainterController.get() setVerticalScrollerImp:newVerticalPainter]; 1201 macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter); 1202 1203 // The different scrollbar styles have different thicknesses, so we must re-set the 1204 // frameRect to the new thickness, and the re-layout below will ensure the position 1205 // and length are properly updated. 1206 int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize()); 1207 verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness)); 1208 } 1209 1210 if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) { 1211 horizontalScrollbar->invalidate(); 1212 1213 ScrollbarPainter oldHorizontalPainter = [m_scrollbarPainterController.get() horizontalScrollerImp]; 1214 ScrollbarPainter newHorizontalPainter = [NSClassFromString(@"NSScrollerImp") scrollerImpWithStyle:newStyle 1215 controlSize:(NSControlSize)horizontalScrollbar->controlSize() 1216 horizontal:YES 1217 replacingScrollerImp:oldHorizontalPainter]; 1218 [m_scrollbarPainterController.get() setHorizontalScrollerImp:newHorizontalPainter]; 1219 macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter); 1220 1221 // The different scrollbar styles have different thicknesses, so we must re-set the 1222 // frameRect to the new thickness, and the re-layout below will ensure the position 1223 // and length are properly updated. 1224 int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize()); 1225 horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness)); 1226 } 1227 1228 // If m_needsScrollerStyleUpdate is true, then the page is restoring from the page cache, and 1229 // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves. 1230 scrollableArea()->scrollbarStyleChanged(newStyle, !m_needsScrollerStyleUpdate); 1231 1232 m_needsScrollerStyleUpdate = false; 1233} 1234 1235void ScrollAnimatorMac::startScrollbarPaintTimer() 1236{ 1237 m_initialScrollbarPaintTimer.startOneShot(0.1); 1238} 1239 1240bool ScrollAnimatorMac::scrollbarPaintTimerIsActive() const 1241{ 1242 return m_initialScrollbarPaintTimer.isActive(); 1243} 1244 1245void ScrollAnimatorMac::stopScrollbarPaintTimer() 1246{ 1247 m_initialScrollbarPaintTimer.stop(); 1248} 1249 1250void ScrollAnimatorMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorMac>*) 1251{ 1252 if (isScrollbarOverlayAPIAvailable()) { 1253 // To force the scrollbars to flash, we have to call hide first. Otherwise, the ScrollbarPainterController 1254 // might think that the scrollbars are already showing and bail early. 1255 [m_scrollbarPainterController.get() hideOverlayScrollers]; 1256 [m_scrollbarPainterController.get() flashScrollers]; 1257 } 1258} 1259 1260void ScrollAnimatorMac::sendContentAreaScrolledSoon(const FloatSize& delta) 1261{ 1262 m_contentAreaScrolledTimerScrollDelta = delta; 1263 1264 if (!m_sendContentAreaScrolledTimer.isActive()) 1265 m_sendContentAreaScrolledTimer.startOneShot(0); 1266} 1267 1268void ScrollAnimatorMac::sendContentAreaScrolledTimerFired(Timer<ScrollAnimatorMac>*) 1269{ 1270 if (supportsContentAreaScrolledInDirection()) { 1271 [m_scrollbarPainterController.get() contentAreaScrolledInDirection:NSMakePoint(m_contentAreaScrolledTimerScrollDelta.width(), m_contentAreaScrolledTimerScrollDelta.height())]; 1272 m_contentAreaScrolledTimerScrollDelta = FloatSize(); 1273 } else 1274 [m_scrollbarPainterController.get() contentAreaScrolled]; 1275} 1276 1277void ScrollAnimatorMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb) 1278{ 1279 IntRect rectInViewCoordinates = scrollerThumb; 1280 if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar()) 1281 rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb); 1282 1283 if (rectInViewCoordinates == m_visibleScrollerThumbRect) 1284 return; 1285 1286 m_visibleScrollerThumbRect = rectInViewCoordinates; 1287} 1288 1289bool ScrollAnimatorMac::canUseCoordinatedScrollbar() { 1290 return isScrollbarOverlayAPIAvailable(); 1291} 1292 1293} // namespace WebCore 1294