Title: [293067] branches/safari-613-branch
Revision
293067
Author
[email protected]
Date
2022-04-19 22:38:01 -0700 (Tue, 19 Apr 2022)

Log Message

Cherry-pick r290201. rdar://problem/88672183

    REGRESSION (r287524): hihello.me does not show sliding sheet at the bottom of the page
    https://bugs.webkit.org/show_bug.cgi?id=236838
    rdar://88672183

    Reviewed by Dean Jackson.

    LayoutTests/imported/w3c:

    Add new WPT tests to check we correctly compute implicit keyframes when a 0% and/or 100% keyframe
    is defined but only specifies a timing function. One test checks the output of getKeyframes() and
    the other that we correctly account for the implicit vaues when computing styles.

    * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt:
    * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html:
    * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function-expected.txt: Added.
    * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html: Added.

    Source/WebCore:

    When we fixed bug 234799 we fixed the behavior of keyframe deduplication in
    Style::Resolver::keyframeRulesForName(). While this was a good fix, code that
    relied on the KeyframeList that would eventually be yielded from that function
    did not quite correctly add implicit keyframes for the 0% and 100% case in some
    relatively obscure situation.

    The site hihello.me made this shortcoming apparent. This site has this odd keyframe rule:

        from, 60%, 75%, 90%, to {
            animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
        }

    It appears the intention of the author with this rule is to replicate the timing function
    on multiple keyframes. However, this does not work. This timing function will not be used
    for *anything* since if a rule is specified without an animation-timing-function value it
    will use the value set on the element, not one on a different keyframe.

    This also means that while there are explicit 0% and 100% keyframes, they are not adequate
    to then compute implicit properties since the timing function wouldn't match the default
    timing function (unless the element that those keyframes are applied to would happen to
    specify that exact same timing function).

    To correctly handle this, we need to do several things.

    First of all, we remove the implicit keyframe code found in KeyframeEffect::getKeyframes()
    and let KeyframeList::fillImplicitKeyframes() do all the work to correctly fill-in values
    for properties not eplicitly specified on a 0% or 100% keyframe.

    This means we need to improve that function to correctly do the task that it's supposed to
    do. Now provided with a KeyframeEffect and an underlying style as parameters, for 0% and
    100% it correctly:

        1. compiles a list of implicit properties for the given keyframe
        2. find a compatible keyframe for the implicit properties or create one
        3. set the implicit properties on that compatible keyframe to match the values found
           in the underlying style

    This correctly takes cares of calls to getKeyframes() as well as the generation of keyframes
    passed down to RenderLayerBacking when starting an accelerated animation.

    But RenderLayerBacking also had some outdated assumptions on the KeyframeList it receives.
    Indeed, it would always assume that a 0% or 100% keyframe would *always* specify properties
    for the animated property. That was incorrect since we correctly deduplicate keyframes and
    having several 0% or 100% keyframes is perfectly valid. Now we don't give special treatment
    to 0% or 100% keyframes in RenderLayerBacking::startAnimation() and always check that a keyframe
    features values for the animated property before creating an animation value to send down
    to GraphicsLayer.

    Finally, another place we make assumptions on implicit keyframes was when resolving styles
    as effects are applied in KeyframeEffect::setAnimatedPropertiesInStyle(). There we assumed
    that a 0% or 100% keyframe would always qualify as a keyframe containing the animated property,
    whereas the steps for resolving styles as specified by the Web Animations specification has
    logic to deal with the case where we don't find a 0% or 100% keyframe with an explicit value
    for the animated property. So we simplified the checks there to only ever check for an
    explicit value.

    This rather obscure way to specify keyframes was not previously tested by WPT, so this patch
    improves the testing coverage in a way that would have caught this regression in the first place.

    Test: imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html

    * animation/KeyframeEffect.cpp:
    (WebCore::KeyframeEffect::getKeyframes):
    (WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):
    (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
    * rendering/RenderLayerBacking.cpp:
    (WebCore::RenderLayerBacking::startAnimation):
    * rendering/style/KeyframeList.cpp:
    (WebCore::KeyframeList::insert):
    (WebCore::KeyframeList::fillImplicitKeyframes):
    * rendering/style/KeyframeList.h:

    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@290201 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Modified Paths

Added Paths

Diff

Modified: branches/safari-613-branch/LayoutTests/imported/w3c/ChangeLog (293066 => 293067)


--- branches/safari-613-branch/LayoutTests/imported/w3c/ChangeLog	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/LayoutTests/imported/w3c/ChangeLog	2022-04-20 05:38:01 UTC (rev 293067)
@@ -1,3 +1,117 @@
+2022-04-19  Alan Coon  <[email protected]>
+
+        Cherry-pick r290201. rdar://problem/88672183
+
+    REGRESSION (r287524): hihello.me does not show sliding sheet at the bottom of the page
+    https://bugs.webkit.org/show_bug.cgi?id=236838
+    rdar://88672183
+    
+    Reviewed by Dean Jackson.
+    
+    LayoutTests/imported/w3c:
+    
+    Add new WPT tests to check we correctly compute implicit keyframes when a 0% and/or 100% keyframe
+    is defined but only specifies a timing function. One test checks the output of getKeyframes() and
+    the other that we correctly account for the implicit vaues when computing styles.
+    
+    * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt:
+    * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html:
+    * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function-expected.txt: Added.
+    * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html: Added.
+    
+    Source/WebCore:
+    
+    When we fixed bug 234799 we fixed the behavior of keyframe deduplication in
+    Style::Resolver::keyframeRulesForName(). While this was a good fix, code that
+    relied on the KeyframeList that would eventually be yielded from that function
+    did not quite correctly add implicit keyframes for the 0% and 100% case in some
+    relatively obscure situation.
+    
+    The site hihello.me made this shortcoming apparent. This site has this odd keyframe rule:
+    
+        from, 60%, 75%, 90%, to {
+            animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+        }
+    
+    It appears the intention of the author with this rule is to replicate the timing function
+    on multiple keyframes. However, this does not work. This timing function will not be used
+    for *anything* since if a rule is specified without an animation-timing-function value it
+    will use the value set on the element, not one on a different keyframe.
+    
+    This also means that while there are explicit 0% and 100% keyframes, they are not adequate
+    to then compute implicit properties since the timing function wouldn't match the default
+    timing function (unless the element that those keyframes are applied to would happen to
+    specify that exact same timing function).
+    
+    To correctly handle this, we need to do several things.
+    
+    First of all, we remove the implicit keyframe code found in KeyframeEffect::getKeyframes()
+    and let KeyframeList::fillImplicitKeyframes() do all the work to correctly fill-in values
+    for properties not eplicitly specified on a 0% or 100% keyframe.
+    
+    This means we need to improve that function to correctly do the task that it's supposed to
+    do. Now provided with a KeyframeEffect and an underlying style as parameters, for 0% and
+    100% it correctly:
+    
+        1. compiles a list of implicit properties for the given keyframe
+        2. find a compatible keyframe for the implicit properties or create one
+        3. set the implicit properties on that compatible keyframe to match the values found
+           in the underlying style
+    
+    This correctly takes cares of calls to getKeyframes() as well as the generation of keyframes
+    passed down to RenderLayerBacking when starting an accelerated animation.
+    
+    But RenderLayerBacking also had some outdated assumptions on the KeyframeList it receives.
+    Indeed, it would always assume that a 0% or 100% keyframe would *always* specify properties
+    for the animated property. That was incorrect since we correctly deduplicate keyframes and
+    having several 0% or 100% keyframes is perfectly valid. Now we don't give special treatment
+    to 0% or 100% keyframes in RenderLayerBacking::startAnimation() and always check that a keyframe
+    features values for the animated property before creating an animation value to send down
+    to GraphicsLayer.
+    
+    Finally, another place we make assumptions on implicit keyframes was when resolving styles
+    as effects are applied in KeyframeEffect::setAnimatedPropertiesInStyle(). There we assumed
+    that a 0% or 100% keyframe would always qualify as a keyframe containing the animated property,
+    whereas the steps for resolving styles as specified by the Web Animations specification has
+    logic to deal with the case where we don't find a 0% or 100% keyframe with an explicit value
+    for the animated property. So we simplified the checks there to only ever check for an
+    explicit value.
+    
+    This rather obscure way to specify keyframes was not previously tested by WPT, so this patch
+    improves the testing coverage in a way that would have caught this regression in the first place.
+    
+    Test: imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html
+    
+    * animation/KeyframeEffect.cpp:
+    (WebCore::KeyframeEffect::getKeyframes):
+    (WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):
+    (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
+    * rendering/RenderLayerBacking.cpp:
+    (WebCore::RenderLayerBacking::startAnimation):
+    * rendering/style/KeyframeList.cpp:
+    (WebCore::KeyframeList::insert):
+    (WebCore::KeyframeList::fillImplicitKeyframes):
+    * rendering/style/KeyframeList.h:
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@290201 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2022-02-19  Antoine Quint  <[email protected]>
+
+            REGRESSION (r287524): hihello.me does not show sliding sheet at the bottom of the page
+            https://bugs.webkit.org/show_bug.cgi?id=236838
+            rdar://88672183
+
+            Reviewed by Dean Jackson.
+
+            Add new WPT tests to check we correctly compute implicit keyframes when a 0% and/or 100% keyframe
+            is defined but only specifies a timing function. One test checks the output of getKeyframes() and
+            the other that we correctly account for the implicit vaues when computing styles.
+
+            * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt:
+            * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html:
+            * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function-expected.txt: Added.
+            * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html: Added.
+
 2022-04-18  Kocsen Chung  <[email protected]>
 
         Cherry-pick r292400. rdar://problem/89382543

Modified: branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt (293066 => 293067)


--- branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt	2022-04-20 05:38:01 UTC (rev 293067)
@@ -24,4 +24,5 @@
 PASS KeyframeEffect.getKeyframes() returns expected values for animations with a CSS variable which is overriden by the value in keyframe
 PASS KeyframeEffect.getKeyframes() returns expected values for animations with only custom property in a keyframe
 PASS KeyframeEffect.getKeyframes() reflects changes to @keyframes rules
+PASS KeyframeEffect.getKeyframes() returns expected values for animations with implicit values and a non-default timingfunction specified for 0% and 100%
 

Modified: branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html (293066 => 293067)


--- branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html	2022-04-20 05:38:01 UTC (rev 293067)
@@ -156,6 +156,12 @@
   from { transform: translate(100px, 0) }
   to { --not-used: 200px }
 }
+
+@keyframes anim-only-timing-function-for-from-and-to {
+    from, to { animation-timing-function: linear }
+    50% { left: 10px }
+}
+
 </style>
 <body>
 <div id="log"></div>
@@ -696,5 +702,23 @@
   );
 }, 'KeyframeEffect.getKeyframes() reflects changes to @keyframes rules');
 
