Bug 1308296 Show post-install notification draft
authorAndrew Swan <aswan@mozilla.com>
Fri, 20 Jan 2017 10:28:47 -0800
changeset 464246 234a7d1a5123066f07ed6cb9366eca55fe2282f6
parent 464229 311366a5c625b95c13cab7e30b9756888f975310
child 464610 bcae8aa6f63702bd2730fabb9372f1f27f50c362
push id42315
push useraswan@mozilla.com
push dateFri, 20 Jan 2017 18:35:48 +0000
bugs1308296
milestone53.0a1
Bug 1308296 Show post-install notification MozReview-Commit-ID: KenyFW6MyDO
browser/base/content/popup-notifications.inc
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/ExtensionsUI.jsm
browser/themes/linux/browser.css
browser/themes/osx/browser.css
browser/themes/windows/browser.css
toolkit/mozapps/extensions/AddonManager.jsm
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -75,8 +75,15 @@
     <popupnotification id="addon-webext-permissions-notification" hidden="true">
       <popupnotificationcontent orient="vertical">
         <description id="addon-webext-perm-header" class="addon-webext-perm-header"/>
         <description id="addon-webext-perm-text" class="addon-webext-perm-text"/>
         <label id="addon-webext-perm-intro" class="addon-webext-perm-text"/>
         <html:ul id="addon-webext-perm-list" class="addon-webext-perm-list"/>
       </popupnotificationcontent>
     </popupnotification>
+
+    <popupnotification id="addon-installed-notification" hidden="true">
+      <popupnotificationcontent orient="vertical">
+        <description id="addon-installed-notification-header"/>
+        <description id="addon-installed-notification-message"/>
+      </popupnotificationcontent>
+    </popupnotification>
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -114,16 +114,30 @@ webextPerms.hostDescription.oneSite=Acce
 
 # LOCALIZATION NOTE (webextPerms.hostDescription.tooManySites)
 # Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 will be replaced by an integer indicating the number of additional
 # hosts for which this webextension is requesting permission.
 webextPerms.hostDescription.tooManySites=Access your data on #1 other site;Access your data on #1 other sites
 
+# LOCALIZATION NOTE (addonPostInstall.message)
+# %1$S is replaced with the localized named of the extension that was
+# just installed.
+# %2$S is replaced with the localized name of the application.
+addonPostInstall.message1=%1$S has been added to %2$S.
+
+# LOCALIZATION NOTE (addonPostInstall.message2)
+# %1$S is replaced with the localized name of the extension.
+# %2$S is replaced with the icon for the add-ons menu.
+# %3$S is replaced with the icon for the toolbar menu.
+# Note, this string will be used as raw markup. Avoid characters like <, >, &
+addonPostInstall.message2=Manage %1$S by clicking %2$S in the %3$S menu.
+addonPostInstall.okay.label=OK
+addonPostInstall.okay.key=O
 
 # LOCALIZATION NOTE (addonDownloadingAndVerifying):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # Also see https://bugzilla.mozilla.org/show_bug.cgi?id=570012 for mockups
 addonDownloadingAndVerifying=Downloading and verifying add-on…;Downloading and verifying #1 add-ons…
 addonDownloadVerifying=Verifying
 
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -17,27 +17,28 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
                                   "resource:///modules/RecentWindow.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
                                       "extensions.webextPermissionPrompts", false);
 
-const DEFAULT_EXENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
+const DEFAULT_EXTENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 this.ExtensionsUI = {
   sideloaded: new Set(),
   updates: new Set(),
 
   init() {
     Services.obs.addObserver(this, "webextension-permission-prompt", false);
     Services.obs.addObserver(this, "webextension-update-permissions", false);
+    Services.obs.addObserver(this, "webextension-install-notify", false);
 
     this._checkForSideloaded();
   },
 
   _checkForSideloaded() {
     AddonManager.getAllAddons(addons => {
       // Check for any side-loaded addons that the user is allowed
       // to enable.
@@ -136,16 +137,19 @@ this.ExtensionsUI = {
         reply(true);
       } else {
         info.permissions = perms;
         this.showPermissionsPrompt(target, info).then(reply);
       }
     } else if (topic == "webextension-update-permissions") {
       this.updates.add(subject.wrappedJSObject);
       this.emit("change");
+    } else if (topic == "webextension-install-notify") {
+      let {target, addon} = subject.wrappedJSObject;
+      this.showInstallNotification(target, addon);
     }
   },
 
   showPermissionsPrompt(target, info) {
     let perms = info.permissions;
     if (!perms) {
       return Promise.resolve();
     }
@@ -306,11 +310,52 @@ this.ExtensionsUI = {
                                     {
                                       label: cancelText,
                                       accessKey: cancelKey,
                                       callback: () => resolve(false),
                                     },
                                   ], popupOptions);
     });
   },
