chrome-devtools-frontend 1.0.1553956 → 1.0.1555430

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 (85) hide show
  1. package/front_end/Images/src/spark.svg +10 -0
  2. package/front_end/core/protocol_client/InspectorBackend.ts +1 -1
  3. package/front_end/core/root/Runtime.ts +0 -4
  4. package/front_end/core/sdk/DOMModel.ts +101 -7
  5. package/front_end/core/sdk/ResourceTreeModel.ts +0 -1
  6. package/front_end/generated/SupportedCSSProperties.js +18 -0
  7. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
  8. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  9. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -1
  10. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
  11. package/front_end/{ui/components → models}/annotations/AnnotationRepository.ts +3 -3
  12. package/front_end/models/annotations/README.md +7 -0
  13. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +8 -0
  14. package/front_end/models/javascript_metadata/NativeFunctions.js +15 -23
  15. package/front_end/models/stack_trace/StackTrace.ts +13 -2
  16. package/front_end/models/stack_trace/StackTraceImpl.ts +81 -6
  17. package/front_end/models/stack_trace/StackTraceModel.ts +35 -3
  18. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +45 -4
  19. package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +57 -0
  20. package/front_end/panels/ai_assistance/components/ChatView.ts +1 -0
  21. package/front_end/panels/ai_assistance/components/artifactsViewer.css +10 -0
  22. package/front_end/panels/application/BounceTrackingMitigationsTreeElement.ts +2 -6
  23. package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +133 -118
  24. package/front_end/panels/application/preloading/PreloadingView.ts +12 -6
  25. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +230 -237
  26. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +96 -79
  27. package/front_end/panels/application/preloading/components/preloadingGrid.css +26 -29
  28. package/front_end/panels/application/preloading/preloadingView.css +6 -0
  29. package/front_end/panels/common/Annotation.ts +1 -1
  30. package/front_end/panels/common/AnnotationManager.ts +1 -1
  31. package/front_end/panels/common/ExtensionView.ts +1 -0
  32. package/front_end/panels/console/ConsoleContextSelector.ts +74 -9
  33. package/front_end/panels/console/consoleContextSelector.css +31 -29
  34. package/front_end/panels/coverage/coverageListView.css +59 -57
  35. package/front_end/panels/elements/ElementsPanel.ts +1 -1
  36. package/front_end/panels/elements/ElementsTreeElement.ts +39 -1
  37. package/front_end/panels/elements/ElementsTreeOutline.ts +23 -21
  38. package/front_end/panels/elements/TopLayerContainer.ts +26 -91
  39. package/front_end/panels/explain/components/ConsoleInsight.ts +3 -3
  40. package/front_end/panels/network/NetworkItemView.ts +1 -1
  41. package/front_end/panels/network/NetworkLogView.ts +1 -1
  42. package/front_end/panels/network/NetworkPanel.ts +1 -1
  43. package/front_end/panels/recorder/RecorderController.ts +0 -1
  44. package/front_end/panels/security/CookieControlsView.ts +21 -10
  45. package/front_end/panels/security/SecurityPanelSidebar.ts +5 -0
  46. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -1
  47. package/front_end/panels/timeline/TimelineUIUtils.ts +5 -8
  48. package/front_end/panels/timeline/components/Sidebar.ts +16 -7
  49. package/front_end/panels/timeline/components/SidebarInsightsTab.ts +169 -129
  50. package/front_end/panels/timeline/components/TimelineSummary.ts +75 -54
  51. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +16 -25
  52. package/front_end/panels/timeline/components/insights/Cache.ts +12 -8
  53. package/front_end/panels/timeline/components/insights/Checklist.ts +53 -43
  54. package/front_end/panels/timeline/components/insights/DOMSize.ts +25 -21
  55. package/front_end/panels/timeline/components/insights/DocumentLatency.ts +6 -3
  56. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +7 -7
  57. package/front_end/panels/timeline/components/insights/FontDisplay.ts +7 -5
  58. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +11 -9
  59. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +7 -6
  60. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +7 -5
  61. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +20 -18
  62. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +12 -12
  63. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +7 -3
  64. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +7 -7
  65. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +7 -5
  66. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +15 -13
  67. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +2 -2
  68. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +15 -14
  69. package/front_end/panels/timeline/components/insights/Table.ts +152 -130
  70. package/front_end/panels/timeline/components/insights/ThirdParties.ts +11 -9
  71. package/front_end/panels/timeline/components/sidebarInsightsTab.css +50 -48
  72. package/front_end/panels/timeline/components/timelineSummary.css +58 -57
  73. package/front_end/panels/timeline/thirdPartyTreeView.css +109 -0
  74. package/front_end/panels/timeline/timelineDetailsView.css +2 -4
  75. package/front_end/panels/timeline/timelinePanel.css +0 -110
  76. package/front_end/third_party/chromium/README.chromium +1 -1
  77. package/front_end/ui/components/settings/SettingCheckbox.ts +4 -6
  78. package/front_end/ui/legacy/TabbedPane.ts +20 -13
  79. package/front_end/ui/legacy/ViewManager.ts +2 -32
  80. package/front_end/ui/legacy/Widget.ts +1 -3
  81. package/front_end/ui/legacy/tabbedPane.css +4 -7
  82. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  83. package/package.json +1 -1
  84. /package/front_end/{ui/components → models}/annotations/AnnotationType.ts +0 -0
  85. /package/front_end/{ui/components → models}/annotations/annotations.ts +0 -0
