double Animation::timeToEffectChange() { ASSERT(!m_outdated); if (!hasStartTime()) return std::numeric_limits<double>::infinity(); double currentTime = calculateCurrentTime(); if (m_held) { if (limited(currentTime)) { if (m_playbackRate > 0 && m_endClip + effectEnd() > currentTime) return m_endClip + effectEnd() - currentTime; if (m_playbackRate < 0 && m_startClip <= currentTime) return m_startClip - currentTime; } return std::numeric_limits<double>::infinity(); } if (!m_content) return -currentTimeInternal() / m_playbackRate; double result = m_playbackRate > 0 ? m_content->timeToForwardsEffectChange() / m_playbackRate : m_content->timeToReverseEffectChange() / -m_playbackRate; return !hasActiveAnimationsOnCompositor() && m_content->phase() == AnimationEffect::PhaseActive ? 0 : clipTimeToEffectChange(result); }
void AnimationPlayer::unpauseInternal() { if (!m_paused) return; m_paused = false; setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); }
void AnimationTimeline::serviceAnimations(TimingUpdateReason reason) { TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations"); m_lastCurrentTimeInternal = currentTimeInternal(); m_timing->cancelWake(); WillBeHeapVector<RawPtrWillBeMember<Animation>> animations; animations.reserveInitialCapacity(m_animationsNeedingUpdate.size()); for (RefPtrWillBeMember<Animation> animation : m_animationsNeedingUpdate) animations.append(animation.get()); std::sort(animations.begin(), animations.end(), Animation::hasLowerPriority); for (Animation* animation : animations) { if (!animation->update(reason)) m_animationsNeedingUpdate.remove(animation); } ASSERT(m_outdatedAnimationCount == 0); #if ENABLE(ASSERT) for (const auto& animation : m_animationsNeedingUpdate) ASSERT(!animation->outdated()); #endif }
bool AnimationTimeline::needsAnimationTimingUpdate() { if (currentTimeInternal() == m_lastCurrentTimeInternal) return false; if (std::isnan(currentTimeInternal()) && std::isnan(m_lastCurrentTimeInternal)) return false; // We allow m_lastCurrentTimeInternal to advance here when there // are no animations to allow animations spawned during style // recalc to not invalidate this flag. if (m_animationsNeedingUpdate.isEmpty()) m_lastCurrentTimeInternal = currentTimeInternal(); return !m_animationsNeedingUpdate.isEmpty(); }
void AnimationPlayer::reverse() { if (!m_playbackRate) { return; } if (m_content) { if (m_playbackRate > 0 && currentTimeInternal() > sourceEnd()) { setCurrentTimeInternal(sourceEnd(), TimingUpdateOnDemand); ASSERT(finished()); } else if (m_playbackRate < 0 && currentTimeInternal() < 0) { setCurrentTimeInternal(0, TimingUpdateOnDemand); ASSERT(finished()); } } setPlaybackRate(-m_playbackRate); unpauseInternal(); }
double Animation::currentTime() { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (m_currentTimePending || playStateInternal() == Idle) return std::numeric_limits<double>::quiet_NaN(); return currentTimeInternal() * 1000; }
void AnimationPlayer::pauseForTesting(double pauseTime) { RELEASE_ASSERT(!paused()); setCurrentTimeInternal(pauseTime, TimingUpdateOnDemand); if (hasActiveAnimationsOnCompositor()) toAnimation(m_content.get())->pauseAnimationForTestingOnCompositor(currentTimeInternal()); m_isPausedForTesting = true; pause(); }
double AnimationPlayer::currentTime() { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); UseCounter::count(executionContext(), UseCounter::AnimationPlayerGetCurrentTime); if (m_currentTimePending || playStateInternal() == Idle) return std::numeric_limits<double>::quiet_NaN(); return currentTimeInternal() * 1000; }
void AnimationPlayer::pause() { if (m_paused) return; if (playing()) { setCompositorPending(); m_currentTimePending = true; } m_paused = true; setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); }
double AnimationPlayer::timeToEffectChange() { ASSERT(!m_outdated); if (m_held || !hasStartTime()) return std::numeric_limits<double>::infinity(); if (!m_content) return -currentTimeInternal() / m_playbackRate; if (m_playbackRate > 0) return m_content->timeToForwardsEffectChange() / m_playbackRate; return m_content->timeToReverseEffectChange() / -m_playbackRate; }
bool AnimationPlayer::maybeStartAnimationOnCompositor() { if (!canStartAnimationOnCompositor()) return false; bool reversed = m_playbackRate < 0; double startTime = timeline()->zeroTime() + startTimeInternal(); if (reversed) { startTime -= sourceEnd() / fabs(m_playbackRate); } double timeOffset = 0; if (std::isnan(startTime)) { timeOffset = reversed ? sourceEnd() - currentTimeInternal() : currentTimeInternal(); timeOffset = timeOffset / fabs(m_playbackRate); } ASSERT(m_compositorGroup != 0); return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(m_compositorGroup, startTime, timeOffset, m_playbackRate); }
bool AnimationPlayer::maybeStartAnimationOnCompositor() { if (!canStartAnimationOnCompositor()) return false; double startTime = timeline()->zeroTime() + startTimeInternal(); double timeOffset = 0; if (std::isnan(startTime)) { timeOffset = currentTimeInternal(); } return toAnimation(m_content.get())->maybeStartAnimationOnCompositor(startTime, timeOffset); }
void AnimationPlayer::pause() { if (m_paused) return; PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (playing()) { m_currentTimePending = true; } m_paused = true; setCurrentTimeInternal(currentTimeInternal(), TimingUpdateOnDemand); }
void Animation::cancel() { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (playStateInternal() == Idle) return; m_holdTime = currentTimeInternal(); m_held = true; // TODO m_playState = Idle; m_startTime = nullValue(); m_currentTimePending = false; }
double AnimationPlayer::timeToEffectChange() { ASSERT(!m_outdated); if (m_held || !hasStartTime()) return std::numeric_limits<double>::infinity(); if (!m_content) return -currentTimeInternal() / m_playbackRate; double result = m_playbackRate > 0 ? m_content->timeToForwardsEffectChange() / m_playbackRate : m_content->timeToReverseEffectChange() / -m_playbackRate; return !hasActiveAnimationsOnCompositor() && m_content->phase() == AnimationNode::PhaseActive ? 0 : result; }
void AnimationTimeline::setPlaybackRate(double playbackRate) { if (!isActive()) return; double currentTime = currentTimeInternal(); m_playbackRate = playbackRate; m_zeroTime = playbackRate == 0 ? currentTime : document()->animationClock().currentTime() - currentTime / playbackRate; m_zeroTimeInitialized = true; // Corresponding compositor animation may need to be restarted to pick up // the new playback rate. Marking the effect changed forces this. setAllCompositorPending(true); }
void AnimationPlayer::setPlaybackRateInternal(double playbackRate) { ASSERT(std::isfinite(playbackRate)); ASSERT(playbackRate != m_playbackRate); if (!finished() && !paused() && hasStartTime()) m_currentTimePending = true; double storedCurrentTime = currentTimeInternal(); if ((m_playbackRate < 0 && playbackRate >= 0) || (m_playbackRate > 0 && playbackRate <= 0)) m_finished = false; m_playbackRate = playbackRate; m_startTime = std::numeric_limits<double>::quiet_NaN(); setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); }
void Animation::notifyStartTime(double timelineTime) { if (playing()) { ASSERT(std::isnan(m_startTime)); ASSERT(m_held); if (m_playbackRate == 0) { setStartTimeInternal(timelineTime); } else { setStartTimeInternal(timelineTime + currentTimeInternal() / -m_playbackRate); } // FIXME: This avoids marking this animation as outdated needlessly when a start time // is notified, but we should refactor how outdating works to avoid this. clearOutdated(); m_currentTimePending = false; } }
void AnimationPlayer::setSource(AnimationNode* newSource) { if (m_content == newSource) return; setCompositorPending(true); double storedCurrentTime = currentTimeInternal(); if (m_content) m_content->detach(); m_content = newSource; if (newSource) { // FIXME: This logic needs to be updated once groups are implemented if (newSource->player()) newSource->player()->cancel(); newSource->attach(this); setOutdated(); } setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); }
void AnimationPlayer::cancel() { PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand); if (playStateInternal() == Idle) return; m_holdTime = currentTimeInternal(); m_held = true; // TODO m_playState = Idle; m_startTime = nullValue(); m_currentTimePending = false; // after cancelation, transitions must be downgraded or they'll fail // to be considered when retriggering themselves. This can happen if // the transition is captured through getAnimationPlayers then played. if (m_content && m_content->isAnimation()) toAnimation(m_content.get())->downgradeToNormalAnimation(); }
void Animation::setEffect(AnimationEffect* newEffect) { if (m_content == newEffect) return; PlayStateUpdateScope updateScope(*this, TimingUpdateOnDemand, SetCompositorPendingWithEffectChanged); double storedCurrentTime = currentTimeInternal(); if (m_content) m_content->detach(); m_content = newEffect; if (newEffect) { // FIXME: This logic needs to be updated once groups are implemented if (newEffect->animation()) { newEffect->animation()->cancel(); newEffect->animation()->setEffect(0); } newEffect->attach(this); setOutdated(); } setCurrentTimeInternal(storedCurrentTime, TimingUpdateOnDemand); }
void AnimationTimeline::serviceAnimations(TimingUpdateReason reason) { TRACE_EVENT0("blink", "AnimationTimeline::serviceAnimations"); m_lastCurrentTimeInternal = currentTimeInternal(); m_timing->cancelWake(); WillBeHeapVector<RawPtrWillBeMember<AnimationPlayer>> players; players.reserveInitialCapacity(m_playersNeedingUpdate.size()); for (RefPtrWillBeMember<AnimationPlayer> player : m_playersNeedingUpdate) players.append(player.get()); std::sort(players.begin(), players.end(), AnimationPlayer::hasLowerPriority); for (AnimationPlayer* player : players) { if (!player->update(reason)) m_playersNeedingUpdate.remove(player); } ASSERT(!hasOutdatedAnimationPlayer()); }
double AnimationTimeline::currentTime() { return currentTimeInternal() * 1000; }
bool AnimationPlayer::update(TimingUpdateReason reason) { if (!m_timeline) return false; updateCurrentTimingState(reason); m_outdated = false; if (m_content) { double inheritedTime = isNull(m_timeline->currentTimeInternal()) ? nullValue() : currentTimeInternal(); m_content->updateInheritedTime(inheritedTime, reason); } if (finished() && !m_finished) { if (reason == TimingUpdateForAnimationFrame && hasStartTime()) { const AtomicString& eventType = EventTypeNames::finish; if (executionContext() && hasEventListeners(eventType)) { m_pendingFinishedEvent = AnimationPlayerEvent::create(eventType, currentTime(), timeline()->currentTime()); m_pendingFinishedEvent->setTarget(this); m_pendingFinishedEvent->setCurrentTarget(this); m_timeline->document()->enqueueAnimationFrameEvent(m_pendingFinishedEvent); } m_finished = true; } } ASSERT(!m_outdated); return !m_finished || !finished(); }
bool AnimationTimeline::needsAnimationTimingUpdate() { return m_playersNeedingUpdate.size() && currentTimeInternal() != m_lastCurrentTimeInternal; }
double AnimationTimeline::currentTime(bool& isNull) { return currentTimeInternal(isNull) * 1000; }
double AnimationTimeline::effectiveTime() { double time = currentTimeInternal(); return std::isnan(time) ? 0 : time; }
double AnimationTimeline::currentTimeInternal() { bool isNull; return currentTimeInternal(isNull); }
double AnimationPlayer::currentTime() { if (m_currentTimePending) return std::numeric_limits<double>::quiet_NaN(); return currentTimeInternal() * 1000; }