Keyframe animation doesn't 't show up in the Animations timeline
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Jun 2020 16:12:36 +0000 (16:12 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Jun 2020 16:12:36 +0000 (16:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=213441

Reviewed by Brian Burg.

Source/JavaScriptCore:

* inspector/protocol/Animation.json:
An `interationCount` of `Infinity` is not JSON serializable, so represent it as `-1` instead.

Source/WebCore:

Test: inspector/animation/lifecycle-css-animation.html

* inspector/agents/InspectorAnimationAgent.cpp:
(WebCore::buildObjectForEffect):

Source/WebInspectorUI:

* UserInterface/Models/Animation.js:
(WI.Animation):
(WI.Animation.prototype._updateEffect):
An `interationCount` of `Infinity` is not JSON serializable, so represent it as `-1` instead.

* UserInterface/Models/MediaTimelineRecord.js:
(WI.MediaTimelineRecord):
* UserInterface/Views/MediaTimelineDataGridNode.js:
(WI.MediaTimelineDataGridNode.prototype.createCellContent):
(WI.MediaTimelineDataGridNode.prototype.filterableDataForColumn):
The DOM node may not be able to be instrumented if the timeline recording starts before the
frontend has had a chance to request the main document, so it's possible for the DOM node
to not be set.

LayoutTests:

* inspector/animation/resources/lifecycle-utilities.js:
* inspector/animation/lifecycle-css-animation.html:
* inspector/animation/lifecycle-css-animation-expected.txt:

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

15 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/animation/lifecycle-css-animation-expected.txt
LayoutTests/inspector/animation/lifecycle-css-animation.html
LayoutTests/inspector/animation/resources/lifecycle-utilities.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/Animation.json
Source/WebCore/ChangeLog
Source/WebCore/inspector/agents/InspectorAnimationAgent.cpp
Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp
Source/WebCore/inspector/agents/InspectorDOMAgent.cpp
Source/WebCore/inspector/agents/InspectorLayerTreeAgent.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Models/Animation.js
Source/WebInspectorUI/UserInterface/Models/MediaTimelineRecord.js
Source/WebInspectorUI/UserInterface/Views/MediaTimelineDataGridNode.js

index a23f647..cb5daea 100644 (file)
@@ -1,3 +1,14 @@
+2020-06-23  Devin Rousso  <drousso@apple.com>
+
+        Keyframe animation doesn't 't show up in the Animations timeline
+        https://bugs.webkit.org/show_bug.cgi?id=213441
+
+        Reviewed by Brian Burg.
+
+        * inspector/animation/resources/lifecycle-utilities.js:
+        * inspector/animation/lifecycle-css-animation.html:
+        * inspector/animation/lifecycle-css-animation-expected.txt:
+
 2020-06-23  Sergio Villar Senin  <svillar@igalia.com>
 
         [css-flex] Allow indefinite size flex items to be definite wrt resolving percentages inside them
index ddb497a..2c8a6f2 100644 (file)
@@ -2,7 +2,7 @@ Tests for the Animation.animationCreated and Animation.animationDestroyed events
 
 
 == Running test suite: Animation.Lifecycle
--- Running test case: Animation.Lifecycle.CSSAnimation
+-- Running test case: Animation.Lifecycle.CSSAnimation.Finite
 PASS: There should not be any animations.
 PASS: Animation created 'fade-in'.
 PASS: Animation type should be CSS Animation.
@@ -36,3 +36,37 @@ Destroying animations...
 PASS: Animation destroyed.
 PASS: Removed animation has expected ID.
 
+-- Running test case: Animation.Lifecycle.CSSAnimation.Infinite
+PASS: There should not be any animations.
+PASS: Animation created 'fade-in'.
+PASS: Animation type should be CSS Animation.
+startDelay: 100
+iterationCount: Infinity
+iterationDuration: 400
+timingFunction: "linear"
+playbackDirection: "alternate"
+fillMode: "both"
+keyframes:
+[
+  {
+    "offset": 0,
+    "easing": "cubic-bezier(0.1, 0.2, 0.3, 0.4)",
+    "style": "color: rgb(255, 0, 0);\nopacity: 0;"
+  },
+  {
+    "offset": 0.5,
+    "easing": "cubic-bezier(0.1, 0.2, 0.3, 0.4)",
+    "style": "color: rgb(0, 128, 0);\nopacity: 0.5;"
+  },
+  {
+    "offset": 1,
+    "easing": "cubic-bezier(0.1, 0.2, 0.3, 0.4)",
+    "style": "color: rgb(0, 0, 255);\nopacity: 1;"
+  }
+]
+
+Destroying animations...
+
+PASS: Animation destroyed.
+PASS: Removed animation has expected ID.
+
index 46da095..f115ccf 100644 (file)
@@ -23,10 +23,15 @@ div#target.active {
     animation-duration: 400ms;
     animation-timing-function: cubic-bezier(0.1, 0.2, 0.3, 0.4);
     animation-delay: 100ms;
-    animation-iteration-count: 2;
     animation-direction: alternate;
     animation-fill-mode: both;
 }
+div#target.active.finite {
+    animation-iteration-count: 2;
+}
+div#target.active.infinite {
+    animation-iteration-count: infinite;
+}
 </style>
 <script>
 function test()