+test(t => {
+  const div = addDiv(t);
+  div.style.animation = 'anim-only-timing-function-for-from-and-to 100s';
+
+  const frames = getKeyframes(div);
+
+  const expected = [
+    { offset: 0,   computedOffset: 0,   easing: "ease",   composite: "auto", left: "auto" },
+    { offset: 0,   computedOffset: 0,   easing: "linear", composite: "auto" },
+    { offset: 0.5, computedOffset: 0.5, easing: "ease",   composite: "auto", left: "10px" },
+    { offset: 1,   computedOffset: 1,   easing: "linear", composite: "auto" },
+    { offset: 1,   computedOffset: 1,   easing: "ease",   composite: "auto", left: "auto" }
+  ];
+  assert_frame_lists_equal(frames, expected);
+}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
+   'animations with implicit values and a non-default timing' +
+   'function specified for 0% and 100%');
+
 </script>
 </body>

Added: branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function-expected.txt (0 => 293067)


--- branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function-expected.txt	                        (rev 0)
+++ branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function-expected.txt	2022-04-20 05:38:01 UTC (rev 293067)
@@ -0,0 +1,3 @@
+
+PASS Animations should not use an implicit value when the first rule within a @keyframes rule specifies "from" and "to" but only specifies a timing function.
+

