Bug 705530 - Support strictCompatibility option in update.rdf. r=dtownsend a=akeybl
authorBlair McBride <bmcbride@mozilla.com>
Fri, 02 Dec 2011 15:28:16 +1300
changeset 80755 29d36e0a2564
parent 80754 b15fd1a50810
child 80756 31127fa275a9
push id591
push userbmcbride@mozilla.com
push date2011-12-10 03:51 +0000
treeherdermozilla-aurora@31127fa275a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdtownsend, akeybl
bugs705530
milestone10.0a2
Bug 705530 - Support strictCompatibility option in update.rdf. r=dtownsend a=akeybl
toolkit/mozapps/extensions/AddonUpdateChecker.jsm
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf
toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf
toolkit/mozapps/extensions/test/xpcshell/test_update.js
toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js
--- a/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
+++ b/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
@@ -390,16 +390,17 @@ function parseRDFManifest(aId, aType, aU
       }
 
       let result = {
         id: aId,
         version: version,
         updateURL: getProperty(ds, targetApp, "updateLink"),
         updateHash: getProperty(ds, targetApp, "updateHash"),
         updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"),
+        strictCompatibility: getProperty(ds, targetApp, "strictCompatibility") == "true",
         targetApplications: [appEntry]
       };
 
       if (result.updateURL && checkSecurity &&
           result.updateURL.substring(0, 6) != "https:" &&
           (!result.updateHash || result.updateHash.substring(0, 3) != "sha")) {
         WARN("updateLink " + result.updateURL + " is not secure and is not verified" +
              " by a strong enough hash (needs to be sha1 or stronger).");
@@ -593,31 +594,37 @@ UpdateParser.prototype = {
  * @param  aUpdate
  *         The available update
  * @param  aAppVersion
  *         The application version to use
  * @param  aPlatformVersion
  *         The platform version to use
  * @param  aIgnoreMaxVersion
  *         Ignore maxVersion when testing if an update matches. Optional.
+ * @param  aIgnoreStrictCompat
+ *         Ignore strictCompatibility when testing if an update matches. Optional.
  * @param  aCompatOverrides
  *         AddonCompatibilityOverride objects to match against. Optional.
  * @return true if the update is compatible with the application/platform
  */
 function matchesVersions(aUpdate, aAppVersion, aPlatformVersion,
-                         aIgnoreMaxVersion, aCompatOverrides) {
+                         aIgnoreMaxVersion, aIgnoreStrictCompat,
+                         aCompatOverrides) {
   if (aCompatOverrides) {
     let override = AddonRepository.findMatchingCompatOverride(aUpdate.version,
                                                               aCompatOverrides,
                                                               aAppVersion,
                                                               aPlatformVersion);
     if (override && override.type == "incompatible")
       return false;
   }
 
+  if (aUpdate.strictCompatibility && !aIgnoreStrictCompat)
+    aIgnoreMaxVersion = false;
+
   let result = false;
   for (let i = 0; i < aUpdate.targetApplications.length; i++) {
     let app = aUpdate.targetApplications[i];
     if (app.id == Services.appinfo.ID) {
       return (Services.vc.compare(aAppVersion, app.minVersion) >= 0) &&
              (aIgnoreMaxVersion || (Services.vc.compare(aAppVersion, app.maxVersion) <= 0));
     }
     if (app.id == TOOLKIT_ID) {
@@ -653,39 +660,42 @@ var AddonUpdateChecker = {
    *         An optional parameter to get the first compatibility update that
    *         is compatible with any version of the application or toolkit
    * @param  aAppVersion
    *         The version of the application or null to use the current version
    * @param  aPlatformVersion
    *         The version of the platform or null to use the current version
    * @param  aIgnoreMaxVersion
    *         Ignore maxVersion when testing if an update matches. Optional.
+   * @param  aIgnoreStrictCompat
+   *         Ignore strictCompatibility when testing if an update matches. Optional.
    * @return an update object if one matches or null if not
    */
   getCompatibilityUpdate: function AUC_getCompatibilityUpdate(aUpdates, aVersion,
                                                               aIgnoreCompatibility,
                                                               aAppVersion,
                                                               aPlatformVersion,
-                                                              aIgnoreMaxVersion) {
+                                                              aIgnoreMaxVersion,
+                                                              aIgnoreStrictCompat) {
     if (!aAppVersion)
       aAppVersion = Services.appinfo.version;
     if (!aPlatformVersion)
       aPlatformVersion = Services.appinfo.platformVersion;
 
     for (let i = 0; i < aUpdates.length; i++) {
       if (Services.vc.compare(aUpdates[i].version, aVersion) == 0) {
         if (aIgnoreCompatibility) {
           for (let j = 0; j < aUpdates[i].targetApplications.length; j++) {
             let id = aUpdates[i].targetApplications[j].id;
             if (id == Services.appinfo.ID || id == TOOLKIT_ID)
               return aUpdates[i];
           }
         }
         else if (matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion,
-                                 aIgnoreMaxVersion)) {
+                                 aIgnoreMaxVersion, aIgnoreStrictCompat)) {
           return aUpdates[i];
         }
       }
     }
     return null;
   },
 
   /**
@@ -694,24 +704,27 @@ var AddonUpdateChecker = {
    * @param  aUpdates
    *         An array of update objects
    * @param  aAppVersion
    *         The version of the application or null to use the current version
    * @param  aPlatformVersion
    *         The version of the platform or null to use the current version
    * @param  aIgnoreMaxVersion
    *         When determining compatible updates, ignore maxVersion. Optional.
+   * @param  aIgnoreMaxVersion
+   *         When determining compatible updates, ignore strictCompatibility. Optional.
    * @param  aCompatOverrides
    *         Array of AddonCompatibilityOverride to take into account. Optional.
    * @return an update object if one matches or null if not
    */
   getNewestCompatibleUpdate: function AUC_getNewestCompatibleUpdate(aUpdates,
                                                                     aAppVersion,
                                                                     aPlatformVersion,
                                                                     aIgnoreMaxVersion,
+                                                                    aIgnoreStrictCompat,
                                                                     aCompatOverrides) {
     if (!aAppVersion)
       aAppVersion = Services.appinfo.version;
     if (!aPlatformVersion)
       aPlatformVersion = Services.appinfo.platformVersion;
 
     let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
                     getService(Ci.nsIBlocklistService);
@@ -721,17 +734,18 @@ var AddonUpdateChecker = {
       if (!aUpdates[i].updateURL)
         continue;
       let state = blocklist.getAddonBlocklistState(aUpdates[i].id, aUpdates[i].version,
                                                    aAppVersion, aPlatformVersion);
       if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
         continue;
       if ((newest == null || (Services.vc.compare(newest.version, aUpdates[i].version) < 0)) &&
           matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion,
-                          aIgnoreMaxVersion, aCompatOverrides)) {
+                          aIgnoreMaxVersion, aIgnoreStrictCompat,
+                          aCompatOverrides)) {
         newest = aUpdates[i];
       }
     }
     return newest;
   },
 
   /**
    * Starts an update check.
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -6858,45 +6858,49 @@ UpdateChecker.prototype = {
    *
    * @param  updates
    *         The list of update details for the add-on
    */
   onUpdateCheckComplete: function UC_onUpdateCheckComplete(aUpdates) {
     let AUC = AddonUpdateChecker;
 
     let ignoreMaxVersion = false;
+    let ignoreStrictCompat = false;
     if (!XPIProvider.checkCompatibility) {
       ignoreMaxVersion = true;
+      ignoreStrictCompat = true;
     } else if (this.addon.type == "extension" &&
                !AddonManager.strictCompatibility &&
                !this.addon.strictCompatibility &&
                !this.addon.hasBinaryComponents) {
       ignoreMaxVersion = true;
     }
 
     // Always apply any compatibility update for the current version
     let compatUpdate = AUC.getCompatibilityUpdate(aUpdates, this.addon.version,
                                                   this.syncCompatibility,
                                                   null, null,
-                                                  ignoreMaxVersion);
+                                                  ignoreMaxVersion,
+                                                  ignoreStrictCompat);
     // Apply the compatibility update to the database
     if (compatUpdate)
       this.addon.applyCompatibilityUpdate(compatUpdate, this.syncCompatibility);
 
     // If the request is for an application or platform version that is
     // different to the current application or platform version then look for a
     // compatibility update for those versions.
     if ((this.appVersion &&
          Services.vc.compare(this.appVersion, Services.appinfo.version) != 0) ||
         (this.platformVersion &&
          Services.vc.compare(this.platformVersion, Services.appinfo.platformVersion) != 0)) {
       compatUpdate = AUC.getCompatibilityUpdate(aUpdates, this.addon.version,
                                                 false, this.appVersion,
                                                 this.platformVersion,
-                                                ignoreMaxVersion);
+                                                ignoreMaxVersion,
+                                                ignoreStrictCompat);
     }
 
     if (compatUpdate)
       this.callListener("onCompatibilityUpdateAvailable", createWrapper(this.addon));
     else
       this.callListener("onNoCompatibilityUpdateAvailable", createWrapper(this.addon));
 
     function sendUpdateAvailableMessages(aSelf, aInstall) {
@@ -6914,16 +6918,17 @@ UpdateChecker.prototype = {
     let compatOverrides = AddonManager.strictCompatibility ?
                             null :
                             this.addon.compatibilityOverrides;
 
     let update = AUC.getNewestCompatibleUpdate(aUpdates,
                                                this.appVersion,
                                                this.platformVersion,
                                                ignoreMaxVersion,
+                                               ignoreStrictCompat,
                                                compatOverrides);
 
     if (update && Services.vc.compare(this.addon.version, update.version) < 0) {
       for (let i = 0; i < XPIProvider.installs.length; i++) {
         // Skip installs that don't match the available update
         if (XPIProvider.installs[i].existingAddon != this.addon ||
             XPIProvider.installs[i].version != update.version)
           continue;
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_update.rdf
@@ -221,9 +221,30 @@
                 <em:updateLink>http://localhost:4444/addons/test_update10.xpi</em:updateLink>
               </Description>
             </em:targetApplication>
           </Description>
         </li>
       </Seq>
     </em:updates>
   </Description>
+
+  <Description about="urn:mozilla:extension:addon11@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>2.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.1</em:minVersion>
+                <em:maxVersion>0.2</em:maxVersion>
+                <em:strictCompatibility>true</em:strictCompatibility>
+                <em:updateLink>http://localhost:4444/addons/test_update11.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
 </RDF>
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecheck.rdf
@@ -389,9 +389,31 @@
                 <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
               </Description>
             </em:targetApplication>
           </Description>
         </li>
       </Seq>
     </em:updates>
   </Description>
+
+  <!-- Opt-in to strict compatibility checking -->
+  <Description about="urn:mozilla:extension:compat-strict-optin@tests.mozilla.org">
+    <em:updates>
+      <Seq>
+        <li>
+          <Description>
+            <em:version>1.0</em:version>
+            <em:targetApplication>
+              <Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>0.1</em:minVersion>
+                <em:maxVersion>0.2</em:maxVersion>
+                <em:strictCompatibility>true</em:strictCompatibility>
+                <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+              </Description>
+            </em:targetApplication>
+          </Description>
+        </li>
+      </Seq>
+    </em:updates>
+  </Description>
 </RDF>
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -1080,13 +1080,48 @@ function run_test_18() {
         do_throw("Should have seen compatibility information");
       },
 
       onUpdateAvailable: function() {
         do_throw("Should not have seen an available update");
       },
 
       onUpdateFinished: function() {
-        end_test();
+        run_test_19();
       }
     }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
   });
 }
+
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_19() {
+  writeInstallRDFForExtension({
+    id: "addon11@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 11",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+    do_check_neq(a11, null);
+
+    a11.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onUpdateAvailable: function() {
+        do_throw("Should not have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        end_test();
+      }
+   }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
@@ -48,18 +48,52 @@ function run_test_1() {
 
   AddonManager.addInstallListener({
     onNewInstall: function(aInstall) {
       if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
         do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
       do_check_eq(aInstall.version, "4.0");
     },
     onDownloadFailed: function(aInstall) {
-      end_test();
+      do_execute_soon(run_test_2);
     }
   });
 
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, "http://localhost:4444/data/test_update.xml");
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
   // Fake a timer event
   gInternalManager.notify(null);
 }
 
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_2() {
+  writeInstallRDFForExtension({
+    id: "addon11@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 11",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+    do_check_neq(a11, null);
+
+    a11.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onNoUpdateAvailable: function() {
+        do_throw("Should have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        end_test();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
@@ -1040,18 +1040,53 @@ function run_test_16() {
 
   AddonManager.addInstallListener({
     onNewInstall: function(aInstall) {
       if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
         do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
       do_check_eq(aInstall.version, "2.0");
     },
     onDownloadFailed: function(aInstall) {
-      end_test();
+      do_execute_soon(run_test_17);
     }
   });
 
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, "http://localhost:4444/data/test_update.xml");
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
   // Fake a timer event
   gInternalManager.notify(null);
 }
 
+// Test that the update check correctly observes when an addon opts-in to
+// strict compatibility checking.
+function run_test_17() {
+
+  writeInstallRDFForExtension({
+    id: "addon11@tests.mozilla.org",
+    version: "1.0",
+    updateURL: "http://localhost:4444/data/test_update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "0.1",
+      maxVersion: "0.2"
+    }],
+    name: "Test Addon 11",
+  }, profileDir);
+  restartManager();
+
+  AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+    do_check_neq(a11, null);
+
+    a11.findUpdates({
+      onCompatibilityUpdateAvailable: function() {
+        do_throw("Should have not have seen compatibility information");
+      },
+
+      onUpdateAvailable: function() {
+        do_throw("Should not have seen an available update");
+      },
+
+      onUpdateFinished: function() {
+        end_test();
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_updatecheck.js
@@ -284,19 +284,41 @@ function run_test_13() {
         appID: "xpcshell@tests.mozilla.org",
         appMinVersion: 1,
         appMaxVersion: 2
       }];
       let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
                                                                 null,
                                                                 null,
                                                                 true,
+                                                                false,
                                                                 overrides);
       do_check_neq(update, null);
       do_check_eq(update.version, 1);
+      run_test_14();
+    },
+
+    onUpdateCheckError: function(status) {
+      do_throw("Update check failed with status " + status);
+    }
+  });
+}
+
+function run_test_14() {
+  AddonUpdateChecker.checkForUpdates("compat-strict-optin@tests.mozilla.org",
+                                     "extension", null,
+                                     "http://localhost:4444/data/test_updatecheck.rdf", {
+    onUpdateCheckComplete: function(updates) {
+      do_check_eq(updates.length, 1);
+      let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates,
+                                                                null,
+                                                                null,
+                                                                true,
+                                                                false);
+      do_check_eq(update, null);
       end_test();
     },
 
     onUpdateCheckError: function(status) {
       do_throw("Update check failed with status " + status);
     }
   });
 }