@@ -34,20 +39,39 @@ function test()
     let suite = InspectorTest.createAsyncSuite("Animation.Lifecycle");
 
     suite.addTestCase({
-        name: "Animation.Lifecycle.CSSAnimation",
+        name: "Animation.Lifecycle.CSSAnimation.Finite",
+        description: "Check that Web Inspector is notified whenever CSS animations are created/destroyed.",
+        async test() {
+            InspectorTest.expectEqual(WI.animationManager.animationCollection.size, 0, "There should not be any animations.");
+
+            let [animation] = await Promise.all([
+                InspectorTest.AnimationLifecycleUtilities.awaitAnimationCreated(WI.Animation.Type.CSSAnimation),
+                InspectorTest.evaluateInPage(`document.getElementById("target").classList.add("active", "finite")`),
+            ]);
+
+            await Promise.all([
+                InspectorTest.AnimationLifecycleUtilities.awaitAnimationDestroyed(animation.animationId),
+                InspectorTest.AnimationLifecycleUtilities.destroyAnimations(),
+                InspectorTest.evaluateInPage(`document.getElementById("target").classList.remove("active", "finite")`),
+            ]);
+        },
+    });
+
+    suite.addTestCase({
+        name: "Animation.Lifecycle.CSSAnimation.Infinite",
         description: "Check that Web Inspector is notified whenever CSS animations are created/destroyed.",
         async test() {
             InspectorTest.expectEqual(WI.animationManager.animationCollection.size, 0, "There should not be any animations.");
 
             let [animation] = await Promise.all([
                 InspectorTest.AnimationLifecycleUtilities.awaitAnimationCreated(WI.Animation.Type.CSSAnimation),
-                InspectorTest.evaluateInPage(`document.getElementById("target").classList.add("active")`),
+                InspectorTest.evaluateInPage(`document.getElementById("target").classList.add("active", "infinite")`),
             ]);
 
             await Promise.all([
                 InspectorTest.AnimationLifecycleUtilities.awaitAnimationDestroyed(animation.animationId),
                 InspectorTest.AnimationLifecycleUtilities.destroyAnimations(),
-                InspectorTest.evaluateInPage(`document.getElementById("target").classList.remove("active")`),
+                InspectorTest.evaluateInPage(`document.getElementById("target").classList.remove("active", "infinite")`),
             ]);
         },
     });
