| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @implements {PerfUI.FlameChartDataProvider} | |
| 7 * @unrestricted | |
| 8 */ | |
| 9 Timeline.TimelineFlameChartNetworkDataProvider = class { | |
| 10 constructor() { | |
| 11 this._font = '11px ' + Host.fontFamily(); | |
| 12 this.setModel(null); | |
| 13 this._style = { | |
| 14 padding: 4, | |
| 15 height: 17, | |
| 16 collapsible: true, | |
| 17 color: UI.themeSupport.patchColor('#222', UI.ThemeSupport.ColorUsage.Foreg
round), | |
| 18 font: this._font, | |
| 19 backgroundColor: UI.themeSupport.patchColor('white', UI.ThemeSupport.Color
Usage.Background), | |
| 20 nestingLevel: 0, | |
| 21 useFirstLineForOverview: false, | |
| 22 useDecoratorsForOverview: true, | |
| 23 shareHeaderLine: false | |
| 24 }; | |
| 25 this._group = {startLevel: 0, name: Common.UIString('Network'), expanded: fa
lse, style: this._style}; | |
| 26 } | |
| 27 | |
| 28 /** | |
| 29 * @param {?Timeline.PerformanceModel} performanceModel | |
| 30 */ | |
| 31 setModel(performanceModel) { | |
| 32 this._model = performanceModel && performanceModel.timelineModel(); | |
| 33 this._maxLevel = 0; | |
| 34 this._timelineData = null; | |
| 35 /** @type {!Array<!TimelineModel.TimelineModel.NetworkRequest>} */ | |
| 36 this._requests = []; | |
| 37 } | |
| 38 | |
| 39 /** | |
| 40 * @return {boolean} | |
| 41 */ | |
| 42 isEmpty() { | |
| 43 this.timelineData(); | |
| 44 return !this._requests.length; | |
| 45 } | |
| 46 | |
| 47 /** | |
| 48 * @override | |
| 49 * @return {number} | |
| 50 */ | |
| 51 maxStackDepth() { | |
| 52 return this._maxLevel; | |
| 53 } | |
| 54 | |
| 55 /** | |
| 56 * @override | |
| 57 * @return {!PerfUI.FlameChart.TimelineData} | |
| 58 */ | |
| 59 timelineData() { | |
| 60 if (this._timelineData) | |
| 61 return this._timelineData; | |
| 62 /** @type {!Array<!TimelineModel.TimelineModel.NetworkRequest>} */ | |
| 63 this._requests = []; | |
| 64 this._timelineData = new PerfUI.FlameChart.TimelineData([], [], [], []); | |
| 65 if (this._model) | |
| 66 this._appendTimelineData(this._model.mainThreadEvents()); | |
| 67 return this._timelineData; | |
| 68 } | |
| 69 | |
| 70 /** | |
| 71 * @override | |
| 72 * @return {number} | |
| 73 */ | |
| 74 minimumBoundary() { | |
| 75 return this._minimumBoundary; | |
| 76 } | |
| 77 | |
| 78 /** | |
| 79 * @override | |
| 80 * @return {number} | |
| 81 */ | |
| 82 totalTime() { | |
| 83 return this._timeSpan; | |
| 84 } | |
| 85 | |
| 86 /** | |
| 87 * @param {number} startTime | |
| 88 * @param {number} endTime | |
| 89 */ | |
| 90 setWindowTimes(startTime, endTime) { | |
| 91 this._startTime = startTime; | |
| 92 this._endTime = endTime; | |
| 93 this._updateTimelineData(); | |
| 94 } | |
| 95 | |
| 96 /** | |
| 97 * @param {number} index | |
| 98 * @return {?Timeline.TimelineSelection} | |
| 99 */ | |
| 100 createSelection(index) { | |
| 101 if (index === -1) | |
| 102 return null; | |
| 103 var request = this._requests[index]; | |
| 104 this._lastSelection = | |
| 105 new Timeline.TimelineFlameChartView.Selection(Timeline.TimelineSelection
.fromNetworkRequest(request), index); | |
| 106 return this._lastSelection.timelineSelection; | |
| 107 } | |
| 108 | |
| 109 /** | |
| 110 * @param {?Timeline.TimelineSelection} selection | |
| 111 * @return {number} | |
| 112 */ | |
| 113 entryIndexForSelection(selection) { | |
| 114 if (!selection) | |
| 115 return -1; | |
| 116 | |
| 117 if (this._lastSelection && this._lastSelection.timelineSelection.object() ==
= selection.object()) | |
| 118 return this._lastSelection.entryIndex; | |
| 119 | |
| 120 if (selection.type() !== Timeline.TimelineSelection.Type.NetworkRequest) | |
| 121 return -1; | |
| 122 var request = /** @type{!TimelineModel.TimelineModel.NetworkRequest} */ (sel
ection.object()); | |
| 123 var index = this._requests.indexOf(request); | |
| 124 if (index !== -1) { | |
| 125 this._lastSelection = | |
| 126 new Timeline.TimelineFlameChartView.Selection(Timeline.TimelineSelecti
on.fromNetworkRequest(request), index); | |
| 127 } | |
| 128 return index; | |
| 129 } | |
| 130 | |
| 131 /** | |
| 132 * @override | |
| 133 * @param {number} index | |
| 134 * @return {string} | |
| 135 */ | |
| 136 entryColor(index) { | |
| 137 var request = /** @type {!TimelineModel.TimelineModel.NetworkRequest} */ (th
is._requests[index]); | |
| 138 var category = Timeline.TimelineUIUtils.networkRequestCategory(request); | |
| 139 return Timeline.TimelineUIUtils.networkCategoryColor(category); | |
| 140 } | |
| 141 | |
| 142 /** | |
| 143 * @override | |
| 144 * @param {number} index | |
| 145 * @return {string} | |
| 146 */ | |
| 147 textColor(index) { | |
| 148 return Timeline.FlameChartStyle.textColor; | |
| 149 } | |
| 150 | |
| 151 /** | |
| 152 * @override | |
| 153 * @param {number} index | |
| 154 * @return {?string} | |
| 155 */ | |
| 156 entryTitle(index) { | |
| 157 var request = /** @type {!TimelineModel.TimelineModel.NetworkRequest} */ (th
is._requests[index]); | |
| 158 var parsedURL = new Common.ParsedURL(request.url || ''); | |
| 159 return parsedURL.isValid ? `${parsedURL.displayName} (${parsedURL.host})` :
request.url || null; | |
| 160 } | |
| 161 | |
| 162 /** | |
| 163 * @override | |
| 164 * @param {number} index | |
| 165 * @return {?string} | |
| 166 */ | |
| 167 entryFont(index) { | |
| 168 return this._font; | |
| 169 } | |
| 170 | |
| 171 /** | |
| 172 * @override | |
| 173 * @param {number} index | |
| 174 * @param {!CanvasRenderingContext2D} context | |
| 175 * @param {?string} text | |
| 176 * @param {number} barX | |
| 177 * @param {number} barY | |
| 178 * @param {number} barWidth | |
| 179 * @param {number} barHeight | |
| 180 * @param {number} unclippedBarX | |
| 181 * @param {number} timeToPixelRatio | |
| 182 * @return {boolean} | |
| 183 */ | |
| 184 decorateEntry(index, context, text, barX, barY, barWidth, barHeight, unclipped
BarX, timeToPixelRatio) { | |
| 185 var request = /** @type {!TimelineModel.TimelineModel.NetworkRequest} */ (th
is._requests[index]); | |
| 186 if (!request.timing) | |
| 187 return false; | |
| 188 | |
| 189 /** | |
| 190 * @param {number} time | |
| 191 * @return {number} | |
| 192 */ | |
| 193 function timeToPixel(time) { | |
| 194 return Math.floor(unclippedBarX + (time - startTime) * timeToPixelRatio); | |
| 195 } | |
| 196 | |
| 197 var /** @const */ minBarWidthPx = 2; | |
| 198 var startTime = request.startTime; | |
| 199 var endTime = request.endTime; | |
| 200 var requestTime = request.timing.requestTime * 1000; | |
| 201 var sendStart = Math.max(timeToPixel(requestTime + request.timing.sendStart)
, unclippedBarX); | |
| 202 var headersEnd = Math.max(timeToPixel(requestTime + request.timing.receiveHe
adersEnd), sendStart); | |
| 203 var finish = Math.max(timeToPixel(request.finishTime || endTime), headersEnd
+ minBarWidthPx); | |
| 204 var end = Math.max(timeToPixel(endTime), finish); | |
| 205 | |
| 206 context.fillStyle = 'hsla(0, 100%, 100%, 0.8)'; | |
| 207 context.fillRect(sendStart + 0.5, barY + 0.5, headersEnd - sendStart - 0.5,
barHeight - 2); | |
| 208 context.fillStyle = UI.themeSupport.patchColor('white', UI.ThemeSupport.Colo
rUsage.Background); | |
| 209 context.fillRect(barX, barY - 0.5, sendStart - barX, barHeight); | |
| 210 context.fillRect(finish, barY - 0.5, barX + barWidth - finish, barHeight); | |
| 211 | |
| 212 /** | |
| 213 * @param {number} begin | |
| 214 * @param {number} end | |
| 215 * @param {number} y | |
| 216 */ | |
| 217 function drawTick(begin, end, y) { | |
| 218 var /** @const */ tickHeightPx = 6; | |
| 219 context.moveTo(begin, y - tickHeightPx / 2); | |
| 220 context.lineTo(begin, y + tickHeightPx / 2); | |
| 221 context.moveTo(begin, y); | |
| 222 context.lineTo(end, y); | |
| 223 } | |
| 224 | |
| 225 context.lineWidth = 1; | |
| 226 context.strokeStyle = '#ccc'; | |
| 227 var lineY = Math.floor(barY + barHeight / 2) + 0.5; | |
| 228 var leftTick = Math.floor(unclippedBarX) + 0.5; | |
| 229 var rightTick = end - 0.5; | |
| 230 drawTick(leftTick, sendStart, lineY); | |
| 231 drawTick(rightTick, finish, lineY); | |
| 232 context.stroke(); | |
| 233 | |
| 234 if (typeof request.priority === 'string') { | |
| 235 var color = this._colorForPriority(request.priority); | |
| 236 if (color) { | |
| 237 context.fillStyle = color; | |
| 238 context.fillRect(sendStart + 0.5, barY + 0.5, 3.5, 3.5); | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 var textStart = Math.max(sendStart, 0); | |
| 243 var textWidth = finish - textStart; | |
| 244 var /** @const */ minTextWidthPx = 20; | |
| 245 if (textWidth >= minTextWidthPx) { | |
| 246 text = this.entryTitle(index) || ''; | |
| 247 if (request.fromServiceWorker) | |
| 248 text = '⚙ ' + text; | |
| 249 if (text) { | |
| 250 var /** @const */ textPadding = 4; | |
| 251 var /** @const */ textBaseline = 5; | |
| 252 var textBaseHeight = barHeight - textBaseline; | |
| 253 var trimmedText = UI.trimTextEnd(context, text, textWidth - 2 * textPadd
ing); | |
| 254 context.fillStyle = '#333'; | |
| 255 context.fillText(trimmedText, textStart + textPadding, barY + textBaseHe
ight); | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 return true; | |
| 260 } | |
| 261 | |
| 262 /** | |
| 263 * @override | |
| 264 * @param {number} index | |
| 265 * @return {boolean} | |
| 266 */ | |
| 267 forceDecoration(index) { | |
| 268 return true; | |
| 269 } | |
| 270 | |
| 271 /** | |
| 272 * @override | |
| 273 * @param {number} index | |
| 274 * @return {?Element} | |
| 275 */ | |
| 276 prepareHighlightedEntryInfo(index) { | |
| 277 var /** @const */ maxURLChars = 80; | |
| 278 var request = /** @type {!TimelineModel.TimelineModel.NetworkRequest} */ (th
is._requests[index]); | |
| 279 if (!request.url) | |
| 280 return null; | |
| 281 var element = createElement('div'); | |
| 282 var root = UI.createShadowRootWithCoreStyles(element, 'timeline/timelineFlam
echartPopover.css'); | |
| 283 var contents = root.createChild('div', 'timeline-flamechart-popover'); | |
| 284 var duration = request.endTime - request.startTime; | |
| 285 if (request.startTime && isFinite(duration)) | |
| 286 contents.createChild('span', 'timeline-info-network-time').textContent = N
umber.millisToString(duration); | |
| 287 if (typeof request.priority === 'string') { | |
| 288 var div = contents.createChild('span'); | |
| 289 div.textContent = | |
| 290 NetworkConditions.uiLabelForPriority(/** @type {!Protocol.Network.Reso
urcePriority} */ (request.priority)); | |
| 291 div.style.color = this._colorForPriority(request.priority) || 'black'; | |
| 292 } | |
| 293 contents.createChild('span').textContent = request.url.trimMiddle(maxURLChar
s); | |
| 294 return element; | |
| 295 } | |
| 296 | |
| 297 /** | |
| 298 * @override | |
| 299 * @param {number} entryIndex | |
| 300 */ | |
| 301 highlightEntry(entryIndex) { | |
| 302 } | |
| 303 | |
| 304 /** | |
| 305 * @param {string} priority | |
| 306 * @return {?string} | |
| 307 */ | |
| 308 _colorForPriority(priority) { | |
| 309 if (!this._priorityToValue) { | |
| 310 var priorities = Protocol.Network.ResourcePriority; | |
| 311 this._priorityToValue = new Map([ | |
| 312 [priorities.VeryLow, 1], [priorities.Low, 2], [priorities.Medium, 3], [p
riorities.High, 4], | |
| 313 [priorities.VeryHigh, 5] | |
| 314 ]); | |
| 315 } | |
| 316 var value = this._priorityToValue.get(priority); | |
| 317 return value ? `hsla(214, 80%, 50%, ${value / 5})` : null; | |
| 318 } | |
| 319 | |
| 320 /** | |
| 321 * @param {!Array.<!SDK.TracingModel.Event>} events | |
| 322 */ | |
| 323 _appendTimelineData(events) { | |
| 324 this._minimumBoundary = this._model.minimumRecordTime(); | |
| 325 this._maximumBoundary = this._model.maximumRecordTime(); | |
| 326 this._timeSpan = this._model.isEmpty() ? 1000 : this._maximumBoundary - this
._minimumBoundary; | |
| 327 this._model.networkRequests().forEach(this._appendEntry.bind(this)); | |
| 328 this._updateTimelineData(); | |
| 329 } | |
| 330 | |
| 331 _updateTimelineData() { | |
| 332 if (!this._timelineData) | |
| 333 return; | |
| 334 var lastTimeByLevel = []; | |
| 335 var maxLevel = 0; | |
| 336 for (var i = 0; i < this._requests.length; ++i) { | |
| 337 var r = this._requests[i]; | |
| 338 var visible = r.startTime < this._endTime && r.endTime > this._startTime; | |
| 339 if (!visible) { | |
| 340 this._timelineData.entryLevels[i] = -1; | |
| 341 continue; | |
| 342 } | |
| 343 while (lastTimeByLevel.length && lastTimeByLevel.peekLast() <= r.startTime
) | |
| 344 lastTimeByLevel.pop(); | |
| 345 this._timelineData.entryLevels[i] = lastTimeByLevel.length; | |
| 346 lastTimeByLevel.push(r.endTime); | |
| 347 maxLevel = Math.max(maxLevel, lastTimeByLevel.length); | |
| 348 } | |
| 349 for (var i = 0; i < this._requests.length; ++i) { | |
| 350 if (this._timelineData.entryLevels[i] === -1) | |
| 351 this._timelineData.entryLevels[i] = maxLevel; | |
| 352 } | |
| 353 this._timelineData = new PerfUI.FlameChart.TimelineData( | |
| 354 this._timelineData.entryLevels, this._timelineData.entryTotalTimes, this
._timelineData.entryStartTimes, | |
| 355 [this._group]); | |
| 356 this._maxLevel = maxLevel; | |
| 357 } | |
| 358 | |
| 359 | |
| 360 /** | |
| 361 * @param {!TimelineModel.TimelineModel.NetworkRequest} request | |
| 362 */ | |
| 363 _appendEntry(request) { | |
| 364 this._requests.push(request); | |
| 365 this._timelineData.entryStartTimes.push(request.startTime); | |
| 366 this._timelineData.entryTotalTimes.push(request.endTime - request.startTime)
; | |
| 367 this._timelineData.entryLevels.push(this._requests.length - 1); | |
| 368 } | |
| 369 | |
| 370 /** | |
| 371 * @return {number} | |
| 372 */ | |
| 373 preferredHeight() { | |
| 374 return this._style.height * (this._group.expanded ? Number.constrain(this._m
axLevel + 1, 4, 8.5) : 1); | |
| 375 } | |
| 376 | |
| 377 /** | |
| 378 * @return {boolean} | |
| 379 */ | |
| 380 isExpanded() { | |
| 381 return this._group.expanded; | |
| 382 } | |
| 383 | |
| 384 /** | |
| 385 * @override | |
| 386 * @param {number} value | |
| 387 * @param {number=} precision | |
| 388 * @return {string} | |
| 389 */ | |
| 390 formatValue(value, precision) { | |
| 391 return Number.preciseMillisToString(value, precision); | |
| 392 } | |
| 393 | |
| 394 /** | |
| 395 * @override | |
| 396 * @param {number} entryIndex | |
| 397 * @return {boolean} | |
| 398 */ | |
| 399 canJumpToEntry(entryIndex) { | |
| 400 return false; | |
| 401 } | |
| 402 }; | |
| OLD | NEW |