Keyframe animation doesn't 't show up in the Animations timeline
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Views / MediaTimelineDataGridNode.js
index a18f471a5de06a1300d2f6176c03e2dc2e85a888..ddf722ef18056ffa31e9bf7f20afc340f1ed1816 100644 (file)
 
 WI.MediaTimelineDataGridNode = class MediaTimelineDataGridNode extends WI.TimelineDataGridNode
 {
-    constructor(record, graphDataSource)
+    constructor(record, options = {})
     {
         console.assert(record instanceof WI.MediaTimelineRecord);
 
-        const includesGraph = false;
-        super(includesGraph, graphDataSource);
-
-        this._records = [record];
+        super([record], options);
     }
 
     // Public
 
-    get records() { return this._records; }
-
     get data()
     {
         if (this._cachedData)
@@ -46,11 +41,8 @@ WI.MediaTimelineDataGridNode = class MediaTimelineDataGridNode extends WI.Timeli
 
         this._cachedData = super.data;
         this._cachedData.name = this.record.displayName;
-        if (this.record.domNode)
-            this._cachedData.element = this.record.domNode;
-        this._cachedData.time = this.record.startTime - (this.graphDataSource ? this.graphDataSource.zeroTime : 0);
-        if (this.record.eventType === WI.MediaTimelineRecord.EventType.DOMEvent && this.record.domEvent.originator)
-            this._cachedData.originator = this.record.domEvent.originator;
+        this._cachedData.element = this.record.domNode;
+        this._cachedData.source = this.record.domNode; // Timeline Overview
         return this._cachedData;
     }
 
@@ -61,45 +53,300 @@ WI.MediaTimelineDataGridNode = class MediaTimelineDataGridNode extends WI.Timeli
         switch (columnIdentifier) {
         case "name":
             cell.classList.add(...this.iconClassNames());
-            return value;
+            return this._createNameCellDocumentFragment();
 
         case "element":
-            return value ? WI.linkifyNodeReference(value) : emDash;
-
-        case "time": {
-            const higherResolution = true;
-            return Number.secondsToString(value, higherResolution);
-        }
-
-        case "originator":
-            return value ? WI.linkifyNodeReference(value) : zeroWidthSpace;
+        case "source": // Timeline Overview
+            if (!value)
+                return emDash;
+            if (!(value instanceof WI.DOMNode)) {
+                cell.classList.add(WI.DOMTreeElementPathComponent.DOMNodeIconStyleClassName);
+                return value.displayName;
+            }
+            break;
         }
 
         return super.createCellContent(columnIdentifier, cell);
     }
 
