Index: Source/devtools/front_end/timeline/TimelineEventOverview.js |
diff --git a/Source/devtools/front_end/timeline/TimelineEventOverview.js b/Source/devtools/front_end/timeline/TimelineEventOverview.js |
index 078e9a3e008acfb0c8cef8ca5f29e69d5db9bae2..ebd453fad47cbaf14bde51643dd6d1a2cdbce8f9 100644 |
--- a/Source/devtools/front_end/timeline/TimelineEventOverview.js |
+++ b/Source/devtools/front_end/timeline/TimelineEventOverview.js |
@@ -50,6 +50,8 @@ WebInspector.TimelineEventOverview = function(model) |
/** @const */ |
WebInspector.TimelineEventOverview._stripHeight = 10; |
+/** @const */ |
+WebInspector.TimelineEventOverview._maxNetworkStripHeight = 32; |
WebInspector.TimelineEventOverview.prototype = { |
/** |
@@ -67,14 +69,21 @@ WebInspector.TimelineEventOverview.prototype = { |
*/ |
update: function() |
{ |
- var /** @const */ topPadding = 2; |
+ var /** @const */ padding = 2; |
this.resetCanvas(); |
var threads = this._model.virtualThreads(); |
- var estimatedHeight = 3 * WebInspector.TimelineEventOverview._stripHeight; |
+ var mainThreadEvents = this._model.mainThreadEvents(); |
+ var estimatedHeight = padding + 3 * WebInspector.TimelineEventOverview._stripHeight; |
+ estimatedHeight += padding + WebInspector.TimelineEventOverview._maxNetworkStripHeight; |
this._canvas.height = estimatedHeight * window.devicePixelRatio; |
this._canvas.style.height = estimatedHeight + "px"; |
- var position = topPadding; |
- position += this._drawEvents(this._model.mainThreadEvents(), position); |
+ var position = padding; |
+ if (Runtime.experiments.isEnabled("networkRequestsOnTimeline")) { |
+ position += this._drawNetwork(mainThreadEvents, position); |
+ position += padding; |
+ } |
+ this._drawEvents(mainThreadEvents, position); |
+ position += WebInspector.TimelineEventOverview._stripHeight; |
for (var thread of threads.filter(function(thread) { return !thread.isWorker(); })) |
this._drawEvents(thread.events, position); |
position += WebInspector.TimelineEventOverview._stripHeight; |
@@ -90,9 +99,162 @@ WebInspector.TimelineEventOverview.prototype = { |
* @param {number} position |
* @return {number} |
*/ |
+ _drawNetwork: function(events, position) |
+ { |
+ /** |
+ * @param {!Array.<!WebInspector.TracingModel.Event>} events |
+ * @return {number} |
+ */ |
+ function calculateNetworkBandsCount(events) |
+ { |
+ var openBands = new Set(); |
+ var maxBands = 0; |
+ for (var i = 0; i < events.length; ++i) { |
+ var e = events[i]; |
+ switch (e.name) { |
+ case WebInspector.TimelineModel.RecordType.ResourceSendRequest: |
+ case WebInspector.TimelineModel.RecordType.ResourceReceiveResponse: |
+ case WebInspector.TimelineModel.RecordType.ResourceReceivedData: |
+ var reqId = e.args["data"]["requestId"]; |
+ openBands.add(reqId); |
+ maxBands = Math.max(maxBands, openBands.size); |
+ break; |
+ case WebInspector.TimelineModel.RecordType.ResourceFinish: |
+ var reqId = e.args["data"]["requestId"]; |
+ if (!openBands.has(reqId)) |
+ ++maxBands; |
+ else |
+ openBands.delete(reqId); |
+ break; |
+ } |
+ } |
+ return maxBands; |
+ } |
+ |
+ var /** @const */ maxBandHeight = 4; |
+ var bandsCount = calculateNetworkBandsCount(events); |
+ var bandInterval = Math.min(maxBandHeight, WebInspector.TimelineEventOverview._maxNetworkStripHeight / (bandsCount || 1)); |
+ var bandHeight = Math.ceil(bandInterval); |
+ var timeOffset = this._model.minimumRecordTime(); |
+ var timeSpan = this._model.maximumRecordTime() - timeOffset; |
+ var scale = this._canvas.width / timeSpan; |
+ var loadingCategory = WebInspector.TimelineUIUtils.categories()["loading"]; |
+ var waitingColor = loadingCategory.fillColorStop0; |
+ var processingColor = loadingCategory.fillColorStop1; |
+ |
+ var bandsInUse = new Array(bandsCount); |
+ var freeBandsCount = bandsCount; |
+ var requestsInFlight = new Map(); |
+ var lastBand = 0; |
+ |
+ /** |
+ * @constructor |
+ * @param {number} band |
+ * @param {number} lastTime |
+ * @param {boolean} gotResponse |
+ */ |
+ function RequestInfo(band, lastTime, gotResponse) |
+ { |
+ this.band = band; |
+ this.lastTime = lastTime; |
+ this.gotResponse = gotResponse; |
+ } |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ function seizeBand() |
+ { |
+ console.assert(freeBandsCount); |
+ do { |
+ lastBand = (lastBand + 1) % bandsInUse.length; |
+ } while (bandsInUse[lastBand]); |
+ bandsInUse[lastBand] = true; |
+ --freeBandsCount; |
+ return lastBand; |
+ } |
+ |
+ /** |
+ * @param {number} band |
+ */ |
+ function releaseBand(band) |
+ { |
+ bandsInUse[band] = false; |
+ ++freeBandsCount; |
+ } |
+ |
+ /** |
+ * @param {string} reqId |
+ * @param {number=} time |
+ * @return {!RequestInfo} |
+ */ |
+ function getRequestInfo(reqId, time) |
+ { |
+ var reqInfo = requestsInFlight.get(reqId); |
+ if (!reqInfo) { |
+ reqInfo = new RequestInfo(seizeBand(), time || timeOffset, false); |
+ requestsInFlight.set(reqId, reqInfo); |
+ } |
+ return reqInfo; |
+ } |
+ |
+ /** |
+ * @param {string} reqId |
+ * @param {!RequestInfo} reqInfo |
+ * @param {number} time |
+ * @param {boolean=} finish |
+ * @this {WebInspector.TimelineEventOverview} |
+ */ |
+ function advanceRequest(reqId, reqInfo, time, finish) |
+ { |
+ var band = reqInfo.band; |
+ var start = (reqInfo.lastTime - timeOffset) * scale; |
+ var end = (time - timeOffset) * scale; |
+ var color = reqInfo.gotResponse ? processingColor : waitingColor; |
+ if (finish) { |
+ releaseBand(band); |
+ requestsInFlight.delete(reqId); |
+ } else { |
+ reqInfo.lastTime = time; |
+ reqInfo.gotResponse = true; |
+ } |
+ this._renderBar(Math.floor(start), Math.ceil(end), Math.floor(position + band * bandInterval), bandHeight, color); |
+ } |
+ |
+ for (var i = 0; i < events.length; ++i) { |
+ var event = events[i]; |
+ switch (event.name) { |
+ case WebInspector.TimelineModel.RecordType.ResourceSendRequest: |
+ var reqId = event.args["data"]["requestId"]; |
+ getRequestInfo(reqId, event.startTime); |
+ break; |
+ case WebInspector.TimelineModel.RecordType.ResourceReceivedData: |
+ case WebInspector.TimelineModel.RecordType.ResourceReceiveResponse: |
+ case WebInspector.TimelineModel.RecordType.ResourceFinish: |
+ var reqId = event.args["data"]["requestId"]; |
+ var reqInfo = getRequestInfo(reqId); |
+ var finish = event.name === WebInspector.TimelineModel.RecordType.ResourceFinish; |
+ advanceRequest.call(this, reqId, reqInfo, event.startTime, finish); |
+ break; |
+ } |
+ } |
+ |
+ for (var reqId of requestsInFlight.keys()) |
+ advanceRequest.call(this, reqId, requestsInFlight.get(reqId), timeOffset + timeSpan); |
+ |
+ return Math.ceil(bandInterval * bandsCount); |
+ }, |
+ |
+ /** |
+ * @param {!Array.<!WebInspector.TracingModel.Event>} events |
+ * @param {number} position |
+ * @return {number} |
+ */ |
_drawEvents: function(events, position) |
{ |
+ var /** @const */ padding = 1; |
var stripHeight = WebInspector.TimelineEventOverview._stripHeight; |
+ var visualHeight = stripHeight - padding; |
var timeOffset = this._model.minimumRecordTime(); |
var timeSpan = this._model.maximumRecordTime() - timeOffset; |
var scale = this._canvas.width / timeSpan; |
@@ -102,6 +264,16 @@ WebInspector.TimelineEventOverview.prototype = { |
var drawn = false; |
/** |
+ * @param {!WebInspector.TimelineCategory} category |
+ * @return {string} |
+ * @this {WebInspector.TimelineEventOverview} |
+ */ |
+ function categoryColor(category) |
+ { |
+ return category.hidden ? this._disabledCategoryFillStyle : this._fillStyles[category.name]; |
+ } |
+ |
+ /** |
* @param {!WebInspector.TracingModel.Event} e |
* @this {WebInspector.TimelineEventOverview} |
*/ |
@@ -112,7 +284,7 @@ WebInspector.TimelineEventOverview.prototype = { |
var category = categoryStack.peekLast(); |
var bar = ditherer.appendInterval(category, lastX, pos); |
if (bar) { |
- this._renderBar(bar.start, bar.end, position, stripHeight, category); |
+ this._renderBar(bar.start, bar.end, position, visualHeight, categoryColor.call(this, category)); |
drawn = true; |
} |
} |
@@ -130,7 +302,7 @@ WebInspector.TimelineEventOverview.prototype = { |
var pos = (e.endTime - timeOffset) * scale; |
var bar = ditherer.appendInterval(category, lastX, pos); |
if (bar) { |
- this._renderBar(bar.start, bar.end, position, stripHeight, category); |
+ this._renderBar(bar.start, bar.end, position, visualHeight, categoryColor.call(this, category)); |
drawn = true; |
} |
lastX = pos; |
@@ -150,17 +322,15 @@ WebInspector.TimelineEventOverview.prototype = { |
* @param {number} end |
* @param {number} position |
* @param {number} height |
- * @param {!WebInspector.TimelineCategory} category |
+ * @param {string} color |
*/ |
- _renderBar: function(begin, end, position, height, category) |
+ _renderBar: function(begin, end, position, height, color) |
{ |
- var /** @const */ stripPadding = 1; |
- var innerStripHeight = (height - stripPadding) * window.devicePixelRatio; |
var x = begin; |
var y = position * window.devicePixelRatio; |
var width = end - begin; |
- this._context.fillStyle = category.hidden ? this._disabledCategoryFillStyle : this._fillStyles[category.name]; |
- this._context.fillRect(x, y, width, innerStripHeight); |
+ this._context.fillStyle = color; |
+ this._context.fillRect(x, y, width, height * window.devicePixelRatio); |
}, |
__proto__: WebInspector.TimelineOverviewBase.prototype |