Bug 1425347 - Hide system add-ons by default in about:debugging r?jdescottes draft
authorMark Striemer <mstriemer@mozilla.com>
Mon, 28 May 2018 15:33:26 -0500
changeset 820083 b4ae9c75e593bc07d52fb4350fcf2a1e0655d2f2
parent 819908 ee47b3edea9199a1ef1450d62ca77ad68ae16d7b
push id116714
push userbmo:mstriemer@mozilla.com
push dateWed, 18 Jul 2018 21:05:13 +0000
reviewersjdescottes
bugs1425347
milestone63.0a1
Bug 1425347 - Hide system add-ons by default in about:debugging r?jdescottes MozReview-Commit-ID: 4bhF6Zxsn6w
devtools/client/aboutdebugging/components/addons/Panel.js
devtools/client/aboutdebugging/test/browser_addons_debug_info.js
devtools/client/locales/en-US/aboutdebugging.properties
devtools/client/preferences/devtools-client.js
devtools/server/actors/addon/webextension.js
devtools/server/actors/targets/addon.js
--- a/devtools/client/aboutdebugging/components/addons/Panel.js
+++ b/devtools/client/aboutdebugging/components/addons/Panel.js
@@ -20,16 +20,17 @@ loader.lazyRequireGetter(this, "Debugger
   "devtools/shared/client/debugger-client", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 const CHROME_ENABLED_PREF = "devtools.chrome.enabled";
 const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
+const SYSTEM_ENABLED_PREF = "devtools.aboutdebugging.showSystemAddons";
 const WEB_EXT_URL = "https://developer.mozilla.org/Add-ons" +
                     "/WebExtensions/Getting_started_with_web-ext";
 
 class AddonsPanel extends Component {
   static get propTypes() {
     return {
       client: PropTypes.instanceOf(DebuggerClient).isRequired,
       connect: PropTypes.object,
@@ -38,19 +39,21 @@ class AddonsPanel extends Component {
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
       extensions: [],
       debugDisabled: false,
+      showSystemAddons: false,
     };
 
     this.updateDebugStatus = this.updateDebugStatus.bind(this);
+    this.updateShowSystemStatus = this.updateShowSystemStatus.bind(this);
     this.updateAddonsList = this.updateAddonsList.bind(this);
     this.onInstalled = this.onInstalled.bind(this);
     this.onUninstalled = this.onUninstalled.bind(this);
     this.onEnabled = this.onEnabled.bind(this);
     this.onDisabled = this.onDisabled.bind(this);
   }
 
   componentDidMount() {
@@ -58,49 +61,60 @@ class AddonsPanel extends Component {
     // Listen to startup since that's when errors and warnings
     // get populated on the extension.
     Management.on("startup", this.updateAddonsList);
 
     Services.prefs.addObserver(CHROME_ENABLED_PREF,
       this.updateDebugStatus);
     Services.prefs.addObserver(REMOTE_ENABLED_PREF,
       this.updateDebugStatus);
+    Services.prefs.addObserver(SYSTEM_ENABLED_PREF,
+      this.updateShowSystemStatus);
 
     this.updateDebugStatus();
+    this.updateShowSystemStatus();
     this.updateAddonsList();
   }
 
   componentWillUnmount() {
     AddonManager.removeAddonListener(this);
     Management.off("startup", this.updateAddonsList);
 
     Services.prefs.removeObserver(CHROME_ENABLED_PREF,
       this.updateDebugStatus);
     Services.prefs.removeObserver(REMOTE_ENABLED_PREF,
       this.updateDebugStatus);
+    Services.prefs.removeObserver(SYSTEM_ENABLED_PREF,
+      this.updateShowSystemStatus);
   }
 
   updateDebugStatus() {
     const debugDisabled =
       !Services.prefs.getBoolPref(CHROME_ENABLED_PREF) ||
       !Services.prefs.getBoolPref(REMOTE_ENABLED_PREF);
 
     this.setState({ debugDisabled });
   }
 
+  updateShowSystemStatus() {
+    const showSystemAddons = Services.prefs.getBoolPref(SYSTEM_ENABLED_PREF, false);
+    this.setState({ showSystemAddons });
+  }
+
   updateAddonsList() {
     this.props.client.listAddons()
       .then(({addons}) => {
         const extensions = addons.filter(addon => addon.debuggable).map(addon => {
           return {
             addonTargetActor: addon.actor,
             addonID: addon.id,
             // Forward the whole addon actor form for potential remote debugging.
             form: addon,
             icon: addon.iconURL || ExtensionIcon,
+            isSystem: addon.isSystem,
             manifestURL: addon.manifestURL,
             name: addon.name,
             temporarilyInstalled: addon.temporarilyInstalled,
             url: addon.url,
             warnings: addon.warnings,
           };
         });
 
@@ -135,23 +149,26 @@ class AddonsPanel extends Component {
    * Mandatory callback as AddonManager listener.
    */
   onDisabled() {
     this.updateAddonsList();
   }
 
   render() {
     const { client, connect, id } = this.props;
-    const { debugDisabled, extensions: targets } = this.state;
+    const { debugDisabled, extensions: targets, showSystemAddons } = this.state;
     const installedName = Strings.GetStringFromName("extensions");
     const temporaryName = Strings.GetStringFromName("temporaryExtensions");
+    const systemName = Strings.GetStringFromName("systemExtensions");
     const targetClass = AddonTarget;
 
-    const installedTargets = targets.filter((target) => !target.temporarilyInstalled);
+    const installedTargets = targets.filter(
+      (target) => !target.isSystem && !target.temporarilyInstalled);
     const temporaryTargets = targets.filter((target) => target.temporarilyInstalled);
+    const systemTargets = showSystemAddons && targets.filter((target) => target.isSystem);
 
     return dom.div({
       id: id + "-panel",
       className: "panel",
       role: "tabpanel",
       "aria-labelledby": id + "-header"
     },
     PanelHeader({
@@ -186,13 +203,27 @@ class AddonsPanel extends Component {
         name: installedName,
         targets: installedTargets,
         client,
         connect,
         debugDisabled,
         targetClass,
         sort: true
       })
-    ));
+    ),
+    showSystemAddons ?
+      dom.div({ id: "system-addons" },
+        TargetList({
+          id: "system-extensions",
+          name: systemName,
+          targets: systemTargets,
+          client,
+          connect,
+          debugDisabled,
+          targetClass,
+          sort: true
+        })
+      ) : null,
+    );
   }
 }
 
 module.exports = AddonsPanel;
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_info.js
@@ -1,11 +1,14 @@
 "use strict";
 
+const { Preferences } = ChromeUtils.import("resource://gre/modules/Preferences.jsm", {});
+
 const UUID_REGEX = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/;
+const SHOW_SYSTEM_ADDONS_PREF = "devtools.aboutdebugging.showSystemAddons";
 
 function testFilePath(container, expectedFilePath) {
   // Verify that the path to the install location is shown next to its label.
   const filePath = container.querySelector(".file-path");
   ok(filePath, "file path is in DOM");
   ok(filePath.textContent.endsWith(expectedFilePath), "file path is set correctly");
   is(filePath.previousElementSibling.textContent, "Location", "file path has label");
 }
@@ -114,8 +117,31 @@ add_task(async function testUnknownManif
      "the message is helpful");
   ok(messages[0].classList.contains("addon-target-warning-message"),
      "the message is a warning");
 
   await uninstallAddon({document, id: addonId, name: addonName});
 
   await closeAboutDebugging(tab);
 });
+
+add_task(async function testSystemAddonsHidden() {
+  await pushPref(SHOW_SYSTEM_ADDONS_PREF, false);
+
+  const { document } = await openAboutDebugging("addons");
+  const systemAddonsShown = () => !!document.getElementById("system-extensions");
+
+  await waitForInitialAddonList(document);
+
+  ok(!systemAddonsShown(), "System extensions are hidden");
+
+  Preferences.set(SHOW_SYSTEM_ADDONS_PREF, true);
+
+  await waitUntil(systemAddonsShown);
+
+  ok(systemAddonsShown(), "System extensions are now shown");
+
+  Preferences.set(SHOW_SYSTEM_ADDONS_PREF, false);
+
+  await waitUntil(() => !systemAddonsShown());
+
+  ok(!systemAddonsShown(), "System extensions are hidden again");
+});
--- a/devtools/client/locales/en-US/aboutdebugging.properties
+++ b/devtools/client/locales/en-US/aboutdebugging.properties
@@ -71,16 +71,20 @@ retryTemporaryInstall = Retry
 # LOCALIZATION NOTE (extensions):
 # This string is displayed as a header above the list of loaded add-ons.
 extensions = Extensions
 
 # LOCALIZATION NOTE (temporaryExtensions):
 # This string is displayed as a header above the list of temporarily loaded add-ons.
 temporaryExtensions = Temporary Extensions
 
+# LOCALIZATION NOTE (systemExtensions):
+# This string is displayed as a header above the list of system add-ons.
+systemExtensions = System Extensions
+
 # LOCALIZATION NOTE (internalUUID):
 # This string is displayed as a label for the internal UUID of an extension.
 # The UUID is generated for this profile on install.
 internalUUID = Internal UUID
 
 # LOCALIZATION NOTE (extensionID):
 # This string is displayed as a label for the ID of an extension. This is not the same as the internal UUID.
 extensionID = Extension ID
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -310,8 +310,15 @@ pref("devtools.editor.enableCodeFolding"
 pref("devtools.editor.autocomplete", true);
 
 // Whether to reload when touch simulation is toggled
 pref("devtools.responsive.reloadConditions.touchSimulation", false);
 // Whether to reload when user agent is changed
 pref("devtools.responsive.reloadConditions.userAgent", false);
 // Whether to show the notification about reloading to apply emulation
 pref("devtools.responsive.reloadNotification.enabled", true);
+
+// about:debugging: only show system add-ons in local builds by default.
+#ifdef MOZILLA_OFFICIAL
+  pref("devtools.aboutdebugging.showSystemAddons", false);
+#else
+  pref("devtools.aboutdebugging.showSystemAddons", true);
+#endif
--- a/devtools/server/actors/addon/webextension.js
+++ b/devtools/server/actors/addon/webextension.js
@@ -74,16 +74,17 @@ const WebExtensionActor = protocol.Actor
   form() {
     const policy = ExtensionParent.WebExtensionPolicy.getByID(this.id);
     return {
       actor: this.actorID,
       id: this.id,
       name: this.addon.name,
       url: this.addon.sourceURI ? this.addon.sourceURI.spec : undefined,
       iconURL: this.addon.iconURL,
+      isSystem: this.addon.isSystem,
       debuggable: this.addon.isDebuggable,
       temporarilyInstalled: this.addon.temporarilyInstalled,
       type: this.addon.type,
       isWebExtension: this.addon.isWebExtension,
       isAPIExtension: this.addon.isAPIExtension,
       manifestURL: policy && policy.getURL("manifest.json"),
       warnings: ExtensionParent.DebugUtils.getExtensionManifestWarnings(this.id),
     };
--- a/devtools/server/actors/targets/addon.js
+++ b/devtools/server/actors/targets/addon.js
@@ -82,16 +82,17 @@ AddonTargetActor.prototype = {
     }
 
     return {
       actor: this.actorID,
       id: this.id,
       name: this._addon.name,
       url: this.url,
       iconURL: this._addon.iconURL,
+      isSystem: this._addon.isSystem,
       debuggable: this._addon.isDebuggable,
       temporarilyInstalled: this._addon.temporarilyInstalled,
       type: this._addon.type,
       isWebExtension: this._addon.isWebExtension,
       isAPIExtension: this._addon.isAPIExtension,
       consoleActor: this._consoleActor.actorID,
 
       traits: {