Bug 1356462 Add per-extension notice for non-MPC disabling draft
authorAndrew Swan <aswan@mozilla.com>
Thu, 20 Apr 2017 21:00:19 -0700
changeset 566527 14041458355f3f93beeccc7f80f53b3fc8b59c5f
parent 564706 3f9f6d6086b2d247831d1d03d530095bebd5a6b2
child 566528 4f6b385936ebc8b9d02eea1026eb23b641bea49d
push id55253
push useraswan@mozilla.com
push dateFri, 21 Apr 2017 19:24:08 +0000
bugs1356462
milestone55.0a1
Bug 1356462 Add per-extension notice for non-MPC disabling MozReview-Commit-ID: KQ69E7B4VPg
toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
toolkit/mozapps/extensions/test/browser/browser.ini
toolkit/mozapps/extensions/test/browser/browser_non_mpc.js
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties
@@ -22,16 +22,19 @@ numReviews=#1 review;#1 reviews
 dateUpdated=Updated %S
 
 #LOCALIZATION NOTE (notification.incompatible) %1$S is the add-on name, %2$S is brand name, %3$S is application version
 notification.incompatible=%1$S is incompatible with %2$S %3$S.
 #LOCALIZATION NOTE (notification.unsigned, notification.unsignedAndDisabled) %1$S is the add-on name, %2$S is brand name
 notification.unsignedAndDisabled=%1$S could not be verified for use in %2$S and has been disabled.
 notification.unsigned=%1$S could not be verified for use in %2$S. Proceed with caution.
 notification.unsigned.link=More Information
+#LOCALIZATION NOTE (notification.nonMpcDisabled) %1$S is the add-on name
+notification.nonMpcDisabled=%1$S has been disabled since it is not multiprocess compatible.
+notification.nonMpcDisabled.link=More Information
 #LOCALIZATION NOTE (notification.blocked) %1$S is the add-on name
 notification.blocked=%1$S has been disabled due to security or stability issues.
 notification.blocked.link=More Information
 #LOCALIZATION NOTE (notification.softblocked) %1$S is the add-on name
 notification.softblocked=%1$S is known to cause security or stability issues.
 notification.softblocked.link=More Information
 #LOCALIZATION NOTE (notification.outdated) %1$S is the add-on name
 notification.outdated=An important update is available for %1$S.
@@ -77,16 +80,19 @@ installFailed=Error installing
 installCancelled=Install cancelled
 
 #LOCALIZATION NOTE (details.notification.incompatible) %1$S is the add-on name, %2$S is brand name, %3$S is application version
 details.notification.incompatible=%1$S is incompatible with %2$S %3$S.
 #LOCALIZATION NOTE (details.notification.unsigned, details.notification.unsignedAndDisabled) %1$S is the add-on name, %2$S is brand name
 details.notification.unsignedAndDisabled=%1$S could not be verified for use in %2$S and has been disabled.
 details.notification.unsigned=%1$S could not be verified for use in %2$S. Proceed with caution.
 details.notification.unsigned.link=More Information
+#LOCALIZATION NOTE (details.notification.nonMpcDisabled) %1$S is the add-on name
+details.notification.nonMpcDisabled=%1$S has been disabled since it is not multiprocess compatible.
+details.notification.nonMpcDisabled.link=More Information
 #LOCALIZATION NOTE (details.notification.blocked) %1$S is the add-on name
 details.notification.blocked=%1$S has been disabled due to security or stability issues.
 details.notification.blocked.link=More Information
 #LOCALIZATION NOTE (details.notification.softblocked) %1$S is the add-on name
 details.notification.softblocked=%1$S is known to cause security or stability issues.
 details.notification.softblocked.link=More Information
 #LOCALIZATION NOTE (details.notification.outdated) %1$S is the add-on name
 details.notification.outdated=An important update is available for %1$S.
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -40,16 +40,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
                                   "resource://gre/modules/Preferences.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
   "resource:///modules/experiments/Experiments.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
                                       "extensions.webextPermissionPrompts", false);
+XPCOMUtils.defineLazyPreferenceGetter(this, "ALLOW_NON_MPC",
+                                      "extensions.allow-non-mpc-extensions", true);
 
 const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
 const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
 const PREF_XPI_ENABLED = "xpinstall.enabled";
 const PREF_MAXRESULTS = "extensions.getAddons.maxResults";
 const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
 const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
 const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
