Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/network/NetworkTimelineColumn.js

Issue 2483853004: [Devtools] Renamed Network's Timeline to Waterfall (Closed)
Patch Set: [Devtools] Renamed Network's Timeline to Waterfall Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 * @unrestricted
6 */
7 WebInspector.NetworkTimelineColumn = class extends WebInspector.VBox {
8 /**
9 * @param {number} rowHeight
10 * @param {!WebInspector.NetworkTimeCalculator} calculator
11 */
12 constructor(rowHeight, calculator) {
13 // TODO(allada) Make this a shadowDOM when the NetworkTimelineColumn gets mo ved into NetworkLogViewColumns.
14 super(false);
15 this.registerRequiredCSS('network/networkTimelineColumn.css');
16
17 this._canvas = this.contentElement.createChild('canvas');
18 this._canvas.tabIndex = 1;
19 this.setDefaultFocusedElement(this._canvas);
20 this._canvasPosition = this._canvas.getBoundingClientRect();
21
22 /** @const */
23 this._leftPadding = 5;
24 /** @const */
25 this._fontSize = 10;
26
27 this._rightPadding = 0;
28
29 this._rowHeight = rowHeight;
30 this._headerHeight = 0;
31 this._calculator = calculator;
32
33 this._popoverHelper = new WebInspector.PopoverHelper(this.element);
34 this._popoverHelper.initializeCallbacks(this._getPopoverAnchor.bind(this), t his._showPopover.bind(this));
35 this._popoverHelper.setTimeout(300, 300);
36
37 /** @type {!Array<!WebInspector.NetworkRequest>} */
38 this._requestData = [];
39
40 /** @type {?WebInspector.NetworkRequest} */
41 this._hoveredRequest = null;
42 /** @type {?WebInspector.NetworkRequest.InitiatorGraph} */
43 this._initiatorGraph = null;
44
45 /** @type {?WebInspector.NetworkRequest} */
46 this._navigationRequest = null;
47
48 /** @type {!Map<string, !Array<number>>} */
49 this._eventDividers = new Map();
50
51 var colorUsage = WebInspector.ThemeSupport.ColorUsage;
52 this._rowNavigationRequestColor = WebInspector.themeSupport.patchColor('#def ', colorUsage.Background);
53 this._rowStripeColor = WebInspector.themeSupport.patchColor('#f5f5f5', color Usage.Background);
54 this._rowHoverColor = WebInspector.themeSupport.patchColor(
55 '#ebf2fc', /** @type {!WebInspector.ThemeSupport.ColorUsage} */ (colorUs age.Background | colorUsage.Selection));
56 this._parentInitiatorColor =
57 WebInspector.themeSupport.patchColor('hsla(120, 68%, 54%, 0.2)', colorUs age.Background);
58 this._initiatedColor = WebInspector.themeSupport.patchColor('hsla(0, 68%, 54 %, 0.2)', colorUsage.Background);
59
60 /** @type {!Map<!WebInspector.ResourceType, string>} */
61 this._borderColorsForResourceTypeCache = new Map();
62 /** @type {!Map<string, !CanvasGradient>} */
63 this._colorsForResourceTypeCache = new Map();
64 }
65
66 /**
67 * @override
68 */
69 willHide() {
70 this._popoverHelper.hidePopover();
71 }
72
73 /**
74 * @override
75 */
76 wasShown() {
77 this.update();
78 }
79
80 /**
81 * @param {!Element} element
82 * @param {!Event} event
83 * @return {!AnchorBox|undefined}
84 */
85 _getPopoverAnchor(element, event) {
86 if (!this._hoveredRequest)
87 return;
88 var useTimingBars =
89 !WebInspector.moduleSetting('networkColorCodeResourceTypes').get() && !t his._calculator.startAtZero;
90 if (useTimingBars) {
91 var range = WebInspector.RequestTimingView.calculateRequestTimeRanges(this ._hoveredRequest, 0)
92 .find(data => data.name === WebInspector.RequestTimeRangeNames.Total);
93 var start = this._timeToPosition(range.start);
94 var end = this._timeToPosition(range.end);
95 } else {
96 var range = this._getSimplifiedBarRange(this._hoveredRequest, 0);
97 var start = range.start;
98 var end = range.end;
99 }
100
101 if (event.clientX < this._canvasPosition.left + start || event.clientX > thi s._canvasPosition.left + end)
102 return;
103
104 var rowIndex = this._requestData.findIndex(request => this._hoveredRequest = == request);
105 var barHeight = this._getBarHeight(range.name);
106 var y = this._headerHeight + (this._rowHeight * rowIndex - this._scrollTop) + ((this._rowHeight - barHeight) / 2);
107
108 if (event.clientY < this._canvasPosition.top + y || event.clientY > this._ca nvasPosition.top + y + barHeight)
109 return;
110
111 var anchorBox = this.element.boxInWindow();
112 anchorBox.x += start;
113 anchorBox.y += y;
114 anchorBox.width = end - start;
115 anchorBox.height = barHeight;
116 return anchorBox;
117 }
118
119 /**
120 * @param {!Element|!AnchorBox} anchor
121 * @param {!WebInspector.Popover} popover
122 */
123 _showPopover(anchor, popover) {
124 if (!this._hoveredRequest)
125 return;
126 var content =
127 WebInspector.RequestTimingView.createTimingTable(this._hoveredRequest, t his._calculator.minimumBoundary());
128 popover.showForAnchor(content, anchor);
129 }
130
131 /**
132 * @param {?WebInspector.NetworkRequest} request
133 * @param {boolean} highlightInitiatorChain
134 */
135 setHoveredRequest(request, highlightInitiatorChain) {
136 this._hoveredRequest = request;
137 this._initiatorGraph = (highlightInitiatorChain && request) ? request.initia torGraph() : null;
138 this.update();
139 }
140
141 /**
142 * @param {number} height
143 */
144 setRowHeight(height) {
145 this._rowHeight = height;
146 }
147
148 /**
149 * @param {number} height
150 */
151 setHeaderHeight(height) {
152 this._headerHeight = height;
153 }
154
155 /**
156 * @param {number} padding
157 */
158 setRightPadding(padding) {
159 this._rightPadding = padding;
160 this._calculateCanvasSize();
161 }
162
163 /**
164 * @param {!WebInspector.NetworkTimeCalculator} calculator
165 */
166 setCalculator(calculator) {
167 this._calculator = calculator;
168 }
169
170 /**
171 * @param {number} x
172 * @param {number} y
173 * @return {?WebInspector.NetworkRequest}
174 */
175 getRequestFromPoint(x, y) {
176 return this._requestData[Math.floor((this._scrollTop + y - this._headerHeigh t) / this._rowHeight)] || null;
177 }
178
179 scheduleDraw() {
180 if (this._updateRequestID)
181 return;
182 this._updateRequestID = this.element.window().requestAnimationFrame(() => th is.update());
183 }
184
185 /**
186 * @param {number=} scrollTop
187 * @param {!Map<string, !Array<number>>=} eventDividers
188 * @param {!WebInspector.NetworkTimelineColumn.RequestData=} requestData
189 */
190 update(scrollTop, eventDividers, requestData) {
191 if (scrollTop !== undefined)
192 this._scrollTop = scrollTop;
193 if (requestData) {
194 this._requestData = requestData.requests;
195 this._navigationRequest = requestData.navigationRequest;
196 this._calculateCanvasSize();
197 }
198 if (eventDividers !== undefined)
199 this._eventDividers = eventDividers;
200 this.element.window().cancelAnimationFrame(this._updateRequestID);
201 this._updateRequestID = null;
202
203 this._startTime = this._calculator.minimumBoundary();
204 this._endTime = this._calculator.maximumBoundary();
205 this._resetCanvas();
206 this._draw();
207 }
208
209 _resetCanvas() {
210 var ratio = window.devicePixelRatio;
211 this._canvas.width = this._offsetWidth * ratio;
212 this._canvas.height = this._offsetHeight * ratio;
213 this._canvas.style.width = this._offsetWidth + 'px';
214 this._canvas.style.height = this._offsetHeight + 'px';
215 }
216
217 /**
218 * @override
219 */
220 onResize() {
221 super.onResize();
222 this._calculateCanvasSize();
223 this.scheduleDraw();
224 }
225
226 _calculateCanvasSize() {
227 this._offsetWidth = this.contentElement.offsetWidth - this._rightPadding;
228 this._offsetHeight = this.contentElement.offsetHeight;
229 this._calculator.setDisplayWindow(this._offsetWidth);
230 this._canvasPosition = this._canvas.getBoundingClientRect();
231 }
232
233 /**
234 * @param {!WebInspector.RequestTimeRangeNames} type
235 * @return {string}
236 */
237 _colorForType(type) {
238 var types = WebInspector.RequestTimeRangeNames;
239 switch (type) {
240 case types.Receiving:
241 case types.ReceivingPush:
242 return '#03A9F4';
243 case types.Waiting:
244 return '#00C853';
245 case types.Connecting:
246 return '#FF9800';
247 case types.SSL:
248 return '#9C27B0';
249 case types.DNS:
250 return '#009688';
251 case types.Proxy:
252 return '#A1887F';
253 case types.Blocking:
254 return '#AAAAAA';
255 case types.Push:
256 return '#8CDBff';
257 case types.Queueing:
258 return 'white';
259 case types.ServiceWorker:
260 case types.ServiceWorkerPreparation:
261 default:
262 return 'orange';
263 }
264 }
265
266 /**
267 * @param {number} time
268 * @return {number}
269 */
270 _timeToPosition(time) {
271 var availableWidth = this._offsetWidth - this._leftPadding;
272 var timeToPixel = availableWidth / (this._endTime - this._startTime);
273 return Math.floor(this._leftPadding + (time - this._startTime) * timeToPixel );
274 }
275
276 _draw() {
277 var useTimingBars =
278 !WebInspector.moduleSetting('networkColorCodeResourceTypes').get() && !t his._calculator.startAtZero;
279 var requests = this._requestData;
280 var context = this._canvas.getContext('2d');
281 context.save();
282 context.scale(window.devicePixelRatio, window.devicePixelRatio);
283 context.translate(0, this._headerHeight);
284 context.rect(0, 0, this._offsetWidth, this._offsetHeight);
285 context.clip();
286 var firstRequestIndex = Math.floor(this._scrollTop / this._rowHeight);
287 var lastRequestIndex =
288 Math.min(requests.length, firstRequestIndex + Math.ceil(this._offsetHeig ht / this._rowHeight));
289 for (var i = firstRequestIndex; i < lastRequestIndex; i++) {
290 var rowOffset = this._rowHeight * i;
291 var request = requests[i];
292 this._decorateRow(context, request, i, rowOffset - this._scrollTop);
293 if (useTimingBars)
294 this._drawTimingBars(context, request, rowOffset - this._scrollTop);
295 else
296 this._drawSimplifiedBars(context, request, rowOffset - this._scrollTop);
297 }
298 this._drawEventDividers(context);
299 context.restore();
300
301 const freeZoneAtLeft = 75;
302 WebInspector.TimelineGrid.drawCanvasGrid(context, this._calculator, this._fo ntSize, freeZoneAtLeft);
303 }
304
305 /**
306 * @param {!CanvasRenderingContext2D} context
307 */
308 _drawEventDividers(context) {
309 context.save();
310 context.lineWidth = 1;
311 for (var color of this._eventDividers.keys()) {
312 context.strokeStyle = color;
313 for (var time of this._eventDividers.get(color)) {
314 context.beginPath();
315 var x = this._timeToPosition(time);
316 context.moveTo(x, 0);
317 context.lineTo(x, this._offsetHeight);
318 }
319 context.stroke();
320 }
321 context.restore();
322 }
323
324 /**
325 * @return {number}
326 */
327 _timelineDuration() {
328 return this._calculator.maximumBoundary() - this._calculator.minimumBoundary ();
329 }
330
331 /**
332 * @param {!WebInspector.RequestTimeRangeNames=} type
333 * @return {number}
334 */
335 _getBarHeight(type) {
336 var types = WebInspector.RequestTimeRangeNames;
337 switch (type) {
338 case types.Connecting:
339 case types.SSL:
340 case types.DNS:
341 case types.Proxy:
342 case types.Blocking:
343 case types.Push:
344 case types.Queueing:
345 return 7;
346 default:
347 return 13;
348 }
349 }
350
351 /**
352 * @param {!WebInspector.NetworkRequest} request
353 * @return {string}
354 */
355 _borderColorForResourceType(request) {
356 var resourceType = request.resourceType();
357 if (this._borderColorsForResourceTypeCache.has(resourceType))
358 return this._borderColorsForResourceTypeCache.get(resourceType);
359 var colorsForResourceType = WebInspector.NetworkTimelineColumn._colorsForRes ourceType;
360 var color = colorsForResourceType[resourceType] || colorsForResourceType.Oth er;
361 var parsedColor = WebInspector.Color.parse(color);
362 var hsla = parsedColor.hsla();
363 hsla[1] /= 2;
364 hsla[2] -= Math.min(hsla[2], 0.2);
365 var resultColor = /** @type {string} */ (parsedColor.asString(null));
366 this._borderColorsForResourceTypeCache.set(resourceType, resultColor);
367 return resultColor;
368 }
369
370 /**
371 * @param {!CanvasRenderingContext2D} context
372 * @param {!WebInspector.NetworkRequest} request
373 * @return {string|!CanvasGradient}
374 */
375 _colorForResourceType(context, request) {
376 var colorsForResourceType = WebInspector.NetworkTimelineColumn._colorsForRes ourceType;
377 var resourceType = request.resourceType();
378 var color = colorsForResourceType[resourceType] || colorsForResourceType.Oth er;
379 if (request.cached())
380 return color;
381
382 if (this._colorsForResourceTypeCache.has(color))
383 return this._colorsForResourceTypeCache.get(color);
384 var parsedColor = WebInspector.Color.parse(color);
385 var hsla = parsedColor.hsla();
386 hsla[1] -= Math.min(hsla[1], 0.28);
387 hsla[2] -= Math.min(hsla[2], 0.15);
388 var gradient = context.createLinearGradient(0, 0, 0, this._getBarHeight());
389 gradient.addColorStop(0, color);
390 gradient.addColorStop(1, /** @type {string} */ (parsedColor.asString(null))) ;
391 this._colorsForResourceTypeCache.set(color, gradient);
392 return gradient;
393 }
394
395 /**
396 * @param {!WebInspector.NetworkRequest} request
397 * @param {number} borderOffset
398 * @return {!{start: number, mid: number, end: number}}
399 */
400 _getSimplifiedBarRange(request, borderOffset) {
401 var drawWidth = this._offsetWidth - this._leftPadding;
402 var percentages = this._calculator.computeBarGraphPercentages(request);
403 return {
404 start: this._leftPadding + Math.floor((percentages.start / 100) * drawWidt h) + borderOffset,
405 mid: this._leftPadding + Math.floor((percentages.middle / 100) * drawWidth ) + borderOffset,
406 end: this._leftPadding + Math.floor((percentages.end / 100) * drawWidth) + borderOffset
407 };
408 }
409
410 /**
411 * @param {!CanvasRenderingContext2D} context
412 * @param {!WebInspector.NetworkRequest} request
413 * @param {number} y
414 */
415 _drawSimplifiedBars(context, request, y) {
416 const borderWidth = 1;
417 var borderOffset = borderWidth % 2 === 0 ? 0 : 0.5;
418
419 context.save();
420 var ranges = this._getSimplifiedBarRange(request, borderOffset);
421 var height = this._getBarHeight();
422 y += Math.floor(this._rowHeight / 2 - height / 2 + borderWidth) - borderWidt h / 2;
423
424 context.translate(0, y);
425 context.fillStyle = this._colorForResourceType(context, request);
426 context.strokeStyle = this._borderColorForResourceType(request);
427 context.lineWidth = borderWidth;
428
429 context.beginPath();
430 context.globalAlpha = 0.5;
431 context.rect(ranges.start, 0, ranges.mid - ranges.start, height - borderWidt h);
432 context.fill();
433 context.stroke();
434
435 var barWidth = Math.max(2, ranges.end - ranges.mid);
436 context.beginPath();
437 context.globalAlpha = 1;
438 context.rect(ranges.mid, 0, barWidth, height - borderWidth);
439 context.fill();
440 context.stroke();
441
442 /** @type {?{left: string, right: string, tooltip: (string|undefined)}} */
443 var labels = null;
444 if (request === this._hoveredRequest) {
445 labels = this._calculator.computeBarGraphLabels(request);
446 this._drawSimplifiedBarDetails(
447 context, labels.left, labels.right, ranges.start, ranges.mid, ranges.m id + barWidth + borderOffset);
448 }
449
450 if (!this._calculator.startAtZero) {
451 var queueingRange = WebInspector.RequestTimingView.calculateRequestTimeRan ges(request, 0)
452 .find(data => data.name === WebInspector.RequestTimeRangeNames.Total);
453 var leftLabelWidth = labels ? context.measureText(labels.left).width : 0;
454 var leftTextPlacedInBar = leftLabelWidth < ranges.mid - ranges.start;
455 const wiskerTextPadding = 13;
456 var textOffset = (labels && !leftTextPlacedInBar) ? leftLabelWidth + wiske rTextPadding : 0;
457 var queueingStart = this._timeToPosition(queueingRange.start);
458 if (ranges.start - textOffset > queueingStart) {
459 context.beginPath();
460 context.globalAlpha = 1;
461 context.strokeStyle = WebInspector.themeSupport.patchColor(
462 '#a5a5a5', WebInspector.ThemeSupport.ColorUsage.Foreground);
463 context.moveTo(queueingStart, Math.floor(height / 2));
464 context.lineTo(ranges.start - textOffset, Math.floor(height / 2));
465
466 const wiskerHeight = height / 2;
467 context.moveTo(queueingStart + borderOffset, wiskerHeight / 2);
468 context.lineTo(queueingStart + borderOffset, height - wiskerHeight / 2 - 1);
469 context.stroke();
470 }
471 }
472
473 context.restore();
474 }
475
476 /**
477 * @param {!CanvasRenderingContext2D} context
478 * @param {string} leftText
479 * @param {string} rightText
480 * @param {number} startX
481 * @param {number} midX
482 * @param {number} endX
483 */
484 _drawSimplifiedBarDetails(context, leftText, rightText, startX, midX, endX) {
485 /** @const */
486 var barDotLineLength = 10;
487
488 context.save();
489 var height = this._getBarHeight();
490 var leftLabelWidth = context.measureText(leftText).width;
491 var rightLabelWidth = context.measureText(rightText).width;
492 context.fillStyle = WebInspector.themeSupport.patchColor('#444', WebInspecto r.ThemeSupport.ColorUsage.Foreground);
493 context.strokeStyle = WebInspector.themeSupport.patchColor('#444', WebInspec tor.ThemeSupport.ColorUsage.Foreground);
494 if (leftLabelWidth < midX - startX) {
495 var midBarX = startX + (midX - startX) / 2 - leftLabelWidth / 2;
496 context.fillText(leftText, midBarX, this._fontSize);
497 } else if (barDotLineLength + leftLabelWidth + this._leftPadding < startX) {
498 context.beginPath();
499 context.arc(startX, Math.floor(height / 2), 2, 0, 2 * Math.PI);
500 context.fill();
501 context.fillText(leftText, startX - leftLabelWidth - barDotLineLength - 1, this._fontSize);
502 context.beginPath();
503 context.lineWidth = 1;
504 context.moveTo(startX - barDotLineLength, Math.floor(height / 2));
505 context.lineTo(startX, Math.floor(height / 2));
506 context.stroke();
507 }
508
509 if (rightLabelWidth < endX - midX) {
510 var midBarX = midX + (endX - midX) / 2 - rightLabelWidth / 2;
511 context.fillText(rightText, midBarX, this._fontSize);
512 } else if (endX + barDotLineLength + rightLabelWidth < this._offsetWidth - t his._leftPadding) {
513 context.beginPath();
514 context.arc(endX, Math.floor(height / 2), 2, 0, 2 * Math.PI);
515 context.fill();
516 context.fillText(rightText, endX + barDotLineLength + 1, this._fontSize);
517 context.beginPath();
518 context.lineWidth = 1;
519 context.moveTo(endX, Math.floor(height / 2));
520 context.lineTo(endX + barDotLineLength, Math.floor(height / 2));
521 context.stroke();
522 }
523 context.restore();
524 }
525
526 /**
527 * @param {!CanvasRenderingContext2D} context
528 * @param {!WebInspector.NetworkRequest} request
529 * @param {number} y
530 */
531 _drawTimingBars(context, request, y) {
532 context.save();
533 var ranges = WebInspector.RequestTimingView.calculateRequestTimeRanges(reque st, 0);
534 for (var range of ranges) {
535 if (range.name === WebInspector.RequestTimeRangeNames.Total ||
536 range.name === WebInspector.RequestTimeRangeNames.Sending || range.end - range.start === 0)
537 continue;
538 context.beginPath();
539 var lineWidth = 0;
540 var color = this._colorForType(range.name);
541 var borderColor = color;
542 if (range.name === WebInspector.RequestTimeRangeNames.Queueing) {
543 borderColor = 'lightgrey';
544 lineWidth = 2;
545 }
546 if (range.name === WebInspector.RequestTimeRangeNames.Receiving)
547 lineWidth = 2;
548 context.fillStyle = color;
549 var height = this._getBarHeight(range.name);
550 var middleBarY = y + Math.floor(this._rowHeight / 2 - height / 2) + lineWi dth / 2;
551 var start = this._timeToPosition(range.start);
552 var end = this._timeToPosition(range.end);
553 context.rect(start, middleBarY, end - start, height - lineWidth);
554 if (lineWidth) {
555 context.lineWidth = lineWidth;
556 context.strokeStyle = borderColor;
557 context.stroke();
558 }
559 context.fill();
560 }
561 context.restore();
562 }
563
564 /**
565 * @param {!CanvasRenderingContext2D} context
566 * @param {!WebInspector.NetworkRequest} request
567 * @param {number} rowNumber
568 * @param {number} y
569 */
570 _decorateRow(context, request, rowNumber, y) {
571 if (rowNumber % 2 === 1 && this._hoveredRequest !== request && this._navigat ionRequest !== request &&
572 !this._initiatorGraph)
573 return;
574
575 var color = getRowColor.call(this);
576 if (color === 'transparent')
577 return;
578 context.save();
579 context.beginPath();
580 context.fillStyle = color;
581 context.rect(0, y, this._offsetWidth, this._rowHeight);
582 context.fill();
583 context.restore();
584
585 /**
586 * @return {string}
587 * @this {WebInspector.NetworkTimelineColumn}
588 */
589 function getRowColor() {
590 if (this._hoveredRequest === request)
591 return this._rowHoverColor;
592 if (this._initiatorGraph) {
593 if (this._initiatorGraph.initiators.has(request))
594 return this._parentInitiatorColor;
595 if (this._initiatorGraph.initiated.has(request))
596 return this._initiatedColor;
597 }
598 if (this._navigationRequest === request)
599 return this._rowNavigationRequestColor;
600 if (rowNumber % 2 === 1)
601 return 'transparent';
602 return this._rowStripeColor;
603 }
604 }
605 };
606
607 /**
608 * @typedef {{requests: !Array<!WebInspector.NetworkRequest>, navigationRequest: ?WebInspector.NetworkRequest}}
609 */
610 WebInspector.NetworkTimelineColumn.RequestData;
611
612 WebInspector.NetworkTimelineColumn._colorsForResourceType = {
613 document: 'hsl(215, 100%, 80%)',
614 font: 'hsl(8, 100%, 80%)',
615 media: 'hsl(272, 64%, 80%)',
616 image: 'hsl(272, 64%, 80%)',
617 script: 'hsl(31, 100%, 80%)',
618 stylesheet: 'hsl(90, 50%, 80%)',
619 texttrack: 'hsl(8, 100%, 80%)',
620 websocket: 'hsl(0, 0%, 95%)',
621 xhr: 'hsl(53, 100%, 80%)',
622 other: 'hsl(0, 0%, 95%)'
623 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698