Bug 1557175 - Hide inline options when add-on is disabled r=rpl
authorMark Striemer <mstriemer@mozilla.com>
Thu, 01 Aug 2019 18:55:30 +0000
changeset 488315 140cb3a190e91200a67ac8fcfeebc5194d5cfa81
parent 488314 8f031439c3bcc4c1f9a0859500c1bd22eaeb622c
child 488316 dabda4e90259abe8a2fc766549a0dfd77beb6c31
push id113906
push userncsoregi@mozilla.com
push dateFri, 16 Aug 2019 04:07:24 +0000
treeherdermozilla-inbound@d887276421d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrpl
bugs1557175
milestone70.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 1557175 - Hide inline options when add-on is disabled r=rpl Differential Revision: https://phabricator.services.mozilla.com/D35068
toolkit/mozapps/extensions/content/aboutaddons.js
toolkit/mozapps/extensions/test/browser/browser_html_detail_view.js
toolkit/mozapps/extensions/test/browser/browser_html_options_ui.js
--- a/toolkit/mozapps/extensions/content/aboutaddons.js
+++ b/toolkit/mozapps/extensions/content/aboutaddons.js
@@ -122,18 +122,20 @@ const AddonCardListenerHandler = {
     this.delegateEvent(name, addon, args);
   },
 
   delegateEvent(name, addon, args) {
     let cards;
     if (this.MANAGER_EVENTS.has(name)) {
       cards = document.querySelectorAll("addon-card");
     } else {
-      let card = document.querySelector(`addon-card[addon-id="${addon.id}"]`);
-      cards = card ? [card] : [];
+      let cardSelector = `addon-card[addon-id="${addon.id}"]`;
+      cards = document.querySelectorAll(
+        `${cardSelector}, ${cardSelector} addon-details`
+      );
     }
     for (let card of cards) {
       try {
         if (name in card) {
           card[name](...args);
         }
       } catch (e) {
         Cu.reportError(e);
@@ -1292,16 +1294,44 @@ class AddonDetails extends HTMLElement {
         e.keyCode == KeyEvent.DOM_VK_RETURN &&
         e.target.getAttribute("action") === "pb-learn-more"
       ) {
         e.target.click();
       }
     }
   }
 
+  onInstalled() {
+    let policy = WebExtensionPolicy.getByID(this.addon.id);
+    let extension = policy && policy.extension;
+    if (extension && extension.startupReason === "ADDON_UPGRADE") {
+      // Ensure the options browser is recreated when a new version starts.
+      this.extensionShutdown();
+      this.extensionStartup();
+    }
+  }
+
+  onDisabled(addon) {
+    this.extensionShutdown();
+  }
+
+  onEnabled(addon) {
+    this.extensionStartup();
+  }
+
+  extensionShutdown() {
+    this.inlineOptions.destroyBrowser();
+  }
+
+  extensionStartup() {
+    if (this.deck.selectedViewName === "preferences") {
+      this.inlineOptions.ensureBrowserCreated();
+    }
+  }
+
   get releaseNotesUri() {
     return this.addon.updateInstall
       ? this.addon.updateInstall.releaseNotesURI
       : this.addon.releaseNotesURI;
   }
 
   setAddon(addon) {
     this.addon = addon;
@@ -1314,17 +1344,21 @@ class AddonDetails extends HTMLElement {
     let getButtonByName = name =>
       this.tabGroup.querySelector(`[name="${name}"]`);
     let permsBtn = getButtonByName("permissions");
     permsBtn.hidden = addon.type != "extension";
     let notesBtn = getButtonByName("release-notes");
     notesBtn.hidden = !this.releaseNotesUri;
     let prefsBtn = getButtonByName("preferences");
     prefsBtn.hidden = getOptionsType(addon) !== "inline";
-    if (!prefsBtn.hidden) {
+    if (prefsBtn.hidden) {
+      if (this.deck.selectedViewName === "preferences") {
+        this.deck.selectedViewName = "details";
+      }
+    } else {
       isAddonOptionsUIAllowed(addon).then(allowed => {
         prefsBtn.hidden = !allowed;
       });
     }
 
     // Hide the tab group if "details" is the only visible button.
     this.tabGroup.hidden = Array.from(this.tabGroup.children).every(button => {
       return button.name == "details" || button.hidden;
@@ -1527,21 +1561,17 @@ class AddonCard extends HTMLElement {
     }
   }
 
   get reloading() {
     return this.hasAttribute("reloading");
   }
 
   set reloading(val) {
-    if (val) {
-      this.setAttribute("reloading", "true");
-    } else {
-      this.removeAttribute("reloading");
-    }
+    this.toggleAttribute("reloading", val);
   }
 
   /**
    * Set the add-on for this card. The card will be populated based on the
    * add-on when it is connected to the DOM.
    *
    * @param {AddonWrapper} addon The add-on to use.
    */
--- a/toolkit/mozapps/extensions/test/browser/browser_html_detail_view.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_detail_view.js
@@ -765,24 +765,41 @@ add_task(async function testPrivateBrows
 
   // The badge is still hidden on the detail view.
   card = getAddonCard(doc, id);
   badge = card.querySelector(".addon-badge-private-browsing-allowed");
   ok(badge.hidden, "The PB badge is hidden on the detail view");
   ok(!(await hasPrivateAllowed(id)), "PB is not allowed");
 
   let pbRow = card.querySelector(".addon-detail-row-private-browsing");
+  let name = card.querySelector(".addon-name");
 
   // Allow private browsing.
   let [allow, disallow] = pbRow.querySelectorAll("input");
   let updated = BrowserTestUtils.waitForEvent(card, "update");
+
+  // Check that the disabled state isn't shown while reloading the add-on.
+  let addonDisabled = AddonTestUtils.promiseAddonEvent("onDisabled");
   allow.click();
+  await addonDisabled;
+  is(
+    doc.l10n.getAttributes(name).id,
+    null,
+    "The disabled message is not shown for the add-on"
+  );
+
+  // Check the PB stuff.
   await updated;
   ok(!badge.hidden, "The PB badge is now shown");
   ok(await hasPrivateAllowed(id), "PB is allowed");
+  is(
+    doc.l10n.getAttributes(name).id,
+    null,
+    "The disabled message is not shown for the add-on"
+  );
 
   // Disable the add-on and change the value.
   updated = BrowserTestUtils.waitForEvent(card, "update");
   card.querySelector('[action="toggle-disabled"]').click();
   await updated;
 
   // It's still allowed in PB.
   ok(!badge.hidden, "The PB badge is shown");
--- a/toolkit/mozapps/extensions/test/browser/browser_html_options_ui.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_options_ui.js
@@ -4,16 +4,20 @@ const { AddonTestUtils } = ChromeUtils.i
   "resource://testing-common/AddonTestUtils.jsm"
 );
 const { ExtensionParent } = ChromeUtils.import(
   "resource://gre/modules/ExtensionParent.jsm"
 );
 
 AddonTestUtils.initMochitest(this);
 
+function getAddonCard(doc, id) {
+  return doc.querySelector(`addon-card[addon-id="${id}"]`);
+}
+
 // This test function helps to detect when an addon options browser have been
 // inserted in the about:addons page.
 function waitOptionsBrowserInserted() {
   return new Promise(resolve => {
     async function listener(eventName, browser) {
       // wait for a webextension XUL browser element that is owned by the
       // "about:addons" page.
       if (browser.ownerGlobal.top.location.href == "about:addons") {
@@ -73,17 +77,17 @@ add_task(async function testInlineOption
     useAddonManager: "temporary",
   });
   await extension.startup();
 
   let win = await loadInitialView("extension");
   let doc = win.document;
 
   // Make sure we found the right card.
-  let card = doc.querySelector(`addon-card[addon-id="${id}"]`);
+  let card = getAddonCard(doc, id);
   ok(card, "Found the card");
 
   // The preferences option should be visible.
   let preferences = card.querySelector('[action="preferences"]');
   ok(!preferences.hidden, "The preferences option is visible");
 
   // Open the preferences page.
   let loaded = waitForViewLoad(win);
@@ -202,17 +206,17 @@ add_task(async function testCardRerender
     },
     useAddonManager: "permanent",
   });
   await extension.startup();
 
   let win = await loadInitialView("extension");
   let doc = win.document;
 
-  let card = doc.querySelector(`addon-card[addon-id="${id}"]`);
+  let card = getAddonCard(doc, id);
   let loaded = waitForViewLoad(win);
   card.querySelector('[action="expand"]').click();
   await loaded;
 
   card = doc.querySelector("addon-card");
 
   let browserAdded = waitOptionsBrowserInserted();
   card.querySelector('named-deck-button[name="preferences"]').click();
@@ -221,41 +225,57 @@ add_task(async function testCardRerender
   is(
     doc.querySelectorAll("inline-options-browser").length,
     1,
     "There is 1 inline-options-browser"
   );
   is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
 
   info("Reload the add-on and ensure there's still only one browser");
-  let updated = BrowserTestUtils.waitForEvent(card, "update", () => {
-    // Wait for the update when the add-on name isn't the disabled text.
-    return !card.querySelector(".addon-name").hasAttribute("data-l10n-name");
-  });
+  let updated = BrowserTestUtils.waitForEvent(card, "update");
   card.addon.reload();
   await updated;
 
+  // Since the add-on was disabled, we'll be on the details tab.
+  is(card.details.deck.selectedViewName, "details", "View changed to details");
   is(
     doc.querySelectorAll("inline-options-browser").length,
     1,
     "There is 1 inline-options-browser"
   );
-  is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
+  is(doc.querySelectorAll("browser").length, 0, "The browser was destroyed");
+
+  // Load the permissions tab again.
+  browserAdded = waitOptionsBrowserInserted();
+  card.querySelector('named-deck-button[name="preferences"]').click();
+  await browserAdded;
+
+  // Switching to preferences will create a new browser element.
+  is(
+    card.details.deck.selectedViewName,
+    "preferences",
+    "View switched to preferences"
+  );
+  is(
+    doc.querySelectorAll("inline-options-browser").length,
+    1,
+    "There is 1 inline-options-browser"
+  );
+  is(doc.querySelectorAll("browser").length, 1, "There is a new browser");
 
   info("Re-rendering card to ensure a second browser isn't added");
   updated = BrowserTestUtils.waitForEvent(card, "update");
   card.render();
   await updated;
 
   is(
     card.details.deck.selectedViewName,
     "details",
     "Rendering reverted to the details view"
   );
-
   is(
     doc.querySelectorAll("inline-options-browser").length,
     1,
     "There is still only 1 inline-options-browser after re-render"
   );
   is(doc.querySelectorAll("browser").length, 0, "There is no browser");
 
   let newBrowserAdded = waitOptionsBrowserInserted();
@@ -267,8 +287,203 @@ add_task(async function testCardRerender
     1,
     "There is still only 1 inline-options-browser after opening preferences"
   );
   is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
 
   await closeView(win);
   await extension.unload();
 });
+
+add_task(async function testRemovedOnDisable() {
+  let id = "disable@mochi.test";
+  const xpiFile = AddonTestUtils.createTempWebExtensionFile({
+    manifest: {
+      applications: { gecko: { id } },
+      options_ui: {
+        page: "options.html",
+      },
+    },
+    files: {
+      "options.html": "<h1>Options!</h1>",
+    },
+  });
+  let addon = await AddonManager.installTemporaryAddon(xpiFile);
+
+  let win = await loadInitialView("extension");
+  let doc = win.document;
+
+  // Opens the prefs page.
+  let loaded = waitForViewLoad(win);
+  getAddonCard(doc, id)
+    .querySelector("[action=preferences]")
+    .click();
+  await loaded;
+
+  let inlineOptions = doc.querySelector("inline-options-browser");
+  ok(inlineOptions, "There's an inline-options-browser element");
+  ok(inlineOptions.querySelector("browser"), "The browser exists");
+
+  let card = getAddonCard(doc, id);
+  let { deck } = card.details;
+  is(deck.selectedViewName, "preferences", "Preferences are the active tab");
+
+  info("Disabling the add-on");
+  let updated = BrowserTestUtils.waitForEvent(card, "update");
+  await addon.disable();
+  await updated;
+
+  is(deck.selectedViewName, "details", "Details are now the active tab");
+  ok(inlineOptions, "There's an inline-options-browser element");
+  ok(!inlineOptions.querySelector("browser"), "The browser has been removed");
+
+  info("Enabling the add-on");
+  updated = BrowserTestUtils.waitForEvent(card, "update");
+  await addon.enable();
+  await updated;
+
+  is(deck.selectedViewName, "details", "Details are still the active tab");
+  ok(inlineOptions, "There's an inline-options-browser element");
+  ok(!inlineOptions.querySelector("browser"), "The browser is not created yet");
+
+  info("Switching to preferences tab");
+  let changed = BrowserTestUtils.waitForEvent(deck, "view-changed");
+  let browserAdded = waitOptionsBrowserInserted();
+  deck.selectedViewName = "preferences";
+  await changed;
+  await browserAdded;
+
+  is(deck.selectedViewName, "preferences", "Preferences are selected");
+  ok(inlineOptions, "There's an inline-options-browser element");
+  ok(inlineOptions.querySelector("browser"), "The browser is re-created");
+
+  await closeView(win);
+  await addon.uninstall();
+});
+
+add_task(async function testUpgradeTemporary() {
+  let id = "upgrade-temporary@mochi.test";
+  async function loadExtension(version) {
+    let extension = ExtensionTestUtils.loadExtension({
+      manifest: {
+        applications: { gecko: { id } },
+        version,
+        options_ui: {
+          page: "options.html",
+        },
+      },
+      files: {
+        "options.html": `
+          <html>
+            <head>
+              <script src="options.js"></script>
+            </head>
+            <body>
+              <p>Version <pre>${version}</pre></p>
+            </body>
+          </html>
+        `,
+        "options.js": () => {
+          browser.test.onMessage.addListener(msg => {
+            if (msg === "get-version") {
+              let version = document.querySelector("pre").textContent;
+              browser.test.sendMessage("version", version);
+            }
+          });
+          browser.test.sendMessage("options-loaded");
+        },
+      },
+      useAddonManager: "temporary",
+    });
+    await extension.startup();
+    return extension;
+  }
+
+  let firstExtension = await loadExtension("1");
+  let win = await loadInitialView("extension");
+  let doc = win.document;
+
+  let card = getAddonCard(doc, id);
+  let loaded = waitForViewLoad(win);
+  card.querySelector('[action="expand"]').click();
+  await loaded;
+
+  card = doc.querySelector("addon-card");
+  let browserAdded = waitOptionsBrowserInserted();
+  card.querySelector('named-deck-button[name="preferences"]').click();
+  await browserAdded;
+
+  await firstExtension.awaitMessage("options-loaded");
+  await firstExtension.sendMessage("get-version");
+  let version = await firstExtension.awaitMessage("version");
+  is(version, "1", "Version 1 page is loaded");
+
+  let updated = BrowserTestUtils.waitForEvent(card, "update");
+  browserAdded = waitOptionsBrowserInserted();
+  let secondExtension = await loadExtension("2");
+  await updated;
+  await browserAdded;
+  await secondExtension.awaitMessage("options-loaded");
+
+  await secondExtension.sendMessage("get-version");
+  version = await secondExtension.awaitMessage("version");
+  is(version, "2", "Version 2 page is loaded");
+  let { deck } = card.details;
+  is(deck.selectedViewName, "preferences", "Preferences are still shown");
+
+  await closeView(win);
+  await firstExtension.unload();
+  await secondExtension.unload();
+});
+
+add_task(async function testReloadExtension() {
+  let id = "reload@mochi.test";
+  let xpiFile = AddonTestUtils.createTempWebExtensionFile({
+    manifest: {
+      applications: { gecko: { id } },
+      options_ui: {
+        page: "options.html",
+      },
+    },
+    files: {
+      "options.html": `
+        <html>
+          <head>
+          </head>
+          <body>
+            <p>Options</p>
+          </body>
+        </html>
+      `,
+    },
+  });
+  let addon = await AddonManager.installTemporaryAddon(xpiFile);
+
+  let win = await loadInitialView("extension");
+  let doc = win.document;
+
+  let card = getAddonCard(doc, id);
+  let loaded = waitForViewLoad(win);
+  card.querySelector('[action="expand"]').click();
+  await loaded;
+
+  card = doc.querySelector("addon-card");
+  let { deck } = card.details;
+  is(deck.selectedViewName, "details", "Details load first");
+
+  let browserAdded = waitOptionsBrowserInserted();
+  card.querySelector('named-deck-button[name="preferences"]').click();
+  await browserAdded;
+
+  is(deck.selectedViewName, "preferences", "Preferences are shown");
+
+  let updated = BrowserTestUtils.waitForEvent(card, "update");
+  browserAdded = waitOptionsBrowserInserted();
+  let addonStarted = AddonTestUtils.promiseWebExtensionStartup(id);
+  await addon.reload();
+  await addonStarted;
+  await updated;
+  await browserAdded;
+  is(deck.selectedViewName, "preferences", "Preferences are still shown");
+
+  await closeView(win);
+  await addon.uninstall();
+});