Bug 1245717 - List temporary add-ons separately in about:debugging r=jdescottes
authorMark Striemer <mstriemer@mozilla.com>
Thu, 02 Mar 2017 16:12:25 -0600
changeset 394861 19ef442244e21ccb2247e138a6652e1d86871923
parent 394860 3436c6cf4123d22dd9af0452fdc2fc6c9b19901f
child 394862 7b8d033f771784cdf6f1048a92b5fd00ed48c43c
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdescottes
bugs1245717
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1245717 - List temporary add-ons separately in about:debugging r=jdescottes MozReview-Commit-ID: HbqW3UTQMAq
devtools/client/aboutdebugging/aboutdebugging.css
devtools/client/aboutdebugging/components/addons/panel.js
devtools/client/aboutdebugging/components/addons/target.js
devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
devtools/client/aboutdebugging/test/browser_addons_reload.js
devtools/client/aboutdebugging/test/head.js
devtools/client/locales/en-US/aboutdebugging.properties
--- a/devtools/client/aboutdebugging/aboutdebugging.css
+++ b/devtools/client/aboutdebugging/aboutdebugging.css
@@ -38,24 +38,21 @@ button {
 }
 
 .main-content {
   flex: 1;
 }
 
 .panel {
   max-width: 800px;
+  margin-bottom: 35px;
 }
 
 /* Targets */
 