Added: branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html (0 => 293067)


--- branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html	                        (rev 0)
+++ branches/safari-613-branch/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html	2022-04-20 05:38:01 UTC (rev 293067)
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Multiple from and to keyframes</title>
+<link rel="help" href=""
+<script src=""
+<script src=""
+<script src=""
+<style>
+
+@keyframes multiple-from-to-keyframes {
+    from, to { animation-timing-function: cubic-bezier(0, 0, 1, 1) }
+    from { left: 50px }
+    to { left: 150px }
+}
+
+</style>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(t => {
+  const div = addDiv(t);
+  div.style.animation = 'multiple-from-to-keyframes 100s -50s linear';
+  assert_equals(getComputedStyle(div).left, "100px");
+}, 'Animations should not use an implicit value when the first rule within a @keyframes rule specifies "from" and "to" but only specifies a timing function.');
+
+</script>

Modified: branches/safari-613-branch/Source/WebCore/ChangeLog (293066 => 293067)


--- branches/safari-613-branch/Source/WebCore/ChangeLog	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/Source/WebCore/ChangeLog	2022-04-20 05:38:01 UTC (rev 293067)
@@ -1,3 +1,180 @@
+2022-04-19  Alan Coon  <[email protected]>
+
+        Cherry-pick r290201. rdar://problem/88672183
+
+    REGRESSION (r287524): hihello.me does not show sliding sheet at the bottom of the page
+    https://bugs.webkit.org/show_bug.cgi?id=236838
+    rdar://88672183
+    
+    Reviewed by Dean Jackson.
+    
+    LayoutTests/imported/w3c:
+    
+    Add new WPT tests to check we correctly compute implicit keyframes when a 0% and/or 100% keyframe
+    is defined but only specifies a timing function. One test checks the output of getKeyframes() and
+    the other that we correctly account for the implicit vaues when computing styles.
+    
+    * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative-expected.txt:
+    * web-platform-tests/css/css-animations/KeyframeEffect-getKeyframes.tentative.html:
+    * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function-expected.txt: Added.
+    * web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html: Added.
+    
+    Source/WebCore:
+    
+    When we fixed bug 234799 we fixed the behavior of keyframe deduplication in
+    Style::Resolver::keyframeRulesForName(). While this was a good fix, code that
+    relied on the KeyframeList that would eventually be yielded from that function
+    did not quite correctly add implicit keyframes for the 0% and 100% case in some
+    relatively obscure situation.
+    
+    The site hihello.me made this shortcoming apparent. This site has this odd keyframe rule:
+    
+        from, 60%, 75%, 90%, to {
+            animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+        }
+    
+    It appears the intention of the author with this rule is to replicate the timing function
+    on multiple keyframes. However, this does not work. This timing function will not be used
+    for *anything* since if a rule is specified without an animation-timing-function value it
+    will use the value set on the element, not one on a different keyframe.
+    
+    This also means that while there are explicit 0% and 100% keyframes, they are not adequate
+    to then compute implicit properties since the timing function wouldn't match the default
+    timing function (unless the element that those keyframes are applied to would happen to
+    specify that exact same timing function).
+    
+    To correctly handle this, we need to do several things.
+    
+    First of all, we remove the implicit keyframe code found in KeyframeEffect::getKeyframes()
+    and let KeyframeList::fillImplicitKeyframes() do all the work to correctly fill-in values
+    for properties not eplicitly specified on a 0% or 100% keyframe.
+    
+    This means we need to improve that function to correctly do the task that it's supposed to
+    do. Now provided with a KeyframeEffect and an underlying style as parameters, for 0% and
+    100% it correctly:
+    
+        1. compiles a list of implicit properties for the given keyframe
+        2. find a compatible keyframe for the implicit properties or create one
+        3. set the implicit properties on that compatible keyframe to match the values found
+           in the underlying style
+    
+    This correctly takes cares of calls to getKeyframes() as well as the generation of keyframes
+    passed down to RenderLayerBacking when starting an accelerated animation.
+    
+    But RenderLayerBacking also had some outdated assumptions on the KeyframeList it receives.
+    Indeed, it would always assume that a 0% or 100% keyframe would *always* specify properties
+    for the animated property. That was incorrect since we correctly deduplicate keyframes and
+    having several 0% or 100% keyframes is perfectly valid. Now we don't give special treatment
+    to 0% or 100% keyframes in RenderLayerBacking::startAnimation() and always check that a keyframe
+    features values for the animated property before creating an animation value to send down
+    to GraphicsLayer.
+    
+    Finally, another place we make assumptions on implicit keyframes was when resolving styles
+    as effects are applied in KeyframeEffect::setAnimatedPropertiesInStyle(). There we assumed
+    that a 0% or 100% keyframe would always qualify as a keyframe containing the animated property,
+    whereas the steps for resolving styles as specified by the Web Animations specification has
+    logic to deal with the case where we don't find a 0% or 100% keyframe with an explicit value
+    for the animated property. So we simplified the checks there to only ever check for an
+    explicit value.
+    
+    This rather obscure way to specify keyframes was not previously tested by WPT, so this patch
+    improves the testing coverage in a way that would have caught this regression in the first place.
+    
+    Test: imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html
+    
+    * animation/KeyframeEffect.cpp:
+    (WebCore::KeyframeEffect::getKeyframes):
+    (WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):
+    (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
+    * rendering/RenderLayerBacking.cpp:
+    (WebCore::RenderLayerBacking::startAnimation):
+    * rendering/style/KeyframeList.cpp:
+    (WebCore::KeyframeList::insert):
+    (WebCore::KeyframeList::fillImplicitKeyframes):
+    * rendering/style/KeyframeList.h:
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@290201 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2022-02-19  Antoine Quint  <[email protected]>
+
+            REGRESSION (r287524): hihello.me does not show sliding sheet at the bottom of the page
+            https://bugs.webkit.org/show_bug.cgi?id=236838
+            rdar://88672183
+
+            Reviewed by Dean Jackson.
+
+            When we fixed bug 234799 we fixed the behavior of keyframe deduplication in
+            Style::Resolver::keyframeRulesForName(). While this was a good fix, code that
+            relied on the KeyframeList that would eventually be yielded from that function
+            did not quite correctly add implicit keyframes for the 0% and 100% case in some
+            relatively obscure situation.
+
+            The site hihello.me made this shortcoming apparent. This site has this odd keyframe rule:
+
+                from, 60%, 75%, 90%, to {
+                    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+                }
+
+            It appears the intention of the author with this rule is to replicate the timing function
+            on multiple keyframes. However, this does not work. This timing function will not be used
+            for *anything* since if a rule is specified without an animation-timing-function value it
+            will use the value set on the element, not one on a different keyframe.
+
+            This also means that while there are explicit 0% and 100% keyframes, they are not adequate
+            to then compute implicit properties since the timing function wouldn't match the default
+            timing function (unless the element that those keyframes are applied to would happen to
+            specify that exact same timing function).
+
+            To correctly handle this, we need to do several things.
+
+            First of all, we remove the implicit keyframe code found in KeyframeEffect::getKeyframes()
+            and let KeyframeList::fillImplicitKeyframes() do all the work to correctly fill-in values
+            for properties not eplicitly specified on a 0% or 100% keyframe.
+
+            This means we need to improve that function to correctly do the task that it's supposed to
+            do. Now provided with a KeyframeEffect and an underlying style as parameters, for 0% and
+            100% it correctly:
+
+                1. compiles a list of implicit properties for the given keyframe
+                2. find a compatible keyframe for the implicit properties or create one
+                3. set the implicit properties on that compatible keyframe to match the values found
+                   in the underlying style
+
+            This correctly takes cares of calls to getKeyframes() as well as the generation of keyframes
+            passed down to RenderLayerBacking when starting an accelerated animation.
+
+            But RenderLayerBacking also had some outdated assumptions on the KeyframeList it receives.
+            Indeed, it would always assume that a 0% or 100% keyframe would *always* specify properties
+            for the animated property. That was incorrect since we correctly deduplicate keyframes and
+            having several 0% or 100% keyframes is perfectly valid. Now we don't give special treatment
+            to 0% or 100% keyframes in RenderLayerBacking::startAnimation() and always check that a keyframe
+            features values for the animated property before creating an animation value to send down
+            to GraphicsLayer.
+
+            Finally, another place we make assumptions on implicit keyframes was when resolving styles
+            as effects are applied in KeyframeEffect::setAnimatedPropertiesInStyle(). There we assumed
+            that a 0% or 100% keyframe would always qualify as a keyframe containing the animated property,
+            whereas the steps for resolving styles as specified by the Web Animations specification has
+            logic to deal with the case where we don't find a 0% or 100% keyframe with an explicit value
+            for the animated property. So we simplified the checks there to only ever check for an
+            explicit value.
+
+            This rather obscure way to specify keyframes was not previously tested by WPT, so this patch
+            improves the testing coverage in a way that would have caught this regression in the first place.
+
+            Test: imported/w3c/web-platform-tests/css/css-animations/animation-multiple-from-to-keyframes-with-only-timing-function.html
+
+            * animation/KeyframeEffect.cpp:
+            (WebCore::KeyframeEffect::getKeyframes):
+            (WebCore::KeyframeEffect::setAnimatedPropertiesInStyle):
+            (WebCore::KeyframeEffect::applyPendingAcceleratedActions):
+            * rendering/RenderLayerBacking.cpp:
+            (WebCore::RenderLayerBacking::startAnimation):
+            * rendering/style/KeyframeList.cpp:
+            (WebCore::KeyframeList::insert):
+            (WebCore::KeyframeList::fillImplicitKeyframes):
+            * rendering/style/KeyframeList.h:
+
 2022-04-19  Russell Epstein  <[email protected]>
 
         Apply patch. rdar://problem/90968659

Modified: branches/safari-613-branch/Source/WebCore/animation/KeyframeEffect.cpp (293066 => 293067)


--- branches/safari-613-branch/Source/WebCore/animation/KeyframeEffect.cpp	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/Source/WebCore/animation/KeyframeEffect.cpp	2022-04-20 05:38:01 UTC (rev 293067)
@@ -641,7 +641,7 @@
 
         KeyframeList computedKeyframes(m_blendingKeyframes.animationName());
         computedKeyframes.copyKeyframes(m_blendingKeyframes);
-        computedKeyframes.fillImplicitKeyframes(*m_target, m_target->styleResolver(), lastStyleChangeEventStyle, nullptr);
+        computedKeyframeList.fillImplicitKeyframes(*this, elementStyle);
 
         auto keyframeRules = [&]() -> const Vector<Ref<StyleRuleKeyframe>> {
             if (!is<CSSAnimation>(animation()))
@@ -676,23 +676,7 @@
             }
         }
 
