Skip to content

Commit 15493d5

Browse files
finnurbrekiDevtools-frontend LUCI CQ
authored andcommitted
[AskAi]: Agentize the Font Display insight.
Example response: https://screenshot.googleplex.com/s26km5tacB9GNTk Fixed: 425274836 Change-Id: I9960c4b4d7b097df7576b2c0fffd6243316fb8f1 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6910796 Auto-Submit: Finnur Thorarinsson <finnur@chromium.org> Commit-Queue: Finnur Thorarinsson <finnur@chromium.org> Reviewed-by: Paul Irish <paulirish@chromium.org> Reviewed-by: Connor Clark <cjamcl@chromium.org>
1 parent 69aae5e commit 15493d5

6 files changed

Lines changed: 118 additions & 2 deletions

File tree

front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,51 @@ Line: 0, Column: 390688, Name: Object.values
664664
- https://philipwalton.com/articles/the-state-of-es5-on-the-web/
665665
=== end content
666666

667+
Title: PerformanceInsightFormatter FontDisplay serializes correctly when there are no results
668+
Content:
669+
## Insight Title: Font display
670+
671+
## Insight Summary:
672+
This insight identifies font issues when a webpage uses custom fonts, for example when font-display is not set to `swap`, `fallback` or `optional`, causing the "Flash of Invisible Text" problem (FOIT).
673+
674+
## Detailed analysis:
675+
No font display issues were detected.
676+
677+
## Estimated savings: FCP -Infinity ms
678+
679+
## External resources:
680+
- https://web.dev/articles/preload-optional-fonts
681+
- https://fonts.google.com/knowledge/glossary/foit
682+
- https://developer.chrome.com/blog/font-fallbacks
683+
=== end content
684+
685+
Title: PerformanceInsightFormatter FontDisplay serializes the correct details when problems are found with font display
686+
Content:
687+
## Insight Title: Font display
688+
689+
## Insight Summary:
690+
This insight identifies font issues when a webpage uses custom fonts, for example when font-display is not set to `swap`, `fallback` or `optional`, causing the "Flash of Invisible Text" problem (FOIT).
691+
692+
## Detailed analysis:
693+
The following font display issues were found:
694+
695+
- Font name: jizaRExUiTo99u79D0KExcOPIDU.woff2, URL: https://fonts.gstatic.com/s/ptsans/v17/jizaRExUiTo99u79D0KExcOPIDU.woff2, Property 'font-display' set to: 'auto', Wasted time: 20.0 ms.
696+
- Font name: SlGVmQWMvZQIdix7AFxXkHNSbRYXags.woff2, URL: https://fonts.gstatic.com/s/droidsans/v18/SlGVmQWMvZQIdix7AFxXkHNSbRYXags.woff2, Property 'font-display' set to: 'auto', Wasted time: 15.0 ms.
697+
- Font name: jizfRExUiTo99u79B_mh0O6tLR8a8zI.woff2, URL: https://fonts.gstatic.com/s/ptsans/v17/jizfRExUiTo99u79B_mh0O6tLR8a8zI.woff2, Property 'font-display' set to: 'auto', Wasted time: 15.0 ms.
698+
- Font name: SlGWmQWMvZQIdix7AFxXmMh3eDs1ZyHKpWg.woff2, URL: https://fonts.gstatic.com/s/droidsans/v18/SlGWmQWMvZQIdix7AFxXmMh3eDs1ZyHKpWg.woff2, Property 'font-display' set to: 'auto', Wasted time: 15.0 ms.
699+
- Font name: EJRVQgYoZZY2vCFuvAFWzr-_dSb_.woff2, URL: https://fonts.gstatic.com/s/ptserif/v18/EJRVQgYoZZY2vCFuvAFWzr-_dSb_.woff2, Property 'font-display' set to: 'auto', Wasted time: 15.0 ms.
700+
- Font name: S6u9w4BMUTPHh6UVSwiPGQ3q5d0.woff2, URL: https://fonts.gstatic.com/s/lato/v24/S6u9w4BMUTPHh6UVSwiPGQ3q5d0.woff2, Property 'font-display' set to: 'auto', Wasted time: 10.0 ms.
701+
702+
Consider setting [`font-display`](https://developer.chrome.com/blog/font-display) to `swap` or `optional` to ensure text is consistently visible. `swap` can be further optimized to mitigate layout shifts with [font metric overrides](https://developer.chrome.com/blog/font-fallbacks).
703+
704+
## Estimated savings: FCP 20 ms
705+
706+
## External resources:
707+
- https://web.dev/articles/preload-optional-fonts
708+
- https://fonts.google.com/knowledge/glossary/foit
709+
- https://developer.chrome.com/blog/font-fallbacks
710+
=== end content
711+
667712
Title: PerformanceInsightFormatter ImageDelivery serializes the correct details when there are no optimizable images
668713
Content:
669714
## Insight Title: Improve image delivery

front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,28 @@ describeWithEnvironment('PerformanceInsightFormatter', () => {
346346
});
347347
});
348348

