chrome-devtools-frontend 1.0.1595925 → 1.0.1596535

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 (129) hide show
  1. package/.stylelintrc.json +3 -1
  2. package/docs/ui_engineering.md +0 -36
  3. package/front_end/core/common/Console.ts +6 -6
  4. package/front_end/core/common/Settings.ts +12 -8
  5. package/front_end/core/host/UserMetrics.ts +0 -1
  6. package/front_end/core/root/DevToolsContext.ts +13 -7
  7. package/front_end/core/root/ExperimentNames.ts +0 -1
  8. package/front_end/core/sdk/CPUThrottlingManager.ts +12 -9
  9. package/front_end/core/sdk/DOMModel.ts +206 -0
  10. package/front_end/core/sdk/FrameManager.ts +7 -8
  11. package/front_end/core/sdk/SourceMapScopesInfo.ts +1 -1
  12. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +1 -1
  13. package/front_end/entrypoints/greendev_floaty/floaty.css +2 -2
  14. package/front_end/entrypoints/main/MainImpl.ts +1 -7
  15. package/front_end/foundation/Universe.ts +22 -11
  16. package/front_end/generated/InspectorBackendCommands.ts +7 -0
  17. package/front_end/generated/protocol-mapping.d.ts +16 -0
  18. package/front_end/generated/protocol-proxy-api.d.ts +25 -0
  19. package/front_end/generated/protocol.ts +71 -0
  20. package/front_end/models/ai_assistance/AiConversation.ts +6 -95
  21. package/front_end/models/ai_assistance/ConversationHandler.ts +1 -1
  22. package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -1
  23. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +14 -0
  24. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -1
  25. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -0
  26. package/front_end/panels/ai_assistance/components/ChatMessage.ts +73 -35
  27. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +30 -6
  28. package/front_end/panels/ai_assistance/components/chatMessage.css +4 -2
  29. package/front_end/panels/application/IndexedDBModel.ts +2 -4
  30. package/front_end/panels/application/ServiceWorkersView.ts +3 -11
  31. package/front_end/panels/developer_resources/DeveloperResourcesListView.ts +1 -1
  32. package/front_end/panels/elements/ComputedStyleWidget.ts +25 -16
  33. package/front_end/panels/elements/ElementsPanel.ts +0 -1
  34. package/front_end/panels/elements/StandaloneStylesContainer.ts +48 -12
  35. package/front_end/panels/elements/StylePropertiesSection.ts +97 -1
  36. package/front_end/panels/elements/StylePropertyTreeElement.ts +27 -5
  37. package/front_end/panels/elements/StylesContainer.ts +1 -0
  38. package/front_end/panels/elements/StylesSidebarPane.ts +48 -32
  39. package/front_end/panels/elements/nodeStackTraceWidget.css +1 -1
  40. package/front_end/panels/elements/stylePropertiesTreeOutline.css +1 -1
  41. package/front_end/panels/emulation/DeviceModeToolbar.ts +171 -89
  42. package/front_end/panels/greendev/GreenDevPanel.css +2 -2
  43. package/front_end/panels/issues/AffectedPermissionElementsView.ts +9 -6
  44. package/front_end/panels/lighthouse/LighthouseController.ts +13 -5
  45. package/front_end/panels/lighthouse/LighthousePanel.ts +22 -5
  46. package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +11 -3
  47. package/front_end/panels/recorder/components/StepView.ts +0 -2
  48. package/front_end/panels/recorder/components/stepView.css +13 -13
  49. package/front_end/panels/recorder/components/timelineSection.css +6 -7
  50. package/front_end/panels/settings/components/SyncSection.ts +4 -13
  51. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +2 -15
  52. package/front_end/panels/timeline/RecordingMetadata.ts +1 -1
  53. package/front_end/panels/timeline/TimelineDetailsView.ts +2 -2
  54. package/front_end/panels/timeline/TimelinePanel.ts +18 -0
  55. package/front_end/panels/timeline/TimelineUIUtils.ts +7 -4
  56. package/front_end/panels/timeline/components/CWVMetrics.ts +339 -0
  57. package/front_end/panels/timeline/components/Sidebar.ts +17 -0
  58. package/front_end/panels/timeline/components/SidebarInsightsTab.ts +10 -0
  59. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +25 -267
  60. package/front_end/panels/timeline/components/Utils.ts +2 -2
  61. package/front_end/panels/timeline/components/components.ts +2 -0
  62. package/front_end/panels/timeline/components/cwvMetrics.css +107 -0
  63. package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +0 -102
  64. package/front_end/panels/timeline/timeline-meta.ts +11 -0
  65. package/front_end/panels/timeline/utils/Helpers.ts +5 -1
  66. package/front_end/panels/webauthn/webauthnPane.css +1 -1
  67. package/front_end/third_party/chromium/README.chromium +1 -1
  68. package/front_end/third_party/puppeteer/README.chromium +2 -2
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +7 -0
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +1 -0
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -0
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +1 -0
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +6 -0
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +1 -0
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +5 -0
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +7 -0
  91. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +14 -3
  92. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +7 -0
  93. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +1 -0
  96. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -0
  98. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +1 -0
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +6 -0
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +1 -0
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +5 -0
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/types.d.ts +7 -0
  112. package/front_end/third_party/puppeteer/package/package.json +1 -1
  113. package/front_end/third_party/puppeteer/package/src/api/Page.ts +8 -0
  114. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +4 -0
  115. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +7 -0
  116. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +6 -0
  117. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  118. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  119. package/front_end/ui/components/text_editor/AutocompleteHistory.ts +89 -5
  120. package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -1
  121. package/front_end/ui/legacy/ProgressIndicator.ts +1 -1
  122. package/front_end/ui/legacy/Toolbar.ts +1 -1
  123. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +14 -7
  124. package/front_end/ui/legacy/legacy.ts +0 -2
  125. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  126. package/inspector_overlay/loadCSS.rollup.js +2 -2
  127. package/inspector_overlay/tool_source_order.css +1 -0
  128. package/package.json +1 -1
  129. package/front_end/ui/legacy/Fragment.ts +0 -234
