Commit 59f06466 authored by Stephen McGruer's avatar Stephen McGruer Committed by Commit Bot

Reland "Web Animations: use WTF::Optional for Animation::start_time_"

This is a reland of c838bf40

The original CL contained two subtle bugs from a comparison of
compositor_state_->start_time to start_time_. Previously if both were
'null' (represented by NaN), the comparison would still say they were
inequal because NaN != NaN. The original CL 'fixed' those comparisons,
but it turns out the behavior was intentional. This version of the CL
restores the intentional behavior, adds a test for it, and documents it.

Bug: 791086, 819591
Change-Id: I6587d028e66c6f6a9213b60f8d5f51b1dac3f899
Reviewed-on: https://chromium-review.googlesource.com/956280Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Commit-Queue: Stephen McGruer <smcgruer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#544679}
parent c5f34793
...@@ -117,7 +117,7 @@ Animation::Animation(ExecutionContext* execution_context, ...@@ -117,7 +117,7 @@ Animation::Animation(ExecutionContext* execution_context,
: ContextLifecycleObserver(execution_context), : ContextLifecycleObserver(execution_context),
play_state_(kIdle), play_state_(kIdle),
playback_rate_(1), playback_rate_(1),
start_time_(NullValue()), start_time_(),
hold_time_(0), hold_time_(0),
sequence_number_(NextSequenceNumber()), sequence_number_(NextSequenceNumber()),
content_(content), content_(content),
...@@ -200,14 +200,14 @@ void Animation::SetCurrentTimeInternal(double new_current_time, ...@@ -200,14 +200,14 @@ void Animation::SetCurrentTimeInternal(double new_current_time,
bool old_held = held_; bool old_held = held_;
bool outdated = false; bool outdated = false;
bool is_limited = Limited(new_current_time); bool is_limited = Limited(new_current_time);
held_ = paused_ || !playback_rate_ || is_limited || std::isnan(start_time_); held_ = paused_ || !playback_rate_ || is_limited || !start_time_;
if (held_) { if (held_) {
if (!old_held || hold_time_ != new_current_time) if (!old_held || hold_time_ != new_current_time)
outdated = true; outdated = true;
hold_time_ = new_current_time; hold_time_ = new_current_time;
if (paused_ || !playback_rate_) { if (paused_ || !playback_rate_) {
start_time_ = NullValue(); start_time_ = WTF::nullopt;
} else if (is_limited && std::isnan(start_time_) && } else if (is_limited && !start_time_ &&
reason == kTimingUpdateForAnimationFrame) { reason == kTimingUpdateForAnimationFrame) {
start_time_ = CalculateStartTime(new_current_time); start_time_ = CalculateStartTime(new_current_time);
} }
...@@ -229,7 +229,7 @@ void Animation::UpdateCurrentTimingState(TimingUpdateReason reason) { ...@@ -229,7 +229,7 @@ void Animation::UpdateCurrentTimingState(TimingUpdateReason reason) {
return; return;
if (held_) { if (held_) {
double new_current_time = hold_time_; double new_current_time = hold_time_;
if (play_state_ == kFinished && !IsNull(start_time_) && timeline_) { if (play_state_ == kFinished && start_time_ && timeline_) {
// Add hystersis due to floating point error accumulation // Add hystersis due to floating point error accumulation
if (!Limited(CalculateCurrentTime() + 0.001 * playback_rate_)) { if (!Limited(CalculateCurrentTime() + 0.001 * playback_rate_)) {
// The current time became unlimited, eg. due to a backwards // The current time became unlimited, eg. due to a backwards
...@@ -250,13 +250,14 @@ void Animation::UpdateCurrentTimingState(TimingUpdateReason reason) { ...@@ -250,13 +250,14 @@ void Animation::UpdateCurrentTimingState(TimingUpdateReason reason) {
} }
double Animation::startTime(bool& is_null) const { double Animation::startTime(bool& is_null) const {
double result = startTime(); WTF::Optional<double> result = startTime();
is_null = std::isnan(result); is_null = !result;
return result; return result.value_or(0);
} }
double Animation::startTime() const { WTF::Optional<double> Animation::startTime() const {
return start_time_ * 1000; return start_time_ ? WTF::make_optional(start_time_.value() * 1000)
: WTF::nullopt;
} }
double Animation::currentTime(bool& is_null) { double Animation::currentTime(bool& is_null) {
...@@ -268,7 +269,7 @@ double Animation::currentTime(bool& is_null) { ...@@ -268,7 +269,7 @@ double Animation::currentTime(bool& is_null) {
double Animation::currentTime() { double Animation::currentTime() {
PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand); PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
if (PlayStateInternal() == kIdle || (!held_ && !HasStartTime())) if (PlayStateInternal() == kIdle || (!held_ && !start_time_))
return std::numeric_limits<double>::quiet_NaN(); return std::numeric_limits<double>::quiet_NaN();
return CurrentTimeInternal() * 1000; return CurrentTimeInternal() * 1000;
...@@ -292,7 +293,7 @@ double Animation::UnlimitedCurrentTimeInternal() const { ...@@ -292,7 +293,7 @@ double Animation::UnlimitedCurrentTimeInternal() const {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
CurrentTimeInternal(); CurrentTimeInternal();
#endif #endif
return PlayStateInternal() == kPaused || IsNull(start_time_) return PlayStateInternal() == kPaused || !start_time_
? CurrentTimeInternal() ? CurrentTimeInternal()
: CalculateCurrentTime(); : CalculateCurrentTime();
} }
...@@ -309,7 +310,8 @@ bool Animation::PreCommit( ...@@ -309,7 +310,8 @@ bool Animation::PreCommit(
(Paused() || compositor_state_->playback_rate != playback_rate_); (Paused() || compositor_state_->playback_rate != playback_rate_);
bool hard_change = bool hard_change =
compositor_state_ && (compositor_state_->effect_changed || compositor_state_ && (compositor_state_->effect_changed ||
compositor_state_->start_time != start_time_); compositor_state_->start_time != start_time_ ||
!compositor_state_->start_time || !start_time_);
// FIXME: softChange && !hardChange should generate a Pause/ThenStart, // FIXME: softChange && !hardChange should generate a Pause/ThenStart,
// not a Cancel, but we can't communicate these to the compositor yet. // not a Cancel, but we can't communicate these to the compositor yet.
...@@ -329,7 +331,7 @@ bool Animation::PreCommit( ...@@ -329,7 +331,7 @@ bool Animation::PreCommit(
compositor_state_ = nullptr; compositor_state_ = nullptr;
} }
DCHECK(!compositor_state_ || !std::isnan(compositor_state_->start_time)); DCHECK(!compositor_state_ || compositor_state_->start_time);
if (!should_start) { if (!should_start) {
current_time_pending_ = false; current_time_pending_ = false;
...@@ -382,17 +384,18 @@ void Animation::PostCommit(double timeline_time) { ...@@ -382,17 +384,18 @@ void Animation::PostCommit(double timeline_time) {
switch (compositor_state_->pending_action) { switch (compositor_state_->pending_action) {
case kStart: case kStart:
if (!std::isnan(compositor_state_->start_time)) { if (compositor_state_->start_time) {
DCHECK_EQ(start_time_, compositor_state_->start_time); DCHECK_EQ(start_time_.value(), compositor_state_->start_time.value());
compositor_state_->pending_action = kNone; compositor_state_->pending_action = kNone;
} }
break; break;
case kPause: case kPause:
case kPauseThenStart: case kPauseThenStart:
DCHECK(std::isnan(start_time_)); DCHECK(!start_time_);
compositor_state_->pending_action = kNone; compositor_state_->pending_action = kNone;
SetCurrentTimeInternal( SetCurrentTimeInternal(
(timeline_time - compositor_state_->start_time) * playback_rate_, (timeline_time - compositor_state_->start_time.value()) *
playback_rate_,
kTimingUpdateForAnimationFrame); kTimingUpdateForAnimationFrame);
current_time_pending_ = false; current_time_pending_ = false;
break; break;
...@@ -407,12 +410,14 @@ void Animation::NotifyCompositorStartTime(double timeline_time) { ...@@ -407,12 +410,14 @@ void Animation::NotifyCompositorStartTime(double timeline_time) {
if (compositor_state_) { if (compositor_state_) {
DCHECK_EQ(compositor_state_->pending_action, kStart); DCHECK_EQ(compositor_state_->pending_action, kStart);
DCHECK(std::isnan(compositor_state_->start_time)); DCHECK(!compositor_state_->start_time);
double initial_compositor_hold_time = compositor_state_->hold_time; double initial_compositor_hold_time = compositor_state_->hold_time;
compositor_state_->pending_action = kNone; compositor_state_->pending_action = kNone;
// TODO(crbug.com/791086): Determine whether this can ever be null.
double start_time = timeline_time + CurrentTimeInternal() / -playback_rate_;
compositor_state_->start_time = compositor_state_->start_time =
timeline_time + CurrentTimeInternal() / -playback_rate_; IsNull(start_time) ? WTF::nullopt : WTF::make_optional(start_time);
if (start_time_ == timeline_time) { if (start_time_ == timeline_time) {
// The start time was set to the incoming compositor start time. // The start time was set to the incoming compositor start time.
...@@ -423,8 +428,7 @@ void Animation::NotifyCompositorStartTime(double timeline_time) { ...@@ -423,8 +428,7 @@ void Animation::NotifyCompositorStartTime(double timeline_time) {
return; return;
} }
if (!std::isnan(start_time_) || if (start_time_ || CurrentTimeInternal() != initial_compositor_hold_time) {
CurrentTimeInternal() != initial_compositor_hold_time) {
// A new start time or current time was set while starting. // A new start time or current time was set while starting.
SetCompositorPending(true); SetCompositorPending(true);
return; return;
...@@ -436,7 +440,7 @@ void Animation::NotifyCompositorStartTime(double timeline_time) { ...@@ -436,7 +440,7 @@ void Animation::NotifyCompositorStartTime(double timeline_time) {
void Animation::NotifyStartTime(double timeline_time) { void Animation::NotifyStartTime(double timeline_time) {
if (Playing()) { if (Playing()) {
DCHECK(std::isnan(start_time_)); DCHECK(!start_time_);
DCHECK(held_); DCHECK(held_);
if (playback_rate_ == 0) { if (playback_rate_ == 0) {
...@@ -465,17 +469,22 @@ bool Animation::Affects(const Element& element, ...@@ -465,17 +469,22 @@ bool Animation::Affects(const Element& element,
effect->Affects(PropertyHandle(property)); effect->Affects(PropertyHandle(property));
} }
double Animation::CalculateStartTime(double current_time) const { WTF::Optional<double> Animation::CalculateStartTime(double current_time) const {
return timeline_->EffectiveTime() - current_time / playback_rate_; WTF::Optional<double> start_time =
timeline_->EffectiveTime() - current_time / playback_rate_;
DCHECK(!IsNull(start_time.value()));
return start_time;
} }
double Animation::CalculateCurrentTime() const { double Animation::CalculateCurrentTime() const {
// TODO(crbug.com/818196): By spec, this should be unresolved, not 0. // TODO(crbug.com/818196): By spec, this should be unresolved, not 0.
if (IsNull(start_time_) || !timeline_) if (!start_time_ || !timeline_)
return 0; return 0;
return (timeline_->EffectiveTime() - start_time_) * playback_rate_; return (timeline_->EffectiveTime() - start_time_.value()) * playback_rate_;
} }
// TODO(crbug.com/771722): This doesn't handle anim.startTime = null; we just
// silently convert that to anim.startTime = 0.
void Animation::setStartTime(double start_time, bool is_null) { void Animation::setStartTime(double start_time, bool is_null) {
PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand); PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
...@@ -488,12 +497,12 @@ void Animation::setStartTime(double start_time, bool is_null) { ...@@ -488,12 +497,12 @@ void Animation::setStartTime(double start_time, bool is_null) {
SetStartTimeInternal(start_time / 1000); SetStartTimeInternal(start_time / 1000);
} }
void Animation::SetStartTimeInternal(double new_start_time) { void Animation::SetStartTimeInternal(WTF::Optional<double> new_start_time) {
DCHECK(!paused_); DCHECK(!paused_);
DCHECK(std::isfinite(new_start_time)); DCHECK(new_start_time.has_value());
DCHECK_NE(new_start_time, start_time_); DCHECK(new_start_time != start_time_);
bool had_start_time = HasStartTime(); bool had_start_time = start_time_.has_value();
double previous_current_time = CurrentTimeInternal(); double previous_current_time = CurrentTimeInternal();
start_time_ = new_start_time; start_time_ = new_start_time;
if (held_ && playback_rate_) { if (held_ && playback_rate_) {
...@@ -565,12 +574,12 @@ Animation::AnimationPlayState Animation::PlayStateInternal() const { ...@@ -565,12 +574,12 @@ Animation::AnimationPlayState Animation::PlayStateInternal() const {
return play_state_; return play_state_;
} }
Animation::AnimationPlayState Animation::CalculatePlayState() { Animation::AnimationPlayState Animation::CalculatePlayState() const {
if (paused_ && !current_time_pending_) if (paused_ && !current_time_pending_)
return kPaused; return kPaused;
if (play_state_ == kIdle) if (play_state_ == kIdle)
return kIdle; return kIdle;
if (current_time_pending_ || (IsNull(start_time_) && playback_rate_ != 0)) if (current_time_pending_ || (!start_time_ && playback_rate_ != 0))
return kPending; return kPending;
if (Limited()) if (Limited())
return kFinished; return kFinished;
...@@ -631,7 +640,7 @@ void Animation::play(ExceptionState& exception_state) { ...@@ -631,7 +640,7 @@ void Animation::play(ExceptionState& exception_state) {
} }
if (!Playing()) { if (!Playing()) {
start_time_ = NullValue(); start_time_ = WTF::nullopt;
} }
if (PlayStateInternal() == kIdle) { if (PlayStateInternal() == kIdle) {
...@@ -644,11 +653,11 @@ void Animation::play(ExceptionState& exception_state) { ...@@ -644,11 +653,11 @@ void Animation::play(ExceptionState& exception_state) {
UnpauseInternal(); UnpauseInternal();
if (playback_rate_ > 0 && (current_time < 0 || current_time >= EffectEnd())) { if (playback_rate_ > 0 && (current_time < 0 || current_time >= EffectEnd())) {
start_time_ = NullValue(); start_time_ = WTF::nullopt;
SetCurrentTimeInternal(0, kTimingUpdateOnDemand); SetCurrentTimeInternal(0, kTimingUpdateOnDemand);
} else if (playback_rate_ < 0 && } else if (playback_rate_ < 0 &&
(current_time <= 0 || current_time > EffectEnd())) { (current_time <= 0 || current_time > EffectEnd())) {
start_time_ = NullValue(); start_time_ = WTF::nullopt;
SetCurrentTimeInternal(EffectEnd(), kTimingUpdateOnDemand); SetCurrentTimeInternal(EffectEnd(), kTimingUpdateOnDemand);
} }
} }
...@@ -753,12 +762,12 @@ void Animation::setPlaybackRate(double playback_rate) { ...@@ -753,12 +762,12 @@ void Animation::setPlaybackRate(double playback_rate) {
PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand); PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
double start_time_before = start_time_; WTF::Optional<double> start_time_before = start_time_;
SetPlaybackRateInternal(playback_rate); SetPlaybackRateInternal(playback_rate);
// Adds a UseCounter to check if setting playbackRate causes a compensatory // Adds a UseCounter to check if setting playbackRate causes a compensatory
// seek forcing a change in start_time_ // seek forcing a change in start_time_
if (!std::isnan(start_time_before) && start_time_ != start_time_before && if (start_time_before && start_time_ != start_time_before &&
play_state_ != kFinished) { play_state_ != kFinished) {
UseCounter::Count(GetExecutionContext(), UseCounter::Count(GetExecutionContext(),
WebFeature::kAnimationSetPlaybackRateCompensatorySeek); WebFeature::kAnimationSetPlaybackRateCompensatorySeek);
...@@ -769,7 +778,7 @@ void Animation::SetPlaybackRateInternal(double playback_rate) { ...@@ -769,7 +778,7 @@ void Animation::SetPlaybackRateInternal(double playback_rate) {
DCHECK(std::isfinite(playback_rate)); DCHECK(std::isfinite(playback_rate));
DCHECK_NE(playback_rate, playback_rate_); DCHECK_NE(playback_rate, playback_rate_);
if (!Limited() && !Paused() && HasStartTime()) if (!Limited() && !Paused() && start_time_)
current_time_pending_ = true; current_time_pending_ = true;
double stored_current_time = CurrentTimeInternal(); double stored_current_time = CurrentTimeInternal();
...@@ -778,7 +787,7 @@ void Animation::SetPlaybackRateInternal(double playback_rate) { ...@@ -778,7 +787,7 @@ void Animation::SetPlaybackRateInternal(double playback_rate) {
finished_ = false; finished_ = false;
playback_rate_ = playback_rate; playback_rate_ = playback_rate;
start_time_ = std::numeric_limits<double>::quiet_NaN(); start_time_ = WTF::nullopt;
SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand); SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand);
} }
...@@ -858,7 +867,7 @@ Animation::CheckCanStartAnimationOnCompositorInternal( ...@@ -858,7 +867,7 @@ Animation::CheckCanStartAnimationOnCompositorInternal(
// If the optional element id set has no value we must be in SPv1 mode in // If the optional element id set has no value we must be in SPv1 mode in
// which case we trust the compositing logic will create a layer if needed. // which case we trust the compositing logic will create a layer if needed.
if (composited_element_ids.has_value()) { if (composited_element_ids) {
DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled()); DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
Element* target_element = Element* target_element =
ToKeyframeEffectReadOnly(content_.Get())->target(); ToKeyframeEffectReadOnly(content_.Get())->target();
...@@ -899,17 +908,19 @@ void Animation::StartAnimationOnCompositor( ...@@ -899,17 +908,19 @@ void Animation::StartAnimationOnCompositor(
bool reversed = playback_rate_ < 0; bool reversed = playback_rate_ < 0;
double start_time = TimelineInternal()->ZeroTime() + StartTimeInternal(); // TODO(crbug.com/791086): Make StartAnimationOnCompositor use WTF::Optional.
if (reversed) { double start_time = NullValue();
start_time -= EffectEnd() / fabs(playback_rate_);
}
double time_offset = 0; double time_offset = 0;
if (std::isnan(start_time)) { if (start_time_) {
start_time = TimelineInternal()->ZeroTime() + start_time_.value();
if (reversed)
start_time -= EffectEnd() / fabs(playback_rate_);
} else {
time_offset = time_offset =
reversed ? EffectEnd() - CurrentTimeInternal() : CurrentTimeInternal(); reversed ? EffectEnd() - CurrentTimeInternal() : CurrentTimeInternal();
time_offset = time_offset / fabs(playback_rate_); time_offset = time_offset / fabs(playback_rate_);
} }
DCHECK_NE(compositor_group_, 0); DCHECK_NE(compositor_group_, 0);
ToKeyframeEffectReadOnly(content_.Get()) ToKeyframeEffectReadOnly(content_.Get())
->StartAnimationOnCompositor(compositor_group_, start_time, time_offset, ->StartAnimationOnCompositor(compositor_group_, start_time, time_offset,
...@@ -928,9 +939,16 @@ void Animation::SetCompositorPending(bool effect_changed) { ...@@ -928,9 +939,16 @@ void Animation::SetCompositorPending(bool effect_changed) {
if (compositor_pending_ || is_paused_for_testing_) { if (compositor_pending_ || is_paused_for_testing_) {
return; return;
} }
// In general, we need to update the compositor-side if anything has changed
// on the blink version of the animation. There is also an edge case; if
// neither the compositor nor blink side have a start time we still have to
// sync them. This can happen if the blink side animation was started, the
// compositor side hadn't started on its side yet, and then the blink side
// start time was cleared (e.g. by setting current time).
if (!compositor_state_ || compositor_state_->effect_changed || if (!compositor_state_ || compositor_state_->effect_changed ||
compositor_state_->playback_rate != playback_rate_ || compositor_state_->playback_rate != playback_rate_ ||
compositor_state_->start_time != start_time_) { compositor_state_->start_time != start_time_ ||
!compositor_state_->start_time || !start_time_) {
compositor_pending_ = true; compositor_pending_ = true;
TimelineInternal()->GetDocument()->GetPendingAnimations().Add(this); TimelineInternal()->GetDocument()->GetPendingAnimations().Add(this);
} }
...@@ -986,7 +1004,7 @@ bool Animation::Update(TimingUpdateReason reason) { ...@@ -986,7 +1004,7 @@ bool Animation::Update(TimingUpdateReason reason) {
} }
if ((idle || Limited()) && !finished_) { if ((idle || Limited()) && !finished_) {
if (reason == kTimingUpdateForAnimationFrame && (idle || HasStartTime())) { if (reason == kTimingUpdateForAnimationFrame && (idle || start_time_)) {
if (idle) { if (idle) {
const AtomicString& event_type = EventTypeNames::cancel; const AtomicString& event_type = EventTypeNames::cancel;
if (GetExecutionContext() && HasEventListeners(event_type)) { if (GetExecutionContext() && HasEventListeners(event_type)) {
...@@ -1032,12 +1050,12 @@ void Animation::SpecifiedTimingChanged() { ...@@ -1032,12 +1050,12 @@ void Animation::SpecifiedTimingChanged() {
} }
bool Animation::IsEventDispatchAllowed() const { bool Animation::IsEventDispatchAllowed() const {
return Paused() || HasStartTime(); return Paused() || start_time_;
} }
double Animation::TimeToEffectChange() { double Animation::TimeToEffectChange() {
DCHECK(!outdated_); DCHECK(!outdated_);
if (!HasStartTime() || held_) if (!start_time_ || held_)
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
if (!content_) if (!content_)
...@@ -1061,7 +1079,7 @@ void Animation::cancel() { ...@@ -1061,7 +1079,7 @@ void Animation::cancel() {
held_ = false; held_ = false;
paused_ = false; paused_ = false;
play_state_ = kIdle; play_state_ = kIdle;
start_time_ = NullValue(); start_time_ = WTF::nullopt;
current_time_pending_ = false; current_time_pending_ = false;
ForceServiceOnNextFrame(); ForceServiceOnNextFrame();
} }
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include "platform/animation/CompositorAnimationDelegate.h" #include "platform/animation/CompositorAnimationDelegate.h"
#include "platform/graphics/CompositorElementId.h" #include "platform/graphics/CompositorElementId.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
#include "platform/wtf/Optional.h"
namespace blink { namespace blink {
...@@ -157,13 +158,10 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData, ...@@ -157,13 +158,10 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
const DocumentTimeline* TimelineInternal() const { return timeline_; } const DocumentTimeline* TimelineInternal() const { return timeline_; }
DocumentTimeline* TimelineInternal() { return timeline_; } DocumentTimeline* TimelineInternal() { return timeline_; }
double CalculateStartTime(double current_time) const;
bool HasStartTime() const { return !IsNull(start_time_); }
double startTime(bool& is_null) const; double startTime(bool& is_null) const;
double startTime() const; WTF::Optional<double> startTime() const;
double StartTimeInternal() const { return start_time_; } WTF::Optional<double> StartTimeInternal() const { return start_time_; }
void setStartTime(double, bool is_null); void setStartTime(double, bool is_null);
void SetStartTimeInternal(double);
const AnimationEffectReadOnly* effect() const { return content_.Get(); } const AnimationEffectReadOnly* effect() const { return content_.Get(); }
AnimationEffectReadOnly* effect() { return content_.Get(); } AnimationEffectReadOnly* effect() { return content_.Get(); }
...@@ -228,6 +226,8 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData, ...@@ -228,6 +226,8 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
void Trace(blink::Visitor*) override; void Trace(blink::Visitor*) override;
bool CompositorPendingForTesting() const { return compositor_pending_; }
protected: protected:
DispatchEventResult DispatchEventInternal(Event*) override; DispatchEventResult DispatchEventInternal(Event*) override;
void AddedEventListener(const AtomicString& event_type, void AddedEventListener(const AtomicString& event_type,
...@@ -242,11 +242,13 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData, ...@@ -242,11 +242,13 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
double EffectEnd() const; double EffectEnd() const;
bool Limited(double current_time) const; bool Limited(double current_time) const;
AnimationPlayState CalculatePlayState(); AnimationPlayState CalculatePlayState() const;
WTF::Optional<double> CalculateStartTime(double current_time) const;
double CalculateCurrentTime() const; double CalculateCurrentTime() const;
void UnpauseInternal(); void UnpauseInternal();
void SetPlaybackRateInternal(double); void SetPlaybackRateInternal(double);
void SetStartTimeInternal(WTF::Optional<double>);
void UpdateCurrentTimingState(TimingUpdateReason); void UpdateCurrentTimingState(TimingUpdateReason);
void BeginUpdatingState(); void BeginUpdatingState();
...@@ -276,7 +278,7 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData, ...@@ -276,7 +278,7 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
AnimationPlayState play_state_; AnimationPlayState play_state_;
double playback_rate_; double playback_rate_;
double start_time_; WTF::Optional<double> start_time_;
double hold_time_; double hold_time_;
unsigned sequence_number_; unsigned sequence_number_;
...@@ -317,7 +319,7 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData, ...@@ -317,7 +319,7 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
playback_rate(animation.playback_rate_), playback_rate(animation.playback_rate_),
effect_changed(false), effect_changed(false),
pending_action(kStart) {} pending_action(kStart) {}
double start_time; WTF::Optional<double> start_time;
double hold_time; double hold_time;
double playback_rate; double playback_rate;
bool effect_changed; bool effect_changed;
......
...@@ -108,8 +108,7 @@ TEST_F(AnimationAnimationTest, InitialState) { ...@@ -108,8 +108,7 @@ TEST_F(AnimationAnimationTest, InitialState) {
EXPECT_EQ(0, animation->CurrentTimeInternal()); EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Paused()); EXPECT_FALSE(animation->Paused());
EXPECT_EQ(1, animation->playbackRate()); EXPECT_EQ(1, animation->playbackRate());
EXPECT_FALSE(animation->HasStartTime()); EXPECT_FALSE(animation->StartTimeInternal());
EXPECT_TRUE(IsNull(animation->StartTimeInternal()));
StartTimeline(); StartTimeline();
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal()); EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
...@@ -118,7 +117,6 @@ TEST_F(AnimationAnimationTest, InitialState) { ...@@ -118,7 +117,6 @@ TEST_F(AnimationAnimationTest, InitialState) {
EXPECT_FALSE(animation->Paused()); EXPECT_FALSE(animation->Paused());
EXPECT_EQ(1, animation->playbackRate()); EXPECT_EQ(1, animation->playbackRate());
EXPECT_EQ(0, animation->StartTimeInternal()); EXPECT_EQ(0, animation->StartTimeInternal());
EXPECT_TRUE(animation->HasStartTime());
} }
TEST_F(AnimationAnimationTest, CurrentTimeDoesNotSetOutdated) { TEST_F(AnimationAnimationTest, CurrentTimeDoesNotSetOutdated) {
...@@ -253,7 +251,7 @@ TEST_F(AnimationAnimationTest, StartTimePauseFinish) { ...@@ -253,7 +251,7 @@ TEST_F(AnimationAnimationTest, StartTimePauseFinish) {
NonThrowableExceptionState exception_state; NonThrowableExceptionState exception_state;
animation->pause(); animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal()); EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
animation->finish(exception_state); animation->finish(exception_state);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal()); EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(-30000, animation->startTime()); EXPECT_EQ(-30000, animation->startTime());
...@@ -274,13 +272,13 @@ TEST_F(AnimationAnimationTest, StartTimeFinishPause) { ...@@ -274,13 +272,13 @@ TEST_F(AnimationAnimationTest, StartTimeFinishPause) {
animation->finish(exception_state); animation->finish(exception_state);
EXPECT_EQ(-30 * 1000, animation->startTime()); EXPECT_EQ(-30 * 1000, animation->startTime());
animation->pause(); animation->pause();
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
} }
TEST_F(AnimationAnimationTest, StartTimeWithZeroPlaybackRate) { TEST_F(AnimationAnimationTest, StartTimeWithZeroPlaybackRate) {
animation->setPlaybackRate(0); animation->setPlaybackRate(0);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal()); EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
SimulateFrame(10); SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal()); EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
} }
...@@ -733,11 +731,11 @@ TEST_F(AnimationAnimationTest, PlayAfterCancel) { ...@@ -733,11 +731,11 @@ TEST_F(AnimationAnimationTest, PlayAfterCancel) {
animation->cancel(); animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal()); EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime())); EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
animation->play(); animation->play();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal()); EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime()); EXPECT_EQ(0, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
SimulateFrame(10); SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal()); EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime()); EXPECT_EQ(0, animation->currentTime());
...@@ -751,11 +749,11 @@ TEST_F(AnimationAnimationTest, PlayBackwardsAfterCancel) { ...@@ -751,11 +749,11 @@ TEST_F(AnimationAnimationTest, PlayBackwardsAfterCancel) {
animation->cancel(); animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal()); EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime())); EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
animation->play(); animation->play();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal()); EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime()); EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
SimulateFrame(10); SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal()); EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime()); EXPECT_EQ(30 * 1000, animation->currentTime());
...@@ -766,11 +764,11 @@ TEST_F(AnimationAnimationTest, ReverseAfterCancel) { ...@@ -766,11 +764,11 @@ TEST_F(AnimationAnimationTest, ReverseAfterCancel) {
animation->cancel(); animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal()); EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime())); EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
animation->reverse(); animation->reverse();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal()); EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime()); EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
SimulateFrame(10); SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal()); EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime()); EXPECT_EQ(30 * 1000, animation->currentTime());
...@@ -782,7 +780,7 @@ TEST_F(AnimationAnimationTest, FinishAfterCancel) { ...@@ -782,7 +780,7 @@ TEST_F(AnimationAnimationTest, FinishAfterCancel) {
animation->cancel(); animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal()); EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime())); EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
animation->finish(exception_state); animation->finish(exception_state);
EXPECT_EQ(30000, animation->currentTime()); EXPECT_EQ(30000, animation->currentTime());
EXPECT_EQ(-30000, animation->startTime()); EXPECT_EQ(-30000, animation->startTime());
...@@ -793,11 +791,11 @@ TEST_F(AnimationAnimationTest, PauseAfterCancel) { ...@@ -793,11 +791,11 @@ TEST_F(AnimationAnimationTest, PauseAfterCancel) {
animation->cancel(); animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal()); EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime())); EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
animation->pause(); animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal()); EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime()); EXPECT_EQ(0, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime())); EXPECT_FALSE(animation->startTime());
} }
TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) { TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) {
...@@ -841,4 +839,106 @@ TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) { ...@@ -841,4 +839,106 @@ TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) {
.Ok()); .Ok());
} }
// Regression test for http://crbug.com/819591 . If a compositable animation is
// played and then paused before any start time is set (either blink or
// compositor side), the pausing must still set compositor pending or the pause
// won't be synced.
TEST_F(AnimationAnimationTest, SetCompositorPendingWithUnresolvedStartTimes) {
// Get rid of the default animation.
animation->cancel();
EnableCompositing();
SetBodyInnerHTML("<div id='target'></div>");
// Create a compositable animation; in this case opacity from 1 to 0.
Timing timing;
timing.iteration_duration = 30;
scoped_refptr<StringKeyframe> start_keyframe = StringKeyframe::Create();
start_keyframe->SetCSSPropertyValue(
CSSPropertyOpacity, "1.0", SecureContextMode::kInsecureContext, nullptr);
scoped_refptr<StringKeyframe> end_keyframe = StringKeyframe::Create();
end_keyframe->SetCSSPropertyValue(
CSSPropertyOpacity, "0.0", SecureContextMode::kInsecureContext, nullptr);
StringKeyframeVector keyframes;
keyframes.push_back(start_keyframe);
keyframes.push_back(end_keyframe);
auto* element = GetElementById("target");
StringKeyframeEffectModel* model =
StringKeyframeEffectModel::Create(keyframes);
KeyframeEffect* keyframe_effect_composited =
KeyframeEffect::Create(element, model, timing);
Animation* animation = timeline->Play(keyframe_effect_composited);
// After creating the animation we need to clean the lifecycle so that the
// animation can be pushed to the compositor.
UpdateAllLifecyclePhases();
document->GetAnimationClock().UpdateTime(0);
document->GetPendingAnimations().Update(WTF::nullopt, true);
// At this point, the animation exists on both the compositor and blink side,
// but no start time has arrived on either side. The compositor is currently
// synced, no update is pending.
EXPECT_FALSE(animation->CompositorPendingForTesting());
// However, if we pause the animation then the compositor should still be
// marked pending. This is required because otherwise the compositor will go
// ahead and start playing the animation once it receives a start time (e.g.
// on the next compositor frame).
animation->pause();
EXPECT_TRUE(animation->CompositorPendingForTesting());
}
TEST_F(AnimationAnimationTest, PreCommitWithUnresolvedStartTimes) {
// Get rid of the default animation.
animation->cancel();
EnableCompositing();
SetBodyInnerHTML("<div id='target'></div>");
// Create a compositable animation; in this case opacity from 1 to 0.
Timing timing;
timing.iteration_duration = 30;
scoped_refptr<StringKeyframe> start_keyframe = StringKeyframe::Create();
start_keyframe->SetCSSPropertyValue(
CSSPropertyOpacity, "1.0", SecureContextMode::kInsecureContext, nullptr);
scoped_refptr<StringKeyframe> end_keyframe = StringKeyframe::Create();
end_keyframe->SetCSSPropertyValue(
CSSPropertyOpacity, "0.0", SecureContextMode::kInsecureContext, nullptr);
StringKeyframeVector keyframes;
keyframes.push_back(start_keyframe);
keyframes.push_back(end_keyframe);
auto* element = GetElementById("target");
StringKeyframeEffectModel* model =
StringKeyframeEffectModel::Create(keyframes);
KeyframeEffect* keyframe_effect_composited =
KeyframeEffect::Create(element, model, timing);
Animation* animation = timeline->Play(keyframe_effect_composited);
// After creating the animation we need to clean the lifecycle so that the
// animation can be pushed to the compositor.
UpdateAllLifecyclePhases();
document->GetAnimationClock().UpdateTime(0);
document->GetPendingAnimations().Update(WTF::nullopt, true);
// At this point, the animation exists on both the compositor and blink side,
// but no start time has arrived on either side. The compositor is currently
// synced, no update is pending.
EXPECT_FALSE(animation->CompositorPendingForTesting());
// At this point, a call to PreCommit should bail out and tell us to wait for
// next commit because there are no resolved start times.
EXPECT_FALSE(animation->PreCommit(0, WTF::nullopt, true));
}
} // namespace blink } // namespace blink
...@@ -76,14 +76,14 @@ bool PendingAnimations::Update( ...@@ -76,14 +76,14 @@ bool PendingAnimations::Update(
animation->HasActiveAnimationsOnCompositor(); animation->HasActiveAnimationsOnCompositor();
// Animations with a start time do not participate in compositor start-time // Animations with a start time do not participate in compositor start-time
// grouping. // grouping.
if (animation->PreCommit(animation->HasStartTime() ? 1 : compositor_group, if (animation->PreCommit(animation->startTime() ? 1 : compositor_group,
composited_element_ids, start_on_compositor)) { composited_element_ids, start_on_compositor)) {
if (animation->HasActiveAnimationsOnCompositor() && if (animation->HasActiveAnimationsOnCompositor() &&
!had_compositor_animation) { !had_compositor_animation) {
started_synchronized_on_compositor = true; started_synchronized_on_compositor = true;
} }
if (animation->Playing() && !animation->HasStartTime() && if (animation->Playing() && !animation->startTime() &&
animation->TimelineInternal() && animation->TimelineInternal() &&
animation->TimelineInternal()->IsActive()) { animation->TimelineInternal()->IsActive()) {
waiting_for_start_time.push_back(animation.Get()); waiting_for_start_time.push_back(animation.Get());
...@@ -98,13 +98,13 @@ bool PendingAnimations::Update( ...@@ -98,13 +98,13 @@ bool PendingAnimations::Update(
// start time. Otherwise they may start immediately. // start time. Otherwise they may start immediately.
if (started_synchronized_on_compositor) { if (started_synchronized_on_compositor) {
for (auto& animation : waiting_for_start_time) { for (auto& animation : waiting_for_start_time) {
if (!animation->HasStartTime()) { if (!animation->startTime()) {
waiting_for_compositor_animation_start_.push_back(animation); waiting_for_compositor_animation_start_.push_back(animation);
} }
} }
} else { } else {
for (auto& animation : waiting_for_start_time) { for (auto& animation : waiting_for_start_time) {
if (!animation->HasStartTime()) { if (!animation->startTime()) {
animation->NotifyCompositorStartTime( animation->NotifyCompositorStartTime(
animation->TimelineInternal()->CurrentTimeInternal()); animation->TimelineInternal()->CurrentTimeInternal());
} }
...@@ -148,7 +148,7 @@ void PendingAnimations::NotifyCompositorAnimationStarted( ...@@ -148,7 +148,7 @@ void PendingAnimations::NotifyCompositorAnimationStarted(
animations.swap(waiting_for_compositor_animation_start_); animations.swap(waiting_for_compositor_animation_start_);
for (auto animation : animations) { for (auto animation : animations) {
if (animation->HasStartTime() || if (animation->startTime() ||
animation->PlayStateInternal() != Animation::kPending || animation->PlayStateInternal() != Animation::kPending ||
!animation->TimelineInternal() || !animation->TimelineInternal() ||
!animation->TimelineInternal()->IsActive()) { !animation->TimelineInternal()->IsActive()) {
......
...@@ -532,8 +532,9 @@ void CSSAnimations::MaybeApplyPendingUpdate(Element* element) { ...@@ -532,8 +532,9 @@ void CSSAnimations::MaybeApplyPendingUpdate(Element* element) {
pending_update_.NewTransitions().end() && pending_update_.NewTransitions().end() &&
!animation->Limited()) { !animation->Limited()) {
retargeted_compositor_transitions.insert( retargeted_compositor_transitions.insert(
property, std::pair<KeyframeEffectReadOnly*, double>( property,
effect, animation->StartTimeInternal())); std::pair<KeyframeEffectReadOnly*, double>(
effect, animation->StartTimeInternal().value_or(NullValue())));
} }
animation->cancel(); animation->cancel();
// after cancelation, transitions must be downgraded or they'll fail // after cancelation, transitions must be downgraded or they'll fail
......
...@@ -254,8 +254,8 @@ Response InspectorAnimationAgent::getCurrentTime(const String& id, ...@@ -254,8 +254,8 @@ Response InspectorAnimationAgent::getCurrentTime(const String& id,
*current_time = animation->currentTime(); *current_time = animation->currentTime();
} else { } else {
// Use startTime where possible since currentTime is limited. // Use startTime where possible since currentTime is limited.
*current_time = *current_time = animation->TimelineInternal()->currentTime() -
animation->TimelineInternal()->currentTime() - animation->startTime(); animation->startTime().value_or(NullValue());
} }
return Response::OK(); return Response::OK();
} }
...@@ -274,8 +274,8 @@ Response InspectorAnimationAgent::setPaused( ...@@ -274,8 +274,8 @@ Response InspectorAnimationAgent::setPaused(
return Response::Error("Failed to clone detached animation"); return Response::Error("Failed to clone detached animation");
if (paused && !clone->Paused()) { if (paused && !clone->Paused()) {
// Ensure we restore a current time if the animation is limited. // Ensure we restore a current time if the animation is limited.
double current_time = double current_time = clone->TimelineInternal()->currentTime() -
clone->TimelineInternal()->currentTime() - clone->startTime(); clone->startTime().value_or(NullValue());
clone->pause(); clone->pause();
clone->setCurrentTime(current_time, false); clone->setCurrentTime(current_time, false);
} else if (!paused && clone->Paused()) { } else if (!paused && clone->Paused()) {
...@@ -323,7 +323,7 @@ blink::Animation* InspectorAnimationAgent::AnimationClone( ...@@ -323,7 +323,7 @@ blink::Animation* InspectorAnimationAgent::AnimationClone(
id_to_animation_clone_.Set(id, clone); id_to_animation_clone_.Set(id, clone);
id_to_animation_.Set(String::Number(clone->SequenceNumber()), clone); id_to_animation_.Set(String::Number(clone->SequenceNumber()), clone);
clone->play(); clone->play();
clone->setStartTime(animation->startTime(), false); clone->setStartTime(animation->startTime().value_or(NullValue()), false);
animation->SetEffectSuppressed(true); animation->SetEffectSuppressed(true);
} }
...@@ -551,10 +551,12 @@ DocumentTimeline& InspectorAnimationAgent::ReferenceTimeline() { ...@@ -551,10 +551,12 @@ DocumentTimeline& InspectorAnimationAgent::ReferenceTimeline() {
double InspectorAnimationAgent::NormalizedStartTime( double InspectorAnimationAgent::NormalizedStartTime(
blink::Animation& animation) { blink::Animation& animation) {
if (ReferenceTimeline().PlaybackRate() == 0) { if (ReferenceTimeline().PlaybackRate() == 0) {
return animation.startTime() + ReferenceTimeline().currentTime() - return animation.startTime().value_or(NullValue()) +
ReferenceTimeline().currentTime() -
animation.TimelineInternal()->currentTime(); animation.TimelineInternal()->currentTime();
} }
return animation.startTime() + (animation.TimelineInternal()->ZeroTime() - return animation.startTime().value_or(NullValue()) +
(animation.TimelineInternal()->ZeroTime() -
ReferenceTimeline().ZeroTime()) * ReferenceTimeline().ZeroTime()) *
1000 * ReferenceTimeline().PlaybackRate(); 1000 * ReferenceTimeline().PlaybackRate();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment