Bug 700201 - Addons that haven't been updated for an extremely long time should not be compatible by default. r=dtownsend
authorBlair McBride <bmcbride@mozilla.com>
Mon, 14 Nov 2011 14:26:35 +1300
changeset 81928 9591426bab8e6ffcb11d39c7889fae638837a57f
parent 81927 4d16d843aedd71170374de70d97c77351801b362
child 81929 188ee9eaabe70f6c1db0666fa0230e4d5718e9b6
push idunknown
push userunknown
push dateunknown
reviewersdtownsend
bugs700201
milestone11.0a1
Bug 700201 - Addons that haven't been updated for an extremely long time should not be compatible by default. r=dtownsend
browser/app/profile/firefox.js
modules/libpref/src/init/all.js
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -53,16 +53,20 @@ pref("browser.hiddenWindowChromeURL", "c
 
 // Enables some extra Extension System Logging (can reduce performance)
 pref("extensions.logging.enabled", false);
 
 // Enables strict compatibility. To be toggled in bug 698653, to make addons
 // compatibile by default.
 pref("extensions.strictCompatibility", true);
 
+// Specifies a minimum maxVersion an addon needs to say it's compatible with
+// for it to be compatible by default.
+pref("extensions.minCompatibleAppVersion", "4.0");
+
 // Preferences for AMO integration
 pref("extensions.getAddons.cache.enabled", true);
 pref("extensions.getAddons.maxResults", 15);
 pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
 pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%");
 pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%?src=firefox");
 pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%");
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3334,16 +3334,17 @@ pref("html5.flushtimer.subsequentdelay",
 pref("browser.history.allowPushState", true);
 pref("browser.history.allowReplaceState", true);
 pref("browser.history.allowPopState", true);
 pref("browser.history.maxStateObjectSize", 655360);
 
 // XPInstall prefs
 pref("xpinstall.whitelist.required", true);
 pref("extensions.alwaysUnpack", false);
+pref("extensions.minCompatiblePlatformVersion", "2.0");
 
 pref("network.buffer.cache.count", 24);
 pref("network.buffer.cache.size",  32768);
 
 // Desktop Notification
 pref("notification.feature.enabled", false);
 
 // Alert sliding effect
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -77,16 +77,19 @@ const PREF_XPI_WHITELIST_REQUIRED     = 
 const PREF_XPI_WHITELIST_PERMISSIONS  = "xpinstall.whitelist.add";
 const PREF_XPI_BLACKLIST_PERMISSIONS  = "xpinstall.blacklist.add";
 const PREF_XPI_UNPACK                 = "extensions.alwaysUnpack";
 const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
 const PREF_INSTALL_DISTRO_ADDONS      = "extensions.installDistroAddons";
 const PREF_BRANCH_INSTALLED_ADDON     = "extensions.installedDistroAddon.";
 const PREF_SHOWN_SELECTION_UI         = "extensions.shownSelectionUI";
 
+const PREF_EM_MIN_COMPAT_APP_VERSION      = "extensions.minCompatibleAppVersion";
+const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
+
 const URI_EXTENSION_SELECT_DIALOG     = "chrome://mozapps/content/extensions/selectAddons.xul";
 const URI_EXTENSION_UPDATE_DIALOG     = "chrome://mozapps/content/extensions/update.xul";
 const URI_EXTENSION_STRINGS           = "chrome://mozapps/locale/extensions/extensions.properties";
 
 const STRING_TYPE_NAME                = "type.%ID%.name";
 
 const DIR_EXTENSIONS                  = "extensions";
 const DIR_STAGE                       = "staged";
@@ -1468,16 +1471,20 @@ var XPIProvider = {
   // The selected skin to be used by the application when it is restarted. This
   // will be the same as currentSkin when it is the skin to be used when the
   // application is restarted
   selectedSkin: null,
   // The value of the checkCompatibility preference
   checkCompatibility: true,
   // The value of the checkUpdateSecurity preference
   checkUpdateSecurity: true,
+  // The value of the minCompatibleAppVersion preference
+  minCompatibleAppVersion: null,
+  // The value of the minCompatiblePlatformVersion preference
+  minCompatiblePlatformVersion: null,
   // A dictionary of the file descriptors for bootstrappable add-ons by ID
   bootstrappedAddons: {},
   // A dictionary of JS scopes of loaded bootstrappable add-ons by ID
   bootstrapScopes: {},
   // True if the platform could have activated extensions
   extensionsActive: false,
 
   // True if all of the add-ons found during startup were installed in the
@@ -1593,20 +1600,26 @@ var XPIProvider = {
                                          this.defaultSkin);
     this.selectedSkin = this.currentSkin;
     this.applyThemeChange();
 
     this.checkCompatibility = Prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY,
                                                 true)
     this.checkUpdateSecurity = Prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY,
                                                  true)
+    this.minCompatibleAppVersion = Prefs.getCharPref(PREF_EM_MIN_COMPAT_APP_VERSION,
+                                                     null);
+    this.minCompatiblePlatformVersion = Prefs.getCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION,
+                                                          null);
     this.enabledAddons = [];
 
     Services.prefs.addObserver(PREF_EM_CHECK_COMPATIBILITY, this, false);
     Services.prefs.addObserver(PREF_EM_CHECK_UPDATE_SECURITY, this, false);
