blob: 1063f2b6024abd9ede7bdff24bac03867d90ec91 [file] [log] [blame]
/*
* Copyright (C) 2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
WI.MediaTimelineDataGridNode = class MediaTimelineDataGridNode extends WI.TimelineDataGridNode
{
constructor(record, options = {})
{
console.assert(record instanceof WI.MediaTimelineRecord);
super([record], options);
}
// Public
get data()
{
if (this._cachedData)
return this._cachedData;
this._cachedData = super.data;
this._cachedData.name = this.record.displayName;
this._cachedData.element = this.record.domNode;
this._cachedData.source = this.record.domNode; // Timeline Overview
return this._cachedData;
}
createCellContent(columnIdentifier, cell)
{
let value = this.data[columnIdentifier];
switch (columnIdentifier) {
case "name":
cell.classList.add(...this.iconClassNames());
return this._createNameCellDocumentFragment();
case "element":
case "source": // Timeline Overview
if (!(value instanceof WI.DOMNode)) {
cell.classList.add(WI.DOMTreeElementPathComponent.DOMNodeIconStyleClassName);
return value.displayName;
}
break;
}
return super.createCellContent(columnIdentifier, cell);
}
// TimelineRecordBar delegate
timelineRecordBarCustomChildren(timelineRecordBar)
{
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)
{
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;
}
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 fragment;
}
};