@@ -3,69 +3,25 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as i18n from '../../../core/i18n/i18n.js';
6
- import * as Platform from '../../../core/platform/platform.js';
7
6
  import * as AIAssistance from '../../../models/ai_assistance/ai_assistance.js';
8
- import * as CrUXManager from '../../../models/crux-manager/crux-manager.js';
9
7
  import * as Trace from '../../../models/trace/trace.js';
10
- import * as Buttons from '../../../ui/components/buttons/buttons.js';
11
8
  import * as UI from '../../../ui/legacy/legacy.js';
12
9
  import * as Lit from '../../../ui/lit/lit.js';
13
- import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
14
10
 
15
- import {md, shouldRenderForCategory} from './insights/Helpers.js';
11
+ import {CWVMetrics, getFieldMetrics} from './CWVMetrics.js';
12
+ import {shouldRenderForCategory} from './insights/Helpers.js';
16
13
  import * as Insights from './insights/insights.js';
17
14
  import type {ActiveInsight} from './Sidebar.js';
18
15
  import sidebarSingleInsightSetStyles from './sidebarSingleInsightSet.css.js';
19
- import {isFieldWorseThanLocal, NumberWithUnit} from './Utils.js';
20
16
 
21
17
  const {html} = Lit.StaticHtml;
22
18
 
