@paulirish/trace_engine 0.0.40 → 0.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tmp/tsbuildinfo/tsconfig.tsbuildinfo +1 -1
- package/generated/protocol.d.ts +79 -3
- package/locales/en-US.json +222 -0
- package/locales/en-XL.json +222 -0
- package/models/cpu_profile/CPUProfileDataModel.d.ts +7 -0
- package/models/cpu_profile/CPUProfileDataModel.js +7 -0
- package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
- package/models/cpu_profile/ProfileTreeModel.d.ts +1 -1
- package/models/cpu_profile/ProfileTreeModel.js.map +1 -1
- package/models/trace/LanternComputationData.d.ts +1 -1
- package/models/trace/LanternComputationData.js +7 -5
- package/models/trace/LanternComputationData.js.map +1 -1
- package/models/trace/Processor.d.ts +2 -16
- package/models/trace/Processor.js +6 -4
- package/models/trace/Processor.js.map +1 -1
- package/models/trace/extras/FilmStrip.d.ts +5 -5
- package/models/trace/extras/FilmStrip.js +2 -1
- package/models/trace/extras/FilmStrip.js.map +1 -1
- package/models/trace/extras/MainThreadActivity.d.ts +1 -1
- package/models/trace/extras/MainThreadActivity.js +3 -3
- package/models/trace/extras/MainThreadActivity.js.map +1 -1
- package/models/trace/extras/ThirdParties.d.ts +14 -13
- package/models/trace/extras/ThirdParties.js +51 -61
- package/models/trace/extras/ThirdParties.js.map +1 -1
- package/models/trace/extras/TimelineJSProfile.d.ts +1 -1
- package/models/trace/extras/TimelineJSProfile.js +9 -4
- package/models/trace/extras/TimelineJSProfile.js.map +1 -1
- package/models/trace/extras/TraceTree.d.ts +6 -6
- package/models/trace/extras/TraceTree.js +4 -1
- package/models/trace/extras/TraceTree.js.map +1 -1
- package/models/trace/handlers/AnimationFramesHandler.js +1 -1
- package/models/trace/handlers/AnimationFramesHandler.js.map +1 -1
- package/models/trace/handlers/ExtensionTraceDataHandler.js +8 -2
- package/models/trace/handlers/ExtensionTraceDataHandler.js.map +1 -1
- package/models/trace/handlers/FlowsHandler.js.map +1 -1
- package/models/trace/handlers/FramesHandler.d.ts +12 -12
- package/models/trace/handlers/FramesHandler.js +3 -3
- package/models/trace/handlers/FramesHandler.js.map +1 -1
- package/models/trace/handlers/LayoutShiftsHandler.d.ts +2 -2
- package/models/trace/handlers/LayoutShiftsHandler.js +25 -16
- package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
- package/models/trace/handlers/MetaHandler.d.ts +2 -2
- package/models/trace/handlers/MetaHandler.js +18 -18
- package/models/trace/handlers/MetaHandler.js.map +1 -1
- package/models/trace/handlers/NetworkRequestsHandler.js +37 -38
- package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
- package/models/trace/handlers/PageLoadMetricsHandler.d.ts +6 -6
- package/models/trace/handlers/PageLoadMetricsHandler.js +9 -9
- package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
- package/models/trace/handlers/RendererHandler.js +3 -3
- package/models/trace/handlers/RendererHandler.js.map +1 -1
- package/models/trace/handlers/SamplesHandler.d.ts +1 -1
- package/models/trace/handlers/SamplesHandler.js +50 -42
- package/models/trace/handlers/SamplesHandler.js.map +1 -1
- package/models/trace/handlers/ScreenshotsHandler.d.ts +6 -3
- package/models/trace/handlers/ScreenshotsHandler.js +21 -7
- package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
- package/models/trace/handlers/ServerTimingsHandler.js +4 -4
- package/models/trace/handlers/ServerTimingsHandler.js.map +1 -1
- package/models/trace/handlers/Threads.js +3 -4
- package/models/trace/handlers/Threads.js.map +1 -1
- package/models/trace/handlers/UserInteractionsHandler.d.ts +2 -2
- package/models/trace/handlers/UserInteractionsHandler.js +12 -12
- package/models/trace/handlers/UserInteractionsHandler.js.map +1 -1
- package/models/trace/handlers/WarningsHandler.d.ts +2 -2
- package/models/trace/handlers/WarningsHandler.js +2 -2
- package/models/trace/handlers/WarningsHandler.js.map +1 -1
- package/models/trace/handlers/helpers.d.ts +3 -3
- package/models/trace/handlers/helpers.js +16 -19
- package/models/trace/handlers/helpers.js.map +1 -1
- package/models/trace/handlers/types.d.ts +1 -1
- package/models/trace/handlers/types.js.map +1 -1
- package/models/trace/helpers/SamplesIntegrator.js +52 -13
- package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
- package/models/trace/helpers/Timing.d.ts +23 -23
- package/models/trace/helpers/Timing.js +11 -11
- package/models/trace/helpers/Timing.js.map +1 -1
- package/models/trace/helpers/Trace.d.ts +14 -13
- package/models/trace/helpers/Trace.js +10 -4
- package/models/trace/helpers/Trace.js.map +1 -1
- package/models/trace/helpers/TreeHelpers.d.ts +2 -2
- package/models/trace/helpers/TreeHelpers.js +4 -4
- package/models/trace/helpers/TreeHelpers.js.map +1 -1
- package/models/trace/insights/CLSCulprits.d.ts +53 -1
- package/models/trace/insights/CLSCulprits.js +45 -3
- package/models/trace/insights/CLSCulprits.js.map +1 -1
- package/models/trace/insights/Common.d.ts +11 -7
- package/models/trace/insights/Common.js +85 -26
- package/models/trace/insights/Common.js.map +1 -1
- package/models/trace/insights/DOMSize.d.ts +36 -1
- package/models/trace/insights/DOMSize.js +45 -8
- package/models/trace/insights/DOMSize.js.map +1 -1
- package/models/trace/insights/DocumentLatency.d.ts +60 -3
- package/models/trace/insights/DocumentLatency.js +52 -5
- package/models/trace/insights/DocumentLatency.js.map +1 -1
- package/models/trace/insights/FontDisplay.d.ts +16 -2
- package/models/trace/insights/FontDisplay.js +11 -5
- package/models/trace/insights/FontDisplay.js.map +1 -1
- package/models/trace/insights/ForcedReflow.d.ts +30 -0
- package/models/trace/insights/ForcedReflow.js +181 -0
- package/models/trace/insights/ForcedReflow.js.map +1 -0
- package/models/trace/insights/ImageDelivery.d.ts +47 -1
- package/models/trace/insights/ImageDelivery.js +22 -2
- package/models/trace/insights/ImageDelivery.js.map +1 -1
- package/models/trace/insights/InteractionToNextPaint.d.ts +36 -1
- package/models/trace/insights/InteractionToNextPaint.js +28 -2
- package/models/trace/insights/InteractionToNextPaint.js.map +1 -1
- package/models/trace/insights/LCPDiscovery.d.ts +48 -2
- package/models/trace/insights/LCPDiscovery.js +39 -3
- package/models/trace/insights/LCPDiscovery.js.map +1 -1
- package/models/trace/insights/LCPPhases.d.ts +47 -7
- package/models/trace/insights/LCPPhases.js +38 -9
- package/models/trace/insights/LCPPhases.js.map +1 -1
- package/models/trace/insights/LongCriticalNetworkTree.d.ts +22 -0
- package/models/trace/insights/LongCriticalNetworkTree.js +40 -0
- package/models/trace/insights/LongCriticalNetworkTree.js.map +1 -0
- package/models/trace/insights/Models.d.ts +2 -0
- package/models/trace/insights/Models.js +2 -0
- package/models/trace/insights/Models.js.map +1 -1
- package/models/trace/insights/RenderBlocking.d.ts +24 -1
- package/models/trace/insights/RenderBlocking.js +15 -2
- package/models/trace/insights/RenderBlocking.js.map +1 -1
- package/models/trace/insights/SlowCSSSelector.d.ts +37 -2
- package/models/trace/insights/SlowCSSSelector.js +28 -3
- package/models/trace/insights/SlowCSSSelector.js.map +1 -1
- package/models/trace/insights/ThirdParties.d.ts +22 -4
- package/models/trace/insights/ThirdParties.js +22 -13
- package/models/trace/insights/ThirdParties.js.map +1 -1
- package/models/trace/insights/Viewport.d.ts +10 -1
- package/models/trace/insights/Viewport.js +3 -2
- package/models/trace/insights/Viewport.js.map +1 -1
- package/models/trace/insights/insights-tsconfig.json +2 -0
- package/models/trace/insights/types.d.ts +23 -8
- package/models/trace/insights/types.js.map +1 -1
- package/models/trace/lantern/graph/CPUNode.d.ts +1 -1
- package/models/trace/lantern/graph/CPUNode.js.map +1 -1
- package/models/trace/lantern/graph/NetworkNode.d.ts +1 -1
- package/models/trace/lantern/graph/NetworkNode.js.map +1 -1
- package/models/trace/types/Configuration.d.ts +15 -0
- package/models/trace/types/Configuration.js.map +1 -1
- package/models/trace/types/File.d.ts +4 -4
- package/models/trace/types/File.js.map +1 -1
- package/models/trace/types/Timing.d.ts +7 -13
- package/models/trace/types/Timing.js +5 -2
- package/models/trace/types/Timing.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +143 -108
- package/models/trace/types/TraceEvents.js +9 -7
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/package.json +1 -1
- package/test/test-trace-engine.mjs +18 -14
|
@@ -6,7 +6,7 @@ import * as Handlers from '../handlers/handlers.js';
|
|
|
6
6
|
import * as Helpers from '../helpers/helpers.js';
|
|
7
7
|
import * as Types from '../types/types.js';
|
|
8
8
|
import { InsightCategory, InsightWarning, } from './types.js';
|
|
9
|
-
const UIStrings = {
|
|
9
|
+
export const UIStrings = {
|
|
10
10
|
/**
|
|
11
11
|
*@description Title of an insight that provides details about the LCP metric, broken down by phases / parts.
|
|
12
12
|
*/
|
|
@@ -16,9 +16,37 @@ const UIStrings = {
|
|
|
16
16
|
* This is displayed after a user expands the section to see more. No character length limits.
|
|
17
17
|
*/
|
|
18
18
|
description: 'Each [phase has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.',
|
|
19
|
+
/**
|
|
20
|
+
*@description Time to first byte title for the Largest Contentful Paint's phases timespan breakdown.
|
|
21
|
+
*/
|
|
22
|
+
timeToFirstByte: 'Time to first byte',
|
|
23
|
+
/**
|
|
24
|
+
*@description Resource load delay title for the Largest Contentful Paint phases timespan breakdown.
|
|
25
|
+
*/
|
|
26
|
+
resourceLoadDelay: 'Resource load delay',
|
|
27
|
+
/**
|
|
28
|
+
*@description Resource load duration title for the Largest Contentful Paint phases timespan breakdown.
|
|
29
|
+
*/
|
|
30
|
+
resourceLoadDuration: 'Resource load duration',
|
|
31
|
+
/**
|
|
32
|
+
*@description Element render delay title for the Largest Contentful Paint phases timespan breakdown.
|
|
33
|
+
*/
|
|
34
|
+
elementRenderDelay: 'Element render delay',
|
|
35
|
+
/**
|
|
36
|
+
*@description Label used for the phase/component/stage/section of a larger duration.
|
|
37
|
+
*/
|
|
38
|
+
phase: 'Phase',
|
|
39
|
+
/**
|
|
40
|
+
*@description Label used for the percentage a single phase/component/stage/section takes up of a larger duration.
|
|
41
|
+
*/
|
|
42
|
+
percentLCP: '% of LCP',
|
|
43
|
+
/**
|
|
44
|
+
* @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.
|
|
45
|
+
*/
|
|
46
|
+
noLcp: 'No LCP detected',
|
|
19
47
|
};
|
|
20
48
|
// const str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPPhases.ts', UIStrings);
|
|
21
|
-
const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
49
|
+
export const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
22
50
|
export function deps() {
|
|
23
51
|
return ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'];
|
|
24
52
|
}
|
|
@@ -38,22 +66,22 @@ function breakdownPhases(nav, docRequest, lcpMs, lcpRequest) {
|
|
|
38
66
|
}
|
|
39
67
|
const firstDocByteTs = Helpers.Timing.secondsToMicro(docReqTiming.requestTime) +
|
|
40
68
|
Helpers.Timing.milliToMicro(docReqTiming.receiveHeadersStart);
|
|
41
|
-
const firstDocByteTiming = Types.Timing.
|
|
69
|
+
const firstDocByteTiming = Types.Timing.Micro(firstDocByteTs - nav.ts);
|
|
42
70
|
const ttfb = Helpers.Timing.microToMilli(firstDocByteTiming);
|
|
43
|
-
let renderDelay = Types.Timing.
|
|
71
|
+
let renderDelay = Types.Timing.Milli(lcpMs - ttfb);
|
|
44
72
|
if (!lcpRequest) {
|
|
45
73
|
if (anyValuesNaN(ttfb, renderDelay)) {
|
|
46
74
|
return null;
|
|
47
75
|
}
|
|
48
76
|
return { ttfb, renderDelay };
|
|
49
77
|
}
|
|
50
|
-
const lcpStartTs = Types.Timing.
|
|
78
|
+
const lcpStartTs = Types.Timing.Micro(lcpRequest.ts - nav.ts);
|
|
51
79
|
const requestStart = Helpers.Timing.microToMilli(lcpStartTs);
|
|
52
|
-
const lcpReqEndTs = Types.Timing.
|
|
80
|
+
const lcpReqEndTs = Types.Timing.Micro(lcpRequest.args.data.syntheticData.finishTime - nav.ts);
|
|
53
81
|
const requestEnd = Helpers.Timing.microToMilli(lcpReqEndTs);
|
|
54
|
-
const loadDelay = Types.Timing.
|
|
55
|
-
const loadTime = Types.Timing.
|
|
56
|
-
renderDelay = Types.Timing.
|
|
82
|
+
const loadDelay = Types.Timing.Milli(requestStart - ttfb);
|
|
83
|
+
const loadTime = Types.Timing.Milli(requestEnd - requestStart);
|
|
84
|
+
renderDelay = Types.Timing.Milli(lcpMs - requestEnd);
|
|
57
85
|
if (anyValuesNaN(ttfb, loadDelay, loadTime, renderDelay)) {
|
|
58
86
|
return null;
|
|
59
87
|
}
|
|
@@ -73,6 +101,7 @@ function finalize(partialModel) {
|
|
|
73
101
|
relatedEvents.push(partialModel.lcpRequest);
|
|
74
102
|
}
|
|
75
103
|
return {
|
|
104
|
+
strings: UIStrings,
|
|
76
105
|
title: i18nString(UIStrings.title),
|
|
77
106
|
description: i18nString(UIStrings.description),
|
|
78
107
|
category: InsightCategory.LCP,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LCPPhases.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPPhases.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n type InsightModel,\n type InsightSetContext,\n InsightWarning,\n type RequiredData,\n} from './types.js';\n\nconst UIStrings = {\n /**\n *@description Title of an insight that provides details about the LCP metric, broken down by phases / parts.\n */\n title: 'LCP by phase',\n /**\n * @description Description of a DevTools insight that presents a breakdown for the LCP metric by phases.\n * This is displayed after a user expands the section to see more. No character length limits.\n */\n description:\n 'Each [phase has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.',\n};\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPPhases.ts', UIStrings);\nconst i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport function deps(): ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'] {\n return ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'];\n}\n\ninterface LCPPhases {\n /**\n * The time between when the user initiates loading the page until when\n * the browser receives the first byte of the html response.\n */\n ttfb: Types.Timing.MilliSeconds;\n /**\n * The time between ttfb and the LCP request request being started.\n * For a text LCP, this is undefined given no request is loaded.\n */\n loadDelay?: Types.Timing.MilliSeconds;\n /**\n * The time it takes to load the LCP request.\n */\n loadTime?: Types.Timing.MilliSeconds;\n /**\n * The time between when the LCP request finishes loading and when\n * the LCP element is rendered.\n */\n renderDelay: Types.Timing.MilliSeconds;\n}\n\nexport type LCPPhasesInsightModel = InsightModel<{\n lcpMs?: Types.Timing.MilliSeconds,\n lcpTs?: Types.Timing.MilliSeconds,\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n phases?: LCPPhases,\n}>;\n\nfunction anyValuesNaN(...values: number[]): boolean {\n return values.some(v => Number.isNaN(v));\n}\n/**\n * Calculates the 4 phases of an LCP and the timings of each.\n * Will return `null` if any required values were missing. We don't ever expect\n * them to be missing on newer traces, but old trace files may lack some of the\n * data we rely on, so we want to handle that case.\n */\nfunction breakdownPhases(\n nav: Types.Events.NavigationStart, docRequest: Types.Events.SyntheticNetworkRequest,\n lcpMs: Types.Timing.MilliSeconds, lcpRequest: Types.Events.SyntheticNetworkRequest|undefined): LCPPhases|null {\n const docReqTiming = docRequest.args.data.timing;\n if (!docReqTiming) {\n throw new Error('no timing for document request');\n }\n const firstDocByteTs = Helpers.Timing.secondsToMicro(docReqTiming.requestTime) +\n Helpers.Timing.milliToMicro(docReqTiming.receiveHeadersStart);\n\n const firstDocByteTiming = Types.Timing.MicroSeconds(firstDocByteTs - nav.ts);\n const ttfb = Helpers.Timing.microToMilli(firstDocByteTiming);\n let renderDelay = Types.Timing.MilliSeconds(lcpMs - ttfb);\n\n if (!lcpRequest) {\n if (anyValuesNaN(ttfb, renderDelay)) {\n return null;\n }\n return {ttfb, renderDelay};\n }\n\n const lcpStartTs = Types.Timing.MicroSeconds(lcpRequest.ts - nav.ts);\n const requestStart = Helpers.Timing.microToMilli(lcpStartTs);\n\n const lcpReqEndTs = Types.Timing.MicroSeconds(lcpRequest.args.data.syntheticData.finishTime - nav.ts);\n const requestEnd = Helpers.Timing.microToMilli(lcpReqEndTs);\n\n const loadDelay = Types.Timing.MilliSeconds(requestStart - ttfb);\n const loadTime = Types.Timing.MilliSeconds(requestEnd - requestStart);\n renderDelay = Types.Timing.MilliSeconds(lcpMs - requestEnd);\n if (anyValuesNaN(ttfb, loadDelay, loadTime, renderDelay)) {\n return null;\n }\n\n return {\n ttfb,\n loadDelay,\n loadTime,\n renderDelay,\n };\n}\n\nfunction finalize(partialModel: Omit<LCPPhasesInsightModel, 'title'|'description'|'category'|'shouldShow'>):\n LCPPhasesInsightModel {\n const relatedEvents = [];\n if (partialModel.lcpEvent) {\n relatedEvents.push(partialModel.lcpEvent);\n }\n if (partialModel.lcpRequest) {\n relatedEvents.push(partialModel.lcpRequest);\n }\n return {\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n // TODO: should move the component's \"getPhaseData\" to model.\n shouldShow: Boolean(partialModel.phases) && (partialModel.lcpMs ?? 0) > 0,\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): LCPPhasesInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const networkRequests = parsedTrace.NetworkRequests;\n\n const frameMetrics = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);\n if (!frameMetrics) {\n throw new Error('no frame metrics');\n }\n\n const navMetrics = frameMetrics.get(context.navigationId);\n if (!navMetrics) {\n throw new Error('no navigation metrics');\n }\n const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);\n const lcpEvent = metricScore?.event;\n if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {\n return finalize({warnings: [InsightWarning.NO_LCP]});\n }\n\n // This helps calculate the phases.\n const lcpMs = Helpers.Timing.microToMilli(metricScore.timing);\n // This helps position things on the timeline's UI accurately for a trace.\n const lcpTs = metricScore.event?.ts ? Helpers.Timing.microToMilli(metricScore.event?.ts) : undefined;\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigation.get(context.navigation);\n\n const docRequest = networkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);\n if (!docRequest) {\n return finalize({lcpMs, lcpTs, lcpEvent, lcpRequest, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n if (!lcpRequest) {\n return finalize({\n lcpMs,\n lcpTs,\n lcpEvent,\n lcpRequest,\n phases: breakdownPhases(context.navigation, docRequest, lcpMs, lcpRequest) ?? undefined,\n });\n }\n\n return finalize({\n lcpMs,\n lcpTs,\n lcpEvent,\n lcpRequest,\n phases: breakdownPhases(context.navigation, docRequest, lcpMs, lcpRequest) ?? undefined,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"LCPPhases.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPPhases.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n type InsightModel,\n type InsightSetContext,\n InsightWarning,\n type PartialInsightModel,\n type RequiredData,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n *@description Title of an insight that provides details about the LCP metric, broken down by phases / parts.\n */\n title: 'LCP by phase',\n /**\n * @description Description of a DevTools insight that presents a breakdown for the LCP metric by phases.\n * This is displayed after a user expands the section to see more. No character length limits.\n */\n description:\n 'Each [phase has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.',\n /**\n *@description Time to first byte title for the Largest Contentful Paint's phases timespan breakdown.\n */\n timeToFirstByte: 'Time to first byte',\n /**\n *@description Resource load delay title for the Largest Contentful Paint phases timespan breakdown.\n */\n resourceLoadDelay: 'Resource load delay',\n /**\n *@description Resource load duration title for the Largest Contentful Paint phases timespan breakdown.\n */\n resourceLoadDuration: 'Resource load duration',\n /**\n *@description Element render delay title for the Largest Contentful Paint phases timespan breakdown.\n */\n elementRenderDelay: 'Element render delay',\n /**\n *@description Label used for the phase/component/stage/section of a larger duration.\n */\n phase: 'Phase',\n /**\n *@description Label used for the percentage a single phase/component/stage/section takes up of a larger duration.\n */\n percentLCP: '% of LCP',\n /**\n * @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.\n */\n noLcp: 'No LCP detected',\n};\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPPhases.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport function deps(): ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'] {\n return ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'];\n}\n\ninterface LCPPhases {\n /**\n * The time between when the user initiates loading the page until when\n * the browser receives the first byte of the html response.\n */\n ttfb: Types.Timing.Milli;\n /**\n * The time between ttfb and the LCP request request being started.\n * For a text LCP, this is undefined given no request is loaded.\n */\n loadDelay?: Types.Timing.Milli;\n /**\n * The time it takes to load the LCP request.\n */\n loadTime?: Types.Timing.Milli;\n /**\n * The time between when the LCP request finishes loading and when\n * the LCP element is rendered.\n */\n renderDelay: Types.Timing.Milli;\n}\n\nexport type LCPPhasesInsightModel = InsightModel<typeof UIStrings, {\n lcpMs?: Types.Timing.Milli,\n lcpTs?: Types.Timing.Milli,\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n phases?: LCPPhases,\n}>;\n\nfunction anyValuesNaN(...values: number[]): boolean {\n return values.some(v => Number.isNaN(v));\n}\n/**\n * Calculates the 4 phases of an LCP and the timings of each.\n * Will return `null` if any required values were missing. We don't ever expect\n * them to be missing on newer traces, but old trace files may lack some of the\n * data we rely on, so we want to handle that case.\n */\nfunction breakdownPhases(\n nav: Types.Events.NavigationStart, docRequest: Types.Events.SyntheticNetworkRequest, lcpMs: Types.Timing.Milli,\n lcpRequest: Types.Events.SyntheticNetworkRequest|undefined): LCPPhases|null {\n const docReqTiming = docRequest.args.data.timing;\n if (!docReqTiming) {\n throw new Error('no timing for document request');\n }\n const firstDocByteTs = Helpers.Timing.secondsToMicro(docReqTiming.requestTime) +\n Helpers.Timing.milliToMicro(docReqTiming.receiveHeadersStart);\n\n const firstDocByteTiming = Types.Timing.Micro(firstDocByteTs - nav.ts);\n const ttfb = Helpers.Timing.microToMilli(firstDocByteTiming);\n let renderDelay = Types.Timing.Milli(lcpMs - ttfb);\n\n if (!lcpRequest) {\n if (anyValuesNaN(ttfb, renderDelay)) {\n return null;\n }\n return {ttfb, renderDelay};\n }\n\n const lcpStartTs = Types.Timing.Micro(lcpRequest.ts - nav.ts);\n const requestStart = Helpers.Timing.microToMilli(lcpStartTs);\n\n const lcpReqEndTs = Types.Timing.Micro(lcpRequest.args.data.syntheticData.finishTime - nav.ts);\n const requestEnd = Helpers.Timing.microToMilli(lcpReqEndTs);\n\n const loadDelay = Types.Timing.Milli(requestStart - ttfb);\n const loadTime = Types.Timing.Milli(requestEnd - requestStart);\n renderDelay = Types.Timing.Milli(lcpMs - requestEnd);\n if (anyValuesNaN(ttfb, loadDelay, loadTime, renderDelay)) {\n return null;\n }\n\n return {\n ttfb,\n loadDelay,\n loadTime,\n renderDelay,\n };\n}\n\nfunction finalize(partialModel: PartialInsightModel<LCPPhasesInsightModel>): LCPPhasesInsightModel {\n const relatedEvents = [];\n if (partialModel.lcpEvent) {\n relatedEvents.push(partialModel.lcpEvent);\n }\n if (partialModel.lcpRequest) {\n relatedEvents.push(partialModel.lcpRequest);\n }\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n // TODO: should move the component's \"getPhaseData\" to model.\n shouldShow: Boolean(partialModel.phases) && (partialModel.lcpMs ?? 0) > 0,\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): LCPPhasesInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const networkRequests = parsedTrace.NetworkRequests;\n\n const frameMetrics = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId);\n if (!frameMetrics) {\n throw new Error('no frame metrics');\n }\n\n const navMetrics = frameMetrics.get(context.navigationId);\n if (!navMetrics) {\n throw new Error('no navigation metrics');\n }\n const metricScore = navMetrics.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP);\n const lcpEvent = metricScore?.event;\n if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {\n return finalize({warnings: [InsightWarning.NO_LCP]});\n }\n\n // This helps calculate the phases.\n const lcpMs = Helpers.Timing.microToMilli(metricScore.timing);\n // This helps position things on the timeline's UI accurately for a trace.\n const lcpTs = metricScore.event?.ts ? Helpers.Timing.microToMilli(metricScore.event?.ts) : undefined;\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigation.get(context.navigation);\n\n const docRequest = networkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);\n if (!docRequest) {\n return finalize({lcpMs, lcpTs, lcpEvent, lcpRequest, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n if (!lcpRequest) {\n return finalize({\n lcpMs,\n lcpTs,\n lcpEvent,\n lcpRequest,\n phases: breakdownPhases(context.navigation, docRequest, lcpMs, lcpRequest) ?? undefined,\n });\n }\n\n return finalize({\n lcpMs,\n lcpTs,\n lcpEvent,\n lcpRequest,\n phases: breakdownPhases(context.navigation, docRequest, lcpMs, lcpRequest) ?? undefined,\n });\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as Types from '../types/types.js';
|
|
2
|
+
import { type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
3
|
+
export declare const UIStrings: {
|
|
4
|
+
/**
|
|
5
|
+
* @description Title of an insight that recommends avoiding chaining critical requests.
|
|
6
|
+
*/
|
|
7
|
+
title: string;
|
|
8
|
+
/**
|
|
9
|
+
* @description Description of an insight that recommends avoiding chaining critical requests.
|
|
10
|
+
*/
|
|
11
|
+
description: string;
|
|
12
|
+
/**
|
|
13
|
+
* @description Text status indicating that there isn't long chaining critical network requests.
|
|
14
|
+
*/
|
|
15
|
+
noLongCriticalNetworkTree: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const i18nString: (id: string, values?: Record<string, string> | undefined) => Record<string, string>;
|
|
18
|
+
export type LongCriticalNetworkTreeInsightModel = InsightModel<typeof UIStrings, {
|
|
19
|
+
longChains: Types.Events.SyntheticNetworkRequest[][];
|
|
20
|
+
}>;
|
|
21
|
+
export declare function deps(): ['NetworkRequests'];
|
|
22
|
+
export declare function generateInsight(_parsedTrace: RequiredData<typeof deps>, _context: InsightSetContext): LongCriticalNetworkTreeInsightModel;
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
// import * as i18n from '../../../core/i18n/i18n.js';
|
|
5
|
+
import { InsightCategory } from './types.js';
|
|
6
|
+
export const UIStrings = {
|
|
7
|
+
/**
|
|
8
|
+
* @description Title of an insight that recommends avoiding chaining critical requests.
|
|
9
|
+
*/
|
|
10
|
+
title: 'Long critical network tree',
|
|
11
|
+
/**
|
|
12
|
+
* @description Description of an insight that recommends avoiding chaining critical requests.
|
|
13
|
+
*/
|
|
14
|
+
description: '[Avoid chaining critical requests](https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains) by reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load.',
|
|
15
|
+
/**
|
|
16
|
+
* @description Text status indicating that there isn't long chaining critical network requests.
|
|
17
|
+
*/
|
|
18
|
+
noLongCriticalNetworkTree: 'No rendering tasks impacted by long critical network tree',
|
|
19
|
+
};
|
|
20
|
+
// const str_ = i18n.i18n.registerUIStrings('models/trace/insights/LongCriticalNetworkTree.ts', UIStrings);
|
|
21
|
+
export const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
22
|
+
export function deps() {
|
|
23
|
+
return ['NetworkRequests'];
|
|
24
|
+
}
|
|
25
|
+
function finalize(partialModel) {
|
|
26
|
+
return {
|
|
27
|
+
strings: UIStrings,
|
|
28
|
+
title: i18nString(UIStrings.title),
|
|
29
|
+
description: i18nString(UIStrings.description),
|
|
30
|
+
category: InsightCategory.LCP,
|
|
31
|
+
shouldShow: partialModel.longChains.length > 0,
|
|
32
|
+
...partialModel,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function generateInsight(_parsedTrace, _context) {
|
|
36
|
+
return finalize({
|
|
37
|
+
longChains: [],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=LongCriticalNetworkTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LongCriticalNetworkTree.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LongCriticalNetworkTree.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,IAAI,MAAM,4BAA4B,CAAC;AAGnD,OAAO,EACL,eAAe,EAKhB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;;OAEG;IACH,KAAK,EAAE,4BAA4B;IACnC;;OAEG;IACH,WAAW,EACP,0QAA0Q;IAC9Q;;OAEG;IACH,yBAAyB,EAAE,2DAA2D;CACvF,CAAC;AAEF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,kDAAkD,EAAE,SAAS,CAAC,CAAC;AACxG,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAM7E,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,QAAQ,CAAC,YAAsE;IAEtF,OAAO;QACL,OAAO,EAAE,SAAS;QAClB,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,KAAK,CAAC;QAClC,WAAW,EAAE,UAAU,CAAC,SAAS,CAAC,WAAW,CAAC;QAC9C,QAAQ,EAAE,eAAe,CAAC,GAAG;QAC7B,UAAU,EAAE,YAAY,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAC9C,GAAG,YAAY;KAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC3B,YAAuC,EAAE,QAA2B;IACtE,OAAO,QAAQ,CAAC;QACd,UAAU,EAAE,EAAE;KACf,CAAC,CAAC;AACL,CAAC","sourcesContent":["// Copyright 2025 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport type * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n type InsightModel,\n type InsightSetContext,\n type PartialInsightModel,\n type RequiredData\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that recommends avoiding chaining critical requests.\n */\n title: 'Long critical network tree',\n /**\n * @description Description of an insight that recommends avoiding chaining critical requests.\n */\n description:\n '[Avoid chaining critical requests](https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains) by reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load.',\n /**\n * @description Text status indicating that there isn't long chaining critical network requests.\n */\n noLongCriticalNetworkTree: 'No rendering tasks impacted by long critical network tree',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LongCriticalNetworkTree.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type LongCriticalNetworkTreeInsightModel = InsightModel<typeof UIStrings, {\n longChains: Types.Events.SyntheticNetworkRequest[][],\n}>;\n\nexport function deps(): ['NetworkRequests'] {\n return ['NetworkRequests'];\n}\n\nfunction finalize(partialModel: PartialInsightModel<LongCriticalNetworkTreeInsightModel>):\n LongCriticalNetworkTreeInsightModel {\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n shouldShow: partialModel.longChains.length > 0,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n _parsedTrace: RequiredData<typeof deps>, _context: InsightSetContext): LongCriticalNetworkTreeInsightModel {\n return finalize({\n longChains: [],\n });\n}\n"]}
|
|
@@ -2,10 +2,12 @@ export * as CLSCulprits from './CLSCulprits.js';
|
|
|
2
2
|
export * as DocumentLatency from './DocumentLatency.js';
|
|
3
3
|
export * as DOMSize from './DOMSize.js';
|
|
4
4
|
export * as FontDisplay from './FontDisplay.js';
|
|
5
|
+
export * as ForcedReflow from './ForcedReflow.js';
|
|
5
6
|
export * as ImageDelivery from './ImageDelivery.js';
|
|
6
7
|
export * as InteractionToNextPaint from './InteractionToNextPaint.js';
|
|
7
8
|
export * as LCPDiscovery from './LCPDiscovery.js';
|
|
8
9
|
export * as LCPPhases from './LCPPhases.js';
|
|
10
|
+
export * as LongCriticalNetworkTree from './LongCriticalNetworkTree.js';
|
|
9
11
|
export * as RenderBlocking from './RenderBlocking.js';
|
|
10
12
|
export * as SlowCSSSelector from './SlowCSSSelector.js';
|
|
11
13
|
export * as ThirdParties from './ThirdParties.js';
|
|
@@ -5,10 +5,12 @@ export * as CLSCulprits from './CLSCulprits.js';
|
|
|
5
5
|
export * as DocumentLatency from './DocumentLatency.js';
|
|
6
6
|
export * as DOMSize from './DOMSize.js';
|
|
7
7
|
export * as FontDisplay from './FontDisplay.js';
|
|
8
|
+
export * as ForcedReflow from './ForcedReflow.js';
|
|
8
9
|
export * as ImageDelivery from './ImageDelivery.js';
|
|
9
10
|
export * as InteractionToNextPaint from './InteractionToNextPaint.js';
|
|
10
11
|
export * as LCPDiscovery from './LCPDiscovery.js';
|
|
11
12
|
export * as LCPPhases from './LCPPhases.js';
|
|
13
|
+
export * as LongCriticalNetworkTree from './LongCriticalNetworkTree.js';
|
|
12
14
|
export * as RenderBlocking from './RenderBlocking.js';
|
|
13
15
|
export * as SlowCSSSelector from './SlowCSSSelector.js';
|
|
14
16
|
export * as ThirdParties from './ThirdParties.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Models.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Models.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,sBAAsB,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nexport * as CLSCulprits from './CLSCulprits.js';\nexport * as DocumentLatency from './DocumentLatency.js';\nexport * as DOMSize from './DOMSize.js';\nexport * as FontDisplay from './FontDisplay.js';\nexport * as ImageDelivery from './ImageDelivery.js';\nexport * as InteractionToNextPaint from './InteractionToNextPaint.js';\nexport * as LCPDiscovery from './LCPDiscovery.js';\nexport * as LCPPhases from './LCPPhases.js';\nexport * as RenderBlocking from './RenderBlocking.js';\nexport * as SlowCSSSelector from './SlowCSSSelector.js';\nexport * as ThirdParties from './ThirdParties.js';\nexport * as Viewport from './Viewport.js';\n"]}
|
|
1
|
+
{"version":3,"file":"Models.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Models.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,sBAAsB,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAC5C,OAAO,KAAK,uBAAuB,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nexport * as CLSCulprits from './CLSCulprits.js';\nexport * as DocumentLatency from './DocumentLatency.js';\nexport * as DOMSize from './DOMSize.js';\nexport * as FontDisplay from './FontDisplay.js';\nexport * as ForcedReflow from './ForcedReflow.js';\nexport * as ImageDelivery from './ImageDelivery.js';\nexport * as InteractionToNextPaint from './InteractionToNextPaint.js';\nexport * as LCPDiscovery from './LCPDiscovery.js';\nexport * as LCPPhases from './LCPPhases.js';\nexport * as LongCriticalNetworkTree from './LongCriticalNetworkTree.js';\nexport * as RenderBlocking from './RenderBlocking.js';\nexport * as SlowCSSSelector from './SlowCSSSelector.js';\nexport * as ThirdParties from './ThirdParties.js';\nexport * as Viewport from './Viewport.js';\n"]}
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import type * as Types from '../types/types.js';
|
|
2
2
|
import { type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
3
|
-
export
|
|
3
|
+
export declare const UIStrings: {
|
|
4
|
+
/**
|
|
5
|
+
* @description Title of an insight that provides the user with the list of network requests that blocked and therefore slowed down the page rendering and becoming visible to the user.
|
|
6
|
+
*/
|
|
7
|
+
title: string;
|
|
8
|
+
/**
|
|
9
|
+
* @description Text to describe that there are requests blocking rendering, which may affect LCP.
|
|
10
|
+
*/
|
|
11
|
+
description: string;
|
|
12
|
+
/**
|
|
13
|
+
* @description Label to describe a network request (that happens to be render-blocking).
|
|
14
|
+
*/
|
|
15
|
+
renderBlockingRequest: string;
|
|
16
|
+
/**
|
|
17
|
+
*@description Label used for a time duration.
|
|
18
|
+
*/
|
|
19
|
+
duration: string;
|
|
20
|
+
/**
|
|
21
|
+
* @description Text status indicating that no requests blocked the initial render of a navigation
|
|
22
|
+
*/
|
|
23
|
+
noRenderBlocking: string;
|
|
24
|
+
};
|
|
25
|
+
export declare const i18nString: (id: string, values?: Record<string, string> | undefined) => Record<string, string>;
|
|
26
|
+
export type RenderBlockingInsightModel = InsightModel<typeof UIStrings, {
|
|
4
27
|
renderBlockingRequests: Types.Events.SyntheticNetworkRequest[];
|
|
5
28
|
requestIdToWastedMs?: Map<string, number>;
|
|
6
29
|
}>;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import * as Handlers from '../handlers/handlers.js';
|
|
6
6
|
import * as Helpers from '../helpers/helpers.js';
|
|
7
7
|
import { InsightCategory, InsightWarning, } from './types.js';
|
|
8
|
-
const UIStrings = {
|
|
8
|
+
export const UIStrings = {
|
|
9
9
|
/**
|
|
10
10
|
* @description Title of an insight that provides the user with the list of network requests that blocked and therefore slowed down the page rendering and becoming visible to the user.
|
|
11
11
|
*/
|
|
@@ -16,9 +16,21 @@ const UIStrings = {
|
|
|
16
16
|
description: 'Requests are blocking the page\'s initial render, which may delay LCP. ' +
|
|
17
17
|
'[Deferring or inlining](https://web.dev/learn/performance/understanding-the-critical-path#render-blocking_resources/) ' +
|
|
18
18
|
'can move these network requests out of the critical path.',
|
|
19
|
+
/**
|
|
20
|
+
* @description Label to describe a network request (that happens to be render-blocking).
|
|
21
|
+
*/
|
|
22
|
+
renderBlockingRequest: 'Request',
|
|
23
|
+
/**
|
|
24
|
+
*@description Label used for a time duration.
|
|
25
|
+
*/
|
|
26
|
+
duration: 'Duration',
|
|
27
|
+
/**
|
|
28
|
+
* @description Text status indicating that no requests blocked the initial render of a navigation
|
|
29
|
+
*/
|
|
30
|
+
noRenderBlocking: 'No render blocking requests for this navigation',
|
|
19
31
|
};
|
|
20
32
|
// const str_ = i18n.i18n.registerUIStrings('models/trace/insights/RenderBlocking.ts', UIStrings);
|
|
21
|
-
const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
33
|
+
export const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
22
34
|
// Because of the way we detect blocking stylesheets, asynchronously loaded
|
|
23
35
|
// CSS with link[rel=preload] and an onload handler (see https://github.com/filamentgroup/loadCSS)
|
|
24
36
|
// can be falsely flagged as blocking. Therefore, ignore stylesheets that loaded fast enough
|
|
@@ -101,6 +113,7 @@ function computeSavings(parsedTrace, context, renderBlockingRequests) {
|
|
|
101
113
|
}
|
|
102
114
|
function finalize(partialModel) {
|
|
103
115
|
return {
|
|
116
|
+
strings: UIStrings,
|
|
104
117
|
title: i18nString(UIStrings.title),
|
|
105
118
|
description: i18nString(UIStrings.description),
|
|
106
119
|
category: InsightCategory.LCP,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RenderBlocking.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/RenderBlocking.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Protocol from '../../../generated/protocol.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport type * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n type InsightModel,\n type InsightSetContext,\n type InsightSetContextWithNavigation,\n InsightWarning,\n type LanternContext,\n type RequiredData,\n} from './types.js';\n\nconst UIStrings = {\n /**\n * @description Title of an insight that provides the user with the list of network requests that blocked and therefore slowed down the page rendering and becoming visible to the user.\n */\n title: 'Render blocking requests',\n /**\n * @description Text to describe that there are requests blocking rendering, which may affect LCP.\n */\n description: 'Requests are blocking the page\\'s initial render, which may delay LCP. ' +\n '[Deferring or inlining](https://web.dev/learn/performance/understanding-the-critical-path#render-blocking_resources/) ' +\n 'can move these network requests out of the critical path.',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/RenderBlocking.ts', UIStrings);\nconst i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type RenderBlockingInsightModel = InsightModel<{\n renderBlockingRequests: Types.Events.SyntheticNetworkRequest[],\n requestIdToWastedMs?: Map<string, number>,\n}>;\n\n// Because of the way we detect blocking stylesheets, asynchronously loaded\n// CSS with link[rel=preload] and an onload handler (see https://github.com/filamentgroup/loadCSS)\n// can be falsely flagged as blocking. Therefore, ignore stylesheets that loaded fast enough\n// to possibly be non-blocking (and they have minimal impact anyway).\nconst MINIMUM_WASTED_MS = 50;\n\nexport function deps(): ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint'] {\n return ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint'];\n}\n\n/**\n * Given a simulation's nodeTimings, return an object with the nodes/timing keyed by network URL\n */\nfunction getNodesAndTimingByRequestId(nodeTimings: Lantern.Simulation.Result['nodeTimings']):\n Map<string, {node: Lantern.Graph.Node, nodeTiming: Lantern.Types.Simulation.NodeTiming}> {\n const requestIdToNode =\n new Map<string, {node: Lantern.Graph.Node, nodeTiming: Lantern.Types.Simulation.NodeTiming}>();\n\n for (const [node, nodeTiming] of nodeTimings) {\n if (node.type !== 'network') {\n continue;\n }\n\n requestIdToNode.set(node.request.requestId, {node, nodeTiming});\n }\n\n return requestIdToNode;\n}\n\nfunction estimateSavingsWithGraphs(\n deferredIds: Set<string>, lanternContext: LanternContext): Types.Timing.MilliSeconds {\n const simulator = lanternContext.simulator;\n const fcpGraph = lanternContext.metrics.firstContentfulPaint.optimisticGraph;\n const {nodeTimings} = lanternContext.simulator.simulate(fcpGraph);\n const adjustedNodeTimings = new Map(nodeTimings);\n\n const totalChildNetworkBytes = 0;\n const minimalFCPGraph = fcpGraph.cloneWithRelationships(node => {\n // If a node can be deferred, exclude it from the new FCP graph\n const canDeferRequest = deferredIds.has(node.id);\n return !canDeferRequest;\n });\n\n if (minimalFCPGraph.type !== 'network') {\n throw new Error('minimalFCPGraph not a NetworkNode');\n }\n\n // Recalculate the \"before\" time based on our adjusted node timings.\n const estimateBeforeInline = Math.max(...Array.from(\n Array.from(adjustedNodeTimings).map(timing => timing[1].endTime),\n ));\n\n // Add the inlined bytes to the HTML response\n const originalTransferSize = minimalFCPGraph.request.transferSize;\n const safeTransferSize = originalTransferSize || 0;\n minimalFCPGraph.request.transferSize = safeTransferSize + totalChildNetworkBytes;\n const estimateAfterInline = simulator.simulate(minimalFCPGraph).timeInMs;\n minimalFCPGraph.request.transferSize = originalTransferSize;\n return Math.round(Math.max(estimateBeforeInline - estimateAfterInline, 0)) as Types.Timing.MilliSeconds;\n}\n\nfunction hasImageLCP(parsedTrace: RequiredData<typeof deps>, context: InsightSetContextWithNavigation): boolean {\n return parsedTrace.LargestImagePaint.lcpRequestByNavigation.get(context.navigation) !== undefined;\n}\n\nfunction computeSavings(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContextWithNavigation,\n renderBlockingRequests: Types.Events.SyntheticNetworkRequest[]):\n Pick<RenderBlockingInsightModel, 'metricSavings'|'requestIdToWastedMs'>|undefined {\n if (!context.lantern) {\n return;\n }\n\n const nodesAndTimingsByRequestId =\n getNodesAndTimingByRequestId(context.lantern.metrics.firstContentfulPaint.optimisticEstimate.nodeTimings);\n\n const metricSavings = {FCP: 0 as Types.Timing.MilliSeconds, LCP: 0 as Types.Timing.MilliSeconds};\n const requestIdToWastedMs = new Map<string, number>();\n const deferredNodeIds = new Set<string>();\n for (const request of renderBlockingRequests) {\n const nodeAndTiming = nodesAndTimingsByRequestId.get(request.args.data.requestId);\n if (!nodeAndTiming) {\n continue;\n }\n\n const {node, nodeTiming} = nodeAndTiming;\n\n // Mark this node and all its dependents as deferrable\n node.traverse(node => deferredNodeIds.add(node.id));\n\n // \"wastedMs\" is the download time of the network request, responseReceived - requestSent\n const wastedMs = Math.round(nodeTiming.duration);\n if (wastedMs < MINIMUM_WASTED_MS) {\n continue;\n }\n\n requestIdToWastedMs.set(node.id, wastedMs);\n }\n\n if (requestIdToWastedMs.size) {\n metricSavings.FCP = estimateSavingsWithGraphs(deferredNodeIds, context.lantern);\n\n // In most cases, render blocking resources only affect LCP if LCP isn't an image.\n if (!hasImageLCP(parsedTrace, context)) {\n metricSavings.LCP = metricSavings.FCP;\n }\n }\n\n return {metricSavings, requestIdToWastedMs};\n}\n\nfunction finalize(partialModel: Omit<RenderBlockingInsightModel, 'title'|'description'|'category'|'shouldShow'>):\n RenderBlockingInsightModel {\n return {\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n shouldShow: partialModel.renderBlockingRequests.length > 0,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): RenderBlockingInsightModel {\n if (!context.navigation) {\n return finalize({\n renderBlockingRequests: [],\n });\n }\n\n const firstPaintTs = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId)\n ?.get(context.navigationId)\n ?.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.FP)\n ?.event?.ts;\n if (!firstPaintTs) {\n return finalize({\n renderBlockingRequests: [],\n warnings: [InsightWarning.NO_FP],\n });\n }\n\n let renderBlockingRequests: Types.Events.SyntheticNetworkRequest[] = [];\n for (const req of parsedTrace.NetworkRequests.byTime) {\n if (req.args.data.frame !== context.frameId) {\n continue;\n }\n\n if (!Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(req)) {\n continue;\n }\n\n if (req.args.data.syntheticData.finishTime > firstPaintTs) {\n continue;\n }\n\n // If a request is marked `in_body_parser_blocking` it should only be considered render blocking if it is a\n // high enough priority. Some requests (e.g. scripts) are not marked as high priority if they are fetched\n // after a non-preloaded image. (See \"early\" definition in https://web.dev/articles/fetch-priority)\n //\n // There are edge cases and exceptions (e.g. priority hints) but this gives us the best approximation\n // of render blocking resources in the document body.\n if (req.args.data.renderBlocking === 'in_body_parser_blocking') {\n const priority = req.args.data.priority;\n const isScript = req.args.data.resourceType === Protocol.Network.ResourceType.Script;\n const isBlockingScript = isScript && priority === Protocol.Network.ResourcePriority.High;\n if (priority !== Protocol.Network.ResourcePriority.VeryHigh && !isBlockingScript) {\n continue;\n }\n }\n\n const navigation =\n Helpers.Trace.getNavigationForTraceEvent(req, context.frameId, parsedTrace.Meta.navigationsByFrameId);\n if (navigation === context.navigation) {\n renderBlockingRequests.push(req);\n }\n }\n\n const savings = computeSavings(parsedTrace, context, renderBlockingRequests);\n\n // Sort by request duration for insights.\n renderBlockingRequests = renderBlockingRequests.sort((a, b) => {\n return b.dur - a.dur;\n });\n\n return finalize({\n relatedEvents: renderBlockingRequests,\n renderBlockingRequests,\n ...savings,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"RenderBlocking.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/RenderBlocking.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Protocol from '../../../generated/protocol.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport type * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n type InsightModel,\n type InsightSetContext,\n type InsightSetContextWithNavigation,\n InsightWarning,\n type LanternContext,\n type PartialInsightModel,\n type RequiredData,\n} from './types.js';\n\nexport const UIStrings = {\n /**\n * @description Title of an insight that provides the user with the list of network requests that blocked and therefore slowed down the page rendering and becoming visible to the user.\n */\n title: 'Render blocking requests',\n /**\n * @description Text to describe that there are requests blocking rendering, which may affect LCP.\n */\n description: 'Requests are blocking the page\\'s initial render, which may delay LCP. ' +\n '[Deferring or inlining](https://web.dev/learn/performance/understanding-the-critical-path#render-blocking_resources/) ' +\n 'can move these network requests out of the critical path.',\n /**\n * @description Label to describe a network request (that happens to be render-blocking).\n */\n renderBlockingRequest: 'Request',\n /**\n *@description Label used for a time duration.\n */\n duration: 'Duration',\n /**\n * @description Text status indicating that no requests blocked the initial render of a navigation\n */\n noRenderBlocking: 'No render blocking requests for this navigation',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/RenderBlocking.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type RenderBlockingInsightModel = InsightModel<typeof UIStrings, {\n renderBlockingRequests: Types.Events.SyntheticNetworkRequest[],\n requestIdToWastedMs?: Map<string, number>,\n}>;\n\n// Because of the way we detect blocking stylesheets, asynchronously loaded\n// CSS with link[rel=preload] and an onload handler (see https://github.com/filamentgroup/loadCSS)\n// can be falsely flagged as blocking. Therefore, ignore stylesheets that loaded fast enough\n// to possibly be non-blocking (and they have minimal impact anyway).\nconst MINIMUM_WASTED_MS = 50;\n\nexport function deps(): ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint'] {\n return ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint'];\n}\n\n/**\n * Given a simulation's nodeTimings, return an object with the nodes/timing keyed by network URL\n */\nfunction getNodesAndTimingByRequestId(nodeTimings: Lantern.Simulation.Result['nodeTimings']):\n Map<string, {node: Lantern.Graph.Node, nodeTiming: Lantern.Types.Simulation.NodeTiming}> {\n const requestIdToNode =\n new Map<string, {node: Lantern.Graph.Node, nodeTiming: Lantern.Types.Simulation.NodeTiming}>();\n\n for (const [node, nodeTiming] of nodeTimings) {\n if (node.type !== 'network') {\n continue;\n }\n\n requestIdToNode.set(node.request.requestId, {node, nodeTiming});\n }\n\n return requestIdToNode;\n}\n\nfunction estimateSavingsWithGraphs(deferredIds: Set<string>, lanternContext: LanternContext): Types.Timing.Milli {\n const simulator = lanternContext.simulator;\n const fcpGraph = lanternContext.metrics.firstContentfulPaint.optimisticGraph;\n const {nodeTimings} = lanternContext.simulator.simulate(fcpGraph);\n const adjustedNodeTimings = new Map(nodeTimings);\n\n const totalChildNetworkBytes = 0;\n const minimalFCPGraph = fcpGraph.cloneWithRelationships(node => {\n // If a node can be deferred, exclude it from the new FCP graph\n const canDeferRequest = deferredIds.has(node.id);\n return !canDeferRequest;\n });\n\n if (minimalFCPGraph.type !== 'network') {\n throw new Error('minimalFCPGraph not a NetworkNode');\n }\n\n // Recalculate the \"before\" time based on our adjusted node timings.\n const estimateBeforeInline = Math.max(...Array.from(\n Array.from(adjustedNodeTimings).map(timing => timing[1].endTime),\n ));\n\n // Add the inlined bytes to the HTML response\n const originalTransferSize = minimalFCPGraph.request.transferSize;\n const safeTransferSize = originalTransferSize || 0;\n minimalFCPGraph.request.transferSize = safeTransferSize + totalChildNetworkBytes;\n const estimateAfterInline = simulator.simulate(minimalFCPGraph).timeInMs;\n minimalFCPGraph.request.transferSize = originalTransferSize;\n return Math.round(Math.max(estimateBeforeInline - estimateAfterInline, 0)) as Types.Timing.Milli;\n}\n\nfunction hasImageLCP(parsedTrace: RequiredData<typeof deps>, context: InsightSetContextWithNavigation): boolean {\n return parsedTrace.LargestImagePaint.lcpRequestByNavigation.get(context.navigation) !== undefined;\n}\n\nfunction computeSavings(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContextWithNavigation,\n renderBlockingRequests: Types.Events.SyntheticNetworkRequest[]):\n Pick<RenderBlockingInsightModel, 'metricSavings'|'requestIdToWastedMs'>|undefined {\n if (!context.lantern) {\n return;\n }\n\n const nodesAndTimingsByRequestId =\n getNodesAndTimingByRequestId(context.lantern.metrics.firstContentfulPaint.optimisticEstimate.nodeTimings);\n\n const metricSavings = {FCP: 0 as Types.Timing.Milli, LCP: 0 as Types.Timing.Milli};\n const requestIdToWastedMs = new Map<string, number>();\n const deferredNodeIds = new Set<string>();\n for (const request of renderBlockingRequests) {\n const nodeAndTiming = nodesAndTimingsByRequestId.get(request.args.data.requestId);\n if (!nodeAndTiming) {\n continue;\n }\n\n const {node, nodeTiming} = nodeAndTiming;\n\n // Mark this node and all its dependents as deferrable\n node.traverse(node => deferredNodeIds.add(node.id));\n\n // \"wastedMs\" is the download time of the network request, responseReceived - requestSent\n const wastedMs = Math.round(nodeTiming.duration);\n if (wastedMs < MINIMUM_WASTED_MS) {\n continue;\n }\n\n requestIdToWastedMs.set(node.id, wastedMs);\n }\n\n if (requestIdToWastedMs.size) {\n metricSavings.FCP = estimateSavingsWithGraphs(deferredNodeIds, context.lantern);\n\n // In most cases, render blocking resources only affect LCP if LCP isn't an image.\n if (!hasImageLCP(parsedTrace, context)) {\n metricSavings.LCP = metricSavings.FCP;\n }\n }\n\n return {metricSavings, requestIdToWastedMs};\n}\n\nfunction finalize(partialModel: PartialInsightModel<RenderBlockingInsightModel>): RenderBlockingInsightModel {\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n shouldShow: partialModel.renderBlockingRequests.length > 0,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): RenderBlockingInsightModel {\n if (!context.navigation) {\n return finalize({\n renderBlockingRequests: [],\n });\n }\n\n const firstPaintTs = parsedTrace.PageLoadMetrics.metricScoresByFrameId.get(context.frameId)\n ?.get(context.navigationId)\n ?.get(Handlers.ModelHandlers.PageLoadMetrics.MetricName.FP)\n ?.event?.ts;\n if (!firstPaintTs) {\n return finalize({\n renderBlockingRequests: [],\n warnings: [InsightWarning.NO_FP],\n });\n }\n\n let renderBlockingRequests: Types.Events.SyntheticNetworkRequest[] = [];\n for (const req of parsedTrace.NetworkRequests.byTime) {\n if (req.args.data.frame !== context.frameId) {\n continue;\n }\n\n if (!Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(req)) {\n continue;\n }\n\n if (req.args.data.syntheticData.finishTime > firstPaintTs) {\n continue;\n }\n\n // If a request is marked `in_body_parser_blocking` it should only be considered render blocking if it is a\n // high enough priority. Some requests (e.g. scripts) are not marked as high priority if they are fetched\n // after a non-preloaded image. (See \"early\" definition in https://web.dev/articles/fetch-priority)\n //\n // There are edge cases and exceptions (e.g. priority hints) but this gives us the best approximation\n // of render blocking resources in the document body.\n if (req.args.data.renderBlocking === 'in_body_parser_blocking') {\n const priority = req.args.data.priority;\n const isScript = req.args.data.resourceType === Protocol.Network.ResourceType.Script;\n const isBlockingScript = isScript && priority === Protocol.Network.ResourcePriority.High;\n if (priority !== Protocol.Network.ResourcePriority.VeryHigh && !isBlockingScript) {\n continue;\n }\n }\n\n const navigation =\n Helpers.Trace.getNavigationForTraceEvent(req, context.frameId, parsedTrace.Meta.navigationsByFrameId);\n if (navigation === context.navigation) {\n renderBlockingRequests.push(req);\n }\n }\n\n const savings = computeSavings(parsedTrace, context, renderBlockingRequests);\n\n // Sort by request duration for insights.\n renderBlockingRequests = renderBlockingRequests.sort((a, b) => {\n return b.dur - a.dur;\n });\n\n return finalize({\n relatedEvents: renderBlockingRequests,\n renderBlockingRequests,\n ...savings,\n });\n}\n"]}
|
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
import * as Types from '../types/types.js';
|
|
2
2
|
import { type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
3
|
+
export declare const UIStrings: {
|
|
4
|
+
/**
|
|
5
|
+
*@description Title of an insight that provides details about slow CSS selectors.
|
|
6
|
+
*/
|
|
7
|
+
title: string;
|
|
8
|
+
/**
|
|
9
|
+
* @description Text to describe how to improve the performance of CSS selectors.
|
|
10
|
+
*/
|
|
11
|
+
description: string;
|
|
12
|
+
/**
|
|
13
|
+
*@description Column name for count of elements that the engine attempted to match against a style rule
|
|
14
|
+
*/
|
|
15
|
+
matchAttempts: string;
|
|
16
|
+
/**
|
|
17
|
+
*@description Column name for count of elements that matched a style rule
|
|
18
|
+
*/
|
|
19
|
+
matchCount: string;
|
|
20
|
+
/**
|
|
21
|
+
*@description Column name for elapsed time spent computing a style rule
|
|
22
|
+
*/
|
|
23
|
+
elapsed: string;
|
|
24
|
+
/**
|
|
25
|
+
*@description Column name for the selectors that took the longest amount of time/effort.
|
|
26
|
+
*/
|
|
27
|
+
topSelectors: string;
|
|
28
|
+
/**
|
|
29
|
+
*@description Column name for a total sum.
|
|
30
|
+
*/
|
|
31
|
+
total: string;
|
|
32
|
+
/**
|
|
33
|
+
* @description Text status indicating that no CSS selector data was found.
|
|
34
|
+
*/
|
|
35
|
+
enableSelectorData: string;
|
|
36
|
+
};
|
|
37
|
+
export declare const i18nString: (id: string, values?: Record<string, string> | undefined) => Record<string, string>;
|
|
3
38
|
export declare function deps(): ['SelectorStats'];
|
|
4
|
-
export type SlowCSSSelectorInsightModel = InsightModel<{
|
|
5
|
-
totalElapsedMs: Types.Timing.
|
|
39
|
+
export type SlowCSSSelectorInsightModel = InsightModel<typeof UIStrings, {
|
|
40
|
+
totalElapsedMs: Types.Timing.Milli;
|
|
6
41
|
totalMatchAttempts: number;
|
|
7
42
|
totalMatchCount: number;
|
|
8
43
|
topElapsedMs: Types.Events.SelectorTiming[];
|
|
@@ -6,7 +6,7 @@ import * as Helpers from '../helpers/helpers.js';
|
|
|
6
6
|
import { SelectorTimingsKey } from '../types/TraceEvents.js';
|
|
7
7
|
import * as Types from '../types/types.js';
|
|
8
8
|
import { InsightCategory } from './types.js';
|
|
9
|
-
const UIStrings = {
|
|
9
|
+
export const UIStrings = {
|
|
10
10
|
/**
|
|
11
11
|
*@description Title of an insight that provides details about slow CSS selectors.
|
|
12
12
|
*/
|
|
@@ -15,9 +15,33 @@ const UIStrings = {
|
|
|
15
15
|
* @description Text to describe how to improve the performance of CSS selectors.
|
|
16
16
|
*/
|
|
17
17
|
description: 'If Recalculate Style costs remain high, selector optimization can reduce them. [Optimize the selectors](https://developer.chrome.com/docs/devtools/performance/selector-stats) with both high elapsed time and high slow-path %. Simpler selectors, fewer selectors, a smaller DOM, and a shallower DOM will all reduce matching costs.',
|
|
18
|
+
/**
|
|
19
|
+
*@description Column name for count of elements that the engine attempted to match against a style rule
|
|
20
|
+
*/
|
|
21
|
+
matchAttempts: 'Match attempts',
|
|
22
|
+
/**
|
|
23
|
+
*@description Column name for count of elements that matched a style rule
|
|
24
|
+
*/
|
|
25
|
+
matchCount: 'Match count',
|
|
26
|
+
/**
|
|
27
|
+
*@description Column name for elapsed time spent computing a style rule
|
|
28
|
+
*/
|
|
29
|
+
elapsed: 'Elapsed time',
|
|
30
|
+
/**
|
|
31
|
+
*@description Column name for the selectors that took the longest amount of time/effort.
|
|
32
|
+
*/
|
|
33
|
+
topSelectors: 'Top selectors',
|
|
34
|
+
/**
|
|
35
|
+
*@description Column name for a total sum.
|
|
36
|
+
*/
|
|
37
|
+
total: 'Total',
|
|
38
|
+
/**
|
|
39
|
+
* @description Text status indicating that no CSS selector data was found.
|
|
40
|
+
*/
|
|
41
|
+
enableSelectorData: 'No CSS selector data was found. CSS selector stats need to be enabled in the performance panel settings.',
|
|
18
42
|
};
|
|
19
43
|
// const str_ = i18n.i18n.registerUIStrings('models/trace/insights/SlowCSSSelector.ts', UIStrings);
|
|
20
|
-
const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
44
|
+
export const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
21
45
|
export function deps() {
|
|
22
46
|
return ['SelectorStats'];
|
|
23
47
|
}
|
|
@@ -48,6 +72,7 @@ function aggregateSelectorStats(data, context) {
|
|
|
48
72
|
}
|
|
49
73
|
function finalize(partialModel) {
|
|
50
74
|
return {
|
|
75
|
+
strings: UIStrings,
|
|
51
76
|
title: i18nString(UIStrings.title),
|
|
52
77
|
description: i18nString(UIStrings.description),
|
|
53
78
|
category: InsightCategory.ALL,
|
|
@@ -80,7 +105,7 @@ export function generateInsight(parsedTrace, context) {
|
|
|
80
105
|
return finalize({
|
|
81
106
|
// TODO: should we identify UpdateLayout events as linked to this insight?
|
|
82
107
|
relatedEvents: [],
|
|
83
|
-
totalElapsedMs: Types.Timing.
|
|
108
|
+
totalElapsedMs: Types.Timing.Milli(totalElapsedUs / 1000.0),
|
|
84
109
|
totalMatchAttempts,
|
|
85
110
|
totalMatchCount,
|
|
86
111
|
topElapsedMs: sortByElapsedMs.slice(0, 3),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SlowCSSSelector.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/SlowCSSSelector.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,IAAI,MAAM,4BAA4B,CAAC;AACnD,OAAO,KAAK,OAAO,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAsB,kBAAkB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC;AAE3C,OAAO,
|
|
1
|
+
{"version":3,"file":"SlowCSSSelector.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/SlowCSSSelector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport {type SelectorTiming, SelectorTimingsKey} from '../types/TraceEvents.js';\nimport * as Types from '../types/types.js';\n\nimport {\n InsightCategory,\n type InsightModel,\n type InsightSetContext,\n type PartialInsightModel,\n type RequiredData\n} from './types.js';\n\nexport const UIStrings = {\n /**\n *@description Title of an insight that provides details about slow CSS selectors.\n */\n title: 'CSS Selector costs',\n\n /**\n * @description Text to describe how to improve the performance of CSS selectors.\n */\n description:\n 'If Recalculate Style costs remain high, selector optimization can reduce them. [Optimize the selectors](https://developer.chrome.com/docs/devtools/performance/selector-stats) with both high elapsed time and high slow-path %. Simpler selectors, fewer selectors, a smaller DOM, and a shallower DOM will all reduce matching costs.',\n /**\n *@description Column name for count of elements that the engine attempted to match against a style rule\n */\n matchAttempts: 'Match attempts',\n /**\n *@description Column name for count of elements that matched a style rule\n */\n matchCount: 'Match count',\n /**\n *@description Column name for elapsed time spent computing a style rule\n */\n elapsed: 'Elapsed time',\n /**\n *@description Column name for the selectors that took the longest amount of time/effort.\n */\n topSelectors: 'Top selectors',\n /**\n *@description Column name for a total sum.\n */\n total: 'Total',\n /**\n * @description Text status indicating that no CSS selector data was found.\n */\n enableSelectorData:\n 'No CSS selector data was found. CSS selector stats need to be enabled in the performance panel settings.',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/SlowCSSSelector.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport function deps(): ['SelectorStats'] {\n return ['SelectorStats'];\n}\n\nexport type SlowCSSSelectorInsightModel = InsightModel<typeof UIStrings, {\n totalElapsedMs: Types.Timing.Milli,\n totalMatchAttempts: number,\n totalMatchCount: number,\n topElapsedMs: Types.Events.SelectorTiming[],\n topMatchAttempts: Types.Events.SelectorTiming[],\n}>;\n\nfunction aggregateSelectorStats(\n data: Map<Types.Events.UpdateLayoutTree, {\n timings: Types.Events.SelectorTiming[],\n }>,\n context: InsightSetContext): SelectorTiming[] {\n const selectorMap = new Map<String, SelectorTiming>();\n\n for (const [event, value] of data) {\n if (event.args.beginData?.frame !== context.frameId) {\n continue;\n }\n if (!Helpers.Timing.eventIsInBounds(event, context.bounds)) {\n continue;\n }\n for (const timing of value.timings) {\n const key = timing[SelectorTimingsKey.Selector] + '_' + timing[SelectorTimingsKey.StyleSheetId];\n const findTiming = selectorMap.get(key);\n if (findTiming !== undefined) {\n findTiming[SelectorTimingsKey.Elapsed] += timing[SelectorTimingsKey.Elapsed];\n findTiming[SelectorTimingsKey.FastRejectCount] += timing[SelectorTimingsKey.FastRejectCount];\n findTiming[SelectorTimingsKey.MatchAttempts] += timing[SelectorTimingsKey.MatchAttempts];\n findTiming[SelectorTimingsKey.MatchCount] += timing[SelectorTimingsKey.MatchCount];\n } else {\n selectorMap.set(key, {...timing});\n }\n }\n }\n\n return [...selectorMap.values()];\n}\n\nfunction finalize(partialModel: PartialInsightModel<SlowCSSSelectorInsightModel>): SlowCSSSelectorInsightModel {\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.ALL,\n shouldShow: partialModel.topElapsedMs.length !== 0 && partialModel.topMatchAttempts.length !== 0,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): SlowCSSSelectorInsightModel {\n const selectorStatsData = parsedTrace.SelectorStats;\n\n if (!selectorStatsData) {\n throw new Error('no selector stats data');\n }\n\n const selectorTimings = aggregateSelectorStats(selectorStatsData.dataForUpdateLayoutEvent, context);\n\n let totalElapsedUs = 0;\n let totalMatchAttempts = 0;\n let totalMatchCount = 0;\n\n selectorTimings.map(timing => {\n totalElapsedUs += timing[SelectorTimingsKey.Elapsed];\n totalMatchAttempts += timing[SelectorTimingsKey.MatchAttempts];\n totalMatchCount += timing[SelectorTimingsKey.MatchCount];\n });\n\n // sort by elapsed time\n const sortByElapsedMs = [...selectorTimings].sort((a, b) => {\n return b[SelectorTimingsKey.Elapsed] - a[SelectorTimingsKey.Elapsed];\n });\n\n // sort by match attempts\n const sortByMatchAttempts = [...selectorTimings].sort((a, b) => {\n return b[SelectorTimingsKey.MatchAttempts] - a[SelectorTimingsKey.MatchAttempts];\n });\n\n return finalize({\n // TODO: should we identify UpdateLayout events as linked to this insight?\n relatedEvents: [],\n totalElapsedMs: Types.Timing.Milli(totalElapsedUs / 1000.0),\n totalMatchAttempts,\n totalMatchCount,\n topElapsedMs: sortByElapsedMs.slice(0, 3),\n topMatchAttempts: sortByMatchAttempts.slice(0, 3),\n });\n}\n"]}
|
|
@@ -1,11 +1,29 @@
|
|
|
1
1
|
import * as Extras from '../extras/extras.js';
|
|
2
2
|
import type * as Types from '../types/types.js';
|
|
3
3
|
import { type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
4
|
+
export declare const UIStrings: {
|
|
5
|
+
/** Title of an insight that provides details about the code on a web page that the user doesn't control (referred to as "third-party code"). */
|
|
6
|
+
title: string;
|
|
7
|
+
/**
|
|
8
|
+
* @description Description of a DevTools insight that identifies the code on the page that the user doesn't control.
|
|
9
|
+
* This is displayed after a user expands the section to see more. No character length limits.
|
|
10
|
+
*/
|
|
11
|
+
description: string;
|
|
12
|
+
/** Label for a table column that displays the name of a third-party provider. */
|
|
13
|
+
columnThirdParty: string;
|
|
14
|
+
/** Label for a column in a data table; entries will be the download size of a web resource in kilobytes. */
|
|
15
|
+
columnTransferSize: string;
|
|
16
|
+
/** Label for a table column that displays how much time each row spent blocking other work on the main thread, entries will be the number of milliseconds spent. */
|
|
17
|
+
columnBlockingTime: string;
|
|
18
|
+
/**
|
|
19
|
+
* @description Text block indicating that no third party content was detected on the page
|
|
20
|
+
*/
|
|
21
|
+
noThirdParties: string;
|
|
22
|
+
};
|
|
23
|
+
export declare const i18nString: (id: string, values?: Record<string, string> | undefined) => Record<string, string>;
|
|
4
24
|
export declare function deps(): ['Meta', 'NetworkRequests', 'Renderer', 'ImagePainting'];
|
|
5
|
-
export type ThirdPartiesInsightModel = InsightModel<{
|
|
6
|
-
|
|
7
|
-
requestsByEntity: Map<Extras.ThirdParties.Entity, Types.Events.Event[]>;
|
|
8
|
-
summaryByEvent: Map<Types.Events.Event, Extras.ThirdParties.Summary>;
|
|
25
|
+
export type ThirdPartiesInsightModel = InsightModel<typeof UIStrings, {
|
|
26
|
+
eventsByEntity: Map<Extras.ThirdParties.Entity, Types.Events.Event[]>;
|
|
9
27
|
summaryByEntity: Map<Extras.ThirdParties.Entity, Extras.ThirdParties.Summary>;
|
|
10
28
|
/** The entity for this navigation's URL. Any other entity is from a third party. */
|
|
11
29
|
firstPartyEntity?: Extras.ThirdParties.Entity;
|