+
+  showInstallNotification(target, addon) {
+    let win = target.ownerGlobal;
+    let popups = win.PopupNotifications;
+
+    let addonLabel = `<label class="addon-webext-name">${addon.name}</label>`;
+    let addonIcon = '<image class="addon-addon-icon"/>';
+    let toolbarIcon = '<image class="addon-toolbar-icon"/>';
+
+    let brandBundle = win.document.getElementById("bundle_brand");
+    let appName = brandBundle.getString("brandShortName");
+
+    let bundle = win.gNavigatorBundle;
+    let msg1 = bundle.getFormattedString("addonPostInstall.message1",
+                                         [addonLabel, appName]);
+    let msg2 = bundle.getFormattedString("addonPostInstall.message2",
+                                         [addonLabel, addonIcon, toolbarIcon]);
+
+    let action = {
+      label: bundle.getString("addonPostInstall.okay.label"),
+      accessKey: bundle.getString("addonPostInstall.okay.key"),
+      callback: () => {},
+    };
+
+    let options = {
+      hideClose: true,
+      popupIconURL: addon.iconURL || DEFAULT_EXTENSION_ICON,
+      eventCallback(topic) {
+        if (topic == "showing") {
+          let doc = this.browser.ownerDocument;
+          doc.getElementById("addon-installed-notification-header")
+             .innerHTML = msg1;
+          doc.getElementById("addon-installed-notification-message")
+             .innerHTML = msg2;
+        }
+      }
+    };
+
+    popups.show(target, "addon-installed", "", "addons-notification-icon",
+                action, null, options);
+  },
 };
 
 EventEmitter.decorate(ExtensionsUI);
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -837,16 +837,30 @@ menuitem.bookmark-item {
   font-size: 1.3em;
 }
 
 .addon-webext-name {
   font-weight: bold;
   margin: 0;
 }
 
+.addon-addon-icon {
+  width: 14px;
+  height: 14px;
+  list-style-image: url("chrome://browser/skin/menuPanel.svg");
+  -moz-image-region: rect(0px, 288px, 32px, 256px);
+}
+
+.addon-toolbar-icon {
+  width: 14px;
+  height: 14px;
+  list-style-image: url("chrome://browser/skin/Toolbar.png");
+  -moz-image-region: rect(0, 486px, 18px, 468px);
+}
+
 /* Notification icon box */
 
 .notification-anchor-icon:-moz-focusring {
   outline: 1px dotted -moz-DialogText;
 }
 
 /* Translation infobar */
 
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3095,16 +3095,30 @@ menulist.translate-infobar-element > .me
   font-size: 1.3em;
 }
 
 .addon-webext-name {
   font-weight: bold;
   margin: 0;
 }
 
+.addon-addon-icon {
+  width: 14px;
+  height: 14px;
+  list-style-image: url("chrome://browser/skin/menuPanel.svg");
+  -moz-image-region: rect(0px, 288px, 32px, 256px);
+}
+
+.addon-toolbar-icon {
+  width: 14px;
+  height: 14px;
+  list-style-image: url("chrome://browser/skin/Toolbar.png");
+  -moz-image-region: rect(0, 486px, 18px, 468px);
+}
+
 /* Status panel */
 
 .statuspanel-label {
   margin: 0;
   padding: 2px 4px;
   background: linear-gradient(#fff, #ddd);
   border: 1px none #ccc;
   border-top-style: solid;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2140,16 +2140,30 @@ toolbarbutton.bookmark-item[dragover="tr
   font-size: 1.3em;
 }
 
 .addon-webext-name {
   font-weight: bold;
   margin: 0;
 }
 
+.addon-addon-icon {
+  width: 14px;
+  height: 14px;
+  list-style-image: url("chrome://browser/skin/menuPanel.svg");
+  -moz-image-region: rect(0px, 288px, 32px, 256px);
+}
+
+.addon-toolbar-icon {
+  width: 14px;
+  height: 14px;
+  list-style-image: url("chrome://browser/skin/Toolbar.png");
+  -moz-image-region: rect(0, 486px, 18px, 468px);
+}
+
 /* Notification icon box */
 
 .notification-anchor-icon:-moz-focusring {
   outline: 1px dotted -moz-DialogText;
 }
 
 /* Translation infobar */
 
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -2104,17 +2104,23 @@ var AddonManagerInternal = {
 
         // If installing a theme that is disabled and can be enabled
         // then enable it
         if (install.addon.type == "theme" &&
             install.addon.userDisabled == true &&
             install.addon.appDisabled == false) {
               install.addon.userDisabled = false;
         }
-        self.installNotifyObservers("addon-install-complete", browser, url, install);
+
+        if (WEBEXT_PERMISSION_PROMPTS) {
+          let subject = {wrappedJSObject: {target: browser, addon: install.addon}};
+          Services.obs.notifyObservers(subject, "webextension-install-notify", null);
+        } else {
+          self.installNotifyObservers("addon-install-complete", browser, url, install);
+        }
       },
     };
 
     install.addListener(listener);
 
     // Start downloading if it hasn't already begun
     install.install();
   },