23
19
  const UIStrings = {
24
- /**
25
- * @description title used for a metric value to tell the user about its score classification
26
- * @example {INP} PH1
27
- * @example {1.2s} PH2
28
- * @example {poor} PH3
29
- */
30
- metricScore: '{PH1}: {PH2} {PH3} score',
31
- /**
32
- * @description title used for a metric value to tell the user that the data is unavailable
33
- * @example {INP} PH1
34
- */
35
- metricScoreUnavailable: '{PH1}: unavailable',
36
20
  /**
37
21
  * @description Summary text for an expandable dropdown that contains all insights in a passing state.
38
22
  * @example {4} PH1
39
23
  */
40
24
  passedInsights: 'Passed insights ({PH1})',
41
- /**
42
- * @description Label denoting that metrics were observed in the field, from real use data (CrUX). Also denotes if from URL or Origin dataset.
43
- * @example {URL} PH1
44
- */
45
- fieldScoreLabel: 'Field ({PH1})',
46
- /**
47
- * @description Label for an option that selects the page's specific URL as opposed to it's entire origin/domain.
48
- */
49
- urlOption: 'URL',
50
- /**
51
- * @description Label for an option that selects the page's entire origin/domain as opposed to it's specific URL.
52
- */
53
- originOption: 'Origin',
54
- /**
55
- * @description Title for button that closes a warning popup.
56
- */
57
- dismissTitle: 'Dismiss',
58
- /**
59
- * @description Title shown in a warning dialog when field metrics (collected from real users) is worse than the locally observed metrics.
60
- */
61
- fieldMismatchTitle: 'Field & local metrics mismatch',
62
- /**
63
- * @description Text shown in a warning dialog when field metrics (collected from real users) is worse than the locally observed metrics.
64
- * Asks user to use features such as throttling and device emulation.
65
- */
66
- fieldMismatchNotice:
67
- 'There are many reasons why local and field metrics [may not match](https://web.dev/articles/lab-and-field-data-differences). ' +
68
- 'Adjust [throttling settings and device emulation](https://developer.chrome.com/docs/devtools/device-mode) to analyze traces more similar to the average user\'s environment.',
69
25
  } as const;
70
26
 
71
27
  const str_ = i18n.i18n.registerUIStrings('panels/timeline/components/SidebarSingleInsightSet.ts', UIStrings);
@@ -83,166 +39,36 @@ interface InsightData {
83
39
  model: Trace.Insights.Types.InsightModel;
84
40
  }
85
41
 
86
- interface LocalMetrics {
87
- lcp: {value: Trace.Types.Timing.Micro, event: Trace.Types.Events.AnyLargestContentfulPaintCandidate}|null;
88
- cls: {value: number, worstClusterEvent: Trace.Types.Events.Event|null};
89
- inp: {value: Trace.Types.Timing.Micro, event: Trace.Types.Events.SyntheticInteractionPair}|null;
90
- }
91
-
92
42
  interface ViewInput {
93
43
  shownInsights: InsightData[];
94
44
  passedInsights: InsightData[];
95
- local: LocalMetrics|null;
96
- field: Trace.Insights.Common.CrUXFieldMetricResults|null;
97
- activeCategory: Trace.Insights.Types.InsightCategory;
98
- showFieldMismatchNotice: boolean;
99
- onDismisFieldMismatchNotice: () => void;
100
- onClickMetric: (traceEvent: Trace.Types.Events.Event) => void;
45
+ insightSetKey: Trace.Types.Events.NavigationId|null;
46
+ parsedTrace: Trace.TraceModel.ParsedTrace|null;
101
47
  renderInsightComponent: (insightData: InsightData) => Lit.LitTemplate;
102
48
  }
103
49
 
104
50
  type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
105
51
 
