blob: 9401c401e9f7d5b2775d8f0fa71a874a9885614a [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 Lippe0efccf02020-02-12 15:15:395import * as Bindings from '../bindings/bindings.js';
Tim van der Lippec02a97c2020-02-14 14:39:276import * as Common from '../common/common.js'; // eslint-disable-line no-unused-vars
Tim van der Lippe0efccf02020-02-12 15:15:397import * as DataGrid from '../data_grid/data_grid.js';
8import * as SDK from '../sdk/sdk.js';
9import * as UI from '../ui/ui.js';
10
Paul Lewisddf58342020-01-15 14:18:0711import {BackgroundServiceModel, Events} from './BackgroundServiceModel.js'; // eslint-disable-line no-unused-vars
12
Tim van der Lippe0efccf02020-02-12 15:15:3913export class BackgroundServiceView extends UI.Widget.VBox {
Rayan Kanso68904202019-02-21 14:16:2514 /**
Rayan Kansoc0bfdd82019-04-24 12:32:2215 * @param {string} serviceName The name of the background service.
16 * @return {string} The UI String to display.
17 */
18 static getUIString(serviceName) {
19 switch (serviceName) {
20 case Protocol.BackgroundService.ServiceName.BackgroundFetch:
21 return ls`Background Fetch`;
22 case Protocol.BackgroundService.ServiceName.BackgroundSync:
23 return ls`Background Sync`;
Fergus Dall0be4f4c2019-05-21 00:01:2924 case Protocol.BackgroundService.ServiceName.PushMessaging:
25 return ls`Push Messaging`;
26 case Protocol.BackgroundService.ServiceName.Notifications:
27 return ls`Notifications`;
Rayan Kanso8d7918a2019-07-03 21:03:3828 case Protocol.BackgroundService.ServiceName.PaymentHandler:
29 return ls`Payment Handler`;
Rayan Kanso54809672019-07-24 18:40:2830 case Protocol.BackgroundService.ServiceName.PeriodicBackgroundSync:
31 return ls`Periodic Background Sync`;
Rayan Kansoc0bfdd82019-04-24 12:32:2232 default:
33 return '';
34 }
35 }
36
37 /**
Rayan Kanso8fe8ee22019-03-04 14:58:4638 * @param {!Protocol.BackgroundService.ServiceName} serviceName
Paul Lewisddf58342020-01-15 14:18:0739 * @param {!BackgroundServiceModel} model
Rayan Kanso68904202019-02-21 14:16:2540 */
Rayan Kanso8fe8ee22019-03-04 14:58:4641 constructor(serviceName, model) {
Rayan Kanso68904202019-02-21 14:16:2542 super(true);
43 this.registerRequiredCSS('resources/backgroundServiceView.css');
Rayan Kanso6156d222019-04-29 23:40:5544 this.registerRequiredCSS('ui/emptyWidget.css');
Rayan Kanso68904202019-02-21 14:16:2545
Rayan Kanso8fe8ee22019-03-04 14:58:4646 /** @const {!Protocol.BackgroundService.ServiceName} */
Rayan Kanso68904202019-02-21 14:16:2547 this._serviceName = serviceName;
48
Paul Lewisddf58342020-01-15 14:18:0749 /** @const {!BackgroundServiceModel} */
Rayan Kanso8fe8ee22019-03-04 14:58:4650 this._model = model;
Paul Lewisddf58342020-01-15 14:18:0751 this._model.addEventListener(Events.RecordingStateChanged, this._onRecordingStateChanged, this);
52 this._model.addEventListener(Events.BackgroundServiceEventReceived, this._onEventReceived, this);
Rayan Kanso8fe8ee22019-03-04 14:58:4653 this._model.enable(this._serviceName);
54
Tim van der Lippe0efccf02020-02-12 15:15:3955 /** @const {?SDK.ServiceWorkerManager.ServiceWorkerManager} */
56 this._serviceWorkerManager = this._model.target().model(SDK.ServiceWorkerManager.ServiceWorkerManager);
Rayan Kansoaca06e72019-03-27 11:57:0657
Tim van der Lippe0efccf02020-02-12 15:15:3958 /** @const {?SDK.SecurityOriginManager.SecurityOriginManager} */
59 this._securityOriginManager = this._model.target().model(SDK.SecurityOriginManager.SecurityOriginManager);
Rayan Kansoaca06e72019-03-27 11:57:0660 this._securityOriginManager.addEventListener(
61 SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, () => this._onOriginChanged());
62
Rayan Kansocc02ea32019-05-02 21:26:5263
Tim van der Lippe0efccf02020-02-12 15:15:3964 /** @const {!UI.Action.Action} */
Paul Lewis24cb7402020-01-24 13:46:3565 this._recordAction =
Tim van der Lippe0efccf02020-02-12 15:15:3966 /** @type {!UI.Action.Action} */ (self.UI.actionRegistry.action('background-service.toggle-recording'));
67 /** @type {?UI.Toolbar.ToolbarButton} */
Rayan Kanso8fe8ee22019-03-04 14:58:4668 this._recordButton = null;
69
Tim van der Lippe0efccf02020-02-12 15:15:3970 /** @type {?UI.Toolbar.ToolbarCheckbox} */
Rayan Kansoaca06e72019-03-27 11:57:0671 this._originCheckbox = null;
72
Tim van der Lippe0efccf02020-02-12 15:15:3973 /** @type {?UI.Toolbar.ToolbarButton} */
Rayan Kansob852ba82019-04-08 13:48:0774 this._saveButton = null;
75
Tim van der Lippe0efccf02020-02-12 15:15:3976 /** @const {!UI.Toolbar.Toolbar} */
77 this._toolbar = new UI.Toolbar.Toolbar('background-service-toolbar', this.contentElement);
Rayan Kanso68904202019-02-21 14:16:2578 this._setupToolbar();
Rayan Kanso3252d5e2019-03-27 11:37:2479
Rayan Kansob451b4f2019-04-04 23:12:1180 /**
81 * This will contain the DataGrid for displaying events, and a panel at the bottom for showing
82 * extra metadata related to the selected event.
Tim van der Lippe0efccf02020-02-12 15:15:3983 * @const {!UI.SplitWidget.SplitWidget}
Rayan Kansob451b4f2019-04-04 23:12:1184 */
Tim van der Lippe0efccf02020-02-12 15:15:3985 this._splitWidget = new UI.SplitWidget.SplitWidget(/* isVertical= */ false, /* secondIsSidebar= */ true);
Rayan Kansob451b4f2019-04-04 23:12:1186 this._splitWidget.show(this.contentElement);
87
Tim van der Lippe0efccf02020-02-12 15:15:3988 /** @const {!DataGrid.DataGrid.DataGridImpl} */
Rayan Kanso3252d5e2019-03-27 11:37:2489 this._dataGrid = this._createDataGrid();
Rayan Kansob451b4f2019-04-04 23:12:1190
Tim van der Lippe0efccf02020-02-12 15:15:3991 /** @const {!UI.Widget.VBox} */
92 this._previewPanel = new UI.Widget.VBox();
Rayan Kansob451b4f2019-04-04 23:12:1193
Tim van der Lippe097cdec2020-01-06 14:44:1794 /** @type {?EventDataNode} */
Rayan Kansoc0bfdd82019-04-24 12:32:2295 this._selectedEventNode = null;
96
Tim van der Lippe0efccf02020-02-12 15:15:3997 /** @type {?UI.Widget.Widget} */
Rayan Kansob451b4f2019-04-04 23:12:1198 this._preview = null;
99
100 this._splitWidget.setMainWidget(this._dataGrid.asWidget());
101 this._splitWidget.setSidebarWidget(this._previewPanel);
102
103 this._showPreview(null);
Rayan Kanso68904202019-02-21 14:16:25104 }
105
106 /**
107 * Creates the toolbar UI element.
108 */
Rayan Kanso8fe8ee22019-03-04 14:58:46109 async _setupToolbar() {
Tim van der Lippe0efccf02020-02-12 15:15:39110 this._recordButton = UI.Toolbar.Toolbar.createActionButton(this._recordAction);
Rayan Kanso8fe8ee22019-03-04 14:58:46111 this._toolbar.appendToolbarItem(this._recordButton);
Rayan Kanso68904202019-02-21 14:16:25112
Tim van der Lippe0efccf02020-02-12 15:15:39113 const clearButton = new UI.Toolbar.ToolbarButton(ls`Clear`, 'largeicon-clear');
114 clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => this._clearEvents());
Rayan Kanso68904202019-02-21 14:16:25115 this._toolbar.appendToolbarItem(clearButton);
116
117 this._toolbar.appendSeparator();
118
Tim van der Lippe0efccf02020-02-12 15:15:39119 this._saveButton = new UI.Toolbar.ToolbarButton(ls`Save events`, 'largeicon-download');
120 this._saveButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, () => this._saveToFile());
Rayan Kansob852ba82019-04-08 13:48:07121 this._saveButton.setEnabled(false);
122 this._toolbar.appendToolbarItem(this._saveButton);
Rayan Kansoc0bfdd82019-04-24 12:32:22123
124 this._toolbar.appendSeparator();
125
Tim van der Lippe0efccf02020-02-12 15:15:39126 this._originCheckbox = new UI.Toolbar.ToolbarCheckbox(
Rayan Kansoe9d4f692020-02-05 13:46:10127 ls`Show events from other domains`, ls`Show events from other domains`, () => this._refreshView());
Rayan Kansoc0bfdd82019-04-24 12:32:22128 this._toolbar.appendToolbarItem(this._originCheckbox);
Rayan Kanso68904202019-02-21 14:16:25129 }
Rayan Kanso8fe8ee22019-03-04 14:58:46130
131 /**
Rayan Kansob451b4f2019-04-04 23:12:11132 * Displays all available events in the grid.
133 */
134 _refreshView() {
135 this._clearView();
136 const events = this._model.getEvents(this._serviceName).filter(event => this._acceptEvent(event));
Tim van der Lippe1d6e57a2019-09-30 11:55:34137 for (const event of events) {
Rayan Kansob451b4f2019-04-04 23:12:11138 this._addEvent(event);
Tim van der Lippe1d6e57a2019-09-30 11:55:34139 }
Rayan Kansob451b4f2019-04-04 23:12:11140 }
141
142 /**
143 * Clears the grid and panel.
144 */
145 _clearView() {
Rayan Kansoc0bfdd82019-04-24 12:32:22146 this._selectedEventNode = null;
Rayan Kansob451b4f2019-04-04 23:12:11147 this._dataGrid.rootNode().removeChildren();
Rayan Kansob852ba82019-04-08 13:48:07148 this._saveButton.setEnabled(false);
Rayan Kansoc0bfdd82019-04-24 12:32:22149 this._showPreview(null);
Rayan Kansob451b4f2019-04-04 23:12:11150 }
151
152 /**
Rayan Kanso8fe8ee22019-03-04 14:58:46153 * Called when the `Toggle Record` button is clicked.
154 */
155 _toggleRecording() {
156 this._model.setRecording(!this._recordButton.toggled(), this._serviceName);
157 }
158
159 /**
Rayan Kanso3252d5e2019-03-27 11:37:24160 * Called when the `Clear` button is clicked.
161 */
Rayan Kansob451b4f2019-04-04 23:12:11162 _clearEvents() {
Rayan Kanso3252d5e2019-03-27 11:37:24163 this._model.clearEvents(this._serviceName);
164 this._clearView();
165 }
166
167 /**
Tim van der Lippec02a97c2020-02-14 14:39:27168 * @param {!Common.EventTarget.EventTargetEvent} event
Rayan Kanso8fe8ee22019-03-04 14:58:46169 */
170 _onRecordingStateChanged(event) {
Tim van der Lippeff5ad472020-02-17 12:35:09171 const state = /** @type {!RecordingState} */ (event.data);
Tim van der Lippe1d6e57a2019-09-30 11:55:34172 if (state.serviceName !== this._serviceName) {
Rayan Kanso8fe8ee22019-03-04 14:58:46173 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34174 }
Rayan Kansoc0bfdd82019-04-24 12:32:22175
Tim van der Lippe1d6e57a2019-09-30 11:55:34176 if (state.isRecording === this._recordButton.toggled()) {
Rayan Kansoc0bfdd82019-04-24 12:32:22177 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34178 }
Rayan Kansoc0bfdd82019-04-24 12:32:22179
Rayan Kanso0b30aba2019-05-17 13:39:59180 this._recordButton.setToggled(state.isRecording);
Rayan Kansoc0bfdd82019-04-24 12:32:22181 this._showPreview(this._selectedEventNode);
Rayan Kanso8fe8ee22019-03-04 14:58:46182 }
Rayan Kansof40e3152019-03-11 13:49:43183
184 /**
Tim van der Lippec02a97c2020-02-14 14:39:27185 * @param {!Common.EventTarget.EventTargetEvent} event
Rayan Kansof40e3152019-03-11 13:49:43186 */
187 _onEventReceived(event) {
188 const serviceEvent = /** @type {!Protocol.BackgroundService.BackgroundServiceEvent} */ (event.data);
Tim van der Lippe1d6e57a2019-09-30 11:55:34189 if (!this._acceptEvent(serviceEvent)) {
Rayan Kansof40e3152019-03-11 13:49:43190 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34191 }
Rayan Kanso3252d5e2019-03-27 11:37:24192 this._addEvent(serviceEvent);
193 }
194
Rayan Kansoaca06e72019-03-27 11:57:06195 _onOriginChanged() {
196 // No need to refresh the view if we are already showing all events.
Tim van der Lippe1d6e57a2019-09-30 11:55:34197 if (this._originCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06198 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34199 }
Rayan Kansoaca06e72019-03-27 11:57:06200 this._refreshView();
201 }
202
Rayan Kanso3252d5e2019-03-27 11:37:24203 /**
204 * @param {!Protocol.BackgroundService.BackgroundServiceEvent} serviceEvent
205 */
206 _addEvent(serviceEvent) {
Rayan Kansoaca06e72019-03-27 11:57:06207 const data = this._createEventData(serviceEvent);
Tim van der Lippe097cdec2020-01-06 14:44:17208 const dataNode = new EventDataNode(data, serviceEvent.eventMetadata);
Rayan Kanso3252d5e2019-03-27 11:37:24209 this._dataGrid.rootNode().appendChild(dataNode);
Rayan Kansob852ba82019-04-08 13:48:07210
Rayan Kansoc0bfdd82019-04-24 12:32:22211 if (this._dataGrid.rootNode().children.length === 1) {
212 this._saveButton.setEnabled(true);
213 this._showPreview(this._selectedEventNode);
214 }
Rayan Kanso3252d5e2019-03-27 11:37:24215 }
216
217 /**
Tim van der Lippe0efccf02020-02-12 15:15:39218 * @return {!DataGrid.DataGrid.DataGridImpl}
Rayan Kanso3252d5e2019-03-27 11:37:24219 */
220 _createDataGrid() {
Tim van der Lippe9a9aba82020-02-14 14:52:29221 const columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([
Rayan Kansob852ba82019-04-08 13:48:07222 {id: 'id', title: ls`#`, weight: 1},
223 {id: 'timestamp', title: ls`Timestamp`, weight: 8},
Rayan Kansoc0bfdd82019-04-24 12:32:22224 {id: 'eventName', title: ls`Event`, weight: 10},
Rayan Kansob852ba82019-04-08 13:48:07225 {id: 'origin', title: ls`Origin`, weight: 10},
Rayan Kansocc02ea32019-05-02 21:26:52226 {id: 'swScope', title: ls`SW Scope`, weight: 2},
Rayan Kansob852ba82019-04-08 13:48:07227 {id: 'instanceId', title: ls`Instance ID`, weight: 10},
Rayan Kanso3252d5e2019-03-27 11:37:24228 ]);
Tim van der Lippe0efccf02020-02-12 15:15:39229 const dataGrid = new DataGrid.DataGrid.DataGridImpl({displayName: ls`Background Services`, columns});
Rayan Kanso3252d5e2019-03-27 11:37:24230 dataGrid.setStriped(true);
Rayan Kansob451b4f2019-04-04 23:12:11231
232 dataGrid.addEventListener(
Tim van der Lippe097cdec2020-01-06 14:44:17233 DataGrid.DataGrid.Events.SelectedNode, event => this._showPreview(/** @type {!EventDataNode} */ (event.data)));
Rayan Kansob451b4f2019-04-04 23:12:11234
Rayan Kanso3252d5e2019-03-27 11:37:24235 return dataGrid;
236 }
237
238 /**
Rayan Kansoaca06e72019-03-27 11:57:06239 * Creates the data object to pass to the DataGrid Node.
240 * @param {!Protocol.BackgroundService.BackgroundServiceEvent} serviceEvent
Tim van der Lippeff5ad472020-02-17 12:35:09241 * @return {!EventData}
Rayan Kansoaca06e72019-03-27 11:57:06242 */
243 _createEventData(serviceEvent) {
Rayan Kansocc02ea32019-05-02 21:26:52244 let swScope = '';
Rayan Kansoaca06e72019-03-27 11:57:06245
Rayan Kansocc02ea32019-05-02 21:26:52246 // Try to get the scope of the Service Worker registration to be more user-friendly.
247 const registration = this._serviceWorkerManager.registrations().get(serviceEvent.serviceWorkerRegistrationId);
Tim van der Lippe1d6e57a2019-09-30 11:55:34248 if (registration) {
Rayan Kansocc02ea32019-05-02 21:26:52249 swScope = registration.scopeURL.substr(registration.securityOrigin.length);
Tim van der Lippe1d6e57a2019-09-30 11:55:34250 }
Rayan Kansoaca06e72019-03-27 11:57:06251
252 return {
Rayan Kansoc79a8bb2019-05-29 21:08:38253 id: this._dataGrid.rootNode().children.length + 1,
Tim van der Lippe0efccf02020-02-12 15:15:39254 timestamp: UI.UIUtils.formatTimestamp(serviceEvent.timestamp * 1000, /* full= */ true),
Rayan Kansoaca06e72019-03-27 11:57:06255 origin: serviceEvent.origin,
Rayan Kansocc02ea32019-05-02 21:26:52256 swScope,
Rayan Kansoaca06e72019-03-27 11:57:06257 eventName: serviceEvent.eventName,
258 instanceId: serviceEvent.instanceId,
259 };
260 }
261
262 /**
Rayan Kanso3252d5e2019-03-27 11:37:24263 * Filtration function to know whether event should be shown or not.
264 * @param {!Protocol.BackgroundService.BackgroundServiceEvent} event
265 * @return {boolean}
266 */
267 _acceptEvent(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34268 if (event.service !== this._serviceName) {
Rayan Kansoaca06e72019-03-27 11:57:06269 return false;
Tim van der Lippe1d6e57a2019-09-30 11:55:34270 }
Rayan Kansoaca06e72019-03-27 11:57:06271
Tim van der Lippe1d6e57a2019-09-30 11:55:34272 if (this._originCheckbox.checked()) {
Rayan Kansoaca06e72019-03-27 11:57:06273 return true;
Tim van der Lippe1d6e57a2019-09-30 11:55:34274 }
Rayan Kansoaca06e72019-03-27 11:57:06275
276 // Trim the trailing '/'.
277 const origin = event.origin.substr(0, event.origin.length - 1);
278
279 return this._securityOriginManager.securityOrigins().includes(origin);
Rayan Kanso3252d5e2019-03-27 11:37:24280 }
Rayan Kansob451b4f2019-04-04 23:12:11281
282 /**
Rayan Kanso42c0f9e2019-09-02 12:21:09283 * @return {!Element}
284 */
285 _createLearnMoreLink() {
286 let url =
287 'https://developers.google.com/web/tools/chrome-devtools/javascript/background-services?utm_source=devtools';
288
289 switch (this._serviceName) {
290 case Protocol.BackgroundService.ServiceName.BackgroundFetch:
291 url += '#fetch';
292 break;
293 case Protocol.BackgroundService.ServiceName.BackgroundSync:
294 url += '#sync';
295 break;
296 case Protocol.BackgroundService.ServiceName.PushMessaging:
297 url += '#push';
298 break;
299 case Protocol.BackgroundService.ServiceName.Notifications:
300 url += '#notifications';
301 break;
302 default:
303 break;
304 }
305
Tim van der Lippe0efccf02020-02-12 15:15:39306 return UI.XLink.XLink.create(url, ls`Learn more`);
Rayan Kanso42c0f9e2019-09-02 12:21:09307 }
308
309 /**
Tim van der Lippe097cdec2020-01-06 14:44:17310 * @param {?EventDataNode} dataNode
Rayan Kansob451b4f2019-04-04 23:12:11311 */
312 _showPreview(dataNode) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34313 if (this._selectedEventNode && this._selectedEventNode === dataNode) {
Rayan Kansoc0bfdd82019-04-24 12:32:22314 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34315 }
Rayan Kansoc0bfdd82019-04-24 12:32:22316
317 this._selectedEventNode = dataNode;
318
Tim van der Lippe1d6e57a2019-09-30 11:55:34319 if (this._preview) {
Rayan Kansob451b4f2019-04-04 23:12:11320 this._preview.detach();
Tim van der Lippe1d6e57a2019-09-30 11:55:34321 }
Rayan Kansob451b4f2019-04-04 23:12:11322
Rayan Kansoc0bfdd82019-04-24 12:32:22323 if (this._selectedEventNode) {
324 this._preview = this._selectedEventNode.createPreview();
Rayan Kanso4476ca52019-07-22 11:51:24325 this._preview.show(this._previewPanel.contentElement);
326 return;
327 }
328
Tim van der Lippe0efccf02020-02-12 15:15:39329 this._preview = new UI.Widget.VBox();
Rayan Kanso4476ca52019-07-22 11:51:24330 this._preview.contentElement.classList.add('background-service-preview', 'fill');
331 const centered = this._preview.contentElement.createChild('div');
332
333 if (this._dataGrid.rootNode().children.length) {
Rayan Kansoc0bfdd82019-04-24 12:32:22334 // Inform users that grid entries are clickable.
Rayan Kanso4476ca52019-07-22 11:51:24335 centered.createChild('p').textContent = ls`Select an entry to view metadata`;
Rayan Kansoc0bfdd82019-04-24 12:32:22336 } else if (this._recordButton.toggled()) {
337 // Inform users that we are recording/waiting for events.
Tim van der Lippe097cdec2020-01-06 14:44:17338 const featureName = BackgroundServiceView.getUIString(this._serviceName);
Rayan Kanso4476ca52019-07-22 11:51:24339 centered.createChild('p').textContent = ls`Recording ${featureName} activity...`;
340 centered.createChild('p').textContent =
341 ls`DevTools will record all ${featureName} activity for up to 3 days, even when closed.`;
Rayan Kansoc0bfdd82019-04-24 12:32:22342 } else {
Tim van der Lippe0efccf02020-02-12 15:15:39343 const landingRecordButton = UI.Toolbar.Toolbar.createActionButton(this._recordAction);
Rayan Kansoc0bfdd82019-04-24 12:32:22344
Rayan Kanso6156d222019-04-29 23:40:55345 const recordKey = createElementWithClass('b', 'background-service-shortcut');
346 recordKey.textContent =
Paul Lewis05eb37f2020-01-24 14:31:40347 self.UI.shortcutRegistry.shortcutDescriptorsForAction('background-service.toggle-recording')[0].name;
Rayan Kanso6156d222019-04-29 23:40:55348
Tim van der Lippe0efccf02020-02-12 15:15:39349 const inlineButton = UI.UIUtils.createInlineButton(landingRecordButton);
Rayan Kansof402ef32019-08-12 13:05:18350 inlineButton.classList.add('background-service-record-inline-button');
Tim van der Lippe0efccf02020-02-12 15:15:39351 centered.createChild('p').appendChild(UI.UIUtils.formatLocalized(
352 'Click the record button %s or hit %s to start recording.', [inlineButton, recordKey]));
Rayan Kanso42c0f9e2019-09-02 12:21:09353
354 centered.appendChild(this._createLearnMoreLink());
Rayan Kansoc0bfdd82019-04-24 12:32:22355 }
Rayan Kansob451b4f2019-04-04 23:12:11356
357 this._preview.show(this._previewPanel.contentElement);
358 }
Rayan Kansob852ba82019-04-08 13:48:07359
360 /**
361 * Saves all currently displayed events in a file (JSON format).
362 */
363 async _saveToFile() {
364 const fileName = `${this._serviceName}-${new Date().toISO8601Compact()}.json`;
Tim van der Lippe0efccf02020-02-12 15:15:39365 const stream = new Bindings.FileUtils.FileOutputStream();
Rayan Kansob852ba82019-04-08 13:48:07366
367 const accepted = await stream.open(fileName);
Tim van der Lippe1d6e57a2019-09-30 11:55:34368 if (!accepted) {
Rayan Kansob852ba82019-04-08 13:48:07369 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34370 }
Rayan Kansob852ba82019-04-08 13:48:07371
372 const events = this._model.getEvents(this._serviceName).filter(event => this._acceptEvent(event));
373 await stream.write(JSON.stringify(events, undefined, 2));
374 stream.close();
375 }
Tim van der Lippe097cdec2020-01-06 14:44:17376}
Rayan Kanso3252d5e2019-03-27 11:37:24377
Tim van der Lippe0efccf02020-02-12 15:15:39378export class EventDataNode extends DataGrid.DataGrid.DataGridNode {
Rayan Kanso3252d5e2019-03-27 11:37:24379 /**
Rayan Kansoaca06e72019-03-27 11:57:06380 * @param {!Object<string, string>} data
381 * @param {!Array<!Protocol.BackgroundService.EventMetadata>} eventMetadata
Rayan Kanso3252d5e2019-03-27 11:37:24382 */
Rayan Kansoaca06e72019-03-27 11:57:06383 constructor(data, eventMetadata) {
384 super(data);
Rayan Kanso3252d5e2019-03-27 11:37:24385
386 /** @const {!Array<!Protocol.BackgroundService.EventMetadata>} */
Rayan Kansoc79a8bb2019-05-29 21:08:38387 this._eventMetadata = eventMetadata.sort((m1, m2) => m1.key.compareTo(m2.key));
Rayan Kanso3252d5e2019-03-27 11:37:24388 }
389
390 /**
Tim van der Lippe0efccf02020-02-12 15:15:39391 * @return {!UI.Widget.VBox}
Rayan Kanso3252d5e2019-03-27 11:37:24392 */
Rayan Kansob451b4f2019-04-04 23:12:11393 createPreview() {
Tim van der Lippe0efccf02020-02-12 15:15:39394 const preview = new UI.Widget.VBox();
Rayan Kansoc0bfdd82019-04-24 12:32:22395 preview.element.classList.add('background-service-metadata');
396
397 for (const entry of this._eventMetadata) {
398 const div = createElementWithClass('div', 'background-service-metadata-entry');
399 div.createChild('div', 'background-service-metadata-name').textContent = entry.key + ': ';
Rayan Kanso4476ca52019-07-22 11:51:24400 if (entry.value) {
401 div.createChild('div', 'background-service-metadata-value source-code').textContent = entry.value;
402 } else {
403 div.createChild('div', 'background-service-metadata-value background-service-empty-value').textContent =
404 ls`empty`;
405 }
Rayan Kansoc0bfdd82019-04-24 12:32:22406 preview.element.appendChild(div);
407 }
408
409 if (!preview.element.children.length) {
410 const div = createElementWithClass('div', 'background-service-metadata-entry');
411 div.createChild('div', 'background-service-metadata-name').textContent = ls`No metadata for this event`;
412 preview.element.appendChild(div);
413 }
414
415 return preview;
Rayan Kansof40e3152019-03-11 13:49:43416 }
Tim van der Lippe097cdec2020-01-06 14:44:17417}
Rayan Kanso6156d222019-04-29 23:40:55418
419/**
Tim van der Lippe0efccf02020-02-12 15:15:39420 * @implements {UI.ActionDelegate.ActionDelegate}
Rayan Kanso6156d222019-04-29 23:40:55421 * @unrestricted
422 */
Tim van der Lippe097cdec2020-01-06 14:44:17423export class ActionDelegate {
Rayan Kanso6156d222019-04-29 23:40:55424 /**
425 * @override
Tim van der Lippe0efccf02020-02-12 15:15:39426 * @param {!UI.Context.Context} context
Rayan Kanso6156d222019-04-29 23:40:55427 * @param {string} actionId
428 * @return {boolean}
429 */
430 handleAction(context, actionId) {
Tim van der Lippe097cdec2020-01-06 14:44:17431 const view = context.flavor(BackgroundServiceView);
Rayan Kanso6156d222019-04-29 23:40:55432 switch (actionId) {
433 case 'background-service.toggle-recording':
434 view._toggleRecording();
435 return true;
436 }
437 return false;
438 }
Tim van der Lippe097cdec2020-01-06 14:44:17439}
Tim van der Lippeff5ad472020-02-17 12:35:09440
441/**
442 * @typedef {!{isRecording: boolean, serviceName: !Protocol.BackgroundService.ServiceName}}
443 */
444export let RecordingState;
445
446/**
447 * @typedef {{
448 * id: number,
449 * timestamp: string,
450 * origin: string,
451 * swScope: string,
452 * eventName: string,
453 * instanceId: string,
454 * }}
455 */
456export let EventData;