Bug 1356027 Add pref to disable non-MPC extensions draft
authorAndrew Swan <aswan@mozilla.com>
Wed, 12 Apr 2017 22:13:49 -0700
changeset 563751 9c65c33564c936724d1cd47df64c2b43d8162006
parent 563687 05c212a94183838f12feebb2c3fd483a6eec18c2
child 563936 22fea51fa4d26ad629968f02976e997acae011d6
push id54407
push useraswan@mozilla.com
push dateMon, 17 Apr 2017 18:22:13 +0000
bugs1356027
milestone55.0a1
Bug 1356027 Add pref to disable non-MPC extensions MozReview-Commit-ID: 9TdZf3hnLZl
modules/libpref/init/all.js
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/test_multiprocessCompatible.js
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4782,16 +4782,17 @@ pref("browser.meta_refresh_when_inactive
 
 // XPInstall prefs
 pref("xpinstall.whitelist.required", true);
 // Only Firefox requires add-on signatures
 pref("xpinstall.signatures.required", false);
 pref("extensions.alwaysUnpack", false);
 pref("extensions.minCompatiblePlatformVersion", "2.0");
 pref("extensions.webExtensionsMinPlatformVersion", "42.0a1");
+pref("extensions.allow-non-mpc-extensions", true);
 
 // Other webextensions prefs
 pref("extensions.webextensions.keepStorageOnUninstall", false);
 pref("extensions.webextensions.keepUuidOnUninstall", false);
 // Redirect basedomain used by identity api
 pref("extensions.webextensions.identity.redirectDomain", "extensions.allizom.org");
 // Whether or not webextension themes are supported.
 pref("extensions.webextensions.themes.enabled", false);
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -127,16 +127,17 @@ const PREF_INSTALL_DISTRO_ADDONS      = 
 const PREF_BRANCH_INSTALLED_ADDON     = "extensions.installedDistroAddon.";
 const PREF_INTERPOSITION_ENABLED      = "extensions.interposition.enabled";
 const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
 const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
 const PREF_E10S_BLOCK_ENABLE          = "extensions.e10sBlocksEnabling";
 const PREF_E10S_ADDON_BLOCKLIST       = "extensions.e10s.rollout.blocklist";
 const PREF_E10S_ADDON_POLICY          = "extensions.e10s.rollout.policy";
 const PREF_E10S_HAS_NONEXEMPT_ADDON   = "extensions.e10s.rollout.hasAddon";
+const PREF_ALLOW_NON_MPC              = "extensions.allow-non-mpc-extensions";
 
 const PREF_EM_MIN_COMPAT_APP_VERSION      = "extensions.minCompatibleAppVersion";
 const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
 
 const PREF_CHECKCOMAT_THEMEOVERRIDE   = "extensions.checkCompatibility.temporaryThemeOverride_minAppVersion";
 
 const PREF_EM_HOTFIX_ID               = "extensions.hotfix.id";
 const PREF_EM_CERT_CHECKATTRIBUTES    = "extensions.hotfix.cert.checkAttributes";
@@ -180,16 +181,18 @@ const RDFURI_INSTALL_MANIFEST_ROOT    = 
 const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
 const XPI_SIGNATURE_CHECK_PERIOD      = 24 * 60 * 60;
 
 XPCOMUtils.defineConstant(this, "DB_SCHEMA", 19);
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "ALLOW_NON_MPC", PREF_ALLOW_NON_MPC);
+
 const NOTIFICATION_TOOLBOXPROCESS_LOADED      = "ToolboxProcessLoaded";
 
 // Properties that exist in the install manifest
 const PROP_METADATA      = ["id", "version", "type", "internalName", "updateURL",
                             "updateKey", "optionsURL", "optionsType", "aboutURL",
                             "iconURL", "icon64URL"];
 const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
 const PROP_LOCALE_MULTI  = ["developers", "translators", "contributors"];
@@ -800,16 +803,21 @@ function isUsableAddon(aAddon) {
       let active = XPIProvider.activeAddons.get(id);
       return active && !active.disable;
     };
 
     if (aAddon.dependencies.some(id => !isActive(id)))
       return false;
   }
 
