Bug 655254: When an add-on moves to a different directory but doesn't change in any other way we should keep updated compatibility information. r=robstrong
authorDave Townsend <dtownsend@oxymoronical.com>
Fri, 10 Jun 2011 13:10:48 -0700
changeset 70884 fbeb460473f5adfbee2c749b4f0c0d488a7aa57a
parent 70883 28217403cd022d48ad50df409a8adde2c1b35b76
child 70892 6da2c7c5c170e779421c34670d8cc9606f410e22
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersrobstrong
bugs655254
milestone7.0a1
Bug 655254: When an add-on moves to a different directory but doesn't change in any other way we should keep updated compatibility information. r=robstrong
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/test/addons/test_bug655254/install.rdf
toolkit/mozapps/extensions/test/xpcshell/data/test_bug655254.rdf
toolkit/mozapps/extensions/test/xpcshell/test_bug655254.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -2176,17 +2176,18 @@ var XPIProvider = {
    * @param  aMigrateData
    *         an object generated from a previous version of the database
    *         holding information about what add-ons were previously userDisabled
    *         and updated compatibility information if present
    * @param  aActiveBundles
    *         When performing recovery after startup this will be an array of
    *         persistent descriptors of add-ons that are known to be active,
    *         otherwise it will be null
-   * @return true if a change requiring a restart was detected
+   * @return a boolean indicating if a change requiring flushing the caches was
+   *         detected
    */
   processFileChanges: function XPI_processFileChanges(aState, aManifests,
                                                       aUpdateCompatibility,
                                                       aOldAppVersion,
                                                       aOldPlatformVersion,
                                                       aMigrateData,
                                                       aActiveBundles) {
     let visibleAddons = {};
@@ -2200,17 +2201,17 @@ var XPIProvider = {
      *
      * @param  aInstallLocation
      *         The install location containing the add-on
      * @param  aOldAddon
      *         The AddonInternal as it appeared the last time the application
      *         ran
      * @param  aAddonState
      *         The new state of the add-on
-     * @return true if restarting the application is required to complete
+     * @return a boolean indicating if flushing caches is required to complete
      *         changing this add-on
      */
     function updateMetadata(aInstallLocation, aOldAddon, aAddonState) {
       LOG("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name);
 
       // Check if there is an updated install manifest for this add-on
       let newAddon = aManifests[aInstallLocation.name][aOldAddon.id];
 
@@ -2272,30 +2273,61 @@ var XPIProvider = {
         // Otherwise the caches will need to be invalidated
         return true;
       }
 
       return false;
     }
 
     /**
+     * Updates an add-on's descriptor for when the add-on has moved in the
+     * filesystem but hasn't changed in any other way.
+     *
+     * @param  aInstallLocation
+     *         The install location containing the add-on
+     * @param  aOldAddon
+     *         The AddonInternal as it appeared the last time the application
+     *         ran
+     * @param  aAddonState
+     *         The new state of the add-on
+     * @return a boolean indicating if flushing caches is required to complete
+     *         changing this add-on
+     */
+    function updateDescriptor(aInstallLocation, aOldAddon, aAddonState) {
+      LOG("Add-on " + aOldAddon.id + " moved to " + aAddonState.descriptor);
+
+      aOldAddon._descriptor = aAddonState.descriptor;
+      aOldAddon.visible = !(aOldAddon.id in visibleAddons);
+
+      // Update the database
+      XPIDatabase.setAddonDescriptor(aOldAddon, aAddonState.descriptor);
+      if (aOldAddon.visible) {
+        visibleAddons[aOldAddon.id] = aOldAddon;
+
+        return true;
+      }
+
+      return false;
+    }
+
+    /**
      * Called when no change has been detected for an add-on's metadata. The
      * add-on may have become visible due to other add-ons being removed or
      * the add-on may need to be updated when the application version has
      * changed.
      *
      * @param  aInstallLocation
      *         The install location containing the add-on
      * @param  aOldAddon
      *         The AddonInternal as it appeared the last time the application
      *         ran
      * @param  aAddonState
      *         The new state of the add-on
-     * @return a boolean indicating if restarting the application is required
-     *         to complete changing this add-on
+     * @return a boolean indicating if flushing caches is required to complete
+     *         changing this add-on
      */
     function updateVisibilityAndCompatibility(aInstallLocation, aOldAddon,
                                               aAddonState) {
       let changed = false;
 
       // This add-ons metadata has not changed but it may have become visible
       if (!(aOldAddon.id in visibleAddons)) {
         visibleAddons[aOldAddon.id] = aOldAddon;
@@ -2394,18 +2426,18 @@ var XPIProvider = {
     /**
      * Called when an add-on has been removed.
      *
      * @param  aInstallLocation
      *         The install location containing the add-on
      * @param  aOldAddon
      *         The AddonInternal as it appeared the last time the application
      *         ran
-     * @return a boolean indicating if restarting the application is required
-     *         to complete changing this add-on
+     * @return a boolean indicating if flushing caches is required to complete
+     *         changing this add-on
      */
     function removeMetadata(aInstallLocation, aOldAddon) {
       // This add-on has disappeared
       LOG("Add-on " + aOldAddon.id + " removed from " + aInstallLocation.name);
       XPIDatabase.removeAddonMetadata(aOldAddon);
       if (aOldAddon.active) {
 
         // Enable the default theme if the previously active theme has been
@@ -2428,18 +2460,18 @@ var XPIProvider = {
      *         The install location containing the add-on
      * @param  aId
      *         The ID of the add-on
      * @param  aAddonState
      *         The new state of the add-on
      * @param  aMigrateData
      *         If during startup the database had to be upgraded this will
      *         contain data that used to be held about this add-on
-     * @return a boolean indicating if restarting the application is required
-     *         to complete changing this add-on
+     * @return a boolean indicating if flushing caches is required to complete
+     *         changing this add-on
      */
     function addMetadata(aInstallLocation, aId, aAddonState, aMigrateData) {
       LOG("New add-on " + aId + " installed in " + aInstallLocation.name);
 
       let newAddon = null;
       // Check the updated manifests lists for the install location, If there
       // is no manifest for the add-on ID then newAddon will be undefined
       if (aInstallLocation.name in aManifests)
@@ -2601,27 +2633,29 @@ var XPIProvider = {
             let addonState = addonStates[aOldAddon.id];
             delete addonStates[aOldAddon.id];
 
             // Remember add-ons that were inactive during startup
             if (aOldAddon.visible && !aOldAddon.active)
               XPIProvider.inactiveAddonIDs.push(aOldAddon.id);
 
             // The add-on has changed if the modification time has changed, or
-            // the directory it is installed in has changed or we have an
-            // updated manifest for it. Also reload the metadata for add-ons
-            // in the application directory when the application version has
-            // changed
+            // we have an updated manifest for it. Also reload the metadata for
+            // add-ons in the application directory when the application version
+            // has changed
             if (aOldAddon.id in aManifests[installLocation.name] ||
                 aOldAddon.updateDate != addonState.mtime ||
-                aOldAddon._descriptor != addonState.descriptor ||
                 (aUpdateCompatibility && installLocation.name == KEY_APP_GLOBAL)) {
               changed = updateMetadata(installLocation, aOldAddon, addonState) ||
                         changed;
             }
+            else if (aOldAddon._descriptor != addonState.descriptor) {
+              changed = updateDescriptor(installLocation, aOldAddon, addonState) ||
+                        changed;
+            }
             else {
               changed = updateVisibilityAndCompatibility(installLocation,
                                                          aOldAddon, addonState) ||
                         changed;
             }
             if (aOldAddon.visible && aOldAddon._installLocation.name != KEY_APP_GLOBAL)
               XPIProvider.allAppGlobal = false;
           }
@@ -3877,16 +3911,18 @@ var XPIDatabase = {
     setActiveAddons: "UPDATE addon SET active=MIN(visible, 1 - userDisabled, " +
                      "1 - softDisabled, 1 - appDisabled)",
     setAddonProperties: "UPDATE addon SET userDisabled=:userDisabled, " +
                         "appDisabled=:appDisabled, " +
                         "softDisabled=:softDisabled, " +
                         "pendingUninstall=:pendingUninstall, " +
                         "applyBackgroundUpdates=:applyBackgroundUpdates WHERE " +
                         "internal_id=:internal_id",
+    setAddonDescriptor: "UPDATE addon SET descriptor=:descriptor WHERE " +
+                        "internal_id=:internal_id",
     updateTargetApplications: "UPDATE targetApplication SET " +
                               "minVersion=:minVersion, maxVersion=:maxVersion " +
                               "WHERE addon_internal_id=:internal_id AND id=:id",
 
     createSavepoint: "SAVEPOINT 'default'",
     releaseSavepoint: "RELEASE SAVEPOINT 'default'",
     rollbackSavepoint: "ROLLBACK TO SAVEPOINT 'default'"
   },
@@ -5124,17 +5160,33 @@ var XPIDatabase = {
     else {
       stmt.params.applyBackgroundUpdates = aAddon.applyBackgroundUpdates;
     }
 
     executeStatement(stmt);
   },
 
   /**
-   * Synchronously pdates an add-on's active flag in the database.
+   * Synchronously sets the file descriptor for an add-on.
+   *
+   * @param  aAddon
+   *         The DBAddonInternal being updated
+   * @param  aProperties
+   *         A dictionary of properties to set
+   */
+  setAddonDescriptor: function XPIDB_setAddonDescriptor(aAddon, aDescriptor) {
+    let stmt = this.getStatement("setAddonDescriptor");
+    stmt.params.internal_id = aAddon._internal_id;
+    stmt.params.descriptor = aDescriptor;
+
+    executeStatement(stmt);
+  },
+
+  /**
+   * Synchronously updates an add-on's active flag in the database.
    *
    * @param  aAddon
    *         The DBAddonInternal to update
    */
   updateAddonActive: function XPIDB_updateAddonActive(aAddon) {
     LOG("Updating add-on state");
 
     let stmt = this.getStatement("updateAddonActive");
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_bug655254/install.rdf
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon1@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+    <em:name>Test 1</em:name>
+    <em:updateURL>http://localhost:4444/data/test_bug655254.rdf</em:updateURL>
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>1</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug655254.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <RDF:Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+    <em:updates>
+      <RDF:Seq>
+        <RDF:li>
+          <RDF:Description>
+            <em:version>1</em:version>
+            <em:targetApplication>
+              <RDF:Description>
+                <em:id>xpcshell@tests.mozilla.org</em:id>
+                <em:minVersion>1</em:minVersion>
+                <em:maxVersion>2</em:maxVersion>
+              </RDF:Description>
+            </em:targetApplication>
+          </RDF:Description>
+        </RDF:li>
+      </RDF:Seq>
+    </em:updates>
+  </RDF:Description>
+
+</RDF:RDF>
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug655254.js
@@ -0,0 +1,101 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that moving an extension in the filesystem without any other
+// change still keeps updated compatibility information
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+                          AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9.2");
+
+do_load_httpd_js();
+var testserver;
+
+var userDir = gProfD.clone();
+userDir.append("extensions2");
+userDir.append(gAppInfo.ID);
+
+var dirProvider = {
+  getFile: function(aProp, aPersistent) {
+    aPersistent.value = false;
+    if (aProp == "XREUSysExt")
+      return userDir.parent;
+    return null;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIDirectoryServiceProvider,
+                                         AM_Ci.nsISupports])
+};
+Services.dirsvc.registerProvider(dirProvider);
+
+var addon1 = {
+  id: "addon1@tests.mozilla.org",
+  version: "1.0",
+  name: "Test 1",
+  updateURL: "http://localhost:4444/data/test_bug655254.rdf",
+  targetApplications: [{
+    id: "xpcshell@tests.mozilla.org",
+    minVersion: "1",
+    maxVersion: "1"
+  }]
+};
+
+// Set up the profile
+function run_test() {
+  do_test_pending();
+
+  // Create and configure the HTTP server.
+  testserver = new nsHttpServer();
+  testserver.registerDirectory("/data/", do_get_file("data"));
+  testserver.start(4444);
+
+  var time = Date.now();
+  var dir = writeInstallRDFForExtension(addon1, userDir);
+  setExtensionModifiedTime(dir, time);
+
+  startupManager();
+
+  AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+    do_check_neq(a1, null);
+    do_check_true(a1.appDisabled);
+    do_check_false(a1.isActive);
+    do_check_false(isExtensionInAddonsList(userDir, a1.id));
+
+    a1.findUpdates({
+      onUpdateFinished: function() {
+        restartManager();
+
+        AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+          do_check_neq(a1, null);
+          do_check_false(a1.appDisabled);
+          do_check_true(a1.isActive);
+          do_check_true(isExtensionInAddonsList(userDir, a1.id));
+
+          shutdownManager();
+
+          userDir.parent.moveTo(gProfD, "extensions3");
+          userDir = gProfD.clone();
+          userDir.append("extensions3");
+          userDir.append(gAppInfo.ID);
+          do_check_true(userDir.exists());
+
+          startupManager(false);
+
+          AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+            do_check_neq(a1, null);
+            do_check_false(a1.appDisabled);
+            do_check_true(a1.isActive);
+            do_check_true(isExtensionInAddonsList(userDir, a1.id));
+
+            testserver.stop(do_test_finished);
+          });
+        });
+      }
+    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -49,16 +49,17 @@ tail =
 [test_bug587088.js]
 [test_bug594058.js]
 [test_bug595081.js]
 [test_bug595573.js]
 [test_bug596607.js]
 [test_bug616841.js]
 [test_bug619730.js]
 [test_bug620837.js]
+[test_bug655254.js]
 [test_cacheflush.js]
 [test_checkcompatibility.js]
 [test_corrupt.js]
 [test_disable.js]
 [test_distribution.js]
 [test_dss.js]
 [test_duplicateplugins.js]
 [test_error.js]