Skip to content

Commit 8e114c5

Browse files
Adam RaineDevtools-frontend LUCI CQ
authored andcommitted
[LCPPhases] Add field data LCP phase table
https://screenshot.googleplex.com/58gKxW2XrnsBG6D Bug: 378465601 Change-Id: I69a6f89843f61ca31cee2f0a5859dc44ae153ecf Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6300608 Reviewed-by: Connor Clark <[email protected]> Commit-Queue: Adam Raine <[email protected]>
1 parent 3710d25 commit 8e114c5

File tree

7 files changed

+113
-7
lines changed

7 files changed

+113
-7
lines changed

front_end/models/crux-manager/CrUXManager.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ describeWithMockConnection('CrUXManager', () => {
156156
'interaction_to_next_paint',
157157
'round_trip_time',
158158
'form_factors',
159+
'largest_contentful_paint_image_time_to_first_byte',
160+
'largest_contentful_paint_image_resource_load_delay',
161+
'largest_contentful_paint_image_resource_load_duration',
162+
'largest_contentful_paint_image_element_render_delay',
159163
],
160164
origin: 'https://example.com',
161165
},
@@ -168,6 +172,10 @@ describeWithMockConnection('CrUXManager', () => {
168172
'interaction_to_next_paint',
169173
'round_trip_time',
170174
'form_factors',
175+
'largest_contentful_paint_image_time_to_first_byte',
176+
'largest_contentful_paint_image_resource_load_delay',
177+
'largest_contentful_paint_image_resource_load_duration',
178+
'largest_contentful_paint_image_element_render_delay',
171179
],
172180
origin: 'https://example.com',
173181
},
@@ -179,6 +187,10 @@ describeWithMockConnection('CrUXManager', () => {
179187
'interaction_to_next_paint',
180188
'round_trip_time',
181189
'form_factors',
190+
'largest_contentful_paint_image_time_to_first_byte',
191+
'largest_contentful_paint_image_resource_load_delay',
192+
'largest_contentful_paint_image_resource_load_duration',
193+
'largest_contentful_paint_image_element_render_delay',
182194
],
183195
origin: 'https://example.com',
184196
},
@@ -191,6 +203,10 @@ describeWithMockConnection('CrUXManager', () => {
191203
'interaction_to_next_paint',
192204
'round_trip_time',
193205
'form_factors',
206+
'largest_contentful_paint_image_time_to_first_byte',
207+
'largest_contentful_paint_image_resource_load_delay',
208+
'largest_contentful_paint_image_resource_load_duration',
209+
'largest_contentful_paint_image_element_render_delay',
194210
],
195211
url: 'https://example.com/',
196212
},
@@ -203,6 +219,10 @@ describeWithMockConnection('CrUXManager', () => {
203219
'interaction_to_next_paint',
204220
'round_trip_time',
205221
'form_factors',
222+
'largest_contentful_paint_image_time_to_first_byte',
223+
'largest_contentful_paint_image_resource_load_delay',
224+
'largest_contentful_paint_image_resource_load_duration',
225+
'largest_contentful_paint_image_element_render_delay',
206226
],
207227
url: 'https://example.com/',
208228
},
@@ -214,6 +234,10 @@ describeWithMockConnection('CrUXManager', () => {
214234
'interaction_to_next_paint',
215235
'round_trip_time',
216236
'form_factors',
237+
'largest_contentful_paint_image_time_to_first_byte',
238+
'largest_contentful_paint_image_resource_load_delay',
239+
'largest_contentful_paint_image_resource_load_duration',
240+
'largest_contentful_paint_image_element_render_delay',
217241
],
218242
url: 'https://example.com/',
219243
},

front_end/models/crux-manager/CrUXManager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ const CRUX_API_KEY = 'AIzaSyCCSOx25vrb5z0tbedCB3_JRzzbVW6Uwgw';
2424
const DEFAULT_ENDPOINT = `https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=${CRUX_API_KEY}`;
2525

