chrome-devtools-frontend 1.0.1521880 → 1.0.1522585

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 (59) hide show
  1. package/docs/ui_engineering.md +76 -0
  2. package/front_end/core/host/GdpClient.ts +116 -66
  3. package/front_end/core/root/Runtime.ts +1 -0
  4. package/front_end/core/sdk/EnhancedTracesParser.ts +13 -6
  5. package/front_end/entrypoints/inspector_main/InspectorMain.ts +82 -32
  6. package/front_end/entrypoints/inspector_main/inspector_main-meta.ts +1 -1
  7. package/front_end/entrypoints/main/MainImpl.ts +7 -1
  8. package/front_end/generated/Deprecation.ts +4 -4
  9. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +10 -6
  10. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +559 -0
  11. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +42 -4
  12. package/front_end/models/badges/UserBadges.ts +14 -16
  13. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  14. package/front_end/models/trace/LanternComputationData.ts +1 -0
  15. package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +10 -0
  16. package/front_end/models/trace/insights/DocumentLatency.ts +9 -10
  17. package/front_end/models/trace/types/TraceEvents.ts +6 -5
  18. package/front_end/panels/ai_assistance/components/UserActionRow.ts +1 -2
  19. package/front_end/panels/application/IndexedDBViews.ts +1 -0
  20. package/front_end/panels/application/ReportingApiTreeElement.ts +1 -2
  21. package/front_end/panels/application/ReportingApiView.ts +18 -20
  22. package/front_end/panels/application/ServiceWorkerCacheViews.ts +3 -0
  23. package/front_end/panels/application/components/EndpointsGrid.ts +51 -59
  24. package/front_end/panels/application/components/ReportsGrid.ts +86 -107
  25. package/front_end/panels/application/components/StorageMetadataView.ts +30 -4
  26. package/front_end/panels/application/components/endpointsGrid.css +30 -0
  27. package/front_end/panels/application/components/reportsGrid.css +34 -0
  28. package/front_end/panels/application/components/storageMetadataView.css +9 -0
  29. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +19 -27
  30. package/front_end/panels/common/BadgeNotification.ts +10 -3
  31. package/front_end/panels/network/NetworkPanel.ts +1 -1
  32. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +31 -32
  33. package/front_end/panels/search/SearchResultsPane.ts +14 -13
  34. package/front_end/panels/search/SearchView.ts +3 -20
  35. package/front_end/panels/settings/components/SyncSection.ts +8 -6
  36. package/front_end/panels/sources/SearchSourcesView.ts +1 -1
  37. package/front_end/panels/timeline/TimelineFlameChartView.ts +17 -0
  38. package/front_end/panels/timeline/TimelinePanel.ts +5 -0
  39. package/front_end/panels/timeline/TimelineUIUtils.ts +12 -3
  40. package/front_end/panels/timeline/components/ExportTraceOptions.ts +21 -9
  41. package/front_end/panels/timeline/timelineDetailsView.css +5 -0
  42. package/front_end/panels/whats_new/ReleaseNoteText.ts +15 -11
  43. package/front_end/panels/whats_new/resources/WNDT.md +9 -6
  44. package/front_end/third_party/chromium/README.chromium +1 -1
  45. package/front_end/third_party/diff/README.chromium +0 -1
  46. package/front_end/ui/components/tooltips/Tooltip.ts +13 -4
  47. package/front_end/ui/legacy/Treeoutline.ts +6 -9
  48. package/front_end/ui/legacy/UIUtils.ts +4 -17
  49. package/front_end/ui/legacy/Widget.ts +0 -5
  50. package/front_end/ui/legacy/XElement.ts +0 -33
  51. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -3
  52. package/front_end/ui/legacy/components/perf_ui/FilmStripView.ts +38 -21
  53. package/front_end/ui/legacy/components/perf_ui/filmStripView.css +29 -0
  54. package/front_end/ui/legacy/components/source_frame/XMLView.ts +3 -2
  55. package/front_end/ui/legacy/legacy.ts +0 -2
  56. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  57. package/package.json +1 -1
  58. package/front_end/panels/application/components/reportingApiGrid.css +0 -31
  59. package/front_end/ui/legacy/XWidget.ts +0 -133
