Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / platform / scroll / ScrollAnimatorNone.cpp
blob5aa4e199845f54d94b2da15189e863f0e60c6850
1 /*
2 * Copyright (c) 2011, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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.
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.
31 #include "config.h"
33 #include "platform/scroll/ScrollAnimatorNone.h"
35 #include <algorithm>
36 #include "platform/scroll/ScrollableArea.h"
37 #include "wtf/CurrentTime.h"
38 #include "wtf/PassRefPtr.h"
40 #include "platform/TraceEvent.h"
42 namespace blink {
44 const double kFrameRate = 60;
45 const double kTickTime = 1 / kFrameRate;
46 const double kMinimumTimerInterval = .001;
48 PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea)
50 if (scrollableArea && scrollableArea->scrollAnimatorEnabled())
51 return adoptPtr(new ScrollAnimatorNone(scrollableArea));
52 return adoptPtr(new ScrollAnimator(scrollableArea));
55 ScrollAnimatorNone::Parameters::Parameters()
56 : m_isEnabled(false)
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)
73 double ScrollAnimatorNone::PerAxisData::curveAt(Curve curve, double t)
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;
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;
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;
108 ASSERT_NOT_REACHED();
109 return 0;
112 double ScrollAnimatorNone::PerAxisData::attackCurve(Curve curve, double deltaTime, double curveT, double startPosition, double attackPosition)
114 double t = deltaTime / curveT;
115 double positionFactor = curveAt(curve, t);
116 return startPosition + positionFactor * (attackPosition - startPosition);
119 double ScrollAnimatorNone::PerAxisData::releaseCurve(Curve curve, double deltaTime, double curveT, double releasePosition, double desiredPosition)
121 double t = deltaTime / curveT;
122 double positionFactor = 1 - curveAt(curve, 1 - t);
123 return releasePosition + (positionFactor * (desiredPosition - releasePosition));
126 double ScrollAnimatorNone::PerAxisData::coastCurve(Curve curve, double factor)
128 return 1 - curveAt(curve, 1 - factor);
131 double ScrollAnimatorNone::PerAxisData::curveIntegralAt(Curve curve, double t)
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;
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;
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;
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;
175 ASSERT_NOT_REACHED();
176 return 0;
179 double ScrollAnimatorNone::PerAxisData::attackArea(Curve curve, double startT, double endT)
181 double startValue = curveIntegralAt(curve, startT);
182 double endValue = curveIntegralAt(curve, endT);
183 return endValue - startValue;
186 double ScrollAnimatorNone::PerAxisData::releaseArea(Curve curve, double startT, double endT)
188 double startValue = curveIntegralAt(curve, 1 - endT);
189 double endValue = curveIntegralAt(curve, 1 - startT);
190 return endValue - startValue;
193 ScrollAnimatorNone::PerAxisData::PerAxisData(float* currentPosition, int visibleLength)
194 : m_currentPosition(currentPosition)
195 , m_visibleLength(visibleLength)
197 reset();
200 void ScrollAnimatorNone::PerAxisData::reset()
202 m_currentVelocity = 0;
204 m_desiredPosition = 0;
205 m_desiredVelocity = 0;
207 m_startPosition = 0;
208 m_startTime = 0;
209 m_startVelocity = 0;
211 m_animationTime = 0;
212 m_lastAnimationTime = 0;
214 m_attackPosition = 0;
215 m_attackTime = 0;
216 m_attackCurve = Quadratic;
218 m_releasePosition = 0;
219 m_releaseTime = 0;
220 m_releaseCurve = Quadratic;
224 bool ScrollAnimatorNone::PerAxisData::updateDataFromParameters(float step, float delta, float scrollableSize, double currentTime, Parameters* parameters)
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;
231 float newPosition = m_desiredPosition + pixelDelta;
233 if (newPosition < 0 || newPosition > scrollableSize)
234 newPosition = std::max(std::min(newPosition, scrollableSize), 0.0f);
236 if (newPosition == m_desiredPosition)
237 return false;
239 m_desiredPosition = newPosition;
241 if (!m_startTime) {
242 m_attackTime = parameters->m_attackTime;
243 m_attackCurve = parameters->m_attackCurve;
245 m_animationTime = parameters->m_animationTime;
246 m_releaseTime = parameters->m_releaseTime;
247 m_releaseCurve = parameters->m_releaseCurve;
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;
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;
262 m_startVelocity = m_currentVelocity;
264 double remainingDelta = m_desiredPosition - *m_currentPosition;
266 double attackAreaLeft = 0;
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;
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;
282 if (fabs(remainingDelta) > minCoastDelta) {
283 double maxCoastDelta = parameters->m_maximumCoastTime * targetMaxCoastVelocity;
284 double coastFactor = std::min(1., (fabs(remainingDelta) - minCoastDelta) / (maxCoastDelta - minCoastDelta));
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));
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;
299 double releaseTimeLeft = std::min(timeLeft, m_releaseTime);
300 double sustainTimeLeft = std::max(0., timeLeft - releaseTimeLeft - attackTimeLeft);
302 if (attackTimeLeft) {
303 double attackSpot = deltaTime / m_attackTime;
304 attackAreaLeft = attackArea(m_attackCurve, attackSpot, 1) * m_attackTime;
307 double releaseSpot = (m_releaseTime - releaseTimeLeft) / m_releaseTime;
308 double releaseAreaLeft = releaseArea(m_releaseCurve, releaseSpot, 1) * m_releaseTime;
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;
317 if (sustainTimeLeft) {
318 double roundOff = m_releasePosition - ((attackAreaLeft ? m_attackPosition : *m_currentPosition) + m_desiredVelocity * sustainTimeLeft);
319 m_desiredVelocity += roundOff / sustainTimeLeft;
322 return true;
325 inline double ScrollAnimatorNone::PerAxisData::newScrollAnimationPosition(double deltaTime)
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);
336 // FIXME: Add in jank detection trace events into this function.
337 bool ScrollAnimatorNone::PerAxisData::animateScroll(double currentTime)
339 double lastScrollInterval = currentTime - m_lastAnimationTime;
340 if (lastScrollInterval < kMinimumTimerInterval)
341 return true;
343 m_lastAnimationTime = currentTime;
345 double deltaTime = currentTime - m_startTime;
347 if (deltaTime > m_animationTime) {
348 *m_currentPosition = m_desiredPosition;
349 reset();
350 return false;
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;
358 return true;
361 void ScrollAnimatorNone::PerAxisData::updateVisibleLength(int visibleLength)
363 m_visibleLength = visibleLength;
366 ScrollAnimatorNone::ScrollAnimatorNone(ScrollableArea* scrollableArea)
367 : ScrollAnimator(scrollableArea)
368 , m_horizontalData(&m_currentPosX, scrollableArea->visibleWidth())
369 , m_verticalData(&m_currentPosY, scrollableArea->visibleHeight())
370 , m_startTime(0)
371 , m_animationActive(false)
375 ScrollAnimatorNone::~ScrollAnimatorNone()
377 stopAnimationTimerIfNeeded();
380 ScrollAnimatorNone::Parameters ScrollAnimatorNone::parametersForScrollGranularity(ScrollGranularity granularity) const
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();
394 return Parameters();
397 ScrollResultOneDimensional ScrollAnimatorNone::userScroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta)
399 if (!m_scrollableArea->scrollAnimatorEnabled())
400 return ScrollAnimator::userScroll(orientation, granularity, step, delta);
402 TRACE_EVENT0("blink", "ScrollAnimatorNone::scroll");
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::userScroll(orientation, granularity, step, delta);
418 // If the individual input setting is disabled, bail.
419 if (!parameters.m_isEnabled)
420 return ScrollAnimator::userScroll(orientation, granularity, step, delta);
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));
425 PerAxisData& data = (orientation == VerticalScrollbar) ? m_verticalData : m_horizontalData;
426 bool needToScroll = data.updateDataFromParameters(step, delta, scrollableSize, WTF::monotonicallyIncreasingTime(), &parameters);
427 float unusedDelta = needToScroll ? delta - (data.m_desiredPosition - *data.m_currentPosition) : delta;
428 if (needToScroll && !animationTimerActive()) {
429 m_startTime = data.m_startTime;
430 animationWillStart();
431 animationTimerFired();
432 scrollableArea()->registerForAnimation();
434 return ScrollResultOneDimensional(needToScroll, unusedDelta);
437 void ScrollAnimatorNone::scrollToOffsetWithoutAnimation(const FloatPoint& offset)
439 m_currentPosX = offset.x();
440 m_currentPosY = offset.y();
442 // Must be called after setting the position since canceling the animation resets
443 // the desired position to the current.
444 cancelAnimations();
445 notifyPositionChanged();
448 void ScrollAnimatorNone::cancelAnimations()
450 m_animationActive = false;
452 m_horizontalData.reset();
453 m_verticalData.reset();
454 m_horizontalData.m_desiredPosition = m_currentPosX;
455 m_verticalData.m_desiredPosition = m_currentPosY;
458 void ScrollAnimatorNone::serviceScrollAnimations()
460 if (m_animationActive)
461 animationTimerFired();
464 bool ScrollAnimatorNone::hasRunningAnimation() const
466 return m_animationActive;
469 void ScrollAnimatorNone::updateAfterLayout()
471 updateVisibleLengths();
474 void ScrollAnimatorNone::willEndLiveResize()
476 updateVisibleLengths();
479 void ScrollAnimatorNone::didAddVerticalScrollbar(Scrollbar*)
481 updateVisibleLengths();
484 void ScrollAnimatorNone::didAddHorizontalScrollbar(Scrollbar*)
486 updateVisibleLengths();
489 void ScrollAnimatorNone::updateVisibleLengths()
491 m_horizontalData.updateVisibleLength(scrollableArea()->visibleWidth());
492 m_verticalData.updateVisibleLength(scrollableArea()->visibleHeight());
495 void ScrollAnimatorNone::animationTimerFired()
497 TRACE_EVENT0("blink", "ScrollAnimatorNone::animationTimerFired");
499 double currentTime = WTF::monotonicallyIncreasingTime();
501 bool continueAnimation = false;
502 if (m_horizontalData.m_startTime && m_horizontalData.animateScroll(currentTime))
503 continueAnimation = true;
504 if (m_verticalData.m_startTime && m_verticalData.animateScroll(currentTime))
505 continueAnimation = true;
507 if (continueAnimation)
508 startNextTimer();
509 else
510 m_animationActive = false;
512 TRACE_EVENT0("blink", "ScrollAnimatorNone::notifyPositionChanged");
513 notifyPositionChanged();
515 if (!continueAnimation)
516 animationDidFinish();
519 void ScrollAnimatorNone::startNextTimer()
521 if (scrollableArea()->scheduleAnimation())
522 m_animationActive = true;
525 bool ScrollAnimatorNone::animationTimerActive()
527 return m_animationActive;
530 void ScrollAnimatorNone::stopAnimationTimerIfNeeded()
532 if (animationTimerActive())
533 m_animationActive = false;
536 } // namespace blink