2626
export type StandardMetricNames = 'cumulative_layout_shift'|'first_contentful_paint'|'first_input_delay'|
27-
'interaction_to_next_paint'|'largest_contentful_paint'|'experimental_time_to_first_byte'|'round_trip_time';
27+
'interaction_to_next_paint'|'largest_contentful_paint'|'experimental_time_to_first_byte'|'round_trip_time'|
28+
'largest_contentful_paint_image_time_to_first_byte'|'largest_contentful_paint_image_resource_load_delay'|
29+
'largest_contentful_paint_image_resource_load_duration'|'largest_contentful_paint_image_element_render_delay';
2830
export type MetricNames = StandardMetricNames|'form_factors';
2931
export type FormFactor = 'DESKTOP'|'PHONE'|'TABLET';
3032
export type DeviceScope = FormFactor|'ALL';
@@ -114,6 +116,10 @@ const metrics: MetricNames[] = [
114116
'interaction_to_next_paint',
115117
'round_trip_time',
116118
'form_factors',
119+
'largest_contentful_paint_image_time_to_first_byte',
120+
'largest_contentful_paint_image_resource_load_delay',
121+
'largest_contentful_paint_image_resource_load_duration',
122+
'largest_contentful_paint_image_element_render_delay',
117123
];
118124

