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,
: ContextLifecycleObserver(execution_context),
play_state_(kIdle),
playback_rate_(1),
start_time_(NullValue()),
start_time_(),
hold_time_(0),
sequence_number_(NextSequenceNumber()),
content_(content),
......@@ -200,14 +200,14 @@ void Animation::SetCurrentTimeInternal(double new_current_time,
bool old_held = held_;
bool outdated = false;
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 (!old_held || hold_time_ != new_current_time)
outdated = true;
hold_time_ = new_current_time;
if (paused_ || !playback_rate_) {
start_time_ = NullValue();
} else if (is_limited && std::isnan(start_time_) &&
start_time_ = WTF::nullopt;
} else if (is_limited && !start_time_ &&
reason == kTimingUpdateForAnimationFrame) {
start_time_ = CalculateStartTime(new_current_time);
}
......@@ -229,7 +229,7 @@ void Animation::UpdateCurrentTimingState(TimingUpdateReason reason) {
return;
if (held_) {
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
if (!Limited(CalculateCurrentTime() + 0.001 * playback_rate_)) {
// The current time became unlimited, eg. due to a backwards
......@@ -250,13 +250,14 @@ void Animation::UpdateCurrentTimingState(TimingUpdateReason reason) {
}
double Animation::startTime(bool& is_null) const {
double result = startTime();
is_null = std::isnan(result);
return result;
WTF::Optional<double> result = startTime();
is_null = !result;
return result.value_or(0);
}
double Animation::startTime() const {
return start_time_ * 1000;
WTF::Optional<double> Animation::startTime() const {
return start_time_ ? WTF::make_optional(start_time_.value() * 1000)
: WTF::nullopt;
}
double Animation::currentTime(bool& is_null) {
......@@ -268,7 +269,7 @@ double Animation::currentTime(bool& is_null) {
double Animation::currentTime() {
PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
if (PlayStateInternal() == kIdle || (!held_ && !HasStartTime()))
if (PlayStateInternal() == kIdle || (!held_ && !start_time_))
return std::numeric_limits<double>::quiet_NaN();
return CurrentTimeInternal() * 1000;
......@@ -292,7 +293,7 @@ double Animation::UnlimitedCurrentTimeInternal() const {
#if DCHECK_IS_ON()
CurrentTimeInternal();
#endif
return PlayStateInternal() == kPaused || IsNull(start_time_)
return PlayStateInternal() == kPaused || !start_time_
? CurrentTimeInternal()
: CalculateCurrentTime();
}
......@@ -309,7 +310,8 @@ bool Animation::PreCommit(
(Paused() || compositor_state_->playback_rate != playback_rate_);
bool hard_change =
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,
// not a Cancel, but we can't communicate these to the compositor yet.
......@@ -329,7 +331,7 @@ bool Animation::PreCommit(
compositor_state_ = nullptr;
}
DCHECK(!compositor_state_ || !std::isnan(compositor_state_->start_time));
DCHECK(!compositor_state_ || compositor_state_->start_time);
if (!should_start) {
current_time_pending_ = false;
......@@ -382,17 +384,18 @@ void Animation::PostCommit(double timeline_time) {
switch (compositor_state_->pending_action) {
case kStart:
if (!std::isnan(compositor_state_->start_time)) {
DCHECK_EQ(start_time_, compositor_state_->start_time);
if (compositor_state_->start_time) {
DCHECK_EQ(start_time_.value(), compositor_state_->start_time.value());
compositor_state_->pending_action = kNone;
}
break;
case kPause:
case kPauseThenStart:
DCHECK(std::isnan(start_time_));
DCHECK(!start_time_);
compositor_state_->pending_action = kNone;
SetCurrentTimeInternal(
(timeline_time - compositor_state_->start_time) * playback_rate_,
(timeline_time - compositor_state_->start_time.value()) *
playback_rate_,
kTimingUpdateForAnimationFrame);
current_time_pending_ = false;
break;
......@@ -407,12 +410,14 @@ void Animation::NotifyCompositorStartTime(double timeline_time) {
if (compositor_state_) {
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;
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 =
timeline_time + CurrentTimeInternal() / -playback_rate_;
IsNull(start_time) ? WTF::nullopt : WTF::make_optional(start_time);
if (start_time_ == timeline_time) {
// The start time was set to the incoming compositor start time.
......@@ -423,8 +428,7 @@ void Animation::NotifyCompositorStartTime(double timeline_time) {
return;
}
if (!std::isnan(start_time_) ||
CurrentTimeInternal() != initial_compositor_hold_time) {
if (start_time_ || CurrentTimeInternal() != initial_compositor_hold_time) {
// A new start time or current time was set while starting.
SetCompositorPending(true);
return;
......@@ -436,7 +440,7 @@ void Animation::NotifyCompositorStartTime(double timeline_time) {
void Animation::NotifyStartTime(double timeline_time) {
if (Playing()) {
DCHECK(std::isnan(start_time_));
DCHECK(!start_time_);
DCHECK(held_);
if (playback_rate_ == 0) {
......@@ -465,17 +469,22 @@ bool Animation::Affects(const Element& element,
effect->Affects(PropertyHandle(property));
}
double Animation::CalculateStartTime(double current_time) const {
return timeline_->EffectiveTime() - current_time / playback_rate_;
WTF::Optional<double> Animation::CalculateStartTime(double current_time) const {
WTF::Optional<double> start_time =
timeline_->EffectiveTime() - current_time / playback_rate_;
DCHECK(!IsNull(start_time.value()));
return start_time;
}
double Animation::CalculateCurrentTime() const {
// TODO(crbug.com/818196): By spec, this should be unresolved, not 0.
if (IsNull(start_time_) || !timeline_)
if (!start_time_ || !timeline_)
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) {
PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
......@@ -488,12 +497,12 @@ void Animation::setStartTime(double start_time, bool is_null) {
SetStartTimeInternal(start_time / 1000);
}
void Animation::SetStartTimeInternal(double new_start_time) {
void Animation::SetStartTimeInternal(WTF::Optional<double> new_start_time) {
DCHECK(!paused_);
DCHECK(std::isfinite(new_start_time));
DCHECK_NE(new_start_time, start_time_);
DCHECK(new_start_time.has_value());
DCHECK(new_start_time != start_time_);
bool had_start_time = HasStartTime();
bool had_start_time = start_time_.has_value();
double previous_current_time = CurrentTimeInternal();
start_time_ = new_start_time;
if (held_ && playback_rate_) {
......@@ -565,12 +574,12 @@ Animation::AnimationPlayState Animation::PlayStateInternal() const {
return play_state_;
}
Animation::AnimationPlayState Animation::CalculatePlayState() {
Animation::AnimationPlayState Animation::CalculatePlayState() const {
if (paused_ && !current_time_pending_)
return kPaused;
if (play_state_ == kIdle)
return kIdle;
if (current_time_pending_ || (IsNull(start_time_) && playback_rate_ != 0))
if (current_time_pending_ || (!start_time_ && playback_rate_ != 0))
return kPending;
if (Limited())
return kFinished;
......@@ -631,7 +640,7 @@ void Animation::play(ExceptionState& exception_state) {
}
if (!Playing()) {
start_time_ = NullValue();
start_time_ = WTF::nullopt;
}
if (PlayStateInternal() == kIdle) {
......@@ -644,11 +653,11 @@ void Animation::play(ExceptionState& exception_state) {
UnpauseInternal();
if (playback_rate_ > 0 && (current_time < 0 || current_time >= EffectEnd())) {
start_time_ = NullValue();
start_time_ = WTF::nullopt;
SetCurrentTimeInternal(0, kTimingUpdateOnDemand);
} else if (playback_rate_ < 0 &&
(current_time <= 0 || current_time > EffectEnd())) {
start_time_ = NullValue();
start_time_ = WTF::nullopt;
SetCurrentTimeInternal(EffectEnd(), kTimingUpdateOnDemand);
}
}
......@@ -753,12 +762,12 @@ void Animation::setPlaybackRate(double playback_rate) {
PlayStateUpdateScope update_scope(*this, kTimingUpdateOnDemand);
double start_time_before = start_time_;
WTF::Optional<double> start_time_before = start_time_;
SetPlaybackRateInternal(playback_rate);
// Adds a UseCounter to check if setting playbackRate causes a compensatory
// 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) {
UseCounter::Count(GetExecutionContext(),
WebFeature::kAnimationSetPlaybackRateCompensatorySeek);
......@@ -769,7 +778,7 @@ void Animation::SetPlaybackRateInternal(double playback_rate) {
DCHECK(std::isfinite(playback_rate));
DCHECK_NE(playback_rate, playback_rate_);
if (!Limited() && !Paused() && HasStartTime())
if (!Limited() && !Paused() && start_time_)
current_time_pending_ = true;
double stored_current_time = CurrentTimeInternal();
......@@ -778,7 +787,7 @@ void Animation::SetPlaybackRateInternal(double playback_rate) {
finished_ = false;
playback_rate_ = playback_rate;
start_time_ = std::numeric_limits<double>::quiet_NaN();
start_time_ = WTF::nullopt;
SetCurrentTimeInternal(stored_current_time, kTimingUpdateOnDemand);
}
......@@ -858,7 +867,7 @@ Animation::CheckCanStartAnimationOnCompositorInternal(
// 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.
if (composited_element_ids.has_value()) {
if (composited_element_ids) {
DCHECK(RuntimeEnabledFeatures::SlimmingPaintV2Enabled());
Element* target_element =
ToKeyframeEffectReadOnly(content_.Get())->target();
......@@ -899,17 +908,19 @@ void Animation::StartAnimationOnCompositor(
bool reversed = playback_rate_ < 0;
double start_time = TimelineInternal()->ZeroTime() + StartTimeInternal();
if (reversed) {
start_time -= EffectEnd() / fabs(playback_rate_);
}
// TODO(crbug.com/791086): Make StartAnimationOnCompositor use WTF::Optional.
double start_time = NullValue();
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 =
reversed ? EffectEnd() - CurrentTimeInternal() : CurrentTimeInternal();
time_offset = time_offset / fabs(playback_rate_);
}
DCHECK_NE(compositor_group_, 0);
ToKeyframeEffectReadOnly(content_.Get())
->StartAnimationOnCompositor(compositor_group_, start_time, time_offset,
......@@ -928,9 +939,16 @@ void Animation::SetCompositorPending(bool effect_changed) {
if (compositor_pending_ || is_paused_for_testing_) {
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 ||
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;
TimelineInternal()->GetDocument()->GetPendingAnimations().Add(this);
}
......@@ -986,7 +1004,7 @@ bool Animation::Update(TimingUpdateReason reason) {
}
if ((idle || Limited()) && !finished_) {
if (reason == kTimingUpdateForAnimationFrame && (idle || HasStartTime())) {
if (reason == kTimingUpdateForAnimationFrame && (idle || start_time_)) {
if (idle) {
const AtomicString& event_type = EventTypeNames::cancel;
if (GetExecutionContext() && HasEventListeners(event_type)) {
......@@ -1032,12 +1050,12 @@ void Animation::SpecifiedTimingChanged() {
}
bool Animation::IsEventDispatchAllowed() const {
return Paused() || HasStartTime();
return Paused() || start_time_;
}
double Animation::TimeToEffectChange() {
DCHECK(!outdated_);
if (!HasStartTime() || held_)
if (!start_time_ || held_)
return std::numeric_limits<double>::infinity();
if (!content_)
......@@ -1061,7 +1079,7 @@ void Animation::cancel() {
held_ = false;
paused_ = false;
play_state_ = kIdle;
start_time_ = NullValue();
start_time_ = WTF::nullopt;
current_time_pending_ = false;
ForceServiceOnNextFrame();
}
......
......@@ -52,6 +52,7 @@
#include "platform/animation/CompositorAnimationDelegate.h"
#include "platform/graphics/CompositorElementId.h"
#include "platform/heap/Handle.h"
#include "platform/wtf/Optional.h"
namespace blink {
......@@ -157,13 +158,10 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
const DocumentTimeline* TimelineInternal() const { 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() const;
double StartTimeInternal() const { return start_time_; }
WTF::Optional<double> startTime() const;
WTF::Optional<double> StartTimeInternal() const { return start_time_; }
void setStartTime(double, bool is_null);
void SetStartTimeInternal(double);
const AnimationEffectReadOnly* effect() const { return content_.Get(); }
AnimationEffectReadOnly* effect() { return content_.Get(); }
......@@ -228,6 +226,8 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
void Trace(blink::Visitor*) override;
bool CompositorPendingForTesting() const { return compositor_pending_; }
protected:
DispatchEventResult DispatchEventInternal(Event*) override;
void AddedEventListener(const AtomicString& event_type,
......@@ -242,11 +242,13 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
double EffectEnd() const;
bool Limited(double current_time) const;
AnimationPlayState CalculatePlayState();
AnimationPlayState CalculatePlayState() const;
WTF::Optional<double> CalculateStartTime(double current_time) const;
double CalculateCurrentTime() const;
void UnpauseInternal();
void SetPlaybackRateInternal(double);
void SetStartTimeInternal(WTF::Optional<double>);
void UpdateCurrentTimingState(TimingUpdateReason);
void BeginUpdatingState();
......@@ -276,7 +278,7 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
AnimationPlayState play_state_;
double playback_rate_;
double start_time_;
WTF::Optional<double> start_time_;
double hold_time_;
unsigned sequence_number_;
......@@ -317,7 +319,7 @@ class CORE_EXPORT Animation final : public EventTargetWithInlineData,
playback_rate(animation.playback_rate_),
effect_changed(false),
pending_action(kStart) {}
double start_time;
WTF::Optional<double> start_time;
double hold_time;
double playback_rate;
bool effect_changed;
......
......@@ -108,8 +108,7 @@ TEST_F(AnimationAnimationTest, InitialState) {
EXPECT_EQ(0, animation->CurrentTimeInternal());
EXPECT_FALSE(animation->Paused());
EXPECT_EQ(1, animation->playbackRate());
EXPECT_FALSE(animation->HasStartTime());
EXPECT_TRUE(IsNull(animation->StartTimeInternal()));
EXPECT_FALSE(animation->StartTimeInternal());
StartTimeline();
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
......@@ -118,7 +117,6 @@ TEST_F(AnimationAnimationTest, InitialState) {
EXPECT_FALSE(animation->Paused());
EXPECT_EQ(1, animation->playbackRate());
EXPECT_EQ(0, animation->StartTimeInternal());
EXPECT_TRUE(animation->HasStartTime());
}
TEST_F(AnimationAnimationTest, CurrentTimeDoesNotSetOutdated) {
......@@ -253,7 +251,7 @@ TEST_F(AnimationAnimationTest, StartTimePauseFinish) {
NonThrowableExceptionState exception_state;
animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
animation->finish(exception_state);
EXPECT_EQ(Animation::kFinished, animation->PlayStateInternal());
EXPECT_EQ(-30000, animation->startTime());
......@@ -274,13 +272,13 @@ TEST_F(AnimationAnimationTest, StartTimeFinishPause) {
animation->finish(exception_state);
EXPECT_EQ(-30 * 1000, animation->startTime());
animation->pause();
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
}
TEST_F(AnimationAnimationTest, StartTimeWithZeroPlaybackRate) {
animation->setPlaybackRate(0);
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
}
......@@ -733,11 +731,11 @@ TEST_F(AnimationAnimationTest, PlayAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
animation->play();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime());
......@@ -751,11 +749,11 @@ TEST_F(AnimationAnimationTest, PlayBackwardsAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
animation->play();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
......@@ -766,11 +764,11 @@ TEST_F(AnimationAnimationTest, ReverseAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
animation->reverse();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
SimulateFrame(10);
EXPECT_EQ(Animation::kRunning, animation->PlayStateInternal());
EXPECT_EQ(30 * 1000, animation->currentTime());
......@@ -782,7 +780,7 @@ TEST_F(AnimationAnimationTest, FinishAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
animation->finish(exception_state);
EXPECT_EQ(30000, animation->currentTime());
EXPECT_EQ(-30000, animation->startTime());
......@@ -793,11 +791,11 @@ TEST_F(AnimationAnimationTest, PauseAfterCancel) {
animation->cancel();
EXPECT_EQ(Animation::kIdle, animation->PlayStateInternal());
EXPECT_TRUE(std::isnan(animation->currentTime()));
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
animation->pause();
EXPECT_EQ(Animation::kPending, animation->PlayStateInternal());
EXPECT_EQ(0, animation->currentTime());
EXPECT_TRUE(std::isnan(animation->startTime()));
EXPECT_FALSE(animation->startTime());
}
TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) {
......@@ -841,4 +839,106 @@ TEST_F(AnimationAnimationTest, NoCompositeWithoutCompositedElementId) {
.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
......@@ -76,14 +76,14 @@ bool PendingAnimations::Update(
animation->HasActiveAnimationsOnCompositor();
// Animations with a start time do not participate in compositor start-time
// grouping.
if (animation->PreCommit(animation->HasStartTime() ? 1 : compositor_group,
if (animation->PreCommit(animation->startTime() ? 1 : compositor_group,
composited_element_ids, start_on_compositor)) {
if (animation->HasActiveAnimationsOnCompositor() &&
!had_compositor_animation) {
started_synchronized_on_compositor = true;
}
if (animation->Playing() && !animation->HasStartTime() &&
if (animation->Playing() && !animation->startTime() &&
animation->TimelineInternal() &&
animation->TimelineInternal()->IsActive()) {
waiting_for_start_time.push_back(animation.Get());
......@@ -98,13 +98,13 @@ bool PendingAnimations::Update(
// start time. Otherwise they may start immediately.
if (started_synchronized_on_compositor) {
for (auto& animation : waiting_for_start_time) {
if (!animation->HasStartTime()) {
if (!animation->startTime()) {
waiting_for_compositor_animation_start_.push_back(animation);
}
}
} else {
for (auto& animation : waiting_for_start_time) {
if (!animation->HasStartTime()) {
if (!animation->startTime()) {
animation->NotifyCompositorStartTime(
animation->TimelineInternal()->CurrentTimeInternal());
}
......@@ -148,7 +148,7 @@ void PendingAnimations::NotifyCompositorAnimationStarted(
animations.swap(waiting_for_compositor_animation_start_);
for (auto animation : animations) {
if (animation->HasStartTime() ||
if (animation->startTime() ||
animation->PlayStateInternal() != Animation::kPending ||
!animation->TimelineInternal() ||
!animation->TimelineInternal()->IsActive()) {
......
......@@ -532,8 +532,9 @@ void CSSAnimations::MaybeApplyPendingUpdate(Element* element) {
pending_update_.NewTransitions().end() &&
!animation->Limited()) {
retargeted_compositor_transitions.insert(
property, std::pair<KeyframeEffectReadOnly*, double>(
effect, animation->StartTimeInternal()));
property,
std::pair<KeyframeEffectReadOnly*, double>(
effect, animation->StartTimeInternal().value_or(NullValue())));
}
animation->cancel();
// after cancelation, transitions must be downgraded or they'll fail
......
......@@ -254,8 +254,8 @@ Response InspectorAnimationAgent::getCurrentTime(const String& id,
*current_time = animation->currentTime();
} else {
// Use startTime where possible since currentTime is limited.
*current_time =
animation->TimelineInternal()->currentTime() - animation->startTime();
*current_time = animation->TimelineInternal()->currentTime() -
animation->startTime().value_or(NullValue());
}
return Response::OK();
}
......@@ -274,8 +274,8 @@ Response InspectorAnimationAgent::setPaused(
return Response::Error("Failed to clone detached animation");
if (paused && !clone->Paused()) {
// Ensure we restore a current time if the animation is limited.
double current_time =
clone->TimelineInternal()->currentTime() - clone->startTime();
double current_time = clone->TimelineInternal()->currentTime() -
clone->startTime().value_or(NullValue());
clone->pause();
clone->setCurrentTime(current_time, false);
} else if (!paused && clone->Paused()) {
......@@ -323,7 +323,7 @@ blink::Animation* InspectorAnimationAgent::AnimationClone(
id_to_animation_clone_.Set(id, clone);
id_to_animation_.Set(String::Number(clone->SequenceNumber()), clone);
clone->play();
clone->setStartTime(animation->startTime(), false);
clone->setStartTime(animation->startTime().value_or(NullValue()), false);
animation->SetEffectSuppressed(true);
}
......@@ -551,10 +551,12 @@ DocumentTimeline& InspectorAnimationAgent::ReferenceTimeline() {
double InspectorAnimationAgent::NormalizedStartTime(
blink::Animation& animation) {
if (ReferenceTimeline().PlaybackRate() == 0) {
return animation.startTime() + ReferenceTimeline().currentTime() -
return animation.startTime().value_or(NullValue()) +
ReferenceTimeline().currentTime() -
animation.TimelineInternal()->currentTime();
}
return animation.startTime() + (animation.TimelineInternal()->ZeroTime() -
return animation.startTime().value_or(NullValue()) +
(animation.TimelineInternal()->ZeroTime() -
ReferenceTimeline().ZeroTime()) *
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