Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 1 | // 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 | |
| 5 | Resources.BackgroundServiceView = class extends UI.VBox { |
| 6 | /** |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 7 | * @param {!Protocol.BackgroundService.ServiceName} serviceName |
| 8 | * @param {!Resources.BackgroundServiceModel} model |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 9 | */ |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 10 | constructor(serviceName, model) { |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 11 | super(true); |
| 12 | this.registerRequiredCSS('resources/backgroundServiceView.css'); |
| 13 | |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 14 | /** @const {!Protocol.BackgroundService.ServiceName} */ |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 15 | this._serviceName = serviceName; |
| 16 | |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 17 | /** @const {!Resources.BackgroundServiceModel} */ |
| 18 | this._model = model; |
| 19 | this._model.addEventListener( |
| 20 | Resources.BackgroundServiceModel.Events.RecordingStateChanged, this._onRecordingStateChanged, this); |
Rayan Kanso | f40e315 | 2019-03-11 13:49:43 | [diff] [blame] | 21 | this._model.addEventListener( |
| 22 | Resources.BackgroundServiceModel.Events.BackgroundServiceEventReceived, this._onEventReceived, this); |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 23 | this._model.enable(this._serviceName); |
| 24 | |
| 25 | /** @type {?UI.ToolbarToggle} */ |
| 26 | this._recordButton = null; |
| 27 | |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 28 | /** @const {!UI.Toolbar} */ |
| 29 | this._toolbar = new UI.Toolbar('background-service-toolbar', this.contentElement); |
| 30 | this._setupToolbar(); |
Rayan Kanso | 3252d5e | 2019-03-27 11:37:24 | [diff] [blame^] | 31 | |
| 32 | /** @const {!DataGrid.DataGrid} */ |
| 33 | this._dataGrid = this._createDataGrid(); |
| 34 | this._dataGrid.asWidget().show(this.contentElement); |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 35 | } |
| 36 | |
| 37 | /** |
| 38 | * Creates the toolbar UI element. |
| 39 | */ |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 40 | async _setupToolbar() { |
| 41 | this._recordButton = |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 42 | new UI.ToolbarToggle(Common.UIString('Toggle Record'), 'largeicon-start-recording', 'largeicon-stop-recording'); |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 43 | this._recordButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._toggleRecording()); |
| 44 | this._recordButton.setToggleWithRedColor(true); |
| 45 | this._toolbar.appendToolbarItem(this._recordButton); |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 46 | |
| 47 | const refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh'); |
Rayan Kanso | 3252d5e | 2019-03-27 11:37:24 | [diff] [blame^] | 48 | refreshButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._refreshView()); |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 49 | this._toolbar.appendToolbarItem(refreshButton); |
| 50 | |
| 51 | const clearButton = new UI.ToolbarButton(Common.UIString('Clear'), 'largeicon-clear'); |
Rayan Kanso | 3252d5e | 2019-03-27 11:37:24 | [diff] [blame^] | 52 | clearButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._clearView()); |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 53 | this._toolbar.appendToolbarItem(clearButton); |
| 54 | |
| 55 | this._toolbar.appendSeparator(); |
| 56 | |
| 57 | const deleteButton = new UI.ToolbarButton(Common.UIString('Delete'), 'largeicon-trash-bin'); |
Rayan Kanso | 3252d5e | 2019-03-27 11:37:24 | [diff] [blame^] | 58 | deleteButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._deleteEvents()); |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 59 | this._toolbar.appendToolbarItem(deleteButton); |
| 60 | } |
Rayan Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 61 | |
| 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 Kanso | 3252d5e | 2019-03-27 11:37:24 | [diff] [blame^] | 70 | * 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 Kanso | 8fe8ee2 | 2019-03-04 14:58:46 | [diff] [blame] | 95 | * @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 Kanso | f40e315 | 2019-03-11 13:49:43 | [diff] [blame] | 103 | |
| 104 | /** |
| 105 | * @param {!Common.Event} event |
| 106 | */ |
| 107 | _onEventReceived(event) { |
| 108 | const serviceEvent = /** @type {!Protocol.BackgroundService.BackgroundServiceEvent} */ (event.data); |
Rayan Kanso | 3252d5e | 2019-03-27 11:37:24 | [diff] [blame^] | 109 | if (!this._acceptEvent(serviceEvent)) |
Rayan Kanso | f40e315 | 2019-03-11 13:49:43 | [diff] [blame] | 110 | return; |
Rayan Kanso | 3252d5e | 2019-03-27 11:37:24 | [diff] [blame^] | 111 | 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 | |
| 150 | Resources.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 Kanso | f40e315 | 2019-03-11 13:49:43 | [diff] [blame] | 231 | } |
Rayan Kanso | 6890420 | 2019-02-21 14:16:25 | [diff] [blame] | 232 | }; |