106
- export const DEFAULT_VIEW: View = (input, output, target) => {
52
+ const DEFAULT_VIEW: View = (input, output, target) => {
107
53
  const {
108
54
  shownInsights,
109
55
  passedInsights,
110
- local,
111
- field,
112
- activeCategory,
113
- showFieldMismatchNotice,
114
- onDismisFieldMismatchNotice,
115
- onClickMetric,
56
+ insightSetKey,
57
+ parsedTrace,
116
58
  renderInsightComponent,
117
59
  } = input;
118
60
 
119
- function renderMetrics(): Lit.TemplateResult {
120
- const lcpEl = renderMetricValue('LCP', local?.lcp?.value ?? null, local?.lcp?.event ?? null);
121
- const inpEl = renderMetricValue('INP', local?.inp?.value ?? null, local?.inp?.event ?? null);
122
- const clsEl = renderMetricValue('CLS', local?.cls?.value ?? null, local?.cls?.worstClusterEvent ?? null);
123
-
124
- const localMetricsTemplateResult = html`
125
- <div class="metrics-row">
126
- <span>${lcpEl}</span>
127
- <span>${inpEl}</span>
128
- <span>${clsEl}</span>
129
- <span class="row-label">Local</span>
130
- </div>
131
- <span class="row-border"></span>
132
- `;
133
-
134
- let fieldMetricsTemplateResult;
135
- if (field) {
136
- const {lcp, inp, cls} = field;
137
-
138
- const lcpEl = renderMetricValue('LCP', lcp?.value ?? null, null);
139
- const inpEl = renderMetricValue('INP', inp?.value ?? null, null);
140
- const clsEl = renderMetricValue('CLS', cls?.value ?? null, null);
141
-
142
- let scope = i18nString(UIStrings.originOption);
143
- if (lcp?.pageScope === 'url' || inp?.pageScope === 'url') {
144
- scope = i18nString(UIStrings.urlOption);
145
- }
146
-
147
- // clang-format off
148
- fieldMetricsTemplateResult = html`
149
- <div class="metrics-row">
150
- <span>${lcpEl}</span>
151
- <span>${inpEl}</span>
152
- <span>${clsEl}</span>
153
- <span class="row-label">${i18nString(UIStrings.fieldScoreLabel, {PH1: scope})}</span>
154
- </div>
155
- <span class="row-border"></span>
156
- `;
157
- // clang-format on
158
- }
159
-
160
- let fieldIsDifferentEl;
161
- if (showFieldMismatchNotice) {
162
- // clang-format off
163
- fieldIsDifferentEl = html`
164
- <div class="field-mismatch-notice" jslog=${VisualLogging.section('timeline.insights.field-mismatch')}>
165
- <h3>${i18nString(UIStrings.fieldMismatchTitle)}</h3>
166
- <devtools-button
167
- title=${i18nString(UIStrings.dismissTitle)}
168
- .iconName=${'cross'}
169
- .variant=${Buttons.Button.Variant.ICON}
170
- .jslogContext=${'timeline.insights.dismiss-field-mismatch'}
171
- @click=${onDismisFieldMismatchNotice}
172
- ></devtools-button>
173
- <div class="field-mismatch-notice__body">${md(i18nString(UIStrings.fieldMismatchNotice))}</div>
174
- </div>
175
- `;
176
- // clang-format on
177
- }
178
-
179
- const classes = {metrics: true, 'metrics--field': Boolean(fieldMetricsTemplateResult)};
180
- const metricsTableEl = html`<div class=${Lit.Directives.classMap(classes)}>
181
- <div class="metrics-row">
182
- <span class="metric-label">LCP</span>
183
- <span class="metric-label">INP</span>
184
- <span class="metric-label">CLS</span>
185
- <span class="row-label"></span>
186
- </div>
187
- ${localMetricsTemplateResult}
188
- ${fieldMetricsTemplateResult}
189
- </div>`;
190
-
191
- return html`
192
- ${metricsTableEl}
193
- ${fieldIsDifferentEl}
194
- `;
195
- }
196
-
197
- function renderMetricValue(
198
- metric: 'LCP'|'CLS'|'INP', value: number|null, relevantEvent: Trace.Types.Events.Event|null): Lit.LitTemplate {
199
- let valueText: string;
200
- let valueDisplay: HTMLElement|string;
201
- let classification;
202
- if (value === null) {
203
- valueText = valueDisplay = '-';
204
- classification = Trace.Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.UNCLASSIFIED;
205
- } else if (metric === 'LCP') {
206
- const micros = value as Trace.Types.Timing.Micro;
207
- const {text, element} = NumberWithUnit.formatMicroSecondsAsSeconds(micros);
208
- valueText = text;
209
- valueDisplay = element;
210
- classification =
211
- Trace.Handlers.ModelHandlers.PageLoadMetrics.scoreClassificationForLargestContentfulPaint(micros);
212
- } else if (metric === 'CLS') {
213
- valueText = valueDisplay = value ? value.toFixed(2) : '0';
214
- classification = Trace.Handlers.ModelHandlers.LayoutShifts.scoreClassificationForLayoutShift(value);
215
- } else if (metric === 'INP') {
216
- const micros = value as Trace.Types.Timing.Micro;
217
- const {text, element} = NumberWithUnit.formatMicroSecondsAsMillisFixed(micros);
218
- valueText = text;
219
- valueDisplay = element;
220
- classification =
221
- Trace.Handlers.ModelHandlers.UserInteractions.scoreClassificationForInteractionToNextPaint(micros);
222
- } else {
223
- Platform.TypeScriptUtilities.assertNever(metric, `Unexpected metric ${metric}`);
61
+ function renderMetrics(): Lit.LitTemplate {
62
+ if (!insightSetKey || !parsedTrace) {
63
+ return Lit.nothing;
224
64
  }
225
-
226
- // NOTE: it is deliberate to use the same value for the title and
227
- // aria-label; the aria-label is used to give more context to
228
- // screen-readers, and the title is to aid users who may not know what
229
- // the red/orange/green classification is, or those who are unable to
230
- // easily distinguish the visual colour differences.
231
- // clang-format off
232
- const title = value !== null ?
233
- i18nString(UIStrings.metricScore, {PH1: metric, PH2: valueText, PH3: classification}) :
234
- i18nString(UIStrings.metricScoreUnavailable, {PH1: metric});
235
-
236
- return metricIsVisible(activeCategory, metric) ? html`
237
- <button class="metric"
238
- @click=${relevantEvent ? onClickMetric.bind(relevantEvent) : null}
239
- title=${title}
240
- aria-label=${title}
241
- >
242
- <div class="metric-value metric-value-${classification}">${valueDisplay}</div>
243
- </button>
244
- ` : Lit.nothing;
245
- // clang-format on
65
+ const metricsWidgetConfig = UI.Widget.widgetConfig(CWVMetrics, {
66
+ data: {
67
+ insightSetKey,
68
+ parsedTrace,
69
+ }
70
+ });
71
+ return html`<devtools-widget .widgetConfig=${metricsWidgetConfig}></devtools-widget>`;
246
72
  }
247
73
 
248
74
  function renderInsights(): Lit.LitTemplate {
@@ -275,14 +101,6 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
275
101
  // clang-format on
276
102
  };
277
103
 
278
- function metricIsVisible(activeCategory: Trace.Insights.Types.InsightCategory, label: 'LCP'|'CLS'|'INP'): boolean {
279
- if (activeCategory === Trace.Insights.Types.InsightCategory.ALL) {
280
- return true;
281
- }
282
-
283
- return label === activeCategory;
284
- }
285
-
286
104
  export class SidebarSingleInsightSet extends UI.Widget.Widget {
287
105
  #view: View;
288
106
  #insightRenderer = new Insights.InsightRenderer.InsightRenderer();
@@ -296,8 +114,6 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
296
114
  parsedTrace: null,
297
115
  };
298
116
 
299
- #didDismissFieldMismatchNotice = false;
300
-
301
117
  constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
302
118
  super(element, {useShadowDom: true});
303
119
  this.#view = view;
@@ -330,57 +146,15 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
330
146
  });
331
147
  }
332
148
 
333
- #onClickMetric(traceEvent: Trace.Types.Events.Event): void {
334
- this.element.dispatchEvent(new Insights.EventRef.EventReferenceClick(traceEvent));
335
- }
336
-
337
- #getLocalMetrics(insightSetKey: string): LocalMetrics|null {
338
- if (!this.#data.parsedTrace) {
339
- return null;
340
- }
341
-
342
- const insightSet = this.#data.parsedTrace.insights?.get(insightSetKey);
343
- if (!insightSet) {
344
- return null;
345
- }
346
-
347
- const lcp = Trace.Insights.Common.getLCP(insightSet);
348
- const cls = Trace.Insights.Common.getCLS(insightSet);
349
- const inp = Trace.Insights.Common.getINP(insightSet);
350
-
351
- return {lcp, cls, inp};
352
- }
353
-
354
- #getFieldMetrics(insightSetKey: string): Trace.Insights.Common.CrUXFieldMetricResults|null {
355
- if (!this.#data.parsedTrace) {
356
- return null;
357
- }
358
-
359
- const insightSet = this.#data.parsedTrace.insights?.get(insightSetKey);
360
- if (!insightSet) {
361
- return null;
362
- }
363
-
364
- const fieldMetricsResults = Trace.Insights.Common.getFieldMetricsForInsightSet(
365
- insightSet, this.#data.parsedTrace.metadata, CrUXManager.CrUXManager.instance().getSelectedScope());
366
- if (!fieldMetricsResults) {
367
- return null;
368
- }
369
-
370
- return fieldMetricsResults;
371
- }
372
-
373
- #onDismisFieldMismatchNotice(): void {
374
- this.#didDismissFieldMismatchNotice = true;
375
- this.requestUpdate();
376
- }
377
-
378
149
  static categorizeInsights(
379
150
  insightSets: Trace.Insights.Types.TraceInsightSets|null,
380
151
  insightSetKey: string,
381
152
  activeCategory: Trace.Insights.Types.InsightCategory,
382
153
  ): {shownInsights: InsightData[], passedInsights: InsightData[]} {
383
- const insightSet = insightSets?.get(insightSetKey);
154
+ if (!insightSets || !(insightSets instanceof Map)) {
155
+ return {shownInsights: [], passedInsights: []};
156
+ }
157
+ const insightSet = insightSets.get(insightSetKey);
384
158
  if (!insightSet) {
385
159
  return {shownInsights: [], passedInsights: []};
386
160
  }
@@ -433,7 +207,7 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
433
207
  insightSetKey,
434
208
  } = this.#data;
