Bug 1557792 - Show missing private browsing rows in about:addons. r=mixedpuppy,mstriemer, a=jcristau
authorRob Wu <rob@robwu.nl>
Fri, 14 Jun 2019 15:49:07 +0000
changeset 537104 1c3f96c23a841c7a6e368d73b9ee5b9573d943a3
parent 537103 186a6085f0bc20970bf9ded561a2844b1d176ab1
child 537105 2b7fc852c4c72321cebc915201b419ffc388bdd9
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy, mstriemer, jcristau
bugs1557792, 1551947
milestone68.0
Bug 1557792 - Show missing private browsing rows in about:addons. r=mixedpuppy,mstriemer, a=jcristau For parity with the XUL about:addons page, this commit adds missing rows to the details view: - Bug 1557792 - incognito not_allowed should show "Not Allowed in Private Windows" - Bug 1551947 - extensions with locked private browsing flag should not show inputs to control the flag, but "Requires Access to Private Windows" Differential Revision: https://phabricator.services.mozilla.com/D34936
toolkit/mozapps/extensions/content/aboutaddons.html
toolkit/mozapps/extensions/content/aboutaddons.js
toolkit/mozapps/extensions/test/browser/browser_webext_incognito.js
--- a/toolkit/mozapps/extensions/content/aboutaddons.html
+++ b/toolkit/mozapps/extensions/content/aboutaddons.html
@@ -132,32 +132,44 @@
                 <span data-l10n-id="addon-detail-updates-radio-on"></span>
               </label>
               <label>
                 <input type="radio" name="autoupdate" value="0" data-telemetry-value="">
                 <span data-l10n-id="addon-detail-updates-radio-off"></span>
               </label>
             </div>
           </div>
-          <div class="addon-detail-row addon-detail-row-has-help addon-detail-row-private-browsing">
+          <div class="addon-detail-row addon-detail-row-has-help addon-detail-row-private-browsing" hidden>
             <label data-l10n-id="detail-private-browsing-label"></label>
             <div>
               <label>
                 <input type="radio" name="private-browsing" value="1" data-telemetry-value="on">
                 <span data-l10n-id="addon-detail-private-browsing-allow"></span>
               </label>
               <label>
                 <input type="radio" name="private-browsing" value="0" data-telemetry-value="off">
                 <span data-l10n-id="addon-detail-private-browsing-disallow"></span>
               </label>
             </div>
           </div>
-          <div class="addon-detail-row addon-detail-help-row" data-l10n-id="addon-detail-private-browsing-help">
+          <div class="addon-detail-row addon-detail-help-row" data-l10n-id="addon-detail-private-browsing-help" hidden>
             <a target="_blank" data-l10n-name="learn-more"></a>
           </div>
+          <div class="addon-detail-row addon-detail-row-has-help addon-detail-row-private-browsing-disallowed" hidden>
+            <label data-l10n-id="detail-private-disallowed-label"></label>
+          </div>
+          <div class="addon-detail-row addon-detail-help-row" data-l10n-id="detail-private-disallowed-description" hidden>
+            <label class="text-link" data-l10n-name="detail-private-browsing-learn-more" action="pb-learn-more" aria-role="link" tabindex="0"></label>
+          </div>
+          <div class="addon-detail-row addon-detail-row-has-help addon-detail-row-private-browsing-required" hidden>
+            <label class="learn-more-label-link" data-l10n-id="detail-private-required-label"></label>
+          </div>
+          <div class="addon-detail-row addon-detail-help-row" data-l10n-id="detail-private-required-description" hidden>
+            <label class="text-link" data-l10n-name="detail-private-browsing-learn-more" action="pb-learn-more" aria-role="link" tabindex="0"></label>
+          </div>
           <div class="addon-detail-row addon-detail-row-author">
             <label data-l10n-id="addon-detail-author-label"></label>
             <a target="_blank" data-telemetry-name="author"></a>
           </div>
           <div class="addon-detail-row addon-detail-row-version">
             <label data-l10n-id="addon-detail-version-label"></label>
           </div>
           <div class="addon-detail-row addon-detail-row-lastUpdated">