index f05ec38..bd3bdfe 100644 (file)
@@ -84,7 +84,7 @@ TestPage.registerInitializer(() => {
         if (animation.endDelay)
             InspectorTest.log("endDelay: " + JSON.stringify(animation.endDelay));
         if (animation.iterationCount)
-            InspectorTest.log("iterationCount: " + JSON.stringify(animation.iterationCount));
+            InspectorTest.log("iterationCount: " + (animation.iterationCount === Infinity ? "Infinity" : JSON.stringify(animation.iterationCount)));
         if (animation.iterationStart)
             InspectorTest.log("iterationStart: " + JSON.stringify(animation.iterationStart));
         if (animation.iterationDuration)
index b7a713b..5cfa39d 100644 (file)
@@ -1,3 +1,13 @@
+2020-06-23  Devin Rousso  <drousso@apple.com>
+
+        Keyframe animation doesn't 't show up in the Animations timeline
+        https://bugs.webkit.org/show_bug.cgi?id=213441
+
+        Reviewed by Brian Burg.
+
+        * inspector/protocol/Animation.json:
+        An `interationCount` of `Infinity` is not JSON serializable, so represent it as `-1` instead.
+
 2020-06-22  Saam Barati  <sbarati@apple.com>
 
         Attempt to fix watchOS simulator build.
index 119e4e6..0296510 100644 (file)
@@ -42,7 +42,7 @@
             "properties": [
                 { "name": "startDelay", "type": "number", "optional": true },
                 { "name": "endDelay", "type": "number", "optional": true },
-                { "name": "iterationCount", "type": "number", "optional": true, "description": "Number of iterations in the animation." },
+                { "name": "iterationCount", "type": "number", "optional": true, "description": "Number of iterations in the animation. <code>Infinity</code> is represented as <code>-1</code>." },
                 { "name": "iterationStart", "type": "number", "optional": true, "description": "Index of which iteration to start at." },
                 { "name": "iterationDuration", "type": "number", "optional": true, "description": "Total time of each iteration, measured in milliseconds." },
                 { "name": "timingFunction", "type": "string", "optional": true, "description": "CSS timing function of the overall animation." },
index 12844bf..57e171b 100644 (file)
@@ -1,3 +1,15 @@
+2020-06-23  Devin Rousso  <drousso@apple.com>
+
+        Keyframe animation doesn't 't show up in the Animations timeline
+        https://bugs.webkit.org/show_bug.cgi?id=213441
+
+        Reviewed by Brian Burg.
+
+        Test: inspector/animation/lifecycle-css-animation.html
+
+        * inspector/agents/InspectorAnimationAgent.cpp:
+        (WebCore::buildObjectForEffect):
+
 2020-06-23  Sergio Villar Senin  <svillar@igalia.com>
 
         [css-flex] Allow indefinite size flex items to be definite wrt resolving percentages inside them
index 0fb9e93..71325f5 100644 (file)
@@ -197,7 +197,7 @@ static Ref<Inspector::Protocol::Animation::Effect> buildObjectForEffect(Animatio
     if (auto endDelay = protocolValueForSeconds(effect.endDelay()))
         effectPayload->setEndDelay(endDelay.value());
 
-    effectPayload->setIterationCount(effect.iterations());
+    effectPayload->setIterationCount(effect.iterations() == std::numeric_limits<double>::infinity() ? -1 : effect.iterations());
     effectPayload->setIterationStart(effect.iterationStart());
 
     if (auto iterationDuration = protocolValueForSeconds(effect.iterationDuration()))
index c6309ff..d091682 100644 (file)
@@ -193,6 +193,7 @@ void InspectorCanvasAgent::requestNode(ErrorString& errorString, const String& c
         return;
     }
 
+    // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
     int documentNodeId = m_instrumentingAgents.persistentDOMAgent()->boundNodeId(&node->document());
     if (!documentNodeId) {
         errorString = "Document must have been requested"_s;
@@ -225,6 +226,7 @@ void InspectorCanvasAgent::requestClientNodes(ErrorString& errorString, const St
 
     clientNodeIds = JSON::ArrayOf<int>::create();
     for (auto& clientNode : inspectorCanvas->clientNodes()) {
+        // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
         if (auto documentNodeId = domAgent->boundNodeId(&clientNode->document()))
             clientNodeIds->addItem(domAgent->pushNodeToFrontend(errorString, documentNodeId, clientNode));
     }
index 8acc1f6..77f2b93 100644 (file)
@@ -552,6 +552,8 @@ int InspectorDOMAgent::pushNodeToFrontend(Node* nodeToPush)
     if (!nodeToPush)
         return 0;
 
+    // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
+
     ErrorString ignored;
     return pushNodeToFrontend(ignored, boundNodeId(&nodeToPush->document()), nodeToPush);
 }
@@ -653,6 +655,7 @@ int InspectorDOMAgent::pushNodePathToFrontend(ErrorString errorString, Node* nod
         return 0;
     }
 
+    // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
     if (!m_documentNodeToIdMap.contains(m_document)) {
         errorString = "Document must have been requested"_s;
         return 0;
@@ -1128,6 +1131,7 @@ void InspectorDOMAgent::inspect(Node* inspectedNode)
 
 void InspectorDOMAgent::focusNode()
 {
+    // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
     if (!m_documentRequested)
         return;
 
index b20abe8..bb994bf 100644 (file)
@@ -217,8 +217,10 @@ int InspectorLayerTreeAgent::idForNode(ErrorString& errorString, Node* node)
     InspectorDOMAgent* domAgent = m_instrumentingAgents.persistentDOMAgent();
     
     int nodeId = domAgent->boundNodeId(node);
-    if (!nodeId)
+    if (!nodeId) {
+        // FIXME: <https://webkit.org/b/213499> Web Inspector: allow DOM nodes to be instrumented at any point, regardless of whether the main document has also been instrumented
         nodeId = domAgent->pushNodeToFrontend(errorString, domAgent->boundNodeId(&node->document()), node);
+    }
 
     return nodeId;
 }
index 1d09cf6..a0bd316 100644 (file)
@@ -1,3 +1,24 @@
+2020-06-23  Devin Rousso  <drousso@apple.com>
+
+        Keyframe animation doesn't 't show up in the Animations timeline
+        https://bugs.webkit.org/show_bug.cgi?id=213441
+
+        Reviewed by Brian Burg.
+
+        * UserInterface/Models/Animation.js:
+        (WI.Animation):
+        (WI.Animation.prototype._updateEffect):
+        An `interationCount` of `Infinity` is not JSON serializable, so represent it as `-1` instead.
+
+        * UserInterface/Models/MediaTimelineRecord.js:
+        (WI.MediaTimelineRecord):
+        * UserInterface/Views/MediaTimelineDataGridNode.js:
+        (WI.MediaTimelineDataGridNode.prototype.createCellContent):
+        (WI.MediaTimelineDataGridNode.prototype.filterableDataForColumn):
+        The DOM node may not be able to be instrumented if the timeline recording starts before the
+        frontend has had a chance to request the main document, so it's possible for the DOM node
+        to not be set.
+
 2020-06-22  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: adjust height of undocked title area to match macOS Big Sur
index 156e3ed..6b2054c 100644 (file)
@@ -245,10 +245,19 @@ WI.Animation = class Animation extends WI.Object
     {
         this._effect = effect || {};
 
-        if (this._effect.timingFunction)
+        if ("iterationCount" in this._effect) {
+            if (this._effect.iterationCount === -1)
+                this._effect.iterationCount = Infinity;
+            else if (this._effect.iterationCount === null) {
+                // COMPATIBILITY (iOS 14): an iteration count of `Infinity` was not properly handled.
+                this._effect.iterationCount = Infinity;
+            }
+        }
+
+        if ("timingFunction" in this._effect)
             this._effect.timingFunction = WI.CubicBezier.fromString(this._effect.timingFunction);
 
-        if (this._effect.keyframes) {
+        if ("keyframes" in this._effect) {
             for (let keyframe of this._effect.keyframes) {
                 if (keyframe.easing)
                     keyframe.easing = WI.CubicBezier.fromString(keyframe.easing);
index 5de89a3..51b01ff 100644 (file)
@@ -34,8 +34,8 @@ WI.MediaTimelineRecord = class MediaTimelineRecord extends WI.TimelineRecord
 
         this._eventType = eventType;
         this._domNode = domNodeOrInfo;
-        this._domNodeDisplayName = domNodeOrInfo.displayName;
-        this._domNodeCSSPath = domNodeOrInfo instanceof WI.DOMNode ? WI.cssPath(domNodeOrInfo, {full: true}) : domNodeOrInfo.cssPath;
+        this._domNodeDisplayName = domNodeOrInfo?.displayName;
+        this._domNodeCSSPath = domNodeOrInfo instanceof WI.DOMNode ? WI.cssPath(domNodeOrInfo, {full: true}) : domNodeOrInfo?.cssPath;
 
         // Web Animation
         console.assert(trackingAnimationId === undefined || typeof trackingAnimationId === "string");
index 1063f2b..ddf722e 100644 (file)
@@ -57,6 +57,8 @@ WI.MediaTimelineDataGridNode = class MediaTimelineDataGridNode extends WI.Timeli
 
         case "element":
         case "source": // Timeline Overview
+            if (!value)
+                return emDash;
             if (!(value instanceof WI.DOMNode)) {
                 cell.classList.add(WI.DOMTreeElementPathComponent.DOMNodeIconStyleClassName);
                 return value.displayName;
@@ -326,6 +328,7 @@ WI.MediaTimelineDataGridNode = class MediaTimelineDataGridNode extends WI.Timeli
         case "source": // Timeline Overview
             if (this.record.domNode)
                 return this.record.domNode.displayName;
+            break;
         }
 
         return super.filterableDataForColumn(columnIdentifier);