435
209
 
436
- if (!parsedTrace?.insights || !insightSetKey) {
210
+ if (!parsedTrace?.insights || !insightSetKey || !(parsedTrace.insights instanceof Map)) {
437
211
  return;
438
212
  }
439
213
 
@@ -442,34 +216,18 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
442
216
  return;
443
217
  }
444
218
 
445
- const local = this.#getLocalMetrics(insightSetKey);
446
- const field = this.#getFieldMetrics(insightSetKey);
219
+ const field = getFieldMetrics(parsedTrace, insightSetKey);
447
220
  const {shownInsights, passedInsights} = SidebarSingleInsightSet.categorizeInsights(
448
221
  parsedTrace.insights,
449
222
  insightSetKey,
450
223
  this.#data.activeCategory,
451
224
  );
452
225
 
453
- const localValues = {
454
- lcp: local?.lcp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(local?.lcp.value) : undefined,
455
- inp: local?.inp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(local?.inp.value) : undefined,
456
- };
457
- const fieldValues = field && {
458
- lcp: field.lcp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(field.lcp.value) : undefined,
459
- inp: field.inp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(field.inp.value) : undefined,
460
- };
461
- const showFieldMismatchNotice =
462
- !this.#didDismissFieldMismatchNotice && !!fieldValues && isFieldWorseThanLocal(localValues, fieldValues);
463
-
464
226
  const input: ViewInput = {
465
227
  shownInsights,
466
228
  passedInsights,
467
- local,
468
- field,
469
- activeCategory: this.#data.activeCategory,
470
- showFieldMismatchNotice,
471
- onDismisFieldMismatchNotice: this.#onDismisFieldMismatchNotice.bind(this),
472
- onClickMetric: this.#onClickMetric.bind(this),
229
+ insightSetKey,
230
+ parsedTrace,
473
231
  renderInsightComponent: insightData => this.#renderInsightComponent(insightSet, insightData, field),
474
232
  };
