Skip to content

Commit c029e35

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
[RPP] Add Legacy JavaScript insight model
Bug: 394373852 Change-Id: I97b0ee1af0b025dddfc691a11137c84f6c28c135 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6405705 Commit-Queue: Paul Irish <[email protected]> Auto-Submit: Connor Clark <[email protected]> Reviewed-by: Paul Irish <[email protected]>
1 parent b12601e commit c029e35

20 files changed

+1390
-0
lines changed

config/gni/devtools_grd_files.gni

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ grd_files_release_sources = [
731731
"front_end/third_party/i18n/i18n.js",
732732
"front_end/third_party/intl-messageformat/intl-messageformat.js",
733733
"front_end/third_party/json5/json5.js",
734+
"front_end/third_party/legacy-javascript/legacy-javascript.js",
734735
"front_end/third_party/lighthouse/lighthouse-dt-bundle.js",
735736
"front_end/third_party/lighthouse/report/report.js",
736737
"front_end/third_party/lit/lit.js",
@@ -1172,6 +1173,7 @@ grd_files_debug_sources = [
11721173
"front_end/models/trace/insights/InteractionToNextPaint.js",
11731174
"front_end/models/trace/insights/LCPDiscovery.js",
11741175
"front_end/models/trace/insights/LCPPhases.js",
1176+
"front_end/models/trace/insights/LegacyJavaScript.js",
11751177
"front_end/models/trace/insights/Models.js",
11761178
"front_end/models/trace/insights/ModernHTTP.js",
11771179
"front_end/models/trace/insights/NetworkDependencyTree.js",
@@ -2032,6 +2034,7 @@ grd_files_debug_sources = [
20322034
"front_end/third_party/intl-messageformat/package/intl-messageformat.esm.js",
20332035
"front_end/third_party/json5/lib/index.js",
20342036
"front_end/third_party/json5/package/lib/index.js",
2037+
"front_end/third_party/legacy-javascript/lib/legacy-javascript.js",
20352038
"front_end/third_party/lighthouse/report-assets/report-generator.mjs",
20362039
"front_end/third_party/lighthouse/report/bundle.js",
20372040
"front_end/third_party/lit/lib/decorators.js",

front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ ${checklistBulletPoints.map(point => `- ${point.name}: ${point.passed ? 'PASSED'
268268
return '';
269269
case 'ModernHTTP':
270270
return '';
271+
case 'LegacyJavaScript':
272+
return '';
271273
}
272274
}
273275

@@ -325,6 +327,8 @@ It is important that all of these checks pass to minimize the delay between the
325327
return '';
326328
case 'ModernHTTP':
327329
return '';
330+
case 'LegacyJavaScript':
331+
return '';
328332
}
329333
}
330334
}

front_end/models/trace/Processor.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ describeWithEnvironment('TraceProcessor', function() {
398398
'ForcedReflow',
399399
'Cache',
400400
'ModernHTTP',
401+
'LegacyJavaScript',
401402
]);
402403

403404
const orderWithMetadata = await getInsightOrder(true);
@@ -420,6 +421,7 @@ describeWithEnvironment('TraceProcessor', function() {
420421
'ForcedReflow',
421422
'Cache',
422423
'ModernHTTP',
424+
'LegacyJavaScript',
423425
]);
424426
});
425427
});

front_end/models/trace/Processor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ export class TraceProcessor extends EventTarget {
373373
ForcedReflow: null,
374374
Cache: null,
375375
ModernHTTP: null,
376+
LegacyJavaScript: null,
376377
};
377378

378379
// Determine the weights for each metric based on field data, utilizing the same scoring curve that Lighthouse uses.

front_end/models/trace/insights/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ devtools_module("insights") {
2020
"InteractionToNextPaint.ts",
2121
"LCPDiscovery.ts",
2222
"LCPPhases.ts",
23+
"LegacyJavaScript.ts",
2324
"Models.ts",
2425
"ModernHTTP.ts",
2526
"NetworkDependencyTree.ts",
@@ -32,6 +33,7 @@ devtools_module("insights") {
3233
]
3334

3435
deps = [
36+
"../../../third_party/legacy-javascript:bundle",
3537
"../../../third_party/third-party-web:bundle",
3638
"../extras:bundle",
3739
"../handlers:bundle",
@@ -66,6 +68,7 @@ ts_library("unittests") {
6668
"InteractionToNextPaint.test.ts",
6769
"LCPDiscovery.test.ts",
6870
"LCPPhases.test.ts",
71+
"LegacyJavaScript.test.ts",
6972
"ModernHTTP.test.ts",
7073
"NetworkDependencyTree.test.ts",
7174
"RenderBlocking.test.ts",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2025 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
6+
import {getFirstOrError, getInsightOrError, processTrace} from '../../../testing/InsightHelpers.js';
7+
import type * as Trace from '../trace.js';
8+
9+
describeWithEnvironment('LegacyJavaScript', function() {
10+
it('works', async () => {
11+
const {data, insights} = await processTrace(this, 'dupe-js.json.gz');
12+
assert.strictEqual(insights.size, 1);
13+
const insight =
14+
getInsightOrError('LegacyJavaScript', insights, getFirstOrError(data.Meta.navigationsByNavigationId.values()));
15+
16+
const results =
17+
Object.fromEntries([...insight.legacyJavaScriptResults.entries()].slice(0, 1).map(([script, data]) => {
18+
return [script.url, data];
19+
}));
20+
21+
assert.deepEqual(results, {
22+
'https://dupe-modules-lh-2.surge.sh/bundle.js?v1': {
23+
estimatedByteSavings: 2952,
24+
matches: [
25+
{name: '@babel/plugin-transform-classes', line: 1, column: 165614},
26+
{name: '@babel/plugin-transform-regenerator', line: 1, column: 159765},
27+
{name: '@babel/plugin-transform-spread', line: 1, column: 350646},
28+
],
29+
},
30+
});
31+
32+
assert.deepEqual(insight.metricSavings, {FCP: 0, LCP: 0} as Trace.Insights.Types.MetricSavings);
33+
});
34+
});
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright 2025 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import * as i18n from '../../../core/i18n/i18n.js';
6+
import * as LegacyJavaScriptLib from '../../../third_party/legacy-javascript/legacy-javascript.js';
7+
import type * as Handlers from '../handlers/handlers.js';
8+
import * as Helpers from '../helpers/helpers.js';
9+
10+
import {estimateCompressionRatioForScript, metricSavingsForWastedBytes} from './Common.js';
11+
import {
12+
InsightCategory,
13+
InsightKeys,
14+
type InsightModel,
15+
type InsightSetContext,
16+
type PartialInsightModel,
17+
} from './types.js';
18+
19+
const {detectLegacyJavaScript} = LegacyJavaScriptLib.LegacyJavaScript;
20+
21+
export const UIStrings = {
22+
/**
23+
* @description Title of an insight that identifies polyfills for modern JavaScript features, and recommends their removal.
24+
*/
25+
title: 'Legacy JavaScript',
26+
/**
27+
* @description Description of an insight that identifies polyfills for modern JavaScript features, and recommends their removal.
28+
*/
29+
description: 'Legacy JavaScript',
30+
} as const;
31+
32+
const str_ = i18n.i18n.registerUIStrings('models/trace/insights/LegacyJavaScript.ts', UIStrings);
33+
export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
34+
35+
interface PatternMatchResult {
36+
name: string;
37+
line: number;
38+
column: number;
39+
}
40+
41+
interface LegacyJavaScriptResult {
42+
matches: PatternMatchResult[];
43+
estimatedByteSavings: number;
44+
}
45+
46+
type LegacyJavaScriptResults = Map<Handlers.ModelHandlers.Scripts.Script, LegacyJavaScriptResult>;
47+
48+
export type LegacyJavaScriptInsightModel = InsightModel<typeof UIStrings, {
49+
legacyJavaScriptResults: LegacyJavaScriptResults,
50+
}>;
51+
52+
function finalize(partialModel: PartialInsightModel<LegacyJavaScriptInsightModel>): LegacyJavaScriptInsightModel {
53+
const requests = [...partialModel.legacyJavaScriptResults.keys()].map(script => script.request).filter(e => !!e);
54+
55+
return {
56+
insightKey: InsightKeys.LEGACY_JAVASCRIPT,
57+
strings: UIStrings,
58+
title: i18nString(UIStrings.title),
59+
description: i18nString(UIStrings.description),
60+
category: InsightCategory.ALL,
61+
state: requests.length ? 'fail' : 'pass',
62+
relatedEvents: [...new Set(requests)],
63+
...partialModel,
64+
};
65+
}
66+
67+
export function generateInsight(
68+
parsedTrace: Handlers.Types.ParsedTrace, context: InsightSetContext): LegacyJavaScriptInsightModel {
69+
const scripts = parsedTrace.Scripts.scripts.filter(script => {
70+
if (!context.navigation) {
71+
return false;
72+
}
73+
74+
if (script.frame !== context.frameId) {
75+
return false;
76+
}
77+
78+
if (script.url?.startsWith('chrome-extension://')) {
79+
return false;
80+
}
81+
82+
return Helpers.Timing.timestampIsInBounds(context.bounds, script.ts);
83+
});
84+
85+
const legacyJavaScriptResults: LegacyJavaScriptResults = new Map();
86+
const wastedBytesByRequestId = new Map<string, number>();
87+
88+
for (const script of scripts) {
89+
if (!script.content) {
90+
continue;
91+
}
92+
93+
const result = detectLegacyJavaScript(script.content, script.sourceMap);
94+
legacyJavaScriptResults.set(script, result);
95+
96+
if (script.request) {
97+
const compressionRatio = estimateCompressionRatioForScript(script);
98+
const transferSize = Math.round(result.estimatedByteSavings * compressionRatio);
99+
const requestId = script.request.args.data.requestId;
100+
wastedBytesByRequestId.set(requestId, transferSize);
101+
}
102+
}
103+
104+
return finalize({
105+
legacyJavaScriptResults,
106+
metricSavings: metricSavingsForWastedBytes(wastedBytesByRequestId, context),
107+
});
108+
}

front_end/models/trace/insights/Models.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export * as ImageDelivery from './ImageDelivery.js';
1313
export * as InteractionToNextPaint from './InteractionToNextPaint.js';
1414
export * as LCPDiscovery from './LCPDiscovery.js';
1515
export * as LCPPhases from './LCPPhases.js';
16+
export * as LegacyJavaScript from './LegacyJavaScript.js';
1617
export * as ModernHTTP from './ModernHTTP.js';
1718
export * as NetworkDependencyTree from './NetworkDependencyTree.js';
1819
export * as RenderBlocking from './RenderBlocking.js';

front_end/models/trace/insights/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export const enum InsightKeys {
131131
FORCED_REFLOW = 'ForcedReflow',
132132
IMAGE_DELIVERY = 'ImageDelivery',
133133
LCP_DISCOVERY = 'LCPDiscovery',
134+
LEGACY_JAVASCRIPT = 'LegacyJavaScript',
134135
NETWORK_DEPENDENCY_TREE = 'NetworkDependencyTree',
135136
RENDER_BLOCKING = 'RenderBlocking',
136137
SLOW_CSS_SELECTOR = 'SlowCSSSelector',

front_end/third_party/additional_readme_paths.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"json5",
99
"i18n",
1010
"intl-messageformat",
11+
"legacy-javascript",
1112
"lighthouse",
1213
"lit",
1314
"marked",

0 commit comments

Comments
 (0)