Bug 620837 - Modify blocklist pingCount parameter to track pingCount for profile and for version. r=mossop, a=approval2.0
authorRobert Strong <robert.bugzilla@gmail.com>
Fri, 14 Jan 2011 13:29:27 -0800
changeset 60611 c9420f27b9dcdb9e3aa9d6114f63199fa599ab4c
parent 60610 392f4f212ea995720be6b5fe2ccce57cc890fe4b
child 60612 0609193325c9eb082e323370847295c20fba939a
push idunknown
push userunknown
push dateunknown
reviewersmossop, approval2
bugs620837
milestone2.0b10pre
Bug 620837 - Modify blocklist pingCount parameter to track pingCount for profile and for version. r=mossop, a=approval2.0
browser/app/profile/firefox.js
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/nsBlocklistService.js
toolkit/mozapps/extensions/test/xpcshell/test_bug620837.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -65,17 +65,17 @@ pref("extensions.getAddons.search.url", 
 pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/%APP%/discovery/%VERSION%/%OS%");
 
 // Blocklist preferences
 pref("extensions.blocklist.enabled", true);
 pref("extensions.blocklist.interval", 86400);
 // Controls what level the blocklist switches from warning about items to forcibly
 // blocking them.
 pref("extensions.blocklist.level", 2);
-pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/");
+pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
 pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
 
 pref("extensions.update.autoUpdateDefault", true);
 
 // Dictionary download preference
 pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/%APP%/dictionaries/");
 
 // Update Timer Manager preferences
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -237,17 +237,17 @@ var AddonManagerInternal = {
     }
     catch (e) { }
 
     if (appChanged !== false) {
       LOG("Application has been upgraded");
       Services.prefs.setCharPref(PREF_EM_LAST_APP_VERSION,
                                  Services.appinfo.version);
       Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNT,
-                                (appChanged === undefined ? 0 : 1));
+                                (appChanged === undefined ? 0 : -1));
     }
 
     // Ensure all default providers have had a chance to register themselves
     DEFAULT_PROVIDERS.forEach(function(url) {
       try {
         Components.utils.import(url, {});
       }
       catch (e) {
@@ -854,17 +854,17 @@ var AddonManagerInternal = {
     let pos = 0;
     while (pos < this.addonListeners.length) {
       if (this.addonListeners[pos] == aListener)
         this.addonListeners.splice(pos, 1);
       else
         pos++;
     }
   },
-  
+
   get autoUpdateDefault() {
     try {
       return Services.prefs.getBoolPref(PREF_EM_AUTOUPDATE_DEFAULT);
     } catch(e) { }
     return true;
   }
 };
 
@@ -1021,17 +1021,17 @@ var AddonManager = {
   // Installed for all of this user's profiles.
   SCOPE_USER: 2,
   // Installed and owned by the application.
   SCOPE_APPLICATION: 4,
   // Installed for all users of the computer.
   SCOPE_SYSTEM: 8,
   // The combination of all scopes.
   SCOPE_ALL: 15,
-  
+
   // Constants for Addon.applyBackgroundUpdates.
   // Indicates that the Addon should not update automatically.
   AUTOUPDATE_DISABLE: 0,
   // Indicates that the Addon should update automatically only if
   // that's the global default.
   AUTOUPDATE_DEFAULT: 1,
   // Indicates that the Addon should update automatically.
   AUTOUPDATE_ENABLE: 2,
@@ -1099,17 +1099,17 @@ var AddonManager = {
 
   addAddonListener: function AM_addAddonListener(aListener) {
     AddonManagerInternal.addAddonListener(aListener);
   },
 
   removeAddonListener: function AM_removeAddonListener(aListener) {
     AddonManagerInternal.removeAddonListener(aListener);
   },
-  
+
   get autoUpdateDefault() {
     return AddonManagerInternal.autoUpdateDefault;
   }
 };
 
 Object.freeze(AddonManagerInternal);
 Object.freeze(AddonManagerPrivate);
 Object.freeze(AddonManager);
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -48,21 +48,23 @@ Components.utils.import("resource://gre/
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org"
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
 const FILE_BLOCKLIST                  = "blocklist.xml";
+const PREF_BLOCKLIST_LASTUPDATETIME   = "app.update.lastUpdateTime.blocklist-background-update-timer";
 const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
 const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
 const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
 const PREF_BLOCKLIST_LEVEL            = "extensions.blocklist.level";
 const PREF_BLOCKLIST_PINGCOUNT        = "extensions.blocklist.pingCount";
+const PREF_BLOCKLIST_TOTALPINGCOUNT   = "extensions.blocklist.totalPingCount";
 const PREF_PLUGINS_NOTIFYUSER         = "plugins.update.notifyUser";
 const PREF_GENERAL_USERAGENT_LOCALE   = "general.useragent.locale";
 const PREF_PARTNER_BRANCH             = "app.partner.";
 const PREF_APP_DISTRIBUTION           = "distribution.id";
 const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
 const PREF_APP_UPDATE_CHANNEL         = "app.update.channel";
 const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
 const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
@@ -199,17 +201,17 @@ function newURI(spec) {
 function restartApp() {
   // Notify all windows that an application quit has been requested.
   var os = Cc["@mozilla.org/observer-service;1"].
            getService(Ci.nsIObserverService);
   var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
                    createInstance(Ci.nsISupportsPRBool);
   os.notifyObservers(cancelQuit, "quit-application-requested", null);
 
-  // Something aborted the quit process. 
+  // Something aborted the quit process.
   if (cancelQuit.data)
     return;
 
   var as = Cc["@mozilla.org/toolkit/app-startup;1"].
            getService(Ci.nsIAppStartup);
   as.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
 }
 
@@ -220,23 +222,23 @@ function restartApp() {
  * xpcomabi attribute.
  */
 function matchesOSABI(blocklistElement) {
   if (blocklistElement.hasAttribute("os")) {
     var choices = blocklistElement.getAttribute("os").split(",");
     if (choices.length > 0 && choices.indexOf(gApp.OS) < 0)
       return false;
   }
-  
+
   if (blocklistElement.hasAttribute("xpcomabi")) {
     choices = blocklistElement.getAttribute("xpcomabi").split(",");
     if (choices.length > 0 && choices.indexOf(gApp.XPCOMABI) < 0)
       return false;
   }
-  
+
   return true;
 }
 
 /**
  * Gets the current value of the locale.  It's possible for this preference to
  * be localized, so we have to do a little extra work here.  Similar code
  * exists in nsHttpHandler.cpp when building the UA string.
  */
@@ -422,50 +424,75 @@ Blocklist.prototype = {
       var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
     }
     catch (e) {
       LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" +
           " is missing!");
       return;
     }
 
-    var pingCount = 0;
-    try {
-      pingCount = gPref.getIntPref(PREF_BLOCKLIST_PINGCOUNT);
+    var pingCount = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNT, 0);
+    var totalPingCount = getPref("getIntPref", PREF_BLOCKLIST_TOTALPINGCOUNT, 1);
+    var daysSinceLastPing;
+    if (pingCount < 1) {
+      daysSinceLastPing = pingCount == 0 ? "new" : "reset";
+      pingCount = 1;
     }
-    catch (e) {
+    else {
+      // Seconds in one day is used because nsIUpdateTimerManager stores the
+      // last update time in seconds.
+      let secondsInDay = 60 * 60 * 24;
+      let lastUpdateTime = getPref("getIntPref", PREF_BLOCKLIST_LASTUPDATETIME, 0);
+      if (lastUpdateTime != 0) {
+        let now = Math.round(Date.now() / 1000);
+        daysSinceLastPing = Math.floor((now - lastUpdateTime) / secondsInDay);
+      }
+      else {
+        daysSinceLastPing = "invalid";
+      }
     }
-    if (pingCount < 0)
-      pingCount = 1;
 
     dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
     dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
     dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
     dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
     dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
     dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
     dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
     dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
     dsURI = dsURI.replace(/%CHANNEL%/g, getUpdateChannel());
     dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
     dsURI = dsURI.replace(/%DISTRIBUTION%/g,
                       getDistributionPrefValue(PREF_APP_DISTRIBUTION));
     dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
                       getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
     dsURI = dsURI.replace(/%PING_COUNT%/g, pingCount);
+    dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, totalPingCount);
+    dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
     dsURI = dsURI.replace(/\+/g, "%2B");
 
+    // Under normal operations it will take around 5,883,516 years before the
+    // preferences used to store pingCount and totalPingCount will rollover
+    // so this code doesn't bother trying to do the "right thing" here.
     pingCount++;
     if (pingCount > 2147483647) {
-      // Rollover to 1 if the value is greater than what is support by an
-      // integer preference. The 1 indicates that this is an existing profile.
-      pingCount = 1;
+      // Rollover to -1 if the value is greater than what is support by an
+      // integer preference. The -1 indicates that the counter has been reset.
+      pingCount = -1;
     }
     gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNT, pingCount);
 
+    totalPingCount++;
+    if (totalPingCount > 2147483647) {
+      // Rollover to 1 if the value is greater than what is support by an
+      // integer preference.
+      totalPingCount = 1;
+    }
+    gPref.setIntPref(PREF_BLOCKLIST_TOTALPINGCOUNT, totalPingCount);
+
     // Verify that the URI is valid
     try {
       var uri = newURI(dsURI);
     }
     catch (e) {
       LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" +
           "for: " + dsURI + ", error: " + e);
       return;
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug620837.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+do_load_httpd_js();
+
+const PREF_BLOCKLIST_LASTUPDATETIME = "app.update.lastUpdateTime.blocklist-background-update-timer";
+const PREF_BLOCKLIST_PINGCOUNT      = "extensions.blocklist.pingCount";
+
+const SECONDS_IN_DAY = 60 * 60 * 24;
+
+var gExpectedQueryString = null;
+var gNextTest = null;
+var gTestserver = null;
+
+function notify_blocklist() {
+  var blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+                  getService(AM_Ci.nsITimerCallback);
+  blocklist.notify(null);
+}
+
+function pathHandler(metadata, response) {
+  do_check_eq(metadata.queryString, gExpectedQueryString);
+  gNextTest();
+}
+
+function run_test() {
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+  gTestserver = new nsHttpServer();
+  gTestserver.registerPathHandler("/", pathHandler);
+  gTestserver.start(4444);
+
+  Services.prefs.setCharPref("extensions.blocklist.url",
+                             "http://localhost:4444/?%PING_COUNT%&%TOTAL_PING_COUNT%&%DAYS_SINCE_LAST_PING%");
+
+  do_test_pending();
+  test1();
+}
+
+function getNowInSeconds() {
+  return Math.round(Date.now() / 1000);
+}
+
+function test1() {
+  gNextTest = test2;
+  gExpectedQueryString = "1&1&new";
+  notify_blocklist();
+}
+
+function test2() {
+  gNextTest = test3;
+  gExpectedQueryString = "2&2&invalid";
+  notify_blocklist();
+}
+
+function test3() {
+  Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+                            (getNowInSeconds() - SECONDS_IN_DAY));
+  gNextTest = test4;
+  gExpectedQueryString = "3&3&1";
+  notify_blocklist();
+}
+
+function test4() {
+  Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNT, -1);
+  Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+                            (getNowInSeconds() - (SECONDS_IN_DAY * 2)));
+  gNextTest = test5;
+  gExpectedQueryString = "1&4&reset";
+  notify_blocklist();
+}
+
+function test5() {
+  Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+                            (getNowInSeconds() - (SECONDS_IN_DAY * 3)));
+  gNextTest = finish;
+  gExpectedQueryString = "2&5&3";
+  notify_blocklist();
+}
+
+function finish() {
+  gTestserver.stop(do_test_finished);
+}