Index: third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js |
index 585c9d0b0a957618b08fbe7ecd49486e03af8988..340ee6bc67be899f46aafe52f8e35bab837bad91 100644 |
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js |
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineFlameChartDataProvider.js |
@@ -76,7 +76,9 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
this._headerLevel1 = buildGroupStyle({shareHeaderLine: false}); |
this._headerLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1, collapsible: false}); |
this._staticHeader = buildGroupStyle({collapsible: false}); |
- this._framesHeader = buildGroupStyle({useFirstLineForOverview: true, shareHeaderLine: true, itemsHeight: 150}); |
+ this._framesHeader = buildGroupStyle({useFirstLineForOverview: true}); |
+ this._screenshotsHeader = |
+ buildGroupStyle({useFirstLineForOverview: true, nestingLevel: 1, collapsible: false, itemsHeight: 150}); |
this._interactionsHeaderLevel1 = buildGroupStyle({useFirstLineForOverview: true}); |
this._interactionsHeaderLevel2 = buildGroupStyle({padding: 2, nestingLevel: 1}); |
@@ -99,8 +101,9 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
* @return {?string} |
*/ |
entryTitle(entryIndex) { |
+ var entryTypes = Timeline.TimelineFlameChartDataProvider.EntryType; |
var entryType = this._entryType(entryIndex); |
- if (entryType === Timeline.TimelineFlameChartEntryType.Event) { |
+ if (entryType === entryTypes.Event) { |
var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]); |
if (event.phase === SDK.TracingModel.Phase.AsyncStepInto || event.phase === SDK.TracingModel.Phase.AsyncStepPast) |
return event.name + ':' + event.args['step']; |
@@ -113,10 +116,12 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
return detailsText; |
return detailsText ? Common.UIString('%s (%s)', name, detailsText) : name; |
} |
- if (entryType === Timeline.TimelineFlameChartEntryType.ExtensionEvent) { |
+ if (entryType === entryTypes.ExtensionEvent) { |
var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]); |
return event.name; |
} |
+ if (entryType === entryTypes.Screenshot) |
+ return ''; |
var title = this._entryIndexToTitle[entryIndex]; |
if (!title) { |
title = Common.UIString('Unexpected entryIndex %d', entryIndex); |
@@ -147,11 +152,11 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
reset() { |
this._currentLevel = 0; |
this._timelineData = null; |
- /** @type {!Array<!SDK.TracingModel.Event|!TimelineModel.TimelineFrame|!TimelineModel.TimelineIRModel.Phases>} */ |
+ /** @type {!Array<!SDK.FilmStripModel.Frame|!SDK.TracingModel.Event|!TimelineModel.TimelineFrame|!TimelineModel.TimelineIRModel.Phases>} */ |
this._entryData = []; |
/** @type {!Array<!SDK.TracingModel.Event>} */ |
this._entryParent = []; |
- /** @type {!Array<!Timeline.TimelineFlameChartEntryType>} */ |
+ /** @type {!Array<!Timeline.TimelineFlameChartDataProvider.EntryType>} */ |
this._entryTypeByLevel = []; |
/** @type {!Array<string>} */ |
this._entryIndexToTitle = []; |
@@ -163,8 +168,8 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
this._asyncColorByInteractionPhase = new Map(); |
/** @type {!Array<!{title: string, model: !SDK.TracingModel}>} */ |
this._extensionInfo = []; |
- /** @type {!Map<!TimelineModel.TimelineFrame, ?Image>} */ |
- this._frameImageCache = new Map(); |
+ /** @type {!Map<!SDK.FilmStripModel.Frame, ?Image>} */ |
+ this._screenshotImageCache = new Map(); |
} |
/** |
@@ -193,12 +198,12 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
this._timeSpan = this._model.isEmpty() ? 1000 : this._model.maximumRecordTime() - this._minimumBoundary; |
this._currentLevel = 0; |
- this._appendFrameBars(this._performanceModel.frames()); |
+ this._appendFrameBars(); |
this._appendHeader(Common.UIString('Interactions'), this._interactionsHeaderLevel1); |
this._appendInteractionRecords(); |
- var eventEntryType = Timeline.TimelineFlameChartEntryType.Event; |
+ var eventEntryType = Timeline.TimelineFlameChartDataProvider.EntryType.Event; |
var asyncEventGroups = TimelineModel.TimelineModel.AsyncEventGroup; |
var inputLatencies = this._model.mainThreadAsyncEvents().get(asyncEventGroups.input); |
@@ -243,15 +248,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
for (let extensionIndex = 0; extensionIndex < this._extensionInfo.length; extensionIndex++) |
this._innerAppendExtensionEvents(extensionIndex); |
- /** |
- * @param {!Timeline.TimelineFlameChartMarker} a |
- * @param {!Timeline.TimelineFlameChartMarker} b |
- */ |
- function compareStartTime(a, b) { |
- return a.startTime() - b.startTime(); |
- } |
- |
- this._markers.sort(compareStartTime); |
+ this._markers.sort((a, b) => a.startTime() - b.startTime()); |
this._timelineData.markers = this._markers; |
this._flowEventIndexById.clear(); |
@@ -284,7 +281,8 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
clonedHeader.nestingLevel = level; |
this._appendSyncEvents( |
events, Timeline.TimelineUIUtils.displayNameForFrame(frame), |
- /** @type {!PerfUI.FlameChart.GroupStyle} */ (clonedHeader), Timeline.TimelineFlameChartEntryType.Event); |
+ /** @type {!PerfUI.FlameChart.GroupStyle} */ (clonedHeader), |
+ Timeline.TimelineFlameChartDataProvider.EntryType.Event); |
frame.children.forEach(this._appendFrameEvents.bind(this, level + 1)); |
} |
@@ -295,7 +293,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
* @param {boolean=} forceExpanded |
*/ |
_appendThreadTimelineData(threadTitle, syncEvents, asyncEvents, forceExpanded) { |
- var entryType = Timeline.TimelineFlameChartEntryType.Event; |
+ var entryType = Timeline.TimelineFlameChartDataProvider.EntryType.Event; |
this._appendAsyncEvents(asyncEvents); |
this._appendSyncEvents(syncEvents, threadTitle, this._headerLevel1, entryType, forceExpanded); |
} |
@@ -304,11 +302,11 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
* @param {!Array<!SDK.TracingModel.Event>} events |
* @param {string} title |
* @param {!PerfUI.FlameChart.GroupStyle} style |
- * @param {!Timeline.TimelineFlameChartEntryType} entryType |
+ * @param {!Timeline.TimelineFlameChartDataProvider.EntryType} entryType |
* @param {boolean=} forceExpanded |
*/ |
_appendSyncEvents(events, title, style, entryType, forceExpanded) { |
- var isExtension = entryType === Timeline.TimelineFlameChartEntryType.ExtensionEvent; |
+ var isExtension = entryType === Timeline.TimelineFlameChartDataProvider.EntryType.ExtensionEvent; |
var openEvents = []; |
var flowEventsEnabled = Runtime.experiments.isEnabled('timelineFlowEvents'); |
var blackboxingEnabled = !isExtension && Runtime.experiments.isEnabled('blackboxJSFramesOnTimeline'); |
@@ -383,7 +381,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
* @param {!Map<!TimelineModel.TimelineModel.AsyncEventGroup, !Array<!SDK.TracingModel.AsyncEvent>>} asyncEvents |
*/ |
_appendAsyncEvents(asyncEvents) { |
- var entryType = Timeline.TimelineFlameChartEntryType.Event; |
+ var entryType = Timeline.TimelineFlameChartDataProvider.EntryType.Event; |
var groups = TimelineModel.TimelineModel.AsyncEventGroup; |
var groupArray = Object.keys(groups).map(key => groups[key]); |
@@ -404,7 +402,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
* @param {string} header |
* @param {!Array<!SDK.TracingModel.AsyncEvent>} events |
* @param {!PerfUI.FlameChart.GroupStyle} style |
- * @param {!Timeline.TimelineFlameChartEntryType} entryType |
+ * @param {!Timeline.TimelineFlameChartDataProvider.EntryType} entryType |
*/ |
_appendAsyncEventsGroup(header, events, style, entryType) { |
var lastUsedTimeByLevel = []; |
@@ -430,7 +428,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
} |
_appendGPUEvents() { |
- var eventType = Timeline.TimelineFlameChartEntryType.Event; |
+ var eventType = Timeline.TimelineFlameChartDataProvider.EntryType.Event; |
var gpuEvents = this._model.gpuEvents(); |
if (this._appendSyncEvents(gpuEvents, Common.UIString('GPU'), this._headerLevel1, eventType, false)) |
++this._currentLevel; |
@@ -438,30 +436,45 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
_appendInteractionRecords() { |
this._performanceModel.interactionRecords().forEach(this._appendSegment, this); |
- this._entryTypeByLevel[this._currentLevel++] = Timeline.TimelineFlameChartEntryType.InteractionRecord; |
+ this._entryTypeByLevel[this._currentLevel++] = Timeline.TimelineFlameChartDataProvider.EntryType.InteractionRecord; |
} |
- /** |
- * @param {!Array.<!TimelineModel.TimelineFrame>} frames |
- */ |
- _appendFrameBars(frames) { |
- var hasFilmStrip = !!this._performanceModel.filmStripModel().frames().length; |
+ _appendFrameBars() { |
+ var screenshots = this._performanceModel.filmStripModel().frames(); |
+ var hasFilmStrip = !!screenshots.length; |
this._framesHeader.collapsible = hasFilmStrip; |
this._appendHeader(Common.UIString('Frames'), this._framesHeader); |
this._frameGroup = this._timelineData.groups.peekLast(); |
var style = Timeline.TimelineUIUtils.markerStyleForFrame(); |
- this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartEntryType.Frame; |
- for (var frame of frames) { |
+ this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartDataProvider.EntryType.Frame; |
+ for (var frame of this._performanceModel.frames()) { |
this._markers.push(new Timeline.TimelineFlameChartMarker( |
frame.startTime, frame.startTime - this._model.minimumRecordTime(), style)); |
this._appendFrame(frame); |
} |
++this._currentLevel; |
+ if (!hasFilmStrip) |
+ return; |
+ this._appendHeader('', this._screenshotsHeader); |
+ this._entryTypeByLevel[this._currentLevel] = Timeline.TimelineFlameChartDataProvider.EntryType.Screenshot; |
+ var prevTimestamp; |
+ for (var screenshot of screenshots) { |
+ var index = this._entryData.length; |
+ this._entryData.push(screenshot); |
+ this._timelineData.entryLevels[index] = this._currentLevel; |
+ this._timelineData.entryStartTimes[index] = screenshot.timestamp; |
+ if (prevTimestamp) |
+ this._timelineData.entryTotalTimes[index - 1] = screenshot.timestamp - prevTimestamp; |
+ prevTimestamp = screenshot.timestamp; |
+ } |
+ this._timelineData.entryTotalTimes[this._timelineData.entryTotalTimes.length - 1] = |
+ this._model.maximumRecordTime() - prevTimestamp; |
+ ++this._currentLevel; |
} |
/** |
* @param {number} entryIndex |
- * @return {!Timeline.TimelineFlameChartEntryType} |
+ * @return {!Timeline.TimelineFlameChartDataProvider.EntryType} |
*/ |
_entryType(entryIndex) { |
return this._entryTypeByLevel[this._timelineData.entryLevels[entryIndex]]; |
@@ -477,7 +490,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
var title; |
var warning; |
var type = this._entryType(entryIndex); |
- if (type === Timeline.TimelineFlameChartEntryType.Event) { |
+ if (type === Timeline.TimelineFlameChartDataProvider.EntryType.Event) { |
var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]); |
var totalTime = event.duration; |
var selfTime = event.selfTime; |
@@ -490,7 +503,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
} |
title = this.entryTitle(entryIndex); |
warning = Timeline.TimelineUIUtils.eventWarning(event); |
- } else if (type === Timeline.TimelineFlameChartEntryType.Frame) { |
+ } else if (type === Timeline.TimelineFlameChartDataProvider.EntryType.Frame) { |
var frame = /** @type {!TimelineModel.TimelineFrame} */ (this._entryData[entryIndex]); |
time = Common.UIString( |
'%s ~ %.0f\xa0fps', Number.preciseMillisToString(frame.duration, 1), (1000 / frame.duration)); |
@@ -531,8 +544,9 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
return color; |
} |
+ var entryTypes = Timeline.TimelineFlameChartDataProvider.EntryType; |
var type = this._entryType(entryIndex); |
- if (type === Timeline.TimelineFlameChartEntryType.Event) { |
+ if (type === entryTypes.Event) { |
var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]); |
if (!SDK.TracingModel.isAsyncPhase(event.phase)) |
return this._colorForEvent(event); |
@@ -548,11 +562,11 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
var category = Timeline.TimelineUIUtils.eventStyle(event).category; |
return patchColorAndCache(this._asyncColorByCategory, category, () => category.color); |
} |
- if (type === Timeline.TimelineFlameChartEntryType.Frame) |
+ if (type === entryTypes.Frame) |
return 'white'; |
- if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) |
+ if (type === entryTypes.InteractionRecord) |
return 'transparent'; |
- if (type === Timeline.TimelineFlameChartEntryType.ExtensionEvent) { |
+ if (type === entryTypes.ExtensionEvent) { |
var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]); |
return this._extensionColorGenerator.colorForID(event.name); |
} |
@@ -576,40 +590,45 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
context.fillStyle = frame.idle ? 'white' : (frame.hasWarnings() ? '#fad1d1' : '#d7f0d1'); |
context.fillRect(barX, barY, barWidth, barHeight); |
- var headerHeight = 17; |
var frameDurationText = Number.preciseMillisToString(frame.duration, 1); |
var textWidth = context.measureText(frameDurationText).width; |
if (textWidth <= barWidth) { |
- var font = this.entryFont(entryIndex); |
- if (font) |
- context.font = font; |
context.fillStyle = this.textColor(entryIndex); |
- context.fillText(frameDurationText, barX + (barWidth - textWidth) / 2, barY + headerHeight - 4); |
+ context.fillText(frameDurationText, barX + (barWidth - textWidth) / 2, barY + barHeight - 4); |
} |
+ } |
- var imageHeight = barHeight - headerHeight; |
- if (imageHeight < headerHeight) |
+ /** |
+ * @param {number} entryIndex |
+ * @param {!CanvasRenderingContext2D} context |
+ * @param {number} barX |
+ * @param {number} barY |
+ * @param {number} barWidth |
+ * @param {number} barHeight |
+ */ |
+ async _drawScreenshot(entryIndex, context, barX, barY, barWidth, barHeight) { |
+ var screenshot = /** @type {!SDK.FilmStripModel.Frame} */ (this._entryData[entryIndex]); |
+ if (!this._screenshotImageCache.has(screenshot)) { |
+ this._screenshotImageCache.set(screenshot, null); |
+ var data = await screenshot.imageDataPromise(); |
+ var image = await UI.loadImageFromData(data); |
+ this._screenshotImageCache.set(screenshot, image); |
+ this.dispatchEventToListeners(Timeline.TimelineFlameChartDataProvider.Events.DataChanged); |
return; |
- if (!this._frameImageCache.has(frame)) { |
- this._frameImageCache.set(frame, null); // Mark the image promise is in flight. |
- var modelFrame = this._performanceModel.filmStripModelFrame(frame); |
- if (modelFrame) { |
- modelFrame.imageDataPromise().then(data => UI.loadImageFromData(data)).then(image => { |
- this._frameImageCache.set(frame, image); |
- this.dispatchEventToListeners(Timeline.TimelineFlameChartDataProvider.Events.DataChanged); |
- }); |
- } |
} |
- var image = this._frameImageCache.get(frame); |
+ context.fillStyle = '#eee'; |
+ context.fillRect(barX, barY, barWidth, barHeight); |
+ var image = this._screenshotImageCache.get(screenshot); |
if (!image) |
return; |
- var imageX = barX; |
- var imageY = barY + headerHeight; |
+ var imageX = barX + 1; |
+ var imageY = barY + 1; |
+ var imageHeight = barHeight - 2; |
var scale = imageHeight / image.naturalHeight; |
var imageWidth = image.naturalWidth * scale; |
context.save(); |
context.beginPath(); |
- context.rect(imageX, imageY, barWidth, imageHeight); |
+ context.rect(imageX, imageY, barWidth - 2, imageHeight); |
context.clip(); |
context.drawImage(image, imageX, imageY, imageWidth, imageHeight); |
context.restore(); |
@@ -631,12 +650,19 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
decorateEntry(entryIndex, context, text, barX, barY, barWidth, barHeight, unclippedBarX, timeToPixels) { |
var data = this._entryData[entryIndex]; |
var type = this._entryType(entryIndex); |
- if (type === Timeline.TimelineFlameChartEntryType.Frame) { |
+ var entryTypes = Timeline.TimelineFlameChartDataProvider.EntryType; |
+ |
+ if (type === entryTypes.Frame) { |
this._drawFrame(entryIndex, context, text, barX, barY, barWidth, barHeight); |
return true; |
} |
- if (type === Timeline.TimelineFlameChartEntryType.InteractionRecord) { |
+ if (type === entryTypes.Screenshot) { |
+ this._drawScreenshot(entryIndex, context, barX, barY, barWidth, barHeight); |
+ return true; |
+ } |
+ |
+ if (type === entryTypes.InteractionRecord) { |
var color = Timeline.TimelineUIUtils.interactionPhaseColor( |
/** @type {!TimelineModel.TimelineIRModel.Phases} */ (data)); |
context.fillStyle = color; |
@@ -646,7 +672,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
return false; |
} |
- if (type === Timeline.TimelineFlameChartEntryType.Event) { |
+ if (type === entryTypes.Event) { |
var event = /** @type {!SDK.TracingModel.Event} */ (data); |
if (event.hasCategory(TimelineModel.TimelineModel.Category.LatencyInfo)) { |
var timeWaitingForMainThread = TimelineModel.TimelineData.forEvent(event).timeWaitingForMainThread; |
@@ -688,11 +714,14 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
* @return {boolean} |
*/ |
forceDecoration(entryIndex) { |
+ var entryTypes = Timeline.TimelineFlameChartDataProvider.EntryType; |
var type = this._entryType(entryIndex); |
- if (type === Timeline.TimelineFlameChartEntryType.Frame) |
+ if (type === entryTypes.Frame) |
+ return true; |
+ if (type === entryTypes.Screenshot) |
return true; |
- if (type === Timeline.TimelineFlameChartEntryType.Event) { |
+ if (type === entryTypes.Event) { |
var event = /** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]); |
return !!TimelineModel.TimelineData.forEvent(event).warning; |
} |
@@ -713,7 +742,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
*/ |
_innerAppendExtensionEvents(index) { |
var entry = this._extensionInfo[index]; |
- var entryType = Timeline.TimelineFlameChartEntryType.ExtensionEvent; |
+ var entryType = Timeline.TimelineFlameChartDataProvider.EntryType.ExtensionEvent; |
var allThreads = [].concat(...entry.model.sortedProcesses().map(process => process.sortedThreads())); |
if (!allThreads.length) |
return; |
@@ -845,10 +874,10 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
createSelection(entryIndex) { |
var type = this._entryType(entryIndex); |
var timelineSelection = null; |
- if (type === Timeline.TimelineFlameChartEntryType.Event) { |
+ if (type === Timeline.TimelineFlameChartDataProvider.EntryType.Event) { |
timelineSelection = Timeline.TimelineSelection.fromTraceEvent( |
/** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex])); |
- } else if (type === Timeline.TimelineFlameChartEntryType.Frame) { |
+ } else if (type === Timeline.TimelineFlameChartDataProvider.EntryType.Frame) { |
timelineSelection = Timeline.TimelineSelection.fromFrame( |
/** @type {!TimelineModel.TimelineFrame} */ (this._entryData[entryIndex])); |
} |
@@ -961,7 +990,7 @@ Timeline.TimelineFlameChartDataProvider = class extends Common.Object { |
* @return {?SDK.TracingModel.Event} |
*/ |
eventByIndex(entryIndex) { |
- return this._entryType(entryIndex) === Timeline.TimelineFlameChartEntryType.Event ? |
+ return this._entryType(entryIndex) === Timeline.TimelineFlameChartDataProvider.EntryType.Event ? |
/** @type {!SDK.TracingModel.Event} */ (this._entryData[entryIndex]) : |
null; |
} |
@@ -981,3 +1010,12 @@ Timeline.TimelineFlameChartDataProvider._indexSymbol = Symbol('index'); |
Timeline.TimelineFlameChartDataProvider.Events = { |
DataChanged: Symbol('DataChanged') |
}; |
+ |
+/** @enum {symbol} */ |
+Timeline.TimelineFlameChartDataProvider.EntryType = { |
+ Frame: Symbol('Frame'), |
+ Event: Symbol('Event'), |
+ InteractionRecord: Symbol('InteractionRecord'), |
+ ExtensionEvent: Symbol('ExtensionEvent'), |
+ Screenshot: Symbol('Screenshot'), |
+}; |