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