@paulirish/trace_engine 0.0.38 → 0.0.40
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/core/platform/DevToolsPath.d.ts +30 -9
- package/core/platform/DevToolsPath.js +21 -0
- package/core/platform/DevToolsPath.js.map +1 -1
- package/core/platform/MapUtilities.js +1 -1
- package/core/platform/MapUtilities.js.map +1 -1
- package/core/platform/ServerTiming.d.ts +2 -2
- package/core/platform/ServerTiming.js.map +1 -1
- package/core/platform/StringUtilities.js +1 -1
- package/core/platform/StringUtilities.js.map +1 -1
- package/core/platform/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/core/platform/platform-tsconfig.json +2 -1
- package/generated/protocol.d.ts +203 -73
- package/locales/af.json +86 -0
- package/locales/am.json +86 -0
- package/locales/ar.json +86 -0
- package/locales/as.json +86 -0
- package/locales/az.json +86 -0
- package/locales/be.json +86 -0
- package/locales/bg.json +86 -0
- package/locales/bn.json +86 -0
- package/locales/bs.json +86 -0
- package/locales/ca.json +86 -0
- package/locales/cs.json +86 -0
- package/locales/cy.json +86 -0
- package/locales/da.json +86 -0
- package/locales/de.json +86 -0
- package/locales/el.json +86 -0
- package/locales/en-GB.json +86 -0
- package/locales/en-US.json +86 -0
- package/locales/en-XL.json +86 -0
- package/locales/es-419.json +86 -0
- package/locales/es.json +86 -0
- package/locales/et.json +86 -0
- package/locales/eu.json +86 -0
- package/locales/fa.json +86 -0
- package/locales/fi.json +86 -0
- package/locales/fil.json +86 -0
- package/locales/fr-CA.json +86 -0
- package/locales/fr.json +86 -0
- package/locales/gl.json +86 -0
- package/locales/gu.json +86 -0
- package/locales/he.json +86 -0
- package/locales/hi.json +86 -0
- package/locales/hr.json +86 -0
- package/locales/hu.json +86 -0
- package/locales/hy.json +86 -0
- package/locales/id.json +86 -0
- package/locales/is.json +86 -0
- package/locales/it.json +86 -0
- package/locales/ja.json +86 -0
- package/locales/ka.json +86 -0
- package/locales/kk.json +86 -0
- package/locales/km.json +86 -0
- package/locales/kn.json +86 -0
- package/locales/ko.json +86 -0
- package/locales/ky.json +86 -0
- package/locales/lo.json +86 -0
- package/locales/lt.json +86 -0
- package/locales/lv.json +86 -0
- package/locales/mk.json +86 -0
- package/locales/ml.json +86 -0
- package/locales/mn.json +86 -0
- package/locales/mr.json +86 -0
- package/locales/ms.json +86 -0
- package/locales/my.json +86 -0
- package/locales/ne.json +86 -0
- package/locales/nl.json +86 -0
- package/locales/no.json +86 -0
- package/locales/or.json +86 -0
- package/locales/pa.json +86 -0
- package/locales/pl.json +86 -0
- package/locales/pt-PT.json +86 -0
- package/locales/pt.json +86 -0
- package/locales/ro.json +86 -0
- package/locales/ru.json +86 -0
- package/locales/si.json +86 -0
- package/locales/sk.json +86 -0
- package/locales/sl.json +86 -0
- package/locales/sq.json +86 -0
- package/locales/sr-Latn.json +86 -0
- package/locales/sr.json +86 -0
- package/locales/sv.json +86 -0
- package/locales/sw.json +86 -0
- package/locales/ta.json +86 -0
- package/locales/te.json +86 -0
- package/locales/th.json +86 -0
- package/locales/tr.json +86 -0
- package/locales/uk.json +86 -0
- package/locales/ur.json +86 -0
- package/locales/uz.json +86 -0
- package/locales/vi.json +86 -0
- package/locales/zh-HK.json +86 -0
- package/locales/zh-TW.json +86 -0
- package/locales/zh.json +86 -0
- package/locales/zu.json +86 -0
- package/models/cpu_profile/ProfileTreeModel.js +0 -2
- package/models/cpu_profile/ProfileTreeModel.js.map +1 -1
- package/models/cpu_profile/cpu_profile-tsconfig.json +2 -1
- package/models/cpu_profile/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/LanternComputationData.js +1 -1
- package/models/trace/LanternComputationData.js.map +1 -1
- package/models/trace/ModelImpl.d.ts +6 -6
- package/models/trace/ModelImpl.js +1 -0
- package/models/trace/ModelImpl.js.map +1 -1
- package/models/trace/Processor.d.ts +6 -0
- package/models/trace/Processor.js +90 -9
- package/models/trace/Processor.js.map +1 -1
- package/models/trace/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/extras/StackTraceForEvent.d.ts +12 -0
- package/models/trace/extras/StackTraceForEvent.js +163 -0
- package/models/trace/extras/StackTraceForEvent.js.map +1 -0
- package/models/trace/extras/ThirdParties.d.ts +7 -4
- package/models/trace/extras/ThirdParties.js +56 -65
- package/models/trace/extras/ThirdParties.js.map +1 -1
- package/models/trace/extras/TraceTree.js +8 -8
- package/models/trace/extras/TraceTree.js.map +1 -1
- package/models/trace/extras/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/extras/extras-tsconfig.json +3 -2
- package/models/trace/extras/extras.js.map +1 -1
- package/models/trace/handlers/AnimationFramesHandler.d.ts +11 -0
- package/models/trace/handlers/AnimationFramesHandler.js +102 -0
- package/models/trace/handlers/AnimationFramesHandler.js.map +1 -0
- package/models/trace/handlers/AsyncJSCallsHandler.d.ts +15 -0
- package/models/trace/handlers/AsyncJSCallsHandler.js +153 -0
- package/models/trace/handlers/AsyncJSCallsHandler.js.map +1 -0
- package/models/trace/handlers/DOMStatsHandler.d.ts +8 -0
- package/models/trace/handlers/DOMStatsHandler.js +22 -0
- package/models/trace/handlers/DOMStatsHandler.js.map +1 -0
- package/models/trace/handlers/ExtensionTraceDataHandler.d.ts +80 -2
- package/models/trace/handlers/ExtensionTraceDataHandler.js +163 -13
- package/models/trace/handlers/ExtensionTraceDataHandler.js.map +1 -1
- package/models/trace/handlers/FlowsHandler.js +47 -55
- package/models/trace/handlers/FlowsHandler.js.map +1 -1
- package/models/trace/handlers/InitiatorsHandler.d.ts +11 -0
- package/models/trace/handlers/InitiatorsHandler.js +33 -25
- package/models/trace/handlers/InitiatorsHandler.js.map +1 -1
- package/models/trace/handlers/LargestImagePaintHandler.d.ts +0 -2
- package/models/trace/handlers/LargestImagePaintHandler.js +27 -24
- package/models/trace/handlers/LargestImagePaintHandler.js.map +1 -1
- package/models/trace/handlers/LayoutShiftsHandler.d.ts +2 -2
- package/models/trace/handlers/LayoutShiftsHandler.js +3 -2
- package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
- package/models/trace/handlers/MetaHandler.d.ts +2 -2
- package/models/trace/handlers/MetaHandler.js +1 -1
- package/models/trace/handlers/MetaHandler.js.map +1 -1
- package/models/trace/handlers/ModelHandlers.d.ts +4 -1
- package/models/trace/handlers/ModelHandlers.js +4 -1
- package/models/trace/handlers/ModelHandlers.js.map +1 -1
- package/models/trace/handlers/NetworkRequestsHandler.d.ts +2 -0
- package/models/trace/handlers/NetworkRequestsHandler.js +21 -0
- package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
- package/models/trace/handlers/PageLoadMetricsHandler.d.ts +2 -2
- package/models/trace/handlers/PageLoadMetricsHandler.js +9 -9
- package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
- package/models/trace/handlers/RendererHandler.d.ts +4 -0
- package/models/trace/handlers/RendererHandler.js +33 -2
- package/models/trace/handlers/RendererHandler.js.map +1 -1
- package/models/trace/handlers/SamplesHandler.d.ts +2 -2
- package/models/trace/handlers/SamplesHandler.js +3 -3
- package/models/trace/handlers/SamplesHandler.js.map +1 -1
- package/models/trace/handlers/ServerTimingsHandler.js +2 -2
- package/models/trace/handlers/ServerTimingsHandler.js.map +1 -1
- package/models/trace/handlers/Threads.d.ts +1 -0
- package/models/trace/handlers/Threads.js +8 -0
- package/models/trace/handlers/Threads.js.map +1 -1
- package/models/trace/handlers/UserInteractionsHandler.js +5 -6
- package/models/trace/handlers/UserInteractionsHandler.js.map +1 -1
- package/models/trace/handlers/UserTimingsHandler.d.ts +1 -1
- package/models/trace/handlers/UserTimingsHandler.js +1 -1
- package/models/trace/handlers/UserTimingsHandler.js.map +1 -1
- package/models/trace/handlers/WarningsHandler.js +2 -2
- package/models/trace/handlers/WarningsHandler.js.map +1 -1
- package/models/trace/handlers/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/handlers/handlers-tsconfig.json +9 -1
- package/models/trace/handlers/handlers.d.ts +1 -0
- package/models/trace/handlers/handlers.js +1 -0
- package/models/trace/handlers/handlers.js.map +1 -1
- package/models/trace/handlers/helpers.d.ts +19 -0
- package/models/trace/handlers/helpers.js +123 -0
- package/models/trace/handlers/helpers.js.map +1 -0
- package/models/trace/helpers/SamplesIntegrator.js +2 -2
- package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
- package/models/trace/helpers/SyntheticEvents.js +1 -1
- package/models/trace/helpers/SyntheticEvents.js.map +1 -1
- package/models/trace/helpers/Timing.d.ts +5 -6
- package/models/trace/helpers/Timing.js +22 -31
- package/models/trace/helpers/Timing.js.map +1 -1
- package/models/trace/helpers/Trace.d.ts +24 -10
- package/models/trace/helpers/Trace.js +53 -6
- package/models/trace/helpers/Trace.js.map +1 -1
- package/models/trace/helpers/TreeHelpers.d.ts +0 -31
- package/models/trace/helpers/TreeHelpers.js +1 -142
- package/models/trace/helpers/TreeHelpers.js.map +1 -1
- package/models/trace/helpers/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/helpers/helpers-tsconfig.json +2 -1
- package/models/trace/insights/CLSCulprits.js +1 -2
- package/models/trace/insights/CLSCulprits.js.map +1 -1
- package/models/trace/insights/Common.d.ts +38 -1
- package/models/trace/insights/Common.js +123 -0
- package/models/trace/insights/Common.js.map +1 -1
- package/models/trace/insights/DOMSize.d.ts +9 -0
- package/models/trace/insights/DOMSize.js +102 -0
- package/models/trace/insights/DOMSize.js.map +1 -0
- package/models/trace/insights/DocumentLatency.js +1 -1
- package/models/trace/insights/DocumentLatency.js.map +1 -1
- package/models/trace/insights/FontDisplay.js +1 -1
- package/models/trace/insights/FontDisplay.js.map +1 -1
- package/models/trace/insights/ImageDelivery.d.ts +22 -4
- package/models/trace/insights/ImageDelivery.js +76 -6
- package/models/trace/insights/ImageDelivery.js.map +1 -1
- package/models/trace/insights/LCPDiscovery.js +2 -2
- package/models/trace/insights/LCPDiscovery.js.map +1 -1
- package/models/trace/insights/LCPPhases.js +7 -7
- package/models/trace/insights/LCPPhases.js.map +1 -1
- package/models/trace/insights/Models.d.ts +1 -0
- package/models/trace/insights/Models.js +1 -0
- package/models/trace/insights/Models.js.map +1 -1
- package/models/trace/insights/Statistics.d.ts +14 -0
- package/models/trace/insights/Statistics.js +86 -0
- package/models/trace/insights/Statistics.js.map +1 -0
- package/models/trace/insights/ThirdParties.d.ts +2 -2
- package/models/trace/insights/ThirdParties.js +5 -4
- package/models/trace/insights/ThirdParties.js.map +1 -1
- package/models/trace/insights/Viewport.js +2 -2
- package/models/trace/insights/Viewport.js.map +1 -1
- package/models/trace/insights/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/insights/insights-tsconfig.json +4 -1
- package/models/trace/insights/insights.d.ts +1 -0
- package/models/trace/insights/insights.js +1 -0
- package/models/trace/insights/insights.js.map +1 -1
- package/models/trace/insights/types.d.ts +4 -3
- package/models/trace/insights/types.js.map +1 -1
- package/models/trace/lantern/core/NetworkAnalyzer.d.ts +2 -2
- package/models/trace/lantern/core/NetworkAnalyzer.js +2 -3
- package/models/trace/lantern/core/NetworkAnalyzer.js.map +1 -1
- package/models/trace/lantern/core/core-tsconfig.json +2 -1
- package/models/trace/lantern/core/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/lantern/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/lantern/graph/PageDependencyGraph.js +0 -1
- package/models/trace/lantern/graph/PageDependencyGraph.js.map +1 -1
- package/models/trace/lantern/graph/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/lantern/graph/graph-tsconfig.json +2 -1
- package/models/trace/lantern/lantern-tsconfig.json +2 -1
- package/models/trace/lantern/metrics/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/lantern/metrics/metrics-tsconfig.json +2 -1
- package/models/trace/lantern/simulation/DNSCache.d.ts +2 -2
- package/models/trace/lantern/simulation/DNSCache.js.map +1 -1
- package/models/trace/lantern/simulation/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/lantern/simulation/simulation-tsconfig.json +2 -1
- package/models/trace/lantern/types/Lantern.d.ts +10 -10
- package/models/trace/lantern/types/Lantern.js.map +1 -1
- package/models/trace/lantern/types/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/lantern/types/types-tsconfig.json +2 -1
- package/models/trace/root-causes/LayoutShift.d.ts +6 -6
- package/models/trace/root-causes/LayoutShift.js +1 -1
- package/models/trace/root-causes/LayoutShift.js.map +1 -1
- package/models/trace/root-causes/RootCauses.d.ts +2 -2
- package/models/trace/root-causes/RootCauses.js.map +1 -1
- package/models/trace/root-causes/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/root-causes/root-causes-tsconfig.json +2 -1
- package/models/trace/trace-tsconfig.json +2 -1
- package/models/trace/types/Configuration.d.ts +2 -2
- package/models/trace/types/Configuration.js.map +1 -1
- package/models/trace/types/Extensions.d.ts +3 -3
- package/models/trace/types/Extensions.js.map +1 -1
- package/models/trace/types/File.d.ts +11 -11
- package/models/trace/types/File.js.map +1 -1
- package/models/trace/types/Timing.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +107 -31
- package/models/trace/types/TraceEvents.js +34 -14
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/models/trace/types/devtools_entrypoint-bundle-typescript-tsconfig.json +2 -1
- package/models/trace/types/types-tsconfig.json +2 -1
- package/package.json +1 -1
- package/test/test-trace-engine.mjs +8 -7
|
@@ -36,10 +36,10 @@ function breakdownPhases(nav, docRequest, lcpMs, lcpRequest) {
|
|
|
36
36
|
if (!docReqTiming) {
|
|
37
37
|
throw new Error('no timing for document request');
|
|
38
38
|
}
|
|
39
|
-
const firstDocByteTs = Helpers.Timing.
|
|
40
|
-
Helpers.Timing.
|
|
39
|
+
const firstDocByteTs = Helpers.Timing.secondsToMicro(docReqTiming.requestTime) +
|
|
40
|
+
Helpers.Timing.milliToMicro(docReqTiming.receiveHeadersStart);
|
|
41
41
|
const firstDocByteTiming = Types.Timing.MicroSeconds(firstDocByteTs - nav.ts);
|
|
42
|
-
const ttfb = Helpers.Timing.
|
|
42
|
+
const ttfb = Helpers.Timing.microToMilli(firstDocByteTiming);
|
|
43
43
|
let renderDelay = Types.Timing.MilliSeconds(lcpMs - ttfb);
|
|
44
44
|
if (!lcpRequest) {
|
|
45
45
|
if (anyValuesNaN(ttfb, renderDelay)) {
|
|
@@ -48,9 +48,9 @@ function breakdownPhases(nav, docRequest, lcpMs, lcpRequest) {
|
|
|
48
48
|
return { ttfb, renderDelay };
|
|
49
49
|
}
|
|
50
50
|
const lcpStartTs = Types.Timing.MicroSeconds(lcpRequest.ts - nav.ts);
|
|
51
|
-
const requestStart = Helpers.Timing.
|
|
51
|
+
const requestStart = Helpers.Timing.microToMilli(lcpStartTs);
|
|
52
52
|
const lcpReqEndTs = Types.Timing.MicroSeconds(lcpRequest.args.data.syntheticData.finishTime - nav.ts);
|
|
53
|
-
const requestEnd = Helpers.Timing.
|
|
53
|
+
const requestEnd = Helpers.Timing.microToMilli(lcpReqEndTs);
|
|
54
54
|
const loadDelay = Types.Timing.MilliSeconds(requestStart - ttfb);
|
|
55
55
|
const loadTime = Types.Timing.MilliSeconds(requestEnd - requestStart);
|
|
56
56
|
renderDelay = Types.Timing.MilliSeconds(lcpMs - requestEnd);
|
|
@@ -101,9 +101,9 @@ export function generateInsight(parsedTrace, context) {
|
|
|
101
101
|
return finalize({ warnings: [InsightWarning.NO_LCP] });
|
|
102
102
|
}
|
|
103
103
|
// This helps calculate the phases.
|
|
104
|
-
const lcpMs = Helpers.Timing.
|
|
104
|
+
const lcpMs = Helpers.Timing.microToMilli(metricScore.timing);
|
|
105
105
|
// This helps position things on the timeline's UI accurately for a trace.
|
|
106
|
-
const lcpTs = metricScore.event?.ts ? Helpers.Timing.
|
|
106
|
+
const lcpTs = metricScore.event?.ts ? Helpers.Timing.microToMilli(metricScore.event?.ts) : undefined;
|
|
107
107
|
const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigation.get(context.navigation);
|
|
108
108
|
const docRequest = networkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);
|
|
109
109
|
if (!docRequest) {
|
|
@@ -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.secondsToMicroseconds(docReqTiming.requestTime) +\n Helpers.Timing.millisecondsToMicroseconds(docReqTiming.receiveHeadersStart);\n\n const firstDocByteTiming = Types.Timing.MicroSeconds(firstDocByteTs - nav.ts);\n const ttfb = Helpers.Timing.microSecondsToMilliseconds(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.microSecondsToMilliseconds(lcpStartTs);\n\n const lcpReqEndTs = Types.Timing.MicroSeconds(lcpRequest.args.data.syntheticData.finishTime - nav.ts);\n const requestEnd = Helpers.Timing.microSecondsToMilliseconds(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.microSecondsToMilliseconds(metricScore.timing);\n // This helps position things on the timeline's UI accurately for a trace.\n const lcpTs = metricScore.event?.ts ? Helpers.Timing.microSecondsToMilliseconds(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 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,5 +1,6 @@
|
|
|
1
1
|
export * as CLSCulprits from './CLSCulprits.js';
|
|
2
2
|
export * as DocumentLatency from './DocumentLatency.js';
|
|
3
|
+
export * as DOMSize from './DOMSize.js';
|
|
3
4
|
export * as FontDisplay from './FontDisplay.js';
|
|
4
5
|
export * as ImageDelivery from './ImageDelivery.js';
|
|
5
6
|
export * as InteractionToNextPaint from './InteractionToNextPaint.js';
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
export * as CLSCulprits from './CLSCulprits.js';
|
|
5
5
|
export * as DocumentLatency from './DocumentLatency.js';
|
|
6
|
+
export * as DOMSize from './DOMSize.js';
|
|
6
7
|
export * as FontDisplay from './FontDisplay.js';
|
|
7
8
|
export * as ImageDelivery from './ImageDelivery.js';
|
|
8
9
|
export * as InteractionToNextPaint from './InteractionToNextPaint.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,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 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,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"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the score (1 - percentile) of `value` in a log-normal distribution
|
|
3
|
+
* specified by the `median` value, at which the score will be 0.5, and a 10th
|
|
4
|
+
* percentile value, at which the score will be 0.9. The score represents the
|
|
5
|
+
* amount of the distribution greater than `value`. All values should be in the
|
|
6
|
+
* same units (e.g. milliseconds). See
|
|
7
|
+
* https://www.desmos.com/calculator/o98tbeyt1t
|
|
8
|
+
* for an interactive view of the relationship between these parameters and the
|
|
9
|
+
* typical parameterization (location and shape) of the log-normal distribution.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getLogNormalScore({ median, p10 }: {
|
|
12
|
+
median: number;
|
|
13
|
+
p10: number;
|
|
14
|
+
}, value: number): number;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Copyright 2024 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
|
+
// Lifted from Lighthouse: https://github.com/GoogleChrome/lighthouse/blob/36cac182a6c637b1671c57326d7c0241633d0076/shared/statistics.js
|
|
5
|
+
/**
|
|
6
|
+
* @license
|
|
7
|
+
* Copyright 2017 Google LLC
|
|
8
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
9
|
+
*/
|
|
10
|
+
// The exact double values for the max and min scores possible in each range.
|
|
11
|
+
const MIN_PASSING_SCORE = 0.90000000000000002220446049250313080847263336181640625;
|
|
12
|
+
const MAX_AVERAGE_SCORE = 0.899999999999999911182158029987476766109466552734375;
|
|
13
|
+
const MIN_AVERAGE_SCORE = 0.5;
|
|
14
|
+
const MAX_FAILING_SCORE = 0.499999999999999944488848768742172978818416595458984375;
|
|
15
|
+
/**
|
|
16
|
+
* Approximates the Gauss error function, the probability that a random variable
|
|
17
|
+
* from the standard normal distribution lies within [-x, x]. Moved from
|
|
18
|
+
* traceviewer.b.math.erf, based on Abramowitz and Stegun, formula 7.1.26.
|
|
19
|
+
*/
|
|
20
|
+
function erf(x) {
|
|
21
|
+
// erf(-x) = -erf(x);
|
|
22
|
+
const sign = Math.sign(x);
|
|
23
|
+
x = Math.abs(x);
|
|
24
|
+
const a1 = 0.254829592;
|
|
25
|
+
const a2 = -0.284496736;
|
|
26
|
+
const a3 = 1.421413741;
|
|
27
|
+
const a4 = -1.453152027;
|
|
28
|
+
const a5 = 1.061405429;
|
|
29
|
+
const p = 0.3275911;
|
|
30
|
+
const t = 1 / (1 + p * x);
|
|
31
|
+
const y = t * (a1 + t * (a2 + t * (a3 + t * (a4 + t * a5))));
|
|
32
|
+
return sign * (1 - y * Math.exp(-x * x));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns the score (1 - percentile) of `value` in a log-normal distribution
|
|
36
|
+
* specified by the `median` value, at which the score will be 0.5, and a 10th
|
|
37
|
+
* percentile value, at which the score will be 0.9. The score represents the
|
|
38
|
+
* amount of the distribution greater than `value`. All values should be in the
|
|
39
|
+
* same units (e.g. milliseconds). See
|
|
40
|
+
* https://www.desmos.com/calculator/o98tbeyt1t
|
|
41
|
+
* for an interactive view of the relationship between these parameters and the
|
|
42
|
+
* typical parameterization (location and shape) of the log-normal distribution.
|
|
43
|
+
*/
|
|
44
|
+
export function getLogNormalScore({ median, p10 }, value) {
|
|
45
|
+
// Required for the log-normal distribution.
|
|
46
|
+
if (median <= 0) {
|
|
47
|
+
throw new Error('median must be greater than zero');
|
|
48
|
+
}
|
|
49
|
+
if (p10 <= 0) {
|
|
50
|
+
throw new Error('p10 must be greater than zero');
|
|
51
|
+
}
|
|
52
|
+
// Not strictly required, but if p10 > median, it flips around and becomes the p90 point.
|
|
53
|
+
if (p10 >= median) {
|
|
54
|
+
throw new Error('p10 must be less than the median');
|
|
55
|
+
}
|
|
56
|
+
// Non-positive values aren't in the distribution, so always 1.
|
|
57
|
+
if (value <= 0) {
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
// Closest double to `erfc-1(1/5)`.
|
|
61
|
+
const INVERSE_ERFC_ONE_FIFTH = 0.9061938024368232;
|
|
62
|
+
// Shape (σ) is `|log(p10/median) / (sqrt(2)*erfc^-1(1/5))|` and
|
|
63
|
+
// standardizedX is `1/2 erfc(log(value/median) / (sqrt(2)*σ))`, so simplify a bit.
|
|
64
|
+
const xRatio = Math.max(Number.MIN_VALUE, value / median); // value and median are > 0, so is ratio.
|
|
65
|
+
const xLogRatio = Math.log(xRatio);
|
|
66
|
+
const p10Ratio = Math.max(Number.MIN_VALUE, p10 / median); // p10 and median are > 0, so is ratio.
|
|
67
|
+
const p10LogRatio = -Math.log(p10Ratio); // negate to keep σ positive.
|
|
68
|
+
const standardizedX = xLogRatio * INVERSE_ERFC_ONE_FIFTH / p10LogRatio;
|
|
69
|
+
const complementaryPercentile = (1 - erf(standardizedX)) / 2;
|
|
70
|
+
// Clamp to avoid floating-point out-of-bounds issues and keep score in expected range.
|
|
71
|
+
let score;
|
|
72
|
+
if (value <= p10) {
|
|
73
|
+
// Passing. Clamp to [0.9, 1].
|
|
74
|
+
score = Math.max(MIN_PASSING_SCORE, Math.min(1, complementaryPercentile));
|
|
75
|
+
}
|
|
76
|
+
else if (value <= median) {
|
|
77
|
+
// Average. Clamp to [0.5, 0.9).
|
|
78
|
+
score = Math.max(MIN_AVERAGE_SCORE, Math.min(MAX_AVERAGE_SCORE, complementaryPercentile));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Failing. Clamp to [0, 0.5).
|
|
82
|
+
score = Math.max(0, Math.min(MAX_FAILING_SCORE, complementaryPercentile));
|
|
83
|
+
}
|
|
84
|
+
return score;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=Statistics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Statistics.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Statistics.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\n// Lifted from Lighthouse: https://github.com/GoogleChrome/lighthouse/blob/36cac182a6c637b1671c57326d7c0241633d0076/shared/statistics.js\n\n/**\n * @license\n * Copyright 2017 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// The exact double values for the max and min scores possible in each range.\nconst MIN_PASSING_SCORE = 0.90000000000000002220446049250313080847263336181640625;\nconst MAX_AVERAGE_SCORE = 0.899999999999999911182158029987476766109466552734375;\nconst MIN_AVERAGE_SCORE = 0.5;\nconst MAX_FAILING_SCORE = 0.499999999999999944488848768742172978818416595458984375;\n\n/**\n * Approximates the Gauss error function, the probability that a random variable\n * from the standard normal distribution lies within [-x, x]. Moved from\n * traceviewer.b.math.erf, based on Abramowitz and Stegun, formula 7.1.26.\n */\nfunction erf(x: number): number {\n // erf(-x) = -erf(x);\n const sign = Math.sign(x);\n x = Math.abs(x);\n\n const a1 = 0.254829592;\n const a2 = -0.284496736;\n const a3 = 1.421413741;\n const a4 = -1.453152027;\n const a5 = 1.061405429;\n const p = 0.3275911;\n const t = 1 / (1 + p * x);\n const y = t * (a1 + t * (a2 + t * (a3 + t * (a4 + t * a5))));\n return sign * (1 - y * Math.exp(-x * x));\n}\n\n/**\n * Returns the score (1 - percentile) of `value` in a log-normal distribution\n * specified by the `median` value, at which the score will be 0.5, and a 10th\n * percentile value, at which the score will be 0.9. The score represents the\n * amount of the distribution greater than `value`. All values should be in the\n * same units (e.g. milliseconds). See\n * https://www.desmos.com/calculator/o98tbeyt1t\n * for an interactive view of the relationship between these parameters and the\n * typical parameterization (location and shape) of the log-normal distribution.\n */\nexport function getLogNormalScore({median, p10}: {median: number, p10: number}, value: number): number {\n // Required for the log-normal distribution.\n if (median <= 0) {\n throw new Error('median must be greater than zero');\n }\n if (p10 <= 0) {\n throw new Error('p10 must be greater than zero');\n }\n // Not strictly required, but if p10 > median, it flips around and becomes the p90 point.\n if (p10 >= median) {\n throw new Error('p10 must be less than the median');\n }\n\n // Non-positive values aren't in the distribution, so always 1.\n if (value <= 0) {\n return 1;\n }\n\n // Closest double to `erfc-1(1/5)`.\n const INVERSE_ERFC_ONE_FIFTH = 0.9061938024368232;\n\n // Shape (σ) is `|log(p10/median) / (sqrt(2)*erfc^-1(1/5))|` and\n // standardizedX is `1/2 erfc(log(value/median) / (sqrt(2)*σ))`, so simplify a bit.\n const xRatio = Math.max(Number.MIN_VALUE, value / median); // value and median are > 0, so is ratio.\n const xLogRatio = Math.log(xRatio);\n const p10Ratio = Math.max(Number.MIN_VALUE, p10 / median); // p10 and median are > 0, so is ratio.\n const p10LogRatio = -Math.log(p10Ratio); // negate to keep σ positive.\n const standardizedX = xLogRatio * INVERSE_ERFC_ONE_FIFTH / p10LogRatio;\n const complementaryPercentile = (1 - erf(standardizedX)) / 2;\n\n // Clamp to avoid floating-point out-of-bounds issues and keep score in expected range.\n let score;\n if (value <= p10) {\n // Passing. Clamp to [0.9, 1].\n score = Math.max(MIN_PASSING_SCORE, Math.min(1, complementaryPercentile));\n } else if (value <= median) {\n // Average. Clamp to [0.5, 0.9).\n score = Math.max(MIN_AVERAGE_SCORE, Math.min(MAX_AVERAGE_SCORE, complementaryPercentile));\n } else {\n // Failing. Clamp to [0, 0.5).\n score = Math.max(0, Math.min(MAX_FAILING_SCORE, complementaryPercentile));\n }\n return score;\n}\n"]}
|
|
@@ -4,8 +4,8 @@ import { type InsightModel, type InsightSetContext, type RequiredData } from './
|
|
|
4
4
|
export declare function deps(): ['Meta', 'NetworkRequests', 'Renderer', 'ImagePainting'];
|
|
5
5
|
export type ThirdPartiesInsightModel = InsightModel<{
|
|
6
6
|
entityByRequest: Map<Types.Events.SyntheticNetworkRequest, Extras.ThirdParties.Entity>;
|
|
7
|
-
requestsByEntity: Map<Extras.ThirdParties.Entity, Types.Events.
|
|
8
|
-
|
|
7
|
+
requestsByEntity: Map<Extras.ThirdParties.Entity, Types.Events.Event[]>;
|
|
8
|
+
summaryByEvent: Map<Types.Events.Event, Extras.ThirdParties.Summary>;
|
|
9
9
|
summaryByEntity: Map<Extras.ThirdParties.Entity, Extras.ThirdParties.Summary>;
|
|
10
10
|
/** The entity for this navigation's URL. Any other entity is from a third party. */
|
|
11
11
|
firstPartyEntity?: Extras.ThirdParties.Entity;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// import * as i18n from '../../../core/i18n/i18n.js';
|
|
5
5
|
import * as ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';
|
|
6
6
|
import * as Extras from '../extras/extras.js';
|
|
7
|
+
import * as Handlers from '../handlers/handlers.js';
|
|
7
8
|
import * as Helpers from '../helpers/helpers.js';
|
|
8
9
|
import { InsightCategory } from './types.js';
|
|
9
10
|
const UIStrings = {
|
|
@@ -23,7 +24,7 @@ export function deps() {
|
|
|
23
24
|
}
|
|
24
25
|
function getRelatedEvents(summaries, firstPartyEntity) {
|
|
25
26
|
const events = [];
|
|
26
|
-
for (const [entity, requests] of summaries.
|
|
27
|
+
for (const [entity, requests] of summaries.eventsByEntity.entries()) {
|
|
27
28
|
if (entity !== firstPartyEntity) {
|
|
28
29
|
events.push(...requests);
|
|
29
30
|
}
|
|
@@ -52,12 +53,12 @@ export function generateInsight(parsedTrace, context) {
|
|
|
52
53
|
const { entityByRequest, madeUpEntityCache, summaries } = Extras.ThirdParties.getSummariesAndEntitiesForTraceBounds(parsedTrace, context.bounds, networkRequests);
|
|
53
54
|
const firstPartyUrl = context.navigation?.args.data?.documentLoaderURL ?? parsedTrace.Meta.mainFrameURL;
|
|
54
55
|
const firstPartyEntity = ThirdPartyWeb.ThirdPartyWeb.getEntity(firstPartyUrl) ||
|
|
55
|
-
|
|
56
|
+
Handlers.Helpers.makeUpEntity(madeUpEntityCache, firstPartyUrl);
|
|
56
57
|
return finalize({
|
|
57
58
|
relatedEvents: getRelatedEvents(summaries, firstPartyEntity),
|
|
58
59
|
entityByRequest,
|
|
59
|
-
requestsByEntity: summaries.
|
|
60
|
-
|
|
60
|
+
requestsByEntity: summaries.eventsByEntity,
|
|
61
|
+
summaryByEvent: summaries.byEvent,
|
|
61
62
|
summaryByEntity: summaries.byEntity,
|
|
62
63
|
firstPartyEntity,
|
|
63
64
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThirdParties.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/ThirdParties.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,IAAI,MAAM,4BAA4B,CAAC;AACnD,OAAO,KAAK,aAAa,MAAM,yDAAyD,CAAC;AACzF,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ThirdParties.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/ThirdParties.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 ThirdPartyWeb from '../../../third_party/third-party-web/third-party-web.js';\nimport * as Extras from '../extras/extras.js';\nimport * as Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * as Types from '../types/types.js';\n\nimport {InsightCategory, type InsightModel, type InsightSetContext, type RequiredData} from './types.js';\n\nconst UIStrings = {\n /** 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\"). */\n title: 'Third parties',\n /**\n * @description Description of a DevTools insight that identifies the code on the page that the user doesn't control.\n * This is displayed after a user expands the section to see more. No character length limits.\n */\n description: 'Third party code can significantly impact load performance. ' +\n '[Reduce and defer loading of third party code](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/loading-third-party-javascript/) to prioritize your page\\'s content.',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/ThirdParties.ts', UIStrings);\nconst i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport function deps(): ['Meta', 'NetworkRequests', 'Renderer', 'ImagePainting'] {\n return ['Meta', 'NetworkRequests', 'Renderer', 'ImagePainting'];\n}\n\nexport type ThirdPartiesInsightModel = InsightModel<{\n entityByRequest: Map<Types.Events.SyntheticNetworkRequest, Extras.ThirdParties.Entity>,\n requestsByEntity: Map<Extras.ThirdParties.Entity, Types.Events.Event[]>,\n summaryByEvent: Map<Types.Events.Event, Extras.ThirdParties.Summary>,\n summaryByEntity: Map<Extras.ThirdParties.Entity, Extras.ThirdParties.Summary>,\n /** The entity for this navigation's URL. Any other entity is from a third party. */\n firstPartyEntity?: Extras.ThirdParties.Entity,\n}>;\n\nfunction getRelatedEvents(\n summaries: Extras.ThirdParties.SummaryMaps,\n firstPartyEntity: Extras.ThirdParties.Entity|undefined): Types.Events.Event[] {\n const events = [];\n\n for (const [entity, requests] of summaries.eventsByEntity.entries()) {\n if (entity !== firstPartyEntity) {\n events.push(...requests);\n }\n }\n\n return events;\n}\n\nfunction finalize(partialModel: Omit<ThirdPartiesInsightModel, 'title'|'description'|'category'|'shouldShow'>):\n ThirdPartiesInsightModel {\n return {\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.ALL,\n shouldShow:\n Boolean([...partialModel.summaryByEntity.entries()].find(kv => kv[0] !== partialModel.firstPartyEntity)),\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): ThirdPartiesInsightModel {\n const networkRequests = parsedTrace.NetworkRequests.byTime.filter(event => {\n if (!context.navigation) {\n return false;\n }\n\n if (event.args.data.frame !== context.frameId) {\n return false;\n }\n\n return Helpers.Timing.eventIsInBounds(event, context.bounds);\n });\n\n const {entityByRequest, madeUpEntityCache, summaries} = Extras.ThirdParties.getSummariesAndEntitiesForTraceBounds(\n parsedTrace as Handlers.Types.ParsedTrace, context.bounds, networkRequests);\n\n const firstPartyUrl = context.navigation?.args.data?.documentLoaderURL ?? parsedTrace.Meta.mainFrameURL;\n const firstPartyEntity = ThirdPartyWeb.ThirdPartyWeb.getEntity(firstPartyUrl) ||\n Handlers.Helpers.makeUpEntity(madeUpEntityCache, firstPartyUrl);\n\n return finalize({\n relatedEvents: getRelatedEvents(summaries, firstPartyEntity),\n entityByRequest,\n requestsByEntity: summaries.eventsByEntity,\n summaryByEvent: summaries.byEvent,\n summaryByEntity: summaries.byEntity,\n firstPartyEntity,\n });\n}\n"]}
|
|
@@ -6,11 +6,11 @@ import * as Helpers from '../helpers/helpers.js';
|
|
|
6
6
|
import { InsightCategory, InsightWarning, } from './types.js';
|
|
7
7
|
const UIStrings = {
|
|
8
8
|
/** Title of an insight that provides details about if the page's viewport is optimized for mobile viewing. */
|
|
9
|
-
title: '
|
|
9
|
+
title: 'Optimize viewport for mobile',
|
|
10
10
|
/**
|
|
11
11
|
* @description Text to tell the user how a viewport meta element can improve performance. \xa0 is a non-breaking space
|
|
12
12
|
*/
|
|
13
|
-
description: '
|
|
13
|
+
description: 'Tap interactions may be [delayed by up to 300\xA0ms](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/) if the viewport is not optimized for mobile.',
|
|
14
14
|
};
|
|
15
15
|
// const str_ = i18n.i18n.registerUIStrings('models/trace/insights/Viewport.ts', UIStrings);
|
|
16
16
|
const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Viewport.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Viewport.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,IAAI,MAAM,4BAA4B,CAAC;AACnD,OAAO,KAAK,OAAO,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EACL,eAAe,EAGf,cAAc,GAEf,MAAM,YAAY,CAAC;AAEpB,MAAM,SAAS,GAAG;IAChB,8GAA8G;IAC9G,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"Viewport.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/Viewport.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,IAAI,MAAM,4BAA4B,CAAC;AACnD,OAAO,KAAK,OAAO,MAAM,uBAAuB,CAAC;AAGjD,OAAO,EACL,eAAe,EAGf,cAAc,GAEf,MAAM,YAAY,CAAC;AAEpB,MAAM,SAAS,GAAG;IAChB,8GAA8G;IAC9G,KAAK,EAAE,8BAA8B;IACrC;;OAEG;IACH,WAAW,EACP,iKAAiK;CACtK,CAAC;AAEF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,mCAAmC,EAAE,SAAS,CAAC,CAAC;AACzF,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAEtE,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AACtC,CAAC;AAOD,SAAS,QAAQ,CAAC,YAAuF;IAEvG,OAAO;QACL,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,eAAe,KAAK,KAAK;QAClD,GAAG,YAAY;KAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC3B,WAAsC,EAAE,OAA0B;IACpE,MAAM,gBAAgB,GAAG,WAAW,CAAC,gBAAgB,CAAC,gCAAgC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;QACpG,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;YACzC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC;QAC7B,uCAAuC;QACvC,OAAO,QAAQ,CAAC;YACd,eAAe,EAAE,IAAI;YACrB,QAAQ,EAAE,CAAC,cAAc,CAAC,SAAS,CAAC;SACrC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,aAAa,GAAG,WAAW,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACtF,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,wDAAwD;IACxD,KAAK,MAAM,KAAK,IAAI,gBAAgB,EAAE,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACpC,OAAO,QAAQ,CAAC;gBACd,eAAe,EAAE,KAAK;gBACtB,aAAa;gBACb,aAAa,EAAE,EAAC,GAAG,EAAE,GAAgC,EAAC;aACvD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;QACd,eAAe,EAAE,IAAI;QACrB,aAAa;KACd,CAAC,CAAC;AACL,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\nimport * as i18n from '../../../core/i18n/i18n.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport type * 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 /** Title of an insight that provides details about if the page's viewport is optimized for mobile viewing. */\n title: 'Optimize viewport for mobile',\n /**\n * @description Text to tell the user how a viewport meta element can improve performance. \\xa0 is a non-breaking space\n */\n description:\n 'Tap interactions may be [delayed by up to 300\\xA0ms](https://developer.chrome.com/blog/300ms-tap-delay-gone-away/) if the viewport is not optimized for mobile.',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/Viewport.ts', UIStrings);\nconst i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport function deps(): ['Meta', 'UserInteractions'] {\n return ['Meta', 'UserInteractions'];\n}\n\nexport type ViewportInsightModel = InsightModel<{\n mobileOptimized: boolean | null,\n viewportEvent?: Types.Events.ParseMetaViewport,\n}>;\n\nfunction finalize(partialModel: Omit<ViewportInsightModel, 'title'|'description'|'category'|'shouldShow'>):\n ViewportInsightModel {\n return {\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.INP,\n shouldShow: partialModel.mobileOptimized === false,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): ViewportInsightModel {\n const compositorEvents = parsedTrace.UserInteractions.beginCommitCompositorFrameEvents.filter(event => {\n if (event.args.frame !== context.frameId) {\n return false;\n }\n\n return Helpers.Timing.eventIsInBounds(event, context.bounds);\n });\n\n if (!compositorEvents.length) {\n // Trace doesn't have the data we need.\n return finalize({\n mobileOptimized: null,\n warnings: [InsightWarning.NO_LAYOUT],\n });\n }\n\n const viewportEvent = parsedTrace.UserInteractions.parseMetaViewportEvents.find(event => {\n if (event.args.data.frame !== context.frameId) {\n return false;\n }\n\n return Helpers.Timing.eventIsInBounds(event, context.bounds);\n });\n\n // Returns true only if all events are mobile optimized.\n for (const event of compositorEvents) {\n if (!event.args.is_mobile_optimized) {\n return finalize({\n mobileOptimized: false,\n viewportEvent,\n metricSavings: {INP: 300 as Types.Timing.MilliSeconds},\n });\n }\n }\n\n return finalize({\n mobileOptimized: true,\n viewportEvent,\n });\n}\n"]}
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"../../../../../../../front_end/models/trace/insights/insights.ts",
|
|
33
33
|
"../../../../../../../front_end/legacy/legacy-defs.d.ts",
|
|
34
34
|
"../../../../../../../front_end/global_typings/global_defs.d.ts",
|
|
35
|
-
"../../../../../../../node_modules/@types/filesystem/index.d.ts"
|
|
35
|
+
"../../../../../../../node_modules/@types/filesystem/index.d.ts",
|
|
36
|
+
"../../../../../../../node_modules/@types/wicg-task-scheduling/index.d.ts"
|
|
36
37
|
],
|
|
37
38
|
"references": [
|
|
38
39
|
{
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"files": [
|
|
32
32
|
"../../../../../../../front_end/models/trace/insights/CLSCulprits.ts",
|
|
33
33
|
"../../../../../../../front_end/models/trace/insights/Common.ts",
|
|
34
|
+
"../../../../../../../front_end/models/trace/insights/DOMSize.ts",
|
|
34
35
|
"../../../../../../../front_end/models/trace/insights/DocumentLatency.ts",
|
|
35
36
|
"../../../../../../../front_end/models/trace/insights/FontDisplay.ts",
|
|
36
37
|
"../../../../../../../front_end/models/trace/insights/ImageDelivery.ts",
|
|
@@ -40,12 +41,14 @@
|
|
|
40
41
|
"../../../../../../../front_end/models/trace/insights/Models.ts",
|
|
41
42
|
"../../../../../../../front_end/models/trace/insights/RenderBlocking.ts",
|
|
42
43
|
"../../../../../../../front_end/models/trace/insights/SlowCSSSelector.ts",
|
|
44
|
+
"../../../../../../../front_end/models/trace/insights/Statistics.ts",
|
|
43
45
|
"../../../../../../../front_end/models/trace/insights/ThirdParties.ts",
|
|
44
46
|
"../../../../../../../front_end/models/trace/insights/Viewport.ts",
|
|
45
47
|
"../../../../../../../front_end/models/trace/insights/types.ts",
|
|
46
48
|
"../../../../../../../front_end/legacy/legacy-defs.d.ts",
|
|
47
49
|
"../../../../../../../front_end/global_typings/global_defs.d.ts",
|
|
48
|
-
"../../../../../../../node_modules/@types/filesystem/index.d.ts"
|
|
50
|
+
"../../../../../../../node_modules/@types/filesystem/index.d.ts",
|
|
51
|
+
"../../../../../../../node_modules/@types/wicg-task-scheduling/index.d.ts"
|
|
49
52
|
],
|
|
50
53
|
"references": [
|
|
51
54
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/insights.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,YAAY,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 Common from './Common.js';\nexport * as Models from './Models.js';\nexport * as Types from './types.js';\n"]}
|
|
1
|
+
{"version":3,"file":"insights.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/insights.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,KAAK,MAAM,YAAY,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 Common from './Common.js';\nexport * as Models from './Models.js';\nexport * as Statistics from './Statistics.js';\nexport * as Types from './types.js';\n"]}
|
|
@@ -44,13 +44,14 @@ export declare enum InsightCategory {
|
|
|
44
44
|
LCP = "LCP",
|
|
45
45
|
CLS = "CLS"
|
|
46
46
|
}
|
|
47
|
+
export type RelatedEventsMap = Map<Types.Events.Event, string[]>;
|
|
47
48
|
export type InsightModel<R extends Record<string, unknown>> = R & {
|
|
48
49
|
title: string;
|
|
49
50
|
description: string;
|
|
50
51
|
category: InsightCategory;
|
|
51
52
|
/** True if there is anything of interest to display to the user. */
|
|
52
53
|
shouldShow: boolean;
|
|
53
|
-
relatedEvents?: Types.Events.Event[];
|
|
54
|
+
relatedEvents?: RelatedEventsMap | Types.Events.Event[];
|
|
54
55
|
warnings?: InsightWarning[];
|
|
55
56
|
metricSavings?: MetricSavings;
|
|
56
57
|
};
|
|
@@ -59,7 +60,7 @@ export type InsightModel<R extends Record<string, unknown>> = R & {
|
|
|
59
60
|
* this could instead represent the duration from the beginning of the trace up to the first recorded
|
|
60
61
|
* navigation (or the end of the trace).
|
|
61
62
|
*/
|
|
62
|
-
export
|
|
63
|
+
export interface InsightSet {
|
|
63
64
|
/** If for a navigation, this is the navigationId. Else it is Trace.Types.Events.NO_NAVIGATION. */
|
|
64
65
|
id: Types.Events.NavigationId;
|
|
65
66
|
/** The URL to show in the accordion list. */
|
|
@@ -68,7 +69,7 @@ export type InsightSet = {
|
|
|
68
69
|
bounds: Types.Timing.TraceWindowMicroSeconds;
|
|
69
70
|
model: InsightModels;
|
|
70
71
|
navigation?: Types.Events.NavigationStart;
|
|
71
|
-
}
|
|
72
|
+
}
|
|
72
73
|
/**
|
|
73
74
|
* Contains insights for a specific insight set.
|
|
74
75
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAoC7B,MAAM,CAAN,IAAY,cAMX;AAND,WAAY,cAAc;IACxB,iCAAe,CAAA;IACf,mCAAiB,CAAA;IACjB,uEAAuE;IACvE,6DAA2C,CAAA;IAC3C,yCAAuB,CAAA;AACzB,CAAC,EANW,cAAc,KAAd,cAAc,QAMzB;AAYD,MAAM,CAAN,IAAY,eAKX;AALD,WAAY,eAAe;IACzB,8BAAW,CAAA;IACX,8BAAW,CAAA;IACX,8BAAW,CAAA;IACX,8BAAW,CAAA;AACb,CAAC,EALW,eAAe,KAAf,eAAe,QAK1B","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 type * as Common from '../../../core/common/common.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport type * as Types from '../types/types.js';\n\nimport type * as Models from './Models.js';\n\n/**\n * Context for the portion of the trace an insight should look at.\n */\nexport type InsightSetContext = InsightSetContextWithoutNavigation|InsightSetContextWithNavigation;\n\nexport interface InsightSetContextWithoutNavigation {\n bounds: Types.Timing.TraceWindowMicroSeconds;\n frameId: string;\n navigation?: never;\n}\n\nexport interface InsightSetContextWithNavigation {\n bounds: Types.Timing.TraceWindowMicroSeconds;\n frameId: string;\n navigation: Types.Events.NavigationStart;\n navigationId: string;\n lantern?: LanternContext;\n}\n\nexport interface LanternContext {\n graph: Lantern.Graph.Node<Types.Events.SyntheticNetworkRequest>;\n simulator: Lantern.Simulation.Simulator<Types.Events.SyntheticNetworkRequest>;\n metrics: Record<string, Lantern.Metrics.MetricResult>;\n}\n\nexport type InsightModelsType = typeof Models;\n\nexport enum InsightWarning {\n NO_FP = 'NO_FP',\n NO_LCP = 'NO_LCP',\n // No network request could be identified as the primary HTML document.\n NO_DOCUMENT_REQUEST = 'NO_DOCUMENT_REQUEST',\n NO_LAYOUT = 'NO_LAYOUT',\n}\n\nexport interface MetricSavings {\n /* eslint-disable @typescript-eslint/naming-convention */\n FCP?: Types.Timing.MilliSeconds;\n LCP?: Types.Timing.MilliSeconds;\n TBT?: Types.Timing.MilliSeconds;\n CLS?: number;\n INP?: Types.Timing.MilliSeconds;\n /* eslint-enable @typescript-eslint/naming-convention */\n}\n\nexport enum InsightCategory {\n ALL = 'All',\n INP = 'INP',\n LCP = 'LCP',\n CLS = 'CLS',\n}\n\nexport type InsightModel<R extends Record<string, unknown>> = R&{\n title: Common.UIString.LocalizedString,\n description: Common.UIString.LocalizedString,\n category: InsightCategory,\n /** True if there is anything of interest to display to the user. */\n shouldShow: boolean,\n relatedEvents?: Types.Events.Event[],\n warnings?: InsightWarning[],\n metricSavings?: MetricSavings,\n};\n\n/**\n * Contains insights for a specific navigation. If a trace began after a navigation already started,\n * this could instead represent the duration from the beginning of the trace up to the first recorded\n * navigation (or the end of the trace).\n */\nexport
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAoC7B,MAAM,CAAN,IAAY,cAMX;AAND,WAAY,cAAc;IACxB,iCAAe,CAAA;IACf,mCAAiB,CAAA;IACjB,uEAAuE;IACvE,6DAA2C,CAAA;IAC3C,yCAAuB,CAAA;AACzB,CAAC,EANW,cAAc,KAAd,cAAc,QAMzB;AAYD,MAAM,CAAN,IAAY,eAKX;AALD,WAAY,eAAe;IACzB,8BAAW,CAAA;IACX,8BAAW,CAAA;IACX,8BAAW,CAAA;IACX,8BAAW,CAAA;AACb,CAAC,EALW,eAAe,KAAf,eAAe,QAK1B","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 type * as Common from '../../../core/common/common.js';\nimport type * as Handlers from '../handlers/handlers.js';\nimport type * as Lantern from '../lantern/lantern.js';\nimport type * as Types from '../types/types.js';\n\nimport type * as Models from './Models.js';\n\n/**\n * Context for the portion of the trace an insight should look at.\n */\nexport type InsightSetContext = InsightSetContextWithoutNavigation|InsightSetContextWithNavigation;\n\nexport interface InsightSetContextWithoutNavigation {\n bounds: Types.Timing.TraceWindowMicroSeconds;\n frameId: string;\n navigation?: never;\n}\n\nexport interface InsightSetContextWithNavigation {\n bounds: Types.Timing.TraceWindowMicroSeconds;\n frameId: string;\n navigation: Types.Events.NavigationStart;\n navigationId: string;\n lantern?: LanternContext;\n}\n\nexport interface LanternContext {\n graph: Lantern.Graph.Node<Types.Events.SyntheticNetworkRequest>;\n simulator: Lantern.Simulation.Simulator<Types.Events.SyntheticNetworkRequest>;\n metrics: Record<string, Lantern.Metrics.MetricResult>;\n}\n\nexport type InsightModelsType = typeof Models;\n\nexport enum InsightWarning {\n NO_FP = 'NO_FP',\n NO_LCP = 'NO_LCP',\n // No network request could be identified as the primary HTML document.\n NO_DOCUMENT_REQUEST = 'NO_DOCUMENT_REQUEST',\n NO_LAYOUT = 'NO_LAYOUT',\n}\n\nexport interface MetricSavings {\n /* eslint-disable @typescript-eslint/naming-convention */\n FCP?: Types.Timing.MilliSeconds;\n LCP?: Types.Timing.MilliSeconds;\n TBT?: Types.Timing.MilliSeconds;\n CLS?: number;\n INP?: Types.Timing.MilliSeconds;\n /* eslint-enable @typescript-eslint/naming-convention */\n}\n\nexport enum InsightCategory {\n ALL = 'All',\n INP = 'INP',\n LCP = 'LCP',\n CLS = 'CLS',\n}\n\nexport type RelatedEventsMap = Map<Types.Events.Event, string[]>;\n\nexport type InsightModel<R extends Record<string, unknown>> = R&{\n title: Common.UIString.LocalizedString,\n description: Common.UIString.LocalizedString,\n category: InsightCategory,\n /** True if there is anything of interest to display to the user. */\n shouldShow: boolean,\n relatedEvents?: RelatedEventsMap | Types.Events.Event[],\n warnings?: InsightWarning[],\n metricSavings?: MetricSavings,\n};\n\n/**\n * Contains insights for a specific navigation. If a trace began after a navigation already started,\n * this could instead represent the duration from the beginning of the trace up to the first recorded\n * navigation (or the end of the trace).\n */\nexport interface InsightSet {\n /** If for a navigation, this is the navigationId. Else it is Trace.Types.Events.NO_NAVIGATION. */\n id: Types.Events.NavigationId;\n /** The URL to show in the accordion list. */\n url: URL;\n frameId: string;\n bounds: Types.Timing.TraceWindowMicroSeconds;\n model: InsightModels;\n navigation?: Types.Events.NavigationStart;\n}\n\n/**\n * Contains insights for a specific insight set.\n */\nexport type InsightModels = {\n [I in keyof InsightModelsType]: ReturnType<InsightModelsType[I]['generateInsight']>;\n};\n\n/**\n * Contains insights for the entire trace. Insights are mostly grouped by `navigationId`, with one exception:\n *\n * If the analyzed trace started after the navigation, and has meaningful work with that span, there is no\n * navigation to map it to. In this case `Types.Events.NO_NAVIGATION` is used for the key.\n */\nexport type TraceInsightSets = Map<Types.Events.NavigationId, InsightSet>;\n\n/**\n * Represents the narrow set of dependencies defined by an insight's `deps()` function. `Meta` is always included regardless of `deps()`.\n */\nexport type RequiredData<D extends() => Array<keyof typeof Handlers.ModelHandlers>> =\n Handlers.Types.EnabledHandlerDataWithMeta<Pick<typeof Handlers.ModelHandlers, ReturnType<D>[number]>>;\n"]}
|
|
@@ -24,11 +24,11 @@ interface RTTEstimateOptions {
|
|
|
24
24
|
/** Useful for testing to isolate the different methods of estimation. */
|
|
25
25
|
useHeadersEndEstimates?: boolean;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
interface RequestInfo {
|
|
28
28
|
request: Lantern.NetworkRequest;
|
|
29
29
|
timing: Lantern.ResourceTiming;
|
|
30
30
|
connectionReused?: boolean;
|
|
31
|
-
}
|
|
31
|
+
}
|
|
32
32
|
declare class NetworkAnalyzer {
|
|
33
33
|
static get summary(): string;
|
|
34
34
|
static groupByOrigin(records: Lantern.NetworkRequest[]): Map<string, Lantern.NetworkRequest[]>;
|
|
@@ -31,7 +31,7 @@ class UrlUtils {
|
|
|
31
31
|
urlb.hash = '';
|
|
32
32
|
return urla.href === urlb.href;
|
|
33
33
|
}
|
|
34
|
-
catch
|
|
34
|
+
catch {
|
|
35
35
|
return false;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -310,7 +310,6 @@ class NetworkAnalyzer {
|
|
|
310
310
|
const estimatesByOrigin = new Map();
|
|
311
311
|
for (const [origin, originRequests] of groupedByOrigin.entries()) {
|
|
312
312
|
const originEstimates = [];
|
|
313
|
-
// eslint-disable-next-line no-inner-declarations
|
|
314
313
|
function collectEstimates(estimator, multiplier = 1) {
|
|
315
314
|
for (const request of originRequests) {
|
|
316
315
|
const timing = request.timing;
|
|
@@ -470,7 +469,7 @@ class NetworkAnalyzer {
|
|
|
470
469
|
}
|
|
471
470
|
static findLastDocumentForUrl(records, resourceUrl) {
|
|
472
471
|
// equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first
|
|
473
|
-
const matchingRequests = records.filter(request => request.resourceType === 'Document' &&
|
|
472
|
+
const matchingRequests = records.filter(request => request.resourceType === 'Document' && !request.failed &&
|
|
474
473
|
// Note: `request.url` should never have a fragment, else this optimization gives wrong results.
|
|
475
474
|
resourceUrl.startsWith(request.url) && UrlUtils.equalWithExcludedFragments(request.url, resourceUrl));
|
|
476
475
|
return matchingRequests[matchingRequests.length - 1];
|