--- a/toolkit/mozapps/extensions/content/aboutaddons.js
+++ b/toolkit/mozapps/extensions/content/aboutaddons.js
@@ -58,16 +58,17 @@ const PLUGIN_ICON_URL = "chrome://global
 const PERMISSION_MASKS = {
   "ask-to-activate": AddonManager.PERM_CAN_ASK_TO_ACTIVATE,
   enable: AddonManager.PERM_CAN_ENABLE,
   "always-activate": AddonManager.PERM_CAN_ENABLE,
   disable: AddonManager.PERM_CAN_DISABLE,
   "never-activate": AddonManager.PERM_CAN_DISABLE,
   uninstall: AddonManager.PERM_CAN_UNINSTALL,
   upgrade: AddonManager.PERM_CAN_UPGRADE,
+  "change-privatebrowsing": AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS,
 };
 
 const PREF_DISCOVERY_API_URL = "extensions.getAddons.discovery.api_url";
 const PREF_THEME_RECOMMENDATION_URL =
   "extensions.recommendations.themeRecommendationUrl";
 const PREF_PRIVACY_POLICY_URL = "extensions.recommendations.privacyPolicyUrl";
 const PREF_RECOMMENDATION_ENABLED = "browser.discovery.enabled";
 const PREF_TELEMETRY_ENABLED = "datareporting.healthreport.uploadEnabled";
