Index: third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp |
diff --git a/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp b/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp |
index c3b47315630a09445668430e1400f374fa7d5225..a7fb64b8ffc490b41ffdd4b3cca7192fe8a02213 100644 |
--- a/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp |
+++ b/third_party/WebKit/Source/platform/scroll/ScrollAnimator.cpp |
@@ -32,12 +32,13 @@ |
#include "platform/scroll/ScrollAnimator.h" |
#include "platform/TraceEvent.h" |
+#include "platform/graphics/GraphicsLayer.h" |
#include "platform/scroll/ScrollableArea.h" |
#include "public/platform/Platform.h" |
+#include "public/platform/WebCompositorAnimation.h" |
#include "public/platform/WebCompositorSupport.h" |
#include "wtf/CurrentTime.h" |
#include "wtf/PassRefPtr.h" |
-#include <algorithm> |
namespace blink { |
@@ -50,21 +51,22 @@ PassOwnPtrWillBeRawPtr<ScrollAnimatorBase> ScrollAnimatorBase::create(Scrollable |
ScrollAnimator::ScrollAnimator(ScrollableArea* scrollableArea, WTF::TimeFunction timeFunction) |
: ScrollAnimatorBase(scrollableArea) |
+ , m_lastTickTime(0.0) |
, m_timeFunction(timeFunction) |
{ |
} |
ScrollAnimator::~ScrollAnimator() |
{ |
- cancelAnimations(); |
} |
FloatPoint ScrollAnimator::desiredTargetPosition() const |
{ |
- return m_animationCurve ? FloatPoint(m_animationCurve->targetValue()) : currentPosition(); |
+ return m_animationCurve ? m_targetOffset : currentPosition(); |
} |
-float ScrollAnimator::computeDeltaToConsume(ScrollbarOrientation orientation, float pixelDelta) const |
+float ScrollAnimator::computeDeltaToConsume( |
+ ScrollbarOrientation orientation, float pixelDelta) const |
{ |
FloatPoint pos = desiredTargetPosition(); |
float currentPos = (orientation == HorizontalScrollbar) ? pos.x() : pos.y(); |
@@ -72,7 +74,16 @@ float ScrollAnimator::computeDeltaToConsume(ScrollbarOrientation orientation, fl |
return (currentPos == newPos) ? 0.0f : (newPos - currentPos); |
} |
-ScrollResultOneDimensional ScrollAnimator::userScroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta) |
+void ScrollAnimator::resetAnimationState() |
+{ |
+ ScrollAnimatorCompositorCoordinator::resetAnimationState(); |
+ if (m_animationCurve) |
+ m_animationCurve.clear(); |
+ m_startTime = 0.0; |
+} |
+ |
+ScrollResultOneDimensional ScrollAnimator::userScroll( |
+ ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta) |
{ |
if (!m_scrollableArea->scrollAnimatorEnabled()) |
return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta); |
@@ -83,14 +94,33 @@ ScrollResultOneDimensional ScrollAnimator::userScroll(ScrollbarOrientation orien |
return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta); |
float usedPixelDelta = computeDeltaToConsume(orientation, step * delta); |
- FloatPoint pixelDelta = (orientation == VerticalScrollbar ? FloatPoint(0, usedPixelDelta) : FloatPoint(usedPixelDelta, 0)); |
+ FloatPoint pixelDelta = (orientation == VerticalScrollbar |
+ ? FloatPoint(0, usedPixelDelta) : FloatPoint(usedPixelDelta, 0)); |
FloatPoint targetPos = desiredTargetPosition(); |
targetPos.moveBy(pixelDelta); |
if (m_animationCurve) { |
- if (!(targetPos - m_animationCurve->targetValue()).isZero()) |
- m_animationCurve->updateTarget(m_timeFunction() - m_startTime, targetPos); |
+ if ((targetPos - m_targetOffset).isZero()) { |
+ // Report unused delta only if there is no animation running. See |
+ // comment below regarding scroll latching. |
+ return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); |
+ } |
+ |
+ m_targetOffset = targetPos; |
+ ASSERT(m_runState == RunState::RunningOnMainThread |
+ || m_runState == RunState::RunningOnCompositor |
+ || m_runState == RunState::RunningOnCompositorButNeedsUpdate); |
+ |
+ if (m_runState == RunState::RunningOnCompositor |
+ || m_runState == RunState::RunningOnCompositorButNeedsUpdate) { |
+ m_runState = RunState::RunningOnCompositorButNeedsUpdate; |
+ return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); |
+ } |
+ |
+ // Running on the main thread, simply update the target offset instead |
+ // of sending to the compositor. |
+ m_animationCurve->updateTarget(m_timeFunction() - m_startTime, targetPos); |
return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); |
} |
@@ -101,16 +131,17 @@ ScrollResultOneDimensional ScrollAnimator::userScroll(ScrollbarOrientation orien |
return ScrollResultOneDimensional(/* didScroll */ false, delta); |
} |
- m_animationCurve = adoptPtr(Platform::current()->compositorSupport()->createScrollOffsetAnimationCurve( |
- targetPos, |
- WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, |
- WebScrollOffsetAnimationCurve::ScrollDurationConstant)); |
- |
- m_animationCurve->setInitialValue(currentPosition()); |
+ m_targetOffset = targetPos; |
m_startTime = m_timeFunction(); |
scrollableArea()->registerForAnimation(); |
- animationTimerFired(); |
+ if (!m_scrollableArea->scheduleAnimation()) { |
+ scrollToOffsetWithoutAnimation(targetPos); |
+ resetAnimationState(); |
+ return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); |
+ } |
+ |
+ m_runState = RunState::WaitingToSendToCompositor; |
return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); |
} |
@@ -119,34 +150,23 @@ void ScrollAnimator::scrollToOffsetWithoutAnimation(const FloatPoint& offset) |
m_currentPosX = offset.x(); |
m_currentPosY = offset.y(); |
- cancelAnimations(); |
+ resetAnimationState(); |
notifyPositionChanged(); |
} |
-void ScrollAnimator::cancelAnimations() |
+void ScrollAnimator::tickAnimation(double monotonicTime) |
{ |
- if (m_animationCurve) |
- m_animationCurve.clear(); |
-} |
+ m_lastTickTime = monotonicTime; |
-void ScrollAnimator::serviceScrollAnimations() |
-{ |
- if (hasRunningAnimation()) |
- animationTimerFired(); |
-} |
+ if (m_runState != RunState::RunningOnMainThread) |
+ return; |
-bool ScrollAnimator::hasRunningAnimation() const |
-{ |
- return m_animationCurve; |
-} |
- |
-void ScrollAnimator::animationTimerFired() |
-{ |
- TRACE_EVENT0("blink", "ScrollAnimator::animationTimerFired"); |
- double elapsedTime = m_timeFunction() - m_startTime; |
+ TRACE_EVENT0("blink", "ScrollAnimator::tickAnimation"); |
+ double elapsedTime = monotonicTime - m_startTime; |
bool isFinished = (elapsedTime > m_animationCurve->duration()); |
- FloatPoint offset = isFinished ? m_animationCurve->targetValue() : m_animationCurve->getValue(elapsedTime); |
+ FloatPoint offset = isFinished ? m_animationCurve->targetValue() |
+ : m_animationCurve->getValue(elapsedTime); |
offset = FloatPoint(m_scrollableArea->clampScrollPosition(offset)); |
@@ -154,7 +174,7 @@ void ScrollAnimator::animationTimerFired() |
m_currentPosY = offset.y(); |
if (isFinished) |
- m_animationCurve.clear(); |
+ resetAnimationState(); |
else |
scrollableArea()->scheduleAnimation(); |
@@ -162,6 +182,105 @@ void ScrollAnimator::animationTimerFired() |
notifyPositionChanged(); |
} |
+void ScrollAnimator::updateCompositorAnimations() |
+{ |
+ if (m_compositorAnimationId && m_runState != RunState::RunningOnCompositor |
+ && m_runState != RunState::RunningOnCompositorButNeedsUpdate) { |
+ // If the current run state is WaitingToSendToCompositor but we have a |
+ // non-zero compositor animation id, there's a currently running |
+ // compositor animation that needs to be removed here before the new |
+ // animation is added below. |
+ ASSERT(m_runState == RunState::WaitingToCancelOnCompositor |
+ || m_runState == RunState::WaitingToSendToCompositor); |
+ |
+ abortAnimation(); |
+ |
+ m_compositorAnimationId = 0; |
+ m_compositorAnimationGroupId = 0; |
+ if (m_runState == RunState::WaitingToCancelOnCompositor) { |
+ resetAnimationState(); |
+ return; |
+ } |
+ } |
+ |
+ if (m_runState == RunState::WaitingToSendToCompositor |
+ || m_runState == RunState::RunningOnCompositorButNeedsUpdate) { |
+ if (m_runState == RunState::RunningOnCompositorButNeedsUpdate) { |
+ // Abort the running animation before a new one with an updated |
+ // target is added. |
+ abortAnimation(); |
+ |
+ m_compositorAnimationId = 0; |
+ m_compositorAnimationGroupId = 0; |
+ |
+ m_animationCurve->updateTarget(m_lastTickTime - m_startTime, |
+ m_targetOffset); |
+ m_runState = RunState::WaitingToSendToCompositor; |
+ } |
+ |
+ if (!m_animationCurve) { |
+ m_animationCurve = adoptPtr(Platform::current()->compositorSupport() |
+ ->createScrollOffsetAnimationCurve( |
+ m_targetOffset, |
+ WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, |
+ WebScrollOffsetAnimationCurve::ScrollDurationConstant)); |
+ m_animationCurve->setInitialValue(currentPosition()); |
+ } |
+ |
+ bool sentToCompositor = false; |
+ if (GraphicsLayer* layer = m_scrollableArea->layerForScrolling()) { |
+ ASSERT(layer->scrollableArea() == m_scrollableArea); |
+ if (!layer->platformLayer()->shouldScrollOnMainThread()) { |
+ OwnPtr<WebCompositorAnimation> animation = adoptPtr( |
+ Platform::current()->compositorSupport()->createAnimation( |
+ *m_animationCurve, |
+ WebCompositorAnimation::TargetPropertyScrollOffset)); |
+ // Being here means that either there is an animation that needs |
+ // to be sent to the compositor, or an animation that needs to |
+ // be updated (a new scroll event before the previous animation |
+ // is finished). In either case, the start time is when the |
+ // first animation was initiated. This re-targets the animation |
+ // using the current time on main thread. |
+ animation->setStartTime(m_startTime); |
+ |
+ int animationId = animation->id(); |
+ int animationGroupId = animation->group(); |
+ |
+ sentToCompositor = addAnimation(animation.release()); |
+ if (sentToCompositor) { |
+ m_runState = RunState::RunningOnCompositor; |
+ m_compositorAnimationId = animationId; |
+ m_compositorAnimationGroupId = animationGroupId; |
+ } |
+ } |
+ } |
+ |
+ if (!sentToCompositor) { |
+ m_runState = RunState::RunningOnMainThread; |
+ if (!m_scrollableArea->scheduleAnimation()) { |
+ scrollToOffsetWithoutAnimation(m_targetOffset); |
+ resetAnimationState(); |
+ } |
+ } |
+ } |
+} |
+ |
+void ScrollAnimator::notifyCompositorAnimationFinished(int groupId) |
+{ |
+ ScrollAnimatorCompositorCoordinator::compositorAnimationFinished(groupId); |
+} |
+ |
+void ScrollAnimator::cancelAnimation() |
+{ |
+ ScrollAnimatorCompositorCoordinator::cancelAnimation(); |
+} |
+ |
+void ScrollAnimator::layerForCompositedScrollingDidChange( |
+ WebCompositorAnimationTimeline* timeline) |
+{ |
+ reattachCompositorPlayerIfNeeded(timeline); |
+} |
+ |
DEFINE_TRACE(ScrollAnimator) |
{ |
ScrollAnimatorBase::trace(visitor); |