475
233
  this.#view(input, undefined, this.contentElement);
@@ -227,7 +227,7 @@ export namespace NumberWithUnit {
227
227
  element.append(lastPart);
228
228
  }
229
229
 
230
- return {text: element.textContent ?? '', element};
230
+ return {text: element.textContent, element};
231
231
  }
232
232
 
233
233
  export function formatMicroSecondsAsMillisFixed(time: Platform.Timing.MicroSeconds, fractionDigits = 0):
@@ -253,7 +253,7 @@ export namespace NumberWithUnit {
253
253
  element.append(lastPart);
254
254
  }
255
255
 
256
- return {text: element.textContent ?? '', element};
256
+ return {text: element.textContent, element};
257
257
  }
258
258
  }
259
259
 
@@ -5,6 +5,7 @@
5
5
  import * as Breadcrumbs from './Breadcrumbs.js';
6
6
  import * as BreadcrumbsUI from './BreadcrumbsUI.js';
7
7
  import * as CPUThrottlingSelector from './CPUThrottlingSelector.js';
8
+ import * as CWVMetrics from './CWVMetrics.js';
8
9
  import * as DetailsView from './DetailsView.js';
9
10
  import * as ExportTraceOptions from './ExportTraceOptions.js';
10
11
  import * as FieldSettingsDialog from './FieldSettingsDialog.js';
