Skip to content

Commit d5d7b49

Browse files
finnurbrekiDevtools-frontend LUCI CQ
authored andcommitted
[AskAi]: Agentize the DOM Size insight.
Example response: https://screenshot.googleplex.com/3k2fR2xs97r8ocV Fixed: 425274130 Change-Id: I32a6727ee2bb90f3b9524207a8bf5fd38e6a0602 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6919169 Commit-Queue: Finnur Thorarinsson <[email protected]> Commit-Queue: Connor Clark <[email protected]> Reviewed-by: Connor Clark <[email protected]>
1 parent 56b5a71 commit d5d7b49

File tree

5 files changed

+147
-2
lines changed

5 files changed

+147
-2
lines changed

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,57 @@ Response headers
505505
- expires: Thu, 07 Mar 2024 21:17:02 GMT
506506
=== end content
507507

508+
Title: PerformanceInsightFormatter DomSize serializes correctly when there are no results
509+
Content:
510+
## Insight Title: Optimize DOM size
511+
512+
## Insight Summary:
513+
This insight evaluates some key metrics about the Document Object Model (DOM) and identifies excess in the DOM tree, for example:
514+
- The maximum number of elements within the DOM.
515+
- The maximum number of children for any given element.
516+
- Excessive depth of the DOM structure.
517+
- The largest layout and style recalculation events.
518+
519+
## Detailed analysis:
520+
No DOM size issues were detected.
521+
522+
## Estimated savings: none
523+
524+
## External resources:
525+
- https://developer.chrome.com/docs/lighthouse/performance/dom-size/
526+
=== end content
527+
528+
Title: PerformanceInsightFormatter DomSize serializes the correct details showing DOM issues
529+
Content:
530+
## Insight Title: Optimize DOM size
531+
532+
## Insight Summary:
533+
This insight evaluates some key metrics about the Document Object Model (DOM) and identifies excess in the DOM tree, for example:
534+
- The maximum number of elements within the DOM.
535+
- The maximum number of children for any given element.
536+
- Excessive depth of the DOM structure.
537+
- The largest layout and style recalculation events.
538+
539+
## Detailed analysis:
540+
A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).
541+
542+
Statistic:
543+
544+
Total elements: 1006.
545+
DOM depth: 1001 nodes, starting with element 'SPAN' (node id: 27).
546+
Most children: 5, for parent 'BODY' (node id: 19).
547+
548+
Large layout updates/style calculations:
549+
550+
- Layout update: Duration: 58.9 ms, with 2004 of 2012 nodes needing layout.
551+
- Style recalculation: Duration: 150.5 ms, with 1001 elements affected.
552+
553+
## Estimated savings: none
554+
555+
## External resources:
556+
- https://developer.chrome.com/docs/lighthouse/performance/dom-size/
557+
=== end content
558+
508559
Title: PerformanceInsightFormatter Duplicated javascript serializes the correct details
509560
Content:
510561
## Insight Title: Duplicated JavaScript

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,28 @@ describeWithEnvironment('PerformanceInsightFormatter', () => {
299299
});
300300
});
301301