-        // We need to establish which properties are implicit for 0% and 100%.
-        HashSet<CSSPropertyID> zeroKeyframeProperties = computedKeyframes.properties();
-        HashSet<CSSPropertyID> _oneKeyframeProperties_ = computedKeyframes.properties();
-        zeroKeyframeProperties.remove(CSSPropertyCustom);
-        oneKeyframeProperties.remove(CSSPropertyCustom);
         for (size_t i = 0; i < computedKeyframes.size(); ++i) {
-            auto& keyframe = computedKeyframes[i];
-            if (!keyframe.key()) {
-                for (auto cssPropertyId : keyframe.properties())
-                    zeroKeyframeProperties.remove(cssPropertyId);
-            } else if (keyframe.key() == 1) {
-                for (auto cssPropertyId : keyframe.properties())
-                    oneKeyframeProperties.remove(cssPropertyId);
-            }
-        }
-
-        for (size_t i = 0; i < computedKeyframes.size(); ++i) {
             // 1. Initialize a dictionary object, output keyframe, using the following definition:
             //
             // dictionary BaseComputedKeyframe {
@@ -1570,12 +1554,8 @@
         for (size_t i = 0; i < m_blendingKeyframes.size(); ++i) {
             auto& keyframe = m_blendingKeyframes[i];
             auto offset = keyframe.key();
-            if (!keyframe.containsProperty(cssPropertyId)) {
-                // If we're dealing with a CSS animation, we consider the first and last keyframes to always have the property listed
-                // since the underlying style was provided and should be captured.
-                if (m_blendingKeyframesSource == BlendingKeyframesSource::WebAnimation || (offset && offset < 1))
-                    continue;
-            }
+            if (!keyframe.containsProperty(cssPropertyId))
+                continue;
             if (!offset)
                 numberOfKeyframesWithZeroOffset++;
             if (offset == 1)
@@ -1898,7 +1878,7 @@
 
         KeyframeList explicitKeyframes(m_blendingKeyframes.animationName());
         explicitKeyframes.copyKeyframes(m_blendingKeyframes);
-        explicitKeyframes.fillImplicitKeyframes(*m_target, m_target->styleResolver(), underlyingStyle.get(), nullptr);
+        explicitKeyframes.fillImplicitKeyframes(*this, *underlyingStyle);
         return renderer->startAnimation(timeOffset, backingAnimationForCompositedRenderer(), explicitKeyframes) ? RunningAccelerated::Yes : RunningAccelerated::No;
     };
 

Modified: branches/safari-613-branch/Source/WebCore/rendering/RenderLayerBacking.cpp (293066 => 293067)


--- branches/safari-613-branch/Source/WebCore/rendering/RenderLayerBacking.cpp	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/Source/WebCore/rendering/RenderLayerBacking.cpp	2022-04-20 05:38:01 UTC (rev 293067)
@@ -3694,27 +3694,26 @@
             
         auto* tf = currentKeyframe.timingFunction();
         
-        bool isFirstOrLastKeyframe = key == 0 || key == 1;
-        if ((hasRotate && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyRotate))
+        if (currentKeyframe.containsProperty(CSSPropertyRotate))
             rotateVector.insert(makeUnique<TransformAnimationValue>(key, keyframeStyle->rotate(), tf));
 
-        if ((hasScale && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyScale))
+        if (currentKeyframe.containsProperty(CSSPropertyScale))
             scaleVector.insert(makeUnique<TransformAnimationValue>(key, keyframeStyle->scale(), tf));
 
-        if ((hasTranslate && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyTranslate))
+        if (currentKeyframe.containsProperty(CSSPropertyTranslate))
             translateVector.insert(makeUnique<TransformAnimationValue>(key, keyframeStyle->translate(), tf));
 
-        if ((hasTransform && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyTransform))
+        if (currentKeyframe.containsProperty(CSSPropertyTransform))
             transformVector.insert(makeUnique<TransformAnimationValue>(key, keyframeStyle->transform(), tf));
 
-        if ((hasOpacity && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyOpacity))
+        if (currentKeyframe.containsProperty(CSSPropertyOpacity))
             opacityVector.insert(makeUnique<FloatAnimationValue>(key, keyframeStyle->opacity(), tf));
 
-        if ((hasFilter && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyFilter))
+        if (currentKeyframe.containsProperty(CSSPropertyFilter))
             filterVector.insert(makeUnique<FilterAnimationValue>(key, keyframeStyle->filter(), tf));
 
 #if ENABLE(FILTERS_LEVEL_2)
-        if ((hasBackdropFilter && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyWebkitBackdropFilter))
+        if (currentKeyframe.containsProperty(CSSPropertyWebkitBackdropFilter))
             backdropFilterVector.insert(makeUnique<FilterAnimationValue>(key, keyframeStyle->backdropFilter(), tf));
 #endif
     }

