Bug 1498967 - Ask to restart after installing a legacy extension. r=mkmelin
☠☠ backed out by e51fe5c93b88 ☠ ☠
authorGeoff Lankow <geoff@darktrojan.net>
Tue, 23 Oct 2018 21:32:22 +1300
changeset 33543 8f2aaabd3130c106bd569bd7ed4186d434f9f97e
parent 33542 09209cd0234390b24f1fe072ea9a5d0357a4ec84
child 33544 02352ad526d3ae67e6f3254ff468b253c9ea353f
push id388
push userclokep@gmail.com
push dateMon, 28 Jan 2019 20:54:56 +0000
reviewersmkmelin
bugs1498967
Bug 1498967 - Ask to restart after installing a legacy extension. r=mkmelin
mail/base/content/messenger.xul
mail/base/modules/ExtensionsUI.jsm
mail/locales/en-US/chrome/messenger/addons.properties
--- a/mail/base/content/messenger.xul
+++ b/mail/base/content/messenger.xul
@@ -328,17 +328,19 @@
     <popupnotificationcontent class="addon-webext-perm-notification-content" orient="vertical">
       <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 class="addon-installed-notification-content" orient="vertical"/>
+    <popupnotificationcontent class="addon-installed-notification-content" orient="vertical">
+      <description id="addon-installed-restart-text" class="addon-installed-restart-text"/>
+    </popupnotificationcontent>
   </popupnotification>
 
 #include editContactPanel.inc
 #include ../../components/im/content/chat-menu.inc
 </popupset>
 #ifdef XP_MACOSX
 <popupset>
   <menupopup id="menu_mac_dockmenu">
--- a/mail/base/modules/ExtensionsUI.jsm
+++ b/mail/base/modules/ExtensionsUI.jsm
@@ -260,51 +260,75 @@ var gXPInstallObserver = {
                        "addons-notification-icon", action, secondaryActions, popupOptions);
     });
 
     this.pendingNotifications.set(window, promise);
     promise.finally(() => this.pendingNotifications.delete(window));
     return promise;
   },
 