@@ -1,11 +1,9 @@
1
1
  // Copyright 2024 The Chromium Authors
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
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
4
 
6
5
  import * as Trace from '../../../models/trace/trace.js';
7
6
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
8
- import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
9
7
  import * as UI from '../../../ui/legacy/legacy.js';
10
8
  import * as Lit from '../../../ui/lit/lit.js';
11
9
  import * as Utils from '../utils/utils.js';
@@ -16,9 +14,136 @@ import sidebarInsightsTabStyles from './sidebarInsightsTab.css.js';
16
14
  import {SidebarSingleInsightSet, type SidebarSingleInsightSetData} from './SidebarSingleInsightSet.js';
17
15
 
18
16
  const {html} = Lit;
17
+ const {widgetConfig} = UI.Widget;
18
+
19
+ interface ViewInput {
20
+ parsedTrace: Trace.TraceModel.ParsedTrace;
21
+ labels: string[];
22
+ activeInsightSet: Trace.Insights.Types.InsightSet|null;
23
+ activeInsight: ActiveInsight|null;
24
+ selectedCategory: Trace.Insights.Types.InsightCategory;
25
+ onInsightSetToggled: (insightSet: Trace.Insights.Types.InsightSet) => void;
26
+ onInsightSetHovered: (insightSet: Trace.Insights.Types.InsightSet) => void;
27
+ onInsightSetUnhovered: () => void;
28
+ onZoomClick: (insightSet: Trace.Insights.Types.InsightSet) => void;
29
+ }
30
+
31
+ type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
32
+
33
+ export const DEFAULT_VIEW: View = (input, output, target) => {
34
+ const {
35
+ parsedTrace,
36
+ labels,
37
+ activeInsightSet,
38
+ activeInsight,
39
+ selectedCategory,
40
+ onInsightSetToggled,
41
+ onInsightSetHovered,
42
+ onInsightSetUnhovered,
43
+ onZoomClick,
44
+ } = input;
45
+
46
+ const insights = parsedTrace.insights;
47
+ if (!insights) {
48
+ return;
49
+ }
19
50
 
20
- export class SidebarInsightsTab extends HTMLElement {
21
- readonly #shadow = this.attachShadow({mode: 'open'});
51
+ const hasMultipleInsightSets = insights.size > 1;
52
+
53
+ // clang-format off
54
+ Lit.render(html`
55
+ <style>${sidebarInsightsTabStyles}</style>
56
+ <div class="insight-sets-wrapper">
57
+ ${[...insights.values()].map((insightSet, index) => {
58
+ const {id, url} = insightSet;
59
+ const data: SidebarSingleInsightSetData = {
60
+ insightSetKey: id,
61
+ activeCategory: selectedCategory,
62
+ activeInsight,
63
+ parsedTrace,
64
+ };
65
+ const selected = insightSet === activeInsightSet;
66
+
67
+ const contents = html`
68
+ <devtools-widget
69
+ data-insight-set-key=${id}
70
+ .widgetConfig=${widgetConfig(SidebarSingleInsightSet, {data})}
71
+ ></devtools-widget>
72
+ `;
73
+
74
+ if (hasMultipleInsightSets) {
75
+ return html`<details ?open=${selected}>
76
+ <summary
77
+ @click=${() => onInsightSetToggled(insightSet)}
78
+ @mouseenter=${() => onInsightSetHovered(insightSet)}
79
+ @mouseleave=${() => onInsightSetUnhovered()}
80
+ title=${url.href}>
81
+ ${renderDropdownIcon(selected)}
82
+ <span>${labels[index]}</span>
83
+ <span class='zoom-button'
84
+ @click=${(event: Event) => {
85
+ event.stopPropagation();
86
+ onZoomClick(insightSet);
87
+ }}
88
+ >
89
+ ${renderZoomButton(selected)}
90
+ </span>
91
+ </summary>
92
+ ${contents}
93
+ </details>`;
94
+ }
95
+
96
+ return contents;
97
+ })}
98
+ </div>
99
+ `, target);
100
+ // clang-format on
101
+ };
102
+
103
+ function renderZoomButton(insightSetToggled: boolean): Lit.TemplateResult {
104
+ const classes = Lit.Directives.classMap({
105
+ 'zoom-icon': true,
106
+ active: insightSetToggled,
107
+ });
108
+
109
+ // clang-format off
110
+ return html`
111
+ <div class=${classes}>
112
+ <devtools-button .data=${{
113
+ variant: Buttons.Button.Variant.ICON,
114
+ iconName: 'center-focus-weak',
115
+ size: Buttons.Button.Size.SMALL,
116
+ } as Buttons.Button.ButtonData}
117
+ ></devtools-button></div>`;
118
+ // clang-format on
119
+ }
120
+
121
+ function renderDropdownIcon(insightSetToggled: boolean): Lit.TemplateResult {
122
+ const containerClasses = Lit.Directives.classMap({
123
+ 'dropdown-icon': true,
124
+ active: insightSetToggled,
125
+ });
126
+
127
+ // clang-format off
128
+ return html`
129
+ <div class=${containerClasses}>
130
+ <devtools-button .data=${{
131
+ variant: Buttons.Button.Variant.ICON,
132
+ iconName: 'chevron-right',
133
+ size: Buttons.Button.Size.SMALL,
134
+ } as Buttons.Button.ButtonData}
135
+ ></devtools-button></div>
136
+ `;
137
+ // clang-format on
138
+ }
139
+
140
+ export class SidebarInsightsTab extends UI.Widget.Widget {
141
+ static createWidgetElement(): UI.Widget.WidgetElement<SidebarInsightsTab> {
142
+ const widgetElement = document.createElement('devtools-widget') as UI.Widget.WidgetElement<SidebarInsightsTab>;
143
+ return widgetElement;
144
+ }
145
+
146
+ #view: View;
22
147
  #parsedTrace: Trace.TraceModel.ParsedTrace|null = null;
23
148
  #activeInsight: ActiveInsight|null = null;
24
149
  #selectedCategory = Trace.Insights.Types.InsightCategory.ALL;
@@ -26,9 +151,14 @@ export class SidebarInsightsTab extends HTMLElement {
26
151
  * When a trace has sets of insights, we show an accordion with each
27
152
  * set within. A set can be specific to a single navigation, or include the
28
153
  * beginning of the trace up to the first navigation.
29
- * You can only have one of these open at any time, and we track it via this ID.
154
+ * You can only have one of these open at any time.
30
155
  */
31
- #selectedInsightSetKey: string|null = null;
156
+ #selectedInsightSet: Trace.Insights.Types.InsightSet|null = null;
157
+
158
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
159
+ super(element, {useShadowDom: true});
160
+ this.#view = view;
161
+ }
32
162
 
33
163
  // TODO(paulirish): add back a disconnectedCallback() to avoid memory leaks that doesn't cause b/372943062
34
164
 
@@ -38,14 +168,14 @@ export class SidebarInsightsTab extends HTMLElement {
38
168
  }
39
169
 
40
170
  this.#parsedTrace = data;
41
- this.#selectedInsightSetKey = null;
171
+ this.#selectedInsightSet = null;
42
172
 
43
173
  if (this.#parsedTrace?.insights) {
44
174
  /** Select the first set. Filtering out trivial sets was done back in {@link Trace.Processor.#computeInsightsForInitialTracePeriod} */
45
- this.#selectedInsightSetKey = [...this.#parsedTrace.insights.keys()].at(0) ?? null;
175
+ this.#selectedInsightSet = [...this.#parsedTrace.insights.values()].at(0) ?? null;
46
176
  }
47
177
 
48
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
178
+ this.requestUpdate();
49
179
  }
50
180
 
51
181
  get activeInsight(): ActiveInsight|null {
@@ -56,81 +186,39 @@ export class SidebarInsightsTab extends HTMLElement {
56
186
  if (active === this.#activeInsight) {
57
187
  return;
58
188
  }
189
+
59
190
  this.#activeInsight = active;
60
191
 
61
- // Only update the insightSetKey if there is an active insight. Otherwise, closing an insight
192
+ // Only update selectedInsightSet if there is an active insight. Otherwise, closing an insight
62
193
  // would also collapse the insight set. Usually the proper insight set is already set because
63
194
  // the user has it open already in order for this setter to be called, but insights can also
64
195
  // be activated by clicking on a insight chip in the Summary panel, which may require opening
65
196
  // a different insight set.
66
197
  if (this.#activeInsight) {
67
- this.#selectedInsightSetKey = this.#activeInsight.insightSetKey;
198
+ this.#selectedInsightSet = this.#parsedTrace?.insights?.get(this.#activeInsight.insightSetKey) ?? null;
68
199
  }
69
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
200
+ this.requestUpdate();
70
201
  }
71
202
 
72
- #insightSetToggled(id: string): void {
73
- this.#selectedInsightSetKey = this.#selectedInsightSetKey === id ? null : id;
203
+ #onInsightSetToggled(insightSet: Trace.Insights.Types.InsightSet): void {
204
+ this.#selectedInsightSet = this.#selectedInsightSet === insightSet ? null : insightSet;
74
205
  // Update the active insight set.
75
- if (this.#selectedInsightSetKey !== this.#activeInsight?.insightSetKey) {
76
- this.dispatchEvent(new Insights.SidebarInsight.InsightDeactivated());
206
+ if (this.#selectedInsightSet?.id !== this.#activeInsight?.insightSetKey) {
207
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightDeactivated());
77
208
  }
78
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
79
- }
80
-
81
- #insightSetHovered(id: string): void {
82
- const data = this.#parsedTrace?.insights?.get(id);
83
- data && this.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered(data.bounds));
209
+ this.requestUpdate();
84
210
  }
