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