blob: 2bd2fcd4391332ad3fded8d55f0c7a4f98f8d668 [file] [log] [blame]
Rayan Kanso68904202019-02-21 14:16:251// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Tim van der Lippe877f04a2021-07-23 11:02:545import type * as Common from '../../core/common/common.js';
Tim van der Lippebb352e62021-04-01 17:57:286import * as i18n from '../../core/i18n/i18n.js';
Tim van der Lippeaa1ed7a2021-03-31 14:38:277import * as Platform from '../../core/platform/platform.js';
Tim van der Lippee00b92f2021-03-31 16:52:178import * as SDK from '../../core/sdk/sdk.js';
Kateryna Prokopenkofbb22472022-05-10 15:36:349import * as Protocol from '../../generated/protocol.js';
Tim van der Lippe959b6f02021-04-07 09:07:5910import * as Bindings from '../../models/bindings/bindings.js';
Tim van der Lippe8499fe22021-04-12 16:42:4711import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
Kateryna Prokopenkofbb22472022-05-10 15:36:3412// eslint-disable-next-line rulesdir/es_modules_import
13import emptyWidgetStyles from '../../ui/legacy/emptyWidget.css.js';
Tim van der Lippeaa61faf2021-04-07 15:32:0714import * as UI from '../../ui/legacy/legacy.js';
Kateryna Prokopenkofbb22472022-05-10 15:36:3415
16import backgroundServiceViewStyles from './backgroundServiceView.css.js';
Tim van der Lippe0efccf02020-02-12 15:15:3917
Jack Franklin3a802602022-07-13 08:39:4218import {Events, type BackgroundServiceModel} from './BackgroundServiceModel.js';
Paul Lewisddf58342020-01-15 14:18:0719
Simon Zünd697fb0b2021-03-01 10:12:4220const UIStrings = {
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0121 /**
22 *@description Text in Background Service View of the Application panel
23 */
24 backgroundFetch: 'Background Fetch',
25 /**
26 *@description Text in Background Service View of the Application panel
27 */
28 backgroundSync: 'Background Sync',
29 /**
30 *@description Text in Background Service View of the Application panel
31 */
32 pushMessaging: 'Push Messaging',
33 /**
34 *@description Text in Background Service View of the Application panel
35 */
36 notifications: 'Notifications',
37 /**
38 *@description Text in Background Service View of the Application panel
39 */
40 paymentHandler: 'Payment Handler',
41 /**
42 *@description Text in the Periodic Background Service View of the Application panel
43 */
44 periodicBackgroundSync: 'Periodic Background Sync',
45 /**
46 *@description Text to clear content
47 */
48 clear: 'Clear',
49 /**
50 *@description Tooltip text that appears when hovering over the largeicon download button in the Background Service View of the Application panel
51 */
52 saveEvents: 'Save events',
53 /**
54 *@description Text in Background Service View of the Application panel
55 */
56 showEventsFromOtherDomains: 'Show events from other domains',
57 /**
Kateryna Prokopenko35d6bf62022-11-30 13:56:1958 *@description Text of a checkbox to show events for other dtorage keys
59 */
60 showEventsForOtherStorageKeys: 'Show events from other storage partitions',
61 /**
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0162 *@description Title of an action under the Background Services category that can be invoked through the Command Menu
63 */
64 stopRecordingEvents: 'Stop recording events',
65 /**
66 *@description Title of an action under the Background Services category that can be invoked through the Command Menu
67 */
68 startRecordingEvents: 'Start recording events',
69 /**
70 *@description Text for timestamps of items
71 */
72 timestamp: 'Timestamp',
73 /**
74 *@description Text that refers to some events
75 */
76 event: 'Event',
77 /**
78 *@description Text for the origin of something
79 */
80 origin: 'Origin',
81 /**
Kateryna Prokopenko35d6bf62022-11-30 13:56:1982 *@description Text for the storage key of something
83 */
84 storageKey: 'Storage Key',
85 /**
Sigurd Schneiderbee57a82021-02-23 12:58:2486 *@description Text in Background Service View of the Application panel. The Scope is a URL associated with the Service Worker, which limits which pages/sites the Service Worker operates on.
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0187 */
Sigurd Schneiderbee57a82021-02-23 12:58:2488 swScope: 'Service Worker Scope',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0189 /**
90 *@description Text in Background Service View of the Application panel
91 */
92 instanceId: 'Instance ID',
93 /**
94 *@description Text in Application Panel Sidebar of the Application panel
95 */
96 backgroundServices: 'Background Services',
97 /**
98 *@description Text that is usually a hyperlink to more documentation
99 */
100 learnMore: 'Learn more',
101 /**
102 *@description Text in Background Service View of the Application panel
103 */
104 selectAnEntryToViewMetadata: 'Select an entry to view metadata',
105 /**
106 *@description Text in Background Service View of the Application panel
107 *@example {Background Fetch} PH1
108 */
109 recordingSActivity: 'Recording {PH1} activity...',
110 /**
111 *@description Inform users that DevTools are recording/waiting for events in the Periodic Background Sync tool of the Application panel
112 *@example {Background Fetch} PH1
113 */
114 devtoolsWillRecordAllSActivity: 'DevTools will record all {PH1} activity for up to 3 days, even when closed.',
115 /**
116 *@description Text in Background Service View of the Application panel
117 *@example {record} PH1
118 *@example {Ctrl + R} PH2
119 */
120 clickTheRecordButtonSOrHitSTo: 'Click the record button {PH1} or hit {PH2} to start recording.',
121 /**
122 *@description Text to show an item is empty
123 */
124 empty: 'empty',
125 /**
126 *@description Text in Background Service View of the Application panel
127 */
128 noMetadataForThisEvent: 'No metadata for this event',
129};
Tim van der Lippec59708f2021-03-31 15:07:19130const str_ = i18n.i18n.registerUIStrings('panels/application/BackgroundServiceView.ts', UIStrings);
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01131const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Tim van der Lippe0efccf02020-02-12 15:15:39132export class BackgroundServiceView extends UI.Widget.VBox {
Jan Schefflercd49c322021-08-13 18:56:33133 private readonly serviceName: Protocol.BackgroundService.ServiceName;
134 private readonly model: BackgroundServiceModel;
135 private readonly serviceWorkerManager: SDK.ServiceWorkerManager.ServiceWorkerManager|null;
136 private readonly securityOriginManager: SDK.SecurityOriginManager.SecurityOriginManager;
Kateryna Prokopenko35d6bf62022-11-30 13:56:19137 private readonly storageKeyManager: SDK.StorageKeyManager.StorageKeyManager;
Jan Schefflercd49c322021-08-13 18:56:33138 private recordAction: UI.ActionRegistration.Action;
139 private recordButton!: UI.Toolbar.ToolbarToggle;
140 private originCheckbox!: UI.Toolbar.ToolbarCheckbox;
Kateryna Prokopenko35d6bf62022-11-30 13:56:19141 private storageKeyCheckbox!: UI.Toolbar.ToolbarCheckbox;
Jan Schefflercd49c322021-08-13 18:56:33142 private saveButton!: UI.Toolbar.ToolbarButton;
143 private readonly toolbar: UI.Toolbar.Toolbar;
144 private readonly splitWidget: UI.SplitWidget.SplitWidget;
145 private readonly dataGrid: DataGrid.DataGrid.DataGridImpl<EventData>;
146 private readonly previewPanel: UI.Widget.VBox;
147 private selectedEventNode: EventDataNode|null;
148 private preview: UI.Widget.Widget|null;
Jan Scheffler1026b9d2021-02-26 17:12:00149
150 static getUIString(serviceName: string): string {
Rayan Kansoc0bfdd82019-04-24 12:32:22151 switch (serviceName) {
152 case Protocol.BackgroundService.ServiceName.BackgroundFetch:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01153 return i18nString(UIStrings.backgroundFetch);
Rayan Kansoc0bfdd82019-04-24 12:32:22154 case Protocol.BackgroundService.ServiceName.BackgroundSync:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01155 return i18nString(UIStrings.backgroundSync);
Fergus Dall0be4f4c2019-05-21 00:01:29156 case Protocol.BackgroundService.ServiceName.PushMessaging:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01157 return i18nString(UIStrings.pushMessaging);
Fergus Dall0be4f4c2019-05-21 00:01:29158 case Protocol.BackgroundService.ServiceName.Notifications:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01159 return i18nString(UIStrings.notifications);
Rayan Kanso8d7918a2019-07-03 21:03:38160 case Protocol.BackgroundService.ServiceName.PaymentHandler:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01161 return i18nString(UIStrings.paymentHandler);
Rayan Kanso54809672019-07-24 18:40:28162 case Protocol.BackgroundService.ServiceName.PeriodicBackgroundSync:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01163 return i18nString(UIStrings.periodicBackgroundSync);
Rayan Kansoc0bfdd82019-04-24 12:32:22164 default:
165 return '';
166 }
167 }
168
Jan Scheffler1026b9d2021-02-26 17:12:00169 constructor(serviceName: Protocol.BackgroundService.ServiceName, model: BackgroundServiceModel) {
Rayan Kanso68904202019-02-21 14:16:25170 super(true);
Rayan Kanso68904202019-02-21 14:16:25171
Jan Schefflercd49c322021-08-13 18:56:33172 this.serviceName = serviceName;
Rayan Kanso68904202019-02-21 14:16:25173
Jan Schefflercd49c322021-08-13 18:56:33174 this.model = model;
175 this.model.addEventListener(Events.RecordingStateChanged, this.onRecordingStateChanged, this);
176 this.model.addEventListener(Events.BackgroundServiceEventReceived, this.onEventReceived, this);
177 this.model.enable(this.serviceName);
Rayan Kanso8fe8ee22019-03-04 14:58:46178
Jan Schefflercd49c322021-08-13 18:56:33179 this.serviceWorkerManager = this.model.target().model(SDK.ServiceWorkerManager.ServiceWorkerManager);
Rayan Kansoaca06e72019-03-27 11:57:06180
Jan Schefflercd49c322021-08-13 18:56:33181 this.securityOriginManager = this.model.target().model(SDK.SecurityOriginManager.SecurityOriginManager) as
Jan Scheffler1026b9d2021-02-26 17:12:00182 SDK.SecurityOriginManager.SecurityOriginManager;
Jan Schefflercd49c322021-08-13 18:56:33183 if (!this.securityOriginManager) {
Alex Rudenko60a3e942020-11-02 12:27:57184 throw new Error('SecurityOriginManager instance is missing');
185 }
Jan Schefflercd49c322021-08-13 18:56:33186 this.securityOriginManager.addEventListener(
187 SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, () => this.onOriginChanged());
Kateryna Prokopenko35d6bf62022-11-30 13:56:19188
189 this.storageKeyManager =
190 this.model.target().model(SDK.StorageKeyManager.StorageKeyManager) as SDK.StorageKeyManager.StorageKeyManager;
191 if (!this.storageKeyManager) {
192 throw new Error('StorageKeyManager instance is missing');
193 }
194 this.storageKeyManager.addEventListener(
195 SDK.StorageKeyManager.Events.MainStorageKeyChanged, () => this.onStorageKeyChanged());
196
Jan Schefflercd49c322021-08-13 18:56:33197 this.recordAction =
Jan Scheffler1026b9d2021-02-26 17:12:00198 (UI.ActionRegistry.ActionRegistry.instance().action('background-service.toggle-recording') as
199 UI.ActionRegistration.Action);
Rayan Kanso8fe8ee22019-03-04 14:58:46200
Jan Schefflercd49c322021-08-13 18:56:33201 this.toolbar = new UI.Toolbar.Toolbar('background-service-toolbar', this.contentElement);
Tim van der Lippe2d9a95c2022-01-04 15:18:03202 void this.setupToolbar();
Rayan Kanso3252d5e2019-03-27 11:37:24203
Rayan Kansob451b4f2019-04-04 23:12:11204 /**
205 * This will contain the DataGrid for displaying events, and a panel at the bottom for showing
206 * extra metadata related to the selected event.
Rayan Kansob451b4f2019-04-04 23:12:11207 */
Jan Schefflercd49c322021-08-13 18:56:33208 this.splitWidget = new UI.SplitWidget.SplitWidget(/* isVertical= */ false, /* secondIsSidebar= */ true);
209 this.splitWidget.show(this.contentElement);
Rayan Kansob451b4f2019-04-04 23:12:11210
Jan Schefflercd49c322021-08-13 18:56:33211 this.dataGrid = this.createDataGrid();
Rayan Kansob451b4f2019-04-04 23:12:11212
Jan Schefflercd49c322021-08-13 18:56:33213 this.previewPanel = new UI.Widget.VBox();
Rayan Kansob451b4f2019-04-04 23:12:11214
Jan Schefflercd49c322021-08-13 18:56:33215 this.selectedEventNode = null;
Rayan Kansoc0bfdd82019-04-24 12:32:22216
Jan Schefflercd49c322021-08-13 18:56:33217 this.preview = null;
Rayan Kansob451b4f2019-04-04 23:12:11218
Jan Schefflercd49c322021-08-13 18:56:33219 this.splitWidget.setMainWidget(this.dataGrid.asWidget());
220 this.splitWidget.setSidebarWidget(this.previewPanel);
Rayan Kansob451b4f2019-04-04 23:12:11221
Jan Schefflercd49c322021-08-13 18:56:33222 this.showPreview(null);
Rayan Kanso68904202019-02-21 14:16:25223 }
224
Kateryna Prokopenko35d6bf62022-11-30 13:56:19225 getDataGrid(): DataGrid.DataGrid.DataGridImpl<EventData> {
226 return this.dataGrid;
227 }
228
Rayan Kanso68904202019-02-21 14:16:25229 /**
230 * Creates the toolbar UI element.
231 */
Jan Schefflercd49c322021-08-13 18:56:33232 private async setupToolbar(): Promise<void> {
233 this.recordButton = (UI.Toolbar.Toolbar.createActionButton(this.recordAction) as UI.Toolbar.ToolbarToggle);
234 this.toolbar.appendToolbarItem(this.recordButton);
Rayan Kanso68904202019-02-21 14:16:25235
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01236 const clearButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clear), 'largeicon-clear');
Jan Schefflercd49c322021-08-13 18:56:33237 clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => this.clearEvents());
238 this.toolbar.appendToolbarItem(clearButton);
Rayan Kanso68904202019-02-21 14:16:25239
Jan Schefflercd49c322021-08-13 18:56:33240 this.toolbar.appendSeparator();
Rayan Kanso68904202019-02-21 14:16:25241
Jan Schefflercd49c322021-08-13 18:56:33242 this.saveButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.saveEvents), 'largeicon-download');
243 this.saveButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, _event => {
Tim van der Lippe2d9a95c2022-01-04 15:18:03244 void this.saveToFile();
Tim van der Lippe37a35ff2020-03-03 13:49:02245 });
Jan Schefflercd49c322021-08-13 18:56:33246 this.saveButton.setEnabled(false);
247 this.toolbar.appendToolbarItem(this.saveButton);
Rayan Kansoc0bfdd82019-04-24 12:32:22248
Jan Schefflercd49c322021-08-13 18:56:33249 this.toolbar.appendSeparator();
Rayan Kansoc0bfdd82019-04-24 12:32:22250
Jan Schefflercd49c322021-08-13 18:56:33251 this.originCheckbox = new UI.Toolbar.ToolbarCheckbox(
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01252 i18nString(UIStrings.showEventsFromOtherDomains), i18nString(UIStrings.showEventsFromOtherDomains),
Jan Schefflercd49c322021-08-13 18:56:33253 () => this.refreshView());
254 this.toolbar.appendToolbarItem(this.originCheckbox);
Kateryna Prokopenko35d6bf62022-11-30 13:56:19255
256 this.storageKeyCheckbox = new UI.Toolbar.ToolbarCheckbox(
257 i18nString(UIStrings.showEventsForOtherStorageKeys), i18nString(UIStrings.showEventsForOtherStorageKeys),
258 () => this.refreshView());
259 this.toolbar.appendToolbarItem(this.storageKeyCheckbox);
Rayan Kanso68904202019-02-21 14:16:25260 }
Rayan Kanso8fe8ee22019-03-04 14:58:46261
262 /**
Rayan Kansob451b4f2019-04-04 23:12:11263 * Displays all available events in the grid.
264 */
Jan Schefflercd49c322021-08-13 18:56:33265 private refreshView(): void {
266 this.clearView();
267 const events = this.model.getEvents(this.serviceName).filter(event => this.acceptEvent(event));
Tim van der Lippe1d6e57a2019-09-30 11:55:34268 for (const event of events) {
Jan Schefflercd49c322021-08-13 18:56:33269 this.addEvent(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:34270 }
Rayan Kansob451b4f2019-04-04 23:12:11271 }
272
273 /**
274 * Clears the grid and panel.
275 */
Jan Schefflercd49c322021-08-13 18:56:33276 private clearView(): void {
277 this.selectedEventNode = null;
278 this.dataGrid.rootNode().removeChildren();
279 this.saveButton.setEnabled(false);
280 this.showPreview(null);
Rayan Kansob451b4f2019-04-04 23:12:11281 }
282
283 /**
Rayan Kanso8fe8ee22019-03-04 14:58:46284 * Called when the `Toggle Record` button is clicked.
285 */
Jan Schefflercd49c322021-08-13 18:56:33286 toggleRecording(): void {
287 this.model.setRecording(!this.recordButton.toggled(), this.serviceName);
Rayan Kanso8fe8ee22019-03-04 14:58:46288 }
289
290 /**
Rayan Kanso3252d5e2019-03-27 11:37:24291 * Called when the `Clear` button is clicked.
292 */
Jan Schefflercd49c322021-08-13 18:56:33293 private clearEvents(): void {
294 this.model.clearEvents(this.serviceName);
295 this.clearView();
Rayan Kanso3252d5e2019-03-27 11:37:24296 }
297
Benedikt Meurer5ea69422021-09-22 06:59:36298 private onRecordingStateChanged({data: state}: Common.EventTarget.EventTargetEvent<RecordingState>): void {
Jan Schefflercd49c322021-08-13 18:56:33299 if (state.serviceName !== this.serviceName) {
Rayan Kanso8fe8ee22019-03-04 14:58:46300 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34301 }
Rayan Kansoc0bfdd82019-04-24 12:32:22302
Jan Schefflercd49c322021-08-13 18:56:33303 if (state.isRecording === this.recordButton.toggled()) {
Rayan Kansoc0bfdd82019-04-24 12:32:22304 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34305 }
Rayan Kansoc0bfdd82019-04-24 12:32:22306
Jan Schefflercd49c322021-08-13 18:56:33307 this.recordButton.setToggled(state.isRecording);
308 this.updateRecordButtonTooltip();
309 this.showPreview(this.selectedEventNode);
Rayan Kanso8fe8ee22019-03-04 14:58:46310 }
Rayan Kansof40e3152019-03-11 13:49:43311
Jan Schefflercd49c322021-08-13 18:56:33312 private updateRecordButtonTooltip(): void {
313 const buttonTooltip = this.recordButton.toggled() ? i18nString(UIStrings.stopRecordingEvents) :
314 i18nString(UIStrings.startRecordingEvents);
315 this.recordButton.setTitle(buttonTooltip, 'background-service.toggle-recording');
Christy Chen13a12ff2020-08-20 07:43:26316 }
317
Benedikt Meurer5ea69422021-09-22 06:59:36318 private onEventReceived({
319 data: serviceEvent,
320 }: Common.EventTarget.EventTargetEvent<Protocol.BackgroundService.BackgroundServiceEvent>): void {
Jan Schefflercd49c322021-08-13 18:56:33321 if (!this.acceptEvent(serviceEvent)) {
Rayan Kansof40e3152019-03-11 13:49:43322 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34323 }
Jan Schefflercd49c322021-08-13 18:56:33324 this.addEvent(serviceEvent);
Rayan Kanso3252d5e2019-03-27 11:37:24325 }
326
Jan Schefflercd49c322021-08-13 18:56:33327 private onOriginChanged(): void {
Rayan Kansoaca06e72019-03-27 11:57:06328 // No need to refresh the view if we are already showing all events.
Jan Schefflercd49c322021-08-13 18:56:33329 if (this.originCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06330 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34331 }
Jan Schefflercd49c322021-08-13 18:56:33332 this.refreshView();
Rayan Kansoaca06e72019-03-27 11:57:06333 }
334
Kateryna Prokopenko35d6bf62022-11-30 13:56:19335 private onStorageKeyChanged(): void {
336 if (this.storageKeyCheckbox.checked()) {
337 return;
338 }
339 this.refreshView();
340 }
341
Jan Schefflercd49c322021-08-13 18:56:33342 private addEvent(serviceEvent: Protocol.BackgroundService.BackgroundServiceEvent): void {
343 const data = this.createEventData(serviceEvent);
Tim van der Lippe097cdec2020-01-06 14:44:17344 const dataNode = new EventDataNode(data, serviceEvent.eventMetadata);
Jan Schefflercd49c322021-08-13 18:56:33345 this.dataGrid.rootNode().appendChild(dataNode);
Rayan Kansob852ba82019-04-08 13:48:07346
Jan Schefflercd49c322021-08-13 18:56:33347 if (this.dataGrid.rootNode().children.length === 1) {
348 this.saveButton.setEnabled(true);
349 this.showPreview(this.selectedEventNode);
Rayan Kansoc0bfdd82019-04-24 12:32:22350 }
Rayan Kanso3252d5e2019-03-27 11:37:24351 }
352
Jan Schefflercd49c322021-08-13 18:56:33353 private createDataGrid(): DataGrid.DataGrid.DataGridImpl<EventData> {
Jan Scheffler1026b9d2021-02-26 17:12:00354 const columns = ([
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01355 {id: 'id', title: '#', weight: 1},
Kateryna Prokopenko35d6bf62022-11-30 13:56:19356 {id: 'timestamp', title: i18nString(UIStrings.timestamp), weight: 7},
357 {id: 'eventName', title: i18nString(UIStrings.event), weight: 8},
358 {id: 'origin', title: i18nString(UIStrings.origin), weight: 8},
359 {id: 'storageKey', title: i18nString(UIStrings.storageKey), weight: 8},
360 {id: 'swScope', title: i18nString(UIStrings.swScope), weight: 4},
361 {id: 'instanceId', title: i18nString(UIStrings.instanceId), weight: 8},
Jan Scheffler1026b9d2021-02-26 17:12:00362 ] as DataGrid.DataGrid.ColumnDescriptor[]);
Alex Rudenko60a3e942020-11-02 12:27:57363 const dataGrid = new DataGrid.DataGrid.DataGridImpl({
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01364 displayName: i18nString(UIStrings.backgroundServices),
Alex Rudenko60a3e942020-11-02 12:27:57365 columns,
366 editCallback: undefined,
367 refreshCallback: undefined,
Jan Scheffler1026b9d2021-02-26 17:12:00368 deleteCallback: undefined,
Alex Rudenko60a3e942020-11-02 12:27:57369 });
Rayan Kanso3252d5e2019-03-27 11:37:24370 dataGrid.setStriped(true);
Rayan Kansob451b4f2019-04-04 23:12:11371
372 dataGrid.addEventListener(
Jan Schefflercd49c322021-08-13 18:56:33373 DataGrid.DataGrid.Events.SelectedNode, event => this.showPreview((event.data as EventDataNode)));
Rayan Kansob451b4f2019-04-04 23:12:11374
Rayan Kanso3252d5e2019-03-27 11:37:24375 return dataGrid;
376 }
377
378 /**
Rayan Kansoaca06e72019-03-27 11:57:06379 * Creates the data object to pass to the DataGrid Node.
Rayan Kansoaca06e72019-03-27 11:57:06380 */
Jan Schefflercd49c322021-08-13 18:56:33381 private createEventData(serviceEvent: Protocol.BackgroundService.BackgroundServiceEvent): EventData {
Rayan Kansocc02ea32019-05-02 21:26:52382 let swScope = '';
Rayan Kansoaca06e72019-03-27 11:57:06383
Rayan Kansocc02ea32019-05-02 21:26:52384 // Try to get the scope of the Service Worker registration to be more user-friendly.
Jan Schefflercd49c322021-08-13 18:56:33385 const registration = this.serviceWorkerManager ?
386 this.serviceWorkerManager.registrations().get(serviceEvent.serviceWorkerRegistrationId) :
Alex Rudenko60a3e942020-11-02 12:27:57387 undefined;
Tim van der Lippe1d6e57a2019-09-30 11:55:34388 if (registration) {
Rayan Kansocc02ea32019-05-02 21:26:52389 swScope = registration.scopeURL.substr(registration.securityOrigin.length);
Tim van der Lippe1d6e57a2019-09-30 11:55:34390 }
Rayan Kansoaca06e72019-03-27 11:57:06391
392 return {
Jan Schefflercd49c322021-08-13 18:56:33393 id: this.dataGrid.rootNode().children.length + 1,
Tim van der Lippe0efccf02020-02-12 15:15:39394 timestamp: UI.UIUtils.formatTimestamp(serviceEvent.timestamp * 1000, /* full= */ true),
Rayan Kansoaca06e72019-03-27 11:57:06395 origin: serviceEvent.origin,
Kateryna Prokopenko35d6bf62022-11-30 13:56:19396 storageKey: serviceEvent.storageKey,
Rayan Kansocc02ea32019-05-02 21:26:52397 swScope,
Rayan Kansoaca06e72019-03-27 11:57:06398 eventName: serviceEvent.eventName,
399 instanceId: serviceEvent.instanceId,
400 };
401 }
402
403 /**
Rayan Kanso3252d5e2019-03-27 11:37:24404 * Filtration function to know whether event should be shown or not.
Rayan Kanso3252d5e2019-03-27 11:37:24405 */
Jan Schefflercd49c322021-08-13 18:56:33406 private acceptEvent(event: Protocol.BackgroundService.BackgroundServiceEvent): boolean {
407 if (event.service !== this.serviceName) {
Rayan Kansoaca06e72019-03-27 11:57:06408 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34409 }
Rayan Kansoaca06e72019-03-27 11:57:06410
Kateryna Prokopenko35d6bf62022-11-30 13:56:19411 if (this.originCheckbox.checked() || this.storageKeyCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06412 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34413 }
Rayan Kansoaca06e72019-03-27 11:57:06414
415 // Trim the trailing '/'.
416 const origin = event.origin.substr(0, event.origin.length - 1);
Kateryna Prokopenko35d6bf62022-11-30 13:56:19417 const storageKey = event.storageKey;
Rayan Kansoaca06e72019-03-27 11:57:06418
Kateryna Prokopenko35d6bf62022-11-30 13:56:19419 return this.securityOriginManager.securityOrigins().includes(origin) ||
420 this.storageKeyManager.storageKeys().includes(storageKey);
Rayan Kanso3252d5e2019-03-27 11:37:24421 }
Rayan Kansob451b4f2019-04-04 23:12:11422
Jan Schefflercd49c322021-08-13 18:56:33423 private createLearnMoreLink(): Element {
Jecelyn90c04162021-04-07 08:41:35424 let url = 'https://developer.chrome.com/docs/devtools/javascript/background-services/?utm_source=devtools';
Rayan Kanso42c0f9e2019-09-02 12:21:09425
Jan Schefflercd49c322021-08-13 18:56:33426 switch (this.serviceName) {
Rayan Kanso42c0f9e2019-09-02 12:21:09427 case Protocol.BackgroundService.ServiceName.BackgroundFetch:
428 url += '#fetch';
429 break;
430 case Protocol.BackgroundService.ServiceName.BackgroundSync:
431 url += '#sync';
432 break;
433 case Protocol.BackgroundService.ServiceName.PushMessaging:
434 url += '#push';
435 break;
436 case Protocol.BackgroundService.ServiceName.Notifications:
437 url += '#notifications';
438 break;
439 default:
440 break;
441 }
442
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01443 return UI.XLink.XLink.create(url, i18nString(UIStrings.learnMore));
Rayan Kanso42c0f9e2019-09-02 12:21:09444 }
445
Jan Schefflercd49c322021-08-13 18:56:33446 private showPreview(dataNode: EventDataNode|null): void {
447 if (this.selectedEventNode && this.selectedEventNode === dataNode) {
Rayan Kansoc0bfdd82019-04-24 12:32:22448 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34449 }
Rayan Kansoc0bfdd82019-04-24 12:32:22450
Jan Schefflercd49c322021-08-13 18:56:33451 this.selectedEventNode = dataNode;
Rayan Kansoc0bfdd82019-04-24 12:32:22452
Jan Schefflercd49c322021-08-13 18:56:33453 if (this.preview) {
454 this.preview.detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34455 }
Rayan Kansob451b4f2019-04-04 23:12:11456
Jan Schefflercd49c322021-08-13 18:56:33457 if (this.selectedEventNode) {
458 this.preview = this.selectedEventNode.createPreview();
459 this.preview.show(this.previewPanel.contentElement);
Rayan Kanso4476ca52019-07-22 11:51:24460 return;
461 }
462
Jan Schefflercd49c322021-08-13 18:56:33463 this.preview = new UI.Widget.VBox();
464 this.preview.contentElement.classList.add('background-service-preview', 'fill');
465 const centered = this.preview.contentElement.createChild('div');
Rayan Kanso4476ca52019-07-22 11:51:24466
Jan Schefflercd49c322021-08-13 18:56:33467 if (this.dataGrid.rootNode().children.length) {
Rayan Kansoc0bfdd82019-04-24 12:32:22468 // Inform users that grid entries are clickable.
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01469 centered.createChild('p').textContent = i18nString(UIStrings.selectAnEntryToViewMetadata);
Jan Schefflercd49c322021-08-13 18:56:33470 } else if (this.recordButton.toggled()) {
Rayan Kansoc0bfdd82019-04-24 12:32:22471 // Inform users that we are recording/waiting for events.
Jan Schefflercd49c322021-08-13 18:56:33472 const featureName = BackgroundServiceView.getUIString(this.serviceName);
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01473 centered.createChild('p').textContent = i18nString(UIStrings.recordingSActivity, {PH1: featureName});
474 centered.createChild('p').textContent = i18nString(UIStrings.devtoolsWillRecordAllSActivity, {PH1: featureName});
Rayan Kansoc0bfdd82019-04-24 12:32:22475 } else {
Jan Schefflercd49c322021-08-13 18:56:33476 const landingRecordButton = UI.Toolbar.Toolbar.createActionButton(this.recordAction);
Rayan Kansoc0bfdd82019-04-24 12:32:22477
Tim van der Lippef49e2322020-05-01 15:03:09478 const recordKey = document.createElement('b');
479 recordKey.classList.add('background-service-shortcut');
Tim van der Lippe9c9fb122020-09-08 15:06:17480 recordKey.textContent = UI.ShortcutRegistry.ShortcutRegistry.instance()
481 .shortcutsForAction('background-service.toggle-recording')[0]
482 .title();
Rayan Kanso6156d222019-04-29 23:40:55483
Tim van der Lippe0efccf02020-02-12 15:15:39484 const inlineButton = UI.UIUtils.createInlineButton(landingRecordButton);
Rayan Kansof402ef32019-08-12 13:05:18485 inlineButton.classList.add('background-service-record-inline-button');
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01486 centered.createChild('p').appendChild(i18n.i18n.getFormatLocalizedString(
487 str_, UIStrings.clickTheRecordButtonSOrHitSTo, {PH1: inlineButton, PH2: recordKey}));
Rayan Kanso42c0f9e2019-09-02 12:21:09488
Jan Schefflercd49c322021-08-13 18:56:33489 centered.appendChild(this.createLearnMoreLink());
Rayan Kansoc0bfdd82019-04-24 12:32:22490 }
Rayan Kansob451b4f2019-04-04 23:12:11491
Jan Schefflercd49c322021-08-13 18:56:33492 this.preview.show(this.previewPanel.contentElement);
Rayan Kansob451b4f2019-04-04 23:12:11493 }
Rayan Kansob852ba82019-04-08 13:48:07494
495 /**
496 * Saves all currently displayed events in a file (JSON format).
497 */
Jan Schefflercd49c322021-08-13 18:56:33498 private async saveToFile(): Promise<void> {
Kateryna Prokopenko380fdfa2022-03-16 16:39:32499 const fileName = `${this.serviceName}-${Platform.DateUtilities.toISO8601Compact(new Date())}.json` as
500 Platform.DevToolsPath.RawPathString;
Tim van der Lippe0efccf02020-02-12 15:15:39501 const stream = new Bindings.FileUtils.FileOutputStream();
Rayan Kansob852ba82019-04-08 13:48:07502
503 const accepted = await stream.open(fileName);
Tim van der Lippe1d6e57a2019-09-30 11:55:34504 if (!accepted) {
Rayan Kansob852ba82019-04-08 13:48:07505 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34506 }
Rayan Kansob852ba82019-04-08 13:48:07507
Jan Schefflercd49c322021-08-13 18:56:33508 const events = this.model.getEvents(this.serviceName).filter(event => this.acceptEvent(event));
Rayan Kansob852ba82019-04-08 13:48:07509 await stream.write(JSON.stringify(events, undefined, 2));
Tim van der Lippe2d9a95c2022-01-04 15:18:03510 void stream.close();
Rayan Kansob852ba82019-04-08 13:48:07511 }
Kriti Sapra928b70c2021-08-17 09:03:41512 wasShown(): void {
513 super.wasShown();
514 this.registerCSSFiles([emptyWidgetStyles, backgroundServiceViewStyles]);
515 }
Tim van der Lippe097cdec2020-01-06 14:44:17516}
Rayan Kanso3252d5e2019-03-27 11:37:24517
Jan Scheffler1026b9d2021-02-26 17:12:00518export class EventDataNode extends DataGrid.DataGrid.DataGridNode<EventData> {
Jan Schefflercd49c322021-08-13 18:56:33519 private readonly eventMetadata: Protocol.BackgroundService.EventMetadata[];
Jan Scheffler1026b9d2021-02-26 17:12:00520
521 constructor(data: EventData, eventMetadata: Protocol.BackgroundService.EventMetadata[]) {
Rayan Kansoaca06e72019-03-27 11:57:06522 super(data);
Rayan Kanso3252d5e2019-03-27 11:37:24523
Jan Schefflercd49c322021-08-13 18:56:33524 this.eventMetadata = eventMetadata.sort((m1, m2) => Platform.StringUtilities.compare(m1.key, m2.key));
Rayan Kanso3252d5e2019-03-27 11:37:24525 }
526
Jan Scheffler1026b9d2021-02-26 17:12:00527 createPreview(): UI.Widget.VBox {
Tim van der Lippe0efccf02020-02-12 15:15:39528 const preview = new UI.Widget.VBox();
Rayan Kansoc0bfdd82019-04-24 12:32:22529 preview.element.classList.add('background-service-metadata');
530
Jan Schefflercd49c322021-08-13 18:56:33531 for (const entry of this.eventMetadata) {
Tim van der Lippef49e2322020-05-01 15:03:09532 const div = document.createElement('div');
533 div.classList.add('background-service-metadata-entry');
Rayan Kansoc0bfdd82019-04-24 12:32:22534 div.createChild('div', 'background-service-metadata-name').textContent = entry.key + ': ';
Rayan Kanso4476ca52019-07-22 11:51:24535 if (entry.value) {
536 div.createChild('div', 'background-service-metadata-value source-code').textContent = entry.value;
537 } else {
538 div.createChild('div', 'background-service-metadata-value background-service-empty-value').textContent =
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01539 i18nString(UIStrings.empty);
Rayan Kanso4476ca52019-07-22 11:51:24540 }
Rayan Kansoc0bfdd82019-04-24 12:32:22541 preview.element.appendChild(div);
542 }
543
544 if (!preview.element.children.length) {
Tim van der Lippef49e2322020-05-01 15:03:09545 const div = document.createElement('div');
546 div.classList.add('background-service-metadata-entry');
Sigurd Schneiderfec75bb2021-03-19 10:23:34547 div.createChild('div', 'background-service-metadata-name background-service-empty-value').textContent =
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01548 i18nString(UIStrings.noMetadataForThisEvent);
Rayan Kansoc0bfdd82019-04-24 12:32:22549 preview.element.appendChild(div);
550 }
551
552 return preview;
Rayan Kansof40e3152019-03-11 13:49:43553 }
Tim van der Lippe097cdec2020-01-06 14:44:17554}
Rayan Kanso6156d222019-04-29 23:40:55555
Jan Scheffler1026b9d2021-02-26 17:12:00556let actionDelegateInstance: ActionDelegate;
Andres Olivares79578512021-02-02 00:27:49557
Jan Scheffler1026b9d2021-02-26 17:12:00558export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
559 static instance(opts: {
560 forceNew: boolean|null,
561 } = {forceNew: null}): ActionDelegate {
Andres Olivares79578512021-02-02 00:27:49562 const {forceNew} = opts;
563 if (!actionDelegateInstance || forceNew) {
564 actionDelegateInstance = new ActionDelegate();
565 }
566
567 return actionDelegateInstance;
568 }
569
Jan Scheffler1026b9d2021-02-26 17:12:00570 handleAction(context: UI.Context.Context, actionId: string): boolean {
Tim van der Lippe097cdec2020-01-06 14:44:17571 const view = context.flavor(BackgroundServiceView);
Rayan Kanso6156d222019-04-29 23:40:55572 switch (actionId) {
Alex Rudenko60a3e942020-11-02 12:27:57573 case 'background-service.toggle-recording': {
574 if (!view) {
575 throw new Error('BackgroundServiceView instance is missing');
576 }
Jan Schefflercd49c322021-08-13 18:56:33577 view.toggleRecording();
Rayan Kanso6156d222019-04-29 23:40:55578 return true;
Alex Rudenko60a3e942020-11-02 12:27:57579 }
Rayan Kanso6156d222019-04-29 23:40:55580 }
581 return false;
582 }
Tim van der Lippe097cdec2020-01-06 14:44:17583}
Jan Scheffler1026b9d2021-02-26 17:12:00584export interface RecordingState {
585 isRecording: boolean;
586 serviceName: Protocol.BackgroundService.ServiceName;
587}
588export interface EventData {
589 id: number;
590 timestamp: string;
591 origin: string;
Kateryna Prokopenko35d6bf62022-11-30 13:56:19592 storageKey: string;
Jan Scheffler1026b9d2021-02-26 17:12:00593 swScope: string;
594 eventName: string;
595 instanceId: string;
596}