85
211
 
86
- #insightSetUnhovered(): void {
87
- this.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered());
212
+ #onInsightSetHovered(insightSet: Trace.Insights.Types.InsightSet): void {
213
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered(insightSet.bounds));
88
214
  }
89
215
 
90
- #onZoomClick(event: Event, id: string): void {
91
- event.stopPropagation();
92
- const data = this.#parsedTrace?.insights?.get(id);
93
- if (!data) {
94
- return;
95
- }
96
- this.dispatchEvent(new Insights.SidebarInsight.InsightSetZoom(data.bounds));
216
+ #onInsightSetUnhovered(): void {
217
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered());
97
218
  }
98
219
 
99
- #renderZoomButton(insightSetToggled: boolean): Lit.TemplateResult {
100
- const classes = Lit.Directives.classMap({
101
- 'zoom-icon': true,
102
- active: insightSetToggled,
103
- });
104
-
105
- // clang-format off
106
- return html`
107
- <div class=${classes}>
108
- <devtools-button .data=${{
109
- variant: Buttons.Button.Variant.ICON,
110
- iconName: 'center-focus-weak',
111
- size: Buttons.Button.Size.SMALL,
112
- } as Buttons.Button.ButtonData}
113
- ></devtools-button></div>`;
114
- // clang-format on
115
- }
116
-
117
- #renderDropdownIcon(insightSetToggled: boolean): Lit.TemplateResult {
118
- const containerClasses = Lit.Directives.classMap({
119
- 'dropdown-icon': true,
120
- active: insightSetToggled,
121
- });
122
-
123
- // clang-format off
124
- return html`
125
- <div class=${containerClasses}>
126
- <devtools-button .data=${{
127
- variant: Buttons.Button.Variant.ICON,
128
- iconName: 'chevron-right',
129
- size: Buttons.Button.Size.SMALL,
130
- } as Buttons.Button.ButtonData}
131
- ></devtools-button></div>
132
- `;
133
- // clang-format on
220
+ #onZoomClick(insightSet: Trace.Insights.Types.InsightSet): void {
221
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetZoom(insightSet.bounds));
134
222
  }