-    iconClassNames()
+    // TimelineRecordBar delegate
+
+    timelineRecordBarCustomChildren(timelineRecordBar)
     {
-        let iconClassNames = super.iconClassNames();
-        if (this.record.eventType === WI.MediaTimelineRecord.EventType.DOMEvent && this.record.domEvent.eventName === "webkitfullscreenchange")
-            iconClassNames.push("fullscreen");
-        return iconClassNames;
+        let children = [];
+
+        let record = this.record;
+        let timestamps = record.timestamps;
+
+        switch (record.eventType) {
+        case WI.MediaTimelineRecord.EventType.CSSAnimation:
+        case WI.MediaTimelineRecord.EventType.CSSTransition: {
+            let readyStartTime = NaN;
+            function addReadySegment(startTime, endTime) {
+                children.push({
+                    startTime,
+                    endTime,
+                    classNames: ["segment", "css-animation-ready"],
+                    title: WI.UIString("Ready", "Tooltip for a time range bar that represents when a CSS animation/transition exists but has not started processing"),
+                });
+                readyStartTime = NaN;
+            }
+
+            let delayStartTime = NaN;
+            function addDelaySegment(startTime, endTime) {
+                children.push({
+                    startTime,
+                    endTime,
+                    classNames: ["segment", "css-animation-delay"],
+                    title: WI.UIString("Delay", "Tooltip for a time range bar that represents when a CSS animation/transition is delayed"),
+                });
+                delayStartTime = NaN;
+            }
+
+            let activeStartTime = NaN;
+            function addActiveSegment(startTime, endTime) {
+                children.push({
+                    startTime,
+                    endTime,
+                    classNames: ["segment", "css-animation-active"],
+                    title: WI.UIString("Active", "Tooltip for a time range bar that represents when a CSS animation/transition is running"),
+                });
+                activeStartTime = NaN;
+            }
+
+            for (let item of timestamps) {
+                switch (item.type) {
+                case WI.MediaTimelineRecord.TimestampType.CSSAnimationReady:
+                    if (isNaN(readyStartTime))
+                        readyStartTime = item.timestamp;
+                    break;
+                case WI.MediaTimelineRecord.TimestampType.CSSAnimationDelay:
+                    if (isNaN(delayStartTime))
+                        delayStartTime = item.timestamp;
+                    if (!isNaN(readyStartTime))
+                        addReadySegment(readyStartTime, item.timestamp);
+                    break;
+                case WI.MediaTimelineRecord.TimestampType.CSSAnimationActive:
+                    if (isNaN(activeStartTime))
+                        activeStartTime = item.timestamp;
+                    if (!isNaN(readyStartTime))
+                        addReadySegment(readyStartTime, item.timestamp);
+                    if (!isNaN(delayStartTime))
+                        addDelaySegment(delayStartTime, item.timestamp);
+                    break;
+                case WI.MediaTimelineRecord.TimestampType.CSSAnimationCancel:
+                case WI.MediaTimelineRecord.TimestampType.CSSAnimationDone:
+                    if (!isNaN(readyStartTime))
+                        addReadySegment(readyStartTime, item.timestamp);
+                    if (!isNaN(delayStartTime))
+                        addDelaySegment(delayStartTime, item.timestamp);
+                    if (!isNaN(activeStartTime))
+                        addActiveSegment(activeStartTime, item.timestamp);
+                    break;
+                }
+            }
+
+            if (!isNaN(readyStartTime))
+                addReadySegment(readyStartTime, NaN);
+            if (!isNaN(delayStartTime))
+                addDelaySegment(delayStartTime, NaN);
+            if (!isNaN(activeStartTime))
+                addActiveSegment(activeStartTime, NaN);
+
+            break;
+        }
+
+        case WI.MediaTimelineRecord.EventType.MediaElement: {
+            let fullScreenSegments = [];
+            let powerEfficientPlaybackSegments = [];
+            let activeSegments = [];
+
+            let fullScreenStartTime = NaN;
+            let fullScreenOriginator = null;
+            function addFullScreenSegment(startTime, endTime) {
+                fullScreenSegments.push({
+                    startTime,
+                    endTime,
+                    classNames: ["segment", "media-element-full-screen"],
+                    title: fullScreenOriginator ? WI.UIString("Full-Screen from \u201C%s\u201D").format(fullScreenOriginator.displayName) : WI.UIString("Full-Screen"),
+                });
+                fullScreenStartTime = NaN;
+                fullScreenOriginator = null;
+            }
+
+            let powerEfficientPlaybackStartTime = NaN;
+            function addPowerEfficientPlaybackSegment(startTime, endTime) {
+                powerEfficientPlaybackSegments.push({
+                    startTime,
+                    endTime,
+                    classNames: ["segment", "media-element-power-efficient-playback"],
+                    title: WI.UIString("Power Efficient Playback"),
+                });
+                powerEfficientPlaybackStartTime = NaN;
+            }
+
+            let pausedStartTime = NaN;
+            function addPausedSegment(startTime, endTime) {
+                activeSegments.push({
+                    startTime,
+                    endTime,
+                    classNames: ["segment", "media-element-paused"],
+                    title: WI.UIString("Paused", "Tooltip for a time range bar that represents when the playback of a audio/video element is paused"),
+                });
+                pausedStartTime = NaN;
+            }
+
+            let playingStartTime = NaN;
+            function addPlayingSegment(startTime, endTime) {
+                activeSegments.push({
+                    startTime,
+                    endTime,
+                    classNames: ["segment", "media-element-playing"],
+                    title: WI.UIString("Playing", "Tooltip for a time range bar that represents when the playback of a audio/video element is running"),
+                });
+                playingStartTime = NaN;
+            }
+
+            for (let item of timestamps) {
+                if (item.type === WI.MediaTimelineRecord.TimestampType.MediaElementDOMEvent) {
+                    if (WI.DOMNode.isPlayEvent(item.eventName)) {
+                        if (isNaN(playingStartTime))
+                            playingStartTime = item.timestamp;
+                        if (!isNaN(pausedStartTime))
+                            addPausedSegment(pausedStartTime, item.timestamp);
+                    } else if (WI.DOMNode.isPauseEvent(item.eventName)) {
+                        if (isNaN(pausedStartTime))
+                            pausedStartTime = item.timestamp;
+                        if (!isNaN(playingStartTime))
+                            addPlayingSegment(playingStartTime, item.timestamp);
+                    } else if (WI.DOMNode.isStopEvent(item.eventName)) {
+                        if (!isNaN(pausedStartTime))
+                            addPausedSegment(pausedStartTime, item.timestamp);
+                        if (!isNaN(playingStartTime))
+                            addPlayingSegment(playingStartTime, item.timestamp);
+                    } else if (item.eventName === "webkitfullscreenchange") {
+                        if (!fullScreenOriginator && item.originator)
+                            fullScreenOriginator = item.originator;
+
+                        if (isNaN(fullScreenStartTime)) {
+                            if (item.data && item.data.enabled)
+                                fullScreenStartTime = item.timestamp;
+                            else
+                                addFullScreenSegment(this.graphDataSource ? this.graphDataSource.startTime : record.startTime, item.timestamp);
+                        } else if (!item.data || !item.data.enabled)
+                            addFullScreenSegment(fullScreenStartTime, item.timestamp);
+                    }
+                } else if (item.type === WI.MediaTimelineRecord.TimestampType.MediaElementPowerEfficientPlaybackStateChange) {
+                    if (isNaN(powerEfficientPlaybackStartTime)) {
+                        if (item.isPowerEfficient)
+                            powerEfficientPlaybackStartTime = item.timestamp;
+                        else
+                            addPowerEfficientPlaybackSegment(this.graphDataSource ? this.graphDataSource.startTime : record.startTime, item.timestamp);
+                    } else if (!item.isPowerEfficient)
+                        addPowerEfficientPlaybackSegment(powerEfficientPlaybackStartTime, item.timestamp);
+                }
+            }
+
+            if (!isNaN(fullScreenStartTime))
+                addFullScreenSegment(fullScreenStartTime, NaN);
+            if (!isNaN(powerEfficientPlaybackStartTime))
+                addPowerEfficientPlaybackSegment(powerEfficientPlaybackStartTime, NaN);
+            if (!isNaN(pausedStartTime))
+                addPausedSegment(pausedStartTime, NaN);
+            if (!isNaN(playingStartTime))
+                addPlayingSegment(playingStartTime, NaN);
+
+            children.pushAll(fullScreenSegments);
+            children.pushAll(powerEfficientPlaybackSegments);
+            children.pushAll(activeSegments);
+            break;
+        }
+        }
+
+        timestamps.forEach((item, i) => {
+            let image = {
+                startTime: item.timestamp,
+                classNames: [],
+            };
+
+            switch (item.type) {
+            case WI.MediaTimelineRecord.TimestampType.CSSAnimationReady:
+            case WI.MediaTimelineRecord.TimestampType.CSSAnimationDelay:
+            case WI.MediaTimelineRecord.TimestampType.CSSAnimationDone:
+            case WI.MediaTimelineRecord.TimestampType.MediaElementPowerEfficientPlaybackStateChange:
+                // These timestamps are handled by the range segments above.
+                return;
+
+            case WI.MediaTimelineRecord.TimestampType.CSSAnimationActive:
+                // Don't create a marker segment for the first active timestamp, as that will be
+                // handled by an active range segment above.
+                if (!i || timestamps[i - 1].type !== WI.MediaTimelineRecord.TimestampType.CSSAnimationActive)
+                    return;
+
+                image.image = "Images/EventIteration.svg";
+                image.title = WI.UIString("Iteration", "Tooltip for a timestamp marker that represents when a CSS animation/transition iterates");
+                break;
+
+            case WI.MediaTimelineRecord.TimestampType.CSSAnimationCancel:
+                image.image = "Images/EventCancel.svg";
+                image.title = WI.UIString("Canceled", "Tooltip for a timestamp marker that represents when a CSS animation/transition is canceled");
+                break;
+
+            case WI.MediaTimelineRecord.TimestampType.MediaElementDOMEvent:
+                // Don't create a marker segment full-screen timestamps, as that will be handled by a
+                // range segment above.
+                if (item.eventName === "webkitfullscreenchange")
+                    return;
+
+                image.title = WI.UIString("DOM Event \u201C%s\u201D").format(item.eventName);
+                if (WI.DOMNode.isPlayEvent(item.eventName))
+                    image.image = "Images/EventPlay.svg";
+                else if (WI.DOMNode.isPauseEvent(item.eventName))
+                    image.image = "Images/EventPause.svg";
+                else if (WI.DOMNode.isStopEvent(item.eventName))
+                    image.image = "Images/EventStop.svg";
+                else
+                    image.image = "Images/EventProcessing.svg";
+                break;
+            }
+
+            children.push(image);
+        });
+
+        return children;
     }
 
     // Protected
 
     filterableDataForColumn(columnIdentifier)
     {
-        if (columnIdentifier === "element") {
+        switch (columnIdentifier) {
+        case "name":
+            return [this.record.displayName, this.record.subtitle];
+
+        case "element":
+        case "source": // Timeline Overview
             if (this.record.domNode)
                 return this.record.domNode.displayName;
+            break;
         }
 
-        if (columnIdentifier === "originator") {
-            if (this.record.eventType === WI.MediaTimelineRecord.EventType.DOMEvent && this.record.domEvent.originator)
-                return this.record.domEvent.originator.displayName;
+        return super.filterableDataForColumn(columnIdentifier);
+    }
+
+    // Private
+
+    _createNameCellDocumentFragment()
+    {
+        let fragment = document.createDocumentFragment();
+        fragment.append(this.record.displayName);
+
+        if (this.record.subtitle) {
+            let subtitleElement = fragment.appendChild(document.createElement("span"));
+            subtitleElement.className = "subtitle";
+            subtitleElement.textContent = this.record.subtitle;
         }
 
-        return super.filterableDataForColumn(columnIdentifier);
+        return fragment;
     }
 };