Bug 1533172 - Show post install notification when enabling sideload extensions. r=mixedpuppy,kmag a=pascalc
authorLuca Greco <lgreco@mozilla.com>
Fri, 22 Mar 2019 19:19:35 +0000
changeset 525758 dccd60f0b7bac255d706a50d712e9b839ffe6bb0
parent 525757 acb4a36667140807290d693ab897c98ffb16ea7f
child 525759 db9f6521276f71271f8836b16ba94c3baf401033
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
bugs1533172
milestone67.0
Bug 1533172 - Show post install notification when enabling sideload extensions. r=mixedpuppy,kmag a=pascalc Differential Revision: https://phabricator.services.mozilla.com/D22700
browser/base/content/test/webextensions/browser_extension_sideloading.js
browser/modules/ExtensionsUI.jsm
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/test/browser/browser_extension_sideloading_permission.js
--- a/browser/base/content/test/webextensions/browser_extension_sideloading.js
+++ b/browser/base/content/test/webextensions/browser_extension_sideloading.js
@@ -34,16 +34,17 @@ function promiseEvent(eventEmitter, even
 add_task(async function() {
   const DEFAULT_ICON_URL = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 
   await SpecialPowers.pushPrefEnv({
     set: [
       ["xpinstall.signatures.required", false],
       ["extensions.autoDisableScopes", 15],
       ["extensions.ui.ignoreUnsigned", true],
+      ["extensions.allowPrivateBrowsingByDefault", false],
     ],
   });
 
   const ID1 = "addon1@tests.mozilla.org";
   await createWebExtension({
     id: ID1,
     name: "Test 1",
     userDisabled: true,
@@ -100,16 +101,18 @@ add_task(async function() {
   is(menuButton.getAttribute("badge-status"), "addon-alert", "Should have addon alert badge");
 
   // Find the menu entries for sideloaded extensions
   await gCUITestUtils.openMainMenu();
 
   let addons = PanelUI.addonNotificationContainer;
   is(addons.children.length, 3, "Have 3 menu entries for sideloaded extensions");
 
+  info("Test disabling sideloaded addon 1 using the permission prompt secondary button");
+
   // Click the first sideloaded extension
   let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
   addons.children[0].click();
 
   // The click should hide the main menu. This is currently synchronous.
   ok(PanelUI.panel.state != "open", "Main menu is closed or closing.");
 
   // When we get the permissions prompt, we should be at the extensions
@@ -159,56 +162,88 @@ add_task(async function() {
 
   let item = Array.from(list.children).find(_item => _item.value == ID2);
   ok(item, "Found entry for sideloaded extension in about:addons");
   item.scrollIntoView({behavior: "instant"});
 
   ok(BrowserTestUtils.is_visible(item._enableBtn), "Enable button is visible for sideloaded extension");
   ok(BrowserTestUtils.is_hidden(item._disableBtn), "Disable button is not visible for sideloaded extension");
 
+  info("Test enabling sideloaded addon 2 from about:addons enable button");
+
   // When clicking enable we should see the permissions notification
   popupPromise = promisePopupNotificationShown("addon-webext-permissions");
   BrowserTestUtils.synthesizeMouseAtCenter(item._enableBtn, {},
                                            gBrowser.selectedBrowser);
   panel = await popupPromise;
   checkNotification(panel, DEFAULT_ICON_URL, [["webextPerms.hostDescription.allUrls"]]);
 
+  // Test incognito checkbox in post install notification
+  function setupPostInstallNotificationTest() {
+    let promiseNotificationShown = promiseAppMenuNotificationShown("addon-installed");
+    return async function(addon) {
+      info(`Expect post install notification for "${addon.name}"`);
+      let postInstallPanel = await promiseNotificationShown;
+      let incognitoCheckbox = postInstallPanel.querySelector("#addon-incognito-checkbox");
+      is(window.AppMenuNotifications.activeNotification.options.name, addon.name,
+         "Got the expected addon name in the active notification");
+      ok(incognitoCheckbox, "Got an incognito checkbox in the post install notification panel");
+      ok(!incognitoCheckbox.hidden, "Incognito checkbox should not be hidden");
+      // Dismiss post install notification.
+      postInstallPanel.button.click();
+    };
+  }
+
+  // Setup async test for post install notification on addon 2
+  let testPostInstallIncognitoCheckbox = setupPostInstallNotificationTest();
+
   // Accept the permissions
   panel.button.click();
   await promiseEvent(ExtensionsUI, "change");
 
   addon2 = await AddonManager.getAddonByID(ID2);
   is(addon2.userDisabled, false, "Addon 2 should be enabled");
 
+  // Test post install notification on addon 2.
+  await testPostInstallIncognitoCheckbox(addon2);
+
   // Should still have 1 entry in the hamburger menu
   await gCUITestUtils.openMainMenu();
 
   addons = PanelUI.addonNotificationContainer;
   is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions");
 
   // Close the hamburger menu and go to the detail page for this addon
   await gCUITestUtils.hideMainMenu();
 
   win = await BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(ID3)}`);
-  let button = win.document.getElementById("detail-enable-btn");
 
-  // When clicking enable we should see the permissions notification
+  info("Test enabling sideloaded addon 3 from app menu");
+  // Trigger addon 3 install as triggered from the app menu, to be able to cover the
+  // post install notification that should be triggered when the permission
+  // dialog is accepted from that flow.
   popupPromise = promisePopupNotificationShown("addon-webext-permissions");
-  BrowserTestUtils.synthesizeMouseAtCenter(button, {},
-                                           gBrowser.selectedBrowser);
+  ExtensionsUI.showSideloaded(gBrowser, addon3);
+
   panel = await popupPromise;
   checkNotification(panel, DEFAULT_ICON_URL, [["webextPerms.hostDescription.allUrls"]]);
 
+  // Setup async test for post install notification on addon 3
+  testPostInstallIncognitoCheckbox = setupPostInstallNotificationTest();
+
   // Accept the permissions
   panel.button.click();
   await promiseEvent(ExtensionsUI, "change");
 
   addon3 = await AddonManager.getAddonByID(ID3);
   is(addon3.userDisabled, false, "Addon 3 should be enabled");
 
+  // Test post install notification on addon 3.
+  await testPostInstallIncognitoCheckbox(addon3);
+
   // We should have recorded 1 cancelled followed by 2 accepted sideloads.
   expectTelemetry(["sideloadRejected", "sideloadAccepted", "sideloadAccepted"]);
 
   isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
 
   await new Promise(resolve => setTimeout(resolve, 100));
 
   for (let addon of [addon1, addon2, addon3]) {
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -81,17 +81,17 @@ var ExtensionsUI = {
       if (!this.sideloadListener) {
         this.sideloadListener = {
           onEnabled: addon => {
             if (!this.sideloaded.has(addon)) {
               return;
             }
 
             this.sideloaded.delete(addon);
-              this._updateNotifications();
+            this._updateNotifications();
 
             if (this.sideloaded.size == 0) {
               AddonManager.removeAddonListener(this.sideloadListener);
               this.sideloadListener = null;
             }
           },
         };
         AddonManager.addAddonListener(this.sideloadListener);
@@ -135,16 +135,26 @@ var ExtensionsUI = {
     AMTelemetry.recordManageEvent(addon, "sideload_prompt", {
       num_strings: strings.msgs.length,
     });
 
     this.showAddonsManager(browser, strings, addon.iconURL, "sideload")
         .then(async answer => {
           if (answer) {
             await addon.enable();
+
+            this._updateNotifications();
+
+            // If private browsing access is not allowed by default,
+            // show the post-install doorhanger notification to
+            // allow the user to give the extension access from the
+            // checkbox included in the doorhanger.
+            if (!allowPrivateBrowsingByDefault && addon.incognito !== "not_allowed") {
+              this.showInstallNotification(browser, addon);
+            }
           }
           this.emit("sideload-response");
         });
   },
 
   showUpdate(browser, info) {
     AMTelemetry.recordInstallEvent(info.install, {
       step: "permissions_prompt",
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -1267,27 +1267,39 @@ var gViewController = {
         let addonType = AddonManager.addonTypes[aAddon.type];
         return (!(addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
                 hasPermission(aAddon, "enable"));
       },
       doCommand(aAddon) {
         if (aAddon.isWebExtension && !aAddon.seen && WEBEXT_PERMISSION_PROMPTS) {
           let perms = aAddon.userPermissions;
           if (perms.origins.length > 0 || perms.permissions.length > 0) {
+            const target = getBrowserElement();
+
             let subject = {
               wrappedJSObject: {
-                target: getBrowserElement(),
+                target,
                 info: {
                   type: "sideload",
                   addon: aAddon,
                   icon: aAddon.iconURL,
                   permissions: perms,
                   resolve() {
                     aAddon.markAsSeen();
-                    aAddon.enable();
+                    aAddon.enable().then(() => {
+                      // If private browsing access is not allowed by default,
+                      // show the post-install doorhanger notification to
+                      // allow the user to give the extension access from the
+                      // checkbox included in the doorhanger.
+                      if (!allowPrivateBrowsingByDefault && aAddon.incognito !== "not_allowed") {
+                        Services.obs.notifyObservers({
+                          addon: aAddon, target,
+                        }, "webextension-install-notify");
+                      }
+                    });
                   },
                   reject() {},
                 },
               },
             };
             Services.obs.notifyObservers(subject, "webextension-permission-prompt");
             return;
           }
--- a/toolkit/mozapps/extensions/test/browser/browser_extension_sideloading_permission.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_extension_sideloading_permission.js
@@ -74,18 +74,22 @@ add_task(async function test() {
   EventUtils.synthesizeMouseAtCenter(
     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");
+
   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");
 
   ok(!PopupNotifications.isPanelOpen, "Permission popup should not be visible");
 
   await addon.uninstall();