tizen beta release
[profile/ivi/webkit-efl.git] / Source / WebCore / platform / chromium / ScrollAnimatorChromiumMac.mm
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 <sys/time.h>
29 #include <sys/sysctl.h>
30
31 #include "ScrollAnimatorChromiumMac.h"
32
33 #include "FloatPoint.h"
34 #include "PlatformGestureEvent.h"
35 #include "PlatformWheelEvent.h"
36 #include "ScrollView.h"
37 #include "ScrollableArea.h"
38 #include "ScrollbarTheme.h"
39 #include "ScrollbarThemeChromiumMac.h"
40 #include "WebCoreSystemInterface.h"
41 #include <wtf/PassOwnPtr.h>
42 #include <wtf/UnusedParam.h>
43 #include <QuartzCore/QuartzCore.h>
44
45 using namespace WebCore;
46 using namespace std;
47
48 #if !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
49 @protocol NSAnimationDelegate
50 @end
51
52 @interface NSProcessInfo (ScrollAnimatorChromiumMacExt)
53 - (NSTimeInterval)systemUptime;
54 @end
55
56 @implementation NSProcessInfo (ScrollAnimatorChromiumMacExt)
57 - (NSTimeInterval)systemUptime
58 {
59     // Get how long system has been up. Found by looking getting "boottime" from the kernel.
60     static struct timeval boottime = {0};
61     if (!boottime.tv_sec) {
62         int mib[2] = {CTL_KERN, KERN_BOOTTIME};
63         size_t size = sizeof(boottime);
64         if (-1 == sysctl(mib, 2, &boottime, &size, 0, 0))
65             boottime.tv_sec = 0;
66     }
67     struct timeval now;
68     if (boottime.tv_sec && -1 != gettimeofday(&now, 0)) {
69         struct timeval uptime;
70         timersub(&now, &boottime, &uptime);
71         NSTimeInterval result = uptime.tv_sec + (uptime.tv_usec / 1E+6);
72         return result;
73     }
74     return 0;
75 }
76 @end
77 #endif
78
79 @interface NSObject (ScrollAnimationHelperDetails)
80 - (id)initWithDelegate:(id)delegate;
81 - (void)_stopRun;
82 - (BOOL)_isAnimating;
83 - (NSPoint)targetOrigin;
84 - (CGFloat)_progress;
85 @end
86
87 @interface ScrollAnimationHelperDelegate : NSObject
88 {
89     WebCore::ScrollAnimatorChromiumMac* _animator;
90 }
91 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator;
92 @end
93
94 static NSSize abs(NSSize size)
95 {
96     NSSize finalSize = size;
97     if (finalSize.width < 0)
98         finalSize.width = -finalSize.width;
99     if (finalSize.height < 0)
100         finalSize.height = -finalSize.height;
101     return finalSize;    
102 }
103
104 @implementation ScrollAnimationHelperDelegate
105
106 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator
107 {
108     self = [super init];
109     if (!self)
110         return nil;
111
112     _animator = scrollAnimator;
113     return self;
114 }
115
116 - (void)scrollAnimatorDestroyed
117 {
118     _animator = 0;
119 }
120
121 - (NSRect)bounds
122 {
123     if (!_animator)
124         return NSZeroRect;
125
126     WebCore::FloatPoint currentPosition = _animator->currentPosition();
127     return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0);
128 }
129
130 - (void)_immediateScrollToPoint:(NSPoint)newPosition
131 {
132     if (!_animator)
133         return;
134     _animator->immediateScrollToPointForScrollAnimation(newPosition);
135 }
136
137 - (NSPoint)_pixelAlignProposedScrollPosition:(NSPoint)newOrigin
138 {
139     return newOrigin;
140 }
141
142 - (NSSize)convertSizeToBase:(NSSize)size
143 {
144     return abs(size);
145 }
146
147 - (NSSize)convertSizeFromBase:(NSSize)size
148 {
149     return abs(size);
150 }
151
152 - (NSSize)convertSizeToBacking:(NSSize)size
153 {
154     return abs(size);
155 }
156
157 - (NSSize)convertSizeFromBacking:(NSSize)size
158 {
159     return abs(size);
160 }
161
162 - (id)superview
163 {
164     return nil;
165 }
166
167 - (id)documentView
168 {
169     return nil;
170 }
171
172 - (id)window
173 {
174     return nil;
175 }
176
177 - (void)_recursiveRecomputeToolTips
178 {
179 }
180
181 @end
182
183 #if USE(WK_SCROLLBAR_PAINTER)
184
185 @interface ScrollbarPainterControllerDelegate : NSObject
186 {
187     WebCore::ScrollAnimatorChromiumMac* _animator;
188 }
189 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator;
190 @end
191
192 @implementation ScrollbarPainterControllerDelegate
193
194 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator
195 {
196     self = [super init];
197     if (!self)
198         return nil;
199     
200     _animator = scrollAnimator;
201     return self;
202 }
203
204 - (void)scrollAnimatorDestroyed
205 {
206     _animator = 0;
207 }
208
209 - (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair
210 {
211     UNUSED_PARAM(scrollerImpPair);
212     if (!_animator)
213         return NSZeroRect;
214
215     WebCore::IntSize contentsSize = _animator->scrollableArea()->contentsSize();
216     return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height());
217 }
218
219 - (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair
220 {
221     UNUSED_PARAM(scrollerImpPair);
222     if (!_animator)
223         return NO;
224
225     return _animator->scrollableArea()->inLiveResize();
226 }
227
228 - (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair
229 {
230     UNUSED_PARAM(scrollerImpPair);
231     if (!_animator)
232         return NSZeroPoint;
233
234     return _animator->scrollableArea()->currentMousePosition();
235 }
236
237 - (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp
238 {
239     UNUSED_PARAM(scrollerImpPair);
240     if (!_animator)
241         return NSZeroPoint;
242
243     WebCore::Scrollbar* scrollbar = 0;
244     if (wkScrollbarPainterIsHorizontal((WKScrollbarPainterRef)scrollerImp))
245         scrollbar = _animator->scrollableArea()->horizontalScrollbar();
246     else 
247         scrollbar = _animator->scrollableArea()->verticalScrollbar();
248
249     // It is possible to have a null scrollbar here since it is possible for this delegate
250     // method to be called between the moment when a scrollbar has been set to 0 and the
251     // moment when its destructor has been called. We should probably de-couple some
252     // of the clean-up work in ScrollbarThemeChromiumMac::unregisterScrollbar() to avoid this
253     // issue.
254     if (!scrollbar)
255         return WebCore::IntPoint();
256     
257     return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea));
258 }
259
260 - (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect
261 {
262     UNUSED_PARAM(scrollerImpPair);
263     UNUSED_PARAM(rect);
264 }
265
266 - (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle
267 {
268     if (!_animator)
269         return;
270
271     wkSetScrollbarPainterControllerStyle((WKScrollbarPainterControllerRef)scrollerImpPair, newRecommendedScrollerStyle);
272     _animator->updateScrollerStyle();
273 }
274
275 @end
276
277 @interface ScrollbarPartAnimation : NSAnimation
278 {
279     RetainPtr<WKScrollbarPainterRef> _scrollerPainter;
280     WebCore::ScrollbarPart _part;
281     WebCore::ScrollAnimatorChromiumMac* _animator;
282     CGFloat _initialAlpha;
283     CGFloat _newAlpha;
284 }
285 - (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration;
286 @end
287
288 @implementation ScrollbarPartAnimation
289
290 - (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
291 {
292     self = [super initWithDuration:duration animationCurve:NSAnimationEaseInOut];
293     if (!self)
294         return nil;
295     
296     _scrollerPainter = scrollerPainter;
297     _part = part;
298     _animator = scrollAnimator;
299     _initialAlpha = _part == WebCore::ThumbPart ? wkScrollbarPainterKnobAlpha(_scrollerPainter.get()) : wkScrollbarPainterTrackAlpha(_scrollerPainter.get());
300     _newAlpha = newAlpha;
301     
302     return self;    
303 }
304
305 - (void)setCurrentProgress:(NSAnimationProgress)progress
306 {
307     [super setCurrentProgress:progress];
308
309     if (!_animator)
310         return;
311
312     CGFloat currentAlpha;
313     if (_initialAlpha > _newAlpha)
314         currentAlpha = 1 - progress;
315     else
316         currentAlpha = progress;
317     
318     if (_part == WebCore::ThumbPart)
319         wkSetScrollbarPainterKnobAlpha(_scrollerPainter.get(), currentAlpha);
320     else
321         wkSetScrollbarPainterTrackAlpha(_scrollerPainter.get(), currentAlpha);
322
323     // Invalidate the scrollbars so that they paint the animation
324     if (WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar())
325         verticalScrollbar->invalidateRect(WebCore::IntRect(0, 0, verticalScrollbar->width(), verticalScrollbar->height()));
326     if (WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar())
327         horizontalScrollbar->invalidateRect(WebCore::IntRect(0, 0, horizontalScrollbar->width(), horizontalScrollbar->height()));
328 }
329
330 - (void)scrollAnimatorDestroyed
331 {
332     [self stopAnimation];
333     _animator = 0;
334 }
335
336 @end
337
338 @interface ScrollbarPainterDelegate : NSObject<NSAnimationDelegate>
339 {
340     WebCore::ScrollAnimatorChromiumMac* _animator;
341
342     RetainPtr<ScrollbarPartAnimation> _verticalKnobAnimation;
343     RetainPtr<ScrollbarPartAnimation> _horizontalKnobAnimation;
344
345     RetainPtr<ScrollbarPartAnimation> _verticalTrackAnimation;
346     RetainPtr<ScrollbarPartAnimation> _horizontalTrackAnimation;
347 }
348 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator;
349 - (void)cancelAnimations;
350 @end
351
352 @implementation ScrollbarPainterDelegate
353
354 - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorChromiumMac*)scrollAnimator
355 {
356     self = [super init];
357     if (!self)
358         return nil;
359     
360     _animator = scrollAnimator;
361     return self;
362 }
363
364 - (void)cancelAnimations
365 {
366     [_verticalKnobAnimation.get() stopAnimation];
367     [_horizontalKnobAnimation.get() stopAnimation];
368     [_verticalTrackAnimation.get() stopAnimation];
369     [_horizontalTrackAnimation.get() stopAnimation];
370 }
371
372 - (NSRect)convertRectToBacking:(NSRect)aRect
373 {
374     return aRect;
375 }
376
377 - (NSRect)convertRectFromBacking:(NSRect)aRect
378 {
379     return aRect;
380 }
381
382 #if !PLATFORM(CHROMIUM)
383 - (CALayer *)layer
384 {
385     if (!_animator)
386         return nil;
387     if (!_animator->isDrawingIntoLayer())
388         return nil;
389
390     // FIXME: This should attempt to return an actual layer.
391     static CALayer *dummyLayer = [[CALayer alloc] init];
392     return dummyLayer;
393 }
394 #endif
395
396 - (void)setUpAnimation:(RetainPtr<ScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration
397 {
398     // If the user has scrolled the page, then the scrollbars must be animated here. 
399     // This overrides the early returns.
400     bool mustAnimate = _animator->haveScrolledSincePageLoad();
401
402     if (_animator->scrollbarPaintTimerIsActive() && !mustAnimate)
403         return;
404
405     if (_animator->scrollableArea()->shouldSuspendScrollAnimations() && !mustAnimate) {
406         _animator->startScrollbarPaintTimer();
407         return;
408     }
409
410     // At this point, we are definitely going to animate now, so stop the timer.
411     _animator->stopScrollbarPaintTimer();
412
413     // If we are currently animating, stop
414     if (scrollbarPartAnimation) {
415         [scrollbarPartAnimation.get() stopAnimation];
416         scrollbarPartAnimation = nil;
417     }
418
419     if (part == WebCore::ThumbPart && !wkScrollbarPainterIsHorizontal(scrollerPainter)) {
420         if (newAlpha == 1) {
421             IntRect thumbRect = IntRect(wkScrollbarPainterKnobRect(scrollerPainter));
422             _animator->setVisibleScrollerThumbRect(thumbRect);
423         } else
424             _animator->setVisibleScrollerThumbRect(IntRect());
425     }
426
427     [NSAnimationContext beginGrouping];
428     [[NSAnimationContext currentContext] setDuration:duration];
429     scrollbarPartAnimation.adoptNS([[ScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter 
430                                                                     part:part
431                                                                     scrollAnimator:_animator 
432                                                                     animateAlphaTo:newAlpha 
433                                                                     duration:duration]);
434     [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking];
435     [scrollbarPartAnimation.get() startAnimation];
436     [NSAnimationContext endGrouping];
437 }
438
439 - (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration
440 {
441     if (!_animator)
442         return;
443
444     WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
445     if (wkScrollbarPainterIsHorizontal(scrollerPainter))
446         [self setUpAnimation:_horizontalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
447     else
448         [self setUpAnimation:_verticalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration];
449 }
450
451 - (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration
452 {
453     if (!_animator)
454         return;
455
456     WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp;
457     if (wkScrollbarPainterIsHorizontal(scrollerPainter))
458         [self setUpAnimation:_horizontalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
459     else
460         [self setUpAnimation:_verticalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration];
461 }
462
463 - (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState
464 {
465     UNUSED_PARAM(scrollerImp);
466     UNUSED_PARAM(newOverlayScrollerState);
467 }
468
469 - (void)scrollAnimatorDestroyed
470 {
471     _animator = 0;
472     [_verticalKnobAnimation.get() scrollAnimatorDestroyed];
473     [_horizontalKnobAnimation.get() scrollAnimatorDestroyed];
474     [_verticalTrackAnimation.get() scrollAnimatorDestroyed];
475     [_horizontalTrackAnimation.get() scrollAnimatorDestroyed];
476 }
477
478 @end
479
480 #endif // USE(WK_SCROLLBAR_PAINTER)
481
482 namespace WebCore {
483
484 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
485 {
486     return adoptPtr(new ScrollAnimatorChromiumMac(scrollableArea));
487 }
488
489 static ScrollbarThemeChromiumMac* chromiumScrollbarTheme()
490 {
491     ScrollbarTheme* scrollbarTheme = ScrollbarTheme::theme();
492     return !scrollbarTheme->isMockTheme() ? static_cast<ScrollbarThemeChromiumMac*>(scrollbarTheme) : 0;
493 }
494
495 ScrollAnimatorChromiumMac::ScrollAnimatorChromiumMac(ScrollableArea* scrollableArea)
496     : ScrollAnimator(scrollableArea)
497 #if USE(WK_SCROLLBAR_PAINTER)
498     , m_initialScrollbarPaintTimer(this, &ScrollAnimatorChromiumMac::initialScrollbarPaintTimerFired)
499 #endif
500 #if ENABLE(RUBBER_BANDING)
501     , m_inScrollGesture(false)
502     , m_momentumScrollInProgress(false)
503     , m_ignoreMomentumScrolls(false)
504     , m_lastMomentumScrollTimestamp(0)
505     , m_startTime(0)
506     , m_snapRubberBandTimer(this, &ScrollAnimatorChromiumMac::snapRubberBandTimerFired)
507 #endif
508     , m_drawingIntoLayer(false)
509     , m_haveScrolledSincePageLoad(false)
510     , m_needsScrollerStyleUpdate(false)
511 {
512     m_scrollAnimationHelperDelegate.adoptNS([[ScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]);
513     m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]);
514
515     if (isScrollbarOverlayAPIAvailable()) {
516         m_scrollbarPainterControllerDelegate.adoptNS([[ScrollbarPainterControllerDelegate alloc] initWithScrollAnimator:this]);
517         m_scrollbarPainterController = wkMakeScrollbarPainterController(m_scrollbarPainterControllerDelegate.get());
518         m_scrollbarPainterDelegate.adoptNS([[ScrollbarPainterDelegate alloc] initWithScrollAnimator:this]);
519     }
520 }
521
522 ScrollAnimatorChromiumMac::~ScrollAnimatorChromiumMac()
523 {
524     if (isScrollbarOverlayAPIAvailable()) {
525         [m_scrollbarPainterControllerDelegate.get() scrollAnimatorDestroyed];
526         [(id)m_scrollbarPainterController.get() setDelegate:nil];
527         [m_scrollbarPainterDelegate.get() scrollAnimatorDestroyed];
528         [m_scrollAnimationHelperDelegate.get() scrollAnimatorDestroyed];
529     }
530 }
531
532 bool ScrollAnimatorChromiumMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
533 {
534     m_haveScrolledSincePageLoad = true;
535
536 #if !ENABLE(SMOOTH_SCROLLING)
537     return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
538 #endif
539
540     if (![[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollAnimationEnabled"] || !m_scrollableArea->scrollAnimatorEnabled())
541         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
542
543     if (granularity == ScrollByPixel)
544         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
545
546     float currentPos = orientation == HorizontalScrollbar ? m_currentPosX : m_currentPosY;
547     float newPos = std::max<float>(std::min<float>(currentPos + (step * multiplier), static_cast<float>(m_scrollableArea->scrollSize(orientation))), 0);
548     if (currentPos == newPos)
549         return false;
550
551     NSPoint newPoint;
552     if ([m_scrollAnimationHelper.get() _isAnimating]) {
553         NSPoint targetOrigin = [m_scrollAnimationHelper.get() targetOrigin];
554         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, targetOrigin.y) : NSMakePoint(targetOrigin.x, newPos);
555     } else {
556         newPoint = orientation == HorizontalScrollbar ? NSMakePoint(newPos, m_currentPosY) : NSMakePoint(m_currentPosX, newPos);
557         m_scrollableArea->didStartAnimatedScroll();
558     }
559     [m_scrollAnimationHelper.get() scrollToPoint:newPoint];
560     return true;
561 }
562
563 void ScrollAnimatorChromiumMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
564 {
565     [m_scrollAnimationHelper.get() _stopRun];
566     immediateScrollToPoint(offset);
567 }
568
569 float ScrollAnimatorChromiumMac::adjustScrollXPositionIfNecessary(float position) const
570 {
571     if (!m_scrollableArea->constrainsScrollingToContentEdge())
572         return position;
573
574     return max<float>(min<float>(position, m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
575 }
576
577 float ScrollAnimatorChromiumMac::adjustScrollYPositionIfNecessary(float position) const
578 {
579     if (!m_scrollableArea->constrainsScrollingToContentEdge())
580         return position;
581
582     return max<float>(min<float>(position, m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
583 }
584
585 FloatPoint ScrollAnimatorChromiumMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const
586 {
587     if (!m_scrollableArea->constrainsScrollingToContentEdge())
588         return position;
589
590     float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0);
591     float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0);
592
593     return FloatPoint(newX, newY);
594 }
595
596 void ScrollAnimatorChromiumMac::immediateScrollToPoint(const FloatPoint& newPosition)
597 {
598     FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition);
599  
600     m_currentPosX = adjustedPosition.x();
601     m_currentPosY = adjustedPosition.y();
602     notifyPositionChanged();
603 }
604
605 void ScrollAnimatorChromiumMac::immediateScrollByDeltaX(float deltaX)
606 {
607     float newPosX = adjustScrollXPositionIfNecessary(m_currentPosX + deltaX);
608     
609     if (newPosX == m_currentPosX)
610         return;
611     
612     m_currentPosX = newPosX;
613     notifyPositionChanged();
614 }
615
616 void ScrollAnimatorChromiumMac::immediateScrollByDeltaY(float deltaY)
617 {
618     float newPosY = adjustScrollYPositionIfNecessary(m_currentPosY + deltaY);
619     
620     if (newPosY == m_currentPosY)
621         return;
622     
623     m_currentPosY = newPosY;
624     notifyPositionChanged();
625 }
626
627 void ScrollAnimatorChromiumMac::immediateScrollToPointForScrollAnimation(const FloatPoint& newPosition)
628 {
629     ASSERT(m_scrollAnimationHelper);
630     CGFloat progress = [m_scrollAnimationHelper.get() _progress];
631     
632     immediateScrollToPoint(newPosition);
633
634     if (progress >= 1.0)
635         m_scrollableArea->didCompleteAnimatedScroll();
636 }
637
638 void ScrollAnimatorChromiumMac::notifyPositionChanged()
639 {
640     if (isScrollbarOverlayAPIAvailable())
641         wkContentAreaScrolled(m_scrollbarPainterController.get());
642     ScrollAnimator::notifyPositionChanged();
643 }
644
645 void ScrollAnimatorChromiumMac::contentAreaWillPaint() const
646 {
647     if (isScrollbarOverlayAPIAvailable())
648         wkContentAreaWillPaint(m_scrollbarPainterController.get());
649 }
650
651 void ScrollAnimatorChromiumMac::mouseEnteredContentArea() const
652 {
653     if (isScrollbarOverlayAPIAvailable())
654         wkMouseEnteredContentArea(m_scrollbarPainterController.get());
655 }
656
657 void ScrollAnimatorChromiumMac::mouseExitedContentArea() const
658 {
659     if (isScrollbarOverlayAPIAvailable())
660         wkMouseExitedContentArea(m_scrollbarPainterController.get());
661 }
662
663 void ScrollAnimatorChromiumMac::mouseMovedInContentArea() const
664 {
665     if (isScrollbarOverlayAPIAvailable())
666         wkMouseMovedInContentArea(m_scrollbarPainterController.get());
667 }
668
669 void ScrollAnimatorChromiumMac::willStartLiveResize()
670 {
671     if (isScrollbarOverlayAPIAvailable())
672         wkWillStartLiveResize(m_scrollbarPainterController.get());
673 }
674
675 void ScrollAnimatorChromiumMac::contentsResized() const
676 {
677     if (isScrollbarOverlayAPIAvailable())
678         wkContentAreaResized(m_scrollbarPainterController.get());
679 }
680
681 void ScrollAnimatorChromiumMac::willEndLiveResize()
682 {
683     if (isScrollbarOverlayAPIAvailable())
684         wkWillEndLiveResize(m_scrollbarPainterController.get());
685 }
686
687 void ScrollAnimatorChromiumMac::contentAreaDidShow() const
688 {
689     if (isScrollbarOverlayAPIAvailable())
690         wkContentAreaDidShow(m_scrollbarPainterController.get());
691 }
692
693 void ScrollAnimatorChromiumMac::contentAreaDidHide() const
694 {
695     if (isScrollbarOverlayAPIAvailable())
696         wkContentAreaDidHide(m_scrollbarPainterController.get());
697 }
698
699 void ScrollAnimatorChromiumMac::didBeginScrollGesture() const
700 {
701     if (isScrollbarOverlayAPIAvailable())
702         wkDidBeginScrollGesture(m_scrollbarPainterController.get());
703 }
704
705 void ScrollAnimatorChromiumMac::didEndScrollGesture() const
706 {
707     if (isScrollbarOverlayAPIAvailable())
708         wkDidEndScrollGesture(m_scrollbarPainterController.get());
709 }
710
711 void ScrollAnimatorChromiumMac::didAddVerticalScrollbar(Scrollbar* scrollbar)
712 {
713     if (!isScrollbarOverlayAPIAvailable())
714         return;
715         
716     if (ScrollbarThemeChromiumMac* theme = chromiumScrollbarTheme()) {
717         WKScrollbarPainterRef painter = theme->painterForScrollbar(scrollbar);
718         wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
719         wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, false);
720         if (scrollableArea()->inLiveResize())
721             wkSetScrollbarPainterKnobAlpha(painter, 1);
722     }
723 }
724
725 void ScrollAnimatorChromiumMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar)
726 {
727     if (!isScrollbarOverlayAPIAvailable())
728         return;
729
730     if (ScrollbarThemeChromiumMac* theme = chromiumScrollbarTheme()) {
731         WKScrollbarPainterRef painter = theme->painterForScrollbar(scrollbar);
732         wkScrollbarPainterSetDelegate(painter, nil);
733         wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, false);
734     }
735 }
736
737 void ScrollAnimatorChromiumMac::didAddHorizontalScrollbar(Scrollbar* scrollbar)
738 {
739     if (!isScrollbarOverlayAPIAvailable())
740         return;
741
742     if (ScrollbarThemeChromiumMac* theme = chromiumScrollbarTheme()) {
743         WKScrollbarPainterRef painter = theme->painterForScrollbar(scrollbar);
744         wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get());
745         wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, true);
746         if (scrollableArea()->inLiveResize())
747             wkSetScrollbarPainterKnobAlpha(painter, 1);
748     }
749 }
750
751 void ScrollAnimatorChromiumMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar)
752 {
753     if (!isScrollbarOverlayAPIAvailable())
754         return;
755
756     if (ScrollbarThemeChromiumMac* theme = chromiumScrollbarTheme()) {
757         WKScrollbarPainterRef painter = theme->painterForScrollbar(scrollbar);
758         wkScrollbarPainterSetDelegate(painter, nil);
759         wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, true);
760     }
761 }
762
763 void ScrollAnimatorChromiumMac::cancelAnimations()
764 {
765     m_haveScrolledSincePageLoad = false;
766
767     if (isScrollbarOverlayAPIAvailable()) {
768         if (scrollbarPaintTimerIsActive())
769             stopScrollbarPaintTimer();
770         [m_scrollbarPainterDelegate.get() cancelAnimations];
771     }
772 }
773
774 #if ENABLE(RUBBER_BANDING)
775
776 static const float scrollVelocityZeroingTimeout = 0.10f;
777 static const float rubberbandStiffness = 20;
778 static const float rubberbandDirectionLockStretchRatio = 1;
779 static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10;
780 static const float rubberbandAmplitude = 0.31f;
781 static const float rubberbandPeriod = 1.6f;
782
783 static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime)
784 {
785     float amplitude = rubberbandAmplitude;
786     float period = rubberbandPeriod;
787     float criticalDampeningFactor = expf((-elapsedTime * rubberbandStiffness) / period);
788              
789     return (initialPosition + (-initialVelocity * elapsedTime * amplitude)) * criticalDampeningFactor;
790 }
791
792 static float elasticDeltaForReboundDelta(float delta)
793 {
794     float stiffness = std::max(rubberbandStiffness, 1.0f);
795     return delta / stiffness;
796 }
797
798 static float reboundDeltaForElasticDelta(float delta)
799 {
800     return delta * rubberbandStiffness;
801 }
802
803 static float scrollWheelMultiplier()
804 {
805     static float multiplier = -1;
806     if (multiplier < 0) {
807         multiplier = [[NSUserDefaults standardUserDefaults] floatForKey:@"NSScrollWheelMultiplier"];
808         if (multiplier <= 0)
809             multiplier = 1;
810     }
811     return multiplier;
812 }
813
814 static inline bool isScrollingLeftAndShouldNotRubberBand(const PlatformWheelEvent& wheelEvent, ScrollableArea* scrollableArea)
815 {
816     return wheelEvent.deltaX() > 0 && !scrollableArea->shouldRubberBandInDirection(ScrollLeft);
817 }
818
819 static inline bool isScrollingRightAndShouldNotRubberBand(const PlatformWheelEvent& wheelEvent, ScrollableArea* scrollableArea)
820 {
821     return wheelEvent.deltaX() < 0 && !scrollableArea->shouldRubberBandInDirection(ScrollRight);
822 }
823
824 bool ScrollAnimatorChromiumMac::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
825 {
826     m_haveScrolledSincePageLoad = true;
827
828     if (!wheelEvent.hasPreciseScrollingDeltas())
829         return ScrollAnimator::handleWheelEvent(wheelEvent);
830
831     // FIXME: This is somewhat roundabout hack to allow forwarding wheel events
832     // up to the parent scrollable area. It takes advantage of the fact that
833     // the base class implemenatation of handleWheelEvent will not accept the
834     // wheel event if there is nowhere to scroll.
835     if (fabsf(wheelEvent.deltaY()) >= fabsf(wheelEvent.deltaX())) {
836         if (!allowsVerticalStretching())
837             return ScrollAnimator::handleWheelEvent(wheelEvent);
838     } else {
839         if (!allowsHorizontalStretching())
840             return ScrollAnimator::handleWheelEvent(wheelEvent);
841         
842         if (m_scrollableArea->horizontalScrollbar()) {
843             // If there is a scrollbar, we aggregate the wheel events to get an
844             // overall trend of the scroll. If the direction of the scroll is ever
845             // in the opposite direction of the pin location, then we switch the
846             // boolean, and rubber band. That is, if we were pinned to the left,
847             // and we ended up scrolling to the right, we rubber band.
848             m_cumulativeHorizontalScroll += wheelEvent.deltaX();
849             if (m_scrollerInitiallyPinnedOnLeft && m_cumulativeHorizontalScroll < 0)
850                 m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = true;
851             if (m_scrollerInitiallyPinnedOnRight && m_cumulativeHorizontalScroll > 0)
852                 m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = true;
853         }
854
855         // After a gesture begins, we go through:
856         // 1+ PlatformWheelEventPhaseNone
857         // 0+ PlatformWheelEventPhaseChanged
858         // 1 PlatformWheelEventPhaseEnded if there was at least one changed event
859         if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseNone && !m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin) {
860             if ((isScrollingLeftAndShouldNotRubberBand(wheelEvent, m_scrollableArea) &&
861                 m_scrollerInitiallyPinnedOnLeft &&
862                 m_scrollableArea->isHorizontalScrollerPinnedToMinimumPosition()) ||
863                 (isScrollingRightAndShouldNotRubberBand(wheelEvent, m_scrollableArea) &&
864                 m_scrollerInitiallyPinnedOnRight &&
865                 m_scrollableArea->isHorizontalScrollerPinnedToMaximumPosition())) {
866                 return ScrollAnimator::handleWheelEvent(wheelEvent);
867             }
868         }
869     }
870
871     bool isMomentumScrollEvent = (wheelEvent.momentumPhase() != PlatformWheelEventPhaseNone);
872     if (m_ignoreMomentumScrolls && (isMomentumScrollEvent || m_snapRubberBandTimer.isActive())) {
873         if (wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded) {
874             m_ignoreMomentumScrolls = false;
875             return true;
876         }
877         return false;
878     }
879
880     smoothScrollWithEvent(wheelEvent);
881     return true;
882 }
883
884 void ScrollAnimatorChromiumMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent)
885 {
886     if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType)
887         beginScrollGesture();
888     else if (gestureEvent.type() == PlatformGestureEvent::ScrollEndType)
889         endScrollGesture();
890 }
891
892 bool ScrollAnimatorChromiumMac::pinnedInDirection(float deltaX, float deltaY)
893 {
894     FloatSize limitDelta;
895     if (fabsf(deltaY) >= fabsf(deltaX)) {
896         if (deltaY < 0) {
897             // We are trying to scroll up.  Make sure we are not pinned to the top
898             limitDelta.setHeight(m_scrollableArea->visibleContentRect().y() + + m_scrollableArea->scrollOrigin().y());
899         } else {
900             // We are trying to scroll down.  Make sure we are not pinned to the bottom
901             limitDelta.setHeight(m_scrollableArea->contentsSize().height() - (m_scrollableArea->visibleContentRect().maxY() + m_scrollableArea->scrollOrigin().y()));
902         }
903     } else if (deltaX != 0) {
904         if (deltaX < 0) {
905             // We are trying to scroll left.  Make sure we are not pinned to the left
906             limitDelta.setWidth(m_scrollableArea->visibleContentRect().x() + m_scrollableArea->scrollOrigin().x());
907         } else {
908             // We are trying to scroll right.  Make sure we are not pinned to the right
909             limitDelta.setWidth(m_scrollableArea->contentsSize().width() - (m_scrollableArea->visibleContentRect().maxX() + m_scrollableArea->scrollOrigin().x()));
910         }
911     }
912     
913     if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))
914         return true;
915     return false;
916 }
917
918 bool ScrollAnimatorChromiumMac::allowsVerticalStretching() const
919 {
920     switch (m_scrollableArea->verticalScrollElasticity()) {
921     case ScrollElasticityAutomatic: {
922         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
923         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
924         return (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled())));
925     }
926     case ScrollElasticityNone:
927         return false;
928     case ScrollElasticityAllowed:
929         return true;
930     }
931
932     ASSERT_NOT_REACHED();
933     return false;
934 }
935
936 bool ScrollAnimatorChromiumMac::allowsHorizontalStretching() const
937 {
938     switch (m_scrollableArea->horizontalScrollElasticity()) {
939     case ScrollElasticityAutomatic: {
940         Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
941         Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
942         return (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled())));
943     }
944     case ScrollElasticityNone:
945         return false;
946     case ScrollElasticityAllowed:
947         return true;
948     }
949
950     ASSERT_NOT_REACHED();
951     return false;
952 }
953
954 void ScrollAnimatorChromiumMac::smoothScrollWithEvent(const PlatformWheelEvent& wheelEvent)
955 {
956     m_haveScrolledSincePageLoad = true;
957
958     float deltaX = m_overflowScrollDelta.width();
959     float deltaY = m_overflowScrollDelta.height();
960
961     // Reset overflow values because we may decide to remove delta at various points and put it into overflow.
962     m_overflowScrollDelta = FloatSize();
963
964     float eventCoalescedDeltaX = -wheelEvent.deltaX();
965     float eventCoalescedDeltaY = -wheelEvent.deltaY();
966
967     deltaX += eventCoalescedDeltaX;
968     deltaY += eventCoalescedDeltaY;
969
970     // Slightly prefer scrolling vertically by applying the = case to deltaY
971     if (fabsf(deltaY) >= fabsf(deltaX))
972         deltaX = 0;
973     else
974         deltaY = 0;
975
976     bool isVerticallyStretched = false;
977     bool isHorizontallyStretched = false;
978     bool shouldStretch = false;
979     
980     IntSize stretchAmount = m_scrollableArea->overhangAmount();
981
982     isHorizontallyStretched = stretchAmount.width();
983     isVerticallyStretched = stretchAmount.height();
984
985     PlatformWheelEventPhase phase = wheelEvent.momentumPhase();
986
987     // If we are starting momentum scrolling then do some setup.
988     if (!m_momentumScrollInProgress && (phase == PlatformWheelEventPhaseBegan || phase == PlatformWheelEventPhaseChanged))
989         m_momentumScrollInProgress = true;
990
991     CFTimeInterval timeDelta = wheelEvent.timestamp() - m_lastMomentumScrollTimestamp;
992     if (m_inScrollGesture || m_momentumScrollInProgress) {
993         if (m_lastMomentumScrollTimestamp && timeDelta > 0 && timeDelta < scrollVelocityZeroingTimeout) {
994             m_momentumVelocity.setWidth(eventCoalescedDeltaX / (float)timeDelta);
995             m_momentumVelocity.setHeight(eventCoalescedDeltaY / (float)timeDelta);
996             m_lastMomentumScrollTimestamp = wheelEvent.timestamp();
997         } else {
998             m_lastMomentumScrollTimestamp = wheelEvent.timestamp();
999             m_momentumVelocity = FloatSize();
1000         }
1001
1002         if (isVerticallyStretched) {
1003             if (!isHorizontallyStretched && pinnedInDirection(deltaX, 0)) {                
1004                 // Stretching only in the vertical.
1005                 if (deltaY != 0 && (fabsf(deltaX / deltaY) < rubberbandDirectionLockStretchRatio))
1006                     deltaX = 0;
1007                 else if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
1008                     m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
1009                     deltaX = 0;
1010                 } else
1011                     m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
1012             }
1013         } else if (isHorizontallyStretched) {
1014             // Stretching only in the horizontal.
1015             if (pinnedInDirection(0, deltaY)) {
1016                 if (deltaX != 0 && (fabsf(deltaY / deltaX) < rubberbandDirectionLockStretchRatio))
1017                     deltaY = 0;
1018                 else if (fabsf(deltaY) < rubberbandMinimumRequiredDeltaBeforeStretch) {
1019                     m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
1020                     deltaY = 0;
1021                 } else
1022                     m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY);
1023             }
1024         } else {
1025             // Not stretching at all yet.
1026             if (pinnedInDirection(deltaX, deltaY)) {
1027                 if (fabsf(deltaY) >= fabsf(deltaX)) {
1028                     if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) {
1029                         m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
1030                         deltaX = 0;
1031                     } else
1032                         m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX);
1033                 }
1034                 shouldStretch = true;
1035             }
1036         }
1037     }
1038
1039     if (deltaX != 0 || deltaY != 0) {
1040         if (!(shouldStretch || isVerticallyStretched || isHorizontallyStretched)) {
1041             if (deltaY != 0) {
1042                 deltaY *= scrollWheelMultiplier();
1043                 immediateScrollByDeltaY(deltaY);
1044             }
1045             if (deltaX != 0) {
1046                 deltaX *= scrollWheelMultiplier();
1047                 immediateScrollByDeltaX(deltaX);
1048             }
1049         } else {
1050             if (!allowsHorizontalStretching()) {
1051                 deltaX = 0;
1052                 eventCoalescedDeltaX = 0;
1053             } else if ((deltaX != 0) && !isHorizontallyStretched && !pinnedInDirection(deltaX, 0)) {
1054                 deltaX *= scrollWheelMultiplier();
1055
1056                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1057                 immediateScrollByDeltaX(deltaX);
1058                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1059
1060                 deltaX = 0;
1061             }
1062             
1063             if (!allowsVerticalStretching()) {
1064                 deltaY = 0;
1065                 eventCoalescedDeltaY = 0;
1066             } else if ((deltaY != 0) && !isVerticallyStretched && !pinnedInDirection(0, deltaY)) {
1067                 deltaY *= scrollWheelMultiplier();
1068
1069                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1070                 immediateScrollByDeltaY(deltaY);
1071                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1072
1073                 deltaY = 0;
1074             }
1075             
1076             IntSize stretchAmount = m_scrollableArea->overhangAmount();
1077         
1078             if (m_momentumScrollInProgress) {
1079                 if ((pinnedInDirection(eventCoalescedDeltaX, eventCoalescedDeltaY) || (fabsf(eventCoalescedDeltaX) + fabsf(eventCoalescedDeltaY) <= 0)) && m_lastMomentumScrollTimestamp) {
1080                     m_ignoreMomentumScrolls = true;
1081                     m_momentumScrollInProgress = false;
1082                     snapRubberBand();
1083                 }
1084             }
1085
1086             m_stretchScrollForce.setWidth(m_stretchScrollForce.width() + deltaX);
1087             m_stretchScrollForce.setHeight(m_stretchScrollForce.height() + deltaY);
1088
1089             FloatSize dampedDelta(ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.width())), ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.height())));
1090             FloatPoint origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - stretchAmount;
1091             FloatPoint newOrigin = origOrigin + dampedDelta;
1092
1093             if (origOrigin != newOrigin) {
1094                 m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1095                 immediateScrollToPoint(newOrigin);
1096                 m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1097             }
1098         }
1099     }
1100
1101     if (m_momentumScrollInProgress && phase == PlatformWheelEventPhaseEnded) {
1102         m_momentumScrollInProgress = false;
1103         m_ignoreMomentumScrolls = false;
1104         m_lastMomentumScrollTimestamp = 0;
1105     }
1106 }
1107
1108 void ScrollAnimatorChromiumMac::beginScrollGesture()
1109 {
1110     didBeginScrollGesture();
1111
1112     m_haveScrolledSincePageLoad = true;
1113     m_inScrollGesture = true;
1114     m_momentumScrollInProgress = false;
1115     m_ignoreMomentumScrolls = false;
1116     m_lastMomentumScrollTimestamp = 0;
1117     m_momentumVelocity = FloatSize();
1118     m_scrollerInitiallyPinnedOnLeft = m_scrollableArea->isHorizontalScrollerPinnedToMinimumPosition();
1119     m_scrollerInitiallyPinnedOnRight = m_scrollableArea->isHorizontalScrollerPinnedToMaximumPosition();
1120     m_cumulativeHorizontalScroll = 0;
1121     m_didCumulativeHorizontalScrollEverSwitchToOppositeDirectionOfPin = false;
1122     
1123     IntSize stretchAmount = m_scrollableArea->overhangAmount();
1124     m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(stretchAmount.width()));
1125     m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(stretchAmount.height()));
1126
1127     m_overflowScrollDelta = FloatSize();
1128     
1129     if (m_snapRubberBandTimer.isActive())
1130         m_snapRubberBandTimer.stop();
1131 }
1132
1133 void ScrollAnimatorChromiumMac::endScrollGesture()
1134 {
1135     didEndScrollGesture();
1136
1137     snapRubberBand();
1138 }
1139
1140 void ScrollAnimatorChromiumMac::snapRubberBand()
1141 {
1142     CFTimeInterval timeDelta = [[NSProcessInfo processInfo] systemUptime] - m_lastMomentumScrollTimestamp;
1143     if (m_lastMomentumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout)
1144         m_momentumVelocity = FloatSize();
1145
1146     m_inScrollGesture = false;
1147
1148     if (m_snapRubberBandTimer.isActive())
1149         return;
1150
1151     m_startTime = [NSDate timeIntervalSinceReferenceDate];
1152     m_startStretch = FloatSize();
1153     m_origOrigin = FloatPoint();
1154     m_origVelocity = FloatSize();
1155
1156     m_snapRubberBandTimer.startRepeating(1.0/60.0);
1157 }
1158
1159 static inline float roundTowardZero(float num)
1160 {
1161     return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f);
1162 }
1163
1164 static inline float roundToDevicePixelTowardZero(float num)
1165 {
1166     float roundedNum = roundf(num);
1167     if (fabs(num - roundedNum) < 0.125)
1168         num = roundedNum;
1169
1170     return roundTowardZero(num);
1171 }
1172
1173 void ScrollAnimatorChromiumMac::snapRubberBandTimerFired(Timer<ScrollAnimatorChromiumMac>*)
1174 {
1175     if (!m_momentumScrollInProgress || m_ignoreMomentumScrolls) {
1176         CFTimeInterval timeDelta = [NSDate timeIntervalSinceReferenceDate] - m_startTime;
1177
1178         if (m_startStretch == FloatSize()) {
1179             m_startStretch = m_scrollableArea->overhangAmount();
1180             if (m_startStretch == FloatSize()) {    
1181                 m_snapRubberBandTimer.stop();
1182                 m_stretchScrollForce = FloatSize();
1183                 m_startTime = 0;
1184                 m_startStretch = FloatSize();
1185                 m_origOrigin = FloatPoint();
1186                 m_origVelocity = FloatSize();
1187
1188                 return;
1189             }
1190
1191             m_scrollableArea->didStartRubberBand(roundedIntSize(m_startStretch));
1192
1193             m_origOrigin = (m_scrollableArea->visibleContentRect().location() + m_scrollableArea->scrollOrigin()) - m_startStretch;
1194             m_origVelocity = m_momentumVelocity;
1195
1196             // Just like normal scrolling, prefer vertical rubberbanding
1197             if (fabsf(m_origVelocity.height()) >= fabsf(m_origVelocity.width()))
1198                 m_origVelocity.setWidth(0);
1199             
1200             // Don't rubber-band horizontally if it's not possible to scroll horizontally
1201             Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar();
1202             if (!hScroller || !hScroller->enabled())
1203                 m_origVelocity.setWidth(0);
1204             
1205             // Don't rubber-band vertically if it's not possible to scroll horizontally
1206             Scrollbar* vScroller = m_scrollableArea->verticalScrollbar();
1207             if (!vScroller || !vScroller->enabled())
1208                 m_origVelocity.setHeight(0);
1209         }
1210
1211         FloatPoint delta(roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.width(), -m_origVelocity.width(), (float)timeDelta)),
1212                          roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.height(), -m_origVelocity.height(), (float)timeDelta)));
1213
1214         if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) {
1215             FloatPoint newOrigin = m_origOrigin + delta;
1216
1217             m_scrollableArea->setConstrainsScrollingToContentEdge(false);
1218             immediateScrollToPoint(newOrigin);
1219             m_scrollableArea->setConstrainsScrollingToContentEdge(true);
1220
1221             FloatSize newStretch = m_scrollableArea->overhangAmount();
1222             
1223             m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(newStretch.width()));
1224             m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(newStretch.height()));
1225         } else {
1226             immediateScrollToPoint(m_origOrigin);
1227
1228             m_scrollableArea->didCompleteRubberBand(roundedIntSize(m_startStretch));
1229
1230             m_snapRubberBandTimer.stop();
1231             m_stretchScrollForce = FloatSize();
1232             
1233             m_startTime = 0;
1234             m_startStretch = FloatSize();
1235             m_origOrigin = FloatPoint();
1236             m_origVelocity = FloatSize();
1237         }
1238     } else {
1239         m_startTime = [NSDate timeIntervalSinceReferenceDate];
1240         m_startStretch = FloatSize();
1241     }
1242 }
1243 #endif
1244
1245 void ScrollAnimatorChromiumMac::setIsActive()
1246 {
1247     if (isScrollbarOverlayAPIAvailable()) {
1248         if (needsScrollerStyleUpdate())
1249             updateScrollerStyle();
1250     }
1251 }
1252
1253 #if USE(WK_SCROLLBAR_PAINTER)
1254 void ScrollAnimatorChromiumMac::updateScrollerStyle()
1255 {
1256     if (!scrollableArea()->isOnActivePage()) {
1257         setNeedsScrollerStyleUpdate(true);
1258         return;
1259     }
1260
1261     ScrollbarThemeChromiumMac* macTheme = chromiumScrollbarTheme();
1262     if (!macTheme) {
1263         setNeedsScrollerStyleUpdate(false);
1264         return;
1265     }
1266
1267     int newStyle = wkScrollbarPainterControllerStyle(m_scrollbarPainterController.get());
1268
1269     if (Scrollbar* verticalScrollbar = scrollableArea()->verticalScrollbar()) {
1270         verticalScrollbar->invalidate();
1271
1272         WKScrollbarPainterRef oldVerticalPainter = wkVerticalScrollbarPainterForController(m_scrollbarPainterController.get());
1273         WKScrollbarPainterRef newVerticalPainter = wkMakeScrollbarReplacementPainter(oldVerticalPainter,
1274                                                                                      newStyle,
1275                                                                                      verticalScrollbar->controlSize(),
1276                                                                                      false);
1277         macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter);
1278         wkSetPainterForPainterController(m_scrollbarPainterController.get(), newVerticalPainter, false);
1279
1280         // The different scrollbar styles have different thicknesses, so we must re-set the 
1281         // frameRect to the new thickness, and the re-layout below will ensure the position
1282         // and length are properly updated.
1283         int thickness = macTheme->scrollbarThickness(verticalScrollbar->controlSize());
1284         verticalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1285     }
1286
1287     if (Scrollbar* horizontalScrollbar = scrollableArea()->horizontalScrollbar()) {
1288         horizontalScrollbar->invalidate();
1289
1290         WKScrollbarPainterRef oldHorizontalPainter = wkHorizontalScrollbarPainterForController(m_scrollbarPainterController.get());
1291         WKScrollbarPainterRef newHorizontalPainter = wkMakeScrollbarReplacementPainter(oldHorizontalPainter,
1292                                                                                        newStyle,
1293                                                                                        horizontalScrollbar->controlSize(),
1294                                                                                        true);
1295         macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter);
1296         wkSetPainterForPainterController(m_scrollbarPainterController.get(), newHorizontalPainter, true);
1297
1298         // The different scrollbar styles have different thicknesses, so we must re-set the 
1299         // frameRect to the new thickness, and the re-layout below will ensure the position
1300         // and length are properly updated.
1301         int thickness = macTheme->scrollbarThickness(horizontalScrollbar->controlSize());
1302         horizontalScrollbar->setFrameRect(IntRect(0, 0, thickness, thickness));
1303     }
1304
1305     // If needsScrollerStyleUpdate() is true, then the page is restoring from the page cache, and 
1306     // a relayout will happen on its own. Otherwise, we must initiate a re-layout ourselves.
1307     scrollableArea()->scrollbarStyleChanged(newStyle, !needsScrollerStyleUpdate());
1308
1309     setNeedsScrollerStyleUpdate(false);
1310 }
1311
1312 void ScrollAnimatorChromiumMac::startScrollbarPaintTimer()
1313 {
1314     m_initialScrollbarPaintTimer.startOneShot(0.1);
1315 }
1316
1317 bool ScrollAnimatorChromiumMac::scrollbarPaintTimerIsActive() const
1318 {
1319     return m_initialScrollbarPaintTimer.isActive();
1320 }
1321
1322 void ScrollAnimatorChromiumMac::stopScrollbarPaintTimer()
1323 {
1324     m_initialScrollbarPaintTimer.stop();
1325 }
1326
1327 void ScrollAnimatorChromiumMac::initialScrollbarPaintTimerFired(Timer<ScrollAnimatorChromiumMac>*)
1328 {
1329     wkScrollbarPainterForceFlashScrollers(m_scrollbarPainterController.get());
1330 }
1331 #endif
1332
1333 void ScrollAnimatorChromiumMac::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
1334 {
1335     IntRect rectInViewCoordinates = scrollerThumb;
1336     if (Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar())
1337         rectInViewCoordinates = verticalScrollbar->convertToContainingView(scrollerThumb);
1338
1339     if (rectInViewCoordinates == m_visibleScrollerThumbRect)
1340         return;
1341
1342     m_scrollableArea->setVisibleScrollerThumbRect(rectInViewCoordinates);
1343     m_visibleScrollerThumbRect = rectInViewCoordinates;
1344 }
1345
1346 } // namespace WebCore