802ab8afdbbb7b16a93622dfb0274b0966750466
[WebKit-https.git] / Source / WebCore / platform / ScrollAnimatorNone.cpp
1 /*
2  * Copyright (c) 2011, Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32
33 #if ENABLE(SMOOTH_SCROLLING)
34
35 #include "ScrollAnimatorNone.h"
36
37 #include "FloatPoint.h"
38 #include "NotImplemented.h"
39 #include <wtf/OwnArrayPtr.h>
40 #include "ScrollableArea.h"
41 #include "ScrollbarTheme.h"
42 #include <algorithm>
43 #include <wtf/CurrentTime.h>
44 #include <wtf/PassOwnPtr.h>
45
46 #if PLATFORM(CHROMIUM)
47 #include "TraceEvent.h"
48 #endif
49
50 #if ENABLE(GESTURE_EVENTS)
51 #include "PlatformGestureEvent.h"
52 #endif
53
54 using namespace std;
55
56 namespace WebCore {
57
58 const double kFrameRate = 60;
59 const double kTickTime = 1 / kFrameRate;
60 const double kMinimumTimerInterval = .001;
61 const double kZoomTicks = 11;
62
63 #if !(PLATFORM(BLACKBERRY))
64 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
65 {
66     if (scrollableArea && scrollableArea->scrollAnimatorEnabled())
67         return adoptPtr(new ScrollAnimatorNone(scrollableArea));
68     return adoptPtr(new ScrollAnimator(scrollableArea));
69 }
70 #endif
71
72 ScrollAnimatorNone::Parameters::Parameters()
73     : m_isEnabled(false)
74 {
75 }
76
77 ScrollAnimatorNone::Parameters::Parameters(bool isEnabled, double animationTime, double repeatMinimumSustainTime, Curve attackCurve, double attackTime, Curve releaseCurve, double releaseTime, Curve coastTimeCurve, double maximumCoastTime)
78     : m_isEnabled(isEnabled)
79     , m_animationTime(animationTime)
80     , m_repeatMinimumSustainTime(repeatMinimumSustainTime)
81     , m_attackCurve(attackCurve)
82     , m_attackTime(attackTime)
83     , m_releaseCurve(releaseCurve)
84     , m_releaseTime(releaseTime)
85     , m_coastTimeCurve(coastTimeCurve)
86     , m_maximumCoastTime(maximumCoastTime)
87 {
88 }
89
90 double ScrollAnimatorNone::PerAxisData::curveAt(Curve curve, double t)
91 {
92     switch (curve) {
93     case Linear:
94         return t;
95     case Quadratic:
96         return t * t;
97     case Cubic:
98         return t * t * t;
99     case Quartic:
100         return t * t * t * t;
101     case Bounce:
102         // Time base is chosen to keep the bounce points simpler:
103         // 1 (half bounce coming in) + 1 + .5 + .25
104         const double kTimeBase = 2.75;
105         const double kTimeBaseSquared = kTimeBase * kTimeBase;
106         if (t < 1 / kTimeBase)
107             return kTimeBaseSquared * t * t;
108         if (t < 2 / kTimeBase) {
109             // Invert a [-.5,.5] quadratic parabola, center it in [1,2].
110             double t1 = t - 1.5 / kTimeBase;
111             const double kParabolaAtEdge = 1 - .5 * .5;
112             return kTimeBaseSquared * t1 * t1 + kParabolaAtEdge;
113         }
114         if (t < 2.5 / kTimeBase) {
115             // Invert a [-.25,.25] quadratic parabola, center it in [2,2.5].
116             double t2 = t - 2.25 / kTimeBase;
117             const double kParabolaAtEdge = 1 - .25 * .25;
118             return kTimeBaseSquared * t2 * t2 + kParabolaAtEdge;
119         }
120             // Invert a [-.125,.125] quadratic parabola, center it in [2.5,2.75].
121         const double kParabolaAtEdge = 1 - .125 * .125;
122         t -= 2.625 / kTimeBase;
123         return kTimeBaseSquared * t * t + kParabolaAtEdge;
124     }
125     ASSERT_NOT_REACHED();
126     return 0;
127 }
128
129 double ScrollAnimatorNone::PerAxisData::attackCurve(Curve curve, double deltaTime, double curveT, double startPosition, double attackPosition)
130 {
131     double t = deltaTime / curveT;
132     double positionFactor = curveAt(curve, t);
133     return startPosition + positionFactor * (attackPosition - startPosition);
134 }
135
136 double ScrollAnimatorNone::PerAxisData::releaseCurve(Curve curve, double deltaTime, double curveT, double releasePosition, double desiredPosition)
137 {
138     double t = deltaTime / curveT;
139     double positionFactor = 1 - curveAt(curve, 1 - t);
140     return releasePosition + (positionFactor * (desiredPosition - releasePosition));
141 }
142
143 double ScrollAnimatorNone::PerAxisData::coastCurve(Curve curve, double factor)
144 {
145     return 1 - curveAt(curve, 1 - factor);
146 }
147
148 double ScrollAnimatorNone::PerAxisData::curveIntegralAt(Curve curve, double t)
149 {
150     switch (curve) {
151     case Linear:
152         return t * t / 2;
153     case Quadratic:
154         return t * t * t / 3;
155     case Cubic:
156         return t * t * t * t / 4;
157     case Quartic:
158         return t * t * t * t * t / 5;
159     case Bounce:
160         const double kTimeBase = 2.75;
161         const double kTimeBaseSquared = kTimeBase * kTimeBase;
162         const double kTimeBaseSquaredOverThree = kTimeBaseSquared / 3;
163         double area;
164         double t1 = min(t, 1 / kTimeBase);
165         area = kTimeBaseSquaredOverThree * t1 * t1 * t1;
166         if (t < 1 / kTimeBase)
167             return area;
168
169         t1 = min(t - 1 / kTimeBase, 1 / kTimeBase);
170         // The integral of kTimeBaseSquared * (t1 - .5 / kTimeBase) * (t1 - .5 / kTimeBase) + kParabolaAtEdge
171         const double kSecondInnerOffset = kTimeBaseSquared * .5 / kTimeBase;
172         double bounceArea = t1 * (t1 * (kTimeBaseSquaredOverThree * t1 - kSecondInnerOffset) + 1);
173         area += bounceArea;
174         if (t < 2 / kTimeBase)
175             return area;
176
177         t1 = min(t - 2 / kTimeBase, 0.5 / kTimeBase);
178         // The integral of kTimeBaseSquared * (t1 - .25 / kTimeBase) * (t1 - .25 / kTimeBase) + kParabolaAtEdge
179         const double kThirdInnerOffset = kTimeBaseSquared * .25 / kTimeBase;
180         bounceArea =  t1 * (t1 * (kTimeBaseSquaredOverThree * t1 - kThirdInnerOffset) + 1);
181         area += bounceArea;
182         if (t < 2.5 / kTimeBase)
183             return area;
184
185         t1 = t - 2.5 / kTimeBase;
186         // The integral of kTimeBaseSquared * (t1 - .125 / kTimeBase) * (t1 - .125 / kTimeBase) + kParabolaAtEdge
187         const double kFourthInnerOffset = kTimeBaseSquared * .125 / kTimeBase;
188         bounceArea = t1 * (t1 * (kTimeBaseSquaredOverThree * t1 - kFourthInnerOffset) + 1);
189         area += bounceArea;
190         return area;
191     }
192     ASSERT_NOT_REACHED();
193     return 0;
194 }
195
196 double ScrollAnimatorNone::PerAxisData::attackArea(Curve curve, double startT, double endT)
197 {
198     double startValue = curveIntegralAt(curve, startT);
199     double endValue = curveIntegralAt(curve, endT);
200     return endValue - startValue;
201 }
202
203 double ScrollAnimatorNone::PerAxisData::releaseArea(Curve curve, double startT, double endT)
204 {
205     double startValue = curveIntegralAt(curve, 1 - endT);
206     double endValue = curveIntegralAt(curve, 1 - startT);
207     return endValue - startValue;
208 }
209
210 ScrollAnimatorNone::PerAxisData::PerAxisData(ScrollAnimatorNone* parent, float* currentPosition, int visibleLength)
211     : m_currentPosition(currentPosition)
212     , m_visibleLength(visibleLength)
213 {
214     reset();
215 }
216
217 void ScrollAnimatorNone::PerAxisData::reset()
218 {
219     m_currentVelocity = 0;
220
221     m_desiredPosition = 0;
222     m_desiredVelocity = 0;
223
224     m_startPosition = 0;
225     m_startTime = 0;
226     m_startVelocity = 0;
227
228     m_animationTime = 0;
229     m_lastAnimationTime = 0;
230
231     m_attackPosition = 0;
232     m_attackTime = 0;
233     m_attackCurve = Quadratic;
234
235     m_releasePosition = 0;
236     m_releaseTime = 0;
237     m_releaseCurve = Quadratic;
238 }
239
240
241 bool ScrollAnimatorNone::PerAxisData::updateDataFromParameters(float step, float multiplier, float scrollableSize, double currentTime, Parameters* parameters)
242 {
243     float delta = step * multiplier;
244     if (!m_startTime || !delta || (delta < 0) != (m_desiredPosition - *m_currentPosition < 0)) {
245         m_desiredPosition = *m_currentPosition;
246         m_startTime = 0;
247     }
248     float newPosition = m_desiredPosition + delta;
249
250     if (newPosition < 0 || newPosition > scrollableSize)
251         newPosition = max(min(newPosition, scrollableSize), 0.0f);
252
253     if (newPosition == m_desiredPosition)
254         return false;
255
256     m_desiredPosition = newPosition;
257
258     if (!m_startTime) {
259         m_attackTime = parameters->m_attackTime;
260         m_attackCurve = parameters->m_attackCurve;
261     }
262     m_animationTime = parameters->m_animationTime;
263     m_releaseTime = parameters->m_releaseTime;
264     m_releaseCurve = parameters->m_releaseCurve;
265
266     // Prioritize our way out of over constraint.
267     if (m_attackTime + m_releaseTime > m_animationTime) {
268         if (m_releaseTime > m_animationTime)
269             m_releaseTime = m_animationTime;
270         m_attackTime = m_animationTime - m_releaseTime;
271     }
272
273     if (!m_startTime) {
274         // FIXME: This should be the time from the event that got us here.
275         m_startTime = currentTime - kTickTime / 2;
276         m_startPosition = *m_currentPosition;
277         m_lastAnimationTime = m_startTime;
278     }
279     m_startVelocity = m_currentVelocity;
280
281     double remainingDelta = m_desiredPosition - *m_currentPosition;
282
283     double attackAreaLeft = 0;
284
285     double deltaTime = m_lastAnimationTime - m_startTime;
286     double attackTimeLeft = max(0., m_attackTime - deltaTime);
287     double timeLeft = m_animationTime - deltaTime;
288     double minTimeLeft = m_releaseTime + min(parameters->m_repeatMinimumSustainTime, m_animationTime - m_releaseTime - attackTimeLeft);
289     if (timeLeft < minTimeLeft) {
290         m_animationTime = deltaTime + minTimeLeft;
291         timeLeft = minTimeLeft;
292     }
293
294     if (parameters->m_maximumCoastTime > (parameters->m_repeatMinimumSustainTime + parameters->m_releaseTime)) {
295         double targetMaxCoastVelocity = m_visibleLength * .25 * kFrameRate;
296         // This needs to be as minimal as possible while not being intrusive to page up/down.
297         double minCoastDelta = m_visibleLength;
298
299         if (fabs(remainingDelta) > minCoastDelta) {
300             double maxCoastDelta = parameters->m_maximumCoastTime * targetMaxCoastVelocity;
301             double coastFactor = min(1., (fabs(remainingDelta) - minCoastDelta) / (maxCoastDelta - minCoastDelta));
302
303             // We could play with the curve here - linear seems a little soft. Initial testing makes me want to feed into the sustain time more aggressively.
304             double coastMinTimeLeft = min(parameters->m_maximumCoastTime, minTimeLeft + coastCurve(parameters->m_coastTimeCurve, coastFactor) * (parameters->m_maximumCoastTime - minTimeLeft));
305
306             double additionalTime = max(0., coastMinTimeLeft - minTimeLeft);
307             if (additionalTime) {
308                 double additionalReleaseTime = min(additionalTime, parameters->m_releaseTime / (parameters->m_releaseTime + parameters->m_repeatMinimumSustainTime) * additionalTime);
309                 m_releaseTime = parameters->m_releaseTime + additionalReleaseTime;
310                 m_animationTime = deltaTime + coastMinTimeLeft;
311                 timeLeft = coastMinTimeLeft;
312             }
313         }
314     }
315
316     double releaseTimeLeft = min(timeLeft, m_releaseTime);
317     double sustainTimeLeft = max(0., timeLeft - releaseTimeLeft - attackTimeLeft);
318
319     if (attackTimeLeft) {
320         double attackSpot = deltaTime / m_attackTime;
321         attackAreaLeft = attackArea(m_attackCurve, attackSpot, 1) * m_attackTime;
322     }
323
324     double releaseSpot = (m_releaseTime - releaseTimeLeft) / m_releaseTime;
325     double releaseAreaLeft  = releaseArea(m_releaseCurve, releaseSpot, 1) * m_releaseTime;
326
327     m_desiredVelocity = remainingDelta / (attackAreaLeft + sustainTimeLeft + releaseAreaLeft);
328     m_releasePosition = m_desiredPosition - m_desiredVelocity * releaseAreaLeft;
329     if (attackAreaLeft)
330         m_attackPosition = m_startPosition + m_desiredVelocity * attackAreaLeft;
331     else
332         m_attackPosition = m_releasePosition - (m_animationTime - m_releaseTime - m_attackTime) * m_desiredVelocity;
333
334     if (sustainTimeLeft) {
335         double roundOff = m_releasePosition - ((attackAreaLeft ? m_attackPosition : *m_currentPosition) + m_desiredVelocity * sustainTimeLeft);
336         m_desiredVelocity += roundOff / sustainTimeLeft;
337     }
338
339     return true;
340 }
341
342 // FIXME: Add in jank detection trace events into this function.
343 bool ScrollAnimatorNone::PerAxisData::animateScroll(double currentTime)
344 {
345     double lastScrollInterval = currentTime - m_lastAnimationTime;
346     if (lastScrollInterval < kMinimumTimerInterval)
347         return true;
348
349     m_lastAnimationTime = currentTime;
350
351     double deltaTime = currentTime - m_startTime;
352     double newPosition = *m_currentPosition;
353
354     if (deltaTime > m_animationTime) {
355         *m_currentPosition = m_desiredPosition;
356         reset();
357         return false;
358     }
359     if (deltaTime < m_attackTime)
360         newPosition = attackCurve(m_attackCurve, deltaTime, m_attackTime, m_startPosition, m_attackPosition);
361     else if (deltaTime < (m_animationTime - m_releaseTime))
362         newPosition = m_attackPosition + (deltaTime - m_attackTime) * m_desiredVelocity;
363     else {
364         // release is based on targeting the exact final position.
365         double releaseDeltaT = deltaTime - (m_animationTime - m_releaseTime);
366         newPosition = releaseCurve(m_releaseCurve, releaseDeltaT, m_releaseTime, m_releasePosition, m_desiredPosition);
367     }
368
369     // Normalize velocity to a per second amount. Could be used to check for jank.
370     if (lastScrollInterval > 0)
371         m_currentVelocity = (newPosition - *m_currentPosition) / lastScrollInterval;
372     *m_currentPosition = newPosition;
373
374     return true;
375 }
376
377 void ScrollAnimatorNone::PerAxisData::updateVisibleLength(int visibleLength)
378 {
379     m_visibleLength = visibleLength;
380 }
381
382 ScrollAnimatorNone::ScrollAnimatorNone(ScrollableArea* scrollableArea)
383     : ScrollAnimator(scrollableArea)
384     , m_horizontalData(this, &m_currentPosX, scrollableArea->visibleWidth())
385     , m_verticalData(this, &m_currentPosY, scrollableArea->visibleHeight())
386     , m_startTime(0)
387 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
388     , m_animationTimer(this, &ScrollAnimatorNone::animationTimerFired)
389 #else
390     , m_animationActive(false)
391 #endif
392 {
393 }
394
395 ScrollAnimatorNone::~ScrollAnimatorNone()
396 {
397     stopAnimationTimerIfNeeded();
398 }
399
400 ScrollAnimatorNone::Parameters ScrollAnimatorNone::parametersForScrollGranularity(ScrollGranularity granularity) const
401 {
402 #if !PLATFORM(QT)
403     switch (granularity) {
404     case ScrollByDocument:
405         return Parameters(true, 20 * kTickTime, 10 * kTickTime, Cubic, 10 * kTickTime, Cubic, 10 * kTickTime, Linear, 1);
406     case ScrollByLine:
407         return Parameters(true, 10 * kTickTime, 7 * kTickTime, Cubic, 3 * kTickTime, Cubic, 3 * kTickTime, Linear, 1);
408     case ScrollByPage:
409         return Parameters(true, 15 * kTickTime, 10 * kTickTime, Cubic, 5 * kTickTime, Cubic, 5 * kTickTime, Linear, 1);
410     case ScrollByPixel:
411         return Parameters(true, 11 * kTickTime, 2 * kTickTime, Cubic, 3 * kTickTime, Cubic, 3 * kTickTime, Quadratic, 1.25);
412     default:
413         ASSERT_NOT_REACHED();
414     }
415 #else
416     // This is a slightly different strategy for the animation with a steep attack curve and natural release curve.
417     // The fast acceleration makes the animation look more responsive to user input.
418     switch (granularity) {
419     case ScrollByDocument:
420         return Parameters(true, 20 * kTickTime, 10 * kTickTime, Cubic, 6 * kTickTime, Quadratic, 10 * kTickTime, Quadratic, 22 * kTickTime);
421     case ScrollByLine:
422         return Parameters(true, 6 * kTickTime, 5 * kTickTime, Cubic, 1 * kTickTime, Quadratic, 4 * kTickTime, Linear, 1);
423     case ScrollByPage:
424         return Parameters(true, 12 * kTickTime, 10 * kTickTime, Cubic, 3 * kTickTime, Quadratic, 6 * kTickTime, Linear, 1);
425     case ScrollByPixel:
426         return Parameters(true, 8 * kTickTime, 3 * kTickTime, Cubic, 2 * kTickTime, Quadratic, 5 * kTickTime, Quadratic, 1.25);
427     default:
428         ASSERT_NOT_REACHED();
429     }
430 #endif
431     return Parameters();
432 }
433
434 bool ScrollAnimatorNone::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier)
435 {
436     if (!m_scrollableArea->scrollAnimatorEnabled())
437         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
438
439 #if PLATFORM(CHROMIUM)
440     TRACE_EVENT0("webkit", "ScrollAnimatorNone::scroll");
441 #endif
442
443     // FIXME: get the type passed in. MouseWheel could also be by line, but should still have different
444     // animation parameters than the keyboard.
445     Parameters parameters;
446     switch (granularity) {
447     case ScrollByDocument:
448     case ScrollByLine:
449     case ScrollByPage:
450     case ScrollByPixel:
451         parameters = parametersForScrollGranularity(granularity);
452         break;
453     case ScrollByPrecisePixel:
454         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
455     }
456
457     // If the individual input setting is disabled, bail.
458     if (!parameters.m_isEnabled)
459         return ScrollAnimator::scroll(orientation, granularity, step, multiplier);
460
461     // This is an animatable scroll. Set the animation in motion using the appropriate parameters.
462     float scrollableSize = static_cast<float>(m_scrollableArea->scrollSize(orientation));
463
464     PerAxisData& data = (orientation == VerticalScrollbar) ? m_verticalData : m_horizontalData;
465     bool needToScroll = data.updateDataFromParameters(step, multiplier, scrollableSize, WTF::monotonicallyIncreasingTime(), &parameters);
466     if (needToScroll && !animationTimerActive()) {
467         m_startTime = data.m_startTime;
468         animationWillStart();
469         animationTimerFired();
470     }
471     return needToScroll;
472 }
473
474 void ScrollAnimatorNone::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
475 {
476     stopAnimationTimerIfNeeded();
477
478     m_horizontalData.reset();
479     *m_horizontalData.m_currentPosition = offset.x();
480     m_horizontalData.m_desiredPosition = offset.x();
481
482     m_verticalData.reset();
483     *m_verticalData.m_currentPosition = offset.y();
484     m_verticalData.m_desiredPosition = offset.y();
485
486     notifyPositionChanged();
487 }
488
489 #if !USE(REQUEST_ANIMATION_FRAME_TIMER)
490 void ScrollAnimatorNone::cancelAnimations()
491 {
492     m_animationActive = false;
493 }
494
495 void ScrollAnimatorNone::serviceScrollAnimations()
496 {
497     if (m_animationActive)
498         animationTimerFired();
499 }
500 #endif
501
502 void ScrollAnimatorNone::willEndLiveResize()
503 {
504     updateVisibleLengths();
505 }
506
507 void ScrollAnimatorNone::didAddVerticalScrollbar(Scrollbar*)
508 {
509     updateVisibleLengths();
510 }
511
512 void ScrollAnimatorNone::didAddHorizontalScrollbar(Scrollbar*)
513 {
514     updateVisibleLengths();
515 }
516
517 void ScrollAnimatorNone::updateVisibleLengths()
518 {
519     m_horizontalData.updateVisibleLength(scrollableArea()->visibleWidth());
520     m_verticalData.updateVisibleLength(scrollableArea()->visibleHeight());
521 }
522
523 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
524 void ScrollAnimatorNone::animationTimerFired(Timer<ScrollAnimatorNone>* timer)
525 {
526     animationTimerFired();
527 }
528 #endif
529
530 void ScrollAnimatorNone::animationTimerFired()
531 {
532 #if PLATFORM(CHROMIUM)
533     TRACE_EVENT0("webkit", "ScrollAnimatorNone::animationTimerFired");
534 #endif
535
536     double currentTime = WTF::monotonicallyIncreasingTime();
537     double deltaToNextFrame = ceil((currentTime - m_startTime) * kFrameRate) / kFrameRate - (currentTime - m_startTime);
538     currentTime += deltaToNextFrame;
539
540     bool continueAnimation = false;
541     if (m_horizontalData.m_startTime && m_horizontalData.animateScroll(currentTime))
542         continueAnimation = true;
543     if (m_verticalData.m_startTime && m_verticalData.animateScroll(currentTime))
544         continueAnimation = true;
545
546     if (continueAnimation)
547 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
548         startNextTimer(max(kMinimumTimerInterval, deltaToNextFrame));
549 #else
550         startNextTimer();
551     else
552         m_animationActive = false;
553 #endif
554
555 #if PLATFORM(CHROMIUM)
556     TRACE_EVENT0("webkit", "ScrollAnimatorNone::notifyPositionChanged");
557 #endif
558     notifyPositionChanged();
559
560     if (!continueAnimation)
561         animationDidFinish();
562 }
563
564 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
565 void ScrollAnimatorNone::startNextTimer(double delay)
566 {
567     m_animationTimer.startOneShot(delay);
568 }
569 #else
570 void ScrollAnimatorNone::startNextTimer()
571 {
572     if (scrollableArea()->scheduleAnimation())
573         m_animationActive = true;
574 }
575 #endif
576
577 bool ScrollAnimatorNone::animationTimerActive()
578 {
579 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
580     return m_animationTimer.isActive();
581 #else
582     return m_animationActive;
583 #endif
584 }
585
586 void ScrollAnimatorNone::stopAnimationTimerIfNeeded()
587 {
588     if (animationTimerActive())
589 #if USE(REQUEST_ANIMATION_FRAME_TIMER)
590         m_animationTimer.stop();
591 #else
592         m_animationActive = false;
593 #endif
594 }
595
596 } // namespace WebCore
597
598 #endif // ENABLE(SMOOTH_SCROLLING)