Bug 562930: Extensions containing JS components break on every update/reinstall. r=robstrong
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 27 May 2010 15:09:26 -0700
changeset 42884 20d24f34a512a10d68e5119784bc7734e2716764
parent 42883 9600ed035ee0f470420c9f3bc90ae84c7d1a3d38
child 42885 317fcd674ee701ff14a3c4ddc6faf57f1e252a1a
push idunknown
push userunknown
push dateunknown
reviewersrobstrong
bugs562930
milestone1.9.3a5pre
Bug 562930: Extensions containing JS components break on every update/reinstall. r=robstrong
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_update.js
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -945,18 +945,27 @@ var XPIProvider = {
 
     Services.prefs.removeObserver(this.checkCompatibilityPref, this);
     Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
 
     this.bootstrappedAddons = {};
     this.bootstrapScopes = {};
     this.enabledAddons = null;
 
-    if (Prefs.getBoolPref(PREF_PENDING_OPERATIONS, false)) {
+    // Get the list of IDs of add-ons that are pending update.
+    let updates = [i.addon.id for each (i in this.installs)
+                   if ((i.state == AddonManager.STATE_INSTALLED) &&
+                       i.existingAddon)];
+
+    // If there are pending operations or installs waiting to complete then
+    // we must update the list of active add-ons
+    if (Prefs.getBoolPref(PREF_PENDING_OPERATIONS, false) ||
+        updates.length > 0) {
       XPIDatabase.updateActiveAddons();
+      XPIDatabase.writeAddonsList(updates);
       Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
     }
     XPIDatabase.shutdown();
     this.installs = null;
 
     this.installLocations = null;
     this.installLocationsByName = null;
   },
@@ -1583,30 +1592,31 @@ var XPIProvider = {
       }
     }
 
     // If the application crashed before completing any pending operations then
     // we should perform them now.
     if (changed || Prefs.getBoolPref(PREF_PENDING_OPERATIONS)) {
       LOG("Restart necessary");
       XPIDatabase.updateActiveAddons();
+      XPIDatabase.writeAddonsList([]);
       Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
       Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS,
                                  JSON.stringify(this.bootstrappedAddons));
       return true;
     }
 
     LOG("No changes found");
 
     // Check that the add-ons list still exists
     let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
                                        true);
     if (!addonsList.exists()) {
       LOG("Add-ons list is missing, recreating");
-      XPIDatabase.writeAddonsList();
+      XPIDatabase.writeAddonsList([]);
       Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS,
                                  JSON.stringify(this.bootstrappedAddons));
       return true;
     }
 
     for (let id in this.bootstrappedAddons) {
       let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
       dir.persistentDescriptor = this.bootstrappedAddons[id].descriptor;
@@ -3261,47 +3271,55 @@ var XPIDatabase = {
 
   /**
    * Synchronously calculates and updates all the active flags in the database.
    */
   updateActiveAddons: function XPIDB_updateActiveAddons() {
     LOG("Updating add-on states");
     let stmt = this.getStatement("setActiveAddons");
     stmt.execute();
-
-    this.writeAddonsList();
   },
 
   /**
    * Writes out the XPI add-ons list for the platform to read.
+   *
+   * @param  aPendingUpdateIDs
+   *         An array of IDs of add-ons that are pending update and so shouldn't
+   *         be included in the add-ons list.
    */
-  writeAddonsList: function XPIDB_writeAddonsList() {
+  writeAddonsList: function XPIDB_writeAddonsList(aPendingUpdateIDs) {
     LOG("Writing add-ons list");
     Services.appinfo.invalidateCachesOnRestart();
     let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
                                        true);
 
     let enabledAddons = [];
     let text = "[ExtensionDirs]\r\n";
     let count = 0;
 
     let stmt = this.getStatement("getActiveAddons");
 
     for (let row in resultRows(stmt)) {
+      // Don't include add-ons that are waiting to be updated
+      if (aPendingUpdateIDs.indexOf(row.id) != -1)
+        continue;
       text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
       enabledAddons.push(row.id + ":" + row.version);
     }
 
     // The selected skin may come from an inactive theme (the default theme
     // when a lightweight theme is applied for example)
     text += "\r\n[ThemeDirs]\r\n";
     stmt = this.getStatement("getActiveTheme");
     stmt.params.internalName = XPIProvider.selectedSkin;
     count = 0;
     for (let row in resultRows(stmt)) {
+      // Don't include add-ons that are waiting to be updated
+      if (aPendingUpdateIDs.indexOf(row.id) != -1)
+        continue;
       text += "Extension" + (count++) + "=" + row.descriptor + "\r\n";
       enabledAddons.push(row.id + ":" + row.version);
     }
 
     var fos = FileUtils.openSafeFileOutputStream(addonsList);
     fos.write(text, text.length);
     FileUtils.closeSafeFileOutputStream(fos);
 
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -194,16 +194,19 @@ function shutdownManager() {
     return;
 
   let obs = AM_Cc["@mozilla.org/observer-service;1"].
             getService(AM_Ci.nsIObserverService);
   obs.notifyObservers(null, "quit-application-granted", null);
   gInternalManager.observe(null, "xpcom-shutdown", null);
   gInternalManager = null;
 
+  // Load the add-ons list as it was after application shutdown
+  loadAddonsList(false);
+
   // Clear any crash report annotations
   gAppInfo.annotations = {};
 }
 
 function loadAddonsList(aAppChanged) {
   function readDirectories(aSection) {
     var dirs = [];
     var keys = parser.getKeys(aSection);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -139,17 +139,25 @@ function run_test_2() {
 }
 
 function check_test_2() {
   ensure_test_completed();
 
   AddonManager.getAddonByID("addon1@tests.mozilla.org", function(olda1) {
     do_check_neq(olda1, null);
     do_check_eq(olda1.version, "1.0");
-    restartManager(1);
+    do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
+
+    shutdownManager();
+
+    do_check_false(isExtensionInAddonsList(profileDir, olda1.id));
+
+    startupManager(1);
+
+    do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
 
     AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
       do_check_neq(a1, null);
       do_check_eq(a1.version, "2.0");
       do_check_true(isExtensionInAddonsList(profileDir, a1.id));
       do_check_false(a1.applyBackgroundUpdates);
       a1.uninstall();
       restartManager(0);