+  if (!ALLOW_NON_MPC && aAddon.multiprocessCompatible !== true) {
+    logger.warn(`disabling ${aAddon.id} since it is not multiprocess compatible`);
+    return false;
+  }
+
   if (AddonManager.checkCompatibility) {
     if (!aAddon.isCompatible) {
       logger.warn(`Add-on ${aAddon.id} is not compatible with application version.`);
       return false;
     }
   } else {
     let app = aAddon.matchingTargetApplication;
     if (!app) {
@@ -2810,16 +2818,17 @@ this.XPIProvider = {
       this.enabledAddons = "";
 
       Services.prefs.addObserver(PREF_EM_MIN_COMPAT_APP_VERSION, this);
       Services.prefs.addObserver(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, this);
       Services.prefs.addObserver(PREF_E10S_ADDON_BLOCKLIST, this);
       Services.prefs.addObserver(PREF_E10S_ADDON_POLICY, this);
       if (!REQUIRE_SIGNING)
         Services.prefs.addObserver(PREF_XPI_SIGNATURES_REQUIRED, this);
+      Services.prefs.addObserver(PREF_ALLOW_NON_MPC, this);
       Services.obs.addObserver(this, NOTIFICATION_FLUSH_PERMISSIONS);
 
       // Cu.isModuleLoaded can fail here for external XUL apps where there is
       // no chrome.manifest that defines resource://devtools.
       if (ResProtocolHandler.hasSubstitution("devtools")) {
         if (Cu.isModuleLoaded("resource://devtools/client/framework/ToolboxProcess.jsm")) {
           // If BrowserToolboxProcess is already loaded, set the boolean to true
           // and do whatever is needed
@@ -4473,16 +4482,17 @@ this.XPIProvider = {
         this.updateAddonAppDisabledStates();
         break;
       case PREF_EM_MIN_COMPAT_PLATFORM_VERSION:
         this.minCompatiblePlatformVersion = Preferences.get(PREF_EM_MIN_COMPAT_PLATFORM_VERSION,
                                                             null);
         this.updateAddonAppDisabledStates();
         break;
       case PREF_XPI_SIGNATURES_REQUIRED:
+      case PREF_ALLOW_NON_MPC:
         this.updateAddonAppDisabledStates();
         break;
 
       case PREF_E10S_ADDON_BLOCKLIST:
       case PREF_E10S_ADDON_POLICY:
         XPIDatabase.updateAddonsBlockingE10s();
         break;
       }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_multiprocessCompatible.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_multiprocessCompatible.js
@@ -97,16 +97,86 @@ function build_test(multiprocessCompatib
 for (let bootstrap of [false, true]) {
   for (let multiprocessCompatible of [undefined, false, true]) {
     for (let updateMultiprocessCompatible of [undefined, false, true]) {
       add_task(build_test(multiprocessCompatible, bootstrap, updateMultiprocessCompatible));
     }
   }
 }
 
+add_task(async function test_disable() {
+  const PREF = "extensions.allow-non-mpc-extensions";
+  const ID_MPC = "mpc@tests.mozilla.org";
+  const ID_NON_MPC = "non-mpc@tests.mozilla.org";
+
+  let addonData = {
+    name: "Test Add-on",
+    version: "1.0",
+    bootstrap: true,
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "1"
+    }]
+  }
+
+  let xpi1 = createTempXPIFile(Object.assign({
+    id: ID_MPC,
+    multiprocessCompatible: true,
+  }, addonData));
+  let xpi2 = createTempXPIFile(Object.assign({
+      id: ID_NON_MPC,
+      multiprocessCompatible: false,
+  }, addonData));
+
+  async function testOnce(initialAllow) {
+    if (initialAllow !== undefined) {
+      Services.prefs.setBoolPref(PREF, initialAllow);
+    }
+
+    let install1 = await AddonManager.getInstallForFile(xpi1);
+    let install2 = await AddonManager.getInstallForFile(xpi2);
+    await promiseCompleteAllInstalls([install1, install2]);
+
+    let [addon1, addon2] = await AddonManager.getAddonsByIDs([ID_MPC, ID_NON_MPC]);
+    do_check_neq(addon1, null);
+    do_check_eq(addon1.multiprocessCompatible, true);
+    do_check_eq(addon1.appDisabled, false);
+
+    do_check_neq(addon2, null);
+    do_check_eq(addon2.multiprocessCompatible, false);
+    do_check_eq(addon2.appDisabled, initialAllow === false);
+
+    // Flip the allow-non-mpc preference
+    let newValue = (initialAllow === true) ? false : true;
+    Services.prefs.setBoolPref(PREF, newValue);
+
+    // the mpc extension should never become appDisabled
+    do_check_eq(addon1.appDisabled, false);
+
+    // The non-mpc extension should become disabled if we don't allow non-mpc
+    do_check_eq(addon2.appDisabled, !newValue);
+
+    // Flip the pref back and check appDisabled
+    Services.prefs.setBoolPref(PREF, !newValue);
+
+    do_check_eq(addon1.appDisabled, false);
+    do_check_eq(addon2.appDisabled, newValue);
+
+    addon1.uninstall();
+    addon2.uninstall();
+  }
+
+  await testOnce(undefined);
+  await testOnce(true);
+  await testOnce(false);
+
+  Services.prefs.clearUserPref(PREF);
+});
+
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
   startupManager();
 
   // Create and configure the HTTP server.
   gServer = new HttpServer();
   gServer.registerDirectory("/data/", gTmpD);
   gServer.start(-1);