Files
react-native-devtools-frontend/front_end/network/NetworkWaterfallColumn.js
T
Jack Franklin e2fe0e4a4b [Dark Mode]: migrate CSS files with no colors
This CL batches up changes to multiple CSS files where they had either
no colors to change, or one very easy color to change.

Bug: 1152736
Change-Id: I061ebd909539dfaca7e8c3a70b0e97f0a0ba5f07
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2567115
Auto-Submit: Jack Franklin <jacktfranklin@chromium.org>
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
2020-12-01 15:59:15 +00:00

700 lines
24 KiB
JavaScript

// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Common from '../common/common.js';
import * as PerfUI from '../perf_ui/perf_ui.js';
import * as SDK from '../sdk/sdk.js'; // eslint-disable-line no-unused-vars
import * as ThemeSupport from '../theme_support/theme_support.js';
import * as UI from '../ui/ui.js';
import {NetworkNode} from './NetworkDataGridNode.js'; // eslint-disable-line no-unused-vars
import {NetworkTimeCalculator} from './NetworkTimeCalculator.js'; // eslint-disable-line no-unused-vars
import {RequestTimeRange, RequestTimeRangeNames, RequestTimingView} from './RequestTimingView.js'; // eslint-disable-line no-unused-vars
export class NetworkWaterfallColumn extends UI.Widget.VBox {
/**
* @param {!NetworkTimeCalculator} calculator
*/
constructor(calculator) {
// TODO(allada) Make this a shadowDOM when the NetworkWaterfallColumn gets moved into NetworkLogViewColumns.
super(false);
this.registerRequiredCSS('network/networkWaterfallColumn.css', {enableLegacyPatching: false});
/** @type {!HTMLCanvasElement} */
this._canvas = /** @type {!HTMLCanvasElement} */ (this.contentElement.createChild('canvas'));
this._canvas.tabIndex = -1;
this.setDefaultFocusedElement(this._canvas);
this._canvasPosition = this._canvas.getBoundingClientRect();
/** @const */
this._leftPadding = 5;
/** @const */
this._fontSize = 10;
this._rightPadding = 0;
this._scrollTop = 0;
this._headerHeight = 0;
this._calculator = calculator;
// this._rawRowHeight captures model height (41 or 21px),
// this._rowHeight is computed height of the row in CSS pixels, can be 20.8 for zoomed-in content.
this._rawRowHeight = 0;
this._rowHeight = 0;
this._offsetWidth = 0;
this._offsetHeight = 0;
this._startTime = this._calculator.minimumBoundary();
this._endTime = this._calculator.maximumBoundary();
this._popoverHelper = new UI.PopoverHelper.PopoverHelper(this.element, this._getPopoverRequest.bind(this));
this._popoverHelper.setHasPadding(true);
this._popoverHelper.setTimeout(300, 300);
/** @type {!Array<!NetworkNode>} */
this._nodes = [];
/** @type {?NetworkNode} */
this._hoveredNode = null;
/** @type {!Map<string, !Array<number>>} */
this._eventDividers = new Map();
/** @type {(number|undefined)} */
this._updateRequestID;
this.element.addEventListener('mousemove', this._onMouseMove.bind(this), true);
this.element.addEventListener('mouseleave', event => this._setHoveredNode(null, false), true);
this.element.addEventListener('click', this._onClick.bind(this), true);
this._styleForTimeRangeName = NetworkWaterfallColumn._buildRequestTimeRangeStyle();
const resourceStyleTuple = NetworkWaterfallColumn._buildResourceTypeStyle();
/** @type {!Map<!Common.ResourceType.ResourceType, !_LayerStyle>} */
this._styleForWaitingResourceType = resourceStyleTuple[0];
/** @type {!Map<!Common.ResourceType.ResourceType, !_LayerStyle>} */
this._styleForDownloadingResourceType = resourceStyleTuple[1];
const baseLineColor =
ThemeSupport.ThemeSupport.instance().patchColorText('#a5a5a5', ThemeSupport.ThemeSupport.ColorUsage.Foreground);
/** @type {!_LayerStyle} */
this._wiskerStyle = {borderColor: baseLineColor, lineWidth: 1, fillStyle: undefined};
/** @type {!_LayerStyle} */
this._hoverDetailsStyle = {fillStyle: baseLineColor, lineWidth: 1, borderColor: baseLineColor};
/** @type {!Map<!_LayerStyle, !Path2D>} */
this._pathForStyle = new Map();
/** @type {!Array<!_TextLayer>} */
this._textLayers = [];
}
/**
* @return {!Map<!RequestTimeRangeNames, !_LayerStyle>}
*/
static _buildRequestTimeRangeStyle() {
const types = RequestTimeRangeNames;
const styleMap = new Map();
styleMap.set(types.Connecting, {fillStyle: '#FF9800'});
styleMap.set(types.SSL, {fillStyle: '#9C27B0'});
styleMap.set(types.DNS, {fillStyle: '#009688'});
styleMap.set(types.Proxy, {fillStyle: '#A1887F'});
styleMap.set(types.Blocking, {fillStyle: '#AAAAAA'});
styleMap.set(types.Push, {fillStyle: '#8CDBff'});
styleMap.set(types.Queueing, {fillStyle: 'white', lineWidth: 2, borderColor: 'lightgrey'});
// This ensures we always show at least 2 px for a request.
styleMap.set(types.Receiving, {fillStyle: '#03A9F4', lineWidth: 2, borderColor: '#03A9F4'});
styleMap.set(types.Waiting, {fillStyle: '#00C853'});
styleMap.set(types.ReceivingPush, {fillStyle: '#03A9F4'});
styleMap.set(types.ServiceWorker, {fillStyle: 'orange'});
styleMap.set(types.ServiceWorkerPreparation, {fillStyle: 'orange'});
styleMap.set(types.ServiceWorkerRespondWith, {fillStyle: '#A8A3FF'});
return styleMap;
}
/**
* @return {!Array<!Map<!Common.ResourceType.ResourceType, !_LayerStyle>>}
*/
static _buildResourceTypeStyle() {
const baseResourceTypeColors = new Map([
['document', 'hsl(215, 100%, 80%)'],
['font', 'hsl(8, 100%, 80%)'],
['media', 'hsl(90, 50%, 80%)'],
['image', 'hsl(90, 50%, 80%)'],
['script', 'hsl(31, 100%, 80%)'],
['stylesheet', 'hsl(272, 64%, 80%)'],
['texttrack', 'hsl(8, 100%, 80%)'],
['websocket', 'hsl(0, 0%, 95%)'],
['xhr', 'hsl(53, 100%, 80%)'],
['fetch', 'hsl(53, 100%, 80%)'],
['other', 'hsl(0, 0%, 95%)'],
]);
const waitingStyleMap = new Map();
const downloadingStyleMap = new Map();
for (const resourceType of Object.values(Common.ResourceType.resourceTypes)) {
let color = baseResourceTypeColors.get(resourceType.name());
if (!color) {
color = baseResourceTypeColors.get('other');
}
const borderColor = toBorderColor(/** @type {string} */ (color));
waitingStyleMap.set(
resourceType,
{fillStyle: toWaitingColor(/** @type {string} */ (color)), lineWidth: 1, borderColor: borderColor});
downloadingStyleMap.set(resourceType, {fillStyle: color, lineWidth: 1, borderColor: borderColor});
}
return [waitingStyleMap, downloadingStyleMap];
/**
* @param {string} color
*/
function toBorderColor(color) {
const parsedColor = Common.Color.Color.parse(color);
if (!parsedColor) {
return '';
}
const hsla = parsedColor.hsla();
hsla[1] /= 2;
hsla[2] -= Math.min(hsla[2], 0.2);
return parsedColor.asString(null);
}
/**
* @param {string} color
*/
function toWaitingColor(color) {
const parsedColor = Common.Color.Color.parse(color);
if (!parsedColor) {
return '';
}
const hsla = parsedColor.hsla();
hsla[2] *= 1.1;
return parsedColor.asString(null);
}
}
_resetPaths() {
this._pathForStyle.clear();
this._pathForStyle.set(this._wiskerStyle, new Path2D());
this._styleForTimeRangeName.forEach(style => this._pathForStyle.set(style, new Path2D()));
this._styleForWaitingResourceType.forEach(style => this._pathForStyle.set(style, new Path2D()));
this._styleForDownloadingResourceType.forEach(style => this._pathForStyle.set(style, new Path2D()));
this._pathForStyle.set(this._hoverDetailsStyle, new Path2D());
}
/**
* @override
*/
willHide() {
this._popoverHelper.hidePopover();
}
/**
* @override
*/
wasShown() {
this.update();
}
/**
* @param {!Event} event
*/
_onMouseMove(/** @type {!MouseEvent} */ event) {
this._setHoveredNode(this.getNodeFromPoint(event.offsetX, event.offsetY), event.shiftKey);
}
/**
* @param {!Event} event
*/
_onClick(/** @type {!MouseEvent} */ event) {
const handled = this._setSelectedNode(this.getNodeFromPoint(event.offsetX, event.offsetY));
if (handled) {
event.consume(true);
}
}
/**
* @param {!MouseEvent} event
* @return {?UI.PopoverHelper.PopoverRequest}
*/
_getPopoverRequest(event) {
if (!this._hoveredNode) {
return null;
}
const request = this._hoveredNode.request();
if (!request) {
return null;
}
const useTimingBars = !Common.Settings.Settings.instance().moduleSetting('networkColorCodeResourceTypes').get() &&
!this._calculator.startAtZero;
let range;
let start;
let end;
if (useTimingBars) {
range = RequestTimingView.calculateRequestTimeRanges(request, 0)
.find(data => data.name === RequestTimeRangeNames.Total);
start = this._timeToPosition(/** @type {!RequestTimeRange} */ (range).start);
end = this._timeToPosition(/** @type {!RequestTimeRange} */ (range).end);
} else {
range = this._getSimplifiedBarRange(request, 0);
start = range.start;
end = range.end;
}
if (end - start < 50) {
const halfWidth = (end - start) / 2;
start = start + halfWidth - 25;
end = end - halfWidth + 25;
}
if (event.clientX < this._canvasPosition.left + start || event.clientX > this._canvasPosition.left + end) {
return null;
}
const rowIndex = this._nodes.findIndex(node => node.hovered());
const barHeight = this._getBarHeight(/** @type {!RequestTimeRange} */ (range).name);
const y = this._headerHeight + (this._rowHeight * rowIndex - this._scrollTop) + ((this._rowHeight - barHeight) / 2);
if (event.clientY < this._canvasPosition.top + y || event.clientY > this._canvasPosition.top + y + barHeight) {
return null;
}
const anchorBox = this.element.boxInWindow();
anchorBox.x += start;
anchorBox.y += y;
anchorBox.width = end - start;
anchorBox.height = barHeight;
return {
box: anchorBox,
/** @param {!UI.GlassPane.GlassPane} popover */
show: popover => {
const content = RequestTimingView.createTimingTable(
/** @type {!SDK.NetworkRequest.NetworkRequest} */ (request), this._calculator);
popover.contentElement.appendChild(content);
return Promise.resolve(true);
},
hide: undefined
};
}
/**
* @param {?NetworkNode} node
* @param {boolean} highlightInitiatorChain
*/
_setHoveredNode(node, highlightInitiatorChain) {
if (this._hoveredNode) {
this._hoveredNode.setHovered(false, false);
}
this._hoveredNode = node;
if (this._hoveredNode) {
this._hoveredNode.setHovered(true, highlightInitiatorChain);
}
}
/**
* @param {?NetworkNode} node
* @returns {boolean}
*/
_setSelectedNode(node) {
if (node && node.dataGrid) {
node.select();
node.dataGrid.element.focus();
return true;
}
return false;
}
/**
* @param {number} height
*/
setRowHeight(height) {
this._rawRowHeight = height;
this._updateRowHeight();
}
_updateRowHeight() {
this._rowHeight = Math.round(this._rawRowHeight * window.devicePixelRatio) / window.devicePixelRatio;
}
/**
* @param {number} height
*/
setHeaderHeight(height) {
this._headerHeight = height;
}
/**
* @param {number} padding
*/
setRightPadding(padding) {
this._rightPadding = padding;
this._calculateCanvasSize();
}
/**
* @param {!NetworkTimeCalculator} calculator
*/
setCalculator(calculator) {
this._calculator = calculator;
}
/**
* @param {number} x
* @param {number} y
* @return {?NetworkNode}
*/
getNodeFromPoint(x, y) {
if (y <= this._headerHeight) {
return null;
}
return this._nodes[Math.floor((this._scrollTop + y - this._headerHeight) / this._rowHeight)];
}
scheduleDraw() {
if (this._updateRequestID) {
return;
}
this._updateRequestID = this.element.window().requestAnimationFrame(() => this.update());
}
/**
* @param {number=} scrollTop
* @param {!Map<string, !Array<number>>=} eventDividers
* @param {!Array<!NetworkNode>=} nodes
*/
update(scrollTop, eventDividers, nodes) {
if (scrollTop !== undefined && this._scrollTop !== scrollTop) {
this._popoverHelper.hidePopover();
this._scrollTop = scrollTop;
}
if (nodes) {
this._nodes = nodes;
this._calculateCanvasSize();
}
if (eventDividers !== undefined) {
this._eventDividers = eventDividers;
}
if (this._updateRequestID) {
this.element.window().cancelAnimationFrame(this._updateRequestID);
delete this._updateRequestID;
}
this._startTime = this._calculator.minimumBoundary();
this._endTime = this._calculator.maximumBoundary();
this._resetCanvas();
this._resetPaths();
this._textLayers = [];
this._draw();
}
_resetCanvas() {
const ratio = window.devicePixelRatio;
this._canvas.width = this._offsetWidth * ratio;
this._canvas.height = this._offsetHeight * ratio;
this._canvas.style.width = this._offsetWidth + 'px';
this._canvas.style.height = this._offsetHeight + 'px';
}
/**
* @override
*/
onResize() {
super.onResize();
this._updateRowHeight();
this._calculateCanvasSize();
this.scheduleDraw();
}
_calculateCanvasSize() {
this._offsetWidth = this.contentElement.offsetWidth - this._rightPadding;
this._offsetHeight = this.contentElement.offsetHeight;
this._calculator.setDisplayWidth(this._offsetWidth);
this._canvasPosition = this._canvas.getBoundingClientRect();
}
/**
* @param {number} time
* @return {number}
*/
_timeToPosition(time) {
const availableWidth = this._offsetWidth - this._leftPadding;
const timeToPixel = availableWidth / (this._endTime - this._startTime);
return Math.floor(this._leftPadding + (time - this._startTime) * timeToPixel);
}
_didDrawForTest() {
}
_draw() {
const useTimingBars = !Common.Settings.Settings.instance().moduleSetting('networkColorCodeResourceTypes').get() &&
!this._calculator.startAtZero;
const nodes = this._nodes;
/** @type {?CanvasRenderingContext2D} */
const context = /** @type {?CanvasRenderingContext2D} */ (this._canvas.getContext('2d'));
if (!context) {
return;
}
context.save();
context.scale(window.devicePixelRatio, window.devicePixelRatio);
context.translate(0, this._headerHeight);
context.rect(0, 0, this._offsetWidth, this._offsetHeight);
context.clip();
const firstRequestIndex = Math.floor(this._scrollTop / this._rowHeight);
const lastRequestIndex =
Math.min(nodes.length, firstRequestIndex + Math.ceil(this._offsetHeight / this._rowHeight));
for (let i = firstRequestIndex; i < lastRequestIndex; i++) {
const rowOffset = this._rowHeight * i;
const node = nodes[i];
this._decorateRow(context, node, rowOffset - this._scrollTop);
/** @type {!Array<!NetworkNode>} */
let drawNodes = [];
if (node.hasChildren() && !node.expanded) {
drawNodes = /** @type {!Array<!NetworkNode>} */ (node.flatChildren());
}
drawNodes.push(node);
for (const drawNode of drawNodes) {
if (useTimingBars) {
this._buildTimingBarLayers(drawNode, rowOffset - this._scrollTop);
} else {
this._buildSimplifiedBarLayers(context, drawNode, rowOffset - this._scrollTop);
}
}
}
this._drawLayers(context);
context.save();
context.fillStyle =
ThemeSupport.ThemeSupport.instance().patchColorText('#888', ThemeSupport.ThemeSupport.ColorUsage.Foreground);
for (const textData of this._textLayers) {
context.fillText(textData.text, textData.x, textData.y);
}
context.restore();
this._drawEventDividers(context);
context.restore();
const freeZoneAtLeft = 75;
const freeZoneAtRight = 18;
const dividersData = PerfUI.TimelineGrid.TimelineGrid.calculateGridOffsets(this._calculator);
PerfUI.TimelineGrid.TimelineGrid.drawCanvasGrid(context, dividersData);
PerfUI.TimelineGrid.TimelineGrid.drawCanvasHeaders(
context, dividersData, time => this._calculator.formatValue(time, dividersData.precision), this._fontSize,
this._headerHeight, freeZoneAtLeft);
context.clearRect(this._offsetWidth - freeZoneAtRight, 0, freeZoneAtRight, this._headerHeight);
this._didDrawForTest();
}
/**
* @param {!CanvasRenderingContext2D} context
*/
_drawLayers(context) {
for (const entry of this._pathForStyle) {
const style = /** @type {!_LayerStyle} */ (entry[0]);
const path = /** @type {!Path2D} */ (entry[1]);
context.save();
context.beginPath();
if (style.lineWidth) {
context.lineWidth = style.lineWidth;
if (style.borderColor) {
context.strokeStyle = style.borderColor;
}
context.stroke(path);
}
if (style.fillStyle) {
context.fillStyle = style.fillStyle;
context.fill(path);
}
context.restore();
}
}
/**
* @param {!CanvasRenderingContext2D} context
*/
_drawEventDividers(context) {
context.save();
context.lineWidth = 1;
for (const color of this._eventDividers.keys()) {
context.strokeStyle = color;
for (const time of this._eventDividers.get(color) || []) {
context.beginPath();
const x = this._timeToPosition(time);
context.moveTo(x, 0);
context.lineTo(x, this._offsetHeight);
}
context.stroke();
}
context.restore();
}
/**
* @param {!RequestTimeRangeNames=} type
* @return {number}
*/
_getBarHeight(type) {
const types = RequestTimeRangeNames;
switch (type) {
case types.Connecting:
case types.SSL:
case types.DNS:
case types.Proxy:
case types.Blocking:
case types.Push:
case types.Queueing:
return 7;
default:
return 13;
}
}
/**
* @param {!SDK.NetworkRequest.NetworkRequest} request
* @param {number} borderOffset
* @return {!{start: number, mid: number, end: number}}
*/
_getSimplifiedBarRange(request, borderOffset) {
const drawWidth = this._offsetWidth - this._leftPadding;
const percentages = this._calculator.computeBarGraphPercentages(request);
return {
start: this._leftPadding + Math.floor((percentages.start / 100) * drawWidth) + borderOffset,
mid: this._leftPadding + Math.floor((percentages.middle / 100) * drawWidth) + borderOffset,
end: this._leftPadding + Math.floor((percentages.end / 100) * drawWidth) + borderOffset
};
}
/**
* @param {!CanvasRenderingContext2D} context
* @param {!NetworkNode} node
* @param {number} y
*/
_buildSimplifiedBarLayers(context, node, y) {
const request = node.request();
if (!request) {
return;
}
const borderWidth = 1;
const borderOffset = borderWidth % 2 === 0 ? 0 : 0.5;
const ranges = this._getSimplifiedBarRange(request, borderOffset);
const height = this._getBarHeight();
y += Math.floor(this._rowHeight / 2 - height / 2 + borderWidth) - borderWidth / 2;
const waitingStyle = /** @type {!_LayerStyle} */ (this._styleForWaitingResourceType.get(request.resourceType()));
const waitingPath = /** @type {!Path2D} */ (this._pathForStyle.get(waitingStyle));
waitingPath.rect(ranges.start, y, ranges.mid - ranges.start, height - borderWidth);
const barWidth = Math.max(2, ranges.end - ranges.mid);
const downloadingStyle =
/** @type {!_LayerStyle} */ (this._styleForDownloadingResourceType.get(request.resourceType()));
const downloadingPath = /** @type {!Path2D} */ (this._pathForStyle.get(downloadingStyle));
downloadingPath.rect(ranges.mid, y, barWidth, height - borderWidth);
/** @type {?{left: string, right: string, tooltip: (string|undefined)}} */
let labels = null;
if (node.hovered()) {
labels = this._calculator.computeBarGraphLabels(request);
const barDotLineLength = 10;
const leftLabelWidth = context.measureText(labels.left).width;
const rightLabelWidth = context.measureText(labels.right).width;
const hoverLinePath = /** @type {!Path2D} */ (this._pathForStyle.get(this._hoverDetailsStyle));
if (leftLabelWidth < ranges.mid - ranges.start) {
const midBarX = ranges.start + (ranges.mid - ranges.start - leftLabelWidth) / 2;
this._textLayers.push({text: labels.left, x: midBarX, y: y + this._fontSize});
} else if (barDotLineLength + leftLabelWidth + this._leftPadding < ranges.start) {
this._textLayers.push(
{text: labels.left, x: ranges.start - leftLabelWidth - barDotLineLength - 1, y: y + this._fontSize});
hoverLinePath.moveTo(ranges.start - barDotLineLength, y + Math.floor(height / 2));
hoverLinePath.arc(ranges.start, y + Math.floor(height / 2), 2, 0, 2 * Math.PI);
hoverLinePath.moveTo(ranges.start - barDotLineLength, y + Math.floor(height / 2));
hoverLinePath.lineTo(ranges.start, y + Math.floor(height / 2));
}
const endX = ranges.mid + barWidth + borderOffset;
if (rightLabelWidth < endX - ranges.mid) {
const midBarX = ranges.mid + (endX - ranges.mid - rightLabelWidth) / 2;
this._textLayers.push({text: labels.right, x: midBarX, y: y + this._fontSize});
} else if (endX + barDotLineLength + rightLabelWidth < this._offsetWidth - this._leftPadding) {
this._textLayers.push({text: labels.right, x: endX + barDotLineLength + 1, y: y + this._fontSize});
hoverLinePath.moveTo(endX, y + Math.floor(height / 2));
hoverLinePath.arc(endX, y + Math.floor(height / 2), 2, 0, 2 * Math.PI);
hoverLinePath.moveTo(endX, y + Math.floor(height / 2));
hoverLinePath.lineTo(endX + barDotLineLength, y + Math.floor(height / 2));
}
}
if (!this._calculator.startAtZero) {
const queueingRange =
/** @type {!RequestTimeRange} */ (RequestTimingView.calculateRequestTimeRanges(request, 0)
.find(data => data.name === RequestTimeRangeNames.Total));
const leftLabelWidth = labels ? context.measureText(labels.left).width : 0;
const leftTextPlacedInBar = leftLabelWidth < ranges.mid - ranges.start;
const wiskerTextPadding = 13;
const textOffset = (labels && !leftTextPlacedInBar) ? leftLabelWidth + wiskerTextPadding : 0;
const queueingStart = this._timeToPosition(queueingRange.start);
if (ranges.start - textOffset > queueingStart) {
const wiskerPath = /** @type {!Path2D} */ (this._pathForStyle.get(this._wiskerStyle));
wiskerPath.moveTo(queueingStart, y + Math.floor(height / 2));
wiskerPath.lineTo(ranges.start - textOffset, y + Math.floor(height / 2));
// TODO(allada) This needs to be floored.
const wiskerHeight = height / 2;
wiskerPath.moveTo(queueingStart + borderOffset, y + wiskerHeight / 2);
wiskerPath.lineTo(queueingStart + borderOffset, y + height - wiskerHeight / 2 - 1);
}
}
}
/**
* @param {!NetworkNode} node
* @param {number} y
*/
_buildTimingBarLayers(node, y) {
const request = node.request();
if (!request) {
return;
}
const ranges = RequestTimingView.calculateRequestTimeRanges(request, 0);
for (const range of ranges) {
if (range.name === RequestTimeRangeNames.Total || range.name === RequestTimeRangeNames.Sending ||
range.end - range.start === 0) {
continue;
}
const style = /** @type {!_LayerStyle} */ (this._styleForTimeRangeName.get(range.name));
const path = /** @type {!Path2D} */ (this._pathForStyle.get(style));
const lineWidth = style.lineWidth || 0;
const height = this._getBarHeight(range.name);
const middleBarY = y + Math.floor(this._rowHeight / 2 - height / 2) + lineWidth / 2;
const start = this._timeToPosition(range.start);
const end = this._timeToPosition(range.end);
path.rect(start, middleBarY, end - start, height - lineWidth);
}
}
/**
* @param {!CanvasRenderingContext2D} context
* @param {!NetworkNode} node
* @param {number} y
*/
_decorateRow(context, node, y) {
const nodeBgColorId = node.backgroundColor();
context.save();
context.beginPath();
context.fillStyle = ThemeSupport.ThemeSupport.instance().getComputedValue(nodeBgColorId);
context.rect(0, y, this._offsetWidth, this._rowHeight);
context.fill();
context.restore();
}
}
/** @typedef {!{x: number, y: number, text: string}} */
// @ts-ignore typedef
export let _TextLayer;
/** @typedef {!{fillStyle: (string|undefined), lineWidth: (number|undefined), borderColor: (string|undefined)}} */
// @ts-ignore typedef
export let _LayerStyle;