blob: ddd7745dea8431976994a42317791e421a403edc [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
Jan Scheffler1026b9d2021-02-26 17:12:005/* eslint-disable rulesdir/no_underscored_properties */
6
Tim van der Lippe877f04a2021-07-23 11:02:547import type * as Common from '../../core/common/common.js';
Tim van der Lippebb352e62021-04-01 17:57:288import * as i18n from '../../core/i18n/i18n.js';
Tim van der Lippeaa1ed7a2021-03-31 14:38:279import * as Platform from '../../core/platform/platform.js';
Tim van der Lippee00b92f2021-03-31 16:52:1710import * as SDK from '../../core/sdk/sdk.js';
Tim van der Lippe959b6f02021-04-07 09:07:5911import * as Bindings from '../../models/bindings/bindings.js';
Tim van der Lippe8499fe22021-04-12 16:42:4712import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
Tim van der Lippeaa61faf2021-04-07 15:32:0713import * as UI from '../../ui/legacy/legacy.js';
Tim van der Lippe229a54f2021-05-14 16:59:0514import * as Protocol from '../../generated/protocol.js';
Tim van der Lippe0efccf02020-02-12 15:15:3915
Jack Franklina75ae7c2021-05-11 13:22:5416import type {BackgroundServiceModel} from './BackgroundServiceModel.js';
Tim van der Lippec2cbf322021-07-26 14:35:3317import {Events} from './BackgroundServiceModel.js';
Paul Lewisddf58342020-01-15 14:18:0718
Simon Zünd697fb0b2021-03-01 10:12:4219const UIStrings = {
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0120 /**
21 *@description Text in Background Service View of the Application panel
22 */
23 backgroundFetch: 'Background Fetch',
24 /**
25 *@description Text in Background Service View of the Application panel
26 */
27 backgroundSync: 'Background Sync',
28 /**
29 *@description Text in Background Service View of the Application panel
30 */
31 pushMessaging: 'Push Messaging',
32 /**
33 *@description Text in Background Service View of the Application panel
34 */
35 notifications: 'Notifications',
36 /**
37 *@description Text in Background Service View of the Application panel
38 */
39 paymentHandler: 'Payment Handler',
40 /**
41 *@description Text in the Periodic Background Service View of the Application panel
42 */
43 periodicBackgroundSync: 'Periodic Background Sync',
44 /**
45 *@description Text to clear content
46 */
47 clear: 'Clear',
48 /**
49 *@description Tooltip text that appears when hovering over the largeicon download button in the Background Service View of the Application panel
50 */
51 saveEvents: 'Save events',
52 /**
53 *@description Text in Background Service View of the Application panel
54 */
55 showEventsFromOtherDomains: 'Show events from other domains',
56 /**
57 *@description Title of an action under the Background Services category that can be invoked through the Command Menu
58 */
59 stopRecordingEvents: 'Stop recording events',
60 /**
61 *@description Title of an action under the Background Services category that can be invoked through the Command Menu
62 */
63 startRecordingEvents: 'Start recording events',
64 /**
65 *@description Text for timestamps of items
66 */
67 timestamp: 'Timestamp',
68 /**
69 *@description Text that refers to some events
70 */
71 event: 'Event',
72 /**
73 *@description Text for the origin of something
74 */
75 origin: 'Origin',
76 /**
Sigurd Schneiderbee57a82021-02-23 12:58:2477 *@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:0178 */
Sigurd Schneiderbee57a82021-02-23 12:58:2479 swScope: 'Service Worker Scope',
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:0180 /**
81 *@description Text in Background Service View of the Application panel
82 */
83 instanceId: 'Instance ID',
84 /**
85 *@description Text in Application Panel Sidebar of the Application panel
86 */
87 backgroundServices: 'Background Services',
88 /**
89 *@description Text that is usually a hyperlink to more documentation
90 */
91 learnMore: 'Learn more',
92 /**
93 *@description Text in Background Service View of the Application panel
94 */
95 selectAnEntryToViewMetadata: 'Select an entry to view metadata',
96 /**
97 *@description Text in Background Service View of the Application panel
98 *@example {Background Fetch} PH1
99 */
100 recordingSActivity: 'Recording {PH1} activity...',
101 /**
102 *@description Inform users that DevTools are recording/waiting for events in the Periodic Background Sync tool of the Application panel
103 *@example {Background Fetch} PH1
104 */
105 devtoolsWillRecordAllSActivity: 'DevTools will record all {PH1} activity for up to 3 days, even when closed.',
106 /**
107 *@description Text in Background Service View of the Application panel
108 *@example {record} PH1
109 *@example {Ctrl + R} PH2
110 */
111 clickTheRecordButtonSOrHitSTo: 'Click the record button {PH1} or hit {PH2} to start recording.',
112 /**
113 *@description Text to show an item is empty
114 */
115 empty: 'empty',
116 /**
117 *@description Text in Background Service View of the Application panel
118 */
119 noMetadataForThisEvent: 'No metadata for this event',
120};
Tim van der Lippec59708f2021-03-31 15:07:19121const str_ = i18n.i18n.registerUIStrings('panels/application/BackgroundServiceView.ts', UIStrings);
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01122const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Tim van der Lippe0efccf02020-02-12 15:15:39123export class BackgroundServiceView extends UI.Widget.VBox {
Jan Schefflercd49c322021-08-13 18:56:33124 private readonly serviceName: Protocol.BackgroundService.ServiceName;
125 private readonly model: BackgroundServiceModel;
126 private readonly serviceWorkerManager: SDK.ServiceWorkerManager.ServiceWorkerManager|null;
127 private readonly securityOriginManager: SDK.SecurityOriginManager.SecurityOriginManager;
128 private recordAction: UI.ActionRegistration.Action;
129 private recordButton!: UI.Toolbar.ToolbarToggle;
130 private originCheckbox!: UI.Toolbar.ToolbarCheckbox;
131 private saveButton!: UI.Toolbar.ToolbarButton;
132 private readonly toolbar: UI.Toolbar.Toolbar;
133 private readonly splitWidget: UI.SplitWidget.SplitWidget;
134 private readonly dataGrid: DataGrid.DataGrid.DataGridImpl<EventData>;
135 private readonly previewPanel: UI.Widget.VBox;
136 private selectedEventNode: EventDataNode|null;
137 private preview: UI.Widget.Widget|null;
Jan Scheffler1026b9d2021-02-26 17:12:00138
139 static getUIString(serviceName: string): string {
Rayan Kansoc0bfdd82019-04-24 12:32:22140 switch (serviceName) {
141 case Protocol.BackgroundService.ServiceName.BackgroundFetch:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01142 return i18nString(UIStrings.backgroundFetch);
Rayan Kansoc0bfdd82019-04-24 12:32:22143 case Protocol.BackgroundService.ServiceName.BackgroundSync:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01144 return i18nString(UIStrings.backgroundSync);
Fergus Dall0be4f4c2019-05-21 00:01:29145 case Protocol.BackgroundService.ServiceName.PushMessaging:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01146 return i18nString(UIStrings.pushMessaging);
Fergus Dall0be4f4c2019-05-21 00:01:29147 case Protocol.BackgroundService.ServiceName.Notifications:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01148 return i18nString(UIStrings.notifications);
Rayan Kanso8d7918a2019-07-03 21:03:38149 case Protocol.BackgroundService.ServiceName.PaymentHandler:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01150 return i18nString(UIStrings.paymentHandler);
Rayan Kanso54809672019-07-24 18:40:28151 case Protocol.BackgroundService.ServiceName.PeriodicBackgroundSync:
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01152 return i18nString(UIStrings.periodicBackgroundSync);
Rayan Kansoc0bfdd82019-04-24 12:32:22153 default:
154 return '';
155 }
156 }
157
Jan Scheffler1026b9d2021-02-26 17:12:00158 constructor(serviceName: Protocol.BackgroundService.ServiceName, model: BackgroundServiceModel) {
Rayan Kanso68904202019-02-21 14:16:25159 super(true);
Jack Franklin84442c62021-06-28 15:02:06160 this.registerRequiredCSS('panels/application/backgroundServiceView.css');
161 this.registerRequiredCSS('ui/legacy/emptyWidget.css');
Rayan Kanso68904202019-02-21 14:16:25162
Jan Schefflercd49c322021-08-13 18:56:33163 this.serviceName = serviceName;
Rayan Kanso68904202019-02-21 14:16:25164
Jan Schefflercd49c322021-08-13 18:56:33165 this.model = model;
166 this.model.addEventListener(Events.RecordingStateChanged, this.onRecordingStateChanged, this);
167 this.model.addEventListener(Events.BackgroundServiceEventReceived, this.onEventReceived, this);
168 this.model.enable(this.serviceName);
Rayan Kanso8fe8ee22019-03-04 14:58:46169
Jan Schefflercd49c322021-08-13 18:56:33170 this.serviceWorkerManager = this.model.target().model(SDK.ServiceWorkerManager.ServiceWorkerManager);
Rayan Kansoaca06e72019-03-27 11:57:06171
Jan Schefflercd49c322021-08-13 18:56:33172 this.securityOriginManager = this.model.target().model(SDK.SecurityOriginManager.SecurityOriginManager) as
Jan Scheffler1026b9d2021-02-26 17:12:00173 SDK.SecurityOriginManager.SecurityOriginManager;
Jan Schefflercd49c322021-08-13 18:56:33174 if (!this.securityOriginManager) {
Alex Rudenko60a3e942020-11-02 12:27:57175 throw new Error('SecurityOriginManager instance is missing');
176 }
Jan Schefflercd49c322021-08-13 18:56:33177 this.securityOriginManager.addEventListener(
178 SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, () => this.onOriginChanged());
179 this.recordAction =
Jan Scheffler1026b9d2021-02-26 17:12:00180 (UI.ActionRegistry.ActionRegistry.instance().action('background-service.toggle-recording') as
181 UI.ActionRegistration.Action);
Rayan Kanso8fe8ee22019-03-04 14:58:46182
Jan Schefflercd49c322021-08-13 18:56:33183 this.toolbar = new UI.Toolbar.Toolbar('background-service-toolbar', this.contentElement);
184 this.setupToolbar();
Rayan Kanso3252d5e2019-03-27 11:37:24185
Rayan Kansob451b4f2019-04-04 23:12:11186 /**
187 * This will contain the DataGrid for displaying events, and a panel at the bottom for showing
188 * extra metadata related to the selected event.
Rayan Kansob451b4f2019-04-04 23:12:11189 */
Jan Schefflercd49c322021-08-13 18:56:33190 this.splitWidget = new UI.SplitWidget.SplitWidget(/* isVertical= */ false, /* secondIsSidebar= */ true);
191 this.splitWidget.show(this.contentElement);
Rayan Kansob451b4f2019-04-04 23:12:11192
Jan Schefflercd49c322021-08-13 18:56:33193 this.dataGrid = this.createDataGrid();
Rayan Kansob451b4f2019-04-04 23:12:11194
Jan Schefflercd49c322021-08-13 18:56:33195 this.previewPanel = new UI.Widget.VBox();
Rayan Kansob451b4f2019-04-04 23:12:11196
Jan Schefflercd49c322021-08-13 18:56:33197 this.selectedEventNode = null;
Rayan Kansoc0bfdd82019-04-24 12:32:22198
Jan Schefflercd49c322021-08-13 18:56:33199 this.preview = null;
Rayan Kansob451b4f2019-04-04 23:12:11200
Jan Schefflercd49c322021-08-13 18:56:33201 this.splitWidget.setMainWidget(this.dataGrid.asWidget());
202 this.splitWidget.setSidebarWidget(this.previewPanel);
Rayan Kansob451b4f2019-04-04 23:12:11203
Jan Schefflercd49c322021-08-13 18:56:33204 this.showPreview(null);
Rayan Kanso68904202019-02-21 14:16:25205 }
206
207 /**
208 * Creates the toolbar UI element.
209 */
Jan Schefflercd49c322021-08-13 18:56:33210 private async setupToolbar(): Promise<void> {
211 this.recordButton = (UI.Toolbar.Toolbar.createActionButton(this.recordAction) as UI.Toolbar.ToolbarToggle);
212 this.toolbar.appendToolbarItem(this.recordButton);
Rayan Kanso68904202019-02-21 14:16:25213
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01214 const clearButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.clear), 'largeicon-clear');
Jan Schefflercd49c322021-08-13 18:56:33215 clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => this.clearEvents());
216 this.toolbar.appendToolbarItem(clearButton);
Rayan Kanso68904202019-02-21 14:16:25217
Jan Schefflercd49c322021-08-13 18:56:33218 this.toolbar.appendSeparator();
Rayan Kanso68904202019-02-21 14:16:25219
Jan Schefflercd49c322021-08-13 18:56:33220 this.saveButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.saveEvents), 'largeicon-download');
221 this.saveButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, _event => {
222 this.saveToFile();
Tim van der Lippe37a35ff2020-03-03 13:49:02223 });
Jan Schefflercd49c322021-08-13 18:56:33224 this.saveButton.setEnabled(false);
225 this.toolbar.appendToolbarItem(this.saveButton);
Rayan Kansoc0bfdd82019-04-24 12:32:22226
Jan Schefflercd49c322021-08-13 18:56:33227 this.toolbar.appendSeparator();
Rayan Kansoc0bfdd82019-04-24 12:32:22228
Jan Schefflercd49c322021-08-13 18:56:33229 this.originCheckbox = new UI.Toolbar.ToolbarCheckbox(
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01230 i18nString(UIStrings.showEventsFromOtherDomains), i18nString(UIStrings.showEventsFromOtherDomains),
Jan Schefflercd49c322021-08-13 18:56:33231 () => this.refreshView());
232 this.toolbar.appendToolbarItem(this.originCheckbox);
Rayan Kanso68904202019-02-21 14:16:25233 }
Rayan Kanso8fe8ee22019-03-04 14:58:46234
235 /**
Rayan Kansob451b4f2019-04-04 23:12:11236 * Displays all available events in the grid.
237 */
Jan Schefflercd49c322021-08-13 18:56:33238 private refreshView(): void {
239 this.clearView();
240 const events = this.model.getEvents(this.serviceName).filter(event => this.acceptEvent(event));
Tim van der Lippe1d6e57a2019-09-30 11:55:34241 for (const event of events) {
Jan Schefflercd49c322021-08-13 18:56:33242 this.addEvent(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:34243 }
Rayan Kansob451b4f2019-04-04 23:12:11244 }
245
246 /**
247 * Clears the grid and panel.
248 */
Jan Schefflercd49c322021-08-13 18:56:33249 private clearView(): void {
250 this.selectedEventNode = null;
251 this.dataGrid.rootNode().removeChildren();
252 this.saveButton.setEnabled(false);
253 this.showPreview(null);
Rayan Kansob451b4f2019-04-04 23:12:11254 }
255
256 /**
Rayan Kanso8fe8ee22019-03-04 14:58:46257 * Called when the `Toggle Record` button is clicked.
258 */
Jan Schefflercd49c322021-08-13 18:56:33259 toggleRecording(): void {
260 this.model.setRecording(!this.recordButton.toggled(), this.serviceName);
Rayan Kanso8fe8ee22019-03-04 14:58:46261 }
262
263 /**
Rayan Kanso3252d5e2019-03-27 11:37:24264 * Called when the `Clear` button is clicked.
265 */
Jan Schefflercd49c322021-08-13 18:56:33266 private clearEvents(): void {
267 this.model.clearEvents(this.serviceName);
268 this.clearView();
Rayan Kanso3252d5e2019-03-27 11:37:24269 }
270
Jan Schefflercd49c322021-08-13 18:56:33271 private onRecordingStateChanged(event: Common.EventTarget.EventTargetEvent): void {
Jan Scheffler1026b9d2021-02-26 17:12:00272 const state = (event.data as RecordingState);
Jan Schefflercd49c322021-08-13 18:56:33273 if (state.serviceName !== this.serviceName) {
Rayan Kanso8fe8ee22019-03-04 14:58:46274 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34275 }
Rayan Kansoc0bfdd82019-04-24 12:32:22276
Jan Schefflercd49c322021-08-13 18:56:33277 if (state.isRecording === this.recordButton.toggled()) {
Rayan Kansoc0bfdd82019-04-24 12:32:22278 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34279 }
Rayan Kansoc0bfdd82019-04-24 12:32:22280
Jan Schefflercd49c322021-08-13 18:56:33281 this.recordButton.setToggled(state.isRecording);
282 this.updateRecordButtonTooltip();
283 this.showPreview(this.selectedEventNode);
Rayan Kanso8fe8ee22019-03-04 14:58:46284 }
Rayan Kansof40e3152019-03-11 13:49:43285
Jan Schefflercd49c322021-08-13 18:56:33286 private updateRecordButtonTooltip(): void {
287 const buttonTooltip = this.recordButton.toggled() ? i18nString(UIStrings.stopRecordingEvents) :
288 i18nString(UIStrings.startRecordingEvents);
289 this.recordButton.setTitle(buttonTooltip, 'background-service.toggle-recording');
Christy Chen13a12ff2020-08-20 07:43:26290 }
291
Jan Schefflercd49c322021-08-13 18:56:33292 private onEventReceived(event: Common.EventTarget.EventTargetEvent): void {
Jan Scheffler1026b9d2021-02-26 17:12:00293 const serviceEvent = (event.data as Protocol.BackgroundService.BackgroundServiceEvent);
Jan Schefflercd49c322021-08-13 18:56:33294 if (!this.acceptEvent(serviceEvent)) {
Rayan Kansof40e3152019-03-11 13:49:43295 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34296 }
Jan Schefflercd49c322021-08-13 18:56:33297 this.addEvent(serviceEvent);
Rayan Kanso3252d5e2019-03-27 11:37:24298 }
299
Jan Schefflercd49c322021-08-13 18:56:33300 private onOriginChanged(): void {
Rayan Kansoaca06e72019-03-27 11:57:06301 // No need to refresh the view if we are already showing all events.
Jan Schefflercd49c322021-08-13 18:56:33302 if (this.originCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06303 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34304 }
Jan Schefflercd49c322021-08-13 18:56:33305 this.refreshView();
Rayan Kansoaca06e72019-03-27 11:57:06306 }
307
Jan Schefflercd49c322021-08-13 18:56:33308 private addEvent(serviceEvent: Protocol.BackgroundService.BackgroundServiceEvent): void {
309 const data = this.createEventData(serviceEvent);
Tim van der Lippe097cdec2020-01-06 14:44:17310 const dataNode = new EventDataNode(data, serviceEvent.eventMetadata);
Jan Schefflercd49c322021-08-13 18:56:33311 this.dataGrid.rootNode().appendChild(dataNode);
Rayan Kansob852ba82019-04-08 13:48:07312
Jan Schefflercd49c322021-08-13 18:56:33313 if (this.dataGrid.rootNode().children.length === 1) {
314 this.saveButton.setEnabled(true);
315 this.showPreview(this.selectedEventNode);
Rayan Kansoc0bfdd82019-04-24 12:32:22316 }
Rayan Kanso3252d5e2019-03-27 11:37:24317 }
318
Jan Schefflercd49c322021-08-13 18:56:33319 private createDataGrid(): DataGrid.DataGrid.DataGridImpl<EventData> {
Jan Scheffler1026b9d2021-02-26 17:12:00320 const columns = ([
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01321 {id: 'id', title: '#', weight: 1},
322 {id: 'timestamp', title: i18nString(UIStrings.timestamp), weight: 8},
323 {id: 'eventName', title: i18nString(UIStrings.event), weight: 10},
324 {id: 'origin', title: i18nString(UIStrings.origin), weight: 10},
Sigurd Schneiderbee57a82021-02-23 12:58:24325 {id: 'swScope', title: i18nString(UIStrings.swScope), weight: 5},
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01326 {id: 'instanceId', title: i18nString(UIStrings.instanceId), weight: 10},
Jan Scheffler1026b9d2021-02-26 17:12:00327 ] as DataGrid.DataGrid.ColumnDescriptor[]);
Alex Rudenko60a3e942020-11-02 12:27:57328 const dataGrid = new DataGrid.DataGrid.DataGridImpl({
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01329 displayName: i18nString(UIStrings.backgroundServices),
Alex Rudenko60a3e942020-11-02 12:27:57330 columns,
331 editCallback: undefined,
332 refreshCallback: undefined,
Jan Scheffler1026b9d2021-02-26 17:12:00333 deleteCallback: undefined,
Alex Rudenko60a3e942020-11-02 12:27:57334 });
Rayan Kanso3252d5e2019-03-27 11:37:24335 dataGrid.setStriped(true);
Rayan Kansob451b4f2019-04-04 23:12:11336
337 dataGrid.addEventListener(
Jan Schefflercd49c322021-08-13 18:56:33338 DataGrid.DataGrid.Events.SelectedNode, event => this.showPreview((event.data as EventDataNode)));
Rayan Kansob451b4f2019-04-04 23:12:11339
Rayan Kanso3252d5e2019-03-27 11:37:24340 return dataGrid;
341 }
342
343 /**
Rayan Kansoaca06e72019-03-27 11:57:06344 * Creates the data object to pass to the DataGrid Node.
Rayan Kansoaca06e72019-03-27 11:57:06345 */
Jan Schefflercd49c322021-08-13 18:56:33346 private createEventData(serviceEvent: Protocol.BackgroundService.BackgroundServiceEvent): EventData {
Rayan Kansocc02ea32019-05-02 21:26:52347 let swScope = '';
Rayan Kansoaca06e72019-03-27 11:57:06348
Rayan Kansocc02ea32019-05-02 21:26:52349 // Try to get the scope of the Service Worker registration to be more user-friendly.
Jan Schefflercd49c322021-08-13 18:56:33350 const registration = this.serviceWorkerManager ?
351 this.serviceWorkerManager.registrations().get(serviceEvent.serviceWorkerRegistrationId) :
Alex Rudenko60a3e942020-11-02 12:27:57352 undefined;
Tim van der Lippe1d6e57a2019-09-30 11:55:34353 if (registration) {
Rayan Kansocc02ea32019-05-02 21:26:52354 swScope = registration.scopeURL.substr(registration.securityOrigin.length);
Tim van der Lippe1d6e57a2019-09-30 11:55:34355 }
Rayan Kansoaca06e72019-03-27 11:57:06356
357 return {
Jan Schefflercd49c322021-08-13 18:56:33358 id: this.dataGrid.rootNode().children.length + 1,
Tim van der Lippe0efccf02020-02-12 15:15:39359 timestamp: UI.UIUtils.formatTimestamp(serviceEvent.timestamp * 1000, /* full= */ true),
Rayan Kansoaca06e72019-03-27 11:57:06360 origin: serviceEvent.origin,
Rayan Kansocc02ea32019-05-02 21:26:52361 swScope,
Rayan Kansoaca06e72019-03-27 11:57:06362 eventName: serviceEvent.eventName,
363 instanceId: serviceEvent.instanceId,
364 };
365 }
366
367 /**
Rayan Kanso3252d5e2019-03-27 11:37:24368 * Filtration function to know whether event should be shown or not.
Rayan Kanso3252d5e2019-03-27 11:37:24369 */
Jan Schefflercd49c322021-08-13 18:56:33370 private acceptEvent(event: Protocol.BackgroundService.BackgroundServiceEvent): boolean {
371 if (event.service !== this.serviceName) {
Rayan Kansoaca06e72019-03-27 11:57:06372 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34373 }
Rayan Kansoaca06e72019-03-27 11:57:06374
Jan Schefflercd49c322021-08-13 18:56:33375 if (this.originCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06376 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34377 }
Rayan Kansoaca06e72019-03-27 11:57:06378
379 // Trim the trailing '/'.
380 const origin = event.origin.substr(0, event.origin.length - 1);
381
Jan Schefflercd49c322021-08-13 18:56:33382 return this.securityOriginManager.securityOrigins().includes(origin);
Rayan Kanso3252d5e2019-03-27 11:37:24383 }
Rayan Kansob451b4f2019-04-04 23:12:11384
Jan Schefflercd49c322021-08-13 18:56:33385 private createLearnMoreLink(): Element {
Jecelyn90c04162021-04-07 08:41:35386 let url = 'https://developer.chrome.com/docs/devtools/javascript/background-services/?utm_source=devtools';
Rayan Kanso42c0f9e2019-09-02 12:21:09387
Jan Schefflercd49c322021-08-13 18:56:33388 switch (this.serviceName) {
Rayan Kanso42c0f9e2019-09-02 12:21:09389 case Protocol.BackgroundService.ServiceName.BackgroundFetch:
390 url += '#fetch';
391 break;
392 case Protocol.BackgroundService.ServiceName.BackgroundSync:
393 url += '#sync';
394 break;
395 case Protocol.BackgroundService.ServiceName.PushMessaging:
396 url += '#push';
397 break;
398 case Protocol.BackgroundService.ServiceName.Notifications:
399 url += '#notifications';
400 break;
401 default:
402 break;
403 }
404
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01405 return UI.XLink.XLink.create(url, i18nString(UIStrings.learnMore));
Rayan Kanso42c0f9e2019-09-02 12:21:09406 }
407
Jan Schefflercd49c322021-08-13 18:56:33408 private showPreview(dataNode: EventDataNode|null): void {
409 if (this.selectedEventNode && this.selectedEventNode === dataNode) {
Rayan Kansoc0bfdd82019-04-24 12:32:22410 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34411 }
Rayan Kansoc0bfdd82019-04-24 12:32:22412
Jan Schefflercd49c322021-08-13 18:56:33413 this.selectedEventNode = dataNode;
Rayan Kansoc0bfdd82019-04-24 12:32:22414
Jan Schefflercd49c322021-08-13 18:56:33415 if (this.preview) {
416 this.preview.detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34417 }
Rayan Kansob451b4f2019-04-04 23:12:11418
Jan Schefflercd49c322021-08-13 18:56:33419 if (this.selectedEventNode) {
420 this.preview = this.selectedEventNode.createPreview();
421 this.preview.show(this.previewPanel.contentElement);
Rayan Kanso4476ca52019-07-22 11:51:24422 return;
423 }
424
Jan Schefflercd49c322021-08-13 18:56:33425 this.preview = new UI.Widget.VBox();
426 this.preview.contentElement.classList.add('background-service-preview', 'fill');
427 const centered = this.preview.contentElement.createChild('div');
Rayan Kanso4476ca52019-07-22 11:51:24428
Jan Schefflercd49c322021-08-13 18:56:33429 if (this.dataGrid.rootNode().children.length) {
Rayan Kansoc0bfdd82019-04-24 12:32:22430 // Inform users that grid entries are clickable.
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01431 centered.createChild('p').textContent = i18nString(UIStrings.selectAnEntryToViewMetadata);
Jan Schefflercd49c322021-08-13 18:56:33432 } else if (this.recordButton.toggled()) {
Rayan Kansoc0bfdd82019-04-24 12:32:22433 // Inform users that we are recording/waiting for events.
Jan Schefflercd49c322021-08-13 18:56:33434 const featureName = BackgroundServiceView.getUIString(this.serviceName);
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01435 centered.createChild('p').textContent = i18nString(UIStrings.recordingSActivity, {PH1: featureName});
436 centered.createChild('p').textContent = i18nString(UIStrings.devtoolsWillRecordAllSActivity, {PH1: featureName});
Rayan Kansoc0bfdd82019-04-24 12:32:22437 } else {
Jan Schefflercd49c322021-08-13 18:56:33438 const landingRecordButton = UI.Toolbar.Toolbar.createActionButton(this.recordAction);
Rayan Kansoc0bfdd82019-04-24 12:32:22439
Tim van der Lippef49e2322020-05-01 15:03:09440 const recordKey = document.createElement('b');
441 recordKey.classList.add('background-service-shortcut');
Tim van der Lippe9c9fb122020-09-08 15:06:17442 recordKey.textContent = UI.ShortcutRegistry.ShortcutRegistry.instance()
443 .shortcutsForAction('background-service.toggle-recording')[0]
444 .title();
Rayan Kanso6156d222019-04-29 23:40:55445
Tim van der Lippe0efccf02020-02-12 15:15:39446 const inlineButton = UI.UIUtils.createInlineButton(landingRecordButton);
Rayan Kansof402ef32019-08-12 13:05:18447 inlineButton.classList.add('background-service-record-inline-button');
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01448 centered.createChild('p').appendChild(i18n.i18n.getFormatLocalizedString(
449 str_, UIStrings.clickTheRecordButtonSOrHitSTo, {PH1: inlineButton, PH2: recordKey}));
Rayan Kanso42c0f9e2019-09-02 12:21:09450
Jan Schefflercd49c322021-08-13 18:56:33451 centered.appendChild(this.createLearnMoreLink());
Rayan Kansoc0bfdd82019-04-24 12:32:22452 }
Rayan Kansob451b4f2019-04-04 23:12:11453
Jan Schefflercd49c322021-08-13 18:56:33454 this.preview.show(this.previewPanel.contentElement);
Rayan Kansob451b4f2019-04-04 23:12:11455 }
Rayan Kansob852ba82019-04-08 13:48:07456
457 /**
458 * Saves all currently displayed events in a file (JSON format).
459 */
Jan Schefflercd49c322021-08-13 18:56:33460 private async saveToFile(): Promise<void> {
461 const fileName = `${this.serviceName}-${Platform.DateUtilities.toISO8601Compact(new Date())}.json`;
Tim van der Lippe0efccf02020-02-12 15:15:39462 const stream = new Bindings.FileUtils.FileOutputStream();
Rayan Kansob852ba82019-04-08 13:48:07463
464 const accepted = await stream.open(fileName);
Tim van der Lippe1d6e57a2019-09-30 11:55:34465 if (!accepted) {
Rayan Kansob852ba82019-04-08 13:48:07466 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34467 }
Rayan Kansob852ba82019-04-08 13:48:07468
Jan Schefflercd49c322021-08-13 18:56:33469 const events = this.model.getEvents(this.serviceName).filter(event => this.acceptEvent(event));
Rayan Kansob852ba82019-04-08 13:48:07470 await stream.write(JSON.stringify(events, undefined, 2));
471 stream.close();
472 }
Tim van der Lippe097cdec2020-01-06 14:44:17473}
Rayan Kanso3252d5e2019-03-27 11:37:24474
Jan Scheffler1026b9d2021-02-26 17:12:00475export class EventDataNode extends DataGrid.DataGrid.DataGridNode<EventData> {
Jan Schefflercd49c322021-08-13 18:56:33476 private readonly eventMetadata: Protocol.BackgroundService.EventMetadata[];
Jan Scheffler1026b9d2021-02-26 17:12:00477
478 constructor(data: EventData, eventMetadata: Protocol.BackgroundService.EventMetadata[]) {
Rayan Kansoaca06e72019-03-27 11:57:06479 super(data);
Rayan Kanso3252d5e2019-03-27 11:37:24480
Jan Schefflercd49c322021-08-13 18:56:33481 this.eventMetadata = eventMetadata.sort((m1, m2) => Platform.StringUtilities.compare(m1.key, m2.key));
Rayan Kanso3252d5e2019-03-27 11:37:24482 }
483
Jan Scheffler1026b9d2021-02-26 17:12:00484 createPreview(): UI.Widget.VBox {
Tim van der Lippe0efccf02020-02-12 15:15:39485 const preview = new UI.Widget.VBox();
Rayan Kansoc0bfdd82019-04-24 12:32:22486 preview.element.classList.add('background-service-metadata');
487
Jan Schefflercd49c322021-08-13 18:56:33488 for (const entry of this.eventMetadata) {
Tim van der Lippef49e2322020-05-01 15:03:09489 const div = document.createElement('div');
490 div.classList.add('background-service-metadata-entry');
Rayan Kansoc0bfdd82019-04-24 12:32:22491 div.createChild('div', 'background-service-metadata-name').textContent = entry.key + ': ';
Rayan Kanso4476ca52019-07-22 11:51:24492 if (entry.value) {
493 div.createChild('div', 'background-service-metadata-value source-code').textContent = entry.value;
494 } else {
495 div.createChild('div', 'background-service-metadata-value background-service-empty-value').textContent =
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01496 i18nString(UIStrings.empty);
Rayan Kanso4476ca52019-07-22 11:51:24497 }
Rayan Kansoc0bfdd82019-04-24 12:32:22498 preview.element.appendChild(div);
499 }
500
501 if (!preview.element.children.length) {
Tim van der Lippef49e2322020-05-01 15:03:09502 const div = document.createElement('div');
503 div.classList.add('background-service-metadata-entry');
Sigurd Schneiderfec75bb2021-03-19 10:23:34504 div.createChild('div', 'background-service-metadata-name background-service-empty-value').textContent =
Vidal Guillermo Diazleal Ortega0a997bb2021-02-09 04:33:01505 i18nString(UIStrings.noMetadataForThisEvent);
Rayan Kansoc0bfdd82019-04-24 12:32:22506 preview.element.appendChild(div);
507 }
508
509 return preview;
Rayan Kansof40e3152019-03-11 13:49:43510 }
Tim van der Lippe097cdec2020-01-06 14:44:17511}
Rayan Kanso6156d222019-04-29 23:40:55512
Jan Scheffler1026b9d2021-02-26 17:12:00513let actionDelegateInstance: ActionDelegate;
Andres Olivares79578512021-02-02 00:27:49514
Jan Scheffler1026b9d2021-02-26 17:12:00515export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
516 static instance(opts: {
517 forceNew: boolean|null,
518 } = {forceNew: null}): ActionDelegate {
Andres Olivares79578512021-02-02 00:27:49519 const {forceNew} = opts;
520 if (!actionDelegateInstance || forceNew) {
521 actionDelegateInstance = new ActionDelegate();
522 }
523
524 return actionDelegateInstance;
525 }
526
Jan Scheffler1026b9d2021-02-26 17:12:00527 handleAction(context: UI.Context.Context, actionId: string): boolean {
Tim van der Lippe097cdec2020-01-06 14:44:17528 const view = context.flavor(BackgroundServiceView);
Rayan Kanso6156d222019-04-29 23:40:55529 switch (actionId) {
Alex Rudenko60a3e942020-11-02 12:27:57530 case 'background-service.toggle-recording': {
531 if (!view) {
532 throw new Error('BackgroundServiceView instance is missing');
533 }
Jan Schefflercd49c322021-08-13 18:56:33534 view.toggleRecording();
Rayan Kanso6156d222019-04-29 23:40:55535 return true;
Alex Rudenko60a3e942020-11-02 12:27:57536 }
Rayan Kanso6156d222019-04-29 23:40:55537 }
538 return false;
539 }
Tim van der Lippe097cdec2020-01-06 14:44:17540}
Jan Scheffler1026b9d2021-02-26 17:12:00541export interface RecordingState {
542 isRecording: boolean;
543 serviceName: Protocol.BackgroundService.ServiceName;
544}
545export interface EventData {
546 id: number;
547 timestamp: string;
548 origin: string;
549 swScope: string;
550 eventName: string;
551 instanceId: string;
552}