Modified: branches/safari-613-branch/Source/WebCore/rendering/style/KeyframeList.cpp (293066 => 293067)


--- branches/safari-613-branch/Source/WebCore/rendering/style/KeyframeList.cpp	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/Source/WebCore/rendering/style/KeyframeList.cpp	2022-04-20 05:38:01 UTC (rev 293067)
@@ -23,8 +23,12 @@
 #include "KeyframeList.h"
 
 #include "Animation.h"
+#include "CSSAnimation.h"
 #include "CSSKeyframeRule.h"
 #include "CSSPropertyAnimation.h"
+#include "CompositeOperation.h"
+#include "Element.h"
+#include "KeyframeEffect.h"
 #include "RenderObject.h"
 #include "StyleResolver.h"
 
@@ -69,7 +73,7 @@
             break;
         }
     }
-    
+
     if (!inserted)
         m_keyframes.append(WTFMove(keyframe));
 
@@ -116,22 +120,90 @@
     return rule.get().get();
 }
 
-void KeyframeList::fillImplicitKeyframes(const Element& element, Style::Resolver& styleResolver, const RenderStyle* elementStyle, const RenderStyle* parentElementStyle)
+void KeyframeList::fillImplicitKeyframes(const KeyframeEffect& effect, const RenderStyle& underlyingStyle)
 {
-    // If the 0% keyframe is missing, create it (but only if there is at least one other keyframe).
-    auto initialSize = size();
-    if (initialSize > 0 && m_keyframes[0].key()) {
-        KeyframeValue keyframeValue(0, nullptr);
-        keyframeValue.setStyle(styleResolver.styleForKeyframe(element, elementStyle, { parentElementStyle }, &zeroPercentKeyframe(), keyframeValue));
-        insert(WTFMove(keyframeValue));
+    if (isEmpty())
+        return;
+
+    ASSERT(effect.target());
+    auto& element = *effect.target();
+    auto& styleResolver = element.styleResolver();
+
+    // We need to establish which properties are implicit for 0% and 100%.
+    // We start each list off with the full list of properties, and see if
+    // any 0% and 100% keyframes specify them.
+    auto zeroKeyframeImplicitProperties = m_properties;
+    auto _oneKeyframeImplicitProperties_ = m_properties;
+    zeroKeyframeImplicitProperties.remove(CSSPropertyCustom);
+    oneKeyframeImplicitProperties.remove(CSSPropertyCustom);
+
+    KeyframeValue* implicitZeroKeyframe = nullptr;
+    KeyframeValue* implicitOneKeyframe = nullptr;
+
+    auto isSuitableKeyframeForImplicitValues = [&](const KeyframeValue& keyframe) {
+        auto* timingFunction = keyframe.timingFunction();
+
+        // If there is no timing function set on the keyframe, then it uses the element's
+        // timing function, which makes this keyframe suitable.
+        if (!timingFunction)
+            return true;
+
+        if (auto* cssAnimation = dynamicDowncast<CSSAnimation>(effect.animation())) {
+            auto* animationWideTimingFunction = cssAnimation->backingAnimation().defaultTimingFunctionForKeyframes();
+            // If we're dealing with a CSS Animation and if that CSS Animation's backing animation
+            // has a default timing function set, then if that keyframe's timing function matches,
+            // that keyframe is suitable.
+            if (animationWideTimingFunction)
+                return timingFunction == animationWideTimingFunction;
+            // Otherwise, the keyframe will be suitable if its timing function matches the default.
+            return timingFunction == &CubicBezierTimingFunction::defaultTimingFunction();
+        }
+
+        return false;
+    };
+
+    for (auto& keyframe : m_keyframes) {
+        if (!keyframe.key()) {
+            for (auto cssPropertyId : keyframe.properties())
+                zeroKeyframeImplicitProperties.remove(cssPropertyId);
+            if (!implicitZeroKeyframe && isSuitableKeyframeForImplicitValues(keyframe))
+                implicitZeroKeyframe = &keyframe;
+        } else if (keyframe.key() == 1) {
+            for (auto cssPropertyId : keyframe.properties())
+                oneKeyframeImplicitProperties.remove(cssPropertyId);
+            if (!implicitOneKeyframe && isSuitableKeyframeForImplicitValues(keyframe))
+                implicitOneKeyframe = &keyframe;
+        }
     }
 
-    // If the 100% keyframe is missing, create it (but only if there is at least one other keyframe).
-    if (initialSize > 0 && (m_keyframes[size() - 1].key() != 1)) {
-        KeyframeValue keyframeValue(1, nullptr);
-        keyframeValue.setStyle(styleResolver.styleForKeyframe(element, elementStyle, { parentElementStyle }, &hundredPercentKeyframe(), keyframeValue));
-        insert(WTFMove(keyframeValue));
-    }
+    auto addImplicitKeyframe = [&](double key, const HashSet<CSSPropertyID>& implicitProperties, const StyleRuleKeyframe& keyframeRule, KeyframeValue* existingImplicitKeyframeValue) {
+        // If we're provided an existing implicit keyframe, we need to add all the styles for the implicit properties.
+        if (existingImplicitKeyframeValue) {
+            ASSERT(existingImplicitKeyframeValue->style());
+            auto keyframeStyle = RenderStyle::clonePtr(*existingImplicitKeyframeValue->style());
+            for (auto cssPropertyId : implicitProperties) {
+                CSSPropertyAnimation::blendProperties(&effect, cssPropertyId, *keyframeStyle, underlyingStyle, underlyingStyle, 1, CompositeOperation::Replace);
+                existingImplicitKeyframeValue->addProperty(cssPropertyId);
+            }
+            existingImplicitKeyframeValue->setStyle(WTFMove(keyframeStyle));
+            return;
+        }
+
+        // Otherwise we create a new keyframe.
+        KeyframeValue keyframeValue(key, nullptr);
+        keyframeValue.setStyle(styleResolver.styleForKeyframe(element, underlyingStyle, { nullptr }, keyframeRule, keyframeValue));
+        for (auto cssPropertyId : implicitProperties)
+            keyframeValue.addProperty(cssPropertyId);
+        if (!key)
+            m_keyframes.insert(0, WTFMove(keyframeValue));
+        else
+            m_keyframes.append(WTFMove(keyframeValue));
+    };
+
+    if (!zeroKeyframeImplicitProperties.isEmpty())
+        addImplicitKeyframe(0, zeroKeyframeImplicitProperties, zeroPercentKeyframe(), implicitZeroKeyframe);
+    if (!oneKeyframeImplicitProperties.isEmpty())
+        addImplicitKeyframe(1, oneKeyframeImplicitProperties, hundredPercentKeyframe(), implicitOneKeyframe);
 }
 
 bool KeyframeList::containsAnimatableProperty() const

Modified: branches/safari-613-branch/Source/WebCore/rendering/style/KeyframeList.h (293066 => 293067)


--- branches/safari-613-branch/Source/WebCore/rendering/style/KeyframeList.h	2022-04-20 05:37:55 UTC (rev 293066)
+++ branches/safari-613-branch/Source/WebCore/rendering/style/KeyframeList.h	2022-04-20 05:38:01 UTC (rev 293067)
@@ -32,7 +32,7 @@
 
 namespace WebCore {
 
-class Element;
+class KeyframeEffect;
 class RenderStyle;
 class TimingFunction;
 
@@ -101,8 +101,9 @@
 
     void copyKeyframes(KeyframeList&);
     bool hasImplicitKeyframes() const;
-    void fillImplicitKeyframes(const Element&, Style::Resolver&, const RenderStyle* elementStyle, const RenderStyle* parentElementStyle);
+    void fillImplicitKeyframes(const KeyframeEffect&, const RenderStyle& elementStyle);
 
+
 private:
     AtomString m_animationName;
     Vector<KeyframeValue> m_keyframes; // Kept sorted by key.
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to