@@ -3373,16 +3375,24 @@ var gDetailView = {
       } else if (!this._addon.isCompatible && (AddonManager.checkCompatibility ||
         (this._addon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) {
         this.node.setAttribute("notification", "warning");
         document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
           "details.notification.incompatible",
           [this._addon.name, gStrings.brandShortName, gStrings.appVersion], 3
         );
         document.getElementById("detail-warning-link").hidden = true;
+      } else if (this.appDisabled && !this._addon.multiprocessCompatible && !ALLOW_NON_MPC) {
+        this.node.setAttribute("notification", "error");
+        document.getElementById("detail-error").textContent = gStrings.ext.formatStringFromName(
+          "details.notification.nonMpcDisabled", [this._addon.name], 1
+        );
+        let errorLink = document.getElementById("detail-error-link");
+        errorLink.value = gStrings.ext.GetStringFromName("details.notification.nonMpcDisabled.link");
+        errorLink.href = "https://wiki.mozilla.org/Add-ons/ShimsNightly";
       } else if (!isCorrectlySigned(this._addon)) {
         this.node.setAttribute("notification", "warning");
         document.getElementById("detail-warning").textContent = gStrings.ext.formatStringFromName(
           "details.notification.unsigned", [this._addon.name, gStrings.brandShortName], 2
         );
         var warningLink = document.getElementById("detail-warning-link");
         warningLink.value = gStrings.ext.GetStringFromName("details.notification.unsigned.link");
         warningLink.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -1278,16 +1278,25 @@
             || (this.mAddon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.incompatible",
                 [this.mAddon.name, gStrings.brandShortName, gStrings.appVersion], 3
               );
               this._warningLink.hidden = true;
               this._warningBtn.hidden = true;
+            } else if (!isUpgrade && !this.mAddon.multiprocessCompatible &&
+                       !Services.prefs.getBoolPref("extensions.allow-non-mpc-extensions", true)) {
+              this.setAttribute("notification", "error");
+              this._error.textContent = gStrings.ext.formatStringFromName(
+                "notification.nonMpcDisabled", [this.mAddon.name], 1
+              );
+              this._errorLink.value = gStrings.ext.GetStringFromName("notification.nonMpcDisabled.link");
+              this._errorLink.href = "https://wiki.mozilla.org/Add-ons/ShimsNightly";
+              this._errorLink.hidden = false;
             } else if (!isUpgrade && !isCorrectlySigned(this.mAddon)) {
               this.setAttribute("notification", "warning");
               this._warning.textContent = gStrings.ext.formatStringFromName(
                 "notification.unsigned", [this.mAddon.name, gStrings.brandShortName], 2
               );
               this._warningLink.value = gStrings.ext.GetStringFromName("notification.unsigned.link");
               this._warningLink.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons";
               this._warningLink.hidden = false;
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -60,16 +60,17 @@ skip-if = buildapp == 'mulet'
 [browser_getmorethemes.js]
 [browser_gmpProvider.js]
 [browser_hotfix.js]
 # Verifies the old style of signing hotfixes
 skip-if = require_signing
 [browser_install.js]
 [browser_installssl.js]
 [browser_newaddon.js]
+[browser_non_mpc.js]
 [browser_searching.js]
 [browser_system_addons_are_e10s.js]
 [browser_task_next_test.js]
 [browser_update.js]
 [browser_updatessl.js]
 [browser_webapi.js]
 [browser_webapi_access.js]
 [browser_webapi_addon_listener.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_non_mpc.js
@@ -0,0 +1,91 @@
+
+add_task(async function() {
+  let extensions = [
+    {
+      id: "mpc@tests.mozilla.org",
+      name: "Compatible extension",
+      multiprocessCompatible: true,
+    },
+    {
+      id: "not-mpc@tests.mozilla.org",
+      name: "Incompatible extension",
+      multiprocessCompatible: false,
+    },
+  ];
+
+  let provider = new MockProvider();
+  provider.createAddons(extensions);
+
+  let mgrWin = await open_manager(null);
+  let catUtils = new CategoryUtilities(mgrWin);
+
+  async function check(name, disabled) {
+    await catUtils.openType("extension");
+
+    let document = mgrWin.document;
+    // First find the extension entry in the extensions list.
+    let item = Array.from(document.getElementById("addon-list").childNodes)
+                    .find(i => i.getAttribute("name") == name);
+
+    ok(item, `Found ${name} in extensions list`);
+    item.parentNode.ensureElementIsVisible(item);
+
+    // Check individual elements on this item.
+    let disabledPostfix = document.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
+    let enableBtn = document.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
+    let disableBtn = document.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
+    let errorMsg = document.getAnonymousElementByAttribute(item, "anonid", "error");
+    let errorLink = document.getAnonymousElementByAttribute(item, "anonid", "error-link");
+
+    if (disabled) {
+      is_element_visible(disabledPostfix, "Disabled postfix should be visible");
+      is_element_hidden(enableBtn, "Enable button should be hidden");
+      is_element_hidden(disableBtn, "Disable button should be hidden");
+      is_element_visible(errorMsg, "Error message should be visible");
+      is_element_visible(errorLink, "Error link should be visible");
+    } else {
+      is_element_hidden(disabledPostfix, "Disabled postfix should be hidden");
+      is_element_hidden(enableBtn, "Enable button should be hidden");
+      is_element_visible(disableBtn, "Disable button should be visible");
+      is_element_hidden(errorMsg, "Error message should be hidden");
+      is_element_hidden(errorLink, "Error link should be hidden");
+    }
+
+    // Click down to the details page.
+    EventUtils.synthesizeMouseAtCenter(item, {clickCount: 2}, mgrWin);
+    await new Promise(resolve => wait_for_view_load(mgrWin, resolve));
+
+    // And check its contents.
+    enableBtn = mgrWin.document.getElementById("detail-enable-btn");
+    disableBtn = mgrWin.document.getElementById("detail-disable-btn");
+    errorMsg = mgrWin.document.getElementById("detail-error");
+
+    if (disabled) {
+      is_element_hidden(enableBtn, "Enable button should be hidden");
+      is_element_hidden(disableBtn, "Disable button should be hidden");
+      is_element_visible(errorMsg, "Error message should be visible");
+    } else {
+      is_element_hidden(enableBtn, "Enable button should be hidden");
+      is_element_visible(disableBtn, "Disable button should be visible");
+      is_element_hidden(errorMsg, "Error message should be hidden");
+    }
+  }
+
+  // Initially, both extensions should be enabled
+  await check("Compatible extension", false);
+  await check("Incompatible extension", false);
+
+  // Flip the pref, making the non-MPC extension disabled.
+  await SpecialPowers.pushPrefEnv({
+    set: [["extensions.allow-non-mpc-extensions", false]],
+  });
+  extensions[1].multiprocessCompatible = false;
+  extensions[1].appDisabled = true;
+
+  // The compatible extensions should be unaffected, the incompatible
+  // one should have the error message etc.
+  check("Compatible extension", false);
+  check("Incompatible extension", true);
+
+  await close_manager(mgrWin);
+});