Accelerated animations freeze on render tree rebuild
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Feb 2020 05:56:04 +0000 (05:56 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 4 Feb 2020 05:56:04 +0000 (05:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201048
<rdar://problem/54612621>

Reviewed by Antoine Quint.

Source/WebCore:

If there is an accelerated animation in progress for a renderer and the render tree is rebuild the animation
does not continue with the new renderer.

To fix, make sure that the animation leaves the accelerated state when the renderer is removed. The new renderer
will then become accelerated automatically.

Original test case by Tim Guan-tin Chien.

Test: webanimations/accelerated-animation-renderer-change.html

* animation/AnimationTimeline.cpp:
(WebCore::AnimationTimeline::willChangeRendererForElement):
* animation/AnimationTimeline.h:
* animation/KeyframeEffect.cpp:
(WebCore::KeyframeEffect::willChangeRenderer):
* animation/KeyframeEffect.h:
* animation/WebAnimation.cpp:
(WebCore::WebAnimation::willChangeRenderer):
* animation/WebAnimation.h:
* rendering/updating/RenderTreeUpdater.cpp:
(WebCore::RenderTreeUpdater::tearDownRenderers):

LayoutTests:

* webanimations/accelerated-animation-renderer-change-expected.html: Added.
* webanimations/accelerated-animation-renderer-change.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/webanimations/accelerated-animation-renderer-change-expected.html [new file with mode: 0644]
LayoutTests/webanimations/accelerated-animation-renderer-change.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/animation/AnimationTimeline.cpp
Source/WebCore/animation/AnimationTimeline.h
Source/WebCore/animation/KeyframeEffect.cpp
Source/WebCore/animation/KeyframeEffect.h
Source/WebCore/animation/WebAnimation.cpp
Source/WebCore/animation/WebAnimation.h
Source/WebCore/rendering/updating/RenderTreeUpdater.cpp

index dc13818..b2786de 100644 (file)
@@ -1,3 +1,14 @@
+2020-02-03  Antti Koivisto  <antti@apple.com>
+
+        Accelerated animations freeze on render tree rebuild
+        https://bugs.webkit.org/show_bug.cgi?id=201048
+        <rdar://problem/54612621>
+
+        Reviewed by Antoine Quint.
+
+        * webanimations/accelerated-animation-renderer-change-expected.html: Added.
+        * webanimations/accelerated-animation-renderer-change.html: Added.
+
 2020-02-03  Jacob Uphoff  <jacob_uphoff@apple.com>
 
         [ macOS wk1 ] inspector/unit-tests/server-timing-entry.html is flaky timeout
diff --git a/LayoutTests/webanimations/accelerated-animation-renderer-change-expected.html b/LayoutTests/webanimations/accelerated-animation-renderer-change-expected.html
new file mode 100644 (file)
index 0000000..7a0e078
--- /dev/null
@@ -0,0 +1,15 @@
+<style>
+div {
+    background-color: rebeccapurple;
+    width: 100px;
+    height: 100px;
+}
+
+#test{
+    position: absolute;
+    top: 200px;
+    left: 200px;
+}
+
+</style>
+<div id=test></div>
diff --git a/LayoutTests/webanimations/accelerated-animation-renderer-change.html b/LayoutTests/webanimations/accelerated-animation-renderer-change.html
new file mode 100644 (file)
index 0000000..af480da
--- /dev/null
@@ -0,0 +1,67 @@
+<style>
+
+div {
+    background-color: rebeccapurple;
+    width: 100px;
+    height: 100px;
+}
+
+#test{
+    position: absolute;
+    top: 200px;
+    left: 200px;
+}
+
+.animate-class {
+    animation: 1s linear 0s 1 normal scale;
+}
+
+.first-letter::first-letter {
+    color:red;
+}
+
+@keyframes scale {
+    0% {
+        transform: scale(0);
+    }
+
+    10% {
+        transform: scale(1);
+    }
+
+    100% {
+        transform: scale(1);
+    }
+}
+
+</style>
+<script>
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+window.onload = async function() {
+    const element = document.body.appendChild(document.createElement("div"));
+    element.className = "animate-class";
+    element.id = "test";
+
+    const animation = element.getAnimations()[0];
+
+    await animation.ready;
+
+    await new Promise(resolve => requestAnimationFrame(resolve));
+    await new Promise(resolve => requestAnimationFrame(resolve));
+
+    // Force render tree rebuild in the middle of the animation.
+    document.body.classList.add("first-letter");
+
+    // See that the accelerated animation still progresses.
+    while (animation.currentTime < 100)
+        await new Promise(resolve => requestAnimationFrame(resolve));
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+</script>
+
index 3aa028e..3ee70af 100644 (file)
@@ -1,3 +1,33 @@
+2020-02-03  Antti Koivisto  <antti@apple.com>
+
+        Accelerated animations freeze on render tree rebuild
+        https://bugs.webkit.org/show_bug.cgi?id=201048
+        <rdar://problem/54612621>
+
+        Reviewed by Antoine Quint.
+
+        If there is an accelerated animation in progress for a renderer and the render tree is rebuild the animation
+        does not continue with the new renderer.
+
+        To fix, make sure that the animation leaves the accelerated state when the renderer is removed. The new renderer
+        will then become accelerated automatically.
+
+        Original test case by Tim Guan-tin Chien.
+
+        Test: webanimations/accelerated-animation-renderer-change.html
+
+        * animation/AnimationTimeline.cpp:
+        (WebCore::AnimationTimeline::willChangeRendererForElement):
+        * animation/AnimationTimeline.h:
+        * animation/KeyframeEffect.cpp:
+        (WebCore::KeyframeEffect::willChangeRenderer):
+        * animation/KeyframeEffect.h:
+        * animation/WebAnimation.cpp:
+        (WebCore::WebAnimation::willChangeRenderer):
+        * animation/WebAnimation.h:
+        * rendering/updating/RenderTreeUpdater.cpp:
+        (WebCore::RenderTreeUpdater::tearDownRenderers):
+
 2020-02-03  Andres Gonzalez  <andresg_22@apple.com>
 
         [WebAccessibilityObjectWrapper detach] should detach either the wrapped AXObject or the IsolatedObject but not both.
index e3ba9e7..6cb77e8 100644 (file)
@@ -241,6 +241,12 @@ void AnimationTimeline::removeAnimationsForElement(Element& element)
         animation->remove();
 }
 
