blob: d43bcec88b95741336be3ba04ff81aa81eab88dc [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
5Resources.BackgroundServiceView = class extends UI.VBox {
6 /**
Rayan Kanso8fe8ee22019-03-04 14:58:467 * @param {!Protocol.BackgroundService.ServiceName} serviceName
8 * @param {!Resources.BackgroundServiceModel} model
Rayan Kanso68904202019-02-21 14:16:259 */
Rayan Kanso8fe8ee22019-03-04 14:58:4610 constructor(serviceName, model) {
Rayan Kanso68904202019-02-21 14:16:2511 super(true);
12 this.registerRequiredCSS('resources/backgroundServiceView.css');
13
Rayan Kanso8fe8ee22019-03-04 14:58:4614 /** @const {!Protocol.BackgroundService.ServiceName} */
Rayan Kanso68904202019-02-21 14:16:2515 this._serviceName = serviceName;
16
Rayan Kanso8fe8ee22019-03-04 14:58:4617 /** @const {!Resources.BackgroundServiceModel} */
18 this._model = model;
19 this._model.addEventListener(
20 Resources.BackgroundServiceModel.Events.RecordingStateChanged, this._onRecordingStateChanged, this);
Rayan Kansof40e3152019-03-11 13:49:4321 this._model.addEventListener(
22 Resources.BackgroundServiceModel.Events.BackgroundServiceEventReceived, this._onEventReceived, this);
Rayan Kanso8fe8ee22019-03-04 14:58:4623 this._model.enable(this._serviceName);
24
25 /** @type {?UI.ToolbarToggle} */
26 this._recordButton = null;
27
Rayan Kanso68904202019-02-21 14:16:2528 /** @const {!UI.Toolbar} */
29 this._toolbar = new UI.Toolbar('background-service-toolbar', this.contentElement);
30 this._setupToolbar();
Rayan Kanso3252d5e2019-03-27 11:37:2431
32 /** @const {!DataGrid.DataGrid} */
33 this._dataGrid = this._createDataGrid();
34 this._dataGrid.asWidget().show(this.contentElement);
Rayan Kanso68904202019-02-21 14:16:2535 }
36
37 /**
38 * Creates the toolbar UI element.
39 */
Rayan Kanso8fe8ee22019-03-04 14:58:4640 async _setupToolbar() {
41 this._recordButton =
Rayan Kanso68904202019-02-21 14:16:2542 new UI.ToolbarToggle(Common.UIString('Toggle Record'), 'largeicon-start-recording', 'largeicon-stop-recording');
Rayan Kanso8fe8ee22019-03-04 14:58:4643 this._recordButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._toggleRecording());
44 this._recordButton.setToggleWithRedColor(true);
45 this._toolbar.appendToolbarItem(this._recordButton);
Rayan Kanso68904202019-02-21 14:16:2546
47 const refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh');
Rayan Kanso3252d5e2019-03-27 11:37:2448 refreshButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._refreshView());
Rayan Kanso68904202019-02-21 14:16:2549 this._toolbar.appendToolbarItem(refreshButton);
50
51 const clearButton = new UI.ToolbarButton(Common.UIString('Clear'), 'largeicon-clear');
Rayan Kanso3252d5e2019-03-27 11:37:2452 clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._clearView());
Rayan Kanso68904202019-02-21 14:16:2553 this._toolbar.appendToolbarItem(clearButton);
54
55 this._toolbar.appendSeparator();
56
57 const deleteButton = new UI.ToolbarButton(Common.UIString('Delete'), 'largeicon-trash-bin');
Rayan Kanso3252d5e2019-03-27 11:37:2458 deleteButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._deleteEvents());
Rayan Kanso68904202019-02-21 14:16:2559 this._toolbar.appendToolbarItem(deleteButton);
60 }
Rayan Kanso8fe8ee22019-03-04 14:58:4661
62 /**
63 * Called when the `Toggle Record` button is clicked.
64 */
65 _toggleRecording() {
66 this._model.setRecording(!this._recordButton.toggled(), this._serviceName);
67 }
68
69 /**
Rayan Kanso3252d5e2019-03-27 11:37:2470 * Called when the `Refresh` button is clicked.
71 */
72 _refreshView() {
73 this._clearView();
74 const events = this._model.getEvents(this._serviceName).filter(event => this._acceptEvent(event));
75 for (const event of events)
76 this._addEvent(event);
77 }
78
79 /**
80 * Called when the `Clear` button is clicked.
81 */
82 _clearView() {
83 this._dataGrid.rootNode().removeChildren();
84 }
85
86 /**
87 * Called when the `Delete` button is clicked.
88 */
89 _deleteEvents() {
90 this._model.clearEvents(this._serviceName);
91 this._clearView();
92 }
93
94 /**
Rayan Kanso8fe8ee22019-03-04 14:58:4695 * @param {!Common.Event} event
96 */
97 _onRecordingStateChanged(event) {
98 const state = /** @type {!Resources.BackgroundServiceModel.RecordingState} */ (event.data);
99 if (state.serviceName !== this._serviceName)
100 return;
101 this._recordButton.setToggled(state.isRecording);
102 }
Rayan Kansof40e3152019-03-11 13:49:43103
104 /**
105 * @param {!Common.Event} event
106 */
107 _onEventReceived(event) {
108 const serviceEvent = /** @type {!Protocol.BackgroundService.BackgroundServiceEvent} */ (event.data);
Rayan Kanso3252d5e2019-03-27 11:37:24109 if (!this._acceptEvent(serviceEvent))
Rayan Kansof40e3152019-03-11 13:49:43110 return;
Rayan Kanso3252d5e2019-03-27 11:37:24111 this._addEvent(serviceEvent);
112 }
113
114 /**
115 * @param {!Protocol.BackgroundService.BackgroundServiceEvent} serviceEvent
116 */
117 _addEvent(serviceEvent) {
118 const numEvents = this._dataGrid.rootNode().children.length;
119 const dataNode = new Resources.BackgroundServiceView.EventDataNode(numEvents, serviceEvent);
120 this._dataGrid.rootNode().appendChild(dataNode);
121 }
122
123 /**
124 * @return {!DataGrid.DataGrid}
125 */
126 _createDataGrid() {
127 const columns = /** @type {!Array<!DataGrid.DataGrid.ColumnDescriptor>} */ ([
128 {id: 'id', title: Common.UIString('#'), weight: 1},
129 {id: 'timestamp', title: Common.UIString('Timestamp'), weight: 8},
130 {id: 'origin', title: Common.UIString('Origin'), weight: 10},
131 {id: 'swid', title: Common.UIString('SW ID'), weight: 2},
132 {id: 'eventName', title: Common.UIString('Event'), weight: 10},
133 {id: 'instanceId', title: Common.UIString('Instance ID'), weight: 10},
134 ]);
135 const dataGrid = new DataGrid.DataGrid(columns);
136 dataGrid.setStriped(true);
137 return dataGrid;
138 }
139
140 /**
141 * Filtration function to know whether event should be shown or not.
142 * @param {!Protocol.BackgroundService.BackgroundServiceEvent} event
143 * @return {boolean}
144 */
145 _acceptEvent(event) {
146 return event.service === this._serviceName;
147 }
148};
149
150Resources.BackgroundServiceView.EventDataNode = class extends DataGrid.DataGridNode {
151 /**
152 * @param {number} eventId
153 * @param {!Protocol.BackgroundService.BackgroundServiceEvent} serviceEvent
154 */
155 constructor(eventId, serviceEvent) {
156 super(Resources.BackgroundServiceView.EventDataNode._createGridData(eventId, serviceEvent));
157
158 /** @const {!Array<!Protocol.BackgroundService.EventMetadata>} */
159 this._eventMetadata = serviceEvent.eventMetadata;
160
161 /** @type {?UI.PopoverHelper} */
162 this._popoverHelper = null;
163 }
164
165 /**
166 * @param {number} eventId
167 * @param {!Protocol.BackgroundService.BackgroundServiceEvent} serviceEvent
168 * @return {!Object<string, string>}
169 */
170 static _createGridData(eventId, serviceEvent) {
171 return {
172 id: eventId,
173 timestamp: new Date(serviceEvent.timestamp * 1000).toLocaleString(),
174 origin: serviceEvent.origin,
175 swid: serviceEvent.serviceWorkerRegistrationId,
176 eventName: serviceEvent.eventName,
177 instanceId: serviceEvent.instanceId,
178 };
179 }
180
181 /**
182 * @override
183 * @return {!Element}
184 */
185 createElement() {
186 const element = super.createElement();
187
188 this._popoverHelper = new UI.PopoverHelper(element, event => this._createPopover(event));
189 this._popoverHelper.setHasPadding(true);
190 this._popoverHelper.setTimeout(300, 300);
191
192 return element;
193 }
194
195 /**
196 * @param {!Event} event
197 * @return {?UI.PopoverRequest}
198 */
199 _createPopover(event) {
200 if (event.type !== 'mousedown')
201 return null;
202
203 // Create popover container.
204 const container = createElementWithClass('div', 'background-service-popover-container');
205 UI.appendStyle(container, 'resources/backgroundServiceView.css');
206
207 if (!this._eventMetadata.length) {
208 const entryDiv = createElementWithClass('div', 'background-service-metadata-entry');
209 entryDiv.textContent = 'There is no metadata for this event';
210 container.appendChild(entryDiv);
211 }
212
213 for (const entry of this._eventMetadata) {
214 const entryDiv = createElementWithClass('div', 'background-service-metadata-entry');
215 const key = createElementWithClass('label', 'background-service-metadata-key');
216 key.textContent = `${entry.key}: `;
217 const value = createElementWithClass('label', 'background-service-metadata-value');
218 value.textContent = entry.value;
219 entryDiv.appendChild(key);
220 entryDiv.appendChild(value);
221 container.appendChild(entryDiv);
222 }
223
224 return {
225 box: event.target.boxInWindow(),
226 show: popover => {
227 popover.contentElement.appendChild(container);
228 return Promise.resolve(true);
229 },
230 };
Rayan Kansof40e3152019-03-11 13:49:43231 }
Rayan Kanso68904202019-02-21 14:16:25232};