Bug 187942 - [Web Animations] REGRESSION: transition added immediately after element creation doesn't work
Summary: [Web Animations] REGRESSION: transition added immediately after element creat...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Animations (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Antoine Quint
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2018-07-23 23:36 PDT by Tim Horton
Modified: 2018-07-26 19:29 PDT (History)
6 users (show)

See Also:


Attachments
repro (1.54 KB, text/html)
2018-07-23 23:36 PDT, Tim Horton
no flags Details
Reduced test case (645 bytes, text/html)
2018-07-24 06:49 PDT, Antoine Quint
no flags Details
Reduced test case (465 bytes, text/html)
2018-07-24 07:24 PDT, Antoine Quint
no flags Details
Patch (10.55 KB, patch)
2018-07-25 14:44 PDT, Antoine Quint
dino: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tim Horton 2018-07-23 23:36:57 PDT
Created attachment 345640 [details]
repro

Steps to Reproduce:

1. With "Web Animations and CSS Integration" turned on:
2. Drag the black box.

Expected: A growing animation.
Actual: Most of the time, the box does not appear until the animation would have completed.

If the animation does run correctly, refresh the page and try again.

With "Web Animations and CSS Integration" turned off, this problem never reproduces.

Reproduces in STP 61.
Comment 1 Antoine Quint 2018-07-24 06:49:01 PDT
Created attachment 345670 [details]
Reduced test case
Comment 2 Antoine Quint 2018-07-24 07:24:26 PDT
Created attachment 345674 [details]
Reduced test case
Comment 3 Antoine Quint 2018-07-24 07:24:56 PDT
Reduced test case shows a consistent failure.
Comment 4 Antoine Quint 2018-07-24 07:30:39 PDT
The computed keyframes after creating the transitions shows that we think we're animating the "transform" property from "none" to "none", ignoring the "scale(0)" initial value.

Animating "opacity" instead from an initial 0 to the default 1 computes the expected keyframes but does not run the animation.

Finally, animating "margin-left" from an initial "100px" to the default "0" computes the expected keyframes _and_ runs the animation.
Comment 5 Antoine Quint 2018-07-24 07:44:19 PDT
getKeyframes() reporting that we're animating from "none" to "none" is a bug of its own, we actually have a scale(0) operation for the from keyframe. So I think this bug is really about an initial transition of a hardware-accelerated property, so looking at the opacity case should get to the bottom of the issue.
Comment 6 Antoine Quint 2018-07-24 07:51:56 PDT
Using a CSS Animation or Web Animation does not reproduce the issue either, so it's specific to CSS Transitions.
Comment 7 Antoine Quint 2018-07-24 13:29:45 PDT
The animation doesn't start because the renderer says it's not composited when entering KeyframeEffectReadOnly::updateAcceleratedAnimationState(). There must be something that the codepath used to generate blending keyframes for CSS Animations and Web Animations does that the one used for CSS Transitions doesn't do.
Comment 8 Antoine Quint 2018-07-24 14:49:13 PDT
Actually doing what the spec says and setting "will-change" to have all the animated properties in KeyframeEffectReadOnly::apply() does the trick.
Comment 9 Antoine Quint 2018-07-25 14:44:02 PDT
Created attachment 345786 [details]
Patch
Comment 10 Dean Jackson 2018-07-25 21:37:15 PDT
Comment on attachment 345786 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=345786&action=review

> Source/WebCore/animation/DocumentTimeline.cpp:433
> +        }

Don't you want an else continue here? You continue from the inner loop but would just loop through the remainder of the animations. unless you need to resolve the style for all of them, but i don't think so.

> LayoutTests/webanimations/accelerated-transition-by-removing-property.html:22
> +setTimeout(() => {
> +    target.style.removeProperty("transform");
> +    target.style.transition = "transform 10s linear";
> +    setTimeout(() => {

You should add a test that has multiple animations, only one of which is hardware composited.
Comment 11 Antoine Quint 2018-07-26 01:21:03 PDT
(In reply to Dean Jackson from comment #10)
> Comment on attachment 345786 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=345786&action=review
> 
> > Source/WebCore/animation/DocumentTimeline.cpp:433
> > +        }
> 
> Don't you want an else continue here? You continue from the inner loop but
> would just loop through the remainder of the animations. unless you need to
> resolve the style for all of them, but i don't think so.

We do want to resolve the styles for all of them, but we can stop looking for accelerated animations once we've found that at least one isn't.

> > LayoutTests/webanimations/accelerated-transition-by-removing-property.html:22
> > +setTimeout(() => {
> > +    target.style.removeProperty("transform");
> > +    target.style.transition = "transform 10s linear";
> > +    setTimeout(() => {
> 
> You should add a test that has multiple animations, only one of which is
> hardware composited.

Good point, will add such a test which will only test there are no layers.
Comment 12 Antoine Quint 2018-07-26 01:33:41 PDT
Committed r234250: <https://trac.webkit.org/changeset/234250>
Comment 13 Radar WebKit Bug Importer 2018-07-26 01:40:18 PDT
<rdar://problem/42616207>
Comment 14 Simon Fraser (smfr) 2018-07-26 19:29:46 PDT
Comment on attachment 345786 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=345786&action=review

> Source/WebCore/animation/DocumentTimeline.cpp:421
> +            if (auto* effect = animation->effect()) {
> +                if (is<KeyframeEffectReadOnly>(effect)) {

is<> is null-safe so you can write this as if (is<KeyframeEffectReadOnly>(animation->effect()))

> Source/WebCore/animation/DocumentTimeline.cpp:422
> +                    auto* keyframeEffect = downcast<KeyframeEffectReadOnly>(effect);

If you've already null-checked it's better to get a reference:
auto& keyframeEffect = downcast<KeyframeEffectReadOnly>(*effect);

> Source/WebCore/animation/DocumentTimeline.cpp:425
> +                            hasNonAcceleratedAnimations = true;

So much nesting.

> Source/WebCore/animation/DocumentTimeline.cpp:429
> +                        if (!hasPendingAcceleratedAnimations)
> +                            hasPendingAcceleratedAnimations = keyframeEffect->hasPendingAcceleratedAction();

hasPendingAcceleratedAnimations |= keyframeEffect->hasPendingAcceleratedAction()

> Source/WebCore/animation/DocumentTimeline.cpp:438
> +    return !hasNonAcceleratedAnimations && hasPendingAcceleratedAnimations;

The !hasNonAcceleratedAnimations is a double negative and hurts my head.