+    Services.prefs.addObserver(PREF_EM_MIN_COMPAT_APP_VERSION, this, false);
+    Services.prefs.addObserver(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, this, false);
 
     let flushCaches = this.checkForChanges(aAppChanged, aOldAppVersion,
                                            aOldPlatformVersion);
 
     // Changes to installed extensions may have changed which theme is selected
     this.applyThemeChange();
 
     // If the application has been upgraded and there are add-ons outside the
@@ -3325,20 +3338,26 @@ var XPIProvider = {
    * Notified when a preference we're interested in has changed.
    *
    * @see nsIObserver
    */
   observe: function XPI_observe(aSubject, aTopic, aData) {
     switch (aData) {
     case PREF_EM_CHECK_COMPATIBILITY:
     case PREF_EM_CHECK_UPDATE_SECURITY:
+    case PREF_EM_MIN_COMPAT_APP_VERSION:
+    case PREF_EM_MIN_COMPAT_PLATFORM_VERSION:
       this.checkCompatibility = Prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY,
                                                   true);
       this.checkUpdateSecurity = Prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY,
                                                    true);
+      this.minCompatibleAppVersion = Prefs.getCharPref(PREF_EM_MIN_COMPAT_APP_VERSION,
+                                                       null);
+      this.minCompatiblePlatformVersion = Prefs.getCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION,
+                                                            null);
       this.updateAllAddonDisabledStates();
       break;
     }
   },
 
   /**
    * Tests whether enabling an add-on will require a restart.
    *
@@ -6943,16 +6962,28 @@ AddonInternal.prototype = {
       version = aAppVersion;
     else if (app.id == TOOLKIT_ID)
       version = aPlatformVersion
 
     // Only extensions can be compatible by default; themes and language packs
     // always use strict compatibility checking.
     if (this.type == "extension" && !AddonManager.strictCompatibility &&
         !this.strictCompatibility && !this.hasBinaryComponents) {
+
+      // Extremely old extensions should not be compatible by default.
+      let minCompatVersion;
+      if (app.id == Services.appinfo.ID)
+        minCompatVersion = XPIProvider.minCompatibleAppVersion;
+      else if (app.id == TOOLKIT_ID)
+        minCompatVersion = XPIProvider.minCompatiblePlatformVersion;
+
+      if (minCompatVersion &&
+          Services.vc.compare(minCompatVersion, app.maxVersion) > 0)
+        return false;
+
       return Services.vc.compare(version, app.minVersion) >= 0;
     }
 
     return (Services.vc.compare(version, app.minVersion) >= 0) &&
            (Services.vc.compare(version, app.maxVersion) <= 0)
   },
 
   get matchingTargetApplication() {
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -5,16 +5,18 @@
 const AM_Cc = Components.classes;
 const AM_Ci = Components.interfaces;
 
 const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
 const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
 
 const PREF_EM_CHECK_UPDATE_SECURITY   = "extensions.checkUpdateSecurity";
 const PREF_EM_STRICT_COMPATIBILITY    = "extensions.strictCompatibility";
+const PREF_EM_MIN_COMPAT_APP_VERSION      = "extensions.minCompatibleAppVersion";
+const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
 
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/AddonRepository.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
@@ -1100,16 +1102,19 @@ Services.prefs.setCharPref("extensions.u
 Services.prefs.setCharPref("extensions.blocklist.url", "http://127.0.0.1/blocklistURL");
 
 // By default ignore bundled add-ons
 Services.prefs.setBoolPref("extensions.installDistroAddons", false);
 
 // By default use strict compatibility
 Services.prefs.setBoolPref("extensions.strictCompatibility", true);
 
+// By default, set min compatible versions to 0
+Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0");
+Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, "0");
 
 // Register a temporary directory for the tests.
 const gTmpD = gProfD.clone();
 gTmpD.append("temp");
 gTmpD.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 registerDirectory("TmpD", gTmpD);
 
 // Write out an empty blocklist.xml file to the profile to ensure nothing
--- a/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_strictcompatibility.js
@@ -20,74 +20,88 @@ var addon1 = {
 
 // Incompatible in strict compatibility mode
 var addon2 = {
   id: "addon2@tests.mozilla.org",
   version: "1.0",
   name: "Test 2",
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
-    minVersion: "0.1",
-    maxVersion: "0.2"
+    minVersion: "0.7",
+    maxVersion: "0.8"
   }]
 };
 
 // Theme - always uses strict compatibility, so is always incompatible
 var addon3 = {
   id: "addon3@tests.mozilla.org",
   version: "1.0",
   name: "Test 3",
   internalName: "test-theme-3",
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
-    minVersion: "0.1",
-    maxVersion: "0.2"
+    minVersion: "0.8",
+    maxVersion: "0.9"
   }]
 };
 
 // Opt-in to strict compatibility - always incompatible
 var addon4 = {
   id: "addon4@tests.mozilla.org",
   version: "1.0",
   name: "Test 4",
   strictCompatibility: true,
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
-    minVersion: "0.1",
-    maxVersion: "0.2"
+    minVersion: "0.8",
+    maxVersion: "0.9"
   }]
 };
 
 // Addon from the future - would be marked as compatibile-by-default,
 // but minVersion is higher than the app version
 var addon5 = {
   id: "addon5@tests.mozilla.org",
   version: "1.0",
   name: "Test 5",
   targetApplications: [{
     id: "xpcshell@tests.mozilla.org",
     minVersion: "3",
     maxVersion: "5"
   }]
 };
 
+// Extremely old addon - maxVersion is less than the mimimum compat version
+// set in extensions.minCompatibleVersion
+var addon6 = {
+  id: "addon6@tests.mozilla.org",
+  version: "1.0",
+  name: "Test 6",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "0.1",
+    maxVersion: "0.2"
+  }]
+};
+
 
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 
 function do_check_compat_status(aStrict, aAddonCompat, aCallback) {
   do_check_eq(AddonManager.strictCompatibility, aStrict);
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
-                               "addon5@tests.mozilla.org"],
-                              function([a1, a2, a3, a4, a5]) {
+                               "addon5@tests.mozilla.org",
+                               "addon6@tests.mozilla.org"],
+                              function([a1, a2, a3, a4, a5, a6]) {
     do_check_neq(a1, null);
     do_check_eq(a1.isCompatible, aAddonCompat[0]);
     do_check_eq(a1.appDisabled, !aAddonCompat[0]);
     do_check_false(a1.strictCompatibility);
 
     do_check_neq(a2, null);
     do_check_eq(a2.isCompatible, aAddonCompat[1]);
     do_check_eq(a2.appDisabled, !aAddonCompat[1]);
@@ -103,48 +117,67 @@ function do_check_compat_status(aStrict,
     do_check_eq(a4.appDisabled, !aAddonCompat[3]);
     do_check_true(a4.strictCompatibility);
 
     do_check_neq(a5, null);
     do_check_eq(a5.isCompatible, aAddonCompat[4]);
     do_check_eq(a5.appDisabled, !aAddonCompat[4]);
     do_check_false(a5.strictCompatibility);
 
+    do_check_neq(a6, null);
+    do_check_eq(a6.isCompatible, aAddonCompat[5]);
+    do_check_eq(a6.appDisabled, !aAddonCompat[5]);
+    do_check_false(a6.strictCompatibility);
+
     aCallback();
   });
 }
 
 
 function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   
   writeInstallRDFForExtension(addon1, profileDir);
   writeInstallRDFForExtension(addon2, profileDir);
   writeInstallRDFForExtension(addon3, profileDir);
   writeInstallRDFForExtension(addon4, profileDir);
   writeInstallRDFForExtension(addon5, profileDir);
+  writeInstallRDFForExtension(addon6, profileDir);
+
+  Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.1");
 
   startupManager();
-  
+
   // Should default to enabling strict compat.
-  do_check_compat_status(true, [true, false, false, false, false], run_test_1);
+  do_check_compat_status(true, [true, false, false, false, false, false], run_test_1);
 }
 
 function run_test_1() {
+  do_print("Test 1");
   Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
-  do_check_compat_status(false, [true, true, false, false, false], run_test_2);
+  do_check_compat_status(false, [true, true, false, false, false, true], run_test_2);
 }
 
 function run_test_2() {
+  do_print("Test 2");
   restartManager();
-  do_check_compat_status(false, [true, true, false, false, false], run_test_3);
+  do_check_compat_status(false, [true, true, false, false, false, true], run_test_3);
 }
 
 function run_test_3() {
+  do_print("Test 3");
   Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
-  do_check_compat_status(true, [true, false, false, false, false], run_test_4);
+  do_check_compat_status(true, [true, false, false, false, false, false], run_test_4);
 }
 
 function run_test_4() {
+  do_print("Test 4");
   restartManager();
-  do_check_compat_status(true, [true, false, false, false, false], do_test_finished);
+  do_check_compat_status(true, [true, false, false, false, false, false], run_test_5);
 }
+
+function run_test_5() {
+  do_print("Test 5");
+  Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+  Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.4");
+  do_check_compat_status(false, [true, true, false, false, false, false], do_test_finished);
+}
\ No newline at end of file