blob: 68ae706917453283778525c5599924a6b8e7fffc [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/scroll/ProgrammaticScrollAnimator.h"
#include "platform/animation/CompositorAnimation.h"
#include "platform/animation/CompositorScrollOffsetAnimationCurve.h"
#include "platform/geometry/IntSize.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/scroll/ScrollableArea.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCompositorSupport.h"
#include "wtf/PtrUtil.h"
#include <memory>
namespace blink {
ProgrammaticScrollAnimator::ProgrammaticScrollAnimator(
ScrollableArea* scrollableArea)
: m_scrollableArea(scrollableArea), m_startTime(0.0) {}
ProgrammaticScrollAnimator::~ProgrammaticScrollAnimator() {}
void ProgrammaticScrollAnimator::resetAnimationState() {
ScrollAnimatorCompositorCoordinator::resetAnimationState();
m_animationCurve.reset();
m_startTime = 0.0;
}
void ProgrammaticScrollAnimator::notifyOffsetChanged(
const ScrollOffset& offset) {
scrollOffsetChanged(offset, ProgrammaticScroll);
}
void ProgrammaticScrollAnimator::scrollToOffsetWithoutAnimation(
const ScrollOffset& offset) {
cancelAnimation();
notifyOffsetChanged(offset);
}
void ProgrammaticScrollAnimator::animateToOffset(const ScrollOffset& offset) {
if (m_runState == RunState::PostAnimationCleanup)
resetAnimationState();
m_startTime = 0.0;
m_targetOffset = offset;
m_animationCurve = CompositorScrollOffsetAnimationCurve::create(
compositorOffsetFromBlinkOffset(m_targetOffset),
CompositorScrollOffsetAnimationCurve::ScrollDurationDeltaBased);
m_scrollableArea->registerForAnimation();
if (!m_scrollableArea->scheduleAnimation()) {
resetAnimationState();
notifyOffsetChanged(offset);
}
m_runState = RunState::WaitingToSendToCompositor;
}
void ProgrammaticScrollAnimator::cancelAnimation() {
ASSERT(m_runState != RunState::RunningOnCompositorButNeedsUpdate);
ScrollAnimatorCompositorCoordinator::cancelAnimation();
}
void ProgrammaticScrollAnimator::tickAnimation(double monotonicTime) {
if (m_runState != RunState::RunningOnMainThread)
return;
if (!m_startTime)
m_startTime = monotonicTime;
double elapsedTime = monotonicTime - m_startTime;
bool isFinished = (elapsedTime > m_animationCurve->duration());
ScrollOffset offset =
blinkOffsetFromCompositorOffset(m_animationCurve->getValue(elapsedTime));
notifyOffsetChanged(offset);
if (isFinished) {
m_runState = RunState::PostAnimationCleanup;
} else if (!m_scrollableArea->scheduleAnimation()) {
notifyOffsetChanged(offset);
resetAnimationState();
}
}
void ProgrammaticScrollAnimator::updateCompositorAnimations() {
if (m_runState == RunState::PostAnimationCleanup) {
// No special cleanup, simply reset animation state. We have this state
// here because the state machine is shared with ScrollAnimator which
// has to do some cleanup that requires the compositing state to be clean.
return resetAnimationState();
}
if (m_compositorAnimationId && m_runState != RunState::RunningOnCompositor) {
// 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);
removeAnimation();
m_compositorAnimationId = 0;
m_compositorAnimationGroupId = 0;
if (m_runState == RunState::WaitingToCancelOnCompositor) {
resetAnimationState();
return;
}
}
if (m_runState == RunState::WaitingToSendToCompositor) {
if (!m_compositorAnimationAttachedToElementId)
reattachCompositorPlayerIfNeeded(
getScrollableArea()->compositorAnimationTimeline());
bool sentToCompositor = false;
if (!m_scrollableArea->shouldScrollOnMainThread()) {
std::unique_ptr<CompositorAnimation> animation =
CompositorAnimation::create(
*m_animationCurve, CompositorTargetProperty::SCROLL_OFFSET, 0, 0);
int animationId = animation->id();
int animationGroupId = animation->group();
if (addAnimation(std::move(animation))) {
sentToCompositor = true;
m_runState = RunState::RunningOnCompositor;
m_compositorAnimationId = animationId;
m_compositorAnimationGroupId = animationGroupId;
}
}
if (!sentToCompositor) {
m_runState = RunState::RunningOnMainThread;
m_animationCurve->setInitialValue(
compositorOffsetFromBlinkOffset(m_scrollableArea->scrollOffset()));
if (!m_scrollableArea->scheduleAnimation()) {
notifyOffsetChanged(m_targetOffset);
resetAnimationState();
}
}
}
}
void ProgrammaticScrollAnimator::layerForCompositedScrollingDidChange(
CompositorAnimationTimeline* timeline) {
reattachCompositorPlayerIfNeeded(timeline);
// If the composited scrolling layer is lost during a composited animation,
// continue the animation on the main thread.
if (m_runState == RunState::RunningOnCompositor &&
!m_scrollableArea->layerForScrolling()) {
m_runState = RunState::RunningOnMainThread;
m_compositorAnimationId = 0;
m_compositorAnimationGroupId = 0;
m_animationCurve->setInitialValue(
compositorOffsetFromBlinkOffset(m_scrollableArea->scrollOffset()));
m_scrollableArea->registerForAnimation();
if (!m_scrollableArea->scheduleAnimation()) {
resetAnimationState();
notifyOffsetChanged(m_targetOffset);
}
}
}
void ProgrammaticScrollAnimator::notifyCompositorAnimationFinished(
int groupId) {
ASSERT(m_runState != RunState::RunningOnCompositorButNeedsUpdate);
ScrollAnimatorCompositorCoordinator::compositorAnimationFinished(groupId);
}
DEFINE_TRACE(ProgrammaticScrollAnimator) {
visitor->trace(m_scrollableArea);
ScrollAnimatorCompositorCoordinator::trace(visitor);
}
} // namespace blink