chrome-devtools-mcp-for-extension 0.19.2 → 0.20.0

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.
Files changed (153) hide show
  1. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Color.js +13 -9
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ColorConverter.js +9 -7
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Console.js +1 -8
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Debouncer.js +2 -2
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +9 -7
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/MapWithDefault.js +5 -3
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +10 -20
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +0 -11
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ReturnToPanel.js +6 -4
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Revealer.js +5 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/SegmentedRange.js +1 -2
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +110 -99
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/StringOutputStream.js +1 -4
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Throttler.js +2 -2
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/common.js +1 -4
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +21 -2
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/DispatchHttpRequestClient.js +66 -0
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +102 -86
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +32 -450
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostAPI.js +32 -29
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +430 -0
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/Platform.js +5 -3
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +10 -22
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +26 -15
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/host.js +2 -1
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +2 -2
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/HostRuntime.js +19 -0
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +34 -32
  29. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/api/HostRuntime.js +4 -0
  30. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/api/api.js +5 -0
  31. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/browser/HostRuntime.js +63 -0
  32. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/browser/browser.js +5 -0
  33. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/node/HostRuntime.js +72 -0
  34. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/node/node.js +5 -0
  35. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/platform.js +2 -2
  36. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +17 -0
  37. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/ConnectionTransport.js +12 -0
  38. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/DevToolsCDPConnection.js +138 -0
  39. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +49 -312
  40. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/protocol_client.js +4 -9
  41. package/build/node_modules/chrome-devtools-frontend/front_end/core/root/DevToolsContext.js +54 -0
  42. package/build/node_modules/chrome-devtools-frontend/front_end/core/root/root.js +2 -1
  43. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +2 -3
  44. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CPUThrottlingManager.js +0 -3
  45. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +51 -17
  46. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +21 -7
  47. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +11 -5
  48. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +4 -7
  49. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParser.js +11 -9
  50. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +34 -24
  51. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSRule.js +52 -10
  52. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStartingStyle.js +21 -0
  53. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStyleDeclaration.js +4 -4
  54. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +39 -35
  55. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Connections.js +10 -47
  56. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ConsoleModel.js +1 -1
  57. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Cookie.js +1 -1
  58. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +29 -4
  59. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +5 -20
  60. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EnhancedTracesParser.js +17 -3
  61. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EventBreakpointsModel.js +4 -2
  62. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/HttpReasonPhraseStrings.js +4 -2
  63. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/IOModel.js +1 -4
  64. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +428 -121
  65. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +8 -22
  66. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PageResourceLoader.js +44 -34
  67. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/PreloadingModel.js +64 -19
  68. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +139 -12
  69. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RemoteObject.js +5 -2
  70. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +3 -2
  71. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScopeTreeCache.js +36 -0
  72. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ScreenCaptureModel.js +20 -18
  73. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Script.js +26 -1
  74. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ServerSentEventsProtocol.js +3 -0
  75. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +54 -14
  76. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapCache.js +16 -0
  77. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +19 -7
  78. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +240 -8
  79. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +12 -17
  80. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TargetManager.js +38 -29
  81. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/TraceObject.js +11 -8
  82. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk-meta.js +80 -31
  83. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk.js +4 -2
  84. package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1301 -174
  85. package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +67 -22
  86. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +62 -91
  87. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +188 -100
  88. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +30 -3
  89. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +50 -34
  90. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +152 -101
  91. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +11 -6
  92. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +25 -26
  93. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIQueries.js +46 -2
  94. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CSSWorkspaceBinding.js +10 -8
  95. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +82 -3
  96. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +33 -15
  97. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceMapping.js +105 -14
  98. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/ResourceScriptMapping.js +38 -0
  99. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/SASSSourceMapping.js +5 -4
  100. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/CPUProfileDataModel.js +9 -7
  101. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/ProfileTreeModel.js +6 -7
  102. package/build/node_modules/chrome-devtools-frontend/front_end/models/crux-manager/CrUXManager.js +6 -4
  103. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +16 -2
  104. package/build/node_modules/chrome-devtools-frontend/front_end/models/network_time_calculator/RequestTimeRanges.js +6 -4
  105. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/FunctionCodeResolver.js +192 -0
  106. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/NamesResolver.js +12 -16
  107. package/build/node_modules/chrome-devtools-frontend/front_end/models/source_map_scopes/source_map_scopes.js +2 -2
  108. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +1 -1
  109. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/Trie.js +8 -0
  110. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +1 -0
  111. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/ModelImpl.js +6 -3
  112. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +4 -4
  113. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Styles.js +1 -1
  114. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +11 -4
  115. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/FramesHandler.js +7 -5
  116. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +8 -4
  117. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +4 -1
  118. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +29 -3
  119. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/SamplesHandler.js +59 -6
  120. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/ScriptsHandler.js +26 -1
  121. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/UserTimingsHandler.js +1 -1
  122. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/helpers.js +1 -1
  123. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +4 -2
  124. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Trace.js +18 -4
  125. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/CLSCulprits.js +2 -1
  126. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Cache.js +2 -1
  127. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DOMSize.js +2 -1
  128. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DocumentLatency.js +12 -11
  129. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/DuplicatedJavaScript.js +2 -1
  130. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/FontDisplay.js +2 -1
  131. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ForcedReflow.js +3 -2
  132. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/INPBreakdown.js +14 -2
  133. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ImageDelivery.js +2 -1
  134. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +13 -2
  135. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -1
  136. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LegacyJavaScript.js +4 -2
  137. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ModernHTTP.js +2 -1
  138. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +4 -3
  139. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +2 -1
  140. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/SlowCSSSelector.js +2 -1
  141. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/ThirdParties.js +2 -1
  142. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Viewport.js +2 -1
  143. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +9 -3
  144. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +25 -2
  145. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/IgnoreListManager.js +41 -48
  146. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/UISourceCode.js +38 -0
  147. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/WorkspaceImpl.js +5 -8
  148. package/build/node_modules/chrome-devtools-frontend/front_end/models/workspace/workspace-meta.js +34 -0
  149. package/build/src/McpContext.js +8 -1
  150. package/build/src/tools/bookmarks.js +0 -1
  151. package/build/src/tools/gemini-web.js +17 -25
  152. package/build/src/tools/snapshot.js +9 -2
  153. package/package.json +3 -3
