@quick-game/cli
Version:
Command line interface for rapid qg development
198 lines • 9.14 kB
JavaScript
// Copyright 2023 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 i18n from '../../core/i18n/i18n.js';
import * as TraceEngine from '../../models/trace/trace.js';
import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
import { buildGroupStyle, buildTrackHeader, getFormattedTime } from './AppenderUtils.js';
import { TimelineUIUtils } from './TimelineUIUtils.js';
import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
import { InstantEventVisibleDurationMs } from './TimelineFlameChartDataProvider.js';
const UIStrings = {
/**
*@description Text in Timeline Flame Chart Data Provider of the Performance panel
*/
network: 'Network',
};
const str_ = i18n.i18n.registerUIStrings('panels/timeline/NetworkTrackAppender.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class NetworkTrackAppender {
appenderName = 'Network';
#traceParsedData;
#flameChartData;
#font;
#group;
constructor(traceParsedData, flameChartData) {
this.#traceParsedData = traceParsedData;
this.#flameChartData = flameChartData;
this.#font = `${PerfUI.Font.DEFAULT_FONT_SIZE} ${PerfUI.Font.getFontFamilyForCanvas()}`;
ThemeSupport.ThemeSupport.instance().addEventListener(ThemeSupport.ThemeChangeEvent.eventName, () => {
if (this.#group) {
// We only need to update the color here, because FlameChart will call `scheduleUpdate()` when theme is changed.
this.#group.style.color = ThemeSupport.ThemeSupport.instance().getComputedValue('--color-text-primary');
this.#group.style.backgroundColor = ThemeSupport.ThemeSupport.instance().getComputedValue('--color-background');
}
});
}
group() {
return this.#group;
}
font() {
return this.#font;
}
/**
* Appends into the flame chart data the data corresponding to the
* Network track.
* @param trackStartLevel the horizontal level of the flame chart events where
* the track's events will start being appended.
* @param expanded wether the track should be rendered expanded.
* @returns the first available level to append more data after having
* appended the track's events.
*/
appendTrackAtLevel(trackStartLevel, expanded) {
const networkEvents = this.#traceParsedData.NetworkRequests.byTime;
if (networkEvents.length === 0) {
return trackStartLevel;
}
this.#appendTrackHeaderAtLevel(trackStartLevel, expanded);
return this.#appendEventsAtLevel(networkEvents, trackStartLevel);
}
/**
* Adds into the flame chart data the header corresponding to the
* Network track. A header is added in the shape of a group in the
* flame chart data. A group has a predefined style and a reference
* to the definition of the legacy track (which should be removed
* in the future).
* @param currentLevel the flame chart level at which the header is
* appended.
* @param expanded wether the track should be rendered expanded.
*/
#appendTrackHeaderAtLevel(currentLevel, expanded) {
const style = buildGroupStyle({
font: this.#flameChartData,
shareHeaderLine: false,
useFirstLineForOverview: false,
useDecoratorsForOverview: true,
});
this.#group = buildTrackHeader(0, i18nString(UIStrings.network), style, /* selectable= */ true, expanded);
this.#flameChartData.groups.push(this.#group);
}
/**
* Adds into the flame chart data a list of trace events.
* @param events the trace events that will be appended to the flame chart.
* The events should be taken straight from the trace handlers. The handlers
* should sort the events by start time, and the parent event is before the
* child.
* @param trackStartLevel the flame chart level from which the events will
* be appended.
* @param appender the track that the trace events belong to.
* @returns the next level after the last occupied by the appended these
* trace events (the first available level to append next track).
*/
#appendEventsAtLevel(events, trackStartLevel) {
if (events.length === 0) {
return trackStartLevel;
}
const lastTimeByLevel = [];
let maxLevel = 0;
for (let i = 0; i < events.length; ++i) {
const event = events[i];
const startTime = event.ts;
const endTime = event.ts + (event.dur || 0);
while (lastTimeByLevel.length && lastTimeByLevel[lastTimeByLevel.length - 1] <= startTime) {
lastTimeByLevel.pop();
}
const level = lastTimeByLevel.length;
this.#appendEventAtLevel(event, trackStartLevel + level);
lastTimeByLevel.push(endTime);
maxLevel = Math.max(maxLevel, lastTimeByLevel.length);
}
return trackStartLevel + maxLevel;
}
/**
* Adds an event to the flame chart data at a defined level.
* @param event the event to be appended,
* @param level the level to append the event,
* @param appender the track which the event belongs to.
* @returns the index of the event in all events to be rendered in the flamechart.
*/
#appendEventAtLevel(event, level) {
const index = this.#flameChartData.entryLevels.length;
this.#flameChartData.entryLevels[index] = level;
this.#flameChartData.entryStartTimes[index] = TraceEngine.Helpers.Timing.microSecondsToMilliseconds(event.ts);
const msDuration = event.dur ||
TraceEngine.Helpers.Timing.millisecondsToMicroseconds(InstantEventVisibleDurationMs);
this.#flameChartData.entryTotalTimes[index] = TraceEngine.Helpers.Timing.microSecondsToMilliseconds(msDuration);
return level;
}
/**
* Update the flame chart data.
* When users zoom in the flamechart, we only want to show them the network
* requests between startTime and endTime. This function will append those
* invisible events to the last level, and hide them.
* @returns the number of levels used by this track
*/
filterTimelineDataBetweenTimes(startTime, endTime) {
const events = this.#traceParsedData.NetworkRequests.byTime;
if (!this.#flameChartData || events.length === 0) {
return 0;
}
const lastTimeByLevel = [];
let maxLevel = 0;
for (let i = 0; i < events.length; ++i) {
const event = events[i];
const beginTime = TraceEngine.Helpers.Timing.microSecondsToMilliseconds(event.ts);
const eventEndTime = TraceEngine.Helpers.Timing.microSecondsToMilliseconds((event.ts + event.dur));
const visible = beginTime < endTime && eventEndTime > startTime;
if (!visible) {
this.#flameChartData.entryLevels[i] = -1;
continue;
}
while (lastTimeByLevel.length && lastTimeByLevel[lastTimeByLevel.length - 1] <= beginTime) {
lastTimeByLevel.pop();
}
this.#flameChartData.entryLevels[i] = lastTimeByLevel.length;
lastTimeByLevel.push(eventEndTime);
maxLevel = Math.max(maxLevel, lastTimeByLevel.length);
}
for (let i = 0; i < events.length; ++i) {
// -1 means this event is invisible.
if (this.#flameChartData.entryLevels[i] === -1) {
// The maxLevel is an invisible level.
this.#flameChartData.entryLevels[i] = maxLevel;
}
}
return maxLevel;
}
/*
------------------------------------------------------------------------------------
The following methods are invoked by the flame chart renderer to query features about
events on rendering.
------------------------------------------------------------------------------------
*/
/**
* Gets the color an event added by this appender should be rendered with.
*/
colorForEvent(event) {
if (!TraceEngine.Types.TraceEvents.isSyntheticNetworkRequestDetailsEvent(event)) {
throw new Error(`Unexpected Network Request: The event's type is '${event.name}'`);
}
const category = TimelineUIUtils.syntheticNetworkRequestCategory(event);
return TimelineUIUtils.networkCategoryColor(category);
}
/**
* Gets the title an event added by this appender should be rendered with.
*/
titleForEvent(event) {
return event.name;
}
/**
* Returns the info shown when an event added by this appender
* is hovered in the timeline.
*/
highlightedEntryInfo(event) {
const title = this.titleForEvent(event);
return { title, formattedTime: getFormattedTime(event.dur) };
}
}
//# sourceMappingURL=NetworkTrackAppender.js.map