chrome-devtools-frontend 1.0.1526630 → 1.0.1528866
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/docs/ui_engineering.md +159 -0
- package/eslint.config.mjs +6 -1
- package/front_end/core/i18n/i18nImpl.ts +6 -1
- package/front_end/core/protocol_client/protocol_client.ts +1 -1
- package/front_end/core/root/Runtime.ts +28 -4
- package/front_end/core/sdk/CSSMatchedStyles.ts +50 -7
- package/front_end/core/sdk/CSSRule.ts +35 -6
- package/front_end/core/sdk/Connections.ts +2 -1
- package/front_end/core/sdk/DOMModel.ts +4 -0
- package/front_end/core/sdk/DebuggerModel.ts +5 -1
- package/front_end/core/sdk/NetworkManager.ts +214 -31
- package/front_end/core/sdk/PreloadingModel.ts +82 -17
- package/front_end/core/sdk/RehydratingConnection.snapshot.txt +1 -1
- package/front_end/core/sdk/RehydratingConnection.ts +29 -4
- package/front_end/core/sdk/ScopeTreeCache.ts +8 -3
- package/front_end/core/sdk/SourceMap.ts +37 -11
- package/front_end/core/sdk/SourceMapManager.ts +13 -2
- package/front_end/core/sdk/SourceMapScopesInfo.ts +17 -0
- package/front_end/core/sdk/TargetManager.ts +0 -22
- package/front_end/core/sdk/TraceObject.ts +8 -7
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +81 -0
- package/front_end/entrypoints/inspector_main/InspectorMain.ts +3 -1
- package/front_end/entrypoints/main/GlobalAiButton.ts +1 -0
- package/front_end/entrypoints/main/MainImpl.ts +20 -25
- package/front_end/generated/InspectorBackendCommands.js +3 -2
- package/front_end/generated/protocol.ts +17 -3
- package/front_end/models/ai_assistance/BuiltInAi.ts +111 -0
- package/front_end/models/ai_assistance/ai_assistance.ts +53 -24
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +105 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +6 -1
- package/front_end/models/extensions/ExtensionView.ts +3 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +23 -27
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +31 -29
- package/front_end/models/persistence/EditFileSystemView.ts +1 -0
- package/front_end/models/source_map_scopes/NamesResolver.ts +5 -11
- package/front_end/models/stack_trace/Trie.ts +9 -0
- package/front_end/models/trace/lantern/types/Lantern.ts +1 -1
- package/front_end/panels/accessibility/AXBreadcrumbsPane.ts +1 -0
- package/front_end/panels/accessibility/AccessibilitySidebarView.ts +1 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +120 -113
- package/front_end/panels/ai_assistance/PatchWidget.ts +9 -8
- package/front_end/panels/ai_assistance/SelectWorkspaceDialog.ts +2 -0
- package/front_end/panels/ai_assistance/components/ChatView.ts +29 -29
- package/front_end/panels/ai_assistance/components/UserActionRow.ts +1 -0
- package/front_end/panels/animation/AnimationTimeline.ts +1 -0
- package/front_end/panels/application/CookieItemsView.ts +1 -0
- package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -0
- package/front_end/panels/application/ServiceWorkerCacheViews.ts +2 -0
- package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +11 -5
- package/front_end/panels/application/preloading/components/PreloadingMismatchedHeadersGrid.ts +2 -2
- package/front_end/panels/application/preloading/components/PreloadingString.ts +7 -5
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +22 -10
- package/front_end/panels/changes/CombinedDiffView.ts +1 -0
- package/front_end/panels/console/ConsoleInsightTeaser.ts +106 -0
- package/front_end/panels/console/ConsolePanel.ts +2 -0
- package/front_end/panels/console/ConsolePrompt.ts +12 -2
- package/front_end/panels/console/ConsoleSidebar.ts +1 -1
- package/front_end/panels/console/ConsoleView.ts +12 -0
- package/front_end/panels/console/ConsoleViewMessage.ts +27 -0
- package/front_end/panels/{explain → console}/PromptBuilder.ts +12 -7
- package/front_end/panels/console/console.ts +6 -0
- package/front_end/panels/console/consoleInsightTeaser.css +55 -0
- package/front_end/panels/coverage/CoverageListView.ts +29 -11
- package/front_end/panels/coverage/CoverageView.ts +292 -284
- package/front_end/panels/coverage/coverageView.css +17 -0
- package/front_end/panels/elements/ComputedStyleWidget.ts +1 -0
- package/front_end/panels/elements/LayoutPane.ts +1 -0
- package/front_end/panels/elements/NodeStackTraceWidget.ts +1 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +5 -1
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +17 -0
- package/front_end/panels/emulation/DeviceModeView.ts +2 -0
- package/front_end/panels/explain/ActionDelegate.ts +1 -2
- package/front_end/panels/explain/components/ConsoleInsight.ts +14 -12
- package/front_end/panels/explain/explain.ts +0 -1
- package/front_end/panels/js_timeline/js_timeline-meta.ts +1 -1
- package/front_end/panels/layer_viewer/Layers3DView.ts +2 -0
- package/front_end/panels/lighthouse/LighthouseReportSelector.ts +1 -0
- package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +1 -0
- package/front_end/panels/media/MainView.ts +1 -0
- package/front_end/panels/media/TickingFlameChart.ts +2 -0
- package/front_end/panels/network/BlockedURLsPane.ts +111 -85
- package/front_end/panels/network/EventSourceMessagesView.ts +1 -0
- package/front_end/panels/network/NetworkItemView.ts +1 -0
- package/front_end/panels/network/NetworkLogView.ts +9 -7
- package/front_end/panels/network/NetworkOverview.ts +1 -0
- package/front_end/panels/network/RequestCookiesView.ts +1 -0
- package/front_end/panels/network/RequestHTMLView.ts +1 -0
- package/front_end/panels/network/RequestInitiatorView.ts +1 -0
- package/front_end/panels/network/RequestPayloadView.ts +1 -0
- package/front_end/panels/network/RequestPreviewView.ts +1 -0
- package/front_end/panels/network/RequestResponseView.ts +1 -0
- package/front_end/panels/network/RequestTimingView.ts +2 -0
- package/front_end/panels/network/ResourceDirectSocketChunkView.ts +1 -0
- package/front_end/panels/network/ResourceWebSocketFrameView.ts +1 -0
- package/front_end/panels/network/components/RequestHeadersView.ts +2 -0
- package/front_end/panels/network/components/RequestTrustTokensView.ts +2 -0
- package/front_end/panels/performance_monitor/PerformanceMonitor.ts +2 -0
- package/front_end/panels/profiler/HeapSnapshotDataGrids.ts +2 -0
- package/front_end/panels/profiler/HeapSnapshotView.ts +7 -0
- package/front_end/panels/profiler/IsolateSelector.ts +1 -0
- package/front_end/panels/profiler/LiveHeapProfileView.ts +1 -0
- package/front_end/panels/profiler/ProfileView.ts +1 -0
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +1 -0
- package/front_end/panels/recorder/RecorderPanel.ts +2 -0
- package/front_end/panels/screencast/ScreencastView.ts +1 -0
- package/front_end/panels/search/SearchView.ts +1 -0
- package/front_end/panels/settings/AISettingsTab.ts +3 -3
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +2 -0
- package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +2 -2
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +12 -0
- package/front_end/panels/sources/BreakpointsView.ts +1 -0
- package/front_end/panels/sources/DebuggerPlugin.ts +1 -0
- package/front_end/panels/sources/UISourceCodeFrame.ts +17 -2
- package/front_end/panels/timeline/README.md +2 -2
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartView.ts +4 -3
- package/front_end/panels/timeline/TimelineLayersView.ts +1 -0
- package/front_end/panels/timeline/TimelinePaintProfilerView.ts +114 -37
- package/front_end/panels/timeline/TimelinePanel.ts +43 -62
- package/front_end/panels/timeline/TimelineTreeView.ts +1 -0
- package/front_end/panels/timeline/components/LiveMetricsView.ts +4 -8
- package/front_end/panels/timeline/components/Sidebar.ts +2 -0
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +7 -7
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +1 -1
- package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +4 -4
- package/front_end/panels/web_audio/WebAudioView.ts +1 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/lighthouse/README.chromium +2 -2
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1530 -2426
- package/front_end/third_party/lighthouse/locales/ar-XB.json +107 -455
- package/front_end/third_party/lighthouse/locales/ar.json +107 -455
- package/front_end/third_party/lighthouse/locales/bg.json +96 -444
- package/front_end/third_party/lighthouse/locales/ca.json +96 -444
- package/front_end/third_party/lighthouse/locales/cs.json +96 -444
- package/front_end/third_party/lighthouse/locales/da.json +96 -444
- package/front_end/third_party/lighthouse/locales/de.json +96 -444
- package/front_end/third_party/lighthouse/locales/el.json +96 -444
- package/front_end/third_party/lighthouse/locales/en-GB.json +96 -444
- package/front_end/third_party/lighthouse/locales/en-US.json +116 -467
- package/front_end/third_party/lighthouse/locales/en-XA.json +93 -441
- package/front_end/third_party/lighthouse/locales/en-XL.json +116 -467
- package/front_end/third_party/lighthouse/locales/es-419.json +96 -444
- package/front_end/third_party/lighthouse/locales/es.json +96 -444
- package/front_end/third_party/lighthouse/locales/fi.json +96 -444
- package/front_end/third_party/lighthouse/locales/fil.json +96 -444
- package/front_end/third_party/lighthouse/locales/fr.json +96 -444
- package/front_end/third_party/lighthouse/locales/he.json +118 -466
- package/front_end/third_party/lighthouse/locales/hi.json +96 -444
- package/front_end/third_party/lighthouse/locales/hr.json +100 -448
- package/front_end/third_party/lighthouse/locales/hu.json +96 -444
- package/front_end/third_party/lighthouse/locales/id.json +96 -444
- package/front_end/third_party/lighthouse/locales/it.json +96 -444
- package/front_end/third_party/lighthouse/locales/ja.json +96 -444
- package/front_end/third_party/lighthouse/locales/ko.json +97 -445
- package/front_end/third_party/lighthouse/locales/lt.json +96 -444
- package/front_end/third_party/lighthouse/locales/lv.json +97 -445
- package/front_end/third_party/lighthouse/locales/nl.json +96 -444
- package/front_end/third_party/lighthouse/locales/no.json +96 -444
- package/front_end/third_party/lighthouse/locales/pl.json +96 -444
- package/front_end/third_party/lighthouse/locales/pt-PT.json +96 -444
- package/front_end/third_party/lighthouse/locales/pt.json +97 -445
- package/front_end/third_party/lighthouse/locales/ro.json +97 -445
- package/front_end/third_party/lighthouse/locales/ru.json +96 -444
- package/front_end/third_party/lighthouse/locales/sk.json +96 -444
- package/front_end/third_party/lighthouse/locales/sl.json +96 -444
- package/front_end/third_party/lighthouse/locales/sr-Latn.json +96 -444
- package/front_end/third_party/lighthouse/locales/sr.json +96 -444
- package/front_end/third_party/lighthouse/locales/sv.json +96 -444
- package/front_end/third_party/lighthouse/locales/ta.json +96 -444
- package/front_end/third_party/lighthouse/locales/te.json +97 -445
- package/front_end/third_party/lighthouse/locales/th.json +96 -444
- package/front_end/third_party/lighthouse/locales/tr.json +96 -444
- package/front_end/third_party/lighthouse/locales/uk.json +96 -444
- package/front_end/third_party/lighthouse/locales/vi.json +96 -444
- package/front_end/third_party/lighthouse/locales/zh-HK.json +96 -444
- package/front_end/third_party/lighthouse/locales/zh-TW.json +97 -445
- package/front_end/third_party/lighthouse/locales/zh.json +96 -444
- package/front_end/third_party/lighthouse/report/bundle.d.ts +8 -14
- package/front_end/third_party/lighthouse/report/bundle.js +10 -49
- package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +1 -1
- package/front_end/third_party/web-vitals/README.chromium +5 -8
- package/front_end/third_party/web-vitals/package/README.md +191 -152
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/index.d.ts +0 -1
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/index.js +0 -1
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onCLS.d.ts +2 -2
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onCLS.js +45 -26
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFCP.d.ts +2 -2
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFCP.js +3 -3
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.d.ts +10 -10
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +307 -206
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.d.ts +2 -2
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.js +69 -49
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onTTFB.d.ts +2 -2
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onTTFB.js +2 -2
- package/front_end/third_party/web-vitals/package/dist/modules/index.d.ts +0 -1
- package/front_end/third_party/web-vitals/package/dist/modules/index.js +0 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.d.ts +33 -0
- package/front_end/third_party/web-vitals/package/dist/modules/lib/InteractionManager.js +111 -0
- package/front_end/third_party/web-vitals/package/dist/modules/lib/LCPEntryManager.d.ts +4 -0
- package/front_end/third_party/web-vitals/package/dist/modules/{attribution/deprecated.js → lib/LCPEntryManager.js} +6 -7
- package/front_end/third_party/web-vitals/package/dist/modules/lib/LayoutShiftManager.d.ts +6 -0
- package/front_end/third_party/web-vitals/package/dist/modules/lib/LayoutShiftManager.js +44 -0
- package/front_end/third_party/web-vitals/package/dist/modules/lib/bindReporter.js +1 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/generateUniqueID.js +1 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/getActivationStart.js +1 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/getNavigationEntry.js +5 -7
- package/front_end/third_party/web-vitals/package/dist/modules/lib/getSelector.d.ts +1 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/getSelector.js +9 -12
- package/front_end/third_party/web-vitals/package/dist/modules/lib/getVisibilityWatcher.d.ts +1 -0
- package/front_end/third_party/web-vitals/package/dist/modules/lib/getVisibilityWatcher.js +52 -33
- package/front_end/third_party/web-vitals/package/dist/modules/lib/initMetric.d.ts +0 -2
- package/front_end/third_party/web-vitals/package/dist/modules/lib/initMetric.js +2 -2
- package/front_end/third_party/web-vitals/package/dist/modules/lib/initUnique.d.ts +6 -0
- package/front_end/third_party/web-vitals/package/dist/modules/{deprecated.js → lib/initUnique.js} +11 -4
- package/front_end/third_party/web-vitals/package/dist/modules/lib/observe.js +3 -6
- package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/interactionCountPolyfill.js +6 -6
- package/front_end/third_party/web-vitals/package/dist/modules/lib/{whenIdle.d.ts → whenIdleOrHidden.d.ts} +1 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/{whenIdle.js → whenIdleOrHidden.js} +10 -8
- package/front_end/third_party/web-vitals/package/dist/modules/onCLS.js +17 -35
- package/front_end/third_party/web-vitals/package/dist/modules/onFCP.js +3 -5
- package/front_end/third_party/web-vitals/package/dist/modules/onINP.d.ts +9 -7
- package/front_end/third_party/web-vitals/package/dist/modules/onINP.js +27 -19
- package/front_end/third_party/web-vitals/package/dist/modules/onLCP.js +33 -26
- package/front_end/third_party/web-vitals/package/dist/modules/onTTFB.js +2 -4
- package/front_end/third_party/web-vitals/package/dist/modules/types/base.d.ts +6 -5
- package/front_end/third_party/web-vitals/package/dist/modules/types/cls.d.ts +5 -3
- package/front_end/third_party/web-vitals/package/dist/modules/types/inp.d.ts +80 -33
- package/front_end/third_party/web-vitals/package/dist/modules/types/lcp.d.ts +6 -2
- package/front_end/third_party/web-vitals/package/dist/modules/types.d.ts +28 -4
- package/front_end/third_party/web-vitals/package/dist/modules/types.js +0 -1
- package/front_end/third_party/web-vitals/package/package.json +4 -10
- package/front_end/third_party/web-vitals/package/src/attribution/index.ts +0 -1
- package/front_end/third_party/web-vitals/package/src/attribution/onCLS.ts +58 -33
- package/front_end/third_party/web-vitals/package/src/attribution/onFCP.ts +4 -4
- package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +382 -258
- package/front_end/third_party/web-vitals/package/src/attribution/onLCP.ts +96 -69
- package/front_end/third_party/web-vitals/package/src/attribution/onTTFB.ts +3 -3
- package/front_end/third_party/web-vitals/package/src/index.ts +0 -1
- package/front_end/third_party/web-vitals/package/src/lib/InteractionManager.ts +146 -0
- package/front_end/third_party/web-vitals/package/src/{attribution/deprecated.ts → lib/LCPEntryManager.ts} +6 -9
- package/front_end/third_party/web-vitals/package/src/lib/LayoutShiftManager.ts +50 -0
- package/front_end/third_party/web-vitals/package/src/lib/bindReporter.ts +1 -1
- package/front_end/third_party/web-vitals/package/src/lib/generateUniqueID.ts +1 -1
- package/front_end/third_party/web-vitals/package/src/lib/getActivationStart.ts +1 -1
- package/front_end/third_party/web-vitals/package/src/lib/getNavigationEntry.ts +5 -8
- package/front_end/third_party/web-vitals/package/src/lib/getSelector.ts +12 -12
- package/front_end/third_party/web-vitals/package/src/lib/getVisibilityWatcher.ts +57 -35
- package/front_end/third_party/web-vitals/package/src/lib/initMetric.ts +2 -2
- package/front_end/third_party/web-vitals/package/src/{deprecated.ts → lib/initUnique.ts} +14 -8
- package/front_end/third_party/web-vitals/package/src/lib/observe.ts +3 -11
- package/front_end/third_party/web-vitals/package/src/lib/polyfills/interactionCountPolyfill.ts +12 -6
- package/front_end/third_party/web-vitals/package/src/lib/{whenIdle.ts → whenIdleOrHidden.ts} +10 -8
- package/front_end/third_party/web-vitals/package/src/onCLS.ts +17 -38
- package/front_end/third_party/web-vitals/package/src/onFCP.ts +3 -6
- package/front_end/third_party/web-vitals/package/src/onINP.ts +33 -28
- package/front_end/third_party/web-vitals/package/src/onLCP.ts +36 -29
- package/front_end/third_party/web-vitals/package/src/onTTFB.ts +2 -5
- package/front_end/third_party/web-vitals/package/src/types/base.ts +5 -5
- package/front_end/third_party/web-vitals/package/src/types/cls.ts +5 -3
- package/front_end/third_party/web-vitals/package/src/types/inp.ts +88 -33
- package/front_end/third_party/web-vitals/package/src/types/lcp.ts +6 -2
- package/front_end/third_party/web-vitals/package/src/types.ts +47 -4
- package/front_end/third_party/web-vitals/patches/0001-Add-onEachInteraction-to-onINP-options.patch +75 -0
- package/front_end/third_party/web-vitals/rebuild.sh +32 -18
- package/front_end/third_party/web-vitals/web-vitals-tsconfig.json +5 -10
- package/front_end/third_party/web-vitals/web-vitals.ts +0 -2
- package/front_end/ui/components/docs/console_insight/basic.ts +3 -2
- package/front_end/ui/components/legacy_wrapper/LegacyWrapper.ts +2 -0
- package/front_end/ui/components/text_editor/TextEditor.ts +0 -2
- package/front_end/ui/legacy/InspectorView.ts +2 -0
- package/front_end/ui/legacy/SplitWidget.ts +2 -0
- package/front_end/ui/legacy/TabbedPane.ts +1 -0
- package/front_end/ui/legacy/TargetCrashedScreen.ts +1 -0
- package/front_end/ui/legacy/UIUtils.ts +8 -19
- package/front_end/ui/legacy/ViewManager.ts +1 -0
- package/front_end/ui/legacy/components/color_picker/FormatPickerContextMenu.ts +7 -20
- package/front_end/ui/legacy/components/color_picker/Spectrum.ts +2 -0
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +1 -0
- package/front_end/ui/legacy/components/inline_editor/BezierEditor.ts +1 -0
- package/front_end/ui/legacy/components/perf_ui/ChartViewport.ts +1 -0
- package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +1 -0
- package/front_end/ui/legacy/components/source_frame/FontView.ts +1 -0
- package/front_end/ui/legacy/components/source_frame/ImageView.ts +1 -0
- package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -0
- package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +1 -0
- package/front_end/ui/legacy/components/source_frame/StreamingContentHexView.ts +2 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +17 -0
- package/mcp/README.md +7 -0
- package/mcp/mcp.ts +8 -0
- package/package.json +1 -1
- package/front_end/models/live-metrics/web-vitals-injected/OnEachInteraction.ts +0 -34
- package/front_end/third_party/web-vitals/package/attribution.d.ts +0 -16
- package/front_end/third_party/web-vitals/package/attribution.js +0 -18
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/deprecated.d.ts +0 -7
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFID.d.ts +0 -11
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onFID.js +0 -46
- package/front_end/third_party/web-vitals/package/dist/modules/deprecated.d.ts +0 -5
- package/front_end/third_party/web-vitals/package/dist/modules/lib/interactions.d.ts +0 -31
- package/front_end/third_party/web-vitals/package/dist/modules/lib/interactions.js +0 -107
- package/front_end/third_party/web-vitals/package/dist/modules/lib/onHidden.d.ts +0 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/onHidden.js +0 -22
- package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/firstInputPolyfill.d.ts +0 -7
- package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/firstInputPolyfill.js +0 -147
- package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/getFirstHiddenTimePolyfill.d.ts +0 -1
- package/front_end/third_party/web-vitals/package/dist/modules/lib/polyfills/getFirstHiddenTimePolyfill.js +0 -25
- package/front_end/third_party/web-vitals/package/dist/modules/onFID.d.ts +0 -13
- package/front_end/third_party/web-vitals/package/dist/modules/onFID.js +0 -70
- package/front_end/third_party/web-vitals/package/dist/modules/types/fid.d.ts +0 -46
- package/front_end/third_party/web-vitals/package/dist/modules/types/fid.js +0 -16
- package/front_end/third_party/web-vitals/package/src/attribution/onFID.ts +0 -62
- package/front_end/third_party/web-vitals/package/src/lib/interactions.ts +0 -139
- package/front_end/third_party/web-vitals/package/src/lib/onHidden.ts +0 -23
- package/front_end/third_party/web-vitals/package/src/lib/polyfills/firstInputPolyfill.ts +0 -174
- package/front_end/third_party/web-vitals/package/src/onFID.ts +0 -105
- package/front_end/third_party/web-vitals/package/src/types/fid.ts +0 -65
- package/front_end/ui/components/text_editor/textEditor.css +0 -18
- package/front_end/ui/legacy/inlineButton.css +0 -22
- /package/front_end/entrypoints/{rehydrated_devtools_app/rehydrated_devtools_app.ts → trace_app/trace_app.ts} +0 -0
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { getLoadState } from '../lib/getLoadState.js';
|
|
17
17
|
import { getSelector } from '../lib/getSelector.js';
|
|
18
|
-
import {
|
|
18
|
+
import { initUnique } from '../lib/initUnique.js';
|
|
19
|
+
import { InteractionManager } from '../lib/InteractionManager.js';
|
|
19
20
|
import { observe } from '../lib/observe.js';
|
|
20
|
-
import {
|
|
21
|
+
import { whenIdleOrHidden } from '../lib/whenIdleOrHidden.js';
|
|
21
22
|
import { onINP as unattributedOnINP } from '../onINP.js';
|
|
22
23
|
// The maximum number of previous frames for which data is kept.
|
|
23
24
|
// Storing data about previous frames is necessary to handle cases where event
|
|
@@ -27,212 +28,19 @@ import { onINP as unattributedOnINP } from '../onINP.js';
|
|
|
27
28
|
// In most cases this out-of-order data is only off by a frame or two, so
|
|
28
29
|
// keeping the most recent 50 should be more than sufficient.
|
|
29
30
|
const MAX_PREVIOUS_FRAMES = 50;
|
|
30
|
-
// A PerformanceObserver, observing new `long-animation-frame` entries.
|
|
31
|
-
// If this variable is defined it means the browser supports LoAF.
|
|
32
|
-
let loafObserver;
|
|
33
|
-
// A list of LoAF entries that have been dispatched and could potentially
|
|
34
|
-
// intersect with the INP candidate interaction. Note that periodically this
|
|
35
|
-
// list is cleaned up and entries that are known to not match INP are removed.
|
|
36
|
-
let pendingLoAFs = [];
|
|
37
|
-
// An array of groups of all the event timing entries that occurred within a
|
|
38
|
-
// particular frame. Note that periodically this array is cleaned up and entries
|
|
39
|
-
// that are known to not match INP are removed.
|
|
40
|
-
let pendingEntriesGroups = [];
|
|
41
|
-
// The `processingEnd` time of most recently-processed event, chronologically.
|
|
42
|
-
let latestProcessingEnd = 0;
|
|
43
|
-
// A WeakMap to look up the event-timing-entries group of a given entry.
|
|
44
|
-
// Note that this only maps from "important" entries: either the first input or
|
|
45
|
-
// those with an `interactionId`.
|
|
46
|
-
const entryToEntriesGroupMap = new WeakMap();
|
|
47
|
-
// A mapping of interactionIds to the target Node.
|
|
48
|
-
export const interactionTargetMap = new Map();
|
|
49
|
-
// A reference to the idle task used to clean up entries from the above
|
|
50
|
-
// variables. If the value is -1 it means no task is queue, and if it's
|
|
51
|
-
// greater than -1 the value corresponds to the idle callback handle.
|
|
52
|
-
let idleHandle = -1;
|
|
53
|
-
/**
|
|
54
|
-
* Adds new LoAF entries to the `pendingLoAFs` list.
|
|
55
|
-
*/
|
|
56
|
-
const handleLoAFEntries = (entries) => {
|
|
57
|
-
pendingLoAFs = pendingLoAFs.concat(entries);
|
|
58
|
-
queueCleanup();
|
|
59
|
-
};
|
|
60
|
-
// Get a reference to the interaction target element in case it's removed
|
|
61
|
-
// from the DOM later.
|
|
62
|
-
const saveInteractionTarget = (entry) => {
|
|
63
|
-
// TODO(b/376777343): Remove this modification when web-vitals.js doesn't retain DOM nodes anymore
|
|
64
|
-
// Although it is useful for DevTools to retain nodes for diagnostic purposes, it is not preferable
|
|
65
|
-
// to retaining Nodes in memory when the user does not expect them to.
|
|
66
|
-
//
|
|
67
|
-
// if (entry.interactionId &&
|
|
68
|
-
// entry.target &&
|
|
69
|
-
// !interactionTargetMap.has(entry.interactionId)) {
|
|
70
|
-
// interactionTargetMap.set(entry.interactionId, entry.target);
|
|
71
|
-
// }
|
|
72
|
-
};
|
|
73
|
-
/**
|
|
74
|
-
* Groups entries that were presented within the same animation frame by
|
|
75
|
-
* a common `renderTime`. This function works by referencing
|
|
76
|
-
* `pendingEntriesGroups` and using an existing render time if one is found
|
|
77
|
-
* (otherwise creating a new one). This function also adds all interaction
|
|
78
|
-
* entries to an `entryToRenderTimeMap` WeakMap so that the "grouped" entries
|
|
79
|
-
* can be looked up later.
|
|
80
|
-
*/
|
|
81
|
-
const groupEntriesByRenderTime = (entry) => {
|
|
82
|
-
const renderTime = entry.startTime + entry.duration;
|
|
83
|
-
let group;
|
|
84
|
-
latestProcessingEnd = Math.max(latestProcessingEnd, entry.processingEnd);
|
|
85
|
-
// Iterate over all previous render times in reverse order to find a match.
|
|
86
|
-
// Go in reverse since the most likely match will be at the end.
|
|
87
|
-
for (let i = pendingEntriesGroups.length - 1; i >= 0; i--) {
|
|
88
|
-
const potentialGroup = pendingEntriesGroups[i];
|
|
89
|
-
// If a group's render time is within 8ms of the entry's render time,
|
|
90
|
-
// assume they were part of the same frame and add it to the group.
|
|
91
|
-
if (Math.abs(renderTime - potentialGroup.renderTime) <= 8) {
|
|
92
|
-
group = potentialGroup;
|
|
93
|
-
group.startTime = Math.min(entry.startTime, group.startTime);
|
|
94
|
-
group.processingStart = Math.min(entry.processingStart, group.processingStart);
|
|
95
|
-
group.processingEnd = Math.max(entry.processingEnd, group.processingEnd);
|
|
96
|
-
group.entries.push(entry);
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// If there was no matching group, assume this is a new frame.
|
|
101
|
-
if (!group) {
|
|
102
|
-
group = {
|
|
103
|
-
startTime: entry.startTime,
|
|
104
|
-
processingStart: entry.processingStart,
|
|
105
|
-
processingEnd: entry.processingEnd,
|
|
106
|
-
renderTime,
|
|
107
|
-
entries: [entry],
|
|
108
|
-
};
|
|
109
|
-
pendingEntriesGroups.push(group);
|
|
110
|
-
}
|
|
111
|
-
// Store the grouped render time for this entry for reference later.
|
|
112
|
-
if (entry.interactionId || entry.entryType === 'first-input') {
|
|
113
|
-
entryToEntriesGroupMap.set(entry, group);
|
|
114
|
-
}
|
|
115
|
-
queueCleanup();
|
|
116
|
-
};
|
|
117
|
-
const queueCleanup = () => {
|
|
118
|
-
// Queue cleanup of entries that are not part of any INP candidates.
|
|
119
|
-
if (idleHandle < 0) {
|
|
120
|
-
idleHandle = whenIdle(cleanupEntries);
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
const cleanupEntries = () => {
|
|
124
|
-
// Delete any stored interaction target elements if they're not part of one
|
|
125
|
-
// of the 10 longest interactions.
|
|
126
|
-
if (interactionTargetMap.size > 10) {
|
|
127
|
-
interactionTargetMap.forEach((_, key) => {
|
|
128
|
-
if (!longestInteractionMap.has(key)) {
|
|
129
|
-
interactionTargetMap.delete(key);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
// Keep all render times that are part of a pending INP candidate or
|
|
134
|
-
// that occurred within the 50 most recently-dispatched groups of events.
|
|
135
|
-
const longestInteractionGroups = longestInteractionList.map((i) => {
|
|
136
|
-
return entryToEntriesGroupMap.get(i.entries[0]);
|
|
137
|
-
});
|
|
138
|
-
const minIndex = pendingEntriesGroups.length - MAX_PREVIOUS_FRAMES;
|
|
139
|
-
pendingEntriesGroups = pendingEntriesGroups.filter((group, index) => {
|
|
140
|
-
if (index >= minIndex)
|
|
141
|
-
return true;
|
|
142
|
-
return longestInteractionGroups.includes(group);
|
|
143
|
-
});
|
|
144
|
-
// Keep all pending LoAF entries that either:
|
|
145
|
-
// 1) intersect with entries in the newly cleaned up `pendingEntriesGroups`
|
|
146
|
-
// 2) occur after the most recently-processed event entry (for up to MAX_PREVIOUS_FRAMES)
|
|
147
|
-
const loafsToKeep = new Set();
|
|
148
|
-
for (let i = 0; i < pendingEntriesGroups.length; i++) {
|
|
149
|
-
const group = pendingEntriesGroups[i];
|
|
150
|
-
getIntersectingLoAFs(group.startTime, group.processingEnd).forEach((loaf) => {
|
|
151
|
-
loafsToKeep.add(loaf);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
const prevFrameIndexCutoff = pendingLoAFs.length - 1 - MAX_PREVIOUS_FRAMES;
|
|
155
|
-
// Filter `pendingLoAFs` to preserve LoAF order.
|
|
156
|
-
pendingLoAFs = pendingLoAFs.filter((loaf, index) => {
|
|
157
|
-
if (loaf.startTime > latestProcessingEnd && index > prevFrameIndexCutoff) {
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
return loafsToKeep.has(loaf);
|
|
161
|
-
});
|
|
162
|
-
// Reset the idle callback handle so it can be queued again.
|
|
163
|
-
idleHandle = -1;
|
|
164
|
-
};
|
|
165
|
-
entryPreProcessingCallbacks.push(saveInteractionTarget, groupEntriesByRenderTime);
|
|
166
|
-
const getIntersectingLoAFs = (start, end) => {
|
|
167
|
-
const intersectingLoAFs = [];
|
|
168
|
-
for (let i = 0, loaf; (loaf = pendingLoAFs[i]); i++) {
|
|
169
|
-
// If the LoAF ends before the given start time, ignore it.
|
|
170
|
-
if (loaf.startTime + loaf.duration < start)
|
|
171
|
-
continue;
|
|
172
|
-
// If the LoAF starts after the given end time, ignore it and all
|
|
173
|
-
// subsequent pending LoAFs (because they're in time order).
|
|
174
|
-
if (loaf.startTime > end)
|
|
175
|
-
break;
|
|
176
|
-
// Still here? If so this LoAF intersects with the interaction.
|
|
177
|
-
intersectingLoAFs.push(loaf);
|
|
178
|
-
}
|
|
179
|
-
return intersectingLoAFs;
|
|
180
|
-
};
|
|
181
|
-
export const attributeINP = (metric) => {
|
|
182
|
-
const firstEntry = metric.entries[0];
|
|
183
|
-
const group = entryToEntriesGroupMap.get(firstEntry);
|
|
184
|
-
const processingStart = firstEntry.processingStart;
|
|
185
|
-
const processingEnd = group.processingEnd;
|
|
186
|
-
// Sort the entries in processing time order.
|
|
187
|
-
const processedEventEntries = group.entries.sort((a, b) => {
|
|
188
|
-
return a.processingStart - b.processingStart;
|
|
189
|
-
});
|
|
190
|
-
const longAnimationFrameEntries = getIntersectingLoAFs(firstEntry.startTime, processingEnd);
|
|
191
|
-
// The first interaction entry may not have a target defined, so use the
|
|
192
|
-
// first one found in the entry list.
|
|
193
|
-
// TODO: when the following bug is fixed just use `firstInteractionEntry`.
|
|
194
|
-
// https://bugs.chromium.org/p/chromium/issues/detail?id=1367329
|
|
195
|
-
// As a fallback, also check the interactionTargetMap (to account for
|
|
196
|
-
// cases where the element is removed from the DOM before reporting happens).
|
|
197
|
-
const firstEntryWithTarget = metric.entries.find((entry) => entry.target);
|
|
198
|
-
const interactionTargetElement = (firstEntryWithTarget && firstEntryWithTarget.target) ||
|
|
199
|
-
interactionTargetMap.get(firstEntry.interactionId);
|
|
200
|
-
// Since entry durations are rounded to the nearest 8ms, we need to clamp
|
|
201
|
-
// the `nextPaintTime` value to be higher than the `processingEnd` or
|
|
202
|
-
// end time of any LoAF entry.
|
|
203
|
-
const nextPaintTimeCandidates = [
|
|
204
|
-
firstEntry.startTime + firstEntry.duration,
|
|
205
|
-
processingEnd,
|
|
206
|
-
].concat(longAnimationFrameEntries.map((loaf) => loaf.startTime + loaf.duration));
|
|
207
|
-
const nextPaintTime = Math.max.apply(Math, nextPaintTimeCandidates);
|
|
208
|
-
const attribution = {
|
|
209
|
-
interactionTarget: getSelector(interactionTargetElement),
|
|
210
|
-
interactionTargetElement: interactionTargetElement,
|
|
211
|
-
interactionType: firstEntry.name.startsWith('key') ? 'keyboard' : 'pointer',
|
|
212
|
-
interactionTime: firstEntry.startTime,
|
|
213
|
-
nextPaintTime: nextPaintTime,
|
|
214
|
-
processedEventEntries: processedEventEntries,
|
|
215
|
-
longAnimationFrameEntries: longAnimationFrameEntries,
|
|
216
|
-
inputDelay: processingStart - firstEntry.startTime,
|
|
217
|
-
processingDuration: processingEnd - processingStart,
|
|
218
|
-
presentationDelay: Math.max(nextPaintTime - processingEnd, 0),
|
|
219
|
-
loadState: getLoadState(firstEntry.startTime),
|
|
220
|
-
};
|
|
221
|
-
// Use Object.assign to set property to keep tsc happy.
|
|
222
|
-
const metricWithAttribution = Object.assign(metric, { attribution });
|
|
223
|
-
return metricWithAttribution;
|
|
224
|
-
};
|
|
225
31
|
/**
|
|
226
32
|
* Calculates the [INP](https://web.dev/articles/inp) value for the current
|
|
227
33
|
* page and calls the `callback` function once the value is ready, along with
|
|
228
34
|
* the `event` performance entries reported for that interaction. The reported
|
|
229
35
|
* value is a `DOMHighResTimeStamp`.
|
|
230
36
|
*
|
|
231
|
-
* A custom `durationThreshold` configuration option can optionally be passed
|
|
232
|
-
* control what `event-timing` entries are considered for INP reporting. The
|
|
233
|
-
* default threshold is `40`, which means INP scores of less than 40
|
|
234
|
-
* reported
|
|
235
|
-
*
|
|
37
|
+
* A custom `durationThreshold` configuration option can optionally be passed
|
|
38
|
+
* to control what `event-timing` entries are considered for INP reporting. The
|
|
39
|
+
* default threshold is `40`, which means INP scores of less than 40 will not
|
|
40
|
+
* be reported. To avoid reporting no interactions in these cases, the library
|
|
41
|
+
* will fall back to the input delay of the first interaction. Note that this
|
|
42
|
+
* will not affect your 75th percentile INP value unless that value is also
|
|
43
|
+
* less than 40 (well below the recommended
|
|
236
44
|
* [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold).
|
|
237
45
|
*
|
|
238
46
|
* If the `reportAllChanges` configuration option is set to `true`, the
|
|
@@ -242,17 +50,310 @@ export const attributeINP = (metric) => {
|
|
|
242
50
|
*
|
|
243
51
|
* _**Important:** INP should be continually monitored for changes throughout
|
|
244
52
|
* the entire lifespan of a page—including if the user returns to the page after
|
|
245
|
-
* it
|
|
53
|
+
* it has been hidden/backgrounded. However, since browsers often [will not fire
|
|
246
54
|
* additional callbacks once the user has backgrounded a
|
|
247
55
|
* page](https://developer.chrome.com/blog/page-lifecycle-api/#advice-hidden),
|
|
248
56
|
* `callback` is always called when the page's visibility state changes to
|
|
249
57
|
* hidden. As a result, the `callback` function might be called multiple times
|
|
250
58
|
* during the same page load._
|
|
251
59
|
*/
|
|
252
|
-
export const onINP = (onReport, opts) => {
|
|
253
|
-
|
|
254
|
-
|
|
60
|
+
export const onINP = (onReport, opts = {}) => {
|
|
61
|
+
// Clone the opts object to ensure it's unique, so we can initialize a
|
|
62
|
+
// single instance of the `InteractionManager` class that's shared only with
|
|
63
|
+
// this function invocation and the `unattributedOnINP()` invocation below
|
|
64
|
+
// (which is passed the same `opts` object).
|
|
65
|
+
opts = Object.assign({}, opts);
|
|
66
|
+
const interactionManager = initUnique(opts, InteractionManager);
|
|
67
|
+
// A list of LoAF entries that have been dispatched and could potentially
|
|
68
|
+
// intersect with the INP candidate interaction. Note that periodically this
|
|
69
|
+
// list is cleaned up and entries that are known to not match INP are removed.
|
|
70
|
+
let pendingLoAFs = [];
|
|
71
|
+
// An array of groups of all the event timing entries that occurred within a
|
|
72
|
+
// particular frame. Note that periodically this array is cleaned up and entries
|
|
73
|
+
// that are known to not match INP are removed.
|
|
74
|
+
let pendingEntriesGroups = [];
|
|
75
|
+
// The `processingEnd` time of most recently-processed event, chronologically.
|
|
76
|
+
let latestProcessingEnd = 0;
|
|
77
|
+
// A WeakMap to look up the event-timing-entries group of a given entry.
|
|
78
|
+
// Note that this only maps from "important" entries: either the first input or
|
|
79
|
+
// those with an `interactionId`.
|
|
80
|
+
const entryToEntriesGroupMap = new WeakMap();
|
|
81
|
+
// A mapping of interactionIds to the target Node.
|
|
82
|
+
const interactionTargetMap = new WeakMap();
|
|
83
|
+
// A boolean flag indicating whether or not a cleanup task has been queued.
|
|
84
|
+
let cleanupPending = false;
|
|
85
|
+
/**
|
|
86
|
+
* Adds new LoAF entries to the `pendingLoAFs` list.
|
|
87
|
+
*/
|
|
88
|
+
const handleLoAFEntries = (entries) => {
|
|
89
|
+
pendingLoAFs = pendingLoAFs.concat(entries);
|
|
90
|
+
queueCleanup();
|
|
91
|
+
};
|
|
92
|
+
const saveInteractionTarget = (interaction) => {
|
|
93
|
+
if (!interactionTargetMap.get(interaction)) {
|
|
94
|
+
const node = interaction.entries[0].target;
|
|
95
|
+
if (node) {
|
|
96
|
+
const customTarget = opts.generateTarget?.(node) ?? getSelector(node);
|
|
97
|
+
interactionTargetMap.set(interaction, customTarget);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Groups entries that were presented within the same animation frame by
|
|
103
|
+
* a common `renderTime`. This function works by referencing
|
|
104
|
+
* `pendingEntriesGroups` and using an existing render time if one is found
|
|
105
|
+
* (otherwise creating a new one). This function also adds all interaction
|
|
106
|
+
* entries to an `entryToRenderTimeMap` WeakMap so that the "grouped" entries
|
|
107
|
+
* can be looked up later.
|
|
108
|
+
*/
|
|
109
|
+
const groupEntriesByRenderTime = (entry) => {
|
|
110
|
+
const renderTime = entry.startTime + entry.duration;
|
|
111
|
+
let group;
|
|
112
|
+
latestProcessingEnd = Math.max(latestProcessingEnd, entry.processingEnd);
|
|
113
|
+
// Iterate over all previous render times in reverse order to find a match.
|
|
114
|
+
// Go in reverse since the most likely match will be at the end.
|
|
115
|
+
for (let i = pendingEntriesGroups.length - 1; i >= 0; i--) {
|
|
116
|
+
const potentialGroup = pendingEntriesGroups[i];
|
|
117
|
+
// If a group's render time is within 8ms of the entry's render time,
|
|
118
|
+
// assume they were part of the same frame and add it to the group.
|
|
119
|
+
if (Math.abs(renderTime - potentialGroup.renderTime) <= 8) {
|
|
120
|
+
group = potentialGroup;
|
|
121
|
+
group.startTime = Math.min(entry.startTime, group.startTime);
|
|
122
|
+
group.processingStart = Math.min(entry.processingStart, group.processingStart);
|
|
123
|
+
group.processingEnd = Math.max(entry.processingEnd, group.processingEnd);
|
|
124
|
+
group.entries.push(entry);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// If there was no matching group, assume this is a new frame.
|
|
129
|
+
if (!group) {
|
|
130
|
+
group = {
|
|
131
|
+
startTime: entry.startTime,
|
|
132
|
+
processingStart: entry.processingStart,
|
|
133
|
+
processingEnd: entry.processingEnd,
|
|
134
|
+
renderTime,
|
|
135
|
+
entries: [entry],
|
|
136
|
+
};
|
|
137
|
+
pendingEntriesGroups.push(group);
|
|
138
|
+
}
|
|
139
|
+
// Store the grouped render time for this entry for reference later.
|
|
140
|
+
if (entry.interactionId || entry.entryType === 'first-input') {
|
|
141
|
+
entryToEntriesGroupMap.set(entry, group);
|
|
142
|
+
}
|
|
143
|
+
queueCleanup();
|
|
144
|
+
};
|
|
145
|
+
const queueCleanup = () => {
|
|
146
|
+
// Queue cleanup of entries that are not part of any INP candidates.
|
|
147
|
+
if (!cleanupPending) {
|
|
148
|
+
whenIdleOrHidden(cleanupEntries);
|
|
149
|
+
cleanupPending = true;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
const cleanupEntries = () => {
|
|
153
|
+
// Keep all render times that are part of a pending INP candidate or
|
|
154
|
+
// that occurred within the 50 most recently-dispatched groups of events.
|
|
155
|
+
const longestInteractionGroups = interactionManager._longestInteractionList.map((i) => {
|
|
156
|
+
return entryToEntriesGroupMap.get(i.entries[0]);
|
|
157
|
+
});
|
|
158
|
+
const minIndex = pendingEntriesGroups.length - MAX_PREVIOUS_FRAMES;
|
|
159
|
+
pendingEntriesGroups = pendingEntriesGroups.filter((group, index) => {
|
|
160
|
+
if (index >= minIndex)
|
|
161
|
+
return true;
|
|
162
|
+
return longestInteractionGroups.includes(group);
|
|
163
|
+
});
|
|
164
|
+
// Keep all pending LoAF entries that either:
|
|
165
|
+
// 1) intersect with entries in the newly cleaned up `pendingEntriesGroups`
|
|
166
|
+
// 2) occur after the most recently-processed event entry (for up to MAX_PREVIOUS_FRAMES)
|
|
167
|
+
const loafsToKeep = new Set();
|
|
168
|
+
for (const group of pendingEntriesGroups) {
|
|
169
|
+
const loafs = getIntersectingLoAFs(group.startTime, group.processingEnd);
|
|
170
|
+
for (const loaf of loafs) {
|
|
171
|
+
loafsToKeep.add(loaf);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const prevFrameIndexCutoff = pendingLoAFs.length - 1 - MAX_PREVIOUS_FRAMES;
|
|
175
|
+
// Filter `pendingLoAFs` to preserve LoAF order.
|
|
176
|
+
pendingLoAFs = pendingLoAFs.filter((loaf, index) => {
|
|
177
|
+
if (loaf.startTime > latestProcessingEnd &&
|
|
178
|
+
index > prevFrameIndexCutoff) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
return loafsToKeep.has(loaf);
|
|
182
|
+
});
|
|
183
|
+
cleanupPending = false;
|
|
184
|
+
};
|
|
185
|
+
async function handleOnEachInteractionCallback(entry) {
|
|
186
|
+
if (!opts.onEachInteraction) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// Wait a microtask so this "pre" processing callback actually
|
|
190
|
+
// becomes a "post" processing callback.
|
|
191
|
+
void (await Promise.resolve());
|
|
192
|
+
if (!entry.interactionId) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const interaction = attributeINP({
|
|
196
|
+
entries: [entry],
|
|
197
|
+
// The only value we really need for `attributeINP` is `entries`
|
|
198
|
+
// Everything else is included to fill out the type.
|
|
199
|
+
name: 'INP',
|
|
200
|
+
rating: 'good',
|
|
201
|
+
value: entry.duration,
|
|
202
|
+
delta: entry.duration,
|
|
203
|
+
navigationType: 'navigate',
|
|
204
|
+
id: 'N/A',
|
|
205
|
+
});
|
|
206
|
+
opts.onEachInteraction(interaction);
|
|
255
207
|
}
|
|
208
|
+
interactionManager._onBeforeProcessingEntry = (entry) => {
|
|
209
|
+
void handleOnEachInteractionCallback(entry);
|
|
210
|
+
groupEntriesByRenderTime(entry);
|
|
211
|
+
};
|
|
212
|
+
interactionManager._onAfterProcessingINPCandidate = saveInteractionTarget;
|
|
213
|
+
const getIntersectingLoAFs = (start, end) => {
|
|
214
|
+
const intersectingLoAFs = [];
|
|
215
|
+
for (const loaf of pendingLoAFs) {
|
|
216
|
+
// If the LoAF ends before the given start time, ignore it.
|
|
217
|
+
if (loaf.startTime + loaf.duration < start)
|
|
218
|
+
continue;
|
|
219
|
+
// If the LoAF starts after the given end time, ignore it and all
|
|
220
|
+
// subsequent pending LoAFs (because they're in time order).
|
|
221
|
+
if (loaf.startTime > end)
|
|
222
|
+
break;
|
|
223
|
+
// Still here? If so this LoAF intersects with the interaction.
|
|
224
|
+
intersectingLoAFs.push(loaf);
|
|
225
|
+
}
|
|
226
|
+
return intersectingLoAFs;
|
|
227
|
+
};
|
|
228
|
+
const attributeLoAFDetails = (attribution) => {
|
|
229
|
+
// If there is no LoAF data then nothing further to attribute
|
|
230
|
+
if (!attribution.longAnimationFrameEntries?.length) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const interactionTime = attribution.interactionTime;
|
|
234
|
+
const inputDelay = attribution.inputDelay;
|
|
235
|
+
const processingDuration = attribution.processingDuration;
|
|
236
|
+
// Stats across all LoAF entries and scripts.
|
|
237
|
+
let totalScriptDuration = 0;
|
|
238
|
+
let totalStyleAndLayoutDuration = 0;
|
|
239
|
+
let totalPaintDuration = 0;
|
|
240
|
+
let longestScriptDuration = 0;
|
|
241
|
+
let longestScriptEntry;
|
|
242
|
+
let longestScriptSubpart;
|
|
243
|
+
for (const loafEntry of attribution.longAnimationFrameEntries) {
|
|
244
|
+
totalStyleAndLayoutDuration =
|
|
245
|
+
totalStyleAndLayoutDuration +
|
|
246
|
+
loafEntry.startTime +
|
|
247
|
+
loafEntry.duration -
|
|
248
|
+
loafEntry.styleAndLayoutStart;
|
|
249
|
+
for (const script of loafEntry.scripts) {
|
|
250
|
+
const scriptEndTime = script.startTime + script.duration;
|
|
251
|
+
if (scriptEndTime < interactionTime) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const intersectingScriptDuration = scriptEndTime - Math.max(interactionTime, script.startTime);
|
|
255
|
+
// Since forcedStyleAndLayoutDuration doesn't provide timestamps, we
|
|
256
|
+
// apportion the total based on the intersectingScriptDuration. Not
|
|
257
|
+
// correct depending on when it occurred, but the best we can do.
|
|
258
|
+
const intersectingForceStyleAndLayoutDuration = script.duration
|
|
259
|
+
? (intersectingScriptDuration / script.duration) *
|
|
260
|
+
script.forcedStyleAndLayoutDuration
|
|
261
|
+
: 0;
|
|
262
|
+
// For scripts we exclude forcedStyleAndLayout (same as DevTools does
|
|
263
|
+
// in its summary totals) and instead include that in
|
|
264
|
+
// totalStyleAndLayoutDuration
|
|
265
|
+
totalScriptDuration +=
|
|
266
|
+
intersectingScriptDuration - intersectingForceStyleAndLayoutDuration;
|
|
267
|
+
totalStyleAndLayoutDuration += intersectingForceStyleAndLayoutDuration;
|
|
268
|
+
if (intersectingScriptDuration > longestScriptDuration) {
|
|
269
|
+
// Set the subpart this occurred in.
|
|
270
|
+
longestScriptSubpart =
|
|
271
|
+
script.startTime < interactionTime + inputDelay
|
|
272
|
+
? 'input-delay'
|
|
273
|
+
: script.startTime >=
|
|
274
|
+
interactionTime + inputDelay + processingDuration
|
|
275
|
+
? 'presentation-delay'
|
|
276
|
+
: 'processing-duration';
|
|
277
|
+
longestScriptEntry = script;
|
|
278
|
+
longestScriptDuration = intersectingScriptDuration;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Calculate the totalPaintDuration from the last LoAF after
|
|
283
|
+
// presentationDelay starts (where available)
|
|
284
|
+
const lastLoAF = attribution.longAnimationFrameEntries.at(-1);
|
|
285
|
+
const lastLoAFEndTime = lastLoAF
|
|
286
|
+
? lastLoAF.startTime + lastLoAF.duration
|
|
287
|
+
: 0;
|
|
288
|
+
if (lastLoAFEndTime >= interactionTime + inputDelay + processingDuration) {
|
|
289
|
+
totalPaintDuration = attribution.nextPaintTime - lastLoAFEndTime;
|
|
290
|
+
}
|
|
291
|
+
if (longestScriptEntry && longestScriptSubpart) {
|
|
292
|
+
attribution.longestScript = {
|
|
293
|
+
entry: longestScriptEntry,
|
|
294
|
+
subpart: longestScriptSubpart,
|
|
295
|
+
intersectingDuration: longestScriptDuration,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
attribution.totalScriptDuration = totalScriptDuration;
|
|
299
|
+
attribution.totalStyleAndLayoutDuration = totalStyleAndLayoutDuration;
|
|
300
|
+
attribution.totalPaintDuration = totalPaintDuration;
|
|
301
|
+
attribution.totalUnattributedDuration =
|
|
302
|
+
attribution.nextPaintTime -
|
|
303
|
+
interactionTime -
|
|
304
|
+
totalScriptDuration -
|
|
305
|
+
totalStyleAndLayoutDuration -
|
|
306
|
+
totalPaintDuration;
|
|
307
|
+
};
|
|
308
|
+
const attributeINP = (metric) => {
|
|
309
|
+
const firstEntry = metric.entries[0];
|
|
310
|
+
const group = entryToEntriesGroupMap.get(firstEntry);
|
|
311
|
+
const processingStart = firstEntry.processingStart;
|
|
312
|
+
// Due to the fact that durations can be rounded down to the nearest 8ms,
|
|
313
|
+
// we have to clamp `nextPaintTime` so it doesn't appear to occur before
|
|
314
|
+
// processing starts. Note: we can't use `processingEnd` since processing
|
|
315
|
+
// can extend beyond the event duration in some cases (see next comment).
|
|
316
|
+
const nextPaintTime = Math.max(firstEntry.startTime + firstEntry.duration, processingStart);
|
|
317
|
+
// For the purposes of attribution, clamp `processingEnd` to `nextPaintTime`,
|
|
318
|
+
// so processing is never reported as taking longer than INP (which can
|
|
319
|
+
// happen via the web APIs in the case of sync modals, e.g. `alert()`).
|
|
320
|
+
// See: https://github.com/GoogleChrome/web-vitals/issues/492
|
|
321
|
+
const processingEnd = Math.min(group.processingEnd, nextPaintTime);
|
|
322
|
+
// Sort the entries in processing time order.
|
|
323
|
+
const processedEventEntries = group.entries.sort((a, b) => {
|
|
324
|
+
return a.processingStart - b.processingStart;
|
|
325
|
+
});
|
|
326
|
+
const longAnimationFrameEntries = getIntersectingLoAFs(firstEntry.startTime, processingEnd);
|
|
327
|
+
const interaction = interactionManager._longestInteractionMap.get(firstEntry.interactionId);
|
|
328
|
+
const attribution = {
|
|
329
|
+
// TS flags the next line because `interactionTargetMap.get()` might
|
|
330
|
+
// return `undefined`, but we ignore this assuming the user knows what
|
|
331
|
+
// they are doing.
|
|
332
|
+
interactionTarget: interactionTargetMap.get(interaction),
|
|
333
|
+
interactionType: firstEntry.name.startsWith('key')
|
|
334
|
+
? 'keyboard'
|
|
335
|
+
: 'pointer',
|
|
336
|
+
interactionTime: firstEntry.startTime,
|
|
337
|
+
nextPaintTime: nextPaintTime,
|
|
338
|
+
processedEventEntries: processedEventEntries,
|
|
339
|
+
longAnimationFrameEntries: longAnimationFrameEntries,
|
|
340
|
+
inputDelay: processingStart - firstEntry.startTime,
|
|
341
|
+
processingDuration: processingEnd - processingStart,
|
|
342
|
+
presentationDelay: nextPaintTime - processingEnd,
|
|
343
|
+
loadState: getLoadState(firstEntry.startTime),
|
|
344
|
+
longestScript: undefined,
|
|
345
|
+
totalScriptDuration: undefined,
|
|
346
|
+
totalStyleAndLayoutDuration: undefined,
|
|
347
|
+
totalPaintDuration: undefined,
|
|
348
|
+
totalUnattributedDuration: undefined,
|
|
349
|
+
};
|
|
350
|
+
attributeLoAFDetails(attribution);
|
|
351
|
+
// Use `Object.assign()` to ensure the original metric object is returned.
|
|
352
|
+
const metricWithAttribution = Object.assign(metric, { attribution });
|
|
353
|
+
return metricWithAttribution;
|
|
354
|
+
};
|
|
355
|
+
// Start observing LoAF entries for attribution.
|
|
356
|
+
observe('long-animation-frame', handleLoAFEntries);
|
|
256
357
|
unattributedOnINP((metric) => {
|
|
257
358
|
const metricWithAttribution = attributeINP(metric);
|
|
258
359
|
onReport(metricWithAttribution);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LCPMetricWithAttribution,
|
|
1
|
+
import { LCPMetricWithAttribution, AttributionReportOpts } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and
|
|
4
4
|
* calls the `callback` function once the value is ready (along with the
|
|
@@ -10,4 +10,4 @@ import { LCPMetricWithAttribution, ReportOpts } from '../types.js';
|
|
|
10
10
|
* performance entry is dispatched, or once the final value of the metric has
|
|
11
11
|
* been determined.
|
|
12
12
|
*/
|
|
13
|
-
export declare const onLCP: (onReport: (metric: LCPMetricWithAttribution) => void, opts?:
|
|
13
|
+
export declare const onLCP: (onReport: (metric: LCPMetricWithAttribution) => void, opts?: AttributionReportOpts) => void;
|