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