@@ -29,6 +30,7 @@ export {
29
30
  Breadcrumbs,
30
31
  BreadcrumbsUI,
31
32
  CPUThrottlingSelector,
33
+ CWVMetrics,
32
34
  DetailsView,
33
35
  ExportTraceOptions,
34
36
  FieldSettingsDialog,
@@ -0,0 +1,107 @@
1
+ /*
2
+ * Copyright 2026 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ .metrics {
8
+ display: grid;
9
+ align-items: end;
10
+ grid-template-columns: repeat(3, 1fr) 0.5fr;
11
+ row-gap: 5px;
12
+ }
13
+
14
+ .row-border {
15
+ grid-column: 1/5;
16
+ border-top: var(--sys-size-1) solid var(--sys-color-divider);
17
+ }
18
+
19
+ .row-label {
20
+ visibility: hidden;
21
+ font-size: var(--sys-size-7);
22
+ }
23
+
24
+ .metrics--field .row-label {
25
+ visibility: visible;
26
+ }
27
+
28
+ .metrics-row {
29
+ display: contents;
30
+ }
31
+
32
+ .metric {
33
+ flex: 1;
34
+ user-select: text;
35
+ cursor: pointer;
36
+ /* metric container is a button for a11y reasons, so remove default styles
37
+ * */
38
+ background: none;
39
+ border: none;
40
+ padding: 0;
41
+ display: block;
42
+ text-align: left;
43
+ }
44
+
45
+ .metric-value {
46
+ font-size: var(--sys-size-10);
47
+ }
48
+
49
+ .metric-value-bad {
50
+ color: var(--app-color-performance-bad);
51
+ }
52
+
53
+ .metric-value-ok {
54
+ color: var(--app-color-performance-ok);
55
+ }
56
+
57
+ .metric-value-good {
58
+ color: var(--app-color-performance-good);
59
+ }
60
+
61
+ .metric-score-unclassified {
62
+ color: var(--sys-color-token-subtle);
63
+ }
64
+
65
+ .metric-label {
66
+ font: var(--sys-typescale-body4-medium);
67
+ }
68
+
69
+ .number-with-unit {
70
+ white-space: nowrap;
71
+
72
+ .unit {
73
+ font-size: 14px;
74
+ padding: 0 1px;
75
+ }
76
+ }
77
+
78
+ .field-mismatch-notice {
79
+ display: grid;
80
+ grid-template-columns: auto auto;
81
+ align-items: center;
82
+ background-color: var(--sys-color-surface3);
83
+ margin: var(--sys-size-6) 0;
84
+ border-radius: var(--sys-shape-corner-extra-small);
85
+ border: var(--sys-size-1) solid var(--sys-color-divider);
86
+
87
+ h3 {
88
+ margin-block: 3px;
89
+ font: var(--sys-typescale-body4-medium);
90
+ color: var(--sys-color-on-base);
91
+ padding: var(--sys-size-5) var(--sys-size-6) 0 var(--sys-size-6);
92
+ }
93
+
94
+ .field-mismatch-notice__body {
95
+ padding: var(--sys-size-3) var(--sys-size-6) var(--sys-size-5) var(--sys-size-6);
96
+ }
97
+
98
+ button {
99
+ padding: 5px;
100
+ background: unset;
101
+ border: unset;
102
+ font: inherit;
103
+ color: var(--sys-color-primary);
104
+ text-decoration: underline;
105
+ cursor: pointer;
106
+ }
107
+ }
@@ -9,77 +9,6 @@
9
9
  padding: 5px 8px;
10
10
  }
11
11
 
