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