349+
describe('FontDisplay', () => {
350+
it('serializes correctly when there are no results', async function() {
351+
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'simple-js-program.json.gz');
352+
assert.isOk(insights);
353+
const firstNav = getFirstOrError(parsedTrace.Meta.navigationsByNavigationId.values());
354+
const insight = getInsightOrError('FontDisplay', insights, firstNav);
355+
const formatter = new PerformanceInsightFormatter(PERF_AGENT_UNIT_FORMATTERS, parsedTrace, insight);
356+
const output = formatter.formatInsight();
357+
snapshotTester.assert(this, output);
358+
});
359+
360+
it('serializes the correct details when problems are found with font display', async function() {
361+
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'font-display.json.gz');
362+
assert.isOk(insights);
363+
const firstNav = getFirstOrError(parsedTrace.Meta.navigationsByNavigationId.values());
364+
const insight = getInsightOrError('FontDisplay', insights, firstNav);
365+
const formatter = new PerformanceInsightFormatter(PERF_AGENT_UNIT_FORMATTERS, parsedTrace, insight);
366+
const output = formatter.formatInsight();
367+
snapshotTester.assert(this, output);
368+
});
369+
});
370+
349371
describe('ImageDelivery', () => {
350372
it('serializes the correct details when there are no optimizable images', async function() {
351373
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'web-dev-with-commit.json.gz');

front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import * as Common from '../../../core/common/common.js';
56
import * as Trace from '../../trace/trace.js';
67
import type {ConversationSuggestion} from '../agents/AiAgent.js';
78

@@ -185,6 +186,35 @@ export class PerformanceInsightFormatter {
185186
}
186187
}
187188

189+
/**
190+
* Create an AI prompt string out of the NetworkDependencyTree Insight model to use with Ask AI.
191+
* Note: This function accesses the UIStrings within NetworkDependencyTree to help build the
192+
* AI prompt, but does not (and should not) call i18nString to localize these strings. They
193+
* should all be sent in English (at least for now).
194+
* @param insight The Network Dependency Tree Insight Model to query.
195+
* @returns a string formatted for sending to Ask AI.
196+
*/
197+
formatFontDisplayInsight(insight: Trace.Insights.Models.FontDisplay.FontDisplayInsightModel): string {
198+
if (insight.fonts.length === 0) {
199+
return 'No font display issues were detected.';
200+
}
201+
202+
let output = 'The following font display issues were found:\n';
203+
204+
for (const font of insight.fonts) {
205+
let fontName = font.name;
206+
if (!fontName) {
207+
const url = new Common.ParsedURL.ParsedURL(font.request.args.data.url);
208+
fontName = url.isValid ? url.lastPathComponent : '(not available)';
209+
}
210+
output += `\n - Font name: ${fontName}, URL: ${font.request.args.data.url}, Property 'font-display' set to: '${
211+
font.display}', Wasted time: ${this.#formatMilli(font.wastedTime)}.`;
212+
}
213+
214+
output += '\n\n' + Trace.Insights.Models.FontDisplay.UIStrings.description;
215+
return output;
216+
}
217+
188218
/**
189219
* Create an AI prompt string out of the Forced Reflow Insight model to use with Ask AI.
190220
* Note: This function accesses the UIStrings within ForcedReflow model to help build the
@@ -659,6 +689,10 @@ Legacy JavaScript by file:
659689
${filesFormatted}`;
660690
}
661691

692+
if (Trace.Insights.Models.FontDisplay.isFontDisplayInsight(this.#insight)) {
693+
return this.formatFontDisplayInsight(this.#insight);
694+
}
695+
662696
if (Trace.Insights.Models.ForcedReflow.isForcedReflowInsight(this.#insight)) {
663697
return this.formatForcedReflowInsight(this.#insight);
664698
}
@@ -701,7 +735,9 @@ ${filesFormatted}`;
701735
case 'DuplicatedJavaScript':
702736
return '';
703737
case 'FontDisplay':
704-
return '';
738+
return `- https://web.dev/articles/preload-optional-fonts
739+
- https://fonts.google.com/knowledge/glossary/foit
740+
- https://developer.chrome.com/blog/font-fallbacks`;
705741
case 'ForcedReflow':
706742
return '- https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing#avoid-forced-synchronous-layouts';
707743
case 'ImageDelivery':
@@ -757,7 +793,7 @@ ${filesFormatted}`;
757793
return `This insight identifies large, duplicated JavaScript modules that are present in your application and create redundant code.
758794
This wastes network bandwidth and slows down your page, as the user's browser must download and process the same code multiple times.`;
759795
case 'FontDisplay':
760-
return '';
796+
return 'This insight identifies font issues when a webpage uses custom fonts, for example when font-display is not set to `swap`, `fallback` or `optional`, causing the "Flash of Invisible Text" problem (FOIT).';
761797
case 'ForcedReflow':
762798
return `This insight identifies forced synchronous layouts (also known as forced reflows) and layout thrashing caused by JavaScript accessing layout properties at suboptimal points in time.`;
763799
case 'ImageDelivery':

front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,11 @@ Available insights:
891891
estimated wasted bytes: 97.8 kB
892892
example question: What should I do to improve and optimize the time taken to fetch and display images on the page?
893893
example question: Are all images on my site optimized?
894+
- insight name: FontDisplay
895+
description: Consider setting [font-display](https://developer.chrome.com/blog/font-display) to swap or optional to ensure text is consistently visible. swap can be further optimized to mitigate layout shifts with [font metric overrides](https://developer.chrome.com/blog/font-fallbacks).
896+
relevant trace bounds: {min: 157425661196, max: 157425696387}
897+
estimated metric savings: FCP 20 ms
898+
example question: How can I update my CSS to avoid layout shifts caused by incorrect `font-display` properties?
894899
- insight name: ThirdParties
895900
description: 3rd party code can significantly impact load performance. [Reduce and defer loading of 3rd party code](https://web.dev/articles/optimizing-content-efficiency-loading-third-party-javascript/) to prioritize your page's content.
896901
relevant trace bounds: {min: 157423742399, max: 157427277086}

front_end/models/trace/insights/FontDisplay.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ function finalize(partialModel: PartialInsightModel<FontDisplayInsightModel>): F
5656
};
5757
}
5858

59+
export function isFontDisplayInsight(model: InsightModel): model is FontDisplayInsightModel {
60+
return model.insightKey === InsightKeys.FONT_DISPLAY;
61+
}
62+
5963
export function generateInsight(
6064
parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): FontDisplayInsightModel {
6165
const fonts: RemoteFont[] = [];

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ export class FontDisplay extends BaseInsightComponent<FontDisplayInsightModel> {
2222
override internalName = 'font-display';
2323
#overlayForRequest = new Map<Trace.Types.Events.Event, Trace.Types.Overlays.Overlay>();
2424

25+
protected override hasAskAiSupport(): boolean {
26+
return true;
27+
}
28+
2529
protected override createOverlays(): Trace.Types.Overlays.Overlay[] {
2630
this.#overlayForRequest.clear();
2731

0 commit comments

Comments
 (0)