@@ -1161,21 +1162,23 @@ class AddonPermissionsList extends HTMLE
 customElements.define("addon-permissions-list", AddonPermissionsList);
 
 class AddonDetails extends HTMLElement {
   connectedCallback() {
     if (this.children.length == 0) {
       this.render();
     }
     this.deck.addEventListener("view-changed", this);
+    this.addEventListener("keypress", this);
   }
 
   disconnectedCallback() {
     this.inlineOptions.destroyBrowser();
     this.deck.removeEventListener("view-changed", this);
+    this.removeEventListener("keypress", this);
   }
 
   handleEvent(e) {
     if (e.type == "view-changed" && e.target == this.deck) {
       switch (this.deck.selectedViewName) {
         case "release-notes":
           AMTelemetry.recordActionEvent({
             object: "aboutAddons",
@@ -1190,16 +1193,21 @@ class AddonDetails extends HTMLElement {
           }
           break;
         case "preferences":
           if (getOptionsType(this.addon) == "inline") {
             this.inlineOptions.ensureBrowserCreated();
           }
           break;
       }
+    } else if (e.type == "keypress") {
+      if (e.keyCode == KeyEvent.DOM_VK_RETURN &&
+          e.target.getAttribute("action") === "pb-learn-more") {
+        e.target.click();
+      }
     }
   }
 
   get releaseNotesUri() {
     return this.addon.updateInstall ?
       this.addon.updateInstall.releaseNotesURI : this.addon.releaseNotesURI;
   }
 
@@ -1273,29 +1281,39 @@ class AddonDetails extends HTMLElement {
       description.appendChild(nl2br(addon.fullDescription));
     }
 
     this.querySelector(".addon-detail-contribute").hidden =
       !addon.contributionURL;
     this.querySelector(".addon-detail-row-updates").hidden =
       !hasPermission(addon, "upgrade");
 
-    let pbRow = this.querySelector(".addon-detail-row-private-browsing");
-    if (!allowPrivateBrowsingByDefault && addon.type == "extension" &&
-        addon.incognito != "not_allowed") {
+    // By default, all private browsing rows are hidden. Possibly show one.
+    if (allowPrivateBrowsingByDefault || addon.type != "extension") {
+      // All add-addons of this type are allowed in private browsing mode, so
+      // do not show any UI.
+    } else if (addon.incognito == "not_allowed") {
+      let pbRowNotAllowed =
+        this.querySelector(".addon-detail-row-private-browsing-disallowed");
+      pbRowNotAllowed.hidden = false;
+      pbRowNotAllowed.nextElementSibling.hidden = false;
+    } else if (!hasPermission(addon, "change-privatebrowsing")) {
+      let pbRowRequired =
+        this.querySelector(".addon-detail-row-private-browsing-required");
+      pbRowRequired.hidden = false;
+      pbRowRequired.nextElementSibling.hidden = false;
+    } else {
+      let pbRow = this.querySelector(".addon-detail-row-private-browsing");
+      pbRow.hidden = false;
+      pbRow.nextElementSibling.hidden = false;
       let isAllowed = await isAllowedInPrivateBrowsing(addon);
       pbRow.querySelector(`[value="${isAllowed ? 1 : 0}"]`).checked = true;
       let learnMore = pbRow.nextElementSibling
         .querySelector('a[data-l10n-name="learn-more"]');
       learnMore.href = SUPPORT_URL + "extensions-pb";
-    } else {
-      // Remove the help row, which is right after the settings.
-      pbRow.nextElementSibling.hidden = true;
-      // Then remove the actual settings.
-      pbRow.hidden = true;
     }
 
     // Author.
     let creatorRow = this.querySelector(".addon-detail-row-author");
     if (addon.creator) {
       let link = creatorRow.querySelector("a");
       link.hidden = !addon.creator.url;
       if (link.hidden) {
@@ -1551,16 +1569,20 @@ class AddonCard extends HTMLElement {
           openAbuseReport({addonId: addon.id, reportEntryPoint: "menu"});
           break;
         case "link":
           if (e.target.getAttribute("url")) {
             windowRoot.ownerGlobal.openWebLinkIn(
               e.target.getAttribute("url"), "tab");
           }
           break;
+        case "pb-learn-more":
+          windowRoot.ownerGlobal.openTrustedLinkIn(
+            SUPPORT_URL + "extensions-pb", "tab");
+          break;
         default:
           // Handle a click on the card itself.
           if (!this.expanded) {
             loadViewFn("detail", this.addon.id);
           } else if (e.target.localName == "a" &&
                      e.target.getAttribute("data-telemetry-name")) {
             let value = e.target.getAttribute("data-telemetry-name");
             AMTelemetry.recordLinkEvent({
--- a/toolkit/mozapps/extensions/test/browser/browser_webext_incognito.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_webext_incognito.js
@@ -86,63 +86,78 @@ async function setPrivateBrowsingValue(v
 // Check whether the private browsing inputs are visible in the details view.
 function checkIsModifiable(expected) {
   if (gManagerWindow.useHtmlViews) {
     if (expected) {
       is_element_visible(getHtmlElem(".addon-detail-row-private-browsing"), "Private browsing should be visible");
     } else {
       is_element_hidden(getHtmlElem(".addon-detail-row-private-browsing"), "Private browsing should be hidden");
     }
+    checkHelpRow(".addon-detail-row-private-browsing", expected);
     return;
   }
   if (expected) {
     is_element_visible(get("detail-privateBrowsing-row"), "Private browsing should be visible");
     is_element_visible(get("detail-privateBrowsing-row-footer"), "Private browsing footer should be visible");
   } else {
     is_element_hidden(get("detail-privateBrowsing-row"), "Private browsing should be hidden");
     is_element_hidden(get("detail-privateBrowsing-row-footer"), "Private browsing footer should be hidden");
   }
 }
 
 // Check whether the details view shows that private browsing is forcibly disallowed.
 function checkIsDisallowed(expected) {
   if (gManagerWindow.useHtmlViews) {
-    // TODO bug 1557792: Show when private browsing is forcibly disallowed.
     if (expected) {
-      is_element_hidden(getHtmlElem(".addon-detail-row-private-browsing"), "Private browsing cannot both be allowed and disallowed");
+      is_element_visible(getHtmlElem(".addon-detail-row-private-browsing-disallowed"), "Private browsing should be disallowed");
+    } else {
+      is_element_hidden(getHtmlElem(".addon-detail-row-private-browsing-disallowed"), "Private browsing should not be disallowed");
     }
+    checkHelpRow(".addon-detail-row-private-browsing-disallowed", expected);
     return;
   }
   if (expected) {
     is_element_visible(get("detail-privateBrowsing-disallowed"), "Private browsing should be disallowed");
     is_element_visible(get("detail-privateBrowsing-disallowed-footer"), "Private browsing footer should be disallowed");
   } else {
     is_element_hidden(get("detail-privateBrowsing-disallowed"), "Private browsing should not be disallowed");
     is_element_hidden(get("detail-privateBrowsing-disallowed-footer"), "Private browsing footer should not be disallowed");
   }
 }
 
 // Check whether the details view shows that private browsing is forcibly allowed.
 function checkIsRequired(expected) {
   if (gManagerWindow.useHtmlViews) {
-    // TODO bug 1557792: Show when private browsing is forcibly allowed.
     if (expected) {
-      is_element_hidden(getHtmlElem(".addon-detail-row-private-browsing"), "Private browsing cannot both be mutable and required");
+      is_element_visible(getHtmlElem(".addon-detail-row-private-browsing-required"), "Private browsing should be required");
+    } else {
+      is_element_hidden(getHtmlElem(".addon-detail-row-private-browsing-required"), "Private browsing should not be required");
     }
+    checkHelpRow(".addon-detail-row-private-browsing-required", expected);
     return;
   }
   if (expected) {
     is_element_visible(get("detail-privateBrowsing-required"), "Private required should be visible");
     is_element_visible(get("detail-privateBrowsing-required-footer"), "Private required footer should be visible");
   } else {
     is_element_hidden(get("detail-privateBrowsing-required"), "Private required should be hidden");
     is_element_hidden(get("detail-privateBrowsing-required-footer"), "Private required footer should be hidden");
   }
 }
 
+function checkHelpRow(selector, expected) {
+  let helpRow = getHtmlElem(`${selector} + .addon-detail-help-row`);
+  if (expected) {
+    is_element_visible(helpRow, `Help row should be shown: ${selector}`);
+    is_element_visible(helpRow.querySelector("a, [action='pb-learn-more']"), "Expected learn more link");
+  } else {
+    is_element_hidden(helpRow, `Help row should be hidden: ${selector}`);
+  }
+}
+
 async function hasPrivateAllowed(id) {
   let perms = await ExtensionPermissions.get(id);
   return perms.permissions.length == 1 &&
          perms.permissions[0] == "internal:privateBrowsingAllowed";
 }
 
 add_task(function clearInitialTelemetry() {
   // Clear out any telemetry data that existed before this file is run.
@@ -486,19 +501,58 @@ async function test_addon_postinstall_in
       checkIsModifiable(false);
       checkIsRequired(false);
       checkIsDisallowed(false);
     }
     await close_manager(gManagerWindow);
 
     await addon.uninstall();
   }
+
+  // It is not possible to create a privileged add-on and install it, so just
+  // simulate an installed privileged add-on and check the UI.
+  await test_incognito_of_privileged_addons();
+
   // No popPrefEnv because of bug 1557397.
 }
 
+// Checks that the private browsing flag of privileged add-ons cannot be modified.
+async function test_incognito_of_privileged_addons() {
+  // In mochitests it is not possible to create and install a privileged add-on
+  // or a system add-on, so create a mock provider that simulates privileged
+  // add-ons (which lack the PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS permission).
+  let provider = new MockProvider();
+  provider.createAddons([{
+    name: "default incognito",
+    id: "default-incognito@mock",
+    incognito: "spanning", // This is the default.
+    // Anything without the PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS permission.
+    permissions: 0,
+  }, {
+    name: "not_allowed incognito",
+    id: "not-allowed-incognito@mock",
+    incognito: "not_allowed",
+    // Anything without the PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS permission.
+    permissions: 0,
+  }]);
+
+  gManagerWindow = await open_manager("addons://detail/default-incognito%40mock");
+  checkIsModifiable(false);
+  checkIsRequired(true);
+  checkIsDisallowed(false);
+  await close_manager(gManagerWindow);
+
+  gManagerWindow = await open_manager("addons://detail/not-allowed-incognito%40mock");
+  checkIsModifiable(false);
+  checkIsRequired(false);
+  checkIsDisallowed(true);
+  await close_manager(gManagerWindow);
+
+  provider.unregister();
+}
 
 add_task(async function test_badge_and_toggle_incognito_on_XUL_aboutaddons() {
   await SpecialPowers.pushPrefEnv({
     set: [["extensions.htmlaboutaddons.enabled", false]],
   });
   await test_badge_and_toggle_incognito();
   // No popPrefEnv because of bug 1557397.
 });