12
- .metrics {
13
- display: grid;
14
- align-items: end;
15
- grid-template-columns: repeat(3, 1fr) 0.5fr;
16
- row-gap: 5px;
17
- }
18
-
19
- .row-border {
20
- grid-column: 1/5;
21
- border-top: var(--sys-size-1) solid var(--sys-color-divider);
22
- }
23
-
24
- .row-label {
25
- visibility: hidden;
26
- font-size: var(--sys-size-7);
27
- }
28
-
29
- .metrics--field .row-label {
30
- visibility: visible;
31
- }
32
-
33
- .metrics-row {
34
- display: contents;
35
- }
36
-
37
- .metric {
38
- flex: 1;
39
- user-select: text;
40
- cursor: pointer;
41
- /* metric container is a button for a11y reasons, so remove default styles
42
- * */
43
- background: none;
44
- border: none;
45
- padding: 0;
46
- display: block;
47
- text-align: left;
48
- }
49
-
50
- .metric-value {
51
- font-size: var(--sys-size-10);
52
- }
53
-
54
- .metric-value-bad {
55
- color: var(--app-color-performance-bad);
56
- }
57
-
58
- .metric-value-ok {
59
- color: var(--app-color-performance-ok);
60
- }
61
-
62
- .metric-value-good {
63
- color: var(--app-color-performance-good);
64
- }
65
-
66
- .metric-score-unclassified {
67
- color: var(--sys-color-token-subtle);
68
- }
69
-
70
- .metric-label {
71
- font: var(--sys-typescale-body4-medium);
72
- }
73
-
74
- .number-with-unit {
75
- white-space: nowrap;
76
-
77
- .unit {
78
- font-size: 14px;
79
- padding: 0 1px;
80
- }
81
- }
82
-
83
12
  .passed-insights-section {
84
13
  margin-top: var(--sys-size-5);
85
14
 
@@ -87,34 +16,3 @@
87
16
  font-weight: var(--ref-typeface-weight-medium);
88
17
  }
89
18
  }
90
-
91
- .field-mismatch-notice {
92
- display: grid;
93
- grid-template-columns: auto auto;
94
- align-items: center;
95
- background-color: var(--sys-color-surface3);
96
- margin: var(--sys-size-6) 0;
97
- border-radius: var(--sys-shape-corner-extra-small);
98
- border: var(--sys-size-1) solid var(--sys-color-divider);
99
-
100
- h3 {
101
- margin-block: 3px;
102
- font: var(--sys-typescale-body4-medium);
103
- color: var(--sys-color-on-base);
104
- padding: var(--sys-size-5) var(--sys-size-6) 0 var(--sys-size-6);
105
- }
106
-
107
- .field-mismatch-notice__body {
108
- padding: var(--sys-size-3) var(--sys-size-6) var(--sys-size-5) var(--sys-size-6);
109
- }
110
-
111
- button {
112
- padding: 5px;
113
- background: unset;
114
- border: unset;
115
- font: inherit;
116
- color: var(--sys-color-primary);
117
- text-decoration: underline;
118
- cursor: pointer;
119
- }
120
- }
@@ -368,3 +368,14 @@ Common.Revealer.registerRevealer({
368
368
  return new Timeline.TimelinePanel.InsightRevealer();
369
369
  },
370
370
  });
371
+
372
+ Common.Revealer.registerRevealer({
373
+ contextTypes() {
374
+ return maybeRetrieveContextTypes(Timeline => [Timeline.Utils.Helpers.RevealableCoreVitals]);
375
+ },
376
+ destination: Common.Revealer.RevealerDestination.TIMELINE_PANEL,
377
+ async loadRevealer() {
378
+ const Timeline = await loadTimelineModule();
379
+ return new Timeline.TimelinePanel.CoreVitalsRevealer();
380
+ },
381
+ });
@@ -147,8 +147,12 @@ export function formatOriginWithEntity(
147
147
  return originWithEntity;
148
148
  }
149
149
 
150
- /** Thin wrapper class to enable revealing an individual insight in Timeline panel. **/
151
150
  export class RevealableInsight {
152
151
  constructor(public insight: Trace.Insights.Types.InsightModel) {
153
152
  }
154
153
  }
154
+
155
+ export class RevealableCoreVitals {
156
+ constructor(public insightSetKey: string) {
157
+ }
158
+ }
@@ -179,7 +179,7 @@
179
179
  }
180
180
 
181
181
  .code {
182
- font-family: monospace;
182
+ font-family: var(--monospace-font-family);
183
183
  }
184
184
 
185
185
  .learn-more {
@@ -1,7 +1,7 @@
1
1
  Name: Dependencies sourced from the upstream `chromium` repository
2
2
  URL: Internal
3
3
  Version: N/A
4
- Revision: a21e41557a9f4829967a0602aeecaa02de88801c
4
+ Revision: ce01102937348db7b88c8a4257ee4b3ac702eb1a
5
5
  Update Mechanism: Manual (https://crbug.com/428069060)
6
6
  License: BSD-3-Clause
7
7
  License File: LICENSE