Bug 1010449: Add telemetry for add-on compatibility changes, upgrades during startup time add-on update check; r=unfocused
authorIrving Reid <irving@mozilla.com>
Tue, 20 May 2014 13:15:24 -0400
changeset 184087 87b5205bab4fbfaf39ee24af0b162b59e06b232a
parent 184086 a05f2483f74b61e9abb683d72edada3831291a6a
child 184088 60de42b54f914c88aa60dfdcae116acfe9dda9fe
push id26811
push usercbook@mozilla.com
push dateWed, 21 May 2014 11:58:32 +0000
treeherdermozilla-central@97943ef127f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersunfocused
bugs1010449
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1010449: Add telemetry for add-on compatibility changes, upgrades during startup time add-on update check; r=unfocused
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);
           });
         });
       });
     });
   });
 });