Bug 1471795 - Part 10: Implement extension list. r?jdescottes, r?ladybenko
MozReview-Commit-ID: IDQhQgtBUMA
--- a/devtools/client/aboutdebugging-new/aboutdebugging.css
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.css
@@ -5,14 +5,15 @@
@import "chrome://global/skin/in-content/common.css";
@import "resource://devtools/client/themes/variables.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/App.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/RuntimeInfo.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/RuntimesPane.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetItem.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/DebugTargetList.css";
+@import "resource://devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionItem.css";
@import "resource://devtools/client/aboutdebugging-new/src/components/runtime/ThisFirefoxItem.css";
#mount {
height: 100%;
width: 100%;
}
--- a/devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.js
+++ b/devtools/client/aboutdebugging-new/src/components/DebugTargetsPane.js
@@ -6,16 +6,17 @@
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const Runtime = require("../runtimes/runtime");
const DebugTargetList = createFactory(require("./debugtarget/DebugTargetList"));
+const ExtensionItem = createFactory(require("./debugtarget/ExtensionItem"));
const RuntimeInfo = createFactory(require("./RuntimeInfo"));
const TabItem = createFactory(require("./debugtarget/TabItem"));
class DebugTargetsPane extends PureComponent {
static get propTypes() {
return {
runtime: PropTypes.instanceOf(Runtime).isRequired,
};
@@ -23,17 +24,19 @@ class DebugTargetsPane extends PureCompo
constructor(props) {
super(props);
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);
this.update(this.props.runtime);
@@ -42,20 +45,28 @@ class DebugTargetsPane extends PureCompo
componentWillUnmount() {
this.props.runtime.removeTabsUpdateListener(this.onTabsUpdated);
}
update(runtime) {
this.updateRuntimeInfo(runtime);
this.updateTabs(runtime);
+ this.updateExtensions(runtime);
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 });
+ }
+
async updateRuntimeInfo(runtime) {
const info = await runtime.getRuntimeInfo(runtime);
this.setState({ info });
}
async updateTabs(runtime) {
// Filter out closed tabs (represented as `null`).
const tabs = (await runtime.getTabs()).filter(t => !!t);
@@ -65,25 +76,39 @@ class DebugTargetsPane extends PureCompo
onTabsUpdated() {
this.updateTabs(this.props.runtime);
}
render() {
const { runtime } = this.props;
const {
info,
+ installedExtensions,
tabs,
+ temporaryExtensions,
} = this.state;
return dom.div(
{
className: "debug-targets-pane",
},
RuntimeInfo({ info }),
DebugTargetList({
+ className: "debug-target-list--temporary-extensions",
+ debugTargetItemComponent: ExtensionItem,
+ debugTargets: temporaryExtensions,
+ title: "Temporary Extensions",
+ }),
+ DebugTargetList({
+ className: "debug-target-list--installed-extensions",
+ debugTargetItemComponent: ExtensionItem,
+ debugTargets: installedExtensions,
+ title: "Extensions",
+ }),
+ DebugTargetList({
className: "debug-target-list--tabs",
debugTargetItemComponent: TabItem,
debugTargets: tabs,
runtime,
title: "Tabs",
})
);
}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionItem.css
@@ -0,0 +1,19 @@
+/* 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/. */
+
+.debug-target-item__info__detail__extension {
+ display: grid;
+ grid-template-columns: 100px 1fr;
+ margin-block-start: 4px;
+}
+
+.debug-target-item__info__detail__extension__content {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.debug-target-item__info__detail__extension__content__manifest {
+ margin-inline-start: 1ch;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/ExtensionItem.js
@@ -0,0 +1,133 @@
+/* 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 dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+const DebugTargetItem = require("./DebugTargetItem");
+
+class ExtensionItem extends DebugTargetItem {
+ getIcon() {
+ const { debugTarget } = this.props;
+ return debugTarget.iconURL || "chrome://mozapps/skin/extensions/extensionGeneric.svg";
+ }
+
+ getName() {
+ const { debugTarget } = this.props;
+ return debugTarget.name;
+ }
+
+ parseFileUri(url) {
+ // Strip a leading slash from Windows drive letter URIs.
+ // file:///home/foo ~> /home/foo
+ // file:///C:/foo ~> C:/foo
+ const windowsRegex = /^file:\/\/\/([a-zA-Z]:\/.*)/;
+
+ if (windowsRegex.test(url)) {
+ return windowsRegex.exec(url)[1];
+ }
+
+ return url.slice("file://".length);
+ }
+
+ renderActionComponents() {
+ return null;
+ }
+
+ renderDetailComponents() {
+ return dom.div(
+ {
+ className: "debug-target-item__info__detail__extension",
+ },
+ this.renderFilePath(),
+ this.renderExtensionId(),
+ this.renderInternalId()
+ );
+ }
+
+ renderFilePath() {
+ const { debugTarget } = this.props;
+ // Only show file system paths, and only for temporarily installed add-ons.
+ if (!debugTarget.temporarilyInstalled ||
+ !debugTarget.url ||
+ !debugTarget.url.startsWith("file://")) {
+ return null;
+ }
+
+ const path = this.parseFileUri(debugTarget.url);
+
+ return [
+ dom.dt(
+ {
+ className: "debug-target-item__info__detail__extension__label",
+ },
+ "Location"
+ ),
+ dom.dd(
+ {
+ className: "debug-target-item__info__detail__extension__content",
+ title: path,
+ },
+ path,
+ ),
+ ];
+ }
+
+ renderExtensionId() {
+ const { debugTarget } = this.props;
+
+ return [
+ dom.dt(
+ {
+ className: "debug-target-item__info__detail__extension__label",
+ },
+ "Extension ID",
+ ),
+ dom.dd(
+ {
+ className: "debug-target-item__info__detail__extension__content",
+ title: debugTarget.id,
+ },
+ debugTarget.id
+ ),
+ ];
+ }
+
+ renderInternalId() {
+ const { debugTarget } = this.props;
+
+ if (!debugTarget.manifestURL) {
+ return [];
+ }
+
+ // Strip off the protocol and rest, leaving us with just the UUID.
+ const uuid = /moz-extension:\/\/([^/]*)/.exec(debugTarget.manifestURL)[1];
+ return [
+ dom.dt(
+ {
+ className: "debug-target-item__info__detail__extension__label",
+ },
+ "Internal UUID",
+ ),
+ dom.dd(
+ {
+ className: "debug-target-item__info__detail__extension__content",
+ title: uuid,
+ },
+ uuid,
+ dom.a(
+ {
+ className: "debug-target-item__info__detail__extension__content__manifest",
+ href: debugTarget.manifestURL,
+ target: "_blank",
+ },
+ "Manifest URL",
+ ),
+ ),
+ ];
+ }
+}
+
+module.exports = ExtensionItem;
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/moz.build
@@ -2,10 +2,12 @@
# 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/.
DevToolsModules(
'DebugTargetItem.css',
'DebugTargetItem.js',
'DebugTargetList.css',
'DebugTargetList.js',
+ 'ExtensionItem.css',
+ 'ExtensionItem.js',
'TabItem.js'
)
--- a/devtools/client/aboutdebugging-new/src/runtimes/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/runtimes/runtime.js
@@ -21,16 +21,25 @@ class Runtime {
* Connect to this runtime.
* Subclass should override this method.
*/
async connect() {
throw new Error("Subclass of Runtime should override connect()");
}
/**
+ * Return extensions on this runtime.
+ * Subclass should override this method.
+ * @return {Array}
+ */
+ async getExtensions() {
+ throw new Error("Subclass of Runtime should override getExtensions()");
+ }
+
+ /**
* Return this runtime's information.
* Subclass should override this method.
* @return {Object}
* {
* icon: URL of the icon which represents this runtime
* e.g. chrome://branding/content/icon64.png
* name: Name of this runtime
* e.g. Firefox
--- a/devtools/client/aboutdebugging-new/src/runtimes/this-firefox.js
+++ b/devtools/client/aboutdebugging-new/src/runtimes/this-firefox.js
@@ -23,16 +23,21 @@ class ThisFirefox extends Runtime {
// 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 getExtensions() {
+ const { addons } = await this.client.listAddons();
+ return addons;
+ }
+
async getRuntimeInfo() {
return {
icon: "chrome://branding/content/icon64.png",
name: Services.appinfo.name,
version: Services.appinfo.version,
};
}