Commit eadf7030 authored by Kevin Ellis's avatar Kevin Ellis Committed by Commit Bot

Implement Animation::setTimeline

Mutability of the animation timeline is behind the feature flag for
ScrollTimeline. The feature is marked as web-exposed, but with a no-op
implementation if the feature flag is not set. The rationale for this
setup is that we don't support conditionally readonly attributes in IDL.

With the patch, a number of the failing tests are now failing for
different reasons:
* Replaceable animations should consider all timelines when determining
  if an animation can be removed. Our current implementation only
  considers other animations on the same timeline when making the
  decision. Fixing these failures requires some refactoring, which is
  deferred to a followup CL.
* Resetting the timeline cancels a running CSS animation or transition.
  Some refactoring is required to ensure that we properly compute the
  cancel time as it depends on the old timeline. Again, deferring to
  a followup CL.

https://github.com/w3c/csswg-drafts/issues/5159
https://github.com/w3c/csswg-drafts/issues/5422
https://drafts.csswg.org/scroll-animations-1
https://www.chromestatus.com/feature/6752840701706240

Bug: 827626
Change-Id: I1fea09a80e8730694455c06b192cb9e068eb0c55
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2324101Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarOlga Gerchikov <gerchiko@microsoft.com>
Reviewed-by: default avatarAnders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Kevin Ellis <kevers@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805371}
parent 0074e68f
...@@ -349,6 +349,7 @@ void Animation::SetCurrentTimeInternal(double new_current_time) { ...@@ -349,6 +349,7 @@ void Animation::SetCurrentTimeInternal(double new_current_time) {
} else { } else {
start_time_ = CalculateStartTime(new_current_time); start_time_ = CalculateStartTime(new_current_time);
} }
reset_current_time_on_resume_ = false;
// Preserve invariant that we can only set a start time or a hold time in the // Preserve invariant that we can only set a start time or a hold time in the
// absence of an active timeline. // absence of an active timeline.
...@@ -727,6 +728,111 @@ bool Animation::Affects(const Element& element, ...@@ -727,6 +728,111 @@ bool Animation::Affects(const Element& element,
effect->Affects(PropertyHandle(property)); effect->Affects(PropertyHandle(property));
} }
void Animation::setTimeline(AnimationTimeline* timeline) {
// https://drafts.csswg.org/web-animations-1/#setting-the-timeline
// Steps refined to accommodate scroll timelines.
// TODO(crbug.com/827626): Update the web-animation-1 spec.
// https://github.com/w3c/csswg-drafts/pull/5423.
// Unfortunately cannot mark the setter only as being conditionally enabled
// via a feature flag. Conditionally making the feature a no-op is nearly
// equivalent.
if (!RuntimeEnabledFeatures::ScrollTimelineEnabled())
return;
// 1. Let the old timeline be the current timeline of the animation, if any.
AnimationTimeline* old_timeline = timeline_;
// 2. If the new timeline is the same object as the old timeline, abort this
// procedure.
if (old_timeline == timeline)
return;
UpdateIfNecessary();
AnimationPlayState old_play_state = CalculateAnimationPlayState();
base::Optional<double> old_current_time = CurrentTimeInternal();
CancelAnimationOnCompositor();
// 3. Let the timeline of the animation be the new timeline.
// The Blink implementation requires additional steps to link the animation
// to the new timeline. Animations with a null timeline hang off of the
// document timeline in order to be properly included in the results for
// getAnimations calls.
if (old_timeline)
old_timeline->AnimationDetached(this);
else
document_->Timeline().AnimationDetached(this);
timeline_ = timeline;
if (timeline)
timeline->AnimationAttached(this);
else
document_->Timeline().AnimationAttached(this);
SetOutdated();
reset_current_time_on_resume_ = false;
if (timeline) {
if (!timeline->IsMonotonicallyIncreasing()) {
ApplyPendingPlaybackRate();
double boundary_time = (playback_rate_ > 0) ? 0 : EffectEnd();
switch (old_play_state) {
case kIdle:
break;
case kRunning:
case kFinished:
// A non-monotonic timeline has a fixed start time at the beginning or
// end of the timeline.
start_time_ = boundary_time;
break;
case kPaused:
if (old_current_time) {
reset_current_time_on_resume_ = true;
start_time_ = base::nullopt;
hold_time_ = old_current_time.value();
} else if (PendingInternal()) {
start_time_ = boundary_time;
}
break;
default:
NOTREACHED();
}
} else if (old_current_time && old_timeline &&
!old_timeline->IsMonotonicallyIncreasing()) {
SetCurrentTimeInternal(old_current_time.value());
}
}
// 4. If the start time of animation is resolved, make the animation’s hold
// time unresolved. This step ensures that the finished play state of the
// animation is not “sticky” but is re-evaluated based on its updated
// current time.
if (start_time_)
ResetHoldTimeAndPhase();
// 5. Run the procedure to update an animation’s finished state for animation
// with the did seek flag set to false, and the synchronously notify flag
// set to false.
UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync);
if (content_ && !timeline_) {
// Update the timing model to capture the phase change and cancel an active
// CSS animation or transition.
content_->Invalidate();
Update(kTimingUpdateOnDemand);
}
SetCompositorPending(false);
// Inform devtools of a potential change to the play state.
NotifyProbe();
}
base::Optional<double> Animation::CalculateStartTime( base::Optional<double> Animation::CalculateStartTime(
double current_time) const { double current_time) const {
base::Optional<double> start_time; base::Optional<double> start_time;
...@@ -801,6 +907,7 @@ void Animation::setStartTime(base::Optional<double> start_time_ms, ...@@ -801,6 +907,7 @@ void Animation::setStartTime(base::Optional<double> start_time_ms,
} }
} }
start_time_ = new_start_time; start_time_ = new_start_time;
reset_current_time_on_resume_ = false;
// 6. Update animation’s hold time based on the first matching condition from // 6. Update animation’s hold time based on the first matching condition from
// the following, // the following,
...@@ -1155,6 +1262,8 @@ void Animation::PlayInternal(AutoRewind auto_rewind, ...@@ -1155,6 +1262,8 @@ void Animation::PlayInternal(AutoRewind auto_rewind,
// 4. Let has finite timeline be true if animation has an associated timeline // 4. Let has finite timeline be true if animation has an associated timeline
// that is not monotonically increasing. // that is not monotonically increasing.
bool aborted_pause = pending_pause_; bool aborted_pause = pending_pause_;
bool enable_seek =
auto_rewind == AutoRewind::kEnabled || reset_current_time_on_resume_;
bool has_pending_ready_promise = false; bool has_pending_ready_promise = false;
base::Optional<double> seek_time; base::Optional<double> seek_time;
bool has_finite_timeline = bool has_finite_timeline =
...@@ -1186,12 +1295,16 @@ void Animation::PlayInternal(AutoRewind auto_rewind, ...@@ -1186,12 +1295,16 @@ void Animation::PlayInternal(AutoRewind auto_rewind,
double effective_playback_rate = EffectivePlaybackRate(); double effective_playback_rate = EffectivePlaybackRate();
base::Optional<double> current_time = CurrentTimeInternal(); base::Optional<double> current_time = CurrentTimeInternal();
if (effective_playback_rate > 0 && auto_rewind == AutoRewind::kEnabled && if (reset_current_time_on_resume_) {
current_time = base::nullopt;
reset_current_time_on_resume_ = false;
}
if (effective_playback_rate > 0 && enable_seek &&
(!current_time || current_time < 0 || current_time >= EffectEnd())) { (!current_time || current_time < 0 || current_time >= EffectEnd())) {
seek_time = 0; seek_time = 0;
} else if (effective_playback_rate < 0 && } else if (effective_playback_rate < 0 && enable_seek &&
auto_rewind == AutoRewind::kEnabled &&
(!current_time || current_time <= 0 || (!current_time || current_time <= 0 ||
current_time > EffectEnd())) { current_time > EffectEnd())) {
if (EffectEnd() == std::numeric_limits<double>::infinity()) { if (EffectEnd() == std::numeric_limits<double>::infinity()) {
...@@ -1731,14 +1844,14 @@ Animation::CheckCanStartAnimationOnCompositorInternal() const { ...@@ -1731,14 +1844,14 @@ Animation::CheckCanStartAnimationOnCompositorInternal() const {
// reason to composite it. Additionally, mutating the timeline playback rate // reason to composite it. Additionally, mutating the timeline playback rate
// is a debug feature available via devtools; we don't support this on the // is a debug feature available via devtools; we don't support this on the
// compositor currently and there is no reason to do so. // compositor currently and there is no reason to do so.
if (timeline_->IsDocumentTimeline() && if (!timeline_ || (timeline_->IsDocumentTimeline() &&
To<DocumentTimeline>(*timeline_).PlaybackRate() != 1) To<DocumentTimeline>(*timeline_).PlaybackRate() != 1))
reasons |= CompositorAnimations::kInvalidAnimationOrEffect; reasons |= CompositorAnimations::kInvalidAnimationOrEffect;
// If the scroll source is not composited, fall back to main thread. // If the scroll source is not composited, fall back to main thread.
// TODO(crbug.com/476553): Once all ScrollNodes including uncomposited ones // TODO(crbug.com/476553): Once all ScrollNodes including uncomposited ones
// are in the compositor, the animation should be composited. // are in the compositor, the animation should be composited.
if (timeline_->IsScrollTimeline() && if (timeline_ && timeline_->IsScrollTimeline() &&
!CompositorAnimations::CheckUsesCompositedScrolling( !CompositorAnimations::CheckUsesCompositedScrolling(
To<ScrollTimeline>(*timeline_).ResolvedScrollSource())) To<ScrollTimeline>(*timeline_).ResolvedScrollSource()))
reasons |= CompositorAnimations::kTimelineSourceHasInvalidCompositingState; reasons |= CompositorAnimations::kTimelineSourceHasInvalidCompositingState;
...@@ -1801,12 +1914,6 @@ void Animation::StartAnimationOnCompositor( ...@@ -1801,12 +1914,6 @@ void Animation::StartAnimationOnCompositor(
// composited and non-composited animations. The use of 'compositor' in the name // composited and non-composited animations. The use of 'compositor' in the name
// is confusing. // is confusing.
void Animation::SetCompositorPending(bool effect_changed) { void Animation::SetCompositorPending(bool effect_changed) {
// Cannot play an animation with a null timeline.
// TODO(crbug.com/827626) Revisit once timelines are mutable as there will be
// work to do if the timeline is reset.
if (!timeline_)
return;
// FIXME: KeyframeEffect could notify this directly? // FIXME: KeyframeEffect could notify this directly?
if (!HasActiveAnimationsOnCompositor()) { if (!HasActiveAnimationsOnCompositor()) {
DestroyCompositorAnimation(); DestroyCompositorAnimation();
...@@ -1871,9 +1978,6 @@ bool Animation::Update(TimingUpdateReason reason) { ...@@ -1871,9 +1978,6 @@ bool Animation::Update(TimingUpdateReason reason) {
// time of an animation also involves: // time of an animation also involves:
// * Running the update an animation’s finished state procedure. // * Running the update an animation’s finished state procedure.
// * Queueing animation events. // * Queueing animation events.
if (!timeline_)
return false;
ClearOutdated(); ClearOutdated();
bool idle = CalculateAnimationPlayState() == kIdle; bool idle = CalculateAnimationPlayState() == kIdle;
if (!idle) if (!idle)
...@@ -1940,11 +2044,6 @@ void Animation::QueueFinishedEvent() { ...@@ -1940,11 +2044,6 @@ void Animation::QueueFinishedEvent() {
} }
void Animation::UpdateIfNecessary() { void Animation::UpdateIfNecessary() {
// Update is a no-op if there is no timeline_, and will not reset the outdated
// state in this case.
if (!timeline_)
return;
if (Outdated()) if (Outdated())
Update(kTimingUpdateOnDemand); Update(kTimingUpdateOnDemand);
DCHECK(!Outdated()); DCHECK(!Outdated());
......
...@@ -199,6 +199,7 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, ...@@ -199,6 +199,7 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData,
double playbackRate() const; double playbackRate() const;
void setPlaybackRate(double, ExceptionState& = ASSERT_NO_EXCEPTION); void setPlaybackRate(double, ExceptionState& = ASSERT_NO_EXCEPTION);
AnimationTimeline* timeline() { return timeline_; } AnimationTimeline* timeline() { return timeline_; }
void setTimeline(AnimationTimeline* timeline);
Document* GetDocument() const; Document* GetDocument() const;
base::Optional<double> startTime() const; base::Optional<double> startTime() const;
...@@ -386,6 +387,7 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData, ...@@ -386,6 +387,7 @@ class CORE_EXPORT Animation : public EventTargetWithInlineData,
base::Optional<double> hold_time_; base::Optional<double> hold_time_;
base::Optional<TimelinePhase> hold_phase_; base::Optional<TimelinePhase> hold_phase_;
base::Optional<double> previous_current_time_; base::Optional<double> previous_current_time_;
bool reset_current_time_on_resume_ = false;
unsigned sequence_number_; unsigned sequence_number_;
......
...@@ -40,8 +40,7 @@ enum ReplaceState { "active", "removed", "persisted" }; ...@@ -40,8 +40,7 @@ enum ReplaceState { "active", "removed", "persisted" };
] interface Animation : EventTarget { ] interface Animation : EventTarget {
[CallWith=ExecutionContext, RaisesException] constructor(optional AnimationEffect? effect = null, optional AnimationTimeline? timeline); [CallWith=ExecutionContext, RaisesException] constructor(optional AnimationEffect? effect = null, optional AnimationTimeline? timeline);
[Measure] attribute AnimationEffect? effect; [Measure] attribute AnimationEffect? effect;
// TODO(suzyh): Make timeline mutable. [RuntimeEnabled=WebAnimationsAPI] attribute AnimationTimeline? timeline;
[RuntimeEnabled=WebAnimationsAPI] readonly attribute AnimationTimeline? timeline;
[Measure, RaisesException=Setter] attribute double? startTime; [Measure, RaisesException=Setter] attribute double? startTime;
[Measure, RaisesException=Setter] attribute double? currentTime; [Measure, RaisesException=Setter] attribute double? currentTime;
[Measure, RaisesException=Setter] attribute double playbackRate; [Measure, RaisesException=Setter] attribute double playbackRate;
......
...@@ -165,8 +165,14 @@ void PendingAnimations::NotifyCompositorAnimationStarted( ...@@ -165,8 +165,14 @@ void PendingAnimations::NotifyCompositorAnimationStarted(
waiting_for_compositor_animation_start_.push_back(animation); waiting_for_compositor_animation_start_.push_back(animation);
continue; continue;
} }
animation->NotifyReady(monotonic_animation_start_time - if (animation->timeline() &&
animation->timeline()->ZeroTimeInSeconds()); !animation->timeline()->IsMonotonicallyIncreasing()) {
animation->NotifyReady(
animation->timeline()->CurrentTimeSeconds().value_or(0));
} else {
animation->NotifyReady(monotonic_animation_start_time -
animation->timeline()->ZeroTimeInSeconds());
}
} }
} }
......
...@@ -5,7 +5,7 @@ PASS Before -> Active ...@@ -5,7 +5,7 @@ PASS Before -> Active
PASS Before -> After PASS Before -> After
PASS Active -> Idle, display: none PASS Active -> Idle, display: none
PASS After -> Idle, display: none PASS After -> Idle, display: none
FAIL Active -> Idle, setting Animation.timeline = null promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" FAIL Active -> Idle, setting Animation.timeline = null assert_approx_equals: expected 0.1 +/- 0.0005 but got 0
PASS Active -> Idle, calling Animation.cancel() PASS Active -> Idle, calling Animation.cancel()
PASS Active -> Before PASS Active -> Before
PASS Active -> After PASS Active -> After
...@@ -24,7 +24,7 @@ PASS Redundant change, after -> active, then back ...@@ -24,7 +24,7 @@ PASS Redundant change, after -> active, then back
PASS Call Animation.cancel after canceling animation. PASS Call Animation.cancel after canceling animation.
PASS Restart animation after canceling animation immediately. PASS Restart animation after canceling animation immediately.
PASS Call Animation.cancel after restarting animation immediately. PASS Call Animation.cancel after restarting animation immediately.
FAIL Set timeline and play transition after clearing the timeline. promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Set timeline and play transition after clearing the timeline.
PASS Set null target effect after canceling the animation. PASS Set null target effect after canceling the animation.
PASS Cancel the animation after clearing the target effect. PASS Cancel the animation after clearing the target effect.
Harness: the test ran to completion. Harness: the test ran to completion.
......
...@@ -4,15 +4,15 @@ PASS Idle -> Before ...@@ -4,15 +4,15 @@ PASS Idle -> Before
PASS Idle or Pending -> Active PASS Idle or Pending -> Active
PASS Idle or Pending -> After PASS Idle or Pending -> After
PASS Before -> Idle (display: none) PASS Before -> Idle (display: none)
FAIL Before -> Idle (Animation.timeline = null) promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Before -> Idle (Animation.timeline = null)
PASS Before -> Active PASS Before -> Active
PASS Before -> After PASS Before -> After
PASS Active -> Idle, no delay (display: none) PASS Active -> Idle, no delay (display: none)
FAIL Active -> Idle, no delay (Animation.timeline = null) promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Active -> Idle, no delay (Animation.timeline = null)
PASS Active -> Idle, with positive delay (display: none) PASS Active -> Idle, with positive delay (display: none)
FAIL Active -> Idle, with positive delay (Animation.timeline = null) promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Active -> Idle, with positive delay (Animation.timeline = null)
PASS Active -> Idle, with negative delay (display: none) PASS Active -> Idle, with negative delay (display: none)
FAIL Active -> Idle, with negative delay (Animation.timeline = null) promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" FAIL Active -> Idle, with negative delay (Animation.timeline = null) assert_equals: expected 0 but got 100
PASS Active -> Before PASS Active -> Before
PASS Active -> After PASS Active -> After
PASS After -> Before PASS After -> Before
...@@ -22,7 +22,7 @@ FAIL Calculating the interval start and end time with negative end delay. assert ...@@ -22,7 +22,7 @@ FAIL Calculating the interval start and end time with negative end delay. assert
PASS Call Animation.cancel after canceling transition. PASS Call Animation.cancel after canceling transition.
PASS Restart transition after canceling transition immediately PASS Restart transition after canceling transition immediately
PASS Call Animation.cancel after restarting transition immediately PASS Call Animation.cancel after restarting transition immediately
FAIL Set timeline and play transition after clear the timeline promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Set timeline and play transition after clear the timeline
PASS Set null target effect after canceling the transition PASS Set null target effect after canceling the transition
PASS Cancel the transition after clearing the target effect PASS Cancel the transition after clearing the target effect
PASS Cancel the transition after it finishes PASS Cancel the transition after it finishes
......
This is a testharness.js-based test. This is a testharness.js-based test.
PASS Element.animate() creates an animation with the correct timeline when called on an element in a document without a browsing context PASS Element.animate() creates an animation with the correct timeline when called on an element in a document without a browsing context
PASS The timeline associated with an animation trigger on an element in a document without a browsing context is inactive PASS The timeline associated with an animation trigger on an element in a document without a browsing context is inactive
FAIL Replacing the timeline of an animation targetting an element in a document without a browsing context leaves it in the pending state promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" FAIL Replacing the timeline of an animation targetting an element in a document without a browsing context leaves it in the pending state assert_true: The animation should still be pending after replacing the document timeline expected true got false
FAIL Replacing the timeline of an animation targetting an element in a document without a browsing context and then adopting that element causes it to start updating style promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Replacing the timeline of an animation targetting an element in a document without a browsing context and then adopting that element causes it to start updating style
Harness: the test ran to completion. Harness: the test ran to completion.
This is a testharness.js-based test.
PASS All property keys are recognized
PASS Animation.effect produces expected style change events
PASS Animation.startTime produces expected style change events
PASS Animation.currentTime produces expected style change events
PASS Animation.playbackRate produces expected style change events
PASS Animation.playState produces expected style change events
PASS Animation.pending produces expected style change events
PASS Animation.id produces expected style change events
PASS Animation.onfinish produces expected style change events
PASS Animation.oncancel produces expected style change events
PASS Animation.cancel produces expected style change events
PASS Animation.finish produces expected style change events
PASS Animation.pause produces expected style change events
PASS Animation.play produces expected style change events
PASS Animation.reverse produces expected style change events
PASS Animation.updatePlaybackRate produces expected style change events
FAIL Animation.timeline produces expected style change events promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter"
PASS Animation.replaceState produces expected style change events
PASS Animation.onremove produces expected style change events
PASS Animation.finished produces expected style change events
PASS Animation.ready produces expected style change events
PASS Animation.commitStyles produces expected style change events
PASS Animation.persist produces expected style change events
PASS Animation.Animation constructor produces expected style change events
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL After setting timeline on paused animation it is still paused Cannot set property timeline of #<Animation> which has only a getter
FAIL After setting timeline on animation paused outside active interval it is still paused Cannot set property timeline of #<Animation> which has only a getter
FAIL After setting timeline on an idle animation without a start time it is still idle Cannot set property timeline of #<Animation> which has only a getter
FAIL After setting timeline on an idle animation with a start time it is running Cannot set property timeline of #<Animation> which has only a getter
FAIL After setting timeline on an idle animation with a sufficiently ancient start time it is finished Cannot set property timeline of #<Animation> which has only a getter
FAIL After setting timeline on a play-pending animation it begins playing after pending promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter"
FAIL After setting timeline on a pause-pending animation it becomes paused after pending promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter"
FAIL After clearing timeline on paused animation it is still paused Cannot set property timeline of #<Animation> which has only a getter
FAIL After clearing timeline on finished animation it is idle Cannot set property timeline of #<Animation> which has only a getter
FAIL After clearing timeline on running animation it is idle Cannot set property timeline of #<Animation> which has only a getter
FAIL After clearing timeline on idle animation it is still idle Cannot set property timeline of #<Animation> which has only a getter
FAIL After clearing timeline on play-pending animation it is still pending Cannot set property timeline of #<Animation> which has only a getter
FAIL After clearing and re-setting timeline on play-pending animation it begins to play promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter"
FAIL After clearing timeline on a pause-pending animation it is still pending Cannot set property timeline of #<Animation> which has only a getter
FAIL After clearing and re-setting timeline on a pause-pending animation it completes pausing promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter"
FAIL After clearing and re-setting timeline on an animation in the middle of an aborted pause, it continues playing using the same start time promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter"
Harness: the test ran to completion.
...@@ -65,8 +65,8 @@ test(t => { ...@@ -65,8 +65,8 @@ test(t => {
animation.timeline = document.timeline; animation.timeline = document.timeline;
assert_equals(animation.playState, 'running'); assert_equals(animation.playState, 'running');
}, 'After setting timeline on an idle animation with a start time' }, 'After transitioning from a null timeline on an animation with a start time'
+ ' it is running'); + ' it is still running');
test(t => { test(t => {
const animation = const animation =
...@@ -78,8 +78,8 @@ test(t => { ...@@ -78,8 +78,8 @@ test(t => {
animation.timeline = document.timeline; animation.timeline = document.timeline;
assert_equals(animation.playState, 'finished'); assert_equals(animation.playState, 'finished');
}, 'After setting timeline on an idle animation with a sufficiently ancient' }, 'After transitioning from a null timeline on an animation with a ' +
+ ' start time it is finished'); 'sufficiently ancient start time it is finished');
promise_test(async t => { promise_test(async t => {
const animation = const animation =
...@@ -152,9 +152,9 @@ test(t => { ...@@ -152,9 +152,9 @@ test(t => {
animation.timeline = null; animation.timeline = null;
assert_equals(animation.playState, 'idle'); assert_equals(animation.playState, 'running');
assert_times_equal(animation.startTime, initialStartTime); assert_times_equal(animation.startTime, initialStartTime);
}, 'After clearing timeline on finished animation it is idle'); }, 'After clearing timeline on finished animation it is running');
test(t => { test(t => {
const animation = const animation =
...@@ -166,9 +166,9 @@ test(t => { ...@@ -166,9 +166,9 @@ test(t => {
animation.timeline = null; animation.timeline = null;
assert_equals(animation.playState, 'idle'); assert_equals(animation.playState, 'running');
assert_times_equal(animation.startTime, initialStartTime); assert_times_equal(animation.startTime, initialStartTime);
}, 'After clearing timeline on running animation it is idle'); }, 'After clearing timeline on running animation it is still running');
test(t => { test(t => {
const animation = const animation =
......
...@@ -9,8 +9,8 @@ PASS Removes an animation after updating the fill mode of another animation ...@@ -9,8 +9,8 @@ PASS Removes an animation after updating the fill mode of another animation
PASS Removes an animation after updating its fill mode PASS Removes an animation after updating its fill mode
PASS Removes an animation after updating another animation's effect to one with different timing PASS Removes an animation after updating another animation's effect to one with different timing
PASS Removes an animation after updating its effect to one with different timing PASS Removes an animation after updating its effect to one with different timing
FAIL Removes an animation after updating another animation's timeline promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" FAIL Removes an animation after updating another animation's timeline assert_equals: expected "removed" but got "active"
FAIL Removes an animation after updating its timeline promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" FAIL Removes an animation after updating its timeline assert_equals: expected "removed" but got "active"
PASS Removes an animation after updating another animation's effect's properties PASS Removes an animation after updating another animation's effect's properties
PASS Removes an animation after updating its effect's properties PASS Removes an animation after updating its effect's properties
PASS Removes an animation after updating another animation's effect to one with different properties PASS Removes an animation after updating another animation's effect to one with different properties
...@@ -32,11 +32,11 @@ PASS Dispatches an event when removing ...@@ -32,11 +32,11 @@ PASS Dispatches an event when removing
PASS Does NOT dispatch a remove event twice PASS Does NOT dispatch a remove event twice
PASS Does NOT remove an animation after making a redundant change to another animation's current time PASS Does NOT remove an animation after making a redundant change to another animation's current time
PASS Does NOT remove an animation after making a redundant change to its current time PASS Does NOT remove an animation after making a redundant change to its current time
FAIL Does NOT remove an animation after making a redundant change to another animation's timeline promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Does NOT remove an animation after making a redundant change to another animation's timeline
FAIL Does NOT remove an animation after making a redundant change to its timeline promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" PASS Does NOT remove an animation after making a redundant change to its timeline
PASS Does NOT remove an animation after making a redundant change to another animation's effect's properties PASS Does NOT remove an animation after making a redundant change to another animation's effect's properties
PASS Does NOT remove an animation after making a redundant change to its effect's properties PASS Does NOT remove an animation after making a redundant change to its effect's properties
FAIL Updates ALL timelines before checking for replacement promise_test: Unhandled rejection with value: object "TypeError: Cannot set property timeline of #<Animation> which has only a getter" FAIL Updates ALL timelines before checking for replacement assert_equals: expected "removed" but got "active"
PASS Dispatches remove events after finish events PASS Dispatches remove events after finish events
PASS Fires remove event before requestAnimationFrame PASS Fires remove event before requestAnimationFrame
PASS Queues all remove events before running them PASS Queues all remove events before running them
......
...@@ -72,6 +72,7 @@ interface Animation : EventTarget ...@@ -72,6 +72,7 @@ interface Animation : EventTarget
setter onremove setter onremove
setter playbackRate setter playbackRate
setter startTime setter startTime
setter timeline
interface AnimationEffect interface AnimationEffect
attribute @@toStringTag attribute @@toStringTag
method constructor method constructor
......
...@@ -197,6 +197,7 @@ interface Animation : EventTarget ...@@ -197,6 +197,7 @@ interface Animation : EventTarget
setter onremove setter onremove
setter playbackRate setter playbackRate
setter startTime setter startTime
setter timeline
interface AnimationEffect interface AnimationEffect
attribute @@toStringTag attribute @@toStringTag
method constructor method constructor
......
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