Bug 1471795 - Part 12: Update extension list ui when the extensions were added or removed. r?jdescottes
MozReview-Commit-ID: 7AmOebWj0BD
--- a/devtools/client/aboutdebugging-new/aboutdebugging.js
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.js
@@ -38,28 +38,32 @@ const AboutDebugging = {
this.store = configureStore();
this.actions = bindActionCreators(actions, this.store.dispatch);
const thisFirefox = new ThisFirefox();
await this.updateSelectedRuntime(thisFirefox);
render(Provider({ store: this.store }, App({ thisFirefox })), this.mount);
+
+ this.thisFirefox = thisFirefox;
},
destroy() {
unmountComponentAtNode(this.mount);
+ this.thisFirefox.destroy();
},
get mount() {
return document.getElementById("mount");
},
async updateSelectedRuntime(runtime) {
- await this.actions.updateSelectedRuntime(runtime);
+ const previousSelectedRuntime = this.store.getState().runtimes.selectedRuntime;
+ await this.actions.updateSelectedRuntime(runtime, previousSelectedRuntime);
},
};
window.addEventListener("DOMContentLoaded", () => {
AboutDebugging.init();
}, { once: true });
window.addEventListener("unload", () => {
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -3,18 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
UPDATE_SELECTED_RUNTIME,
} = require("../constants");
-function updateSelectedRuntime(runtime) {
+function updateSelectedRuntime(runtime, previousRuntime) {
return async dispatch => {
+ if (previousRuntime) {
+ await previousRuntime.disconnect();
+ }
+
await runtime.connect();
dispatch({
type: UPDATE_SELECTED_RUNTIME,
runtime,
});
};
}
--- a/devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.js
+++ b/devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.js
@@ -20,43 +20,49 @@ class DebugTargetsPane extends PureCompo
return {
runtime: PropTypes.instanceOf(Runtime).isRequired,
};
}
constructor(props) {
super(props);
+ this.onExtensionsUpdated = this.onExtensionsUpdated.bind(this);
this.onTabsUpdated = this.onTabsUpdated.bind(this);
this.state = {
info: {},
installedExtensions: [],
tabs: [],
temporaryExtensions: [],
};
this.update(props.runtime);
}
componentDidUpdate(prevProps) {
if (prevProps.runtime !== this.props.runtime) {
- prevProps.runtime.removeTabsUpdateListener(this.onTabsUpdated);
+ const { runtime } = prevProps;
+ runtime.removeExtensionsUpdateListener(this.onExtensionsUpdated);
+ runtime.removeTabsUpdateListener(this.onTabsUpdated);
this.update(this.props.runtime);
}
}
componentWillUnmount() {
- this.props.runtime.removeTabsUpdateListener(this.onTabsUpdated);
+ const { runtime } = this.props;
+ runtime.removeExtensionsUpdateListener(this.onExtensionsUpdated);
+ runtime.removeTabsUpdateListener(this.onTabsUpdated);
}
update(runtime) {
this.updateRuntimeInfo(runtime);
this.updateTabs(runtime);
this.updateExtensions(runtime);
+ runtime.addExtensionsUpdateListener(this.onExtensionsUpdated);
runtime.addTabsUpdateListener(this.onTabsUpdated);
}
async updateExtensions(runtime) {
const extensions = (await runtime.getExtensions()).filter(t => t.debuggable);
const installedExtensions = extensions.filter(t => !t.temporarilyInstalled);
const temporaryExtensions = extensions.filter(t => t.temporarilyInstalled);
this.setState({ installedExtensions, temporaryExtensions });
@@ -68,16 +74,20 @@ class DebugTargetsPane extends PureCompo
}
async updateTabs(runtime) {
// Filter out closed tabs (represented as `null`).
const tabs = (await runtime.getTabs()).filter(t => !!t);
this.setState({ tabs });
}
+ onExtensionsUpdated() {
+ this.updateExtensions(this.props.runtime);
+ }
+
onTabsUpdated() {
this.updateTabs(this.props.runtime);
}
render() {
const { runtime } = this.props;
const {
info,
--- a/devtools/client/aboutdebugging-new/src/runtimes/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/runtimes/runtime.js
@@ -4,16 +4,25 @@
"use strict";
/**
* This class represents a runtime, such as a remote Firefox.
*/
class Runtime {
/**
+ * Add a extensions update listener to detect extensions change.
+ * Subclass should override this method.
+ * @param {function}
+ */
+ addExtensionsUpdateListener(listener) {
+ throw new Error("Subclass of Runtime should override addExtensionsUpdateListener()");
+ }
+
+ /**
* Add a tab update listener to detect tabs change.
* Subclass should override this method.
* @param {function}
*/
addTabsUpdateListener(listener) {
throw new Error("Subclass of Runtime should override addTabsUpdateListener()");
}
@@ -21,16 +30,32 @@ class Runtime {
* Connect to this runtime.
* Subclass should override this method.
*/
async connect() {
throw new Error("Subclass of Runtime should override connect()");
}
/**
+ * This method will be called when about:debugging page was unloaded.
+ * Subclass should override this method.
+ */
+ destroy() {
+ throw new Error("Subclass of Runtime should override destroy()");
+ }
+
+ /**
+ * This method will be called when this runtime was unselected.
+ * Subclass should override this method.
+ */
+ async disconnect() {
+ throw new Error("Subclass of Runtime should override disconnect()");
+ }
+
+ /**
* Return extensions on this runtime.
* Subclass should override this method.
* @return {Array}
*/
async getExtensions() {
throw new Error("Subclass of Runtime should override getExtensions()");
}
@@ -78,14 +103,24 @@ class Runtime {
throw new Error("Subclass of Runtime should override inspectTab()");
}
/**
* Remove a tab update listener.
* Subclass should override this method.
* @param {function}
*/
+ removeExtensionsUpdateListener(listener) {
+ throw new Error("Subclass of Runtime should override " +
+ "removeExtensionsUpdateListener()");
+ }
+
+ /**
+ * Remove a tab update listener.
+ * Subclass should override this method.
+ * @param {function}
+ */
removeTabsUpdateListener(listener) {
throw new Error("Subclass of Runtime should override removeTabsUpdateListener()");
}
}
module.exports = Runtime;
--- a/devtools/client/aboutdebugging-new/src/runtimes/this-firefox.js
+++ b/devtools/client/aboutdebugging-new/src/runtimes/this-firefox.js
@@ -1,40 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
+const { AddonManager } = require("resource://gre/modules/AddonManager.jsm");
const { BrowserToolboxProcess } =
require("resource://devtools/client/framework/ToolboxProcess.jsm");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
const { DebuggerServer } = require("devtools/server/main");
const Services = require("Services");
const Runtime = require("./runtime");
/**
* This class represents the Firefox instance which runs in the same environment that
* opened about:debugging.
*/
class ThisFirefox extends Runtime {
+ constructor() {
+ super();
+ this.extensionsListeners = [];
+ AddonManager.addAddonListener(this);
+ }
+
+ addExtensionsUpdateListener(listener) {
+ this.extensionsListeners.push(listener);
+ }
+
addTabsUpdateListener(listener) {
this.client.addListener("tabListChanged", listener);
}
async connect() {
// Setup a server if we don't have one already running
DebuggerServer.init();
DebuggerServer.registerAllActors();
const transport = DebuggerServer.connectPipe();
this.client = new DebuggerClient(transport);
return this.client.connect();
}
+ async destroy() {
+ await this.disconnect();
+ this.extensionsListeners = null;
+ }
+
+ async disconnect() {
+ AddonManager.removeAddonListener(this);
+ await this.client.close();
+ DebuggerServer.destroy();
+ this.client = null;
+ }
+
async getExtensions() {
const { addons } = await this.client.listAddons();
return addons;
}
async getRuntimeInfo() {
return {
icon: "chrome://branding/content/icon64.png",
@@ -61,14 +84,56 @@ class ThisFirefox extends Runtime {
}
});
}
async inspectTab(debugTarget) {
window.open(`about:devtools-toolbox?type=tab&id=${ debugTarget.outerWindowID }`);
}
+ removeExtensionsUpdateListener(listener) {
+ const index = this.extensionsListeners.indexOf(listener);
+
+ if (index > -1) {
+ this.extensionsListeners.splice(index, 1);
+ }
+ }
+
removeTabsUpdateListener(listener) {
this.client.removeListener("tabListChanged", listener);
}
+
+ onExtensionsUpdated() {
+ for (const listener of this.extensionsListeners) {
+ listener();
+ }
+ }
+
+ /**
+ * Mandatory callback as AddonManager listener.
+ */
+ onInstalled() {
+ this.onExtensionsUpdated();
+ }
+
+ /**
+ * Mandatory callback as AddonManager listener.
+ */
+ onUninstalled() {
+ this.onExtensionsUpdated();
+ }
+
+ /**
+ * Mandatory callback as AddonManager listener.
+ */
+ onEnabled() {
+ this.onExtensionsUpdated();
+ }
+
+ /**
+ * Mandatory callback as AddonManager listener.
+ */
+ onDisabled() {
+ this.onExtensionsUpdated();
+ }
}
module.exports = ThisFirefox;