@@ -98,6 +98,9 @@ export function handleEvent(event) {
98
98
  storeTraceEventWithRequestId(event.args.data.requestId, 'resourceMarkAsCached', event);
99
99
  return;
100
100
  }
101
+ if (Types.Events.isPreloadRenderBlockingStatusChangeEvent(event)) {
102
+ storeTraceEventWithRequestId(event.args.data.requestId, 'preloadRenderBlockingStatusChange', [event]);
103
+ }
101
104
  if (Types.Events.isWebSocketCreate(event) || Types.Events.isWebSocketInfo(event) ||
102
105
  Types.Events.isWebSocketTransfer(event)) {
103
106
  const identifier = event.args.data.identifier;
@@ -221,6 +224,7 @@ export async function finalize() {
221
224
  *
222
225
  * See `_updateTimingsForLightrider` in Lighthouse for more detail.
223
226
  */
227
+ let lrServerResponseTime;
224
228
  if (isLightrider && request.receiveResponse?.args.data.headers) {
225
229
  timing = {
226
230
  requestTime: Helpers.Timing.microToSeconds(request.sendRequests.at(0)?.ts ?? 0),
@@ -254,6 +258,13 @@ export async function finalize() {
254
258
  timing.connectEnd = TCPMs;
255
259
  timing.sslEnd = TCPMs;
256
260
  }
261
+ // Lightrider does not have any equivalent for `sendEnd` timing values. The
262
+ // closest we can get to the server response time is from a header that
263
+ // Lightrider sets.
264
+ const ResponseMsHeader = request.receiveResponse.args.data.headers.find(h => h.name === 'X-ResponseMs');
265
+ if (ResponseMsHeader) {
266
+ lrServerResponseTime = Math.max(0, parseInt(ResponseMsHeader.value, 10));
267
+ }
257
268
  }
258
269
  // TODO: consider allowing chrome / about.
259
270
  const allowedProtocols = [
@@ -346,6 +357,13 @@ export async function finalize() {
346
357
  const waiting = timing ?
347
358
  Types.Timing.Micro((timing.receiveHeadersEnd - timing.sendEnd) * MILLISECONDS_TO_MICROSECONDS) :
348
359
  Types.Timing.Micro(0);
360
+ // Server Response Time
361
+ // =======================
362
+ // Time from when the send finished going to when the first byte of headers were received.
363
+ const serverResponseTime = timing ?
364
+ Types.Timing.Micro(((timing.receiveHeadersStart ?? timing.receiveHeadersEnd) - timing.sendEnd) *
365
+ MILLISECONDS_TO_MICROSECONDS) :
366
+ Types.Timing.Micro(0);
349
367
  // Download
350
368
  // =======================
351
369
  // Time from receipt of headers to the finish time.
@@ -372,11 +390,18 @@ export async function finalize() {
372
390
  Types.Timing.Micro((timing.connectEnd - timing.connectStart) * MILLISECONDS_TO_MICROSECONDS) :
373
391
  Types.Timing.Micro(0);
374
392
  // Finally get some of the general data from the trace events.
375
- const { frame, url, renderBlocking } = finalSendRequest.args.data;
393
+ const { frame, url, renderBlocking: sendRequestIsRenderBlocking } = finalSendRequest.args.data;
376
394
  const { encodedDataLength, decodedBodyLength } = request.resourceFinish ? request.resourceFinish.args.data : { encodedDataLength: 0, decodedBodyLength: 0 };
377
395
  const parsedUrl = new URL(url);
378
396
  const isHttps = parsedUrl.protocol === 'https:';
379
397
  const requestingFrameUrl = Helpers.Trace.activeURLForFrameAtTime(frame, finalSendRequest.ts, rendererProcessesByFrame) || '';
398
+ // A resource that is preloaded (and not marked as render blocking) can
399
+ // become render blocked later via a PreloadRenderBlockingStatusChange. In
400
+ // this case, we take the render blocking value of the last
401
+ // PreloadRenderBlockingStatusChange for this request.
402
+ const preloadRenderBlockingStatusChange = request.preloadRenderBlockingStatusChange?.at(-1)?.args.data.renderBlocking;
403
+ // In the event the property isn't set, assume non-blocking.
404
+ const isRenderBlocking = preloadRenderBlockingStatusChange ?? sendRequestIsRenderBlocking ?? 'non_blocking';
380
405
  // Construct a synthetic trace event for this network request.
381
406
  const networkEvent = Helpers.SyntheticEvents.SyntheticEventsManager.registerSyntheticEvent({
382
407
  rawSourceEvent: finalSendRequest,
@@ -404,6 +429,7 @@ export async function finalize() {
404
429
  stalled,
405
430
  totalTime,
406
431
  waiting,
432
+ serverResponseTime,
407
433
  },
408
434
  // All fields below are from TraceEventsForNetworkRequest.
409
435
  decodedBodyLength,
@@ -416,8 +442,7 @@ export async function finalize() {
416
442
  initialPriority,
417
443
  protocol: request.receiveResponse?.args.data.protocol ?? 'unknown',
418
444
  redirects,
419
- // In the event the property isn't set, assume non-blocking.
420
- renderBlocking: renderBlocking ?? 'non_blocking',
445
+ renderBlocking: isRenderBlocking,
421
446
  requestId,
422
447
  requestingFrameUrl,
423
448
  requestMethod: finalSendRequest.args.data.requestMethod,
@@ -428,6 +453,7 @@ export async function finalize() {
428
453
  initiator: finalSendRequest.args.data.initiator,
429
454
  stackTrace: finalSendRequest.args.data.stackTrace,
430
455
  timing,
456
+ lrServerResponseTime,
431
457
  url,
432
458
  failed: request.resourceFinish?.args.data.didFail ?? false,
433
459
  finished: Boolean(request.resourceFinish),
@@ -17,23 +17,66 @@ let entryToNode = new Map();
17
17
  // are matched by profile id, which we then finish processing to export
18
18
  // events matched by thread id.
19
19
  let preprocessedData = new Map();
20
+ /**
21
+ * Profile source selection priority when multiple profiles exist for the same thread.
22
+ *
23
+ * Profile sources and their typical scenarios:
24
+ * - 'Internal': Browser-initiated profiling performance panel traces.
25
+ * This is the profiling mechanism when users click "Record" in the Devtools UI.
26
+ * - 'Inspector': User-initiated via console.profile()/profileEnd() calls.
27
+ * Represents explicit developer intent to profile specific code.
28
+ * - 'SelfProfiling': Page-initiated via JS Self-Profiling API.
29
+ * Lower signal vs the two above; treated as fallback.
30
+ *
31
+ * Selection strategy:
32
+ * - CPU Profile mode: Prefer 'Inspector' (explicit user request).
33
+ * - Performance trace: Prefer 'Internal' (integrated timeline context), then 'Inspector'.
34
+ * - Sources not in the priority list (including 'SelfProfiling') act as fallbacks.
35
+ * When no priority source matches, the first candidate profile is selected.
36
+ */
37
+ const PROFILE_SOURCES_BY_PRIORITY = {
38
+ cpuProfile: ['Inspector'],
39
+ performanceTrace: ['Internal', 'Inspector'],
40
+ };
20
41
  function parseCPUProfileData(parseOptions) {
42
+ const priorityList = parseOptions.isCPUProfile ? PROFILE_SOURCES_BY_PRIORITY.cpuProfile : PROFILE_SOURCES_BY_PRIORITY.performanceTrace;
21
43
  for (const [processId, profiles] of preprocessedData) {
44
+ const profilesByThread = new Map();
22
45
  for (const [profileId, preProcessedData] of profiles) {
23
46
  const threadId = preProcessedData.threadId;
24
- if (!preProcessedData.rawProfile.nodes.length || threadId === undefined) {
47
+ if (threadId === undefined) {
48
+ continue;
49
+ }
50
+ const listForThread = Platform.MapUtilities.getWithDefault(profilesByThread, threadId, () => []);
51
+ listForThread.push({ id: profileId, data: preProcessedData });
52
+ }
53
+ for (const [threadId, candidates] of profilesByThread) {
54
+ if (!candidates.length) {
55
+ continue;
56
+ }
57
+ let chosen = candidates[0];
58
+ for (const source of priorityList) {
59
+ const match = candidates.find(p => p.data.source === source);
60
+ if (match) {
61
+ chosen = match;
62
+ break;
63
+ }
64
+ }
65
+ const chosenData = chosen.data;
66
+ if (!chosenData.rawProfile.nodes.length) {
25
67
  continue;
26
68
  }
27
69
  const indexStack = [];
28
- const profileModel = new CPUProfile.CPUProfileDataModel.CPUProfileDataModel(preProcessedData.rawProfile);
70
+ const profileModel = new CPUProfile.CPUProfileDataModel.CPUProfileDataModel(chosenData.rawProfile);
29
71
  const profileTree = Helpers.TreeHelpers.makeEmptyTraceEntryTree();
30
72
  profileTree.maxDepth = profileModel.maxDepth;
73
+ const selectedProfileId = chosen.id;
31
74
  const finalizedData = {
32
- rawProfile: preProcessedData.rawProfile,
75
+ rawProfile: chosenData.rawProfile,
33
76
  parsedProfile: profileModel,
34
77
  profileCalls: [],
35
78
  profileTree,
36
- profileId,
79
+ profileId: selectedProfileId,
37
80
  };
38
81
  const dataByThread = Platform.MapUtilities.getWithDefault(profilesInProcess, processId, () => new Map());
39
82
  dataByThread.set(threadId, finalizedData);
@@ -49,7 +92,7 @@ function parseCPUProfileData(parseOptions) {
49
92
  }
50
93
  const ts = Helpers.Timing.milliToMicro(Types.Timing.Milli(timeStampMilliseconds));
51
94
  const nodeId = node.id;
52
- const profileCall = Helpers.Trace.makeProfileCall(node, profileId, sampleIndex, ts, processId, threadId);
95
+ const profileCall = Helpers.Trace.makeProfileCall(node, selectedProfileId, sampleIndex, ts, processId, threadId);
53
96
  finalizedData.profileCalls.push(profileCall);
54
97
  indexStack.push(finalizedData.profileCalls.length - 1);
55
98
  const traceEntryNode = Helpers.TreeHelpers.makeEmptyTraceEntryNode(profileCall, nodeId);
@@ -68,7 +111,7 @@ function parseCPUProfileData(parseOptions) {
68
111
  }
69
112
  const { callFrame, ts, pid, tid } = profileCall;
70
113
  const traceEntryNode = entryToNode.get(profileCall);
71
- if (callFrame === undefined || ts === undefined || pid === undefined || profileId === undefined ||
114
+ if (callFrame === undefined || ts === undefined || pid === undefined || selectedProfileId === undefined ||
72
115
  tid === undefined || traceEntryNode === undefined) {
73
116
  return;
74
117
  }
@@ -119,6 +162,7 @@ export function handleEvent(event) {
119
162
  const profileData = getOrCreatePreProcessedData(event.pid, event.id);
120
163
  profileData.rawProfile.startTime = event.ts;
121
164
  profileData.threadId = event.tid;
165
+ assignProfileSourceIfKnown(profileData, event.args?.data?.source);
122
166
  return;
123
167
  }
124
168
  if (Types.Events.isProfileChunk(event)) {
@@ -146,9 +190,11 @@ export function handleEvent(event) {
146
190
  }
147
191
  const timeDeltas = event.args.data?.timeDeltas || [];
148
192
  const lines = event.args.data?.lines || Array(samples.length).fill(0);
193
+ const columns = event.args.data?.columns || Array(samples.length).fill(0);
149
194
  cdpProfile.samples?.push(...samples);
150
195
  cdpProfile.timeDeltas?.push(...timeDeltas);
151
196
  cdpProfile.lines?.push(...lines);
197
+ cdpProfile.columns?.push(...columns);
152
198
  if (traceIds) {
153
199
  cdpProfile.traceIds ??= {};
154
200
  for (const key in traceIds) {
@@ -163,12 +209,18 @@ export function handleEvent(event) {
163
209
  const timeDeltas = cdpProfile.timeDeltas;
164
210
  cdpProfile.endTime = timeDeltas.reduce((x, y) => x + y, cdpProfile.startTime);
165
211
  }
212
+ assignProfileSourceIfKnown(profileData, event.args?.data?.source);
166
213
  return;
167
214
  }
168
215
  }
169
216
  export async function finalize(parseOptions = {}) {
170
217
  parseCPUProfileData(parseOptions);
171
218
  }
219
+ function assignProfileSourceIfKnown(profileData, source) {
220
+ if (Types.Events.VALID_PROFILE_SOURCES.includes(source)) {
221
+ profileData.source = source;
222
+ }
223
+ }
172
224
  export function data() {
173
225
  return {
174
226
  profilesInProcess,
@@ -185,6 +237,7 @@ function getOrCreatePreProcessedData(processId, profileId) {
185
237
  samples: [],
186
238
  timeDeltas: [],
187
239
  lines: [],
240
+ columns: [],
188
241
  },
189
242
  profileId,
190
243
  }));
@@ -7,17 +7,19 @@ import * as Types from '../types/types.js';
7
7
  import { data as metaHandlerData } from './MetaHandler.js';
8
8
  import { data as networkRequestsHandlerData } from './NetworkRequestsHandler.js';
9
9
  let scriptById = new Map();
10
+ let frameIdByIsolate = new Map();
10
11
  export function deps() {
11
12
  return ['Meta', 'NetworkRequests'];
12
13
  }
13
14
  export function reset() {
14
15
  scriptById = new Map();
16
+ frameIdByIsolate = new Map();
15
17
  }
16
18
  export function handleEvent(event) {
17
19
  const getOrMakeScript = (isolate, scriptIdAsNumber) => {
18
20
  const scriptId = String(scriptIdAsNumber);
19
21
  const key = `${isolate}.${scriptId}`;
20
- return Platform.MapUtilities.getWithDefault(scriptById, key, () => ({ isolate, scriptId, frame: '', ts: 0 }));
22
+ return Platform.MapUtilities.getWithDefault(scriptById, key, () => ({ isolate, scriptId, frame: '', ts: event.ts }));
21
23
  };
22
24
  if (Types.Events.isRundownScriptCompiled(event) && event.args.data) {
23
25
  const { isolate, scriptId, frame } = event.args.data;
@@ -29,7 +31,11 @@ export function handleEvent(event) {
29
31
  if (Types.Events.isRundownScript(event)) {
30
32
  const { isolate, scriptId, url, sourceUrl, sourceMapUrl, sourceMapUrlElided } = event.args.data;
31
33
  const script = getOrMakeScript(isolate, scriptId);
34
+ if (!script.frame) {
35
+ script.frame = frameIdByIsolate.get(String(isolate)) ?? '';
36
+ }
32
37
  script.url = url;
38
+ script.ts = event.ts;
33
39
  if (sourceUrl) {
34
40
  script.sourceUrl = sourceUrl;
35
41
  }
@@ -58,6 +64,25 @@ export function handleEvent(event) {
58
64
  script.content = (script.content ?? '') + sourceText;
59
65
  return;
60
66
  }
67
+ // Setup frameIdByIsolate, which is used only in the case that we are missing
68
+ // rundown events for a script. We won't get a frame association from the rundown
69
+ // events if the recording started only after the script was first compiled. In
70
+ // that scenario, derive the frame via the isolate / FunctionCall events.
71
+ // TODO: ideally, we put the frame on ScriptCatchup event. So much easier. This approach has some
72
+ // issues.
73
+ if (Types.Events.isFunctionCall(event) && event.args.data?.isolate && event.args.data.frame) {
74
+ const { isolate, frame } = event.args.data;
75
+ const existingValue = frameIdByIsolate.get(isolate);
76
+ if (existingValue !== frame) {
77
+ frameIdByIsolate.set(isolate, frame);
78
+ // Update the scripts we discovered but without knowing their frame.
79
+ for (const script of scriptById.values()) {
80
+ if (!script.frame && script.isolate === isolate) {
81
+ script.frame = frame;
82
+ }
83
+ }
84
+ }
85
+ }
61
86
  }
62
87
  function findFrame(meta, frameId) {
63
88
  for (const frames of meta.frameByProcessId?.values()) {
@@ -108,7 +108,7 @@ function getEventTrack(event) {
108
108
  return undefined;
109
109
  }
110
110
  /**
111
- * Similar to the default {@see Helpers.Trace.eventTimeComparator}
111
+ * Similar to the default {@link Helpers.Trace.eventTimeComparator}
112
112
  * but with a twist:
113
113
  * In case of equal start and end times, put the second event (within a
114
114
  * track) first.
@@ -142,7 +142,7 @@ export function addEventToEntityMapping(event, entityMappings) {
142
142
  }
143
143
  entityMappings.entityByEvent.set(event, entity);
144
144
  }
145
- // A slight upgrade of addEventToEntityMapping to handle the sub-events of a network request.
145
+ /** A slight upgrade of addEventToEntityMapping to handle the sub-events of a network request. **/
146
146
  export function addNetworkRequestToEntityMapping(networkRequest, entityMappings, requestTraceEvents) {
147
147
  const entity = getEntityForEvent(networkRequest, entityMappings);
148
148
  if (!entity) {
@@ -25,8 +25,10 @@ export function timeStampForEventAdjustedByClosestNavigation(event, traceBounds,
25
25
  }
26
26
  return Types.Timing.Micro(eventTimeStamp);
27
27
  }
28
- // Expands the trace window by a provided percentage or, if it the expanded window is smaller than 1 millisecond, expands it to 1 millisecond.
29
- // If the expanded window is outside of the max trace window, cut the overflowing bound to the max trace window bound.
28
+ /**
29
+ * Expands the trace window by a provided percentage or, if it the expanded window is smaller than 1 millisecond, expands it to 1 millisecond.
30
+ * If the expanded window is outside of the max trace window, cut the overflowing bound to the max trace window bound.
31
+ **/
30
32
  export function expandWindowByPercentOrToOneMillisecond(annotationWindow, maxTraceWindow, percentage) {
31
33
  // Expand min and max of the window by half of the provided percentage. That way, in total, the window will be expanded by the provided percentage.
32
34
  let newMin = annotationWindow.min - annotationWindow.range * (percentage / 100) / 2;
@@ -70,8 +70,10 @@ export function extractOriginFromTrace(firstNavigationURL) {
70
70
  }
71
71
  return null;
72
72
  }
73
- // Each thread contains events. Events indicate the thread and process IDs, which are
74
- // used to store the event in the correct process thread entry below.
73
+ /**
74
+ * Each thread contains events. Events indicate the thread and process IDs, which are
75
+ * used to store the event in the correct process thread entry below.
76
+ **/
75
77
  export function addEventToProcessThread(event, eventsInProcessThread) {
76
78
  const { tid, pid } = event;
77
79
  let eventsInThread = eventsInProcessThread.get(pid);
@@ -500,6 +502,16 @@ export function getStackTraceTopCallFrameInEventPayload(event) {
500
502
  }
501
503
  }
502
504
  }
505
+ export function rawCallFrameForEntry(entry) {
506
+ if (Types.Events.isProfileCall(entry)) {
507
+ return entry.callFrame;
508
+ }
509
+ const topCallFrame = getStackTraceTopCallFrameInEventPayload(entry);
510
+ if (topCallFrame) {
511
+ return topCallFrame;
512
+ }
513
+ return null;
514
+ }
503
515
  /**
504
516
  * Given a 1-based call frame creates a 0-based one.
505
517
  */
@@ -704,8 +716,10 @@ export function extractSampleTraceId(event) {
704
716
  }
705
717
  return event.args?.sampleTraceId ?? event.args?.data?.sampleTraceId ?? null;
706
718
  }
707
- // This exactly matches Trace.Styles.visibleTypes. See the runtime verification in maybeInitStylesMap.
708
- // TODO(crbug.com/410884528)
719
+ /**
720
+ * This exactly matches Trace.Styles.visibleTypes. See the runtime verification in maybeInitStylesMap.
721
+ * TODO(crbug.com/410884528)
722
+ **/
709
723
  export const VISIBLE_TRACE_EVENT_TYPES = new Set([
710
724
  "AbortPostTaskCallback" /* Types.Events.Name.ABORT_POST_TASK_CALLBACK */,
711
725
  "Animation" /* Types.Events.Name.ANIMATION */,
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  * @description Description of a DevTools insight that identifies the reasons that elements shift on the page.
15
15
  * This is displayed after a user expands the section to see more. No character length limits.
16
16
  */
17
- description: '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.',
17
+ description: 'Layout shifts occur when elements move absent any user interaction. [Investigate the causes of layout shifts](https://developer.chrome.com/docs/performance/insights/cls-culprit), such as elements being added, removed, or their fonts changing as the page loads.',
18
18
  /**
19
19
  * @description Text indicating the worst layout shift cluster.
20
20
  */
@@ -417,6 +417,7 @@ function finalize(partialModel) {
417
417
  strings: UIStrings,
418
418
  title: i18nString(UIStrings.title),
419
419
  description: i18nString(UIStrings.description),
420
+ docs: 'https://developer.chrome.com/docs/performance/insights/cls-culprit',
420
421
  category: InsightCategory.CLS,
421
422
  state,
422
423
  ...partialModel,
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  /**
15
15
  * @description Text to tell the user about how caching can help improve performance.
16
16
  */
17
- description: 'A long cache lifetime can speed up repeat visits to your page. [Learn more](https://web.dev/uses-long-cache-ttl/).',
17
+ description: 'A long cache lifetime can speed up repeat visits to your page. [Learn more about caching](https://developer.chrome.com/docs/performance/insights/cache).',
18
18
  /**
19
19
  * @description Column for a font loaded by the page to render text.
20
20
  */
@@ -43,6 +43,7 @@ function finalize(partialModel) {
43
43
  strings: UIStrings,
44
44
  title: i18nString(UIStrings.title),
45
45
  description: i18nString(UIStrings.description),
46
+ docs: 'https://developer.chrome.com/docs/performance/insights/cache',
46
47
  category: InsightCategory.ALL,
47
48
  state: partialModel.requests.length > 0 ? 'fail' : 'pass',
48
49
  ...partialModel,
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  /**
15
15
  * @description Description of an insight that recommends reducing the size of the DOM tree as a means to improve page responsiveness. "DOM" is an acronym and should not be translated. "layout reflows" are when the browser will recompute the layout of content on the page.
16
16
  */
17
- description: 'A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).',
17
+ description: 'A large DOM can increase the duration of style calculations and layout reflows, impacting page responsiveness. A large DOM will also increase memory usage. [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/performance/insights/dom-size).',
18
18
  /**
19
19
  * @description Header for a column containing the names of statistics as opposed to the actual statistic values.
20
20
  */
@@ -73,6 +73,7 @@ function finalize(partialModel) {
73
73
  strings: UIStrings,
74
74
  title: i18nString(UIStrings.title),
75
75
  description: i18nString(UIStrings.description),
76
+ docs: 'https://developer.chrome.com/docs/performance/insights/dom-size',
76
77
  category: InsightCategory.INP,
77
78
  state: relatedEvents.length > 0 ? 'informative' : 'pass',
78
79
  ...partialModel,
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  /**
15
15
  * @description Description of an insight that provides a breakdown for how long it took to download the main document.
16
16
  */
17
- description: 'Your first network request is the most important. Reduce its latency by avoiding redirects, ensuring a fast server response, and enabling text compression.',
17
+ description: 'Your first network request is the most important. [Reduce its latency](https://developer.chrome.com/docs/performance/insights/document-latency) by avoiding redirects, ensuring a fast server response, and enabling text compression.',
18
18
  /**
19
19
  * @description Text to tell the user that the document request does not have redirects.
20
20
  */
@@ -67,20 +67,20 @@ const IGNORE_THRESHOLD_IN_BYTES = 1400;
67
67
  export function isDocumentLatencyInsight(x) {
68
68
  return x.insightKey === 'DocumentLatency';
69
69
  }
70
- function getServerResponseTime(request, context) {
71
- // Prefer the value as given by the Lantern provider.
72
- // For PSI, Lighthouse uses this to set a better value for the server response
73
- // time. For technical reasons, in Lightrider we do not have `sendEnd` timing
74
- // values. See Lighthouse's `asLanternNetworkRequest` function for more.
75
- const lanternRequest = context.navigation && context.lantern?.requests.find(r => r.rawRequest === request);
76
- if (lanternRequest?.serverResponseTime !== undefined) {
77
- return lanternRequest.serverResponseTime;
70
+ function getServerResponseTime(request) {
71
+ // For technical reasons, Lightrider does not have `sendEnd` timing values. The
72
+ // closest we can get to the server response time is from a header that Lightrider
73
+ // sets.
74
+ // @ts-expect-error
75
+ const isLightrider = globalThis.isLightrider;
76
+ if (isLightrider) {
77
+ return request.args.data.lrServerResponseTime ?? null;
78
78
  }
79
79
  const timing = request.args.data.timing;
80
80
  if (!timing) {
81
81
  return null;
82
82
  }
83
- const ms = Helpers.Timing.microToMilli(request.args.data.syntheticData.waiting);
83
+ const ms = Helpers.Timing.microToMilli(request.args.data.syntheticData.serverResponseTime);
84
84
  return Math.round(ms);
85
85
  }
86
86
  function getCompressionSavings(request) {
@@ -148,6 +148,7 @@ function finalize(partialModel) {
148
148
  strings: UIStrings,
149
149
  title: i18nString(UIStrings.title),
150
150
  description: i18nString(UIStrings.description),
151
+ docs: 'https://developer.chrome.com/docs/performance/insights/document-latency',
151
152
  category: InsightCategory.ALL,
152
153
  state: hasFailure ? 'fail' : 'pass',
153
154
  ...partialModel,
@@ -162,7 +163,7 @@ export function generateInsight(data, context) {
162
163
  if (!documentRequest) {
163
164
  return finalize({ warnings: [InsightWarning.NO_DOCUMENT_REQUEST] });
164
165
  }
165
- const serverResponseTime = getServerResponseTime(documentRequest, context);
166
+ const serverResponseTime = getServerResponseTime(documentRequest);
166
167
  if (serverResponseTime === null) {
167
168
  throw new Error('missing document request timing');
168
169
  }
@@ -14,7 +14,7 @@ export const UIStrings = {
14
14
  /**
15
15
  * @description Description of an insight that identifies multiple copies of the same JavaScript sources, and recommends removing the duplication.
16
16
  */
17
- description: 'Remove large, duplicate JavaScript modules from bundles to reduce unnecessary bytes consumed by network activity.',
17
+ description: 'Remove large, [duplicate JavaScript modules](https://developer.chrome.com/docs/performance/insights/duplicated-javascript) from bundles to reduce unnecessary bytes consumed by network activity.',
18
18
  /** Label for a column in a data table; entries will be the locations of JavaScript or CSS code, e.g. the name of a Javascript package or module. */
19
19
  columnSource: 'Source',
20
20
  /** Label for a column in a data table; entries will be the number of wasted bytes due to duplication of a web resource. */
@@ -29,6 +29,7 @@ function finalize(partialModel) {
29
29
  strings: UIStrings,
30
30
  title: i18nString(UIStrings.title),
31
31
  description: i18nString(UIStrings.description),
32
+ docs: 'https://developer.chrome.com/docs/performance/insights/duplicated-javascript',
32
33
  category: InsightCategory.LCP,
33
34
  state: Boolean(partialModel.duplication.values().next().value) ? 'fail' : 'pass',
34
35
  relatedEvents: [...new Set(requests)],
@@ -12,7 +12,7 @@ export const UIStrings = {
12
12
  /**
13
13
  * @description Text to tell the user about the font-display CSS feature to help improve a the UX of a page.
14
14
  */
15
- description: 'Consider setting [`font-display`](https://developer.chrome.com/blog/font-display) to `swap` or `optional` to ensure text is consistently visible. `swap` can be further optimized to mitigate layout shifts with [font metric overrides](https://developer.chrome.com/blog/font-fallbacks).',
15
+ description: 'Consider setting [`font-display`](https://developer.chrome.com/docs/performance/insights/font-display) to `swap` or `optional` to ensure text is consistently visible. `swap` can be further optimized to mitigate layout shifts with [font metric overrides](https://developer.chrome.com/blog/font-fallbacks).',
16
16
  /** Column for a font loaded by the page to render text. */
17
17
  fontColumn: 'Font',
18
18
  /** Column for the amount of time wasted. */
@@ -26,6 +26,7 @@ function finalize(partialModel) {
26
26
  strings: UIStrings,
27
27
  title: i18nString(UIStrings.title),
28
28
  description: i18nString(UIStrings.description),
29
+ docs: 'https://developer.chrome.com/docs/performance/insights/font-display',
29
30
  category: InsightCategory.INP,
30
31
  state: partialModel.fonts.find(font => font.wastedTime > 0) ? 'fail' : 'pass',
31
32
  ...partialModel,
@@ -15,11 +15,11 @@ export const UIStrings = {
15
15
  /**
16
16
  * @description Text to describe the forced reflow.
17
17
  */
18
- description: 'A forced reflow occurs when JavaScript queries geometric properties (such as `offsetWidth`) after styles have been invalidated by a change to the DOM state. This can result in poor performance. Learn more about [forced reflows](https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing#avoid-forced-synchronous-layouts) and possible mitigations.',
18
+ description: 'A forced reflow occurs when JavaScript queries geometric properties (such as `offsetWidth`) after styles have been invalidated by a change to the DOM state. This can result in poor performance. Learn more about [forced reflows](https://developer.chrome.com/docs/performance/insights/forced-reflow) and possible mitigations.',
19
19
  /**
20
20
  * @description Title of a list to provide related stack trace data
21
21
  */
22
- relatedStackTrace: 'Stack trace',
22
+ reflowCallFrames: 'Call frames that trigger reflow',
23
23
  /**
24
24
  * @description Text to describe the top time-consuming function call
25
25
  */
@@ -100,6 +100,7 @@ function finalize(partialModel) {
100
100
  strings: UIStrings,
101
101
  title: i18nString(UIStrings.title),
102
102
  description: i18nString(UIStrings.description),
103
+ docs: 'https://developer.chrome.com/docs/performance/insights/forced-reflow',
103
104
  category: InsightCategory.ALL,
104
105
  state: partialModel.aggregatedBottomUpData.length !== 0 ? 'fail' : 'pass',
105
106
  ...partialModel,
@@ -2,13 +2,14 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
  import * as i18n from '../../../core/i18n/i18n.js';
5
+ import * as Handlers from '../handlers/handlers.js';
5
6
  import * as Helpers from '../helpers/helpers.js';
6
7
  import { InsightCategory, } from './types.js';
7
8
  export const UIStrings = {
8
9
  /**
9
10
  * @description Text to tell the user about the longest user interaction.
10
11
  */
11
- description: 'Start investigating with the longest subpart. [Delays can be minimized](https://web.dev/articles/optimize-inp#optimize_interactions). To reduce processing duration, [optimize the main-thread costs](https://web.dev/articles/optimize-long-tasks), often JS.',
12
+ description: 'Start investigating [how to improve INP](https://developer.chrome.com/docs/performance/insights/inp-breakdown) by looking at the longest subpart.',
12
13
  /**
13
14
  * @description Title for the performance insight "INP breakdown", which shows a breakdown of INP by subparts / sections.
14
15
  */
@@ -45,13 +46,24 @@ export function isINPBreakdownInsight(insight) {
45
46
  return insight.insightKey === "INPBreakdown" /* InsightKeys.INP_BREAKDOWN */;
46
47
  }
47
48
  function finalize(partialModel) {
49
+ let state = 'pass';
50
+ if (partialModel.longestInteractionEvent) {
51
+ const classification = Handlers.ModelHandlers.UserInteractions.scoreClassificationForInteractionToNextPaint(partialModel.longestInteractionEvent.dur);
52
+ if (classification === "good" /* Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.GOOD */) {
53
+ state = 'informative';
54
+ }
55
+ else {
56
+ state = 'fail';
57
+ }
58
+ }
48
59
  return {
49
60
  insightKey: "INPBreakdown" /* InsightKeys.INP_BREAKDOWN */,
50
61
  strings: UIStrings,
51
62
  title: i18nString(UIStrings.title),
52
63
  description: i18nString(UIStrings.description),
64
+ docs: 'https://developer.chrome.com/docs/performance/insights/inp-breakdown',
53
65
  category: InsightCategory.INP,
54
- state: partialModel.longestInteractionEvent ? 'informative' : 'pass',
66
+ state,
55
67
  ...partialModel,
56
68
  };
57
69
  }
@@ -13,7 +13,7 @@ export const UIStrings = {
13
13
  /**
14
14
  * @description Description of an insight that recommends ways to reduce the size of images downloaded and used on the page.
15
15
  */
16
- description: 'Reducing the download time of images can improve the perceived load time of the page and LCP. [Learn more about optimizing image size](https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/)',
16
+ description: 'Reducing the download time of images can improve the perceived load time of the page and LCP. [Learn more about optimizing image size](https://developer.chrome.com/docs/performance/insights/image-delivery)',
17
17
  /**
18
18
  * @description Message displayed in a chip explaining that an image file size is large for the # of pixels it has and recommends possible adjustments to improve the image size.
19
19
  */
@@ -114,6 +114,7 @@ function finalize(partialModel) {
114
114
  strings: UIStrings,
115
115
  title: i18nString(UIStrings.title),
116
116
  description: i18nString(UIStrings.description),
117
+ docs: 'https://developer.chrome.com/docs/performance/insights/image-delivery',
117
118
  category: InsightCategory.LCP,
118
119
  state: partialModel.optimizableImages.length > 0 ? 'fail' : 'pass',
119
120
  ...partialModel,
@@ -16,7 +16,7 @@ export const UIStrings = {
16
16
  * @description Description of a DevTools insight that presents a breakdown for the LCP metric by subparts.
17
17
  * This is displayed after a user expands the section to see more. No character length limits.
18
18
  */
19
- description: 'Each [subpart has specific improvement strategies](https://web.dev/articles/optimize-lcp#lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.',
19
+ description: 'Each [subpart has specific improvement strategies](https://developer.chrome.com/docs/performance/insights/lcp-breakdown). Ideally, most of the LCP time should be spent on loading the resources, not within delays.',
20
20
  /**
21
21
  * @description Time to first byte title for the Largest Contentful Paint's subparts timespan breakdown.
22
22
  */
@@ -124,13 +124,24 @@ function finalize(partialModel) {
124
124
  if (partialModel.lcpRequest) {
125
125
  relatedEvents.push(partialModel.lcpRequest);
126
126
  }
127
+ let state = 'pass';
128
+ if (partialModel.lcpMs !== undefined) {
129
+ const classification = Handlers.ModelHandlers.PageLoadMetrics.scoreClassificationForLargestContentfulPaint(Helpers.Timing.milliToMicro(partialModel.lcpMs));
130
+ if (classification === "good" /* Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.GOOD */) {
131
+ state = 'informative';
132
+ }
133
+ else {
134
+ state = 'fail';
135
+ }
136
+ }
127
137
  return {
128
138
  insightKey: "LCPBreakdown" /* InsightKeys.LCP_BREAKDOWN */,
129
139
  strings: UIStrings,
130
140
  title: i18nString(UIStrings.title),
131
141
  description: i18nString(UIStrings.description),
142
+ docs: 'https://developer.chrome.com/docs/performance/insights/lcp-breakdown',
132
143
  category: InsightCategory.LCP,
133
- state: partialModel.lcpEvent || partialModel.lcpRequest ? 'informative' : 'pass',
144
+ state,
134
145
  ...partialModel,
135
146
  relatedEvents,
136
147
  };