-.targets {
-  margin-bottom: 35px;
-}
-
 .target-list {
   margin: 0;
   padding: 0;
 }
 
 .target-container {
   margin-top: 5px;
   min-height: 34px;
--- a/devtools/client/aboutdebugging/components/addons/panel.js
+++ b/devtools/client/aboutdebugging/components/addons/panel.js
@@ -112,35 +112,49 @@ module.exports = createClass({
    */
   onDisabled() {
     this.updateAddonsList();
   },
 
   render() {
     let { client, id } = this.props;
     let { debugDisabled, extensions: targets } = this.state;
-    let name = Strings.GetStringFromName("extensions");
+    let installedName = Strings.GetStringFromName("extensions");
+    let temporaryName = Strings.GetStringFromName("temporaryExtensions");
     let targetClass = AddonTarget;
 
+    const installedTargets = targets.filter((target) => !target.temporarilyInstalled);
+    const temporaryTargets = targets.filter((target) => target.temporarilyInstalled);
+
     return dom.div({
       id: id + "-panel",
       className: "panel",
       role: "tabpanel",
       "aria-labelledby": id + "-header"
     },
     PanelHeader({
       id: id + "-header",
       name: Strings.GetStringFromName("addons")
     }),
     AddonsControls({ debugDisabled }),
+    dom.div({ id: "temporary-addons" },
+      TargetList({
+        id: "temporary-extensions",
+        name: temporaryName,
+        targets: temporaryTargets,
+        client,
+        debugDisabled,
+        targetClass,
+        sort: true
+      })),
     dom.div({ id: "addons" },
       TargetList({
         id: "extensions",
-        name,
-        targets,
+        name: installedName,
+        targets: installedTargets,
         client,
         debugDisabled,
         targetClass,
         sort: true
       })
     ));
   }
 });
--- a/devtools/client/aboutdebugging/components/addons/target.js
+++ b/devtools/client/aboutdebugging/components/addons/target.js
@@ -53,17 +53,18 @@ module.exports = createClass({
     });
   },
 
   render() {
     let { target, debugDisabled } = this.props;
     // Only temporarily installed add-ons can be reloaded.
     const canBeReloaded = target.temporarilyInstalled;
 
-    return dom.li({ className: "target-container" },
+    return dom.li(
+      { className: "target-container", "data-addon-id": target.addonID },
       dom.img({
         className: "target-icon",
         role: "presentation",
         src: target.icon
       }),
       dom.div({ className: "target" },
         dom.div({ className: "target-name", title: target.name }, target.name)
       ),
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_bootstrapped.js
@@ -28,17 +28,17 @@ add_task(function* () {
   yield waitForInitialAddonList(document);
   yield installAddon({
     document,
     path: "addons/unpacked/install.rdf",
     name: ADDON_NAME,
   });
 
   // Retrieve the DEBUG button for the addon
-  let names = [...document.querySelectorAll("#addons .target-name")];
+  let names = getInstalledAddonNames(document);
   let name = names.filter(element => element.textContent === ADDON_NAME)[0];
   ok(name, "Found the addon in the list");
   let targetElement = name.parentNode.parentNode;
   let debugBtn = targetElement.querySelector(".debug-button");
   ok(debugBtn, "Found its debug button");
 
   // Wait for a notification sent by a script evaluated the test addon via
   // the web console.
--- a/devtools/client/aboutdebugging/test/browser_addons_reload.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_reload.js
@@ -26,17 +26,17 @@ function* tearDownAddon(addon) {
   const onUninstalled = promiseAddonEvent("onUninstalled");
   addon.uninstall();
   const [uninstalledAddon] = yield onUninstalled;
   is(uninstalledAddon.id, addon.id,
      `Add-on was uninstalled: ${uninstalledAddon.id}`);
 }
 
 function getReloadButton(document, addonName) {
-  const names = [...document.querySelectorAll("#addons .target-name")];
+  const names = getInstalledAddonNames(document);
   const name = names.filter(element => element.textContent === addonName)[0];
   ok(name, `Found ${addonName} add-on in the list`);
   const targetElement = name.parentNode.parentNode;
   const reloadButton = targetElement.querySelector(".reload-button");
   info(`Found reload button for ${addonName}`);
   return reloadButton;
 }
 
@@ -152,38 +152,38 @@ add_task(function* reloadButtonRefreshes
         "id": ADDON_ID
       }
     }
   };
 
   const tempExt = new TempWebExt(ADDON_ID);
   tempExt.writeManifest(manifestBase);
 
-  const onAddonListUpdated = waitForMutation(getAddonList(document),
+  const onAddonListUpdated = waitForMutation(getTemporaryAddonList(document),
                                              { childList: true });
   const onInstalled = promiseAddonEvent("onInstalled");
   yield AddonManager.installTemporaryAddon(tempExt.sourceDir);
   const [addon] = yield onInstalled;
   info(`addon installed: ${addon.id}`);
   yield onAddonListUpdated;
 
   const newName = "Temporary web extension (updated)";
   tempExt.writeManifest(Object.assign({}, manifestBase, {name: newName}));
 
   // Wait for the add-on list to be updated with the reloaded name.
   const onReInstall = promiseAddonEvent("onInstalled");
-  const onAddonReloaded = waitForContentMutation(getAddonList(document));
+  const onAddonReloaded = waitForContentMutation(getTemporaryAddonList(document));
 
   const reloadButton = getReloadButton(document, manifestBase.name);
   reloadButton.click();
 
   yield onAddonReloaded;
   const [reloadedAddon] = yield onReInstall;
   // Make sure the name was updated correctly.
-  const allAddons = [...document.querySelectorAll("#addons .target-name")]
+  const allAddons = getInstalledAddonNames(document)
     .map(element => element.textContent);
   const nameWasUpdated = allAddons.some(name => name === newName);
   ok(nameWasUpdated, `New name appeared in reloaded add-ons: ${allAddons}`);
 
   yield tearDownAddon(reloadedAddon);
   tempExt.remove();
   yield closeAboutDebugging(tab);
 });
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -84,16 +84,47 @@ function getSupportsFile(path) {
  * @return {DOMNode}                 target list or container element
  */
 function getAddonList(document) {
   return document.querySelector("#addons .target-list") ||
     document.querySelector("#addons .targets");
 }
 
 /**
+ * Depending on whether there are temporary addons installed, return either a
+ * target list element or its container.
+ * @param  {DOMDocument}  document   #temporary-addons section container document
+ * @return {DOMNode}                 target list or container element
+ */
+function getTemporaryAddonList(document) {
+  return document.querySelector("#temporary-addons .target-list") ||
+    document.querySelector("#temporary-addons .targets");
+}
+
+/**
+ * Depending on whether the addon is installed, return either the addon list
+ * element or throw an Error.
+ * @param  {DOMDocument}  document   addon section container document
+ * @return {DOMNode}                 target list
+ * @throws {Error}                   add-on not found error
+ */
+function getAddonListWithAddon(document, id) {
+  const addon = document.querySelector(`[data-addon-id="${id}"]`);
+  if (!addon) {
+    throw new Error("couldn't find add-on by id");
+  }
+  return addon.closest(".target-list");
+}
+
+function getInstalledAddonNames(document) {
+  const selector = "#addons .target-name, #temporary-addons .target-name";
+  return [...document.querySelectorAll(selector)];
+}
+
+/**
  * Depending on whether there are service workers installed, return either a
  * target list element or its container.
  * @param  {DOMDocument}  document   #service-workers section container document
  * @return {DOMNode}                 target list or container element
  */
 function getServiceWorkerList(document) {
   return document.querySelector("#service-workers .target-list") ||
     document.querySelector("#service-workers.targets");
@@ -112,17 +143,17 @@ function getTabList(document) {
 
 function* installAddon({document, path, name, isWebExtension}) {
   // Mock the file picker to select a test addon
   let MockFilePicker = SpecialPowers.MockFilePicker;
   MockFilePicker.init(null);
   let file = getSupportsFile(path);
   MockFilePicker.returnFiles = [file.file];
 
-  let addonList = getAddonList(document);
+  let addonList = getTemporaryAddonList(document);
   let addonListMutation = waitForMutation(addonList, { childList: true });
 
   let onAddonInstalled;
 
   if (isWebExtension) {
     onAddonInstalled = new Promise(done => {
       Management.on("startup", function listener(event, extension) {
         if (extension.name != name) {
@@ -153,17 +184,17 @@ function* installAddon({document, path, 
   yield addonListMutation;
   let names = [...addonList.querySelectorAll(".target-name")];
   names = names.map(element => element.textContent);
   ok(names.includes(name),
     "The addon name appears in the list of addons: " + names);
 }
 
 function* uninstallAddon({document, id, name}) {
-  let addonList = getAddonList(document);
+  let addonList = getAddonListWithAddon(document, id);
   let addonListMutation = waitForMutation(addonList, { childList: true });
 
   // Now uninstall this addon
   yield new Promise(done => {
     AddonManager.getAddonByID(id, addon => {
       let listener = {
         onUninstalled: function (uninstalledAddon) {
           if (uninstalledAddon != addon) {
@@ -334,17 +365,17 @@ function* setupTestAboutDebuggingWebExte
   yield installAddon({
     document,
     path,
     name,
     isWebExtension: true,
   });
 
   // Retrieve the DEBUG button for the addon
-  let names = [...document.querySelectorAll("#addons .target-name")];
+  let names = getInstalledAddonNames(document);
   let nameEl = names.filter(element => element.textContent === name)[0];
   ok(name, "Found the addon in the list");
   let targetElement = nameEl.parentNode.parentNode;
   let debugBtn = targetElement.querySelector(".debug-button");
   ok(debugBtn, "Found its debug button");
 
   return { tab, document, debugBtn };
 }
--- a/devtools/client/locales/en-US/aboutdebugging.properties
+++ b/devtools/client/locales/en-US/aboutdebugging.properties
@@ -56,16 +56,20 @@ moreInfo = more info
 # This string is displayed as a label of a button that allows the user to
 # load additional add-ons.
 loadTemporaryAddon = Load Temporary Add-on
 
 # 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 (selectAddonFromFile2):
 # This string is displayed as the title of the file picker that appears when
 # the user clicks the 'Load Temporary Add-on' button
 selectAddonFromFile2 = Select Manifest File or Package (.xpi)
 
 # LOCALIZATION NOTE (reload):
 # This string is displayed as a label of the button that reloads a given addon.
 reload = Reload