@paulirish/trace_engine 0.0.42 → 0.0.43
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/locales/en-US.json +0 -12
- package/locales/en-XL.json +0 -12
- package/models/cpu_profile/CPUProfileDataModel.js +2 -2
- package/models/cpu_profile/CPUProfileDataModel.js.map +1 -1
- package/models/trace/LanternComputationData.js +1 -1
- package/models/trace/LanternComputationData.js.map +1 -1
- package/models/trace/extras/TraceTree.d.ts +15 -2
- package/models/trace/extras/TraceTree.js +2 -2
- package/models/trace/extras/TraceTree.js.map +1 -1
- package/models/trace/extras/extras.d.ts +0 -1
- package/models/trace/extras/extras.js +0 -1
- package/models/trace/handlers/MetaHandler.js +3 -3
- package/models/trace/handlers/MetaHandler.js.map +1 -1
- package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
- package/models/trace/handlers/RendererHandler.d.ts +1 -1
- package/models/trace/handlers/RendererHandler.js +1 -1
- package/models/trace/handlers/RendererHandler.js.map +1 -1
- package/models/trace/handlers/ScreenshotsHandler.js +1 -1
- package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
- package/models/trace/helpers/SamplesIntegrator.d.ts +1 -1
- package/models/trace/helpers/SamplesIntegrator.js +2 -2
- package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
- package/models/trace/helpers/Trace.d.ts +1 -1
- package/models/trace/helpers/Trace.js +1 -1
- package/models/trace/helpers/Trace.js.map +1 -1
- package/models/trace/helpers/TreeHelpers.js +1 -1
- package/models/trace/helpers/TreeHelpers.js.map +1 -1
- package/models/trace/insights/CLSCulprits.d.ts +2 -0
- package/models/trace/insights/CLSCulprits.js +41 -8
- package/models/trace/insights/CLSCulprits.js.map +1 -1
- package/models/trace/insights/DocumentLatency.d.ts +2 -12
- package/models/trace/insights/DocumentLatency.js +23 -14
- package/models/trace/insights/DocumentLatency.js.map +1 -1
- package/models/trace/insights/LCPDiscovery.d.ts +2 -14
- package/models/trace/insights/LCPDiscovery.js +8 -16
- package/models/trace/insights/LCPDiscovery.js.map +1 -1
- package/models/trace/insights/types.d.ts +4 -0
- package/models/trace/insights/types.js.map +1 -1
- package/models/trace/lantern/graph/BaseNode.d.ts +1 -1
- package/models/trace/lantern/graph/BaseNode.js +1 -1
- package/models/trace/lantern/graph/BaseNode.js.map +1 -1
- package/models/trace/lantern/metrics/FirstContentfulPaint.d.ts +1 -1
- package/models/trace/lantern/metrics/FirstContentfulPaint.js.map +1 -1
- package/models/trace/lantern/simulation/ConnectionPool.js +1 -1
- package/models/trace/lantern/simulation/ConnectionPool.js.map +1 -1
- package/models/trace/lantern/simulation/Simulator.d.ts +2 -2
- package/models/trace/lantern/simulation/Simulator.js +3 -3
- package/models/trace/lantern/simulation/Simulator.js.map +1 -1
- package/models/trace/lantern/types/Lantern.d.ts +2 -2
- package/models/trace/lantern/types/Lantern.js.map +1 -1
- package/models/trace/root-causes/LayoutShift.d.ts +1 -1
- package/models/trace/root-causes/LayoutShift.js +4 -4
- package/models/trace/root-causes/LayoutShift.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +2 -2
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/package.json +1 -1
- package/core/platform/PromiseUtilities.d.ts +0 -10
- package/core/platform/PromiseUtilities.js +0 -18
- package/core/platform/PromiseUtilities.js.map +0 -1
- package/core/platform/SetUtilities.d.ts +0 -2
- package/core/platform/SetUtilities.js +0 -23
- package/core/platform/SetUtilities.js.map +0 -1
- package/models/trace/EntriesFilter.d.ts +0 -72
- package/models/trace/EntriesFilter.js +0 -296
- package/models/trace/EntriesFilter.js.map +0 -1
- package/models/trace/LegacyTracingModel.js.map +0 -1
- package/models/trace/extras/URLForEntry.d.ts +0 -13
- package/models/trace/extras/URLForEntry.js +0 -44
- package/models/trace/extras/URLForEntry.js.map +0 -1
- package/models/trace/handlers/EnhancedTracesHandler.d.ts +0 -48
- package/models/trace/handlers/EnhancedTracesHandler.js +0 -165
- package/models/trace/handlers/EnhancedTracesHandler.js.map +0 -1
- package/models/trace/insights/CumulativeLayoutShift.d.ts +0 -34
- package/models/trace/insights/CumulativeLayoutShift.js +0 -209
- package/models/trace/insights/CumulativeLayoutShift.js.map +0 -1
- package/models/trace/insights/InsightRunners.d.ts +0 -6
- package/models/trace/insights/InsightRunners.js +0 -10
- package/models/trace/insights/InsightRunners.js.map +0 -1
- package/models/trace/insights/LargestContentfulPaint.d.ts +0 -25
- package/models/trace/insights/LargestContentfulPaint.js +0 -93
- package/models/trace/insights/LargestContentfulPaint.js.map +0 -1
- package/models/trace/lantern/BaseNode.d.ts +0 -91
- package/models/trace/lantern/BaseNode.js +0 -268
- package/models/trace/lantern/BaseNode.js.map +0 -1
- package/models/trace/lantern/CPUNode.d.ts +0 -24
- package/models/trace/lantern/CPUNode.js +0 -64
- package/models/trace/lantern/CPUNode.js.map +0 -1
- package/models/trace/lantern/LanternError.d.ts +0 -3
- package/models/trace/lantern/LanternError.js +0 -7
- package/models/trace/lantern/LanternError.js.map +0 -1
- package/models/trace/lantern/MetricsModule.d.ts +0 -11
- package/models/trace/lantern/MetricsModule.js +0 -14
- package/models/trace/lantern/MetricsModule.js.map +0 -1
- package/models/trace/lantern/NetworkNode.d.ts +0 -22
- package/models/trace/lantern/NetworkNode.js +0 -83
- package/models/trace/lantern/NetworkNode.js.map +0 -1
- package/models/trace/lantern/PageDependencyGraph.d.ts +0 -43
- package/models/trace/lantern/PageDependencyGraph.js +0 -509
- package/models/trace/lantern/PageDependencyGraph.js.map +0 -1
- package/models/trace/lantern/SimulationModule.d.ts +0 -17
- package/models/trace/lantern/SimulationModule.js +0 -13
- package/models/trace/lantern/SimulationModule.js.map +0 -1
- package/models/trace/lantern/simulation/NetworkAnalyzer.d.ts +0 -112
- package/models/trace/lantern/simulation/NetworkAnalyzer.js +0 -486
- package/models/trace/lantern/simulation/NetworkAnalyzer.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CLSCulprits.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/CLSCulprits.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 Platform from '../../../core/platform/platform.js';\nimport type * as Protocol from '../../../generated/protocol.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 type PartialInsightModel,\n type RequiredData\n} from './types.js';\n\nexport const UIStrings = {\n /** Title of an insight that provides details about why elements shift/move on the page. The causes for these shifts are referred to as culprits (\"reasons\"). */\n title: 'Layout shift culprits',\n /**\n * @description Description of a DevTools insight that identifies the reasons that elements shift on the page.\n * This is displayed after a user expands the section to see more. No character length limits.\n */\n description:\n 'Layout shifts occur when elements move absent any user interaction. [Investigate the causes of layout shifts](https://web.dev/articles/optimize-cls), such as elements being added, removed, or their fonts changing as the page loads.',\n /**\n *@description Text indicating the worst layout shift cluster.\n */\n worstLayoutShiftCluster: 'Worst layout shift cluster',\n /**\n * @description Text indicating the worst layout shift cluster.\n */\n worstCluster: 'Worst cluster',\n /**\n * @description Text indicating a layout shift cluster and its start time.\n * @example {32 ms} PH1\n */\n layoutShiftCluster: 'Layout shift cluster @ {PH1}',\n /**\n *@description Text indicating the biggest reasons for the layout shifts.\n */\n topCulprits: 'Top layout shift culprits',\n /**\n * @description Text for a culprit type of Injected iframe.\n */\n injectedIframe: 'Injected iframe',\n /**\n * @description Text for a culprit type of Font request.\n */\n fontRequest: 'Font request',\n /**\n * @description Text for a culprit type of Animation.\n */\n animation: 'Animation',\n /**\n * @description Text for a culprit type of Unsized images.\n */\n unsizedImages: 'Unsized Images',\n /**\n * @description Text status when there were no layout shifts detected.\n */\n noLayoutShifts: 'No layout shifts',\n /**\n * @description Text status when there no layout shifts culprits/root causes were found.\n */\n noCulprits: 'Could not detect any layout shift culprits',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/CLSCulprits.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type CLSCulpritsInsightModel = InsightModel<typeof UIStrings, {\n animationFailures: readonly NoncompositedAnimationFailure[],\n shifts: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>,\n clusters: Types.Events.SyntheticLayoutShiftCluster[],\n worstCluster: Types.Events.SyntheticLayoutShiftCluster | undefined,\n}>;\n\nexport function deps(): ['Meta', 'Animations', 'LayoutShifts', 'NetworkRequests'] {\n return ['Meta', 'Animations', 'LayoutShifts', 'NetworkRequests'];\n}\n\nexport const enum AnimationFailureReasons {\n ACCELERATED_ANIMATIONS_DISABLED = 'ACCELERATED_ANIMATIONS_DISABLED',\n EFFECT_SUPPRESSED_BY_DEVTOOLS = 'EFFECT_SUPPRESSED_BY_DEVTOOLS',\n INVALID_ANIMATION_OR_EFFECT = 'INVALID_ANIMATION_OR_EFFECT',\n EFFECT_HAS_UNSUPPORTED_TIMING_PARAMS = 'EFFECT_HAS_UNSUPPORTED_TIMING_PARAMS',\n EFFECT_HAS_NON_REPLACE_COMPOSITE_MODE = 'EFFECT_HAS_NON_REPLACE_COMPOSITE_MODE',\n TARGET_HAS_INVALID_COMPOSITING_STATE = 'TARGET_HAS_INVALID_COMPOSITING_STATE',\n TARGET_HAS_INCOMPATIBLE_ANIMATIONS = 'TARGET_HAS_INCOMPATIBLE_ANIMATIONS',\n TARGET_HAS_CSS_OFFSET = 'TARGET_HAS_CSS_OFFSET',\n ANIMATION_AFFECTS_NON_CSS_PROPERTIES = 'ANIMATION_AFFECTS_NON_CSS_PROPERTIES',\n TRANSFORM_RELATED_PROPERTY_CANNOT_BE_ACCELERATED_ON_TARGET =\n 'TRANSFORM_RELATED_PROPERTY_CANNOT_BE_ACCELERATED_ON_TARGET',\n TRANSFROM_BOX_SIZE_DEPENDENT = 'TRANSFROM_BOX_SIZE_DEPENDENT',\n FILTER_RELATED_PROPERTY_MAY_MOVE_PIXELS = 'FILTER_RELATED_PROPERTY_MAY_MOVE_PIXELS',\n UNSUPPORTED_CSS_PROPERTY = 'UNSUPPORTED_CSS_PROPERTY',\n MIXED_KEYFRAME_VALUE_TYPES = 'MIXED_KEYFRAME_VALUE_TYPES',\n TIMELINE_SOURCE_HAS_INVALID_COMPOSITING_STATE = 'TIMELINE_SOURCE_HAS_INVALID_COMPOSITING_STATE',\n ANIMATION_HAS_NO_VISIBLE_CHANGE = 'ANIMATION_HAS_NO_VISIBLE_CHANGE',\n AFFECTS_IMPORTANT_PROPERTY = 'AFFECTS_IMPORTANT_PROPERTY',\n SVG_TARGET_HAS_INDEPENDENT_TRANSFORM_PROPERTY = 'SVG_TARGET_HAS_INDEPENDENT_TRANSFORM_PROPERTY',\n}\n\nexport interface NoncompositedAnimationFailure {\n /**\n * Animation name.\n */\n name?: string;\n /**\n * Failure reason based on mask number defined in\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22.\n */\n failureReasons: AnimationFailureReasons[];\n /**\n * Unsupported properties.\n */\n unsupportedProperties?: Types.Events.Animation['args']['data']['unsupportedProperties'];\n /**\n * Animation event.\n */\n animation?: Types.Events.SyntheticAnimationPair;\n}\n\n/**\n * Each failure reason is represented by a bit flag. The bit shift operator '<<' is used to define\n * which bit corresponds to each failure reason.\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22\n */\nconst ACTIONABLE_FAILURE_REASONS: Array<{\n flag: number,\n failure: AnimationFailureReasons,\n}> =\n [\n {\n flag: 1 << 0,\n failure: AnimationFailureReasons.ACCELERATED_ANIMATIONS_DISABLED,\n },\n {\n flag: 1 << 1,\n failure: AnimationFailureReasons.EFFECT_SUPPRESSED_BY_DEVTOOLS,\n },\n {\n flag: 1 << 2,\n failure: AnimationFailureReasons.INVALID_ANIMATION_OR_EFFECT,\n },\n {\n flag: 1 << 3,\n failure: AnimationFailureReasons.EFFECT_HAS_UNSUPPORTED_TIMING_PARAMS,\n },\n {\n flag: 1 << 4,\n failure: AnimationFailureReasons.EFFECT_HAS_NON_REPLACE_COMPOSITE_MODE,\n },\n {\n flag: 1 << 5,\n failure: AnimationFailureReasons.TARGET_HAS_INVALID_COMPOSITING_STATE,\n },\n {\n flag: 1 << 6,\n failure: AnimationFailureReasons.TARGET_HAS_INCOMPATIBLE_ANIMATIONS,\n },\n {\n flag: 1 << 7,\n failure: AnimationFailureReasons.TARGET_HAS_CSS_OFFSET,\n },\n // The failure 1 << 8 is marked as obsolete in Blink\n {\n flag: 1 << 9,\n failure: AnimationFailureReasons.ANIMATION_AFFECTS_NON_CSS_PROPERTIES,\n },\n {\n flag: 1 << 10,\n failure: AnimationFailureReasons.TRANSFORM_RELATED_PROPERTY_CANNOT_BE_ACCELERATED_ON_TARGET,\n },\n {\n flag: 1 << 11,\n failure: AnimationFailureReasons.TRANSFROM_BOX_SIZE_DEPENDENT,\n },\n {\n flag: 1 << 12,\n failure: AnimationFailureReasons.FILTER_RELATED_PROPERTY_MAY_MOVE_PIXELS,\n },\n {\n flag: 1 << 13,\n failure: AnimationFailureReasons.UNSUPPORTED_CSS_PROPERTY,\n },\n // The failure 1 << 14 is marked as obsolete in Blink\n {\n flag: 1 << 15,\n failure: AnimationFailureReasons.MIXED_KEYFRAME_VALUE_TYPES,\n },\n {\n flag: 1 << 16,\n failure: AnimationFailureReasons.TIMELINE_SOURCE_HAS_INVALID_COMPOSITING_STATE,\n },\n {\n flag: 1 << 17,\n failure: AnimationFailureReasons.ANIMATION_HAS_NO_VISIBLE_CHANGE,\n },\n {\n flag: 1 << 18,\n failure: AnimationFailureReasons.AFFECTS_IMPORTANT_PROPERTY,\n },\n {\n flag: 1 << 19,\n failure: AnimationFailureReasons.SVG_TARGET_HAS_INDEPENDENT_TRANSFORM_PROPERTY,\n },\n ] as const;\n\n// 500ms window.\n// Use this window to consider events and requests that may have caused a layout shift.\nconst ROOT_CAUSE_WINDOW = Helpers.Timing.secondsToMicro(Types.Timing.Seconds(0.5));\n\nexport interface LayoutShiftRootCausesData {\n iframeIds: string[];\n fontRequests: Types.Events.SyntheticNetworkRequest[];\n nonCompositedAnimations: NoncompositedAnimationFailure[];\n unsizedImages: Protocol.DOM.BackendNodeId[];\n}\n\n/**\n * Returns if an event happens within the root cause window, before the target event.\n * ROOT_CAUSE_WINDOW v target event\n * |------------------------|=======================\n */\nfunction isInRootCauseWindow(event: Types.Events.Event, targetEvent: Types.Events.Event): boolean {\n const eventEnd = event.dur ? event.ts + event.dur : event.ts;\n return eventEnd < targetEvent.ts && eventEnd >= targetEvent.ts - ROOT_CAUSE_WINDOW;\n}\n\nexport function getNonCompositedFailure(animationEvent: Types.Events.SyntheticAnimationPair):\n NoncompositedAnimationFailure[] {\n const failures: NoncompositedAnimationFailure[] = [];\n const beginEvent = animationEvent.args.data.beginEvent;\n const instantEvents = animationEvent.args.data.instantEvents || [];\n /**\n * Animation events containing composite information are ASYNC_NESTABLE_INSTANT ('n').\n * An animation may also contain multiple 'n' events, so we look through those with useful non-composited data.\n */\n for (const event of instantEvents) {\n const failureMask = event.args.data.compositeFailed;\n const unsupportedProperties = event.args.data.unsupportedProperties;\n if (!failureMask) {\n continue;\n }\n const failureReasons =\n ACTIONABLE_FAILURE_REASONS.filter(reason => failureMask & reason.flag).map(reason => reason.failure);\n const failure: NoncompositedAnimationFailure = {\n name: beginEvent.args.data.displayName,\n failureReasons,\n unsupportedProperties,\n animation: animationEvent,\n };\n failures.push(failure);\n }\n return failures;\n}\n\nfunction getNonCompositedFailureRootCauses(\n animationEvents: Types.Events.SyntheticAnimationPair[],\n prePaintEvents: Types.Events.PrePaint[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>,\n ): NoncompositedAnimationFailure[] {\n const allAnimationFailures: NoncompositedAnimationFailure[] = [];\n for (const animation of animationEvents) {\n /**\n * Animation events containing composite information are ASYNC_NESTABLE_INSTANT ('n').\n * An animation may also contain multiple 'n' events, so we look through those with useful non-composited data.\n */\n const failures = getNonCompositedFailure(animation);\n if (!failures) {\n continue;\n }\n allAnimationFailures.push(...failures);\n\n const nextPrePaint = getNextEvent(prePaintEvents, animation) as Types.Events.PrePaint | null;\n // If no following prePaint, this is not a root cause.\n if (!nextPrePaint) {\n continue;\n }\n\n // If the animation event is outside the ROOT_CAUSE_WINDOW, it could not be a root cause.\n if (!isInRootCauseWindow(animation, nextPrePaint)) {\n continue;\n }\n\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n // if no layout shift(s), this is not a root cause.\n if (!shifts) {\n continue;\n }\n\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n rootCausesForShift.nonCompositedAnimations.push(...failures);\n }\n }\n\n return allAnimationFailures;\n}\n\n/**\n * Given an array of layout shift and PrePaint events, returns a mapping from\n * PrePaint events to layout shifts dispatched within it.\n */\nfunction getShiftsByPrePaintEvents(\n layoutShifts: Types.Events.SyntheticLayoutShift[],\n prePaintEvents: Types.Events.PrePaint[],\n ): Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]> {\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = new Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>();\n\n // Associate all shifts to their corresponding PrePaint.\n for (const prePaintEvent of prePaintEvents) {\n const firstShiftIndex =\n Platform.ArrayUtilities.nearestIndexFromBeginning(layoutShifts, shift => shift.ts >= prePaintEvent.ts);\n if (firstShiftIndex === null) {\n // No layout shifts registered after this PrePaint start. Continue.\n continue;\n }\n for (let i = firstShiftIndex; i < layoutShifts.length; i++) {\n const shift = layoutShifts[i];\n if (shift.ts >= prePaintEvent.ts && shift.ts <= prePaintEvent.ts + prePaintEvent.dur) {\n const shiftsInPrePaint = Platform.MapUtilities.getWithDefault(shiftsByPrePaint, prePaintEvent, () => []);\n shiftsInPrePaint.push(shift);\n }\n if (shift.ts > prePaintEvent.ts + prePaintEvent.dur) {\n // Reached all layoutShifts of this PrePaint. Break out to continue with the next prePaint event.\n break;\n }\n }\n }\n return shiftsByPrePaint;\n}\n\n/**\n * Given a source event list, this returns the first event of that list that directly follows the target event.\n */\nfunction getNextEvent(sourceEvents: Types.Events.Event[], targetEvent: Types.Events.Event): Types.Events.Event|\n undefined {\n const index = Platform.ArrayUtilities.nearestIndexFromBeginning(\n sourceEvents, source => source.ts > targetEvent.ts + (targetEvent.dur || 0));\n // No PrePaint event registered after this event\n if (index === null) {\n return undefined;\n }\n\n return sourceEvents[index];\n}\n\n/**\n * An Iframe is considered a root cause if the iframe event occurs before a prePaint event\n * and within this prePaint event a layout shift(s) occurs.\n */\nfunction getIframeRootCauses(\n iframeCreatedEvents: readonly Types.Events.RenderFrameImplCreateChildFrame[],\n prePaintEvents: Types.Events.PrePaint[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>,\n domLoadingEvents: readonly Types.Events.DomLoading[]):\n Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData> {\n for (const iframeEvent of iframeCreatedEvents) {\n const nextPrePaint = getNextEvent(prePaintEvents, iframeEvent) as Types.Events.PrePaint | null;\n // If no following prePaint, this is not a root cause.\n if (!nextPrePaint) {\n continue;\n }\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n // if no layout shift(s), this is not a root cause.\n if (!shifts) {\n continue;\n }\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n\n // Look for the first dom event that occurs within the bounds of the iframe event.\n // This contains the frame id.\n const domEvent = domLoadingEvents.find(e => {\n const maxIframe = Types.Timing.Micro(iframeEvent.ts + (iframeEvent.dur ?? 0));\n return e.ts >= iframeEvent.ts && e.ts <= maxIframe;\n });\n if (domEvent && domEvent.args.frame) {\n rootCausesForShift.iframeIds.push(domEvent.args.frame);\n }\n }\n }\n return rootCausesByShift;\n}\n\n/**\n * An unsized image is considered a root cause if its PaintImage can be correlated to a\n * layout shift. We can correlate PaintImages with unsized images by their matching nodeIds.\n * X <- layout shift\n * |----------------|\n * ^ PrePaint event |-----|\n * ^ PaintImage\n */\nfunction getUnsizedImageRootCauses(\n unsizedImageEvents: readonly Types.Events.LayoutImageUnsized[], paintImageEvents: Types.Events.PaintImage[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>):\n Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData> {\n shiftsByPrePaint.forEach((shifts, prePaint) => {\n const paintImage = getNextEvent(paintImageEvents, prePaint) as Types.Events.PaintImage | null;\n // The unsized image corresponds to this PaintImage.\n const matchingNode =\n unsizedImageEvents.find(unsizedImage => unsizedImage.args.data.nodeId === paintImage?.args.data.nodeId);\n if (!matchingNode) {\n return;\n }\n // The unsized image is a potential root cause of all the shifts of this prePaint.\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n rootCausesForShift.unsizedImages.push(matchingNode.args.data.nodeId);\n }\n });\n return rootCausesByShift;\n}\n\n/**\n * A font request is considered a root cause if the request occurs before a prePaint event\n * and within this prePaint event a layout shift(s) occurs. Additionally, this font request should\n * happen within the ROOT_CAUSE_WINDOW of the prePaint event.\n */\nfunction getFontRootCauses(\n networkRequests: Types.Events.SyntheticNetworkRequest[], prePaintEvents: Types.Events.PrePaint[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>):\n Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData> {\n const fontRequests =\n networkRequests.filter(req => req.args.data.resourceType === 'Font' && req.args.data.mimeType.startsWith('font'));\n\n for (const req of fontRequests) {\n const nextPrePaint = getNextEvent(prePaintEvents, req) as Types.Events.PrePaint | null;\n if (!nextPrePaint) {\n continue;\n }\n\n // If the req is outside the ROOT_CAUSE_WINDOW, it could not be a root cause.\n if (!isInRootCauseWindow(req, nextPrePaint)) {\n continue;\n }\n\n // Get the shifts that belong to this prepaint\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n\n // if no layout shift(s) in this prePaint, the request is not a root cause.\n if (!shifts) {\n continue;\n }\n // Include the root cause to the shifts in this prePaint.\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n rootCausesForShift.fontRequests.push(req);\n }\n }\n return rootCausesByShift;\n}\n\nfunction finalize(partialModel: PartialInsightModel<CLSCulpritsInsightModel>): CLSCulpritsInsightModel {\n let maxScore = 0;\n for (const cluster of partialModel.clusters) {\n if (cluster.clusterCumulativeScore > maxScore) {\n maxScore = cluster.clusterCumulativeScore;\n }\n }\n\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.CLS,\n // TODO: getTopCulprits in component needs to move to model so this can be set properly here.\n shouldShow: maxScore > 0,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): CLSCulpritsInsightModel {\n const isWithinContext = (event: Types.Events.Event): boolean => Helpers.Timing.eventIsInBounds(event, context.bounds);\n\n const compositeAnimationEvents = parsedTrace.Animations.animations.filter(isWithinContext);\n const iframeEvents = parsedTrace.LayoutShifts.renderFrameImplCreateChildFrameEvents.filter(isWithinContext);\n const networkRequests = parsedTrace.NetworkRequests.byTime.filter(isWithinContext);\n const domLoadingEvents = parsedTrace.LayoutShifts.domLoadingEvents.filter(isWithinContext);\n const unsizedImageEvents = parsedTrace.LayoutShifts.layoutImageUnsizedEvents.filter(isWithinContext);\n\n const clusterKey = context.navigation ? context.navigationId : Types.Events.NO_NAVIGATION;\n const clusters = parsedTrace.LayoutShifts.clustersByNavigationId.get(clusterKey) ?? [];\n const clustersByScore = [...clusters].sort((a, b) => b.clusterCumulativeScore - a.clusterCumulativeScore);\n const worstCluster = clustersByScore.at(0);\n const layoutShifts = clusters.flatMap(cluster => cluster.events);\n const prePaintEvents = parsedTrace.LayoutShifts.prePaintEvents.filter(isWithinContext);\n const paintImageEvents = parsedTrace.LayoutShifts.paintImageEvents.filter(isWithinContext);\n\n // Get root causes.\n const rootCausesByShift = new Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>();\n const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);\n\n for (const shift of layoutShifts) {\n rootCausesByShift.set(shift, {iframeIds: [], fontRequests: [], nonCompositedAnimations: [], unsizedImages: []});\n }\n\n // Populate root causes for rootCausesByShift.\n getIframeRootCauses(iframeEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift, domLoadingEvents);\n getFontRootCauses(networkRequests, prePaintEvents, shiftsByPrePaint, rootCausesByShift);\n getUnsizedImageRootCauses(unsizedImageEvents, paintImageEvents, shiftsByPrePaint, rootCausesByShift);\n const animationFailures =\n getNonCompositedFailureRootCauses(compositeAnimationEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift);\n\n const relatedEvents: Types.Events.Event[] = [...layoutShifts];\n if (worstCluster) {\n relatedEvents.push(worstCluster);\n }\n\n return finalize({\n relatedEvents,\n animationFailures,\n shifts: rootCausesByShift,\n clusters,\n worstCluster,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"CLSCulprits.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/CLSCulprits.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 Platform from '../../../core/platform/platform.js';\nimport type * as Protocol from '../../../generated/protocol.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 type PartialInsightModel,\n type RequiredData\n} from './types.js';\n\nexport const UIStrings = {\n /** Title of an insight that provides details about why elements shift/move on the page. The causes for these shifts are referred to as culprits (\"reasons\"). */\n title: 'Layout shift culprits',\n /**\n * @description Description of a DevTools insight that identifies the reasons that elements shift on the page.\n * This is displayed after a user expands the section to see more. No character length limits.\n */\n description:\n 'Layout shifts occur when elements move absent any user interaction. [Investigate the causes of layout shifts](https://web.dev/articles/optimize-cls), such as elements being added, removed, or their fonts changing as the page loads.',\n /**\n *@description Text indicating the worst layout shift cluster.\n */\n worstLayoutShiftCluster: 'Worst layout shift cluster',\n /**\n * @description Text indicating the worst layout shift cluster.\n */\n worstCluster: 'Worst cluster',\n /**\n * @description Text indicating a layout shift cluster and its start time.\n * @example {32 ms} PH1\n */\n layoutShiftCluster: 'Layout shift cluster @ {PH1}',\n /**\n *@description Text indicating the biggest reasons for the layout shifts.\n */\n topCulprits: 'Top layout shift culprits',\n /**\n * @description Text for a culprit type of Injected iframe.\n */\n injectedIframe: 'Injected iframe',\n /**\n * @description Text for a culprit type of Font request.\n */\n fontRequest: 'Font request',\n /**\n * @description Text for a culprit type of Animation.\n */\n animation: 'Animation',\n /**\n * @description Text for a culprit type of Unsized images.\n */\n unsizedImages: 'Unsized Images',\n /**\n * @description Text status when there were no layout shifts detected.\n */\n noLayoutShifts: 'No layout shifts',\n /**\n * @description Text status when there no layout shifts culprits/root causes were found.\n */\n noCulprits: 'Could not detect any layout shift culprits',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/CLSCulprits.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\nexport type CLSCulpritsInsightModel = InsightModel<typeof UIStrings, {\n animationFailures: readonly NoncompositedAnimationFailure[],\n shifts: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>,\n clusters: Types.Events.SyntheticLayoutShiftCluster[],\n worstCluster: Types.Events.SyntheticLayoutShiftCluster | undefined,\n /** The top 3 shift root causes for each cluster. */\n topCulpritsByCluster: Map<Types.Events.SyntheticLayoutShiftCluster, Platform.UIString.LocalizedString[]>,\n}>;\n\nexport function deps(): ['Meta', 'Animations', 'LayoutShifts', 'NetworkRequests'] {\n return ['Meta', 'Animations', 'LayoutShifts', 'NetworkRequests'];\n}\n\nexport const enum AnimationFailureReasons {\n ACCELERATED_ANIMATIONS_DISABLED = 'ACCELERATED_ANIMATIONS_DISABLED',\n EFFECT_SUPPRESSED_BY_DEVTOOLS = 'EFFECT_SUPPRESSED_BY_DEVTOOLS',\n INVALID_ANIMATION_OR_EFFECT = 'INVALID_ANIMATION_OR_EFFECT',\n EFFECT_HAS_UNSUPPORTED_TIMING_PARAMS = 'EFFECT_HAS_UNSUPPORTED_TIMING_PARAMS',\n EFFECT_HAS_NON_REPLACE_COMPOSITE_MODE = 'EFFECT_HAS_NON_REPLACE_COMPOSITE_MODE',\n TARGET_HAS_INVALID_COMPOSITING_STATE = 'TARGET_HAS_INVALID_COMPOSITING_STATE',\n TARGET_HAS_INCOMPATIBLE_ANIMATIONS = 'TARGET_HAS_INCOMPATIBLE_ANIMATIONS',\n TARGET_HAS_CSS_OFFSET = 'TARGET_HAS_CSS_OFFSET',\n ANIMATION_AFFECTS_NON_CSS_PROPERTIES = 'ANIMATION_AFFECTS_NON_CSS_PROPERTIES',\n TRANSFORM_RELATED_PROPERTY_CANNOT_BE_ACCELERATED_ON_TARGET =\n 'TRANSFORM_RELATED_PROPERTY_CANNOT_BE_ACCELERATED_ON_TARGET',\n TRANSFROM_BOX_SIZE_DEPENDENT = 'TRANSFROM_BOX_SIZE_DEPENDENT',\n FILTER_RELATED_PROPERTY_MAY_MOVE_PIXELS = 'FILTER_RELATED_PROPERTY_MAY_MOVE_PIXELS',\n UNSUPPORTED_CSS_PROPERTY = 'UNSUPPORTED_CSS_PROPERTY',\n MIXED_KEYFRAME_VALUE_TYPES = 'MIXED_KEYFRAME_VALUE_TYPES',\n TIMELINE_SOURCE_HAS_INVALID_COMPOSITING_STATE = 'TIMELINE_SOURCE_HAS_INVALID_COMPOSITING_STATE',\n ANIMATION_HAS_NO_VISIBLE_CHANGE = 'ANIMATION_HAS_NO_VISIBLE_CHANGE',\n AFFECTS_IMPORTANT_PROPERTY = 'AFFECTS_IMPORTANT_PROPERTY',\n SVG_TARGET_HAS_INDEPENDENT_TRANSFORM_PROPERTY = 'SVG_TARGET_HAS_INDEPENDENT_TRANSFORM_PROPERTY',\n}\n\nexport interface NoncompositedAnimationFailure {\n /**\n * Animation name.\n */\n name?: string;\n /**\n * Failure reason based on mask number defined in\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22.\n */\n failureReasons: AnimationFailureReasons[];\n /**\n * Unsupported properties.\n */\n unsupportedProperties?: Types.Events.Animation['args']['data']['unsupportedProperties'];\n /**\n * Animation event.\n */\n animation?: Types.Events.SyntheticAnimationPair;\n}\n\n/**\n * Each failure reason is represented by a bit flag. The bit shift operator '<<' is used to define\n * which bit corresponds to each failure reason.\n * https://source.chromium.org/search?q=f:compositor_animations.h%20%22enum%20FailureReason%22\n */\nconst ACTIONABLE_FAILURE_REASONS: Array<{\n flag: number,\n failure: AnimationFailureReasons,\n}> =\n [\n {\n flag: 1 << 0,\n failure: AnimationFailureReasons.ACCELERATED_ANIMATIONS_DISABLED,\n },\n {\n flag: 1 << 1,\n failure: AnimationFailureReasons.EFFECT_SUPPRESSED_BY_DEVTOOLS,\n },\n {\n flag: 1 << 2,\n failure: AnimationFailureReasons.INVALID_ANIMATION_OR_EFFECT,\n },\n {\n flag: 1 << 3,\n failure: AnimationFailureReasons.EFFECT_HAS_UNSUPPORTED_TIMING_PARAMS,\n },\n {\n flag: 1 << 4,\n failure: AnimationFailureReasons.EFFECT_HAS_NON_REPLACE_COMPOSITE_MODE,\n },\n {\n flag: 1 << 5,\n failure: AnimationFailureReasons.TARGET_HAS_INVALID_COMPOSITING_STATE,\n },\n {\n flag: 1 << 6,\n failure: AnimationFailureReasons.TARGET_HAS_INCOMPATIBLE_ANIMATIONS,\n },\n {\n flag: 1 << 7,\n failure: AnimationFailureReasons.TARGET_HAS_CSS_OFFSET,\n },\n // The failure 1 << 8 is marked as obsolete in Blink\n {\n flag: 1 << 9,\n failure: AnimationFailureReasons.ANIMATION_AFFECTS_NON_CSS_PROPERTIES,\n },\n {\n flag: 1 << 10,\n failure: AnimationFailureReasons.TRANSFORM_RELATED_PROPERTY_CANNOT_BE_ACCELERATED_ON_TARGET,\n },\n {\n flag: 1 << 11,\n failure: AnimationFailureReasons.TRANSFROM_BOX_SIZE_DEPENDENT,\n },\n {\n flag: 1 << 12,\n failure: AnimationFailureReasons.FILTER_RELATED_PROPERTY_MAY_MOVE_PIXELS,\n },\n {\n flag: 1 << 13,\n failure: AnimationFailureReasons.UNSUPPORTED_CSS_PROPERTY,\n },\n // The failure 1 << 14 is marked as obsolete in Blink\n {\n flag: 1 << 15,\n failure: AnimationFailureReasons.MIXED_KEYFRAME_VALUE_TYPES,\n },\n {\n flag: 1 << 16,\n failure: AnimationFailureReasons.TIMELINE_SOURCE_HAS_INVALID_COMPOSITING_STATE,\n },\n {\n flag: 1 << 17,\n failure: AnimationFailureReasons.ANIMATION_HAS_NO_VISIBLE_CHANGE,\n },\n {\n flag: 1 << 18,\n failure: AnimationFailureReasons.AFFECTS_IMPORTANT_PROPERTY,\n },\n {\n flag: 1 << 19,\n failure: AnimationFailureReasons.SVG_TARGET_HAS_INDEPENDENT_TRANSFORM_PROPERTY,\n },\n ] as const;\n\n// 500ms window.\n// Use this window to consider events and requests that may have caused a layout shift.\nconst ROOT_CAUSE_WINDOW = Helpers.Timing.secondsToMicro(Types.Timing.Seconds(0.5));\n\nexport interface LayoutShiftRootCausesData {\n iframeIds: string[];\n fontRequests: Types.Events.SyntheticNetworkRequest[];\n nonCompositedAnimations: NoncompositedAnimationFailure[];\n unsizedImages: Protocol.DOM.BackendNodeId[];\n}\n\n/**\n * Returns if an event happens within the root cause window, before the target event.\n * ROOT_CAUSE_WINDOW v target event\n * |------------------------|=======================\n */\nfunction isInRootCauseWindow(event: Types.Events.Event, targetEvent: Types.Events.Event): boolean {\n const eventEnd = event.dur ? event.ts + event.dur : event.ts;\n return eventEnd < targetEvent.ts && eventEnd >= targetEvent.ts - ROOT_CAUSE_WINDOW;\n}\n\nexport function getNonCompositedFailure(animationEvent: Types.Events.SyntheticAnimationPair):\n NoncompositedAnimationFailure[] {\n const failures: NoncompositedAnimationFailure[] = [];\n const beginEvent = animationEvent.args.data.beginEvent;\n const instantEvents = animationEvent.args.data.instantEvents || [];\n /**\n * Animation events containing composite information are ASYNC_NESTABLE_INSTANT ('n').\n * An animation may also contain multiple 'n' events, so we look through those with useful non-composited data.\n */\n for (const event of instantEvents) {\n const failureMask = event.args.data.compositeFailed;\n const unsupportedProperties = event.args.data.unsupportedProperties;\n if (!failureMask) {\n continue;\n }\n const failureReasons =\n ACTIONABLE_FAILURE_REASONS.filter(reason => failureMask & reason.flag).map(reason => reason.failure);\n const failure: NoncompositedAnimationFailure = {\n name: beginEvent.args.data.displayName,\n failureReasons,\n unsupportedProperties,\n animation: animationEvent,\n };\n failures.push(failure);\n }\n return failures;\n}\n\nfunction getNonCompositedFailureRootCauses(\n animationEvents: Types.Events.SyntheticAnimationPair[],\n prePaintEvents: Types.Events.PrePaint[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>,\n ): NoncompositedAnimationFailure[] {\n const allAnimationFailures: NoncompositedAnimationFailure[] = [];\n for (const animation of animationEvents) {\n /**\n * Animation events containing composite information are ASYNC_NESTABLE_INSTANT ('n').\n * An animation may also contain multiple 'n' events, so we look through those with useful non-composited data.\n */\n const failures = getNonCompositedFailure(animation);\n if (!failures) {\n continue;\n }\n allAnimationFailures.push(...failures);\n\n const nextPrePaint = getNextEvent(prePaintEvents, animation) as Types.Events.PrePaint | null;\n // If no following prePaint, this is not a root cause.\n if (!nextPrePaint) {\n continue;\n }\n\n // If the animation event is outside the ROOT_CAUSE_WINDOW, it could not be a root cause.\n if (!isInRootCauseWindow(animation, nextPrePaint)) {\n continue;\n }\n\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n // if no layout shift(s), this is not a root cause.\n if (!shifts) {\n continue;\n }\n\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n rootCausesForShift.nonCompositedAnimations.push(...failures);\n }\n }\n\n return allAnimationFailures;\n}\n\n/**\n * Given an array of layout shift and PrePaint events, returns a mapping from\n * PrePaint events to layout shifts dispatched within it.\n */\nfunction getShiftsByPrePaintEvents(\n layoutShifts: Types.Events.SyntheticLayoutShift[],\n prePaintEvents: Types.Events.PrePaint[],\n ): Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]> {\n // Maps from PrePaint events to LayoutShifts that occurred in each one.\n const shiftsByPrePaint = new Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>();\n\n // Associate all shifts to their corresponding PrePaint.\n for (const prePaintEvent of prePaintEvents) {\n const firstShiftIndex =\n Platform.ArrayUtilities.nearestIndexFromBeginning(layoutShifts, shift => shift.ts >= prePaintEvent.ts);\n if (firstShiftIndex === null) {\n // No layout shifts registered after this PrePaint start. Continue.\n continue;\n }\n for (let i = firstShiftIndex; i < layoutShifts.length; i++) {\n const shift = layoutShifts[i];\n if (shift.ts >= prePaintEvent.ts && shift.ts <= prePaintEvent.ts + prePaintEvent.dur) {\n const shiftsInPrePaint = Platform.MapUtilities.getWithDefault(shiftsByPrePaint, prePaintEvent, () => []);\n shiftsInPrePaint.push(shift);\n }\n if (shift.ts > prePaintEvent.ts + prePaintEvent.dur) {\n // Reached all layoutShifts of this PrePaint. Break out to continue with the next prePaint event.\n break;\n }\n }\n }\n return shiftsByPrePaint;\n}\n\n/**\n * Given a source event list, this returns the first event of that list that directly follows the target event.\n */\nfunction getNextEvent(sourceEvents: Types.Events.Event[], targetEvent: Types.Events.Event): Types.Events.Event|\n undefined {\n const index = Platform.ArrayUtilities.nearestIndexFromBeginning(\n sourceEvents, source => source.ts > targetEvent.ts + (targetEvent.dur || 0));\n // No PrePaint event registered after this event\n if (index === null) {\n return undefined;\n }\n\n return sourceEvents[index];\n}\n\n/**\n * An Iframe is considered a root cause if the iframe event occurs before a prePaint event\n * and within this prePaint event a layout shift(s) occurs.\n */\nfunction getIframeRootCauses(\n iframeCreatedEvents: readonly Types.Events.RenderFrameImplCreateChildFrame[],\n prePaintEvents: Types.Events.PrePaint[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>,\n domLoadingEvents: readonly Types.Events.DomLoading[]):\n Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData> {\n for (const iframeEvent of iframeCreatedEvents) {\n const nextPrePaint = getNextEvent(prePaintEvents, iframeEvent) as Types.Events.PrePaint | null;\n // If no following prePaint, this is not a root cause.\n if (!nextPrePaint) {\n continue;\n }\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n // if no layout shift(s), this is not a root cause.\n if (!shifts) {\n continue;\n }\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n\n // Look for the first dom event that occurs within the bounds of the iframe event.\n // This contains the frame id.\n const domEvent = domLoadingEvents.find(e => {\n const maxIframe = Types.Timing.Micro(iframeEvent.ts + (iframeEvent.dur ?? 0));\n return e.ts >= iframeEvent.ts && e.ts <= maxIframe;\n });\n if (domEvent && domEvent.args.frame) {\n rootCausesForShift.iframeIds.push(domEvent.args.frame);\n }\n }\n }\n return rootCausesByShift;\n}\n\n/**\n * An unsized image is considered a root cause if its PaintImage can be correlated to a\n * layout shift. We can correlate PaintImages with unsized images by their matching nodeIds.\n * X <- layout shift\n * |----------------|\n * ^ PrePaint event |-----|\n * ^ PaintImage\n */\nfunction getUnsizedImageRootCauses(\n unsizedImageEvents: readonly Types.Events.LayoutImageUnsized[], paintImageEvents: Types.Events.PaintImage[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>):\n Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData> {\n shiftsByPrePaint.forEach((shifts, prePaint) => {\n const paintImage = getNextEvent(paintImageEvents, prePaint) as Types.Events.PaintImage | null;\n // The unsized image corresponds to this PaintImage.\n const matchingNode =\n unsizedImageEvents.find(unsizedImage => unsizedImage.args.data.nodeId === paintImage?.args.data.nodeId);\n if (!matchingNode) {\n return;\n }\n // The unsized image is a potential root cause of all the shifts of this prePaint.\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n rootCausesForShift.unsizedImages.push(matchingNode.args.data.nodeId);\n }\n });\n return rootCausesByShift;\n}\n\n/**\n * A font request is considered a root cause if the request occurs before a prePaint event\n * and within this prePaint event a layout shift(s) occurs. Additionally, this font request should\n * happen within the ROOT_CAUSE_WINDOW of the prePaint event.\n */\nfunction getFontRootCauses(\n networkRequests: Types.Events.SyntheticNetworkRequest[], prePaintEvents: Types.Events.PrePaint[],\n shiftsByPrePaint: Map<Types.Events.PrePaint, Types.Events.SyntheticLayoutShift[]>,\n rootCausesByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>):\n Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData> {\n const fontRequests =\n networkRequests.filter(req => req.args.data.resourceType === 'Font' && req.args.data.mimeType.startsWith('font'));\n\n for (const req of fontRequests) {\n const nextPrePaint = getNextEvent(prePaintEvents, req) as Types.Events.PrePaint | null;\n if (!nextPrePaint) {\n continue;\n }\n\n // If the req is outside the ROOT_CAUSE_WINDOW, it could not be a root cause.\n if (!isInRootCauseWindow(req, nextPrePaint)) {\n continue;\n }\n\n // Get the shifts that belong to this prepaint\n const shifts = shiftsByPrePaint.get(nextPrePaint);\n\n // if no layout shift(s) in this prePaint, the request is not a root cause.\n if (!shifts) {\n continue;\n }\n // Include the root cause to the shifts in this prePaint.\n for (const shift of shifts) {\n const rootCausesForShift = rootCausesByShift.get(shift);\n if (!rootCausesForShift) {\n throw new Error('Unaccounted shift');\n }\n rootCausesForShift.fontRequests.push(req);\n }\n }\n return rootCausesByShift;\n}\n\n/**\n * Returns the top 3 shift root causes based on the given cluster.\n */\nfunction getTopCulprits(\n cluster: Types.Events.SyntheticLayoutShiftCluster,\n culpritsByShift: Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>):\n Platform.UIString.LocalizedString[] {\n const MAX_TOP_CULPRITS = 3;\n const causes: Platform.UIString.LocalizedString[] = [];\n\n const shifts = cluster.events;\n for (const shift of shifts) {\n const culprits = culpritsByShift.get(shift);\n if (!culprits) {\n continue;\n }\n\n const fontReq = culprits.fontRequests;\n const iframes = culprits.iframeIds;\n const animations = culprits.nonCompositedAnimations;\n const unsizedImages = culprits.unsizedImages;\n\n for (let i = 0; i < fontReq.length && causes.length < MAX_TOP_CULPRITS; i++) {\n causes.push(i18nString(UIStrings.fontRequest));\n }\n for (let i = 0; i < iframes.length && causes.length < MAX_TOP_CULPRITS; i++) {\n causes.push(i18nString(UIStrings.injectedIframe));\n }\n for (let i = 0; i < animations.length && causes.length < MAX_TOP_CULPRITS; i++) {\n causes.push(i18nString(UIStrings.animation));\n }\n for (let i = 0; i < unsizedImages.length && causes.length < MAX_TOP_CULPRITS; i++) {\n causes.push(i18nString(UIStrings.unsizedImages));\n }\n\n if (causes.length >= MAX_TOP_CULPRITS) {\n break;\n }\n }\n\n return causes.slice(0, MAX_TOP_CULPRITS);\n}\n\nfunction finalize(partialModel: PartialInsightModel<CLSCulpritsInsightModel>): CLSCulpritsInsightModel {\n const topCulprits =\n partialModel.worstCluster ? partialModel.topCulpritsByCluster.get(partialModel.worstCluster) ?? [] : [];\n\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.CLS,\n shouldShow: topCulprits.length > 0,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): CLSCulpritsInsightModel {\n const isWithinContext = (event: Types.Events.Event): boolean => Helpers.Timing.eventIsInBounds(event, context.bounds);\n\n const compositeAnimationEvents = parsedTrace.Animations.animations.filter(isWithinContext);\n const iframeEvents = parsedTrace.LayoutShifts.renderFrameImplCreateChildFrameEvents.filter(isWithinContext);\n const networkRequests = parsedTrace.NetworkRequests.byTime.filter(isWithinContext);\n const domLoadingEvents = parsedTrace.LayoutShifts.domLoadingEvents.filter(isWithinContext);\n const unsizedImageEvents = parsedTrace.LayoutShifts.layoutImageUnsizedEvents.filter(isWithinContext);\n\n const clusterKey = context.navigation ? context.navigationId : Types.Events.NO_NAVIGATION;\n const clusters = parsedTrace.LayoutShifts.clustersByNavigationId.get(clusterKey) ?? [];\n const clustersByScore = [...clusters].sort((a, b) => b.clusterCumulativeScore - a.clusterCumulativeScore);\n const worstCluster = clustersByScore.at(0);\n const layoutShifts = clusters.flatMap(cluster => cluster.events);\n const prePaintEvents = parsedTrace.LayoutShifts.prePaintEvents.filter(isWithinContext);\n const paintImageEvents = parsedTrace.LayoutShifts.paintImageEvents.filter(isWithinContext);\n\n // Get root causes.\n const rootCausesByShift = new Map<Types.Events.SyntheticLayoutShift, LayoutShiftRootCausesData>();\n const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);\n\n for (const shift of layoutShifts) {\n rootCausesByShift.set(shift, {iframeIds: [], fontRequests: [], nonCompositedAnimations: [], unsizedImages: []});\n }\n\n // Populate root causes for rootCausesByShift.\n getIframeRootCauses(iframeEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift, domLoadingEvents);\n getFontRootCauses(networkRequests, prePaintEvents, shiftsByPrePaint, rootCausesByShift);\n getUnsizedImageRootCauses(unsizedImageEvents, paintImageEvents, shiftsByPrePaint, rootCausesByShift);\n const animationFailures =\n getNonCompositedFailureRootCauses(compositeAnimationEvents, prePaintEvents, shiftsByPrePaint, rootCausesByShift);\n\n const relatedEvents: Types.Events.Event[] = [...layoutShifts];\n if (worstCluster) {\n relatedEvents.push(worstCluster);\n }\n\n const topCulpritsByCluster = new Map<Types.Events.SyntheticLayoutShiftCluster, Platform.UIString.LocalizedString[]>();\n for (const cluster of clusters) {\n topCulpritsByCluster.set(cluster, getTopCulprits(cluster, rootCausesByShift));\n }\n\n return finalize({\n relatedEvents,\n animationFailures,\n shifts: rootCausesByShift,\n clusters,\n worstCluster,\n topCulpritsByCluster,\n });\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Types from '../types/types.js';
|
|
2
|
-
import { type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
2
|
+
import { type Checklist, type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
3
3
|
export declare const UIStrings: {
|
|
4
4
|
/**
|
|
5
5
|
*@description Title of an insight that provides a breakdown for how long it took to download the main document.
|
|
@@ -45,25 +45,15 @@ export declare const UIStrings: {
|
|
|
45
45
|
* @description Text for a label describing a network request event as taking longer to download because it wasn't compressed.
|
|
46
46
|
*/
|
|
47
47
|
uncompressedDownload: string;
|
|
48
|
-
/**
|
|
49
|
-
*@description Text for a screen-reader label to tell the user that the icon represents a successful insight check
|
|
50
|
-
*@example {Server response time} PH1
|
|
51
|
-
*/
|
|
52
|
-
successAriaLabel: string;
|
|
53
|
-
/**
|
|
54
|
-
*@description Text for a screen-reader label to tell the user that the icon represents an unsuccessful insight check
|
|
55
|
-
*@example {Server response time} PH1
|
|
56
|
-
*/
|
|
57
|
-
failedAriaLabel: string;
|
|
58
48
|
};
|
|
59
49
|
export declare const i18nString: (id: string, values?: Record<string, string> | undefined) => Record<string, string>;
|
|
60
50
|
export type DocumentLatencyInsightModel = InsightModel<typeof UIStrings, {
|
|
61
51
|
data?: {
|
|
62
52
|
serverResponseTime: Types.Timing.Milli;
|
|
63
|
-
serverResponseTooSlow: boolean;
|
|
64
53
|
redirectDuration: Types.Timing.Milli;
|
|
65
54
|
uncompressedResponseBytes: number;
|
|
66
55
|
documentRequest?: Types.Events.SyntheticNetworkRequest;
|
|
56
|
+
checklist: Checklist<'noRedirects' | 'serverResponseIsFast' | 'usesCompression'>;
|
|
67
57
|
};
|
|
68
58
|
}>;
|
|
69
59
|
export declare function deps(): ['Meta', 'NetworkRequests'];
|
|
@@ -50,16 +50,6 @@ export const UIStrings = {
|
|
|
50
50
|
* @description Text for a label describing a network request event as taking longer to download because it wasn't compressed.
|
|
51
51
|
*/
|
|
52
52
|
uncompressedDownload: 'Uncompressed download',
|
|
53
|
-
/**
|
|
54
|
-
*@description Text for a screen-reader label to tell the user that the icon represents a successful insight check
|
|
55
|
-
*@example {Server response time} PH1
|
|
56
|
-
*/
|
|
57
|
-
successAriaLabel: 'Insight check passed: {PH1}',
|
|
58
|
-
/**
|
|
59
|
-
*@description Text for a screen-reader label to tell the user that the icon represents an unsuccessful insight check
|
|
60
|
-
*@example {Server response time} PH1
|
|
61
|
-
*/
|
|
62
|
-
failedAriaLabel: 'Insight check failed: {PH1}',
|
|
63
53
|
};
|
|
64
54
|
// const str_ = i18n.i18n.registerUIStrings('models/trace/insights/DocumentLatency.ts', UIStrings);
|
|
65
55
|
export const i18nString = string => string; // i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -144,8 +134,8 @@ function getCompressionSavings(request) {
|
|
|
144
134
|
function finalize(partialModel) {
|
|
145
135
|
let hasFailure = false;
|
|
146
136
|
if (partialModel.data) {
|
|
147
|
-
hasFailure = partialModel.data.
|
|
148
|
-
partialModel.data.
|
|
137
|
+
hasFailure = !partialModel.data.checklist.usesCompression.value ||
|
|
138
|
+
!partialModel.data.checklist.serverResponseIsFast.value || !partialModel.data.checklist.noRedirects.value;
|
|
149
139
|
}
|
|
150
140
|
return {
|
|
151
141
|
strings: UIStrings,
|
|
@@ -179,14 +169,33 @@ export function generateInsight(parsedTrace, context) {
|
|
|
179
169
|
FCP: overallSavingsMs,
|
|
180
170
|
LCP: overallSavingsMs,
|
|
181
171
|
};
|
|
172
|
+
const uncompressedResponseBytes = getCompressionSavings(documentRequest);
|
|
173
|
+
const noRedirects = redirectDuration === 0;
|
|
174
|
+
const serverResponseIsFast = !serverResponseTooSlow;
|
|
175
|
+
const usesCompression = uncompressedResponseBytes === 0;
|
|
182
176
|
return finalize({
|
|
183
177
|
relatedEvents: [documentRequest],
|
|
184
178
|
data: {
|
|
185
179
|
serverResponseTime,
|
|
186
|
-
serverResponseTooSlow,
|
|
187
180
|
redirectDuration: Types.Timing.Milli(redirectDuration),
|
|
188
|
-
uncompressedResponseBytes
|
|
181
|
+
uncompressedResponseBytes,
|
|
189
182
|
documentRequest,
|
|
183
|
+
checklist: {
|
|
184
|
+
noRedirects: {
|
|
185
|
+
label: noRedirects ? i18nString(UIStrings.passingRedirects) : i18nString(UIStrings.failedRedirects),
|
|
186
|
+
value: noRedirects
|
|
187
|
+
},
|
|
188
|
+
serverResponseIsFast: {
|
|
189
|
+
label: serverResponseIsFast ? i18nString(UIStrings.passingServerResponseTime) :
|
|
190
|
+
i18nString(UIStrings.failedServerResponseTime),
|
|
191
|
+
value: serverResponseIsFast
|
|
192
|
+
},
|
|
193
|
+
usesCompression: {
|
|
194
|
+
label: usesCompression ? i18nString(UIStrings.passingTextCompression) :
|
|
195
|
+
i18nString(UIStrings.failedTextCompression),
|
|
196
|
+
value: usesCompression
|
|
197
|
+
},
|
|
198
|
+
},
|
|
190
199
|
},
|
|
191
200
|
metricSavings,
|
|
192
201
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentLatency.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/DocumentLatency.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 * 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 a breakdown for how long it took to download the main document.\n */\n title: 'Document request latency',\n /**\n *@description Description of an insight that provides a breakdown for how long it took to download the main document.\n */\n description:\n 'Your first network request is the most important. Reduce its latency by avoiding redirects, ensuring a fast server response, and enabling text compression.',\n /**\n * @description Text to tell the user that the document request does not have redirects.\n */\n passingRedirects: 'Avoids redirects',\n /**\n * @description Text to tell the user that the document request had redirects.\n */\n failedRedirects: 'Had redirects',\n /**\n * @description Text to tell the user that the time starting the document request to when the server started responding is acceptable.\n */\n passingServerResponseTime: 'Server responds quickly',\n /**\n * @description Text to tell the user that the time starting the document request to when the server started responding is not acceptable.\n */\n failedServerResponseTime: 'Server responded slowly',\n /**\n * @description Text to tell the user that text compression (like gzip) was applied.\n */\n passingTextCompression: 'Applies text compression',\n /**\n * @description Text to tell the user that text compression (like gzip) was not applied.\n */\n failedTextCompression: 'No compression applied',\n /**\n * @description Text for a label describing a network request event as having redirects.\n */\n redirectsLabel: 'Redirects',\n /**\n * @description Text for a label describing a network request event as taking too long to start delivery by the server.\n */\n serverResponseTimeLabel: 'Server response time',\n /**\n * @description Text for a label describing a network request event as taking longer to download because it wasn't compressed.\n */\n uncompressedDownload: 'Uncompressed download',\n /**\n *@description Text for a screen-reader label to tell the user that the icon represents a successful insight check\n *@example {Server response time} PH1\n */\n successAriaLabel: 'Insight check passed: {PH1}',\n /**\n *@description Text for a screen-reader label to tell the user that the icon represents an unsuccessful insight check\n *@example {Server response time} PH1\n */\n failedAriaLabel: 'Insight check failed: {PH1}',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/DocumentLatency.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\n// Due to the way that DevTools throttling works we cannot see if server response took less than ~570ms.\n// We set our failure threshold to 600ms to avoid those false positives but we want devs to shoot for 100ms.\nconst TOO_SLOW_THRESHOLD_MS = 600;\nconst TARGET_MS = 100;\n\n// Threshold for compression savings.\nconst IGNORE_THRESHOLD_IN_BYTES = 1400;\n\nexport type DocumentLatencyInsightModel = InsightModel<typeof UIStrings, {\n data?: {\n serverResponseTime: Types.Timing.Milli,\n serverResponseTooSlow: boolean,\n redirectDuration: Types.Timing.Milli,\n uncompressedResponseBytes: number,\n documentRequest?: Types.Events.SyntheticNetworkRequest,\n },\n}>;\n\nexport function deps(): ['Meta', 'NetworkRequests'] {\n return ['Meta', 'NetworkRequests'];\n}\n\nfunction getServerResponseTime(request: Types.Events.SyntheticNetworkRequest): Types.Timing.Milli|null {\n const timing = request.args.data.timing;\n if (!timing) {\n return null;\n }\n\n const ms = Helpers.Timing.microToMilli(request.args.data.syntheticData.waiting);\n return Math.round(ms) as Types.Timing.Milli;\n}\n\nfunction getCompressionSavings(request: Types.Events.SyntheticNetworkRequest): number {\n // Check from headers if compression was already applied.\n // Older devtools logs are lower case, while modern logs are Cased-Like-This.\n const patterns = [\n /^content-encoding$/i,\n /^x-content-encoding-over-network$/i,\n ];\n const compressionTypes = ['gzip', 'br', 'deflate', 'zstd'];\n const isCompressed = request.args.data.responseHeaders.some(\n header => patterns.some(p => header.name.match(p)) && compressionTypes.includes(header.value));\n if (isCompressed) {\n return 0;\n }\n\n // We don't know how many bytes this asset used on the network, but we can guess it was\n // roughly the size of the content gzipped.\n // See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples\n // See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers\n // See https://letstalkaboutwebperf.com/en/gzip-brotli-server-config/ for MIME types to compress\n const originalSize = request.args.data.decodedBodyLength;\n let estimatedSavings = 0;\n switch (request.args.data.mimeType) {\n case 'text/css':\n // Stylesheets tend to compress extremely well.\n estimatedSavings = Math.round(originalSize * 0.8);\n break;\n case 'text/html':\n case 'text/javascript':\n // Scripts and HTML compress fairly well too.\n estimatedSavings = Math.round(originalSize * 0.67);\n break;\n case 'text/plain':\n case 'text/xml':\n case 'text/x-component':\n case 'application/javascript':\n case 'application/json':\n case 'application/manifest+json':\n case 'application/vnd.api+json':\n case 'application/xml':\n case 'application/xhtml+xml':\n case 'application/rss+xml':\n case 'application/atom+xml':\n case 'application/vnd.ms-fontobject':\n case 'application/x-font-ttf':\n case 'application/x-font-opentype':\n case 'application/x-font-truetype':\n case 'image/svg+xml':\n case 'image/x-icon':\n case 'image/vnd.microsoft.icon':\n case 'font/ttf':\n case 'font/eot':\n case 'font/otf':\n case 'font/opentype':\n // Use the average savings in HTTPArchive.\n estimatedSavings = Math.round(originalSize * 0.5);\n break;\n default: // Any other MIME types are likely already compressed.\n }\n // Check if the estimated savings are greater than the byte ignore threshold.\n // Note that the estimated gzip savings are always more than 10%, so there is\n // no percent threshold.\n return estimatedSavings < IGNORE_THRESHOLD_IN_BYTES ? 0 : estimatedSavings;\n}\n\nfunction finalize(partialModel: PartialInsightModel<DocumentLatencyInsightModel>): DocumentLatencyInsightModel {\n let hasFailure = false;\n if (partialModel.data) {\n hasFailure = partialModel.data.redirectDuration > 0 || partialModel.data.serverResponseTooSlow ||\n partialModel.data.uncompressedResponseBytes > 0;\n }\n\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.ALL,\n shouldShow: hasFailure,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): DocumentLatencyInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const documentRequest =\n parsedTrace.NetworkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);\n if (!documentRequest) {\n return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n const serverResponseTime = getServerResponseTime(documentRequest);\n if (serverResponseTime === null) {\n throw new Error('missing document request timing');\n }\n\n const serverResponseTooSlow = serverResponseTime > TOO_SLOW_THRESHOLD_MS;\n\n let overallSavingsMs = 0;\n if (serverResponseTime > TOO_SLOW_THRESHOLD_MS) {\n overallSavingsMs = Math.max(serverResponseTime - TARGET_MS, 0);\n }\n\n const redirectDuration = Math.round(documentRequest.args.data.syntheticData.redirectionDuration / 1000);\n overallSavingsMs += redirectDuration;\n\n const metricSavings = {\n FCP: overallSavingsMs as Types.Timing.Milli,\n LCP: overallSavingsMs as Types.Timing.Milli,\n };\n\n return finalize({\n relatedEvents: [documentRequest],\n data: {\n serverResponseTime,\n serverResponseTooSlow,\n redirectDuration: Types.Timing.Milli(redirectDuration),\n uncompressedResponseBytes: getCompressionSavings(documentRequest),\n documentRequest,\n },\n metricSavings,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"DocumentLatency.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/DocumentLatency.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 * as Types from '../types/types.js';\n\nimport {\n type Checklist,\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 a breakdown for how long it took to download the main document.\n */\n title: 'Document request latency',\n /**\n *@description Description of an insight that provides a breakdown for how long it took to download the main document.\n */\n description:\n 'Your first network request is the most important. Reduce its latency by avoiding redirects, ensuring a fast server response, and enabling text compression.',\n /**\n * @description Text to tell the user that the document request does not have redirects.\n */\n passingRedirects: 'Avoids redirects',\n /**\n * @description Text to tell the user that the document request had redirects.\n */\n failedRedirects: 'Had redirects',\n /**\n * @description Text to tell the user that the time starting the document request to when the server started responding is acceptable.\n */\n passingServerResponseTime: 'Server responds quickly',\n /**\n * @description Text to tell the user that the time starting the document request to when the server started responding is not acceptable.\n */\n failedServerResponseTime: 'Server responded slowly',\n /**\n * @description Text to tell the user that text compression (like gzip) was applied.\n */\n passingTextCompression: 'Applies text compression',\n /**\n * @description Text to tell the user that text compression (like gzip) was not applied.\n */\n failedTextCompression: 'No compression applied',\n /**\n * @description Text for a label describing a network request event as having redirects.\n */\n redirectsLabel: 'Redirects',\n /**\n * @description Text for a label describing a network request event as taking too long to start delivery by the server.\n */\n serverResponseTimeLabel: 'Server response time',\n /**\n * @description Text for a label describing a network request event as taking longer to download because it wasn't compressed.\n */\n uncompressedDownload: 'Uncompressed download',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/DocumentLatency.ts', UIStrings);\nexport const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);\n\n// Due to the way that DevTools throttling works we cannot see if server response took less than ~570ms.\n// We set our failure threshold to 600ms to avoid those false positives but we want devs to shoot for 100ms.\nconst TOO_SLOW_THRESHOLD_MS = 600;\nconst TARGET_MS = 100;\n\n// Threshold for compression savings.\nconst IGNORE_THRESHOLD_IN_BYTES = 1400;\n\nexport type DocumentLatencyInsightModel = InsightModel<typeof UIStrings, {\n data?: {\n serverResponseTime: Types.Timing.Milli,\n redirectDuration: Types.Timing.Milli,\n uncompressedResponseBytes: number,\n documentRequest?: Types.Events.SyntheticNetworkRequest,\n checklist: Checklist<'noRedirects'|'serverResponseIsFast'|'usesCompression'>,\n },\n}>;\n\nexport function deps(): ['Meta', 'NetworkRequests'] {\n return ['Meta', 'NetworkRequests'];\n}\n\nfunction getServerResponseTime(request: Types.Events.SyntheticNetworkRequest): Types.Timing.Milli|null {\n const timing = request.args.data.timing;\n if (!timing) {\n return null;\n }\n\n const ms = Helpers.Timing.microToMilli(request.args.data.syntheticData.waiting);\n return Math.round(ms) as Types.Timing.Milli;\n}\n\nfunction getCompressionSavings(request: Types.Events.SyntheticNetworkRequest): number {\n // Check from headers if compression was already applied.\n // Older devtools logs are lower case, while modern logs are Cased-Like-This.\n const patterns = [\n /^content-encoding$/i,\n /^x-content-encoding-over-network$/i,\n ];\n const compressionTypes = ['gzip', 'br', 'deflate', 'zstd'];\n const isCompressed = request.args.data.responseHeaders.some(\n header => patterns.some(p => header.name.match(p)) && compressionTypes.includes(header.value));\n if (isCompressed) {\n return 0;\n }\n\n // We don't know how many bytes this asset used on the network, but we can guess it was\n // roughly the size of the content gzipped.\n // See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples\n // See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers\n // See https://letstalkaboutwebperf.com/en/gzip-brotli-server-config/ for MIME types to compress\n const originalSize = request.args.data.decodedBodyLength;\n let estimatedSavings = 0;\n switch (request.args.data.mimeType) {\n case 'text/css':\n // Stylesheets tend to compress extremely well.\n estimatedSavings = Math.round(originalSize * 0.8);\n break;\n case 'text/html':\n case 'text/javascript':\n // Scripts and HTML compress fairly well too.\n estimatedSavings = Math.round(originalSize * 0.67);\n break;\n case 'text/plain':\n case 'text/xml':\n case 'text/x-component':\n case 'application/javascript':\n case 'application/json':\n case 'application/manifest+json':\n case 'application/vnd.api+json':\n case 'application/xml':\n case 'application/xhtml+xml':\n case 'application/rss+xml':\n case 'application/atom+xml':\n case 'application/vnd.ms-fontobject':\n case 'application/x-font-ttf':\n case 'application/x-font-opentype':\n case 'application/x-font-truetype':\n case 'image/svg+xml':\n case 'image/x-icon':\n case 'image/vnd.microsoft.icon':\n case 'font/ttf':\n case 'font/eot':\n case 'font/otf':\n case 'font/opentype':\n // Use the average savings in HTTPArchive.\n estimatedSavings = Math.round(originalSize * 0.5);\n break;\n default: // Any other MIME types are likely already compressed.\n }\n // Check if the estimated savings are greater than the byte ignore threshold.\n // Note that the estimated gzip savings are always more than 10%, so there is\n // no percent threshold.\n return estimatedSavings < IGNORE_THRESHOLD_IN_BYTES ? 0 : estimatedSavings;\n}\n\nfunction finalize(partialModel: PartialInsightModel<DocumentLatencyInsightModel>): DocumentLatencyInsightModel {\n let hasFailure = false;\n if (partialModel.data) {\n hasFailure = !partialModel.data.checklist.usesCompression.value ||\n !partialModel.data.checklist.serverResponseIsFast.value || !partialModel.data.checklist.noRedirects.value;\n }\n\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.ALL,\n shouldShow: hasFailure,\n ...partialModel,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): DocumentLatencyInsightModel {\n if (!context.navigation) {\n return finalize({});\n }\n\n const documentRequest =\n parsedTrace.NetworkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);\n if (!documentRequest) {\n return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n const serverResponseTime = getServerResponseTime(documentRequest);\n if (serverResponseTime === null) {\n throw new Error('missing document request timing');\n }\n\n const serverResponseTooSlow = serverResponseTime > TOO_SLOW_THRESHOLD_MS;\n\n let overallSavingsMs = 0;\n if (serverResponseTime > TOO_SLOW_THRESHOLD_MS) {\n overallSavingsMs = Math.max(serverResponseTime - TARGET_MS, 0);\n }\n\n const redirectDuration = Math.round(documentRequest.args.data.syntheticData.redirectionDuration / 1000);\n overallSavingsMs += redirectDuration;\n\n const metricSavings = {\n FCP: overallSavingsMs as Types.Timing.Milli,\n LCP: overallSavingsMs as Types.Timing.Milli,\n };\n\n const uncompressedResponseBytes = getCompressionSavings(documentRequest);\n\n const noRedirects = redirectDuration === 0;\n const serverResponseIsFast = !serverResponseTooSlow;\n const usesCompression = uncompressedResponseBytes === 0;\n\n return finalize({\n relatedEvents: [documentRequest],\n data: {\n serverResponseTime,\n redirectDuration: Types.Timing.Milli(redirectDuration),\n uncompressedResponseBytes,\n documentRequest,\n checklist: {\n noRedirects: {\n label: noRedirects ? i18nString(UIStrings.passingRedirects) : i18nString(UIStrings.failedRedirects),\n value: noRedirects\n },\n serverResponseIsFast: {\n label: serverResponseIsFast ? i18nString(UIStrings.passingServerResponseTime) :\n i18nString(UIStrings.failedServerResponseTime),\n value: serverResponseIsFast\n },\n usesCompression: {\n label: usesCompression ? i18nString(UIStrings.passingTextCompression) :\n i18nString(UIStrings.failedTextCompression),\n value: usesCompression\n },\n },\n },\n metricSavings,\n });\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Types from '../types/types.js';
|
|
2
|
-
import { type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
2
|
+
import { type Checklist, type InsightModel, type InsightSetContext, type RequiredData } from './types.js';
|
|
3
3
|
export declare const UIStrings: {
|
|
4
4
|
/**
|
|
5
5
|
*@description Title of an insight that provides details about the LCP metric, and the network requests necessary to load it. Details how the LCP request was discoverable - in other words, the path necessary to load it (ex: network requests, JavaScript)
|
|
@@ -26,16 +26,6 @@ export declare const UIStrings: {
|
|
|
26
26
|
* @description Text to tell the user that the LCP request does not have the lazy load property applied.
|
|
27
27
|
*/
|
|
28
28
|
lazyLoadNotApplied: string;
|
|
29
|
-
/**
|
|
30
|
-
*@description Text for a screen-reader label to tell the user that the icon represents a successful insight check
|
|
31
|
-
*@example {Server response time} PH1
|
|
32
|
-
*/
|
|
33
|
-
successAriaLabel: string;
|
|
34
|
-
/**
|
|
35
|
-
*@description Text for a screen-reader label to tell the user that the icon represents an unsuccessful insight check
|
|
36
|
-
*@example {Server response time} PH1
|
|
37
|
-
*/
|
|
38
|
-
failedAriaLabel: string;
|
|
39
29
|
/**
|
|
40
30
|
* @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.
|
|
41
31
|
*/
|
|
@@ -49,11 +39,9 @@ export declare const i18nString: (id: string, values?: Record<string, string> |
|
|
|
49
39
|
export declare function deps(): ['NetworkRequests', 'PageLoadMetrics', 'LargestImagePaint', 'Meta'];
|
|
50
40
|
export type LCPDiscoveryInsightModel = InsightModel<typeof UIStrings, {
|
|
51
41
|
lcpEvent?: Types.Events.LargestContentfulPaintCandidate;
|
|
52
|
-
shouldRemoveLazyLoading?: boolean;
|
|
53
|
-
shouldIncreasePriorityHint?: boolean;
|
|
54
|
-
shouldPreloadImage?: boolean;
|
|
55
42
|
/** The network request for the LCP image, if there was one. */
|
|
56
43
|
lcpRequest?: Types.Events.SyntheticNetworkRequest;
|
|
57
44
|
earliestDiscoveryTimeTs?: Types.Timing.Micro;
|
|
45
|
+
checklist?: Checklist<'priorityHinted' | 'requestDiscoverable' | 'eagerlyLoaded'>;
|
|
58
46
|
}>;
|
|
59
47
|
export declare function generateInsight(parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): LCPDiscoveryInsightModel;
|
|
@@ -32,16 +32,6 @@ export const UIStrings = {
|
|
|
32
32
|
* @description Text to tell the user that the LCP request does not have the lazy load property applied.
|
|
33
33
|
*/
|
|
34
34
|
lazyLoadNotApplied: 'lazy load not applied',
|
|
35
|
-
/**
|
|
36
|
-
*@description Text for a screen-reader label to tell the user that the icon represents a successful insight check
|
|
37
|
-
*@example {Server response time} PH1
|
|
38
|
-
*/
|
|
39
|
-
successAriaLabel: 'Insight check passed: {PH1}',
|
|
40
|
-
/**
|
|
41
|
-
*@description Text for a screen-reader label to tell the user that the icon represents an unsuccessful insight check
|
|
42
|
-
*@example {Server response time} PH1
|
|
43
|
-
*/
|
|
44
|
-
failedAriaLabel: 'Insight check failed: {PH1}',
|
|
45
35
|
/**
|
|
46
36
|
* @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.
|
|
47
37
|
*/
|
|
@@ -66,9 +56,9 @@ function finalize(partialModel) {
|
|
|
66
56
|
title: i18nString(UIStrings.title),
|
|
67
57
|
description: i18nString(UIStrings.description),
|
|
68
58
|
category: InsightCategory.LCP,
|
|
69
|
-
shouldShow: Boolean(partialModel.lcpRequest &&
|
|
70
|
-
(partialModel.
|
|
71
|
-
partialModel.
|
|
59
|
+
shouldShow: Boolean(partialModel.lcpRequest && partialModel.checklist &&
|
|
60
|
+
(!partialModel.checklist.eagerlyLoaded.value || !partialModel.checklist.requestDiscoverable.value ||
|
|
61
|
+
!partialModel.checklist.priorityHinted.value)),
|
|
72
62
|
...partialModel,
|
|
73
63
|
relatedEvents,
|
|
74
64
|
};
|
|
@@ -113,11 +103,13 @@ export function generateInsight(parsedTrace, context) {
|
|
|
113
103
|
undefined;
|
|
114
104
|
return finalize({
|
|
115
105
|
lcpEvent,
|
|
116
|
-
shouldRemoveLazyLoading: imageLoadingAttr === 'lazy',
|
|
117
|
-
shouldIncreasePriorityHint: imageFetchPriorityHint !== 'high',
|
|
118
|
-
shouldPreloadImage: !imgPreloadedOrFoundInHTML,
|
|
119
106
|
lcpRequest,
|
|
120
107
|
earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.Micro(earliestDiscoveryTime) : undefined,
|
|
108
|
+
checklist: {
|
|
109
|
+
priorityHinted: { label: i18nString(UIStrings.fetchPriorityApplied), value: imageFetchPriorityHint === 'high' },
|
|
110
|
+
requestDiscoverable: { label: i18nString(UIStrings.requestDiscoverable), value: imgPreloadedOrFoundInHTML },
|
|
111
|
+
eagerlyLoaded: { label: i18nString(UIStrings.lazyLoadNotApplied), value: imageLoadingAttr !== 'lazy' },
|
|
112
|
+
},
|
|
121
113
|
});
|
|
122
114
|
}
|
|
123
115
|
//# sourceMappingURL=LCPDiscovery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LCPDiscovery.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPDiscovery.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,IAAI,MAAM,4BAA4B,CAAC;AACnD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,OAAO,MAAM,uBAAuB,CAAC;AACjD,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EACL,eAAe,EAGf,cAAc,GAGf,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;;OAEG;IACH,KAAK,EAAE,uBAAuB;IAC9B;;OAEG;IACH,WAAW,EACP,8NAA8N;IAClO;;;OAGG;IACH,YAAY,EAAE,oDAAoD;IAClE;;OAEG;IACH,oBAAoB,EAAE,4BAA4B;IAClD;;OAEG;IACH,mBAAmB,EAAE,6CAA6C;IAClE;;OAEG;IACH,kBAAkB,EAAE,uBAAuB;IAC3C;;;OAGG;IACH,gBAAgB,EAAE,6BAA6B;IAC/C;;;OAGG;IACH,eAAe,EAAE,6BAA6B;IAC9C;;OAEG;IACH,KAAK,EAAE,iBAAiB;IACxB;;OAEG;IACH,aAAa,EAAE,0DAA0D;CAC1E,CAAC;AAEF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,uCAAuC,EAAE,SAAS,CAAC,CAAC;AAC7F,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAE7E,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;AAC7E,CAAC;AAYD,SAAS,QAAQ,CAAC,YAA2D;IAC3E,MAAM,aAAa,GAAG,YAAY,CAAC,QAAQ,IAAI,YAAY,CAAC,UAAU,CAAC,CAAC;QACpE,4CAA4C;QAC5C,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC;QAClD,EAAE,CAAC;IACP,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,OAAO,CACf,YAAY,CAAC,UAAU;YACvB,CAAC,YAAY,CAAC,0BAA0B,IAAI,YAAY,CAAC,kBAAkB;gBAC1E,YAAY,CAAC,uBAAuB,CAAC,CAAC;QAC3C,GAAG,YAAY;QACf,aAAa;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC3B,WAAsC,EAAE,OAA0B;IACpE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,MAAM,eAAe,GAAG,WAAW,CAAC,eAAe,CAAC;IAEpD,MAAM,YAAY,GAAG,WAAW,CAAC,eAAe,CAAC,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5F,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,mEAAuD,CAAC;IAC1F,MAAM,QAAQ,GAAG,WAAW,EAAE,KAAK,CAAC;IACpC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,iCAAiC,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3E,OAAO,QAAQ,CAAC,EAAC,QAAQ,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,EAAC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACxG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,QAAQ,CAAC,EAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,cAAc,CAAC,mBAAmB,CAAC,EAAC,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,QAAQ,CAAC,EAAC,QAAQ,EAAC,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC;IACzD,mHAAmH;IACnH,wCAAwC;IACxC,MAAM,kBAAkB,GACpB,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,YAAY,CAAC;IACpG,MAAM,yBAAyB,GAAG,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,kBAAkB,CAAC;IAE5F,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;IACzD,MAAM,sBAAsB,GAAG,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACvE,6EAA6E;IAC7E,MAAM,qBAAqB,GAAG,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YAClE,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAClF,SAAS,CAAC;IAEd,OAAO,QAAQ,CAAC;QACd,QAAQ;QACR,uBAAuB,EAAE,gBAAgB,KAAK,MAAM;QACpD,0BAA0B,EAAE,sBAAsB,KAAK,MAAM;QAC7D,kBAAkB,EAAE,CAAC,yBAAyB;QAC9C,UAAU;QACV,uBAAuB,EAAE,qBAAqB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,SAAS;KACvG,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 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, and the network requests necessary to load it. Details how the LCP request was discoverable - in other words, the path necessary to load it (ex: network requests, JavaScript)\n */\n title: 'LCP request discovery',\n /**\n *@description Description of an insight that provides details about the LCP metric, and the network requests necessary to load it.\n */\n description:\n 'Optimize LCP by making the LCP image [discoverable](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) from the HTML immediately, and [avoiding lazy-loading](https://web.dev/articles/lcp-lazy-loading)',\n /**\n * @description Text to tell the user how long after the earliest discovery time their LCP element loaded.\n * @example {401ms} PH1\n */\n lcpLoadDelay: 'LCP image loaded {PH1} after earliest start point.',\n /**\n * @description Text to tell the user that a fetchpriority property value of \"high\" is applied to the LCP request.\n */\n fetchPriorityApplied: 'fetchpriority=high applied',\n /**\n * @description Text to tell the user that the LCP request is discoverable in the initial document.\n */\n requestDiscoverable: 'Request is discoverable in initial document',\n /**\n * @description Text to tell the user that the LCP request does not have the lazy load property applied.\n */\n lazyLoadNotApplied: 'lazy load not applied',\n /**\n *@description Text for a screen-reader label to tell the user that the icon represents a successful insight check\n *@example {Server response time} PH1\n */\n successAriaLabel: 'Insight check passed: {PH1}',\n /**\n *@description Text for a screen-reader label to tell the user that the icon represents an unsuccessful insight check\n *@example {Server response time} PH1\n */\n failedAriaLabel: 'Insight check failed: {PH1}',\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 /**\n * @description Text status indicating that the Largest Contentful Paint (LCP) metric was text rather than an image. \"LCP\" is an acronym and should not be translated.\n */\n noLcpResource: 'No LCP resource detected because the LCP is not an image',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPDiscovery.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\nexport type LCPDiscoveryInsightModel = InsightModel<typeof UIStrings, {\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n shouldRemoveLazyLoading?: boolean,\n shouldIncreasePriorityHint?: boolean,\n shouldPreloadImage?: boolean,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n earliestDiscoveryTimeTs?: Types.Timing.Micro,\n}>;\n\nfunction finalize(partialModel: PartialInsightModel<LCPDiscoveryInsightModel>): LCPDiscoveryInsightModel {\n const relatedEvents = partialModel.lcpEvent && partialModel.lcpRequest ?\n // TODO: add entire request initiator chain?\n [partialModel.lcpEvent, partialModel.lcpRequest] :\n [];\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n shouldShow: Boolean(\n partialModel.lcpRequest &&\n (partialModel.shouldIncreasePriorityHint || partialModel.shouldPreloadImage ||\n partialModel.shouldRemoveLazyLoading)),\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): LCPDiscoveryInsightModel {\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 const docRequest = networkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);\n if (!docRequest) {\n return finalize({lcpEvent, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigation.get(context.navigation);\n if (!lcpRequest) {\n return finalize({lcpEvent});\n }\n\n const initiatorUrl = lcpRequest.args.data.initiator?.url;\n // TODO(b/372319476): Explore using trace event HTMLDocumentParser::FetchQueuedPreloads to determine if the request\n // is discovered by the preload scanner.\n const initiatedByMainDoc =\n lcpRequest?.args.data.initiator?.type === 'parser' && docRequest.args.data.url === initiatorUrl;\n const imgPreloadedOrFoundInHTML = lcpRequest?.args.data.isLinkPreload || initiatedByMainDoc;\n\n const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;\n const imageFetchPriorityHint = lcpRequest?.args.data.fetchPriorityHint;\n // This is the earliest discovery time an LCP request could have - it's TTFB.\n const earliestDiscoveryTime = docRequest && docRequest.args.data.timing ?\n Helpers.Timing.secondsToMicro(docRequest.args.data.timing.requestTime) +\n Helpers.Timing.milliToMicro(docRequest.args.data.timing.receiveHeadersStart) :\n undefined;\n\n return finalize({\n lcpEvent,\n shouldRemoveLazyLoading: imageLoadingAttr === 'lazy',\n shouldIncreasePriorityHint: imageFetchPriorityHint !== 'high',\n shouldPreloadImage: !imgPreloadedOrFoundInHTML,\n lcpRequest,\n earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.Micro(earliestDiscoveryTime) : undefined,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"LCPDiscovery.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/LCPDiscovery.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAE7B,OAAO,KAAK,IAAI,MAAM,4BAA4B,CAAC;AACnD,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,OAAO,MAAM,uBAAuB,CAAC;AACjD,OAAO,KAAK,KAAK,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAEL,eAAe,EAGf,cAAc,GAGf,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;;OAEG;IACH,KAAK,EAAE,uBAAuB;IAC9B;;OAEG;IACH,WAAW,EACP,8NAA8N;IAClO;;;OAGG;IACH,YAAY,EAAE,oDAAoD;IAClE;;OAEG;IACH,oBAAoB,EAAE,4BAA4B;IAClD;;OAEG;IACH,mBAAmB,EAAE,6CAA6C;IAClE;;OAEG;IACH,kBAAkB,EAAE,uBAAuB;IAC3C;;OAEG;IACH,KAAK,EAAE,iBAAiB;IACxB;;OAEG;IACH,aAAa,EAAE,0DAA0D;CAC1E,CAAC;AAEF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,uCAAuC,EAAE,SAAS,CAAC,CAAC;AAC7F,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAE7E,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;AAC7E,CAAC;AAUD,SAAS,QAAQ,CAAC,YAA2D;IAC3E,MAAM,aAAa,GAAG,YAAY,CAAC,QAAQ,IAAI,YAAY,CAAC,UAAU,CAAC,CAAC;QACpE,4CAA4C;QAC5C,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC;QAClD,EAAE,CAAC;IACP,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,OAAO,CACf,YAAY,CAAC,UAAU,IAAI,YAAY,CAAC,SAAS;YACjD,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,mBAAmB,CAAC,KAAK;gBAChG,CAAC,YAAY,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACnD,GAAG,YAAY;QACf,aAAa;KACd,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC3B,WAAsC,EAAE,OAA0B;IACpE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,MAAM,eAAe,GAAG,WAAW,CAAC,eAAe,CAAC;IAEpD,MAAM,YAAY,GAAG,WAAW,CAAC,eAAe,CAAC,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5F,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,WAAW,GAAG,UAAU,CAAC,GAAG,mEAAuD,CAAC;IAC1F,MAAM,QAAQ,GAAG,WAAW,EAAE,KAAK,CAAC;IACpC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,iCAAiC,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3E,OAAO,QAAQ,CAAC,EAAC,QAAQ,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,EAAC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACxG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,QAAQ,CAAC,EAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,cAAc,CAAC,mBAAmB,CAAC,EAAC,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,QAAQ,CAAC,EAAC,QAAQ,EAAC,CAAC,CAAC;IAC9B,CAAC;IAED,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC;IACzD,mHAAmH;IACnH,wCAAwC;IACxC,MAAM,kBAAkB,GACpB,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,YAAY,CAAC;IACpG,MAAM,yBAAyB,GAAG,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,kBAAkB,CAAC;IAE5F,MAAM,gBAAgB,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;IACzD,MAAM,sBAAsB,GAAG,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC;IACvE,6EAA6E;IAC7E,MAAM,qBAAqB,GAAG,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YAClE,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC;QAClF,SAAS,CAAC;IAEd,OAAO,QAAQ,CAAC;QACd,QAAQ;QACR,UAAU;QACV,uBAAuB,EAAE,qBAAqB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,SAAS;QACtG,SAAS,EAAE;YACT,cAAc,EAAE,EAAC,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,oBAAoB,CAAC,EAAE,KAAK,EAAE,sBAAsB,KAAK,MAAM,EAAC;YAC7G,mBAAmB,EAAE,EAAC,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,mBAAmB,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAC;YACzG,aAAa,EAAE,EAAC,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,gBAAgB,KAAK,MAAM,EAAC;SACrG;KACF,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 Handlers from '../handlers/handlers.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {\n type Checklist,\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, and the network requests necessary to load it. Details how the LCP request was discoverable - in other words, the path necessary to load it (ex: network requests, JavaScript)\n */\n title: 'LCP request discovery',\n /**\n *@description Description of an insight that provides details about the LCP metric, and the network requests necessary to load it.\n */\n description:\n 'Optimize LCP by making the LCP image [discoverable](https://web.dev/articles/optimize-lcp#1_eliminate_resource_load_delay) from the HTML immediately, and [avoiding lazy-loading](https://web.dev/articles/lcp-lazy-loading)',\n /**\n * @description Text to tell the user how long after the earliest discovery time their LCP element loaded.\n * @example {401ms} PH1\n */\n lcpLoadDelay: 'LCP image loaded {PH1} after earliest start point.',\n /**\n * @description Text to tell the user that a fetchpriority property value of \"high\" is applied to the LCP request.\n */\n fetchPriorityApplied: 'fetchpriority=high applied',\n /**\n * @description Text to tell the user that the LCP request is discoverable in the initial document.\n */\n requestDiscoverable: 'Request is discoverable in initial document',\n /**\n * @description Text to tell the user that the LCP request does not have the lazy load property applied.\n */\n lazyLoadNotApplied: 'lazy load not applied',\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 /**\n * @description Text status indicating that the Largest Contentful Paint (LCP) metric was text rather than an image. \"LCP\" is an acronym and should not be translated.\n */\n noLcpResource: 'No LCP resource detected because the LCP is not an image',\n};\n\nconst str_ = i18n.i18n.registerUIStrings('models/trace/insights/LCPDiscovery.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\nexport type LCPDiscoveryInsightModel = InsightModel<typeof UIStrings, {\n lcpEvent?: Types.Events.LargestContentfulPaintCandidate,\n /** The network request for the LCP image, if there was one. */\n lcpRequest?: Types.Events.SyntheticNetworkRequest,\n earliestDiscoveryTimeTs?: Types.Timing.Micro,\n checklist?: Checklist<'priorityHinted'|'requestDiscoverable'|'eagerlyLoaded'>,\n}>;\n\nfunction finalize(partialModel: PartialInsightModel<LCPDiscoveryInsightModel>): LCPDiscoveryInsightModel {\n const relatedEvents = partialModel.lcpEvent && partialModel.lcpRequest ?\n // TODO: add entire request initiator chain?\n [partialModel.lcpEvent, partialModel.lcpRequest] :\n [];\n return {\n strings: UIStrings,\n title: i18nString(UIStrings.title),\n description: i18nString(UIStrings.description),\n category: InsightCategory.LCP,\n shouldShow: Boolean(\n partialModel.lcpRequest && partialModel.checklist &&\n (!partialModel.checklist.eagerlyLoaded.value || !partialModel.checklist.requestDiscoverable.value ||\n !partialModel.checklist.priorityHinted.value)),\n ...partialModel,\n relatedEvents,\n };\n}\n\nexport function generateInsight(\n parsedTrace: RequiredData<typeof deps>, context: InsightSetContext): LCPDiscoveryInsightModel {\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 const docRequest = networkRequests.byTime.find(req => req.args.data.requestId === context.navigationId);\n if (!docRequest) {\n return finalize({lcpEvent, warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});\n }\n\n const lcpRequest = parsedTrace.LargestImagePaint.lcpRequestByNavigation.get(context.navigation);\n if (!lcpRequest) {\n return finalize({lcpEvent});\n }\n\n const initiatorUrl = lcpRequest.args.data.initiator?.url;\n // TODO(b/372319476): Explore using trace event HTMLDocumentParser::FetchQueuedPreloads to determine if the request\n // is discovered by the preload scanner.\n const initiatedByMainDoc =\n lcpRequest?.args.data.initiator?.type === 'parser' && docRequest.args.data.url === initiatorUrl;\n const imgPreloadedOrFoundInHTML = lcpRequest?.args.data.isLinkPreload || initiatedByMainDoc;\n\n const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;\n const imageFetchPriorityHint = lcpRequest?.args.data.fetchPriorityHint;\n // This is the earliest discovery time an LCP request could have - it's TTFB.\n const earliestDiscoveryTime = docRequest && docRequest.args.data.timing ?\n Helpers.Timing.secondsToMicro(docRequest.args.data.timing.requestTime) +\n Helpers.Timing.milliToMicro(docRequest.args.data.timing.receiveHeadersStart) :\n undefined;\n\n return finalize({\n lcpEvent,\n lcpRequest,\n earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.Micro(earliestDiscoveryTime) : undefined,\n checklist: {\n priorityHinted: {label: i18nString(UIStrings.fetchPriorityApplied), value: imageFetchPriorityHint === 'high'},\n requestDiscoverable: {label: i18nString(UIStrings.requestDiscoverable), value: imgPreloadedOrFoundInHTML},\n eagerlyLoaded: {label: i18nString(UIStrings.lazyLoadNotApplied), value: imageLoadingAttr !== 'lazy'},\n },\n });\n}\n"]}
|
|
@@ -57,6 +57,10 @@ export declare enum InsightCategory {
|
|
|
57
57
|
CLS = "CLS"
|
|
58
58
|
}
|
|
59
59
|
export type RelatedEventsMap = Map<Types.Events.Event, string[]>;
|
|
60
|
+
export type Checklist<Keys extends string> = Record<Keys, {
|
|
61
|
+
label: string;
|
|
62
|
+
value: boolean;
|
|
63
|
+
}>;
|
|
60
64
|
export type InsightModel<UIStrings extends Record<string, string>, R extends Record<string, unknown>> = R & {
|
|
61
65
|
/** Not used within DevTools - this is for external consumers (like Lighthouse). */
|
|
62
66
|
strings: UIStrings;
|
|
@@ -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;AAkD7B,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 Protocol from '../../../generated/protocol.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.TraceWindowMicro;\n frameId: string;\n navigation?: never;\n}\n\nexport interface InsightSetContextWithNavigation {\n bounds: Types.Timing.TraceWindowMicro;\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 interface ForcedReflowAggregatedData {\n topLevelFunctionCall: Types.Events.CallFrame|Protocol.Runtime.CallFrame;\n totalReflowTime: number;\n bottomUpData: Set<string>;\n topLevelFunctionCallEvents: Types.Events.Event[];\n}\n\nexport interface BottomUpCallStack {\n bottomUpData: Types.Events.CallFrame|Protocol.Runtime.CallFrame;\n totalTime: number;\n relatedEvents: Types.Events.Event[];\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.Milli;\n LCP?: Types.Timing.Milli;\n TBT?: Types.Timing.Milli;\n CLS?: number;\n INP?: Types.Timing.Milli;\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<UIStrings extends Record<string, string>, R extends Record<string, unknown>> = R&{\n /** Not used within DevTools - this is for external consumers (like Lighthouse). */\n strings: UIStrings,\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\nexport type PartialInsightModel<T> = Omit<T, 'strings'|'title'|'description'|'category'|'shouldShow'>;\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.TraceWindowMicro;\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"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/insights/types.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,yEAAyE;AACzE,6BAA6B;AAkD7B,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 Protocol from '../../../generated/protocol.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.TraceWindowMicro;\n frameId: string;\n navigation?: never;\n}\n\nexport interface InsightSetContextWithNavigation {\n bounds: Types.Timing.TraceWindowMicro;\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 interface ForcedReflowAggregatedData {\n topLevelFunctionCall: Types.Events.CallFrame|Protocol.Runtime.CallFrame;\n totalReflowTime: number;\n bottomUpData: Set<string>;\n topLevelFunctionCallEvents: Types.Events.Event[];\n}\n\nexport interface BottomUpCallStack {\n bottomUpData: Types.Events.CallFrame|Protocol.Runtime.CallFrame;\n totalTime: number;\n relatedEvents: Types.Events.Event[];\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.Milli;\n LCP?: Types.Timing.Milli;\n TBT?: Types.Timing.Milli;\n CLS?: number;\n INP?: Types.Timing.Milli;\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 Checklist<Keys extends string> = Record<Keys, {label: Common.UIString.LocalizedString, value: boolean}>;\n\nexport type InsightModel<UIStrings extends Record<string, string>, R extends Record<string, unknown>> = R&{\n /** Not used within DevTools - this is for external consumers (like Lighthouse). */\n strings: UIStrings,\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\nexport type PartialInsightModel<T> = Omit<T, 'strings'|'title'|'description'|'category'|'shouldShow'>;\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.TraceWindowMicro;\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"]}
|
|
@@ -66,7 +66,7 @@ declare class BaseNode<T = Lantern.AnyNetworkObject> {
|
|
|
66
66
|
* node this was called on is not included in the resulting filtered graph, the method will throw.
|
|
67
67
|
*
|
|
68
68
|
* This does not clone NetworkNode's `record` or `rawRecord` fields. It may be reasonable to clone the former,
|
|
69
|
-
* to assist in graph construction, but the latter should never be cloned as one
|
|
69
|
+
* to assist in graph construction, but the latter should never be cloned as one constraint of Lantern is that
|
|
70
70
|
* the underlying data records are accessible for plain object reference equality checks.
|
|
71
71
|
*/
|
|
72
72
|
cloneWithRelationships(predicate?: (arg0: Node) => boolean): Node;
|
|
@@ -138,7 +138,7 @@ class BaseNode {
|
|
|
138
138
|
* node this was called on is not included in the resulting filtered graph, the method will throw.
|
|
139
139
|
*
|
|
140
140
|
* This does not clone NetworkNode's `record` or `rawRecord` fields. It may be reasonable to clone the former,
|
|
141
|
-
* to assist in graph construction, but the latter should never be cloned as one
|
|
141
|
+
* to assist in graph construction, but the latter should never be cloned as one constraint of Lantern is that
|
|
142
142
|
* the underlying data records are accessible for plain object reference equality checks.
|
|
143
143
|
*/
|
|
144
144
|
cloneWithRelationships(predicate) {
|