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