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