Bug 1010449 - Add telemetry for add-on compatibility changes, upgrades during startup time add-on update check. r=unfocused, a=sledru
authorIrving Reid <irving@mozilla.com>
Tue, 20 May 2014 13:15:24 -0400
changeset 200471 e79674fe4a79d5464e626f837481fb65e4e4c7b2
parent 200470 e812918523be3d7b83c12cc3fca318015a112968
child 200472 3253bb6f2a281521de0f7b1a9cf5ffd7c91a4dc9
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersunfocused, sledru
bugs1010449
milestone31.0a2
Bug 1010449 - Add telemetry for add-on compatibility changes, upgrades during startup time add-on update check. r=unfocused, a=sledru
toolkit/mozapps/extensions/content/update.js
toolkit/mozapps/extensions/test/browser/browser_bug557956.js
--- a/toolkit/mozapps/extensions/content/update.js
+++ b/toolkit/mozapps/extensions/content/update.js
@@ -35,16 +35,24 @@ var gUpdateWizard = {
   shouldSuggestAutoChecking: false,
   shouldAutoCheck: false,
   xpinstallEnabled: true,
   xpinstallLocked: false,
   // cached AddonInstall entries for add-ons we might want to update,
   // keyed by add-on ID
   addonInstalls: new Map(),
   shuttingDown: false,
+  // Count the add-ons disabled by this update, enabled/disabled by
+  // metadata checks, and upgraded.
+  disabled: 0,
+  metadataEnabled: 0,
+  metadataDisabled: 0,
+  upgraded: 0,
+  upgradeFailed: 0,
+  upgradeDeclined: 0,
 
   init: function gUpdateWizard_init()
   {
     this.inactiveAddonIDs = window.arguments[0];
 
     try {
       this.shouldSuggestAutoChecking =
         !Services.prefs.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
@@ -146,16 +154,28 @@ var gOfflinePage = {
 
   toggleOffline: function gOfflinePage_toggleOffline()
   {
     var nextbtn = document.documentElement.getButton("next");
     nextbtn.disabled = !nextbtn.disabled;
   }
 }
 
+// Addon listener to count addons enabled/disabled by metadata checks
+let listener = {
+  onDisabled: function listener_onDisabled(aAddon) {
+    logger.debug("onDisabled for ${id}", aAddon);
+    gUpdateWizard.metadataDisabled++;
+  },
+  onEnabled: function listener_onEnabled(aAddon) {
+    logger.debug("onEnabled for ${id}", aAddon);
+    gUpdateWizard.metadataEnabled++;
+  }
+};
+
 var gVersionInfoPage = {
   _completeCount: 0,
   _totalCount: 0,
   _versionInfoDone: false,
   onPageShow: function gVersionInfoPage_onPageShow()
   {
     gUpdateWizard.setButtonLabels(null, true,
                                   "nextButtonText", true,
@@ -172,35 +192,56 @@ var gVersionInfoPage = {
         logger.debug("getAllAddons completed after dialog closed");
       }
 
       gUpdateWizard.addons = [a for (a of aAddons)
                                if (a.type != "plugin" && a.id != hotfixID)];
 
       gVersionInfoPage._totalCount = gUpdateWizard.addons.length;
 
+      // Count the add-ons newly disabled by this application update
+      for (let addon of gUpdateWizard.addons) {
+        if (gUpdateWizard.inactiveAddonIDs.indexOf(addon.id) != -1) {
+          gUpdateWizard.disabled++;
+        }
+      }
+
       // Ensure compatibility overrides are up to date before checking for
       // individual addon updates.
       let ids = [addon.id for (addon of gUpdateWizard.addons)];
 
+      // Do the metadata ping, listening for any newly enabled/disabled add-ons.
+      AddonManager.addAddonListener(listener);
       AddonRepository.repopulateCache(ids, function gVersionInfoPage_repopulateCache() {
 
         if (gUpdateWizard.shuttingDown) {
           logger.debug("repopulateCache completed after dialog closed");
         }
 
         for (let addon of gUpdateWizard.addons) {
           logger.debug("VersionInfo Finding updates for " + addon.id);
           addon.findUpdates(gVersionInfoPage, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
         }
       }, METADATA_TIMEOUT);
     });
   },
 
   onAllUpdatesFinished: function gVersionInfoPage_onAllUpdatesFinished() {
+    AddonManager.removeAddonListener(listener);
+    AddonManagerPrivate.recordSimpleMeasure("appUpdate_disabled",
+        gUpdateWizard.disabled);
+    AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_enabled",
+        gUpdateWizard.metadataEnabled);
+    AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_disabled",
+        gUpdateWizard.metadataDisabled);
+    // Record 0 for these here in case we exit early; values will be replaced
+    // later if we actually upgrade any.
+    AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded", 0);
+    AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed", 0);
+    AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined", 0);
     // Filter out any add-ons that were disabled before the application was
     // upgraded or are already compatible
     logger.debug("VersionInfo updates finished: inactive " +
          gUpdateWizard.inactiveAddonIDs.toSource() + " found " +
          [addon.id + ":" + addon.appDisabled for (addon of gUpdateWizard.addons)].toSource());
     let filteredAddons = [];
     for (let a of gUpdateWizard.addons) {
       if (a.appDisabled && gUpdateWizard.inactiveAddonIDs.indexOf(a.id) < 0) {
@@ -254,18 +295,20 @@ var gVersionInfoPage = {
            " finished for " + aAddon.id);
     }
 
     // If we're not in the background, just make a list of add-ons that have
     // updates available
     if (!gUpdateWizard.shuttingDown) {
       // If we're still in the update check window and the add-on is now active
       // then it won't have been disabled by startup
-      if (aAddon.active)
+      if (aAddon.active) {
         AddonManagerPrivate.removeStartupChange("disabled", aAddon.id);
+        gUpdateWizard.metadataEnabled++;
+      }
 
       // Update the status text and progress bar
       var updateStrings = document.getElementById("updateStrings");
       var statusElt = document.getElementById("versioninfo.status");
       var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
       statusElt.setAttribute("value", statusString);
 
       // Update the status text and progress bar
@@ -456,16 +499,17 @@ var gInstallingPage = {
                                   null, true);
 
     var foundUpdates = document.getElementById("found.updates");
     var updates = foundUpdates.getElementsByTagName("listitem");
     let toInstall = [];
     for (let update of updates) {
       if (!update.checked) {
         logger.info("User chose to cancel update of " + update.label);
+        gUpdateWizard.upgradeDeclined++;
         update.install.cancel();
         continue;
       }
       toInstall.push(update.install);
     }
     this._strings = document.getElementById("updateStrings");
 
     this.startInstalls(toInstall);
@@ -475,30 +519,37 @@ var gInstallingPage = {
     if (this._currentInstall >= 0) {
       this._installs[this._currentInstall].removeListener(this);
     }
 
     this._currentInstall++;
 
     if (this._installs.length == this._currentInstall) {
       Services.obs.notifyObservers(null, "TEST:all-updates-done", null);
+      AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded",
+          gUpdateWizard.upgraded);
+      AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed",
+          gUpdateWizard.upgradeFailed);
+      AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined",
+          gUpdateWizard.upgradeDeclined);
       this._installing = false;
       if (gUpdateWizard.shuttingDown) {
         return;
       }
       var nextPage = this._errors.length > 0 ? "installerrors" : "finished";
       document.getElementById("installing").setAttribute("next", nextPage);
       document.documentElement.advance();
       return;
     }
 
     let install = this._installs[this._currentInstall];
 
     if (gUpdateWizard.shuttingDown && !AddonManager.shouldAutoUpdate(install.existingAddon)) {
       logger.debug("Don't update " + install.existingAddon.id + " in background");
+      gUpdateWizard.upgradeDeclined++;
       install.cancel();
       this.startNextInstall();
       return;
     }
     install.addListener(this);
     install.install();
   },
 
@@ -523,16 +574,17 @@ var gInstallingPage = {
   },
 
   onDownloadEnded: function gInstallingPage_onDownloadEnded(aInstall) {
   },
 
   onDownloadFailed: function gInstallingPage_onDownloadFailed(aInstall) {
     this._errors.push(aInstall);
 
+    gUpdateWizard.upgradeFailed++;
     this.startNextInstall();
   },
 
   onInstallStarted: function gInstallingPage_onInstallStarted(aInstall) {
     if (gUpdateWizard.shuttingDown) {
       return;
     }
     var strings = document.getElementById("updateStrings");
@@ -543,22 +595,24 @@ var gInstallingPage = {
 
   onInstallEnded: function gInstallingPage_onInstallEnded(aInstall, aAddon) {
     if (!gUpdateWizard.shuttingDown) {
       // Remember that this add-on was updated during startup
       AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
                                            aAddon.id);
     }
 
+    gUpdateWizard.upgraded++;
     this.startNextInstall();
   },
 
   onInstallFailed: function gInstallingPage_onInstallFailed(aInstall) {
     this._errors.push(aInstall);
 
+    gUpdateWizard.upgradeFailed++;
     this.startNextInstall();
   }
 };
 
 var gInstallErrorsPage = {
   onPageShow: function gInstallErrorsPage_onPageShow()
   {
     gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
--- a/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
@@ -1,21 +1,25 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-// Test that the compatibility dialog that normally displays during startup
-// appears to work correctly.
+// Test the compatibility dialog that displays during startup when the browser
+// version changes.
 
 const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
 
 const PREF_GETADDONS_BYIDS            = "extensions.getAddons.get.url";
 const PREF_MIN_PLATFORM_COMPAT        = "extensions.minCompatiblePlatformVersion";
 
 Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
+// avoid the 'leaked window property' check
+let scope = {};
+Components.utils.import("resource://gre/modules/TelemetryPing.jsm", scope);
+let TelemetryPing = scope.TelemetryPing;
 
 /**
  * Test add-ons:
  *
  * Addon    minVersion   maxVersion   Notes
  * addon1   0            *
  * addon2   0            0
  * addon3   0            0
@@ -164,47 +168,66 @@ function wait_for_page(aWindow, aPageId,
 function get_list_names(aList) {
   var items = [];
   for (let listItem of aList.childNodes)
     items.push(listItem.label);
   items.sort();
   return items;
 }
 
+function check_telemetry({disabled, metaenabled, metadisabled, upgraded, failed, declined}) {
+  let ping = TelemetryPing.getPayload();
+  // info(JSON.stringify(ping));
+  let am = ping.simpleMeasurements.addonManager;
+  if (disabled !== undefined)
+    is(am.appUpdate_disabled, disabled, disabled + " add-ons disabled by version change");
+  if (metaenabled !== undefined)
+    is(am.appUpdate_metadata_enabled, metaenabled, metaenabled + " add-ons enabled by metadata");
+  if (metadisabled !== undefined)
+    is(am.appUpdate_metadata_disabled, metadisabled, metadisabled + " add-ons disabled by metadata");
+  if (upgraded !== undefined)
+    is(am.appUpdate_upgraded, upgraded, upgraded + " add-ons upgraded");
+  if (failed !== undefined)
+    is(am.appUpdate_upgradeFailed, failed, failed + " upgrades failed");
+  if (declined !== undefined)
+    is(am.appUpdate_upgradeDeclined, declined, declined + " upgrades declined");
+}
+
 // Tests that the right add-ons show up in the mismatch dialog and updates can
 // be installed
 add_test(function() {
   install_test_addons(function() {
     // These add-ons were inactive in the old application
     var inactiveAddonIds = [
       "addon2@tests.mozilla.org",
       "addon4@tests.mozilla.org",
       "addon5@tests.mozilla.org",
       "addon10@tests.mozilla.org"
     ];
 
-    // Check that compatibility updates were applied.
     AddonManager.getAddonsByIDs(["addon5@tests.mozilla.org",
                                  "addon6@tests.mozilla.org"],
                                  function([a5, a6]) {
+      // Check starting (pre-update) conditions
       ok(!a5.isCompatible, "addon5 should not be compatible");
       ok(!a6.isCompatible, "addon6 should not be compatible");
 
       open_compatibility_window(inactiveAddonIds, function(aWindow) {
         var doc = aWindow.document;
         wait_for_page(aWindow, "mismatch", function(aWindow) {
           var items = get_list_names(doc.getElementById("mismatch.incompatible"));
+          // Check that compatibility updates from individual add-on update checks were applied.
           is(items.length, 4, "Should have seen 4 still incompatible items");
           is(items[0], "Addon3 1.0", "Should have seen addon3 still incompatible");
           is(items[1], "Addon7 1.0", "Should have seen addon7 still incompatible");
           is(items[2], "Addon8 1.0", "Should have seen addon8 still incompatible");
           is(items[3], "Addon9 1.0", "Should have seen addon9 still incompatible");
 
           ok(a5.isCompatible, "addon5 should be compatible");
-          ok(a6.isCompatible, "addon5 should be compatible");
+          ok(a6.isCompatible, "addon6 should be compatible");
 
           var button = doc.documentElement.getButton("next");
           EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
 
           wait_for_page(aWindow, "found", function(aWindow) {
             ok(doc.getElementById("xpinstallDisabledAlert").hidden,
                "Install should be allowed");
 
@@ -242,16 +265,19 @@ add_test(function() {
 
               wait_for_window_close(aWindow, function() {
                 AddonManager.getAddonsByIDs(["addon8@tests.mozilla.org",
                                              "addon9@tests.mozilla.org"],
                                              function([a8, a9]) {
                   is(a8.version, "2.0", "addon8 should have updated");
                   is(a9.version, "2.0", "addon9 should have updated");
   
+                  check_telemetry({disabled: 4, metaenabled: 2, metadisabled: 0,
+                                   upgraded: 2, failed: 0, declined: 1});
+
                   uninstall_test_addons(run_next_test);
                 });
               });
             });
           });
         });
       });
     });
@@ -282,17 +308,17 @@ add_test(function() {
         is(items[2], "Addon8 1.0", "Should have seen addon8 still incompatible");
         is(items[3], "Addon9 1.0", "Should have seen addon9 still incompatible");
 
         // Check that compatibility updates were applied.
         AddonManager.getAddonsByIDs(["addon5@tests.mozilla.org",
                                      "addon6@tests.mozilla.org"],
                                      function([a5, a6]) {
           ok(a5.isCompatible, "addon5 should be compatible");
-          ok(a6.isCompatible, "addon5 should be compatible");
+          ok(a6.isCompatible, "addon6 should be compatible");
 
           var button = doc.documentElement.getButton("next");
           EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
 
           wait_for_page(aWindow, "found", function(aWindow) {
             ok(!doc.getElementById("xpinstallDisabledAlert").hidden,
                "Install should not be allowed");
 
@@ -325,16 +351,19 @@ add_test(function() {
               var button = doc.documentElement.getButton("finish");
               ok(!button.hidden, "Finish button should not be hidden");
               ok(!button.disabled, "Finish button should not be disabled");
 
               wait_for_window_close(aWindow, function() {
                 uninstall_test_addons(run_next_test);
               });
 
+              check_telemetry({disabled: 4, metaenabled: 2, metadisabled: 0,
+                               upgraded: 0, failed: 1, declined: 2});
+
               EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
             });
           });
         });
       });
     });
   });
 });
@@ -473,15 +502,18 @@ add_test(function() {
             var button = doc.documentElement.getButton("finish");
             ok(!button.hidden, "Finish button should not be hidden");
             ok(!button.disabled, "Finish button should not be disabled");
 
             wait_for_window_close(aWindow, function() {
               uninstall_test_addons(run_next_test);
             });
 
+            check_telemetry({disabled: 0, metaenabled: 0, metadisabled: 1,
+                             upgraded: 0, failed: 0, declined: 0});
+
             EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
           });
         });
       });
     });
   });
 });