void ScrollAnimatorCompositorCoordinator::takeoverCompositorAnimation() { switch (m_runState) { case RunState::Idle: case RunState::WaitingToCancelOnCompositor: case RunState::PostAnimationCleanup: case RunState::RunningOnCompositorButNeedsTakeover: case RunState::WaitingToSendToCompositor: case RunState::RunningOnMainThread: break; case RunState::RunningOnCompositorButNeedsUpdate: case RunState::RunningOnCompositor: // We call abortAnimation that makes changes to the animation running on // the compositor. Thus, this function should only be called when in // CompositingClean state. abortAnimation(); m_runState = RunState::RunningOnCompositorButNeedsTakeover; // Get serviced the next time compositor updates are allowed. scrollableArea()->registerForAnimation(); } }
void ScrollAnimator::updateCompositorAnimations() { ScrollAnimatorCompositorCoordinator::updateCompositorAnimations(); if (m_runState == RunState::PostAnimationCleanup) { postAnimationCleanupAndReset(); return; } if (m_runState == RunState::WaitingToCancelOnCompositor) { DCHECK(m_compositorAnimationId); abortAnimation(); postAnimationCleanupAndReset(); return; } if (m_runState == RunState::RunningOnCompositorButNeedsTakeover) { // The call to ::takeOverCompositorAnimation aborted the animation and // put us in this state. The assumption is that takeOver is called // because a main thread scrolling reason is added, and simply trying // to ::sendAnimationToCompositor will fail and we will run on the main // thread. resetAnimationIds(); m_runState = RunState::WaitingToSendToCompositor; } if (m_runState == RunState::RunningOnCompositorButNeedsUpdate || m_runState == RunState::WaitingToCancelOnCompositorButNewScroll || m_runState == RunState::RunningOnCompositorButNeedsAdjustment) { // Abort the running animation before a new one with an updated // target is added. abortAnimation(); resetAnimationIds(); if (m_runState != RunState::RunningOnCompositorButNeedsAdjustment) { // When in RunningOnCompositorButNeedsAdjustment, the call to // ::adjustScrollOffsetAnimation should have made the necessary // adjustment to the curve. m_animationCurve->updateTarget( m_timeFunction() - m_startTime, compositorOffsetFromBlinkOffset(m_targetOffset)); } if (m_runState == RunState::WaitingToCancelOnCompositorButNewScroll) { m_animationCurve->setInitialValue( compositorOffsetFromBlinkOffset(currentOffset())); } m_runState = RunState::WaitingToSendToCompositor; } if (m_runState == RunState::WaitingToSendToCompositor) { if (!m_compositorAnimationAttachedToElementId) reattachCompositorPlayerIfNeeded( getScrollableArea()->compositorAnimationTimeline()); if (!m_animationCurve) createAnimationCurve(); bool runningOnMainThread = false; bool sentToCompositor = sendAnimationToCompositor(); if (!sentToCompositor) { runningOnMainThread = registerAndScheduleAnimation(); if (runningOnMainThread) m_runState = RunState::RunningOnMainThread; } // Main thread should deal with the scroll animations it started. if (sentToCompositor || runningOnMainThread) addMainThreadScrollingReason(); else removeMainThreadScrollingReason(); } }
ScrollResultOneDimensional ScrollAnimator::userScroll( ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float delta) { if (!m_scrollableArea->scrollAnimatorEnabled()) return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta); TRACE_EVENT0("blink", "ScrollAnimator::scroll"); if (granularity == ScrollByPrecisePixel) { if (hasRunningAnimation()) { abortAnimation(); resetAnimationState(); } return ScrollAnimatorBase::userScroll(orientation, granularity, step, delta); } float usedPixelDelta = computeDeltaToConsume(orientation, step * delta); FloatPoint pixelDelta = (orientation == VerticalScrollbar ? FloatPoint(0, usedPixelDelta) : FloatPoint(usedPixelDelta, 0)); FloatPoint targetPos = desiredTargetPosition(); targetPos.moveBy(pixelDelta); if (m_animationCurve) { 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) { if (registerAndScheduleAnimation()) 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); } if ((targetPos - currentPosition()).isZero()) { // Report unused delta only if there is no animation and we are not // starting one. This ensures we latch for the duration of the // animation rather than animating multiple scrollers at the same time. return ScrollResultOneDimensional(/* didScroll */ false, delta); } m_targetOffset = targetPos; m_startTime = m_timeFunction(); m_lastGranularity = granularity; if (registerAndScheduleAnimation()) m_runState = RunState::WaitingToSendToCompositor; return ScrollResultOneDimensional(/* didScroll */ true, /* unusedScrollDelta */ 0); }
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_timeFunction() - m_startTime, m_targetOffset); m_runState = RunState::WaitingToSendToCompositor; } if (!m_animationCurve) { m_animationCurve = adoptPtr(Platform::current()->compositorSupport() ->createScrollOffsetAnimationCurve( m_targetOffset, WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut, m_lastGranularity == ScrollByPixel ? WebScrollOffsetAnimationCurve::ScrollDurationInverseDelta : WebScrollOffsetAnimationCurve::ScrollDurationConstant)); m_animationCurve->setInitialValue(currentPosition()); } bool sentToCompositor = false; if (!m_scrollableArea->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) { if (registerAndScheduleAnimation()) m_runState = RunState::RunningOnMainThread; } } }