@@ -4,7 +4,6 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as Host from '../../core/host/host.js';
7
- import * as Root from '../../core/root/root.js';
8
7
 
9
8
  import {AiExplorerBadge} from './AiExplorerBadge.js';
10
9
  import type {Badge, BadgeAction, BadgeActionEvents, BadgeContext, TriggerOptions} from './Badge.js';
@@ -50,8 +49,7 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
50
49
  super();
51
50
 
52
51
  this.#receiveBadgesSetting = Common.Settings.Settings.instance().moduleSetting('receive-gdp-badges');
53
- if (Host.GdpClient.getGdpProfilesEnterprisePolicy() ===
54
- Root.Runtime.GdpProfilesEnterprisePolicyValue.ENABLED_WITHOUT_BADGES) {
52
+ if (!Host.GdpClient.isBadgesEnabled()) {
55
53
  this.#receiveBadgesSetting.set(false);
56
54
  }
57
55
  this.#receiveBadgesSetting.addChangeListener(this.#reconcileBadges, this);
@@ -106,10 +104,10 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
106
104
  if (!badge.isStarterBadge) {
107
105
  shouldAwardBadge = true;
108
106
  } else {
109
- const gdpProfile = await Host.GdpClient.GdpClient.instance().getProfile();
107
+ const getProfileResponse = await Host.GdpClient.GdpClient.instance().getProfile();
110
108
  const receiveBadgesSettingEnabled = Boolean(this.#receiveBadgesSetting.get());
111
109
  // If there is a GDP profile and the user has enabled receiving badges, we award the starter badge as well.
112
- if (gdpProfile && receiveBadgesSettingEnabled && !this.#isStarterBadgeDismissed() &&
110
+ if (getProfileResponse?.profile && receiveBadgesSettingEnabled && !this.#isStarterBadgeDismissed() &&
113
111
  !this.#isStarterBadgeSnoozed()) {
114
112
  shouldAwardBadge = true;
115
113
  }
@@ -157,27 +155,28 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
157
155
  return;
158
156
  }
159
157
 
160
- if (!Host.GdpClient.isGdpProfilesAvailable() ||
161
- Host.GdpClient.getGdpProfilesEnterprisePolicy() !== Root.Runtime.GdpProfilesEnterprisePolicyValue.ENABLED) {
158
+ if (!Host.GdpClient.isGdpProfilesAvailable() || !Host.GdpClient.isBadgesEnabled()) {
162
159
  this.#deactivateAllBadges();
163
160
  return;
164
161
  }
165
162
 
166
- const gdpProfile = await Host.GdpClient.GdpClient.instance().getProfile();
167
- let isEligibleToCreateProfile = Boolean(gdpProfile);
168
- if (!gdpProfile) {
169
- isEligibleToCreateProfile = await Host.GdpClient.GdpClient.instance().isEligibleToCreateProfile();
163
+ const getProfileResponse = await Host.GdpClient.GdpClient.instance().getProfile();
164
+ if (!getProfileResponse) {
165
+ this.#deactivateAllBadges();
166
+ return;
170
167
  }
171
168
 
169
+ const hasGdpProfile = Boolean(getProfileResponse.profile);
170
+ const isEligibleToCreateProfile = getProfileResponse.isEligible;
172
171
  // User does not have a GDP profile & not eligible to create one.
173
172
  // So, we don't activate any badges for them.
174
- if (!gdpProfile && !isEligibleToCreateProfile) {
173
+ if (!hasGdpProfile && !isEligibleToCreateProfile) {
175
174
  this.#deactivateAllBadges();
176
175
  return;
177
176
  }
178
177
 
179
178
  let awardedBadgeNames: Set<string>|null = null;
180
- if (gdpProfile) {
179
+ if (hasGdpProfile) {
181
180
  awardedBadgeNames = await Host.GdpClient.GdpClient.instance().getAwardedBadgeNames(
182
181
  {names: this.#allBadges.map(badge => badge.name)});
183
182
  // This is a conservative approach. We bail out if `awardedBadgeNames` is null
@@ -202,9 +201,8 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
202
201
  }
203
202
 
204
203
  const shouldActivateStarterBadge = badge.isStarterBadge && isEligibleToCreateProfile &&
205
- !this.#isStarterBadgeDismissed() && !this.#isStarterBadgeSnoozed();
206
- const shouldActivateActivityBasedBadge =
207
- !badge.isStarterBadge && Boolean(gdpProfile) && receiveBadgesSettingEnabled;
204
+ Host.GdpClient.isStarterBadgeEnabled() && !this.#isStarterBadgeDismissed() && !this.#isStarterBadgeSnoozed();
205
+ const shouldActivateActivityBasedBadge = !badge.isStarterBadge && hasGdpProfile && receiveBadgesSettingEnabled;
208
206
  if (shouldActivateStarterBadge || shouldActivateActivityBasedBadge) {
209
207
  badge.activate();
210
208
  } else {
@@ -7982,7 +7982,7 @@ export const NativeFunctions = [
7982
7982
  },
7983
7983
  {
7984
7984
  name: "constant",
7985
- signatures: [["tensor"],["desc","buffer"]]
7985
+ signatures: [["tensor"],["desc","buffer"],["type","value"]]
7986
7986
  },
7987
7987
  {
7988
7988
  name: "argMin",
@@ -192,6 +192,7 @@ function createLanternRequest(
192
192
  priority: request.args.data.priority,
193
193
  frameId: request.args.data.frame,
194
194
  fromWorker,
195
+ serverResponseTime: request.args.data.lrServerResponseTime ?? undefined,
195
196
  // Set later.
196
197
  redirects: undefined,
197
198
  redirectSource: undefined,
@@ -295,6 +295,7 @@ export async function finalize(): Promise<void> {
295
295
  *
296
296
  * See `_updateTimingsForLightrider` in Lighthouse for more detail.
297
297
  */
298
+ let lrServerResponseTime;
298
299
  if (isLightrider && request.receiveResponse?.args.data.headers) {
299
300
  timing = {
300
301
  requestTime: Helpers.Timing.microToSeconds(request.sendRequests.at(0)?.ts ?? 0 as Types.Timing.Micro),
@@ -330,6 +331,14 @@ export async function finalize(): Promise<void> {
330
331
  timing.connectEnd = TCPMs as Types.Timing.Milli;
331
332
  timing.sslEnd = TCPMs as Types.Timing.Milli;
332
333
  }
334
+
335
+ // Lightrider does not have any equivalent for `sendEnd` timing values. The
336
+ // closest we can get to the server response time is from a header that
337
+ // Lightrider sets.
338
+ const ResponseMsHeader = request.receiveResponse.args.data.headers.find(h => h.name === 'X-ResponseMs');
339
+ if (ResponseMsHeader) {
340
+ lrServerResponseTime = Math.max(0, parseInt(ResponseMsHeader.value, 10)) as Types.Timing.Milli;
341
+ }
333
342
  }
334
343
 
335
344
  // TODO: consider allowing chrome / about.
@@ -526,6 +535,7 @@ export async function finalize(): Promise<void> {
526
535
  initiator: finalSendRequest.args.data.initiator,
527
536
  stackTrace: finalSendRequest.args.data.stackTrace,
528
537
  timing,
538
+ lrServerResponseTime,
529
539
  url,
530
540
  failed: request.resourceFinish?.args.data.didFail ?? false,
531
541
  finished: Boolean(request.resourceFinish),
@@ -95,15 +95,14 @@ export type DocumentLatencyInsightModel = InsightModel<typeof UIStrings, {
95
95
  },
96
96
  }>;
97
97
 
98
- function getServerResponseTime(
99
- request: Types.Events.SyntheticNetworkRequest, context: InsightSetContext): Types.Timing.Milli|null {
100
- // Prefer the value as given by the Lantern provider.
101
- // For PSI, Lighthouse uses this to set a better value for the server response
102
- // time. For technical reasons, in Lightrider we do not have `sendEnd` timing
103
- // values. See Lighthouse's `asLanternNetworkRequest` function for more.
104
- const lanternRequest = context.navigation && context.lantern?.requests.find(r => r.rawRequest === request);
105
- if (lanternRequest?.serverResponseTime !== undefined) {
106
- return lanternRequest.serverResponseTime as Types.Timing.Milli;
98
+ function getServerResponseTime(request: Types.Events.SyntheticNetworkRequest): Types.Timing.Milli|null {
99
+ // For technical reasons, Lightrider does not have `sendEnd` timing values. The
100
+ // closest we can get to the server response time is from a header that Lightrider
101
+ // sets.
102
+ // @ts-expect-error
103
+ const isLightrider = globalThis.isLightrider;
104
+ if (isLightrider) {
105
+ return request.args.data.lrServerResponseTime ?? null;
107
106
  }
108
107
 
109
108
  const timing = request.args.data.timing;
@@ -202,7 +201,7 @@ export function generateInsight(
202
201
  return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});
203
202
  }
204
203
 
205
- const serverResponseTime = getServerResponseTime(documentRequest, context);
204
+ const serverResponseTime = getServerResponseTime(documentRequest);
206
205
  if (serverResponseTime === null) {
207
206
  throw new Error('missing document request timing');
208
207
  }
@@ -124,10 +124,10 @@ export interface TraceFrame {
124
124
  processId: ProcessID;
125
125
  url: string;
126
126
  parent?: string;
127
- // Added to Chromium in April 2024:
127
+ // Added to Chrome in April 2024:
128
128
  // crrev.com/c/5424783
129
129
  isOutermostMainFrame?: boolean;
130
- // Added to Chromium in June 2024:
130
+ // Added to Chrome in June 2024:
131
131
  // crrev.com/c/5595033
132
132
  isInPrimaryMainFrame?: boolean;
133
133
  }
@@ -415,6 +415,8 @@ export interface SyntheticNetworkRequest extends Complete, SyntheticBased<Phase.
415
415
  initiator?: Initiator,
416
416
  requestMethod?: string,
417
417
  timing?: ResourceReceiveResponseTimingData,
418
+ /** Server response time according to Lightrider. */
419
+ lrServerResponseTime?: Milli,
418
420
  },
419
421
  };
420
422
  cat: 'loading';
@@ -549,7 +551,6 @@ export function isAuctionWorkletDoneWithProcess(event: Event): event is AuctionW
549
551
  * consuming screenshot events from the ScreenshotHandler, you must make sure
550
552
  * to have your code deal with the two different formats.
551
553
  */
552
- // These are nullable because in January 2025 a CL in Chromium
553
554
  export interface LegacyScreenshot extends Event {
554
555
  /**
555
556
  * @deprecated This value is incorrect. Use ScreenshotHandler.getPresentationTimestamp()
@@ -745,7 +746,7 @@ export interface LargestContentfulPaintCandidate extends Mark {
745
746
  nodeId: Protocol.DOM.BackendNodeId,
746
747
  loadingAttr: string,
747
748
  type?: string,
748
- // Landed in Chromium M140: crrev.com/c/6702010
749
+ // Landed in Chrome M140: crrev.com/c/6702010
749
750
  nodeName?: string,
750
751
  },
751
752
  };
@@ -990,7 +991,7 @@ export interface SyntheticLayoutShift extends Omit<LayoutShift, 'name'>, Synthet
990
991
  export const NO_NAVIGATION = 'NO_NAVIGATION';
991
992
 
992
993
  /**
993
- * This maybe be a navigation id string from Chromium, or `NO_NAVIGATION`, which represents the
994
+ * This maybe be a navigation id string from Chrome, or `NO_NAVIGATION`, which represents the
994
995
  * portion of the trace for which we don't have any navigation event for (as it happeneded prior
995
996
  * to the trace start).
996
997
  */
@@ -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 rulesdir/no-lit-render-outside-of-view */
5
4
 
6
5
  import * as Common from '../../../core/common/common.js';
7
6
  import * as Host from '../../../core/host/host.js';
@@ -273,7 +272,7 @@ export const DEFAULT_VIEW = (input: UserActionRowViewInput, output: ViewOutput,
273
272
  </div>
274
273
  </form>
275
274
  ` : Lit.nothing}
276
- `, target, {host: target});
275
+ `, target);
277
276
  // clang-format on
278
277
  };
279
278
 
@@ -146,6 +146,7 @@ export class IDBDatabaseView extends ApplicationComponents.StorageMetadataView.S
146
146
  super();
147
147
 
148
148
  this.model = model;
149
+ this.setShowOnlyBucket(false);
149
150
  if (database) {
150
151
  this.update(database);
151
152
  }
@@ -8,7 +8,6 @@ import type * as Platform from '../../core/platform/platform.js';
8
8
  import * as IconButton from '../../ui/components/icon_button/icon_button.js';
9
9
 
10
10
  import {ApplicationPanelTreeElement} from './ApplicationPanelTreeElement.js';
11
- import * as ApplicationComponents from './components/components.js';
12
11
  import {ReportingApiView} from './ReportingApiView.js';
13
12
  import type {ResourcesPanel} from './ResourcesPanel.js';
14
13
 
@@ -37,7 +36,7 @@ export class ReportingApiTreeElement extends ApplicationPanelTreeElement {
37
36
  override onselect(selectedByUser?: boolean): boolean {
38
37
  super.onselect(selectedByUser);
39
38
  if (!this.view) {
40
- this.view = new ReportingApiView(new ApplicationComponents.EndpointsGrid.EndpointsGrid());
39
+ this.view = new ReportingApiView();
41
40
  }
42
41
  this.showView(this.view);
43
42
  Host.userMetrics.panelShown('reporting-api');
@@ -50,11 +50,10 @@ const REPORTING_API_EXPLANATION_URL =
50
50
  interface ViewInput {
51
51
  hasReports: boolean;
52
52
  hasEndpoints: boolean;
53
- // TODO (crbug.com/407940329): port EndpointsGrid to a UI Widget and instantiate it in the view
54
- endpointsGrid: ApplicationComponents.EndpointsGrid.EndpointsGrid;
55
- // TODO (crbug.com/407940381): port ReportsGrid to a UI Widget and instantiate it in the view
56
- reportsGrid: ApplicationComponents.ReportsGrid.ReportsGrid;
53
+ endpoints: Map<string, Protocol.Network.ReportingApiEndpoint[]>;
54
+ reports: Protocol.Network.ReportingApiReport[];
57
55
  focusedReport?: Protocol.Network.ReportingApiReport;
56
+ onReportSelected: (id: string) => void;
58
57
  }
59
58
 
60
59
  type View = (input: ViewInput, output: object, target: HTMLElement) => void;
@@ -68,7 +67,9 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
68
67
  ${input.hasReports ? html`
69
68
  <devtools-split-view slot="main" sidebar-position="second" sidebar-initial-size="150">
70
69
  <div slot="main">
71
- ${input.reportsGrid}
70
+ <devtools-widget .widgetConfig=${widgetConfig(ApplicationComponents.ReportsGrid.ReportsGrid, {
71
+ reports: input.reports, onReportSelected: input.onReportSelected,
72
+ })}></devtools-widget>
72
73
  </div>
73
74
  <div slot="sidebar" class="vbox" jslog=${VisualLogging.pane('preview').track({resize: true})}>
74
75
  ${input.focusedReport ? html`
@@ -85,11 +86,15 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
85
86
  </devtools-split-view>
86
87
  ` : html`
87
88
  <div slot="main">
88
- ${input.reportsGrid}
89
+ <devtools-widget .widgetConfig=${widgetConfig(ApplicationComponents.ReportsGrid.ReportsGrid, {
90
+ reports: input.reports, onReportSelected: input.onReportSelected,
91
+ })}></devtools-widget>
89
92
  </div>
90
93
  `}
91
94
  <div slot="sidebar">
92
- ${input.endpointsGrid}
95
+ <devtools-widget .widgetConfig=${widgetConfig(ApplicationComponents.EndpointsGrid.EndpointsGrid, {
96
+ endpoints: input.endpoints,
97
+ })}></devtools-widget>
93
98
  </div>
94
99
  </devtools-split-view>
95
100
  `, target);
@@ -109,20 +114,16 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
109
114
 
110
115
  export class ReportingApiView extends UI.Widget.VBox implements
111
116
  SDK.TargetManager.SDKModelObserver<SDK.NetworkManager.NetworkManager> {
112
- readonly #endpointsGrid: ApplicationComponents.EndpointsGrid.EndpointsGrid;
113
117
  #endpoints: Map<string, Protocol.Network.ReportingApiEndpoint[]>;
114
118
  #view: View;
115
119
  #networkManager?: SDK.NetworkManager.NetworkManager;
116
- #reportsGrid = new ApplicationComponents.ReportsGrid.ReportsGrid();
117
120
  #reports: Protocol.Network.ReportingApiReport[] = [];
118
121
  #focusedReport?: Protocol.Network.ReportingApiReport;
119
122
 
120
- constructor(endpointsGrid: ApplicationComponents.EndpointsGrid.EndpointsGrid, view = DEFAULT_VIEW) {
123
+ constructor(view = DEFAULT_VIEW) {
121
124
  super();
122
125
  this.#view = view;
123
- this.#endpointsGrid = endpointsGrid;
124
126
  this.#endpoints = new Map();
125
- this.#reportsGrid.addEventListener('select', this.#onFocus.bind(this));
126
127
  SDK.TargetManager.TargetManager.instance().observeModels(SDK.NetworkManager.NetworkManager, this);
127
128
  this.requestUpdate();
128
129
  }
@@ -158,35 +159,32 @@ export class ReportingApiView extends UI.Widget.VBox implements
158
159
  const viewInput = {
159
160
  hasReports: this.#reports.length > 0,
160
161
  hasEndpoints: this.#endpoints.size > 0,
161
- endpointsGrid: this.#endpointsGrid,
162
- reportsGrid: this.#reportsGrid,
162
+ endpoints: this.#endpoints,
163
+ reports: this.#reports,
163
164
  focusedReport: this.#focusedReport,
165
+ onReportSelected: this.#onReportSelected.bind(this),
164
166
  };
165
167
  this.#view(viewInput, {}, this.element);
166
168
  }
167
169
 
168
170
  #onEndpointsChangedForOrigin({data}: {data: Protocol.Network.ReportingApiEndpointsChangedForOriginEvent}): void {
169
171
  this.#endpoints.set(data.origin, data.endpoints);
170
- this.#endpointsGrid.data = {endpoints: this.#endpoints};
171
172
  this.requestUpdate();
172
173
  }
173
174
 
174
175
  #onReportAdded({data: report}: {data: Protocol.Network.ReportingApiReport}): void {
175
176
  this.#reports.push(report);
176
- this.#reportsGrid.data = {reports: this.#reports};
177
177
  this.requestUpdate();
178
178
  }
179
179
 
180
180
  #onReportUpdated({data: report}: {data: Protocol.Network.ReportingApiReport}): void {
181
181
  const index = this.#reports.findIndex(oldReport => oldReport.id === report.id);
182
182
  this.#reports[index] = report;
183
- this.#reportsGrid.data = {reports: this.#reports};
184
183
  this.requestUpdate();
185
184
  }
186
185
 
187
- async #onFocus(event: Event): Promise<void> {
188
- const selectEvent = event as CustomEvent<string>;
189
- const report = this.#reports.find(report => report.id === selectEvent.detail);
186
+ #onReportSelected(id: string): void {
187
+ const report = this.#reports.find(report => report.id === id);
190
188
  if (report) {
191
189
  this.#focusedReport = report;
192
190
  this.requestUpdate();
@@ -134,6 +134,9 @@ export class ServiceWorkerCacheView extends UI.View.SimpleView {
134
134
  const bucketInfo = this.model.target()
135
135
  .model(SDK.StorageBucketsModel.StorageBucketsModel)
136
136
  ?.getBucketByName(cache.storageBucket.storageKey, cache.storageBucket.name);
137
+
138
+ this.metadataView.setShowOnlyBucket(false);
139
+
137
140
  if (bucketInfo) {
138
141
  this.metadataView.setStorageBucket(bucketInfo);
139
142
  } else if (cache.storageKey) {
@@ -1,7 +1,6 @@
1
1
  // Copyright 2021 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 rulesdir/no-lit-render-outside-of-view */
5
4
 
6
5
  import '../../../ui/legacy/components/data_grid/data_grid.js';
7
6
 
@@ -11,7 +10,7 @@ import * as UI from '../../../ui/legacy/legacy.js';
11
10
  import * as Lit from '../../../ui/lit/lit.js';
12
11
  import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
13
12
 
14
- import reportingApiGridStyles from './reportingApiGrid.css.js';
13
+ import endpointsGridStyles from './endpointsGrid.css.js';
15
14
 
16
15
  const UIStrings = {
17
16
  /**
@@ -23,76 +22,69 @@ const UIStrings = {
23
22
  * @description Placeholder text when there are no Reporting API endpoints.
24
23
  *(https://developers.google.com/web/updates/2018/09/reportingapi#tldr)
25
24
  */
26
- endpointsDescription: 'Here you will find the list of endpoints that receive the reports'
25
+ endpointsDescription: 'Here you will find the list of endpoints that receive the reports',
27
26
  } as const;
28
27
  const str_ = i18n.i18n.registerUIStrings('panels/application/components/EndpointsGrid.ts', UIStrings);
29
28
  export const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
30
29
 
31
30
  const {render, html} = Lit;
32
31
 
33
- export interface EndpointsGridData {
32
+ export interface ViewInput {
34
33
  endpoints: Map<string, Protocol.Network.ReportingApiEndpoint[]>;
35
34
  }
36
35
 
37
- export class EndpointsGrid extends HTMLElement {
36
+ export const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLElement): void => {
37
+ // clang-format off
38
+ render(html`
39
+ <style>${endpointsGridStyles}</style>
40
+ <style>${UI.inspectorCommonStyles}</style>
41
+ <div class="endpoints-container" jslog=${VisualLogging.section('endpoints')}>
42
+ <div class="endpoints-header">${i18n.i18n.lockedString('Endpoints')}</div>
43
+ ${input.endpoints.size > 0 ? html`
44
+ <devtools-data-grid striped>
45
+ <table>
46
+ <tr>
47
+ <th id="origin" weight="30">${i18n.i18n.lockedString('Origin')}</th>
48
+ <th id="name" weight="20">${i18n.i18n.lockedString('Name')}</th>
49
+ <th id="url" weight="30">${i18n.i18n.lockedString('URL')}</th>
50
+ </tr>
51
+ ${Array.from(input.endpoints).map(([origin, endpointArray]) =>
52
+ endpointArray.map(endpoint => html`<tr>
53
+ <td>${origin}</td>
54
+ <td>${endpoint.groupName}</td>
55
+ <td>${endpoint.url}</td>
56
+ </tr>`))
57
+ .flat()}
58
+ </table>
59
+ </devtools-data-grid>
60
+ ` : html`
61
+ <div class="empty-state">
62
+ <span class="empty-state-header">${i18nString(UIStrings.noEndpointsToDisplay)}</span>
63
+ <span class="empty-state-description">${i18nString(UIStrings.endpointsDescription)}</span>
64
+ </div>
65
+ `}
66
+ </div>
67
+ `, target);
68
+ // clang-format on
69
+ };
38
70
 
39
- readonly #shadow = this.attachShadow({mode: 'open'});
40
- #endpoints = new Map<string, Protocol.Network.ReportingApiEndpoint[]>();
71
+ type View = typeof DEFAULT_VIEW;
41
72
 
42
- connectedCallback(): void {
43
- this.#render();
44
- }
45
-
46
- set data(data: EndpointsGridData) {
47
- this.#endpoints = data.endpoints;
48
- this.#render();
49
- }
73
+ export class EndpointsGrid extends UI.Widget.Widget {
74
+ endpoints = new Map<string, Protocol.Network.ReportingApiEndpoint[]>();
75
+ #view: View;
50
76
 
51
- get data(): EndpointsGridData {
52
- return {endpoints: this.#endpoints};
77
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
78
+ super(element);
79
+ this.#view = view;
80
+ this.requestUpdate();
53
81
  }
54
82
 
55
- #render(): void {
56
- // Disabled until https://crbug.com/1079231 is fixed.
57
- // clang-format off
58
- render(html`
59
- <style>${reportingApiGridStyles}</style>
60
- <style>${UI.inspectorCommonStyles}</style>
61
- <div class="reporting-container" jslog=${VisualLogging.section('endpoints')}>
62
- <div class="reporting-header">${i18n.i18n.lockedString('Endpoints')}</div>
63
- ${this.#endpoints.size > 0 ? html`
64
- <devtools-data-grid striped>
65
- <table>
66
- <tr>
67
- <th id="origin" weight="30">${i18n.i18n.lockedString('Origin')}</th>
68
- <th id="name" weight="20">${i18n.i18n.lockedString('Name')}</th>
69
- <th id="url" weight="30">${i18n.i18n.lockedString('URL')}</th>
70
- </tr>
71
- ${Array.from(this.#endpoints).map(([origin, endpointArray]) =>
72
- endpointArray.map(endpoint => html`<tr>
73
- <td>${origin}</td>
74
- <td>${endpoint.groupName}</td>
75
- <td>${endpoint.url}</td>
76
- </tr>`))
77
- .flat()}
78
- </table>
79
- </devtools-data-grid>
80
- ` : html`
81
- <div class="empty-state">
82
- <span class="empty-state-header">${i18nString(UIStrings.noEndpointsToDisplay)}</span>
83
- <span class="empty-state-description">${i18nString(UIStrings.endpointsDescription)}</span>
84
- </div>
85
- `}
86
- </div>
87
- `, this.#shadow, {host: this});
88
- // clang-format on
89
- }
90
- }
91
-
92
- customElements.define('devtools-resources-endpoints-grid', EndpointsGrid);
93
-
94
- declare global {
95
- interface HTMLElementTagNameMap {
96
- 'devtools-resources-endpoints-grid': EndpointsGrid;
83
+ override performUpdate(): void {
84
+ this.#view(
85
+ {
86
+ endpoints: this.endpoints,
87
+ },
88
+ undefined, this.contentElement);
97
89
  }
98
90
  }