blob: 6fbd92dc0c0aad6df325dd13a4568c8b587911a8 [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 Prokopenko107071c2023-12-21 10:18:4615import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
Kateryna Prokopenkofbb22472022-05-10 15:36:3416
Benedikt Meurer36149e52023-11-16 08:58:0817import {type BackgroundServiceModel, Events} from './BackgroundServiceModel.js';
Kateryna Prokopenkofbb22472022-05-10 15:36:3418import backgroundServiceViewStyles from './backgroundServiceView.css.js';
Tim van der Lippe0efccf02020-02-12 15:15:3919
Simon Zünd697fb0b2021-03-01 10:12:4220const UIStrings = {
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0121 /**
Jack Franklinfd72c072022-12-21 11:45:0122 *@description Text in Background Service View of the Application panel
23 */
Sofia Emelianova4f0a9a32023-07-14 09:01:5424 backgroundFetch: 'Background fetch',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0125 /**
Jack Franklinfd72c072022-12-21 11:45:0126 *@description Text in Background Service View of the Application panel
27 */
Sofia Emelianova4f0a9a32023-07-14 09:01:5428 backgroundSync: 'Background sync',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0129 /**
Jack Franklinfd72c072022-12-21 11:45:0130 *@description Text in Background Service View of the Application panel
31 */
Sofia Emelianova4f0a9a32023-07-14 09:01:5432 pushMessaging: 'Push messaging',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0133 /**
Jack Franklinfd72c072022-12-21 11:45:0134 *@description Text in Background Service View of the Application panel
35 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0136 notifications: 'Notifications',
37 /**
Jack Franklinfd72c072022-12-21 11:45:0138 *@description Text in Background Service View of the Application panel
39 */
Sofia Emelianova4f0a9a32023-07-14 09:01:5440 paymentHandler: 'Payment handler',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0141 /**
Jack Franklinfd72c072022-12-21 11:45:0142 *@description Text in the Periodic Background Service View of the Application panel
43 */
Sofia Emelianova4f0a9a32023-07-14 09:01:5444 periodicBackgroundSync: 'Periodic background sync',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0145 /**
Jack Franklinfd72c072022-12-21 11:45:0146 *@description Text to clear content
47 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0148 clear: 'Clear',
49 /**
Jack Franklinfd72c072022-12-21 11:45:0150 *@description Tooltip text that appears when hovering over the largeicon download button in the Background Service View of the Application panel
51 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0152 saveEvents: 'Save events',
53 /**
Jack Franklinfd72c072022-12-21 11:45:0154 *@description Text in Background Service View of the Application panel
55 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0156 showEventsFromOtherDomains: 'Show events from other domains',
57 /**
Jack Franklinfd72c072022-12-21 11:45:0158 *@description Text of a checkbox to show events for other dtorage keys
59 */
Kateryna Prokopenko35d6bf62022-11-30 13:56:1960 showEventsForOtherStorageKeys: 'Show events from other storage partitions',
61 /**
Jack Franklinfd72c072022-12-21 11:45:0162 *@description Title of an action under the Background Services category that can be invoked through the Command Menu
63 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0164 stopRecordingEvents: 'Stop recording events',
65 /**
Jack Franklinfd72c072022-12-21 11:45:0166 *@description Title of an action under the Background Services category that can be invoked through the Command Menu
67 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0168 startRecordingEvents: 'Start recording events',
69 /**
Jack Franklinfd72c072022-12-21 11:45:0170 *@description Text for timestamps of items
71 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0172 timestamp: 'Timestamp',
73 /**
Jack Franklinfd72c072022-12-21 11:45:0174 *@description Text that refers to some events
75 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0176 event: 'Event',
77 /**
Jack Franklinfd72c072022-12-21 11:45:0178 *@description Text for the origin of something
79 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0180 origin: 'Origin',
81 /**
Jack Franklinfd72c072022-12-21 11:45:0182 *@description Text for the storage key of something
83 */
Kateryna Prokopenko35d6bf62022-11-30 13:56:1984 storageKey: 'Storage Key',
85 /**
Jack Franklinfd72c072022-12-21 11:45:0186 *@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.
87 */
Sigurd Schneiderbee57a82021-02-23 12:58:2488 swScope: 'Service Worker Scope',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0189 /**
Jack Franklinfd72c072022-12-21 11:45:0190 *@description Text in Background Service View of the Application panel
91 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0192 instanceId: 'Instance ID',
93 /**
Jack Franklinfd72c072022-12-21 11:45:0194 *@description Text in Application Panel Sidebar of the Application panel
95 */
Sofia Emelianova4f0a9a32023-07-14 09:01:5496 backgroundServices: 'Background services',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0197 /**
Jack Franklinfd72c072022-12-21 11:45:0198 *@description Text that is usually a hyperlink to more documentation
99 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01100 learnMore: 'Learn more',
101 /**
Jack Franklinfd72c072022-12-21 11:45:01102 *@description Text in Background Service View of the Application panel
103 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01104 selectAnEntryToViewMetadata: 'Select an entry to view metadata',
105 /**
Jack Franklinfd72c072022-12-21 11:45:01106 *@description Text in Background Service View of the Application panel
107 *@example {Background Fetch} PH1
108 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01109 recordingSActivity: 'Recording {PH1} activity...',
110 /**
Jack Franklinfd72c072022-12-21 11:45:01111 *@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 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01114 devtoolsWillRecordAllSActivity: 'DevTools will record all {PH1} activity for up to 3 days, even when closed.',
115 /**
Jack Franklinfd72c072022-12-21 11:45:01116 *@description Text in Background Service View of the Application panel
117 *@example {record} PH1
118 *@example {Ctrl + R} PH2
119 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01120 clickTheRecordButtonSOrHitSTo: 'Click the record button {PH1} or hit {PH2} to start recording.',
121 /**
Jack Franklinfd72c072022-12-21 11:45:01122 *@description Text to show an item is empty
123 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01124 empty: 'empty',
125 /**
Jack Franklinfd72c072022-12-21 11:45:01126 *@description Text in Background Service View of the Application panel
127 */
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01128 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;
Danil Somsikov9ffcf9c2024-02-23 15:25:07173 const kebabName = Platform.StringUtilities.toKebabCase(serviceName);
Kateryna Prokopenko107071c2023-12-21 10:18:46174 this.element.setAttribute('jslog', `${VisualLogging.pane().context(kebabName)}`);
Rayan Kanso68904202019-02-21 14:16:25175
Jan Schefflercd49c322021-08-13 18:56:33176 this.model = model;
177 this.model.addEventListener(Events.RecordingStateChanged, this.onRecordingStateChanged, this);
178 this.model.addEventListener(Events.BackgroundServiceEventReceived, this.onEventReceived, this);
179 this.model.enable(this.serviceName);
Rayan Kanso8fe8ee22019-03-04 14:58:46180
Jan Schefflercd49c322021-08-13 18:56:33181 this.serviceWorkerManager = this.model.target().model(SDK.ServiceWorkerManager.ServiceWorkerManager);
Rayan Kansoaca06e72019-03-27 11:57:06182
Jan Schefflercd49c322021-08-13 18:56:33183 this.securityOriginManager = this.model.target().model(SDK.SecurityOriginManager.SecurityOriginManager) as
Jan Scheffler1026b9d2021-02-26 17:12:00184 SDK.SecurityOriginManager.SecurityOriginManager;
Jan Schefflercd49c322021-08-13 18:56:33185 if (!this.securityOriginManager) {
Alex Rudenko60a3e942020-11-02 12:27:57186 throw new Error('SecurityOriginManager instance is missing');
187 }
Jan Schefflercd49c322021-08-13 18:56:33188 this.securityOriginManager.addEventListener(
189 SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, () => this.onOriginChanged());
Kateryna Prokopenko35d6bf62022-11-30 13:56:19190
191 this.storageKeyManager =
192 this.model.target().model(SDK.StorageKeyManager.StorageKeyManager) as SDK.StorageKeyManager.StorageKeyManager;
193 if (!this.storageKeyManager) {
194 throw new Error('StorageKeyManager instance is missing');
195 }
196 this.storageKeyManager.addEventListener(
197 SDK.StorageKeyManager.Events.MainStorageKeyChanged, () => this.onStorageKeyChanged());
198
Benedikt Meurer36149e52023-11-16 08:58:08199 this.recordAction = UI.ActionRegistry.ActionRegistry.instance().getAction('background-service.toggle-recording');
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);
Danil Somsikovb22c7712024-01-31 13:37:31202 this.toolbar.element.setAttribute('jslog', `${VisualLogging.toolbar()}`);
Tim van der Lippe2d9a95c2022-01-04 15:18:03203 void this.setupToolbar();
Rayan Kanso3252d5e2019-03-27 11:37:24204
Rayan Kansob451b4f2019-04-04 23:12:11205 /**
206 * This will contain the DataGrid for displaying events, and a panel at the bottom for showing
207 * extra metadata related to the selected event.
Rayan Kansob451b4f2019-04-04 23:12:11208 */
Jan Schefflercd49c322021-08-13 18:56:33209 this.splitWidget = new UI.SplitWidget.SplitWidget(/* isVertical= */ false, /* secondIsSidebar= */ true);
210 this.splitWidget.show(this.contentElement);
Rayan Kansob451b4f2019-04-04 23:12:11211
Jan Schefflercd49c322021-08-13 18:56:33212 this.dataGrid = this.createDataGrid();
Rayan Kansob451b4f2019-04-04 23:12:11213
Jan Schefflercd49c322021-08-13 18:56:33214 this.previewPanel = new UI.Widget.VBox();
Danil Somsikov64855db2024-02-21 07:22:59215 this.previewPanel.element.setAttribute('jslog', `${VisualLogging.pane('preview').track({resize: true})}`);
Rayan Kansob451b4f2019-04-04 23:12:11216
Jan Schefflercd49c322021-08-13 18:56:33217 this.selectedEventNode = null;
Rayan Kansoc0bfdd82019-04-24 12:32:22218
Jan Schefflercd49c322021-08-13 18:56:33219 this.preview = null;
Rayan Kansob451b4f2019-04-04 23:12:11220
Jan Schefflercd49c322021-08-13 18:56:33221 this.splitWidget.setMainWidget(this.dataGrid.asWidget());
222 this.splitWidget.setSidebarWidget(this.previewPanel);
Rayan Kansob451b4f2019-04-04 23:12:11223
Jan Schefflercd49c322021-08-13 18:56:33224 this.showPreview(null);
Rayan Kanso68904202019-02-21 14:16:25225 }
226
Kateryna Prokopenko35d6bf62022-11-30 13:56:19227 getDataGrid(): DataGrid.DataGrid.DataGridImpl<EventData> {
228 return this.dataGrid;
229 }
230
Rayan Kanso68904202019-02-21 14:16:25231 /**
232 * Creates the toolbar UI element.
233 */
Jan Schefflercd49c322021-08-13 18:56:33234 private async setupToolbar(): Promise<void> {
Jimmy Seto7deb66b2023-09-27 18:54:16235 this.toolbar.makeWrappable(true);
Jan Schefflercd49c322021-08-13 18:56:33236 this.recordButton = (UI.Toolbar.Toolbar.createActionButton(this.recordAction) as UI.Toolbar.ToolbarToggle);
237 this.toolbar.appendToolbarItem(this.recordButton);
Rayan Kanso68904202019-02-21 14:16:25238
Kateryna Prokopenko107071c2023-12-21 10:18:46239 const clearButton =
240 new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clear), 'clear', undefined, 'background-service.clear');
Jan Schefflercd49c322021-08-13 18:56:33241 clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => this.clearEvents());
242 this.toolbar.appendToolbarItem(clearButton);
Rayan Kanso68904202019-02-21 14:16:25243
Jan Schefflercd49c322021-08-13 18:56:33244 this.toolbar.appendSeparator();
Rayan Kanso68904202019-02-21 14:16:25245
Kateryna Prokopenko107071c2023-12-21 10:18:46246 this.saveButton = new UI.Toolbar.ToolbarButton(
247 i18nString(UIStrings.saveEvents), 'download', undefined, 'background-service.save-events');
Jan Schefflercd49c322021-08-13 18:56:33248 this.saveButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, _event => {
Tim van der Lippe2d9a95c2022-01-04 15:18:03249 void this.saveToFile();
Tim van der Lippe37a35ff2020-03-03 13:49:02250 });
Jan Schefflercd49c322021-08-13 18:56:33251 this.saveButton.setEnabled(false);
252 this.toolbar.appendToolbarItem(this.saveButton);
Rayan Kansoc0bfdd82019-04-24 12:32:22253
Jan Schefflercd49c322021-08-13 18:56:33254 this.toolbar.appendSeparator();
Rayan Kansoc0bfdd82019-04-24 12:32:22255
Jan Schefflercd49c322021-08-13 18:56:33256 this.originCheckbox = new UI.Toolbar.ToolbarCheckbox(
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01257 i18nString(UIStrings.showEventsFromOtherDomains), i18nString(UIStrings.showEventsFromOtherDomains),
Kateryna Prokopenko107071c2023-12-21 10:18:46258 () => this.refreshView(), 'show-events-from-other-domains');
Jan Schefflercd49c322021-08-13 18:56:33259 this.toolbar.appendToolbarItem(this.originCheckbox);
Kateryna Prokopenko35d6bf62022-11-30 13:56:19260
261 this.storageKeyCheckbox = new UI.Toolbar.ToolbarCheckbox(
262 i18nString(UIStrings.showEventsForOtherStorageKeys), i18nString(UIStrings.showEventsForOtherStorageKeys),
Kateryna Prokopenko107071c2023-12-21 10:18:46263 () => this.refreshView(), 'show-events-from-other-partitions');
Kateryna Prokopenko35d6bf62022-11-30 13:56:19264 this.toolbar.appendToolbarItem(this.storageKeyCheckbox);
Rayan Kanso68904202019-02-21 14:16:25265 }
Rayan Kanso8fe8ee22019-03-04 14:58:46266
267 /**
Rayan Kansob451b4f2019-04-04 23:12:11268 * Displays all available events in the grid.
269 */
Jan Schefflercd49c322021-08-13 18:56:33270 private refreshView(): void {
271 this.clearView();
272 const events = this.model.getEvents(this.serviceName).filter(event => this.acceptEvent(event));
Tim van der Lippe1d6e57a2019-09-30 11:55:34273 for (const event of events) {
Jan Schefflercd49c322021-08-13 18:56:33274 this.addEvent(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:34275 }
Rayan Kansob451b4f2019-04-04 23:12:11276 }
277
278 /**
279 * Clears the grid and panel.
280 */
Jan Schefflercd49c322021-08-13 18:56:33281 private clearView(): void {
282 this.selectedEventNode = null;
283 this.dataGrid.rootNode().removeChildren();
284 this.saveButton.setEnabled(false);
285 this.showPreview(null);
Rayan Kansob451b4f2019-04-04 23:12:11286 }
287
288 /**
Rayan Kanso8fe8ee22019-03-04 14:58:46289 * Called when the `Toggle Record` button is clicked.
290 */
Jan Schefflercd49c322021-08-13 18:56:33291 toggleRecording(): void {
Kateryna Prokopenko17cba892024-07-26 13:33:56292 this.model.setRecording(!this.recordButton.isToggled(), this.serviceName);
Rayan Kanso8fe8ee22019-03-04 14:58:46293 }
294
295 /**
Rayan Kanso3252d5e2019-03-27 11:37:24296 * Called when the `Clear` button is clicked.
297 */
Jan Schefflercd49c322021-08-13 18:56:33298 private clearEvents(): void {
299 this.model.clearEvents(this.serviceName);
300 this.clearView();
Rayan Kanso3252d5e2019-03-27 11:37:24301 }
302
Benedikt Meurer5ea69422021-09-22 06:59:36303 private onRecordingStateChanged({data: state}: Common.EventTarget.EventTargetEvent<RecordingState>): void {
Jan Schefflercd49c322021-08-13 18:56:33304 if (state.serviceName !== this.serviceName) {
Rayan Kanso8fe8ee22019-03-04 14:58:46305 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34306 }
Rayan Kansoc0bfdd82019-04-24 12:32:22307
Kateryna Prokopenko17cba892024-07-26 13:33:56308 if (state.isRecording === this.recordButton.isToggled()) {
Rayan Kansoc0bfdd82019-04-24 12:32:22309 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34310 }
Rayan Kansoc0bfdd82019-04-24 12:32:22311
Jan Schefflercd49c322021-08-13 18:56:33312 this.recordButton.setToggled(state.isRecording);
313 this.updateRecordButtonTooltip();
314 this.showPreview(this.selectedEventNode);
Rayan Kanso8fe8ee22019-03-04 14:58:46315 }
Rayan Kansof40e3152019-03-11 13:49:43316
Jan Schefflercd49c322021-08-13 18:56:33317 private updateRecordButtonTooltip(): void {
Kateryna Prokopenko17cba892024-07-26 13:33:56318 const buttonTooltip = this.recordButton.isToggled() ? i18nString(UIStrings.stopRecordingEvents) :
319 i18nString(UIStrings.startRecordingEvents);
Jan Schefflercd49c322021-08-13 18:56:33320 this.recordButton.setTitle(buttonTooltip, 'background-service.toggle-recording');
Christy Chen13a12ff2020-08-20 07:43:26321 }
322
Benedikt Meurer5ea69422021-09-22 06:59:36323 private onEventReceived({
324 data: serviceEvent,
325 }: Common.EventTarget.EventTargetEvent<Protocol.BackgroundService.BackgroundServiceEvent>): void {
Jan Schefflercd49c322021-08-13 18:56:33326 if (!this.acceptEvent(serviceEvent)) {
Rayan Kansof40e3152019-03-11 13:49:43327 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34328 }
Jan Schefflercd49c322021-08-13 18:56:33329 this.addEvent(serviceEvent);
Rayan Kanso3252d5e2019-03-27 11:37:24330 }
331
Jan Schefflercd49c322021-08-13 18:56:33332 private onOriginChanged(): void {
Rayan Kansoaca06e72019-03-27 11:57:06333 // No need to refresh the view if we are already showing all events.
Jan Schefflercd49c322021-08-13 18:56:33334 if (this.originCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06335 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34336 }
Jan Schefflercd49c322021-08-13 18:56:33337 this.refreshView();
Rayan Kansoaca06e72019-03-27 11:57:06338 }
339
Kateryna Prokopenko35d6bf62022-11-30 13:56:19340 private onStorageKeyChanged(): void {
341 if (this.storageKeyCheckbox.checked()) {
342 return;
343 }
344 this.refreshView();
345 }
346
Jan Schefflercd49c322021-08-13 18:56:33347 private addEvent(serviceEvent: Protocol.BackgroundService.BackgroundServiceEvent): void {
348 const data = this.createEventData(serviceEvent);
Tim van der Lippe097cdec2020-01-06 14:44:17349 const dataNode = new EventDataNode(data, serviceEvent.eventMetadata);
Jan Schefflercd49c322021-08-13 18:56:33350 this.dataGrid.rootNode().appendChild(dataNode);
Rayan Kansob852ba82019-04-08 13:48:07351
Jan Schefflercd49c322021-08-13 18:56:33352 if (this.dataGrid.rootNode().children.length === 1) {
353 this.saveButton.setEnabled(true);
354 this.showPreview(this.selectedEventNode);
Rayan Kansoc0bfdd82019-04-24 12:32:22355 }
Rayan Kanso3252d5e2019-03-27 11:37:24356 }
357
Jan Schefflercd49c322021-08-13 18:56:33358 private createDataGrid(): DataGrid.DataGrid.DataGridImpl<EventData> {
Jan Scheffler1026b9d2021-02-26 17:12:00359 const columns = ([
Danil Somsikov848d1872024-01-23 08:09:11360 {id: 'id', title: '#', weight: 1},
361 {id: 'timestamp', title: i18nString(UIStrings.timestamp), weight: 7},
362 {id: 'event-name', title: i18nString(UIStrings.event), weight: 8},
363 {id: 'origin', title: i18nString(UIStrings.origin), weight: 8},
364 {id: 'storage-key', title: i18nString(UIStrings.storageKey), weight: 8},
365 {id: 'sw-scope', title: i18nString(UIStrings.swScope), weight: 4},
366 {id: 'instance-id', title: i18nString(UIStrings.instanceId), weight: 8},
Jan Scheffler1026b9d2021-02-26 17:12:00367 ] as DataGrid.DataGrid.ColumnDescriptor[]);
Alex Rudenko60a3e942020-11-02 12:27:57368 const dataGrid = new DataGrid.DataGrid.DataGridImpl({
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01369 displayName: i18nString(UIStrings.backgroundServices),
Alex Rudenko60a3e942020-11-02 12:27:57370 columns,
371 editCallback: undefined,
372 refreshCallback: undefined,
Jan Scheffler1026b9d2021-02-26 17:12:00373 deleteCallback: undefined,
Alex Rudenko60a3e942020-11-02 12:27:57374 });
Rayan Kanso3252d5e2019-03-27 11:37:24375 dataGrid.setStriped(true);
Rayan Kansob451b4f2019-04-04 23:12:11376
377 dataGrid.addEventListener(
Jan Schefflercd49c322021-08-13 18:56:33378 DataGrid.DataGrid.Events.SelectedNode, event => this.showPreview((event.data as EventDataNode)));
Rayan Kansob451b4f2019-04-04 23:12:11379
Rayan Kanso3252d5e2019-03-27 11:37:24380 return dataGrid;
381 }
382
383 /**
Rayan Kansoaca06e72019-03-27 11:57:06384 * Creates the data object to pass to the DataGrid Node.
Rayan Kansoaca06e72019-03-27 11:57:06385 */
Jan Schefflercd49c322021-08-13 18:56:33386 private createEventData(serviceEvent: Protocol.BackgroundService.BackgroundServiceEvent): EventData {
Rayan Kansocc02ea32019-05-02 21:26:52387 let swScope = '';
Rayan Kansoaca06e72019-03-27 11:57:06388
Rayan Kansocc02ea32019-05-02 21:26:52389 // Try to get the scope of the Service Worker registration to be more user-friendly.
Jan Schefflercd49c322021-08-13 18:56:33390 const registration = this.serviceWorkerManager ?
391 this.serviceWorkerManager.registrations().get(serviceEvent.serviceWorkerRegistrationId) :
Alex Rudenko60a3e942020-11-02 12:27:57392 undefined;
Tim van der Lippe1d6e57a2019-09-30 11:55:34393 if (registration) {
Rayan Kansocc02ea32019-05-02 21:26:52394 swScope = registration.scopeURL.substr(registration.securityOrigin.length);
Tim van der Lippe1d6e57a2019-09-30 11:55:34395 }
Rayan Kansoaca06e72019-03-27 11:57:06396
397 return {
Jan Schefflercd49c322021-08-13 18:56:33398 id: this.dataGrid.rootNode().children.length + 1,
Tim van der Lippe0efccf02020-02-12 15:15:39399 timestamp: UI.UIUtils.formatTimestamp(serviceEvent.timestamp * 1000, /* full= */ true),
Rayan Kansoaca06e72019-03-27 11:57:06400 origin: serviceEvent.origin,
Danil Somsikovf5538f02024-01-17 20:15:17401 'storage-key': serviceEvent.storageKey,
402 'sw-scope': swScope,
403 'event-name': serviceEvent.eventName,
404 'instance-id': serviceEvent.instanceId,
Rayan Kansoaca06e72019-03-27 11:57:06405 };
406 }
407
408 /**
Rayan Kanso3252d5e2019-03-27 11:37:24409 * Filtration function to know whether event should be shown or not.
Rayan Kanso3252d5e2019-03-27 11:37:24410 */
Jan Schefflercd49c322021-08-13 18:56:33411 private acceptEvent(event: Protocol.BackgroundService.BackgroundServiceEvent): boolean {
412 if (event.service !== this.serviceName) {
Rayan Kansoaca06e72019-03-27 11:57:06413 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34414 }
Rayan Kansoaca06e72019-03-27 11:57:06415
Kateryna Prokopenko35d6bf62022-11-30 13:56:19416 if (this.originCheckbox.checked() || this.storageKeyCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06417 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34418 }
Rayan Kansoaca06e72019-03-27 11:57:06419
420 // Trim the trailing '/'.
421 const origin = event.origin.substr(0, event.origin.length - 1);
Kateryna Prokopenko35d6bf62022-11-30 13:56:19422 const storageKey = event.storageKey;
Rayan Kansoaca06e72019-03-27 11:57:06423
Kateryna Prokopenko35d6bf62022-11-30 13:56:19424 return this.securityOriginManager.securityOrigins().includes(origin) ||
425 this.storageKeyManager.storageKeys().includes(storageKey);
Rayan Kanso3252d5e2019-03-27 11:37:24426 }
Rayan Kansob451b4f2019-04-04 23:12:11427
Jan Schefflercd49c322021-08-13 18:56:33428 private createLearnMoreLink(): Element {
Jecelyn90c04162021-04-07 08:41:35429 let url = 'https://developer.chrome.com/docs/devtools/javascript/background-services/?utm_source=devtools';
Rayan Kanso42c0f9e2019-09-02 12:21:09430
Jan Schefflercd49c322021-08-13 18:56:33431 switch (this.serviceName) {
Rayan Kanso42c0f9e2019-09-02 12:21:09432 case Protocol.BackgroundService.ServiceName.BackgroundFetch:
433 url += '#fetch';
434 break;
435 case Protocol.BackgroundService.ServiceName.BackgroundSync:
436 url += '#sync';
437 break;
438 case Protocol.BackgroundService.ServiceName.PushMessaging:
439 url += '#push';
440 break;
441 case Protocol.BackgroundService.ServiceName.Notifications:
442 url += '#notifications';
443 break;
444 default:
445 break;
446 }
447
Kateryna Prokopenko1a4e2612023-12-21 13:23:45448 return UI.XLink.XLink.create(url, i18nString(UIStrings.learnMore), undefined, undefined, 'learn-more');
Rayan Kanso42c0f9e2019-09-02 12:21:09449 }
450
Jan Schefflercd49c322021-08-13 18:56:33451 private showPreview(dataNode: EventDataNode|null): void {
452 if (this.selectedEventNode && this.selectedEventNode === dataNode) {
Rayan Kansoc0bfdd82019-04-24 12:32:22453 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34454 }
Rayan Kansoc0bfdd82019-04-24 12:32:22455
Jan Schefflercd49c322021-08-13 18:56:33456 this.selectedEventNode = dataNode;
Rayan Kansoc0bfdd82019-04-24 12:32:22457
Jan Schefflercd49c322021-08-13 18:56:33458 if (this.preview) {
459 this.preview.detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34460 }
Rayan Kansob451b4f2019-04-04 23:12:11461
Jan Schefflercd49c322021-08-13 18:56:33462 if (this.selectedEventNode) {
463 this.preview = this.selectedEventNode.createPreview();
464 this.preview.show(this.previewPanel.contentElement);
Rayan Kanso4476ca52019-07-22 11:51:24465 return;
466 }
467
Jan Schefflercd49c322021-08-13 18:56:33468 this.preview = new UI.Widget.VBox();
469 this.preview.contentElement.classList.add('background-service-preview', 'fill');
470 const centered = this.preview.contentElement.createChild('div');
Rayan Kanso4476ca52019-07-22 11:51:24471
Jan Schefflercd49c322021-08-13 18:56:33472 if (this.dataGrid.rootNode().children.length) {
Rayan Kansoc0bfdd82019-04-24 12:32:22473 // Inform users that grid entries are clickable.
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01474 centered.createChild('p').textContent = i18nString(UIStrings.selectAnEntryToViewMetadata);
Kateryna Prokopenko17cba892024-07-26 13:33:56475 } else if (this.recordButton.isToggled()) {
Rayan Kansoc0bfdd82019-04-24 12:32:22476 // Inform users that we are recording/waiting for events.
Sofia Emelianova4f0a9a32023-07-14 09:01:54477 const featureName = BackgroundServiceView.getUIString(this.serviceName).toLowerCase();
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01478 centered.createChild('p').textContent = i18nString(UIStrings.recordingSActivity, {PH1: featureName});
479 centered.createChild('p').textContent = i18nString(UIStrings.devtoolsWillRecordAllSActivity, {PH1: featureName});
Rayan Kansoc0bfdd82019-04-24 12:32:22480 } else {
Jan Schefflercd49c322021-08-13 18:56:33481 const landingRecordButton = UI.Toolbar.Toolbar.createActionButton(this.recordAction);
Rayan Kansoc0bfdd82019-04-24 12:32:22482
Tim van der Lippef49e2322020-05-01 15:03:09483 const recordKey = document.createElement('b');
484 recordKey.classList.add('background-service-shortcut');
Tim van der Lippe9c9fb122020-09-08 15:06:17485 recordKey.textContent = UI.ShortcutRegistry.ShortcutRegistry.instance()
486 .shortcutsForAction('background-service.toggle-recording')[0]
487 .title();
Rayan Kanso6156d222019-04-29 23:40:55488
Tim van der Lippe0efccf02020-02-12 15:15:39489 const inlineButton = UI.UIUtils.createInlineButton(landingRecordButton);
Rayan Kansof402ef32019-08-12 13:05:18490 inlineButton.classList.add('background-service-record-inline-button');
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01491 centered.createChild('p').appendChild(i18n.i18n.getFormatLocalizedString(
492 str_, UIStrings.clickTheRecordButtonSOrHitSTo, {PH1: inlineButton, PH2: recordKey}));
Rayan Kanso42c0f9e2019-09-02 12:21:09493
Jan Schefflercd49c322021-08-13 18:56:33494 centered.appendChild(this.createLearnMoreLink());
Rayan Kansoc0bfdd82019-04-24 12:32:22495 }
Rayan Kansob451b4f2019-04-04 23:12:11496
Jan Schefflercd49c322021-08-13 18:56:33497 this.preview.show(this.previewPanel.contentElement);
Rayan Kansob451b4f2019-04-04 23:12:11498 }
Rayan Kansob852ba82019-04-08 13:48:07499
500 /**
501 * Saves all currently displayed events in a file (JSON format).
502 */
Jan Schefflercd49c322021-08-13 18:56:33503 private async saveToFile(): Promise<void> {
Kateryna Prokopenko380fdfa2022-03-16 16:39:32504 const fileName = `${this.serviceName}-${Platform.DateUtilities.toISO8601Compact(new Date())}.json` as
505 Platform.DevToolsPath.RawPathString;
Tim van der Lippe0efccf02020-02-12 15:15:39506 const stream = new Bindings.FileUtils.FileOutputStream();
Rayan Kansob852ba82019-04-08 13:48:07507
508 const accepted = await stream.open(fileName);
Tim van der Lippe1d6e57a2019-09-30 11:55:34509 if (!accepted) {
Rayan Kansob852ba82019-04-08 13:48:07510 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34511 }
Rayan Kansob852ba82019-04-08 13:48:07512
Jan Schefflercd49c322021-08-13 18:56:33513 const events = this.model.getEvents(this.serviceName).filter(event => this.acceptEvent(event));
Rayan Kansob852ba82019-04-08 13:48:07514 await stream.write(JSON.stringify(events, undefined, 2));
Tim van der Lippe2d9a95c2022-01-04 15:18:03515 void stream.close();
Rayan Kansob852ba82019-04-08 13:48:07516 }
Randolf Jungffd14242023-04-19 00:32:25517 override wasShown(): void {
Kriti Sapra928b70c2021-08-17 09:03:41518 super.wasShown();
519 this.registerCSSFiles([emptyWidgetStyles, backgroundServiceViewStyles]);
520 }
Tim van der Lippe097cdec2020-01-06 14:44:17521}
Rayan Kanso3252d5e2019-03-27 11:37:24522
Jan Scheffler1026b9d2021-02-26 17:12:00523export class EventDataNode extends DataGrid.DataGrid.DataGridNode<EventData> {
Jan Schefflercd49c322021-08-13 18:56:33524 private readonly eventMetadata: Protocol.BackgroundService.EventMetadata[];
Jan Scheffler1026b9d2021-02-26 17:12:00525
526 constructor(data: EventData, eventMetadata: Protocol.BackgroundService.EventMetadata[]) {
Rayan Kansoaca06e72019-03-27 11:57:06527 super(data);
Rayan Kanso3252d5e2019-03-27 11:37:24528
Jan Schefflercd49c322021-08-13 18:56:33529 this.eventMetadata = eventMetadata.sort((m1, m2) => Platform.StringUtilities.compare(m1.key, m2.key));
Rayan Kanso3252d5e2019-03-27 11:37:24530 }
531
Jan Scheffler1026b9d2021-02-26 17:12:00532 createPreview(): UI.Widget.VBox {
Tim van der Lippe0efccf02020-02-12 15:15:39533 const preview = new UI.Widget.VBox();
Rayan Kansoc0bfdd82019-04-24 12:32:22534 preview.element.classList.add('background-service-metadata');
Danil Somsikov0cb97462024-01-30 15:11:29535 preview.element.setAttribute('jslog', `${VisualLogging.section('metadata')}`);
Rayan Kansoc0bfdd82019-04-24 12:32:22536
Jan Schefflercd49c322021-08-13 18:56:33537 for (const entry of this.eventMetadata) {
Tim van der Lippef49e2322020-05-01 15:03:09538 const div = document.createElement('div');
539 div.classList.add('background-service-metadata-entry');
Rayan Kansoc0bfdd82019-04-24 12:32:22540 div.createChild('div', 'background-service-metadata-name').textContent = entry.key + ': ';
Rayan Kanso4476ca52019-07-22 11:51:24541 if (entry.value) {
542 div.createChild('div', 'background-service-metadata-value source-code').textContent = entry.value;
543 } else {
544 div.createChild('div', 'background-service-metadata-value background-service-empty-value').textContent =
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01545 i18nString(UIStrings.empty);
Rayan Kanso4476ca52019-07-22 11:51:24546 }
Rayan Kansoc0bfdd82019-04-24 12:32:22547 preview.element.appendChild(div);
548 }
549
550 if (!preview.element.children.length) {
Tim van der Lippef49e2322020-05-01 15:03:09551 const div = document.createElement('div');
552 div.classList.add('background-service-metadata-entry');
Sigurd Schneiderfec75bb2021-03-19 10:23:34553 div.createChild('div', 'background-service-metadata-name background-service-empty-value').textContent =
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01554 i18nString(UIStrings.noMetadataForThisEvent);
Rayan Kansoc0bfdd82019-04-24 12:32:22555 preview.element.appendChild(div);
556 }
557
558 return preview;
Rayan Kansof40e3152019-03-11 13:49:43559 }
Tim van der Lippe097cdec2020-01-06 14:44:17560}
Rayan Kanso6156d222019-04-29 23:40:55561
Jan Scheffler1026b9d2021-02-26 17:12:00562export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
Jan Scheffler1026b9d2021-02-26 17:12:00563 handleAction(context: UI.Context.Context, actionId: string): boolean {
Tim van der Lippe097cdec2020-01-06 14:44:17564 const view = context.flavor(BackgroundServiceView);
Rayan Kanso6156d222019-04-29 23:40:55565 switch (actionId) {
Alex Rudenko60a3e942020-11-02 12:27:57566 case 'background-service.toggle-recording': {
567 if (!view) {
568 throw new Error('BackgroundServiceView instance is missing');
569 }
Jan Schefflercd49c322021-08-13 18:56:33570 view.toggleRecording();
Rayan Kanso6156d222019-04-29 23:40:55571 return true;
Alex Rudenko60a3e942020-11-02 12:27:57572 }
Rayan Kanso6156d222019-04-29 23:40:55573 }
574 return false;
575 }
Tim van der Lippe097cdec2020-01-06 14:44:17576}
Jan Scheffler1026b9d2021-02-26 17:12:00577export interface RecordingState {
578 isRecording: boolean;
579 serviceName: Protocol.BackgroundService.ServiceName;
580}
581export interface EventData {
582 id: number;
583 timestamp: string;
584 origin: string;
Danil Somsikovf5538f02024-01-17 20:15:17585 'storage-key': string;
586 'sw-scope': string;
587 'event-name': string;
588 'instance-id': string;
Jan Scheffler1026b9d2021-02-26 17:12:00589}