119125
export class CrUXManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {

front_end/models/trace/insights/Common.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ export interface CrUXFieldMetricResults {
107107
lcp: CrUXFieldMetricTimingResult|null;
108108
inp: CrUXFieldMetricTimingResult|null;
109109
cls: CrUXFieldMetricNumberResult|null;
110+
lcpPhases: {
111+
ttfb: CrUXFieldMetricTimingResult|null,
112+
loadDelay: CrUXFieldMetricTimingResult|null,
113+
loadDuration: CrUXFieldMetricTimingResult|null,
114+
renderDelay: CrUXFieldMetricTimingResult|null,
115+
};
110116
}
111117

112118
function getPageResult(
@@ -174,6 +180,12 @@ export function getFieldMetricsForInsightSet(
174180
lcp: getMetricTimingResult(pageResult, 'largest_contentful_paint', scope),
175181
inp: getMetricTimingResult(pageResult, 'interaction_to_next_paint', scope),
176182
cls: getMetricResult(pageResult, 'cumulative_layout_shift', scope),
183+
lcpPhases: {
184+
ttfb: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_time_to_first_byte', scope),
185+
loadDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_delay', scope),
186+
loadDuration: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_resource_load_duration', scope),
187+
renderDelay: getMetricTimingResult(pageResult, 'largest_contentful_paint_image_element_render_delay', scope),
188+
}
177189
};
178190
}
179191

front_end/models/trace/insights/LCPPhases.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export const UIStrings = {
5252
* @description Label used for the duration a single phase/component/stage/section takes up of a larger duration.
5353
*/
5454
duration: 'Duration',
55+
/**
56+
* @description Label used for the duration a single phase/component/stage/section takes up of a larger duration. The value will be the 75th percentile of aggregate data. "Field" means that the data was collected from real users in the field as opposed to the developers local environment. "Field" is synonymous with "Real user data".
57+
*/
58+
fieldDuration: 'Field 75th percentile',
5559
/**
5660
* @description Text status indicating that the the Largest Contentful Paint (LCP) metric timing was not found. "LCP" is an acronym and should not be translated.
5761
*/

front_end/panels/timeline/components/SidebarSingleInsightSet.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ export class SidebarSingleInsightSet extends HTMLElement {
214214
return {lcp, cls, inp};
215215
}
216216

217-
#getFieldMetrics(insightSetKey: string): Omit<Trace.Insights.Common.CrUXFieldMetricResults, 'fcp'>|null {
217+
#getFieldMetrics(insightSetKey: string): Trace.Insights.Common.CrUXFieldMetricResults|null {
218218
const insightSet = this.#data.insights?.get(insightSetKey);
219219
if (!insightSet) {
220220
return null;
@@ -380,14 +380,17 @@ export class SidebarSingleInsightSet extends HTMLElement {
380380
continue;
381381
}
382382

383+
const fieldMetrics = this.#getFieldMetrics(insightSetKey);
384+
383385
// clang-format off
384386
const component = html`<div>
385387
<${componentClass.litTagName}
386388
.selected=${this.#data.activeInsight?.model === model}
387389
.model=${model}
388390
.bounds=${insightSet.bounds}
389391
.insightSetKey=${insightSetKey}
390-
.parsedTrace=${this.#data.parsedTrace}>
392+
.parsedTrace=${this.#data.parsedTrace}
393+
.fieldMetrics=${fieldMetrics}>
391394
</${componentClass.litTagName}>
392395
</div>`;
393396
// clang-format on

front_end/panels/timeline/components/insights/BaseInsightComponent.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export abstract class BaseInsightComponent<T extends InsightModel<{}, {}>> exten
6969
#selected = false;
7070
#model: T|null = null;
7171
#parsedTrace: Trace.Handlers.Types.ParsedTrace|null = null;
72+
#fieldMetrics: Trace.Insights.Common.CrUXFieldMetricResults|null = null;
7273

7374
#insightsAskAiEnabled = false;
7475

@@ -140,6 +141,14 @@ export abstract class BaseInsightComponent<T extends InsightModel<{}, {}>> exten
140141
this.#parsedTrace = parsedTrace;
141142
}
142143

144+
set fieldMetrics(fieldMetrics: Trace.Insights.Common.CrUXFieldMetricResults) {
145+
this.#fieldMetrics = fieldMetrics;
146+
}
147+
148+
get fieldMetrics(): Trace.Insights.Common.CrUXFieldMetricResults|null {
149+
return this.#fieldMetrics;
150+
}
151+
143152
#dispatchInsightToggle(): void {
144153
if (this.#selected) {
145154
this.dispatchEvent(new SidebarInsight.InsightDeactivated());

front_end/panels/timeline/components/insights/LCPPhases.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,46 @@ export class LCPPhases extends BaseInsightComponent<LCPPhasesInsightModel> {
150150
return overlays;
151151
}
152152

153+
#renderFieldPhases(): Lit.LitTemplate|null {
154+
if (!this.fieldMetrics) {
155+
return null;
156+
}
157+
158+
const {ttfb, loadDelay, loadDuration, renderDelay} = this.fieldMetrics.lcpPhases;
159+
if (!ttfb || !loadDelay || !loadDuration || !renderDelay) {
160+
return null;
161+
}
162+
163+
const ttfbMillis = i18n.TimeUtilities.preciseMillisToString(Trace.Helpers.Timing.microToMilli(ttfb.value));
164+
const loadDelayMillis =
165+
i18n.TimeUtilities.preciseMillisToString(Trace.Helpers.Timing.microToMilli(loadDelay.value));
166+
const loadDurationMillis =
167+
i18n.TimeUtilities.preciseMillisToString(Trace.Helpers.Timing.microToMilli(loadDuration.value));
168+
const renderDelayMillis =
169+
i18n.TimeUtilities.preciseMillisToString(Trace.Helpers.Timing.microToMilli(renderDelay.value));
170+
171+
const rows = [
172+
{values: [i18nString(UIStrings.timeToFirstByte), ttfbMillis]},
173+
{values: [i18nString(UIStrings.resourceLoadDelay), loadDelayMillis]},
174+
{values: [i18nString(UIStrings.resourceLoadDuration), loadDurationMillis]},
175+
{values: [i18nString(UIStrings.elementRenderDelay), renderDelayMillis]},
176+
];
177+
178+
// clang-format off
179+
return html`
180+
<div class="insight-section">
181+
<devtools-performance-table
182+
.data=${{
183+
insight: this,
184+
headers: [i18nString(UIStrings.phase), i18nString(UIStrings.fieldDuration)],
185+
rows,
186+
} as TableData}>
187+
</devtools-performance-table>
188+
</div>
189+
`;
190+
// clang-format on
191+
}
192+
153193
override renderContent(): Lit.LitTemplate {
154194
if (!this.model) {
155195
return Lit.nothing;
@@ -172,17 +212,25 @@ export class LCPPhases extends BaseInsightComponent<LCPPhasesInsightModel> {
172212
});
173213

174214
// clang-format off
175-
return html`
215+
const sections: Lit.LitTemplate[] = [html`
176216
<div class="insight-section">
177-
${html`<devtools-performance-table
217+
<devtools-performance-table
178218
.data=${{
179219
insight: this,
180220
headers: [i18nString(UIStrings.phase), i18nString(UIStrings.duration)],
181221
rows,
182222
} as TableData}>
183-
</devtools-performance-table>`}
184-
</div>`;
223+
</devtools-performance-table>
224+
</div>`
225+
];
185226
// clang-format on
227+
228+
const fieldDataSection = this.#renderFieldPhases();
229+
if (fieldDataSection) {
230+
sections.push(fieldDataSection);
231+
}
232+
233+
return html`${sections}`;
186234
}
187235
}
188236

0 commit comments

Comments
 (0)