+void AnimationTimeline::willChangeRendererForElement(Element& element)
+{
+    for (auto& animation : animationsForElement(element))
+        animation->willChangeRenderer();
+}
+
 void AnimationTimeline::cancelDeclarativeAnimationsForElement(Element& element)
 {
     for (auto& cssTransition : m_elementToCSSTransitionsMap.get(&element))
index 9837d47..7b8a8b8 100644 (file)
@@ -61,6 +61,7 @@ public:
     Vector<RefPtr<WebAnimation>> animationsForElement(Element&, Ordering ordering = Ordering::Unsorted) const;
     void elementWasRemoved(Element&);
     void removeAnimationsForElement(Element&);
+    void willChangeRendererForElement(Element&);
     void cancelDeclarativeAnimationsForElement(Element&);
     virtual void animationWasAddedToElement(WebAnimation&, Element&);
     virtual void animationWasRemovedFromElement(WebAnimation&, Element&);
index 3a70111..36d8f00 100644 (file)
@@ -1395,6 +1395,12 @@ void KeyframeEffect::animationWasCanceled()
         addPendingAcceleratedAction(AcceleratedAction::Stop);
 }
 
+void KeyframeEffect::willChangeRenderer()
+{
+    if (m_isRunningAccelerated)
+        addPendingAcceleratedAction(AcceleratedAction::Stop);
+}
+
 void KeyframeEffect::animationSuspensionStateDidChange(bool animationIsSuspended)
 {
     if (m_isRunningAccelerated)
index 0c9e7e2..4fd006d 100644 (file)
@@ -122,6 +122,8 @@ public:
     void animationTimingDidChange();
     void applyPendingAcceleratedActions();
 
+    void willChangeRenderer();
+
     void setAnimation(WebAnimation*) final;
 
     RenderElement* renderer() const override;
index ce61606..1c530e0 100644 (file)
@@ -675,6 +675,12 @@ void WebAnimation::cancel(Silently silently)
         m_effect->animationWasCanceled();
 }
 
+void WebAnimation::willChangeRenderer()
+{
+    if (is<KeyframeEffect>(m_effect))
+        downcast<KeyframeEffect>(*m_effect).willChangeRenderer();
+}
+
 void WebAnimation::enqueueAnimationPlaybackEvent(const AtomString& type, Optional<Seconds> currentTime, Optional<Seconds> timelineTime)
 {
     auto event = AnimationPlaybackEvent::create(type, currentTime, timelineTime);
index d3b1b98..3032f28 100644 (file)
@@ -123,6 +123,7 @@ public:
     void effectTargetDidChange(Element* previousTarget, Element* newTarget);
     void acceleratedStateDidChange();
     void applyPendingAcceleratedActions();
+    void willChangeRenderer();
 
     bool isRunningAccelerated() const;
     bool isCompletelyAccelerated() const;
index 38c3be0..28d59ee 100644 (file)
@@ -561,7 +561,9 @@ void RenderTreeUpdater::tearDownRenderers(Element& root, TeardownType teardownTy
         while (teardownStack.size() > depth) {
             auto& element = *teardownStack.takeLast();
 
-            if (teardownType == TeardownType::Full || teardownType == TeardownType::RendererUpdateCancelingAnimations) {
+            switch (teardownType) {
+            case TeardownType::Full:
+            case TeardownType::RendererUpdateCancelingAnimations:
                 if (timeline) {
                     if (document.renderTreeBeingDestroyed())
                         timeline->elementWasRemoved(element);
@@ -569,6 +571,11 @@ void RenderTreeUpdater::tearDownRenderers(Element& root, TeardownType teardownTy
                         timeline->cancelDeclarativeAnimationsForElement(element);
                 }
                 animationController.cancelAnimations(element);
+                break;
+            case TeardownType::RendererUpdate:
+                if (timeline)
+                    timeline->willChangeRendererForElement(element);
+                break;
             }
 
             if (teardownType == TeardownType::Full)