302+
describe('DomSize', () => {
303+
it('serializes correctly when there are no results', async function() {
304+
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'simple-js-program.json.gz');
305+
assert.isOk(insights);
306+
const firstNav = getFirstOrError(parsedTrace.Meta.navigationsByNavigationId.values());
307+
const insight = getInsightOrError('DOMSize', insights, firstNav);
308+
const formatter = new PerformanceInsightFormatter(parsedTrace, insight);
309+
const output = formatter.formatInsight();
310+
snapshotTester.assert(this, output);
311+
});
312+
313+
it('serializes the correct details showing DOM issues', async function() {
314+
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'dom-size.json.gz');
315+
assert.isOk(insights);
316+
const firstNav = getFirstOrError(parsedTrace.Meta.navigationsByNavigationId.values());
317+
const insight = getInsightOrError('DOMSize', insights, firstNav);
318+
const formatter = new PerformanceInsightFormatter(parsedTrace, insight);
319+
const output = formatter.formatInsight();
320+
snapshotTester.assert(this, output);
321+
});
322+
});
323+
302324
describe('Duplicated javascript', () => {
303325
it('serializes the correct details', async function() {
304326
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'dupe-js.json.gz');

front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,62 @@ export class PerformanceInsightFormatter {
181181
}
182182
}
183183

184+
/**
185+
* Create an AI prompt string out of the DOM Size model to use with Ask AI.
186+
* Note: This function accesses the UIStrings within DomSize to help build the
187+
* AI prompt, but does not (and should not) call i18nString to localize these strings. They
188+
* should all be sent in English (at least for now).
189+
* @param insight The DOM Size Insight Model to query.
190+
* @returns a string formatted for sending to Ask AI.
191+
*/
192+
formatDomSizeInsight(insight: Trace.Insights.Models.DOMSize.DOMSizeInsightModel): string {
193+
if (insight.state === 'pass') {
194+
return 'No DOM size issues were detected.';
195+
}
196+
197+
let output = Trace.Insights.Models.DOMSize.UIStrings.description + '\n';
198+
199+
if (insight.maxDOMStats) {
200+
output += '\n' + Trace.Insights.Models.DOMSize.UIStrings.statistic + ':\n\n';
201+
202+
const maxDepthStats = insight.maxDOMStats.args.data.maxDepth;
203+
const maxChildrenStats = insight.maxDOMStats.args.data.maxChildren;
204+
output += Trace.Insights.Models.DOMSize.UIStrings.totalElements + ': ' +
205+
insight.maxDOMStats.args.data.totalElements + '.\n';
206+
if (maxDepthStats) {
207+
output += Trace.Insights.Models.DOMSize.UIStrings.maxDOMDepth + ': ' + maxDepthStats.depth +
208+
` nodes, starting with element '${maxDepthStats.nodeName}'` +
209+
' (node id: ' + maxDepthStats.nodeId + ').\n';
210+
}
211+
if (maxChildrenStats) {
212+
output += Trace.Insights.Models.DOMSize.UIStrings.maxChildren + ': ' + maxChildrenStats.numChildren +
213+
`, for parent '${maxChildrenStats.nodeName}'` +
214+
' (node id: ' + maxChildrenStats.nodeId + ').\n';
215+
}
216+
}
217+
218+
if (insight.largeLayoutUpdates.length > 0 || insight.largeStyleRecalcs.length > 0) {
219+
output += `\nLarge layout updates/style calculations:\n`;
220+
}
221+
222+
if (insight.largeLayoutUpdates.length > 0) {
223+
for (const update of insight.largeLayoutUpdates) {
224+
output += `\n - Layout update: Duration: ${this.#formatMicro(update.dur)},`;
225+
output += ` with ${update.args.beginData.dirtyObjects} of ${
226+
update.args.beginData.totalObjects} nodes needing layout.`;
227+
}
228+
}
229+
230+
if (insight.largeStyleRecalcs.length > 0) {
231+
for (const recalc of insight.largeStyleRecalcs) {
232+
output += `\n - Style recalculation: Duration: ${this.#formatMicro(recalc.dur)}, `;
233+
output += `with ${recalc.args.elementCount} elements affected.`;
234+
}
235+
}
236+
237+
return output;
238+
}
239+
184240
/**
185241
* Create an AI prompt string out of the NetworkDependencyTree Insight model to use with Ask AI.
186242
* Note: This function accesses the UIStrings within NetworkDependencyTree to help build the
@@ -682,6 +738,10 @@ Legacy JavaScript by file:
682738
${filesFormatted}`;
683739
}
684740

741+
if (Trace.Insights.Models.DOMSize.isDomSizeInsight(this.#insight)) {
742+
return this.formatDomSizeInsight(this.#insight);
743+
}
744+
685745
if (Trace.Insights.Models.FontDisplay.isFontDisplayInsight(this.#insight)) {
686746
return this.formatFontDisplayInsight(this.#insight);
687747
}
@@ -724,7 +784,7 @@ ${filesFormatted}`;
724784
case 'DocumentLatency':
725785
return '- https://web.dev/articles/optimize-ttfb';
726786
case 'DOMSize':
727-
return '';
787+
return '- https://developer.chrome.com/docs/lighthouse/performance/dom-size/';
728788
case 'DuplicatedJavaScript':
729789
return '';
730790
case 'FontDisplay':
@@ -781,7 +841,11 @@ ${filesFormatted}`;
781841
2. Did the server respond in 600ms or less? We want developers to aim for as close to 100ms as possible, but our threshold for this insight is 600ms.
782842
3. Was there compression applied to the response to minimize the transfer size?`;
783843
case 'DOMSize':
784-
return '';
844+
return `This insight evaluates some key metrics about the Document Object Model (DOM) and identifies excess in the DOM tree, for example:
845+
- The maximum number of elements within the DOM.
846+
- The maximum number of children for any given element.
847+
- Excessive depth of the DOM structure.
848+
- The largest layout and style recalculation events.`;
785849
case 'DuplicatedJavaScript':
786850
return `This insight identifies large, duplicated JavaScript modules that are present in your application and create redundant code.
787851
This wastes network bandwidth and slows down your page, as the user's browser must download and process the same code multiple times.`;

front_end/models/trace/insights/DOMSize.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ function finalize(partialModel: PartialInsightModel<DOMSizeInsightModel>): DOMSi
104104
};
105105
}
106106

107+
export function isDomSizeInsight(model: InsightModel): model is DOMSizeInsightModel {
108+
return model.insightKey === InsightKeys.DOM_SIZE;
109+
}
110+
107111
export function generateInsight(
108112
parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): DOMSizeInsightModel {
109113
const isWithinContext = (event: Types.Events.Event): boolean => Helpers.Timing.eventIsInBounds(event, context.bounds);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export class DOMSize extends BaseInsightComponent<DOMSizeInsightModel> {
2525
static override readonly litTagName = Lit.StaticHtml.literal`devtools-performance-dom-size`;
2626
override internalName = 'dom-size';
2727

28+
protected override hasAskAiSupport(): boolean {
29+
return true;
30+
}
31+
2832
#renderNodeTable(domStatsData: Trace.Types.Events.DOMStats['args']['data']): Lit.LitTemplate {
2933
const rows: TableData['rows'] = [];
3034

0 commit comments

Comments
 (0)