-  showInstallNotification(browser, addon) {
+  async showInstallNotification(browser, addon) {
+    let document = browser.ownerDocument;
     let window = browser.ownerGlobal;
 
     let brandBundle = window.document.getElementById("bundle_brand");
     let appName = brandBundle.getString("brandShortName");
 
     let message = addonsBundle.getFormattedString("addonPostInstall.message1",
                                                   ["<>", appName]);
-    return new Promise(resolve => {
-      let action = {
+
+    let restartRequired = false;
+    let icon = DEFAULT_EXTENSION_ICON;
+    if (addon.isWebExtension) {
+      let uri = addon.getResourceURI().spec;
+      let data = new ExtensionData(Services.io.newURI(`jar:${uri}!/`));
+      await data.loadManifest();
+      restartRequired = data.manifest.legacy;
+      icon = AddonManager.getPreferredIconURL(addon, 32, window) || icon;
+    }
+
+    let action;
+    let secondaryActions = null;
+    let textEl = document.getElementById("addon-installed-restart-text");
+    if (restartRequired) {
+      action = {
+        label: addonsBundle.getString("addonPostInstall.restart.label"),
+        accessKey: addonsBundle.getString("addonPostInstall.restart.key"),
+        callback: () => {
+          ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
+          BrowserUtils.restartApplication();
+        },
+      };
+      secondaryActions = [{
+        label: addonsBundle.getString("addonPostInstall.noRestart.label"),
+        accessKey: addonsBundle.getString("addonPostInstall.noRestart.key"),
+        callback: () => {},
+      }];
+      textEl.textContent = addonsBundle.getFormattedString(
+        "addonPostInstall.restartRequired.message", [appName]
+      );
+      textEl.hidden = false;
+    } else {
+      action = {
         label: addonsBundle.getString("addonPostInstall.okay.label"),
         accessKey: addonsBundle.getString("addonPostInstall.okay.key"),
-        callback: resolve,
+        callback: () => {},
       };
-
-      let icon = DEFAULT_EXTENSION_ICON;
-      if (addon.isWebExtension) {
-        icon = AddonManager.getPreferredIconURL(addon, 32, window) || icon;
-      }
+      textEl.hidden = true;
+    }
 
-      let options = {
-        hideClose: true,
-        timeout: Date.now() + 30000,
-        popupIconURL: icon,
-        eventCallback(topic) {
-          if (topic == "dismissed") {
-            resolve();
-          }
-        },
-        name: addon.name,
-      };
+    let options = {
+      hideClose: true,
+      timeout: Date.now() + 30000,
+      popupIconURL: icon,
+      name: addon.name,
+    };
 
-      showNotification(browser, "addon-installed", message, "addons-notification-icon",
-                       action, null, options);
-    });
+    showNotification(browser, "addon-installed", message, "addons-notification-icon",
+                     action, secondaryActions, options);
   },
 
   /* eslint-disable complexity */
   observe(subject, topic, data) {
     let installInfo = subject.wrappedJSObject;
     let browser = installInfo.browser || installInfo.target;
     let window = browser.ownerGlobal;
 
@@ -517,17 +541,17 @@ var gXPInstallObserver = {
         showNotification();
         break;
       }
       case "addon-install-complete": {
         this.showInstallNotification(browser, installInfo.installs[0].addon);
         break;
       }
       case "webextension-permission-prompt": {
-        let {info} = subject.wrappedJSObject;
+        let { info } = subject.wrappedJSObject;
 
         // Dismiss the progress notification.  Note that this is bad if
         // there are multiple simultaneous installs happening, see
         // bug 1329884 for a longer explanation.
         let progressNotification = getNotification("addon-progress", browser);
         if (progressNotification) {
           progressNotification.remove();
         }
@@ -553,17 +577,17 @@ var gXPInstallObserver = {
             info.resolve();
           } else {
             info.reject();
           }
         });
         break;
       }
       case "webextension-update-permissions": {
-        let {info} = subject.wrappedJSObject;
+        let { info } = subject.wrappedJSObject;
         info.type = "update";
         let strings = this._buildStrings(info);
 
         // If we don't prompt for any new permissions, just apply it.
         if (strings.msgs.length == 0) {
           info.resolve();
         }
 
@@ -572,29 +596,25 @@ var gXPInstallObserver = {
             info.resolve();
           } else {
             info.reject();
           }
         });
         break;
       }
       case "webextension-install-notify": {
-        let {addon, callback} = subject.wrappedJSObject;
-        this.showInstallNotification(browser, addon).then(() => {
-          if (callback) {
-            callback();
-          }
-        });
+        let { addon } = subject.wrappedJSObject;
+        this.showInstallNotification(browser, addon);
         break;
       }
       case "webextension-optional-permission-prompt": {
-        let {name, icon, permissions, resolve} = subject.wrappedJSObject;
+        let { name, icon, permissions, resolve } = subject.wrappedJSObject;
         let strings = this._buildStrings({
           type: "optional",
-          addon: {name},
+          addon: { name },
           permissions,
         });
 
         // If we don't have any promptable permissions, just proceed
         if (strings.msgs.length == 0) {
           resolve(true);
           return;
         }
@@ -605,17 +625,17 @@ var gXPInstallObserver = {
   },
   /* eslint-enable complexity */
 
   // Create a set of formatted strings for a permission prompt
   _buildStrings(info) {
     // This bundle isn't the same as addonsBundle.
     let bundle = Services.strings.createBundle(ADDONS_PROPERTIES);
     let appName = brandBundle.getString("brandShortName");
-    let info2 = Object.assign({appName}, info);
+    let info2 = Object.assign({ appName }, info);
 
     let strings = ExtensionData.formatPermissionStrings(info2, bundle);
     strings.addonName = info.addon.name;
     return strings;
   },
 
   _removeProgressNotification(browser) {
     let notification = getNotification("addon-progress", browser);
--- a/mail/locales/en-US/chrome/messenger/addons.properties
+++ b/mail/locales/en-US/chrome/messenger/addons.properties
@@ -14,16 +14,24 @@ xpinstallDisabledButton.accesskey=n
 # LOCALIZATION NOTE (addonPostInstall.message1)
 # %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.
 addonPostInstall.okay.label=OK
 addonPostInstall.okay.key=O
 
+# LOCALIZATION NOTE (addonPostInstall.restartRequired.message)
+# %S is the application name
+addonPostInstall.restartRequired.message=%S must be restarted to complete the installation.
+addonPostInstall.restart.label=Restart now
+addonPostInstall.restart.key=R
+addonPostInstall.noRestart.label=Not now
+addonPostInstall.noRestart.key=N
+
 # 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
 
 addonInstall.unsigned=(Unverified)