135
223
 
136
224
  highlightActiveInsight(): void {
@@ -139,76 +227,28 @@ export class SidebarInsightsTab extends HTMLElement {
139
227
  }
140
228
 
141
229
  // Find the right set for this insight via the set key.
142
- const set = this.#shadow?.querySelector<UI.Widget.WidgetElement<SidebarSingleInsightSet>>(
230
+ const set = this.element.shadowRoot?.querySelector<UI.Widget.WidgetElement<SidebarSingleInsightSet>>(
143
231
  `[data-insight-set-key="${this.#activeInsight.insightSetKey}"]`);
144
232
  set?.getWidget()?.highlightActiveInsight();
145
233
  }
146
234
 
147
- #render(): void {
235
+ override performUpdate(): void {
148
236
  if (!this.#parsedTrace?.insights) {
149
- Lit.render(Lit.nothing, this.#shadow, {host: this});
150
237
  return;
151
238
  }
152
239
 
153
- const insights = this.#parsedTrace.insights;
154
- const hasMultipleInsightSets = insights.size > 1;
155
- const labels = Utils.Helpers.createUrlLabels([...insights.values()].map(({url}) => url));
156
-
157
- const contents =
158
- // clang-format off
159
- html`
160
- <style>${sidebarInsightsTabStyles}</style>
161
- <div class="insight-sets-wrapper">
162
- ${[...insights.values()].map(({id, url}, index) => {
163
- const data: SidebarSingleInsightSetData = {
164
- insightSetKey: id,
165
- activeCategory: this.#selectedCategory,
166
- activeInsight: this.#activeInsight,
167
- parsedTrace: this.#parsedTrace,
168
- };
169
-
170
- const contents = html`
171
- <devtools-widget
172
- data-insight-set-key=${id}
173
- .widgetConfig=${UI.Widget.widgetConfig(SidebarSingleInsightSet, {data})}
174
- ></devtools-widget>
175
- `;
176
-
177
- if (hasMultipleInsightSets) {
178
- return html`<details
179
- ?open=${id === this.#selectedInsightSetKey}
180
- >
181
- <summary
182
- @click=${() => this.#insightSetToggled(id)}
183
- @mouseenter=${() => this.#insightSetHovered(id)}
184
- @mouseleave=${() => this.#insightSetUnhovered()}
185
- title=${url.href}>
186
- ${this.#renderDropdownIcon(id === this.#selectedInsightSetKey)}
187
- <span>${labels[index]}</span>
188
- <span class='zoom-button' @click=${(event: Event) => this.#onZoomClick(event, id)}>${this.#renderZoomButton(id === this.#selectedInsightSetKey)}</span>
189
- </summary>
190
- ${contents}
191
- </details>`;
192
- }
193
-
194
- return contents;
195
- })}
196
- </div>
197
- `;
198
- // clang-format on
199
-
200
- // Insight components contain state, so to prevent insights from previous trace loads breaking things we use the parsedTrace
201
- // as a render key.
202
- // Note: newer Lit has `keyed`, but we don't have that, so we do it manually. https://lit.dev/docs/templates/directives/#keyed
203
- const result = Lit.Directives.repeat([contents], () => this.#parsedTrace, template => template);
204
- Lit.render(result, this.#shadow, {host: this});
205
- }
206
- }
207
-
208
- customElements.define('devtools-performance-sidebar-insights', SidebarInsightsTab);
209
-
210
- declare global {
211
- interface HTMLElementTagNameMap {
212
- 'devtools-performance-sidebar-insights': SidebarInsightsTab;
240
+ const insightSets = [...this.#parsedTrace.insights.values()];
241
+ const input: ViewInput = {
242
+ parsedTrace: this.#parsedTrace,
243
+ labels: Utils.Helpers.createUrlLabels(insightSets.map(({url}) => url)),
244
+ activeInsightSet: this.#selectedInsightSet,
245
+ activeInsight: this.#activeInsight,
246
+ selectedCategory: this.#selectedCategory,
247
+ onInsightSetToggled: this.#onInsightSetToggled.bind(this),
248
+ onInsightSetHovered: this.#onInsightSetHovered.bind(this),
249
+ onInsightSetUnhovered: this.#onInsightSetUnhovered.bind(this),
250
+ onZoomClick: this.#onZoomClick.bind(this),
251
+ };
252
+ this.#view(input, undefined, this.contentElement);
213
253
  }
214
254
  }
@@ -1,10 +1,9 @@
1
1
  // Copyright 2024 The Chromium Authors
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
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
4
 
6
5
  import * as i18n from '../../../core/i18n/i18n.js';
7
- import type * as Trace from '../../../models/trace/trace.js';
6
+ import * as Buttons from '../../../ui/components/buttons/buttons.js';
8
7
  import * as UI from '../../../ui/legacy/legacy.js';
9
8
  import * as Lit from '../../../ui/lit/lit.js';
10
9
 
@@ -33,74 +32,96 @@ export interface CategoryData {
33
32
  title: string;
34
33
  }
35
34
 
36
- export interface SummaryTableData {
37
- total: number;
35
+ interface ViewInput {
38
36
  rangeStart: number;
39
37
  rangeEnd: number;
38
+ total: number;
40
39
  categories: CategoryData[];
41
- selectedEvents: Trace.Types.Events.Event[];
42
40
  }
43
41
 
44
- export class CategorySummary extends HTMLElement {
45
- readonly #shadow =
46
- UI.UIUtils.createShadowRootWithCoreStyles(this, {cssFile: timelineSummaryStyles, delegatesFocus: undefined});
42
+ type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
43
+
44
+ export const CATEGORY_SUMMARY_DEFAULT_VIEW: View = (input, _output, target): void => {
45
+ // clang-format off
46
+ render(html`
47
+ <style>${timelineSummaryStyles}</style>
48
+ <style>@scope to (devtools-widget > *) { ${UI.inspectorCommonStyles} }</style>
49
+ <style>@scope to (devtools-widget > *) { ${Buttons.textButtonStyles} }</style>
50
+ <div class="timeline-summary">
51
+ <div class="summary-range">${i18nString(UIStrings.rangeSS, {PH1: i18n.TimeUtilities.millisToString(input.rangeStart), PH2: i18n.TimeUtilities.millisToString(input.rangeEnd)})}</div>
52
+ <div class="category-summary">
53
+ ${input.categories.map(category => {
54
+ return html`
55
+ <div class="category-row">
56
+ <div class="category-swatch" style="background-color: ${category.color};"></div>
57
+ <div class="category-name">${category.title}</div>
58
+ <div class="category-value">
59
+ ${i18n.TimeUtilities.preciseMillisToString(category.value)}
60
+ <div class="background-bar-container">
61
+ <div class="background-bar" style='width: ${(category.value * 100 / input.total).toFixed(1)}%;'></div>
62
+ </div>
63
+ </div>
64
+ </div>`;
65
+ })}
66
+ <div class="category-row">
67
+ <div class="category-swatch"></div>
68
+ <div class="category-name">${i18nString(UIStrings.total)}</div>
69
+ <div class="category-value">
70
+ ${i18n.TimeUtilities.preciseMillisToString(input.total)}
71
+ <div class="background-bar-container">
72
+ <div class="background-bar"></div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ </div>`, target);
81
+ // clang-format on
82
+ };
83
+
84
+ export class CategorySummary extends UI.Widget.Widget {
85
+ #view: View;
47
86
 
48
87
  #rangeStart = 0;
49
88
  #rangeEnd = 0;
50
89
  #total = 0;
51
90
  #categories: CategoryData[] = [];
52
91
 
53
- set data(data: SummaryTableData) {
54
- this.#total = data.total;
55
- this.#categories = data.categories;
56
- this.#rangeStart = data.rangeStart;
57
- this.#rangeEnd = data.rangeEnd;
58
- this.#render();
92
+ constructor(view?: View) {
93
+ super();
94
+ this.#view = view ?? CATEGORY_SUMMARY_DEFAULT_VIEW;
95
+ this.requestUpdate();
59
96
  }
60
97
 
61
- #render(): void {
62
- // clang-format off
63
- const output = html`
64
- <div class="timeline-summary">
65
- <div class="summary-range">${i18nString(UIStrings.rangeSS, {PH1: i18n.TimeUtilities.millisToString(this.#rangeStart), PH2: i18n.TimeUtilities.millisToString(this.#rangeEnd)})}</div>
66
- <div class="category-summary">
67
- ${this.#categories.map(category => {
68
- return html`
69
- <div class="category-row">
70
- <div class="category-swatch" style="background-color: ${category.color};"></div>
71
- <div class="category-name">${category.title}</div>
72
- <div class="category-value">
73
- ${i18n.TimeUtilities.preciseMillisToString(category.value)}
74
- <div class="background-bar-container">
75
- <div class="background-bar" style='width: ${(category.value * 100 / this.#total).toFixed(1)}%;'></div>
76
- </div>
77
- </div>
78
- </div>`;
79
- })}
80
- <div class="category-row">
81
- <div class="category-swatch"></div>
82
- <div class="category-name">${i18nString(UIStrings.total)}</div>
83
- <div class="category-value">
84
- ${i18n.TimeUtilities.preciseMillisToString(this.#total)}
85
- <div class="background-bar-container">
86
- <div class="background-bar"></div>
87
- </div>
88
- </div>
89
- </div>
90
- </div>
91
- </div>
92
- </div>
98
+ set total(total: number) {
99
+ this.#total = total;
100
+ this.requestUpdate();
101
+ }
93
102
 
94
- </div>`;
95
- // clang-format on
96
- render(output, this.#shadow, {host: this});
103
+ set rangeStart(rangeStart: number) {
104
+ this.#rangeStart = rangeStart;
105
+ this.requestUpdate();
97
106
  }
98
- }
99
107
 
100
- customElements.define('devtools-performance-timeline-summary', CategorySummary);
108
+ set rangeEnd(rangeEnd: number) {
109
+ this.#rangeEnd = rangeEnd;
110
+ this.requestUpdate();
111
+ }
112
+
113
+ set categories(categories: CategoryData[]) {
114
+ this.#categories = categories;
115
+ this.requestUpdate();
116
+ }
101
117
 
102
- declare global {
103
- interface HTMLElementTagNameMap {
104
- 'devtools-performance-timeline-summary': CategorySummary;
118
+ override performUpdate(): void {
119
+ const viewInput: ViewInput = {
120
+ rangeStart: this.#rangeStart,
121
+ rangeEnd: this.#rangeEnd,
122
+ total: this.#total,
123
+ categories: this.#categories,
124
+ };
125
+ this.#view(viewInput, undefined, this.contentElement);
105
126
  }
106
127
  }
@@ -1,7 +1,6 @@
1
1
  // Copyright 2024 The Chromium Authors
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
- /* eslint-disable @devtools/no-imperative-dom-api */
5
4
 
6
5
  import '../../../../ui/components/markdown_view/markdown_view.js';
7
6
 
@@ -70,7 +69,7 @@ interface ViewInput {
70
69
  model: InsightModel;
71
70
  selected: boolean;
72
71
  isAIAssistanceContext: boolean;
73
- canShowAskAI: boolean;
72
+ showAskAI: boolean;
74
73
  estimatedSavingsString: string|null;
75
74
  estimatedSavingsAriaLabel: string|null;
76
75
  renderContent: () => Lit.LitTemplate;
@@ -89,7 +88,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
89
88
  estimatedSavingsString,
90
89
  estimatedSavingsAriaLabel,
91
90
  isAIAssistanceContext,
92
- canShowAskAI,
91
+ showAskAI,
93
92
  dispatchInsightToggle,
94
93
  renderContent,
95
94
  onHeaderKeyDown,
@@ -122,7 +121,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
122
121
  <div class="insight-body">
123
122
  <div class="insight-description">${md(model.description)}</div>
124
123
  <div class="insight-content">${content}</div>
125
- ${canShowAskAI ? html`
124
+ ${showAskAI ? html`
126
125
  <div class="ask-ai-btn-wrap">
127
126
  <devtools-button class="ask-ai"
128
127
  .variant=${Buttons.Button.Variant.OUTLINED}
@@ -206,9 +205,6 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
206
205
  #view: View;
207
206
  abstract internalName: string;
208
207
 
209
- // This flag tracks if the Insights AI feature is enabled within Chrome for
210
- // the active user.
211
- #askAiEnabled = false;
212
208
  // Tracks if this component is rendered withing the AI assistance panel.
213
209
  // Currently only relevant to GreenDev.
214
210
  #isAIAssistanceContext = false;
@@ -246,16 +242,6 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
246
242
  return false;
247
243
  }
248
244
 
249
- override wasShown(): void {
250
- super.wasShown();
251
-
252
- // Used for unit test purposes when querying the DOM.
253
- this.element.dataset.insightName = this.internalName;
254
-
255
- const {devToolsAiAssistancePerformanceAgent} = Root.Runtime.hostConfig;
256
- this.#askAiEnabled = Boolean(devToolsAiAssistancePerformanceAgent?.enabled);
257
- }
258
-
259
245
  set isAIAssistanceContext(isAIAssistanceContext: boolean) {
260
246
  this.#isAIAssistanceContext = isAIAssistanceContext;
261
247
  this.requestUpdate();
@@ -300,11 +286,11 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
300
286
  this.requestUpdate();
301
287
  }
302
288
 
303
- set agentFocus(agentFocus: AIAssistance.AIContext.AgentFocus) {
289
+ set agentFocus(agentFocus: AIAssistance.AIContext.AgentFocus|null) {
304
290
  this.#agentFocus = agentFocus;
305
291
  }
306
292
 
307
- set fieldMetrics(fieldMetrics: Trace.Insights.Common.CrUXFieldMetricResults) {
293
+ set fieldMetrics(fieldMetrics: Trace.Insights.Common.CrUXFieldMetricResults|null) {
308
294
  this.#fieldMetrics = fieldMetrics;
309
295
  }
310
296
 
@@ -422,7 +408,7 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
422
408
  estimatedSavingsString: this.getEstimatedSavingsString(),
423
409
  estimatedSavingsAriaLabel: this.#getEstimatedSavingsAriaLabel(),
424
410
  isAIAssistanceContext: this.#isAIAssistanceContext,
425
- canShowAskAI: this.#canShowAskAI(),
411
+ showAskAI: this.#canShowAskAI(),
426
412
  dispatchInsightToggle: () => this.#dispatchInsightToggle(),
427
413
  renderContent: () => this.renderContent(),
428
414
  onHeaderKeyDown: () => this.#onHeaderKeyDown,
@@ -527,14 +513,19 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
527
513
  }
528
514
 
529
515
  #canShowAskAI(): boolean {
530
- if (this.#isAIAssistanceContext) {
516
+ if (this.#isAIAssistanceContext || !this.hasAskAiSupport()) {
531
517
  return false;
532
518
  }
533
519
 
534
- const aiAvailable = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue !==
535
- Root.Runtime.GenAiEnterprisePolicyValue.DISABLE &&
536
- this.#askAiEnabled && Root.Runtime.hostConfig.aidaAvailability?.enabled === true;
520
+ // Check if the Insights AI feature enabled within Chrome for the active user.
521
+ const {devToolsAiAssistancePerformanceAgent} = Root.Runtime.hostConfig;
522
+ const askAiEnabled = Boolean(devToolsAiAssistancePerformanceAgent?.enabled);
523
+ if (!askAiEnabled) {
524
+ return false;
525
+ }
537
526
 
538
- return aiAvailable && this.hasAskAiSupport();
527
+ const {aidaAvailability} = Root.Runtime.hostConfig;
528
+ return aidaAvailability?.enterprisePolicyValue !== Root.Runtime.GenAiEnterprisePolicyValue.DISABLE &&
529
+ aidaAvailability?.enabled === true;
539
530
  }
540
531
  }