Create a protocol + handler for Background Service recording.
- Create protocol for starting/stopping/querying recording mode
- Add handler which talks to the DevTools context
- Add a SDK model that the view communicates with
- flat_map -> array to avoid multi-thread access issues
Bug: 927726
Change-Id: I11e55a747c7c5c60465508485d6b869b976d40dd
Reviewed-on: https://chromium-review.googlesource.com/c/1477851
Reviewed-by: Dmitry Gozman <[email protected]>
Commit-Queue: Rayan Kanso <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#637287}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 31ad82ae99ed2a328d95fa6f73360a7c13561436
diff --git a/front_end/main/Main.js b/front_end/main/Main.js
index 62d2936..3d3214e 100644
--- a/front_end/main/Main.js
+++ b/front_end/main/Main.js
@@ -106,7 +106,7 @@
_initializeExperiments() {
// Keep this sorted alphabetically: both keys and values.
Runtime.experiments.register('applyCustomStylesheet', 'Allow custom UI themes');
- Runtime.experiments.register('backgroundServices', 'Background web platform feature events');
+ Runtime.experiments.register('backgroundServices', 'Background web platform feature events', true);
Runtime.experiments.register('blackboxJSFramesOnTimeline', 'Blackbox JavaScript frames on Timeline', true);
Runtime.experiments.register('emptySourceMapAutoStepping', 'Empty sourcemap auto-stepping');
Runtime.experiments.register('inputEventsOnTimelineOverview', 'Input events on Timeline overview', true);
diff --git a/front_end/resources/ApplicationPanelSidebar.js b/front_end/resources/ApplicationPanelSidebar.js
index b7929d1..18f1c2b 100644
--- a/front_end/resources/ApplicationPanelSidebar.js
+++ b/front_end/resources/ApplicationPanelSidebar.js
@@ -58,10 +58,10 @@
this._applicationTreeElement.appendChild(clearStorageTreeElement);
if (Runtime.experiments.isEnabled('backgroundServices')) {
this.backgroundFetchTreeElement =
- new Resources.BackgroundServiceTreeElement(panel, Common.UIString('Background Fetch'));
+ new Resources.BackgroundServiceTreeElement(panel, Protocol.BackgroundService.ServiceName.BackgroundFetch);
this._applicationTreeElement.appendChild(this.backgroundFetchTreeElement);
this.backgroundSyncTreeElement =
- new Resources.BackgroundServiceTreeElement(panel, Common.UIString('Background Sync'));
+ new Resources.BackgroundServiceTreeElement(panel, Protocol.BackgroundService.ServiceName.BackgroundSync);
this._applicationTreeElement.appendChild(this.backgroundSyncTreeElement);
}
@@ -210,6 +210,11 @@
this.indexedDBListTreeElement._initialize();
const serviceWorkerCacheModel = this._target.model(SDK.ServiceWorkerCacheModel);
this.cacheStorageListTreeElement._initialize(serviceWorkerCacheModel);
+ const backgroundServiceModel = this._target.model(Resources.BackgroundServiceModel);
+ if (Runtime.experiments.isEnabled('backgroundServices')) {
+ this.backgroundFetchTreeElement._initialize(backgroundServiceModel);
+ this.backgroundSyncTreeElement._initialize(backgroundServiceModel);
+ }
}
/**
@@ -683,22 +688,53 @@
Resources.BackgroundServiceTreeElement = class extends Resources.BaseStorageTreeElement {
/**
* @param {!Resources.ResourcesPanel} storagePanel
- * @param {string} serviceName
+ * @param {!Protocol.BackgroundService.ServiceName} serviceName
*/
constructor(storagePanel, serviceName) {
- super(storagePanel, serviceName, false);
+ super(storagePanel, Resources.BackgroundServiceTreeElement._getUIString(serviceName), false);
- /** @const {string} */
+ /** @const {!Protocol.BackgroundService.ServiceName} */
this._serviceName = serviceName;
+ /** @type {boolean} Whether the element has been selected. */
+ this._selected = false;
+
/** @type {?Resources.BackgroundServiceView} */
this._view = null;
+ /** @private {?Resources.BackgroundServiceModel} */
+ this._model = null;
+
const backgroundServiceIcon = UI.Icon.create('mediumicon-table', 'resource-tree-item');
this.setLeadingIcons([backgroundServiceIcon]);
}
/**
+ * @param {string} serviceName The name of the background service.
+ * @return {string} The UI String to display.
+ */
+ static _getUIString(serviceName) {
+ switch (serviceName) {
+ case Protocol.BackgroundService.ServiceName.BackgroundFetch:
+ return Common.UIString('Background Fetch');
+ case Protocol.BackgroundService.ServiceName.BackgroundSync:
+ return Common.UIString('Background Sync');
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * @param {?Resources.BackgroundServiceModel} model
+ */
+ _initialize(model) {
+ this._model = model;
+ // Show the view if the model was initialized after selection.
+ if (this._selected && !this._view)
+ this.onselect(false);
+ }
+
+ /**
* @return {string}
*/
get itemURL() {
@@ -711,8 +747,13 @@
*/
onselect(selectedByUser) {
super.onselect(selectedByUser);
+ this._selected = true;
+
+ if (!this._model)
+ return false;
+
if (!this._view)
- this._view = new Resources.BackgroundServiceView(this._serviceName);
+ this._view = new Resources.BackgroundServiceView(this._serviceName, this._model);
this.showView(this._view);
return false;
}
diff --git a/front_end/resources/BackgroundServiceModel.js b/front_end/resources/BackgroundServiceModel.js
new file mode 100644
index 0000000..b701a33
--- /dev/null
+++ b/front_end/resources/BackgroundServiceModel.js
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/**
+ * @implements {Protocol.BackgroundServiceDispatcher}
+ * @unrestricted
+ */
+Resources.BackgroundServiceModel = class extends SDK.SDKModel {
+ /**
+ * @param {!SDK.Target} target
+ */
+ constructor(target) {
+ super(target);
+ this._backgroundServiceAgent = target.backgroundServiceAgent();
+ target.registerBackgroundServiceDispatcher(this);
+ }
+
+ /**
+ * @param {!Protocol.BackgroundService.ServiceName} serviceName
+ */
+ enable(serviceName) {
+ this._backgroundServiceAgent.enable(serviceName);
+ }
+
+ /**
+ * @param {boolean} shouldRecord
+ * @param {!Protocol.BackgroundService.ServiceName} serviceName
+ */
+ setRecording(shouldRecord, serviceName) {
+ this._backgroundServiceAgent.setRecording(shouldRecord, serviceName);
+ }
+
+ /**
+ * @override
+ * @param {boolean} isRecording
+ * @param {!Protocol.BackgroundService.ServiceName} serviceName
+ */
+ recordingStateChanged(isRecording, serviceName) {
+ this.dispatchEventToListeners(
+ Resources.BackgroundServiceModel.Events.RecordingStateChanged, {isRecording, serviceName});
+ }
+};
+
+SDK.SDKModel.register(Resources.BackgroundServiceModel, SDK.Target.Capability.Browser, false);
+
+/** @enum {symbol} */
+Resources.BackgroundServiceModel.Events = {
+ RecordingStateChanged: Symbol('RecordingStateChanged'),
+};
+
+/**
+ * @typedef {!{isRecording: boolean, serviceName: !Protocol.BackgroundService.ServiceName}}
+ */
+Resources.BackgroundServiceModel.RecordingState;
diff --git a/front_end/resources/BackgroundServiceView.js b/front_end/resources/BackgroundServiceView.js
index 62c2db9..11b47d8 100644
--- a/front_end/resources/BackgroundServiceView.js
+++ b/front_end/resources/BackgroundServiceView.js
@@ -4,15 +4,25 @@
Resources.BackgroundServiceView = class extends UI.VBox {
/**
- * @param {string} serviceName
+ * @param {!Protocol.BackgroundService.ServiceName} serviceName
+ * @param {!Resources.BackgroundServiceModel} model
*/
- constructor(serviceName) {
+ constructor(serviceName, model) {
super(true);
this.registerRequiredCSS('resources/backgroundServiceView.css');
- /** @const {string} */
+ /** @const {!Protocol.BackgroundService.ServiceName} */
this._serviceName = serviceName;
+ /** @const {!Resources.BackgroundServiceModel} */
+ this._model = model;
+ this._model.addEventListener(
+ Resources.BackgroundServiceModel.Events.RecordingStateChanged, this._onRecordingStateChanged, this);
+ this._model.enable(this._serviceName);
+
+ /** @type {?UI.ToolbarToggle} */
+ this._recordButton = null;
+
/** @const {!UI.Toolbar} */
this._toolbar = new UI.Toolbar('background-service-toolbar', this.contentElement);
this._setupToolbar();
@@ -21,13 +31,12 @@
/**
* Creates the toolbar UI element.
*/
- _setupToolbar() {
- const recordButton =
+ async _setupToolbar() {
+ this._recordButton =
new UI.ToolbarToggle(Common.UIString('Toggle Record'), 'largeicon-start-recording', 'largeicon-stop-recording');
- recordButton.addEventListener(
- UI.ToolbarButton.Events.Click, () => recordButton.setToggled(!recordButton.toggled()));
- recordButton.setToggleWithRedColor(true);
- this._toolbar.appendToolbarItem(recordButton);
+ this._recordButton.addEventListener(UI.ToolbarButton.Events.Click, () => this._toggleRecording());
+ this._recordButton.setToggleWithRedColor(true);
+ this._toolbar.appendToolbarItem(this._recordButton);
const refreshButton = new UI.ToolbarButton(Common.UIString('Refresh'), 'largeicon-refresh');
refreshButton.addEventListener(UI.ToolbarButton.Events.Click, () => {});
@@ -43,4 +52,21 @@
deleteButton.addEventListener(UI.ToolbarButton.Events.Click, () => {});
this._toolbar.appendToolbarItem(deleteButton);
}
+
+ /**
+ * Called when the `Toggle Record` button is clicked.
+ */
+ _toggleRecording() {
+ this._model.setRecording(!this._recordButton.toggled(), this._serviceName);
+ }
+
+ /**
+ * @param {!Common.Event} event
+ */
+ _onRecordingStateChanged(event) {
+ const state = /** @type {!Resources.BackgroundServiceModel.RecordingState} */ (event.data);
+ if (state.serviceName !== this._serviceName)
+ return;
+ this._recordButton.setToggled(state.isRecording);
+ }
};
diff --git a/front_end/resources/module.json b/front_end/resources/module.json
index fadebf5..ddfece8 100644
--- a/front_end/resources/module.json
+++ b/front_end/resources/module.json
@@ -34,6 +34,7 @@
"ApplicationCacheModel.js",
"AppManifestView.js",
"ApplicationCacheItemsView.js",
+ "BackgroundServiceModel.js",
"BackgroundServiceView.js",
"ClearStorageView.js",
"StorageItemsView.js",