Bug 1538546 - Hide incognito checkbox in addon-installed notification for not_allowed extensions. r=mixedpuppy,kmag a=pascalc
authorLuca Greco <lgreco@mozilla.com>
Tue, 26 Mar 2019 18:31:57 +0000
changeset 525821 8935c8db2301fb744209e76212a4222bd0d4ede7
parent 525820 44b4f787bfeb2e6888e58488dfec84acafc15941
child 525822 381133904780a376d4991883a605457b2faecc86
push id2032
push userffxbld-merge
push dateMon, 13 May 2019 09:36:57 +0000
treeherdermozilla-release@455c1065dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy, kmag, pascalc
bugs1538546
milestone67.0
Bug 1538546 - Hide incognito checkbox in addon-installed notification for not_allowed extensions. r=mixedpuppy,kmag a=pascalc Differential Revision: https://phabricator.services.mozilla.com/D24719
browser/modules/ExtensionsUI.jsm
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
toolkit/mozapps/extensions/test/browser/browser_extension_sideloading_permission.js
toolkit/mozapps/extensions/test/browser/browser_webapi_install.js
toolkit/mozapps/extensions/test/browser/browser_webapi_theme.js
toolkit/mozapps/extensions/test/browser/browser_webext_incognito.js
toolkit/mozapps/extensions/test/browser/head.js
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -450,17 +450,17 @@ var ExtensionsUI = {
 
     let message = bundle.getFormattedString("addonPostInstall.message1",
                                             ["<>", appName]);
     return new Promise(resolve => {
       // Show or hide private permission ui based on the pref.
       function setCheckbox(win) {
         let checkbox = win.document.getElementById("addon-incognito-checkbox");
         checkbox.checked = false;
-        checkbox.hidden = allowPrivateBrowsingByDefault || addon.type !== "extension";
+        checkbox.hidden = !(addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS);
       }
       setCheckbox(window);
 
       async function actionResolve(win) {
         let checkbox = win.document.getElementById("addon-incognito-checkbox");
         if (checkbox.checked) {
           let perms = {permissions: ["internal:privateBrowsingAllowed"], origins: []};
           await ExtensionPermissions.add(addon.id, perms);
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -35,16 +35,19 @@ XPCOMUtils.defineLazyModuleGetters(this,
   LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   UpdateChecker: "resource://gre/modules/addons/XPIInstall.jsm",
   XPIInstall: "resource://gre/modules/addons/XPIInstall.jsm",
   XPIInternal: "resource://gre/modules/addons/XPIProvider.jsm",
   XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm",
   verifyBundleSignedState: "resource://gre/modules/addons/XPIInstall.jsm",
 });
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "allowPrivateBrowsingByDefault",
+                                      "extensions.allowPrivateBrowsingByDefault", true);
+
 const {nsIBlocklistService} = Ci;
 
 // These are injected from XPIProvider.jsm
 /* globals
  *         BOOTSTRAP_REASONS,
  *         DB_SCHEMA,
  *         XPIStates,
  *         migrateAddonLoader
@@ -614,17 +617,18 @@ class AddonInternal {
       }
 
       permissions |= AddonManager.PERM_CAN_UNINSTALL;
     }
 
     // The permission to "toggle the private browsing access" is locked down
     // when the extension has opted out or it gets the permission automatically
     // on every extension startup (as system, privileged and builtin addons).
-    if (this.incognito !== "not_allowed" &&
+    if (!allowPrivateBrowsingByDefault && this.type === "extension" &&
+        this.incognito !== "not_allowed" &&
         this.signedState !== AddonManager.SIGNEDSTATE_PRIVILEGED &&
         this.signedState !== AddonManager.SIGNEDSTATE_SYSTEM &&
         !this.location.isBuiltin) {
       permissions |= AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS;
     }
 
     if (Services.policies &&
         !Services.policies.isAllowed(`modify-extension:${this.id}`)) {
--- a/toolkit/mozapps/extensions/test/browser/browser_extension_sideloading_permission.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_extension_sideloading_permission.js
@@ -75,17 +75,17 @@ add_task(async function test() {
     manager.document.getAnonymousElementByAttribute(addon, "anonid", "enable-btn"),
     { clickCount: 1 },
     manager
   );
 
   panel = await popupPromise;
   ok(PopupNotifications.isPanelOpen, "Permission popup should be visible");
 
-  let notificationPromise = acceptAppMenuNotificationWhenShown("addon-installed", "extension");
+  let notificationPromise = acceptAppMenuNotificationWhenShown("addon-installed", ADDON_ID);
 
   panel.button.click();
   ok(!PopupNotifications.isPanelOpen, "Permission popup should be closed / closing");
   await notificationPromise;
 
   addon = await AddonManager.getAddonByID(ADDON_ID);
   ok(addon.seen, "Seen flag should be true after permissions are accepted");
 
--- a/toolkit/mozapps/extensions/test/browser/browser_webapi_install.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_install.js
@@ -1,10 +1,11 @@
 const TESTPAGE = `${SECURE_TESTROOT}webapi_checkavailable.html`;
 const XPI_URL = `${SECURE_TESTROOT}../xpinstall/amosigned.xpi`;
+const XPI_ADDON_ID = "amosigned-xpi@tests.mozilla.org";
 
 const XPI_SHA = "sha256:91121ed2c27f670f2307b9aebdd30979f147318c7fb9111c254c14ddbb84e4b0";
 
 const ID = "amosigned-xpi@tests.mozilla.org";
 // eh, would be good to just stat the real file instead of this...
 const XPI_LEN = 4287;
 
 function waitForClear() {
@@ -199,17 +200,17 @@ function makeRegularTest(options, what) 
       },
     ];
 
     let installPromptPromise =
       promisePopupNotificationShown("addon-webext-permissions").then(panel => {
         panel.button.click();
       });
 
-    let promptPromise = acceptAppMenuNotificationWhenShown("addon-installed", "extension");
+    let promptPromise = acceptAppMenuNotificationWhenShown("addon-installed", options.addonId);
 
     await testInstall(browser, options, steps, what);
 
     await installPromptPromise;
 
     await promptPromise;
 
     // Sanity check to ensure that the test in makeInstallTest() that
@@ -225,20 +226,21 @@ function makeRegularTest(options, what) 
 
     await addon.uninstall();
 
     addon = await promiseAddonByID(ID);
     is(addon, null, "Addon was uninstalled");
   });
 }
 
-add_task(makeRegularTest({url: XPI_URL}, "a basic install works"));
-add_task(makeRegularTest({url: XPI_URL, hash: null}, "install with hash=null works"));
-add_task(makeRegularTest({url: XPI_URL, hash: ""}, "install with empty string for hash works"));
-add_task(makeRegularTest({url: XPI_URL, hash: XPI_SHA}, "install with hash works"));
+let addonId = XPI_ADDON_ID;
+add_task(makeRegularTest({url: XPI_URL, addonId}, "a basic install works"));
+add_task(makeRegularTest({url: XPI_URL, addonId, hash: null}, "install with hash=null works"));
+add_task(makeRegularTest({url: XPI_URL, addonId, hash: ""}, "install with empty string for hash works"));
+add_task(makeRegularTest({url: XPI_URL, addonId, hash: XPI_SHA}, "install with hash works"));
 
 add_task(makeInstallTest(async function(browser) {
   let steps = [
     {action: "cancel"},
     {
       event: "onDownloadCancelled",
       props: {
         state: "STATE_CANCELLED",
--- a/toolkit/mozapps/extensions/test/browser/browser_webapi_theme.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_theme.js
@@ -17,27 +17,27 @@ add_task(async function test_theme_insta
       updates.push(data);
     }
     Services.obs.addObserver(observer, "lightweight-theme-styling-update");
     registerCleanupFunction(() => {
       Services.obs.removeObserver(observer, "lightweight-theme-styling-update");
     });
 
 
-    let prompt1 = waitAppMenuNotificationShown("addon-installed", "theme", false);
+    let prompt1 = waitAppMenuNotificationShown("addon-installed", "theme@tests.mozilla.org", false);
     let installPromise = ContentTask.spawn(browser, URL, async (url) => {
       let install = await content.navigator.mozAddonManager.createInstall({url});
       return install.install();
     });
     await prompt1;
 
     // Open a new window and test the app menu panel from there.  This verifies the
     // incognito checkbox as well as finishing install in this case.
     let newWin = await BrowserTestUtils.openNewBrowserWindow();
-    await waitAppMenuNotificationShown("addon-installed", "theme", true, newWin);
+    await waitAppMenuNotificationShown("addon-installed", "theme@tests.mozilla.org", true, newWin);
     await installPromise;
     ok(true, "Theme install completed");
 
     await BrowserTestUtils.closeWindow(newWin);
 
     Assert.equal(updates.length, 1, "Got a single theme update");
     let parsed = JSON.parse(updates[0]);
     ok(parsed.theme.headerURL.endsWith("/testImage.png"),
--- a/toolkit/mozapps/extensions/test/browser/browser_webext_incognito.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_webext_incognito.js
@@ -1,16 +1,19 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 const {ExtensionPermissions} = ChromeUtils.import("resource://gre/modules/ExtensionPermissions.jsm");
 
 var gManagerWindow;
 
+AddonTestUtils.initMochitest(this);
+
 function get_test_items() {
   var items = {};
 
   for (let item of gManagerWindow.document.getElementById("addon-list").childNodes) {
     items[item.mAddon.id] = item;
   }
 
   return items;
@@ -269,8 +272,99 @@ add_task(async function test_addon_prefe
     await close_manager(gManagerWindow);
     await BrowserTestUtils.closeWindow(win);
   }
 
   // run tests in private and non-private windows.
   await runTest(true);
   await runTest(false);
 });
+
+add_task(async function test_addon_postinstall_incognito_hidden_checkbox() {
+  await SpecialPowers.pushPrefEnv({set: [
+    ["extensions.allowPrivateBrowsingByDefault", false],
+    ["extensions.langpacks.signatures.required", false],
+  ]});
+
+  const TEST_ADDONS = [
+    {
+      manifest: {
+        name: "Extension incognito default opt-in",
+        applications: {gecko: {id: "ext-incognito-default-opt-in@mozilla.com"}},
+      },
+    },
+    {
+      manifest: {
+        name: "Extension incognito not_allowed",
+        applications: {gecko: {id: "ext-incognito-not-allowed@mozilla.com"}},
+        incognito: "not_allowed",
+      },
+    },
+    {
+      manifest: {
+        name: "Static Theme",
+        applications: {gecko: {id: "static-theme@mozilla.com"}},
+        theme: {
+          colors: {
+            accentcolor: "#FFFFFF",
+            textcolor: "#000",
+          },
+        },
+      },
+    },
+    {
+      manifest: {
+        name: "Dictionary",
+        applications: {gecko: {id: "dictionary@mozilla.com"}},
+        dictionaries: {
+          "und": "dictionaries/und.dic",
+        },
+      },
+      files: {
+        "dictionaries/und.dic": "",
+        "dictionaries/und.aff": "",
+      },
+    },
+    {
+      manifest: {
+        name: "Langpack",
+        applications: {gecko: {id: "langpack@mozilla.com"}},
+        langpack_id: "und",
+        languages: {
+          und: {
+            chrome_resources: {
+              global: "chrome/und/locale/und/global",
+            },
+            version: "20190326174300",
+          },
+        },
+      },
+    },
+  ];
+
+  for (let definition of TEST_ADDONS) {
+    let {id} = definition.manifest.applications.gecko;
+    info(`Testing incognito checkbox visibility on ${id} post install notification`);
+
+    const xpi = AddonTestUtils.createTempWebExtensionFile(definition);
+    let install = await AddonManager.getInstallForFile(xpi);
+
+    await Promise.all([
+      waitAppMenuNotificationShown("addon-installed", id, true),
+      install.install().then(() => {
+        Services.obs.notifyObservers({
+          addon: install.addon, target: gBrowser.selectedBrowser,
+        }, "webextension-install-notify");
+      }),
+    ]);
+
+    const {permissions} = install.addon;
+    const canChangePBAccess = Boolean(permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS);
+
+    if (id === "ext-incognito-default-opt-in@mozilla.com") {
+      ok(canChangePBAccess, `${id} should have the PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS permission`);
+    } else {
+      ok(!canChangePBAccess, `${id} should not have the PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS permission`);
+    }
+
+    await install.addon.uninstall();
+  }
+});
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -1395,33 +1395,36 @@ function promisePopupNotificationShown(n
 
       PopupNotifications.panel.removeEventListener("popupshown", popupshown);
       resolve(PopupNotifications.panel.firstChild);
     }
     PopupNotifications.panel.addEventListener("popupshown", popupshown);
   });
 }
 
-function waitAppMenuNotificationShown(id, type, accept = false, win = window) {
+function waitAppMenuNotificationShown(id, addonId, accept = false, win = window) {
   const {AppMenuNotifications} = ChromeUtils.import("resource://gre/modules/AppMenuNotifications.jsm");
   return new Promise(resolve => {
     let {document, PanelUI} = win;
 
-    function popupshown() {
+    async function popupshown() {
       let notification = AppMenuNotifications.activeNotification;
       if (!notification) { return; }
 
       is(notification.id, id, `${id} notification shown`);
       ok(PanelUI.isNotificationPanelOpen, "notification panel open");
 
       PanelUI.notificationPanel.removeEventListener("popupshown", popupshown);
 
-      if (id == "addon-installed" && type) {
-        let hidden = type !== "extension" ||
-                     Services.prefs.getBoolPref("extensions.allowPrivateBrowsingByDefault", true);
+      if (id == "addon-installed" && addonId) {
+        let addon = await AddonManager.getAddonByID(addonId);
+        if (!addon) {
+          ok(false, `Addon with id "${addonId}" not found`);
+        }
+        let hidden = !(addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS);
         let checkbox = document.getElementById("addon-incognito-checkbox");
         is(checkbox.hidden, hidden, "checkbox visibility is correct");
       }
       if (accept) {
         let popupnotificationID = PanelUI._getPopupId(notification);
         let popupnotification = document.getElementById(popupnotificationID);
         popupnotification.button.click();
       }
@@ -1433,18 +1436,18 @@ function waitAppMenuNotificationShown(id
     if (notification && PanelUI.isNotificationPanelOpen) {
       popupshown();
       return;
     }
     PanelUI.notificationPanel.addEventListener("popupshown", popupshown);
   });
 }
 
-function acceptAppMenuNotificationWhenShown(id, type) {
-  return waitAppMenuNotificationShown(id, type, true);
+function acceptAppMenuNotificationWhenShown(id, addonId) {
+  return waitAppMenuNotificationShown(id, addonId, true);
 }
 
 function assertTelemetryMatches(events, {filterMethods} = {}) {
   let snapshot = Services.telemetry.snapshotEvents(
     Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
 
   if (events.length == 0) {
     ok(!snapshot.parent || snapshot.parent.length == 0, "There are no telemetry events");