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 |