[go: nahoru, domu]

blob: 38b1e7e336deb85ab7444d318c7526ebfcd1002f [file] [log] [blame]
// 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 TraceEngine from '../../models/trace/trace.js';
import type * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
import {
type CompatibilityTracksAppender,
type TrackAppender,
type HighlightedEntryInfo,
type TrackAppenderName,
} from './CompatibilityTracksAppender.js';
import * as i18n from '../../core/i18n/i18n.js';
import {buildGroupStyle, buildTrackHeader, getFormattedTime} from './AppenderUtils.js';
const UIStrings = {
/**
*@description Text in Timeline Flame Chart Data Provider of the Performance panel
*/
layoutShifts: 'Layout Shifts',
};
const str_ = i18n.i18n.registerUIStrings('panels/timeline/LayoutShiftsTrackAppender.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class LayoutShiftsTrackAppender implements TrackAppender {
readonly appenderName: TrackAppenderName = 'LayoutShifts';
#compatibilityBuilder: CompatibilityTracksAppender;
#flameChartData: PerfUI.FlameChart.FlameChartTimelineData;
#traceParsedData: Readonly<TraceEngine.TraceModel.PartialTraceParseDataDuringMigration>;
constructor(
compatibilityBuilder: CompatibilityTracksAppender, flameChartData: PerfUI.FlameChart.FlameChartTimelineData,
traceParsedData: TraceEngine.TraceModel.PartialTraceParseDataDuringMigration) {
this.#compatibilityBuilder = compatibilityBuilder;
this.#flameChartData = flameChartData;
this.#traceParsedData = traceParsedData;
}
/**
* Appends into the flame chart data the data corresponding to the
* layout shifts 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: number, expanded?: boolean): number {
if (this.#traceParsedData.LayoutShifts.clusters.length === 0) {
return trackStartLevel;
}
this.#appendTrackHeaderAtLevel(trackStartLevel, expanded);
return this.#appendLayoutShiftsAtLevel(trackStartLevel);
}
/**
* Adds into the flame chart data the header corresponding to the
* layout shifts 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.
*/
#appendTrackHeaderAtLevel(currentLevel: number, expanded?: boolean): void {
const style = buildGroupStyle({collapsible: false});
const group = buildTrackHeader(
currentLevel, i18nString(UIStrings.layoutShifts), style,
/* selectable= */ true, expanded);
this.#compatibilityBuilder.registerTrackForGroup(group, this);
}
/**
* Adds into the flame chart data all the layout shifts. These are taken from
* the clusters that are collected in the LayoutShiftsHandler.
* @param currentLevel the flame chart level from which layout shifts will
* be appended.
* @returns the next level after the last occupied by the appended
* layout shifts (the first available level to append more data).
*/
#appendLayoutShiftsAtLevel(currentLevel: number): number {
const allLayoutShifts = this.#traceParsedData.LayoutShifts.clusters.flatMap(cluster => cluster.events);
const newLevel = this.#compatibilityBuilder.appendEventsAtLevel(allLayoutShifts, currentLevel, this);
// Bit of a hack: LayoutShifts are instant events, so have no duration. But
// OPP doesn't do well at making tiny events easy to spot and click. So we
// set it to a small duration so that the user is able to see and click
// them more easily. Long term we will explore a better UI solution to
// allow us to do this properly and not hack around it.
const msDuration = TraceEngine.Types.Timing.MicroSeconds(5_000);
for (let i = 0; i < allLayoutShifts.length; ++i) {
const index = this.#compatibilityBuilder.indexForEvent(allLayoutShifts[i]);
if (index === undefined) {
continue;
}
this.#flameChartData.entryTotalTimes[index] = TraceEngine.Helpers.Timing.microSecondsToMilliseconds(msDuration);
}
return newLevel;
}
/*
------------------------------------------------------------------------------------
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: TraceEngine.Types.TraceEvents.TraceEventData): string {
return 'rgb(155 127 230)';
}
/**
* Gets the title an event added by this appender should be rendered with.
*/
titleForEvent(event: TraceEngine.Types.TraceEvents.TraceEventData): string {
if (TraceEngine.Types.TraceEvents.isTraceEventLayoutShift(event)) {
return 'Layout shift';
}
return event.name;
}
/**
* Returns the info shown when an event added by this appender
* is hovered in the timeline.
*/
highlightedEntryInfo(event: TraceEngine.Types.TraceEvents.TraceEventLayoutShift): HighlightedEntryInfo {
const title = this.titleForEvent(event);
return {title, formattedTime: getFormattedTime(event.dur)};
}
}