Bug 1461069: Remove obsolete state preference migration code. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Thu, 17 May 2018 12:28:57 -0700
changeset 796598 ac89bc1c1e172a34e4768167c02fda50a4a3e4d6
parent 796597 b2873211bf691fd3eae2a926e4c699aa37caccec
child 796604 0e2f65cf66723a6c063a2be606b8ee7b6bfe88a1
push id110301
push usermaglione.k@gmail.com
push dateThu, 17 May 2018 21:37:40 +0000
reviewersaswan
bugs1461069
milestone62.0a1
Bug 1461069: Remove obsolete state preference migration code. r?aswan MozReview-Commit-ID: 6B8JLjJqFlg
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/test_migrate_state_prefs.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -42,29 +42,27 @@ const {nsIBlocklistService} = Ci;
 
 // These are injected from XPIProvider.jsm
 /* globals
  *         BOOTSTRAP_REASONS,
  *         DB_SCHEMA,
  *         SIGNED_TYPES,
  *         XPIProvider,
  *         XPIStates,
- *         descriptorToPath,
  *         isTheme,
  *         isWebExtension,
  *         recordAddonTelemetry,
  */
 
 for (let sym of [
   "BOOTSTRAP_REASONS",
   "DB_SCHEMA",
   "SIGNED_TYPES",
   "XPIProvider",
   "XPIStates",
-  "descriptorToPath",
   "isTheme",
   "isWebExtension",
   "recordAddonTelemetry",
 ]) {
   XPCOMUtils.defineLazyGetter(this, sym, () => XPIInternal[sym]);
 }
 
 ChromeUtils.import("resource://gre/modules/Log.jsm");
@@ -259,20 +257,16 @@ class AddonInternal {
      *   An array of bootstrapped add-on IDs on which this add-on depends.
      *   The add-on will remain appDisabled if any of the dependent
      *   add-ons is not installed and enabled.
      */
     this.dependencies = EMPTY_ARRAY;
     this.hasEmbeddedWebExtension = false;
 
     if (addonData) {
-      if (addonData.descriptor && !addonData.path) {
-        addonData.path = descriptorToPath(addonData.descriptor);
-      }
-
       copyProperties(addonData, PROP_JSON_FIELDS, this);
 
       if (!this.dependencies)
         this.dependencies = [];
       Object.freeze(this.dependencies);
 
       this.addedToDatabase();
 
@@ -1423,19 +1417,16 @@ this.XPIDatabase = {
 
       let forEach = this.syncLoadingDB ? arrayForEach : idleForEach;
 
       // If we got here, we probably have good data
       // Make AddonInternal instances from the loaded data and save them
       let addonDB = new Map();
       await forEach(inputAddons.addons, loadedAddon => {
         try {
-          if (!loadedAddon.path) {
-            loadedAddon.path = descriptorToPath(loadedAddon.descriptor);
-          }
           loadedAddon._sourceBundle = new nsIFile(loadedAddon.path);
         } catch (e) {
           // We can fail here when the path is invalid, usually from the
           // wrong OS
           logger.warn("Could not find source bundle for add-on " + loadedAddon.id, e);
         }
 
         let newAddon = new AddonInternal(loadedAddon);
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -51,18 +51,16 @@ XPCOMUtils.defineLazyServiceGetters(this
 });
 
 const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
                                        "initWithPath");
 const FileInputStream = Components.Constructor("@mozilla.org/network/file-input-stream;1",
                                                "nsIFileInputStream", "init");
 
 const PREF_DB_SCHEMA                  = "extensions.databaseSchema";
-const PREF_XPI_STATE                  = "extensions.xpiState";
-const PREF_BOOTSTRAP_ADDONS           = "extensions.bootstrappedAddons";
 const PREF_PENDING_OPERATIONS         = "extensions.pendingOperations";
 const PREF_EM_ENABLED_SCOPES          = "extensions.enabledScopes";
 const PREF_EM_STARTUP_SCAN_SCOPES     = "extensions.startupScanScopes";
 // xpinstall.signatures.required only supported in dev builds
 const PREF_XPI_SIGNATURES_REQUIRED    = "xpinstall.signatures.required";
 const PREF_LANGPACK_SIGNATURES        = "extensions.langpacks.signatures.required";
 const PREF_XPI_PERMISSIONS_BRANCH     = "xpinstall.";
 const PREF_INSTALL_DISTRO_ADDONS      = "extensions.installDistroAddons";
@@ -70,35 +68,27 @@ const PREF_BRANCH_INSTALLED_ADDON     = 
 const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
 const PREF_ALLOW_LEGACY               = "extensions.legacy.enabled";
 
 const PREF_EM_LAST_APP_BUILD_ID       = "extensions.lastAppBuildId";
 
 // Specify a list of valid built-in add-ons to load.
 const BUILT_IN_ADDONS_URI             = "chrome://browser/content/built_in_addons.json";
 
-const OBSOLETE_PREFERENCES = [
-  "extensions.bootstrappedAddons",
-  "extensions.enabledAddons",
-  "extensions.xpiState",
-  "extensions.installCache",
-];
-
 const URI_EXTENSION_STRINGS           = "chrome://mozapps/locale/extensions/extensions.properties";
 
 const DIR_EXTENSIONS                  = "extensions";
 const DIR_SYSTEM_ADDONS               = "features";
 const DIR_STAGE                       = "staged";
 const DIR_TRASH                       = "trash";
 
 const FILE_XPI_STATES                 = "addonStartup.json.lz4";
 const FILE_DATABASE                   = "extensions.json";
 const FILE_RDF_MANIFEST               = "install.rdf";
 const FILE_WEB_MANIFEST               = "manifest.json";
-const FILE_XPI_ADDONS_LIST            = "extensions.ini";
 
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_ADDON_APP_DIR               = "XREAddonAppDir";
 const KEY_APP_DISTRIBUTION            = "XREAppDist";
 const KEY_APP_FEATURES                = "XREAppFeat";
 
 const KEY_APP_PROFILE                 = "app-profile";
 const KEY_APP_SYSTEM_ADDONS           = "app-system-addons";
@@ -280,58 +270,16 @@ function tryGetMtime(file) {
     // than whatever value it may have cached.
     return file.clone().lastModifiedTime;
   } catch (e) {
     return 0;
   }
 }
 
 /**
- * Returns the path to `file` relative to the directory `dir`, or an
- * absolute path to the file if no directory is passed, or the file is
- * not a descendant of it.
- *
- * @param {nsIFile} file
- *        The file to return a relative path to.
- * @param {nsIFile} [dir]
- *        If passed, return a path relative to this directory.
- * @returns {string}
- */
-function getRelativePath(file, dir) {
-  if (dir && dir.contains(file)) {
-    let path = file.getRelativePath(dir);
-    if (AppConstants.platform == "win") {
-      path = path.replace(/\//g, "\\");
-    }
-    return path;
-  }
-  return file.path;
-}
-
-/**
- * Converts the given opaque descriptor string into an ordinary path string.
- *
- * @param {string} descriptor
- *        The opaque descriptor string to convert.
- * @param {nsIFile} [dir]
- *        If passed, return a path relative to this directory.
- * @returns {string}
- *        The file's path.
- */
-function descriptorToPath(descriptor, dir) {
-  try {
-    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
-    file.persistentDescriptor = descriptor;
-    return getRelativePath(file, dir);
-  } catch (e) {
-    return null;
-  }
-}
-
-/**
  * Helper function that determines whether an addon of a certain type is a
  * WebExtension.
  *
  * @param {string} type
  *        The add-on type to check.
  * @returns {boolean}
  */
 function isWebExtension(type) {
@@ -518,24 +466,16 @@ const JSON_FIELDS = Object.freeze([
   "path",
   "runInSafeMode",
   "startupData",
   "telemetryKey",
   "type",
   "version",
 ]);
 
-const BOOTSTRAPPED_FIELDS = Object.freeze([
-  "dependencies",
-  "hasEmbeddedWebExtension",
-  "runInSafeMode",
-  "type",
-  "version",
-]);
-
 class XPIState {
   constructor(location, id, saved = {}) {
     this.location = location;
     this.id = id;
 
     // Set default values.
     this.type = "extension";
 
@@ -550,53 +490,16 @@ class XPIState {
     }
 
     if (saved.currentModifiedTime && saved.currentModifiedTime != this.lastModifiedTime) {
       this.lastModifiedTime = saved.currentModifiedTime;
       this.changed = true;
     }
   }
 
-  /**
-   * Migrates an add-on's data from xpiState and bootstrappedAddons
-   * preferences, and returns an XPIState object for it.
-   *
-   * @param {XPIStateLocation} location
-   *        The location of the add-on.
-   * @param {string} id
-   *        The ID of the add-on to migrate.
-   * @param {object} saved
-   *        The add-on's data from the xpiState preference.
-   * @param {object} [bootstrapped]
-   *        The add-on's data from the bootstrappedAddons preference, if
-   *        applicable.
-   * @returns {XPIState}
-   */
-  static migrate(location, id, saved, bootstrapped) {
-    let data = {
-      enabled: saved.e,
-      path: descriptorToPath(saved.d, location.dir),
-      lastModifiedTime: saved.mt || saved.st,
-      version: saved.v,
-    };
-
-    if (bootstrapped) {
-      data.enabled = true;
-      data.path = descriptorToPath(bootstrapped.descriptor, location.dir);
-
-      for (let field of BOOTSTRAPPED_FIELDS) {
-        if (field in bootstrapped) {
-          data[field] = bootstrapped[field];
-        }
-      }
-    }
-
-    return new XPIState(location, id, data);
-  }
-
   // Compatibility shim getters for legacy callers in XPIDatabase.jsm.
   get mtime() {
     return this.lastModifiedTime;
   }
   get active() {
     return this.enabled;
   }
 
@@ -917,33 +820,16 @@ class XPIStateLocation extends Map {
 
   * getStagedAddons() {
     for (let [id, metadata] of Object.entries(this.staged)) {
       yield [id, metadata];
     }
   }
 
   /**
-   * Migrates saved state data for the given add-on from the values
-   * stored in xpiState and bootstrappedAddons preferences, and adds it to
-   * the DB.
-   *
-   * @param {string} id
-   *        The ID of the add-on to migrate.
-   * @param {object} state
-   *        The add-on's data from the xpiState preference.
-   * @param {object} [bootstrapped]
-   *        The add-on's data from the bootstrappedAddons preference, if
-   *        applicable.
-   */
-  migrateAddon(id, state, bootstrapped) {
-    this.set(id, XPIState.migrate(this, id, state, bootstrapped));
-  }
-
-  /**
    * Returns true if the given addon was installed in this location by a text
    * file pointing to its real path.
    *
    * @param {string} aId
    *        The ID of the addon
    * @returns {boolean}
    */
   isLinkedAddon(aId) {
@@ -1401,87 +1287,29 @@ var XPIStates = {
     let count = 0;
     for (let location of this.locations()) {
       count += location.size;
     }
     return count;
   },
 
   /**
-   * Migrates state data from the xpiState and bootstrappedAddons
-   * preferences and adds it to the DB. Returns a JSON-compatible
-   * representation of the current state of the DB.
-   *
-   * @returns {object}
-   */
-  migrateStateFromPrefs() {
-    logger.info("No addonStartup.json found. Attempting to migrate data from preferences");
-
-    let state;
-    // Try to migrate state data from old storage locations.
-    let bootstrappedAddons;
-    try {
-      state = JSON.parse(Services.prefs.getStringPref(PREF_XPI_STATE));
-      bootstrappedAddons = JSON.parse(Services.prefs.getStringPref(PREF_BOOTSTRAP_ADDONS, "{}"));
-    } catch (e) {
-      logger.warn("Error parsing extensions.xpiState and " +
-                  "extensions.bootstrappedAddons: ${error}",
-                  {error: e});
-
-    }
-
-    for (let [locName, addons] of Object.entries(state)) {
-      for (let [id, addon] of Object.entries(addons)) {
-        let loc = this.getLocation(locName);
-        if (loc) {
-          loc.migrateAddon(id, addon, bootstrappedAddons[id] || null);
-        }
-      }
-    }
-
-    // Clear out old state data.
-    for (let pref of OBSOLETE_PREFERENCES) {
-      Services.prefs.clearUserPref(pref);
-    }
-    OS.File.remove(OS.Path.join(OS.Constants.Path.profileDir,
-                                FILE_XPI_ADDONS_LIST));
-
-    // Serialize and deserialize so we get the expected JSON data.
-    let data = JSON.parse(JSON.stringify(this));
-
-    logger.debug("Migrated data: ${}", data);
-
-    return data;
-  },
-
-  /**
-   * Load extension state data from addonStartup.json, or migrates it
-   * from legacy state preferences, if they exist.
+   * Load extension state data from addonStartup.json.
    *
    * @returns {Object}
    */
   loadExtensionState() {
     let state;
     try {
       state = aomStartup.readStartupData();
     } catch (e) {
       logger.warn("Error parsing extensions state: ${error}",
                   {error: e});
     }
 
-    if (!state && Services.prefs.getPrefType(PREF_XPI_STATE) != Ci.nsIPrefBranch.PREF_INVALID) {
-      try {
-        state = this.migrateStateFromPrefs();
-      } catch (e) {
-        logger.warn("Error migrating extensions.xpiState and " +
-                    "extensions.bootstrappedAddons: ${error}",
-                    {error: e});
-      }
-    }
-
     logger.debug("Loaded add-on state: ${}", state);
     return state || {};
   },
 
   /**
    * Walk through all install locations, highest priority first,
    * comparing the on-disk state of extensions to what is stored in prefs.
    *
@@ -3180,17 +3008,16 @@ var XPIInternal = {
   TEMPORARY_ADDON_SUFFIX,
   TOOLKIT_ID,
   TemporaryInstallLocation,
   XPIProvider,
   XPIStates,
   XPI_PERMISSION,
   awaitPromise,
   canRunInSafeMode,
-  descriptorToPath,
   getExternalType,
   getURIForResourceInFile,
   isTheme,
   isWebExtension,
 };
 
 var addonTypes = [
   new AddonManagerPrivate.AddonType("extension", URI_EXTENSION_STRINGS,
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate_state_prefs.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-"use strict";
-
-/* globals Preferences */
-ChromeUtils.import("resource://gre/modules/Preferences.jsm");
-
-function getXS() {
-  let XPI = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
-  return XPI.XPIStates;
-}
-
-function installExtension(id, data) {
-  return AddonTestUtils.promiseWriteFilesToExtension(
-    AddonTestUtils.profileExtensions.path, id, data);
-}
-
-add_task(async function test_migrate_prefs() {
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "54");
-
-  ok(!AddonTestUtils.addonStartup.exists(),
-     "addonStartup.json.lz4 should not exist");
-
-  const ID1 = "bootstrapped-enabled@xpcshell.mozilla.org";
-  const ID2 = "bootstrapped-disabled@xpcshell.mozilla.org";
-
-  let targetApplications = [{ id: "toolkit@mozilla.org", "minVersion": "0", "maxVersion": "*" }];
-
-  let file1 = await installExtension(ID1, { "install.rdf": { id: ID1, name: ID1, bootstrap: true, version: "0.1", targetApplications } });
-  let file2 = await installExtension(ID2, { "install.rdf": { id: ID2, name: ID2, bootstrap: true, version: "0.2", targetApplications } });
-
-  function mt(file) {
-    let f = file.clone();
-    if (TEST_UNPACKED) {
-      f.append("install.rdf");
-    }
-    return f.lastModifiedTime;
-  }
-
-  // Startup and shut down the add-on manager so the add-ons are added
-  // to the DB.
-  await promiseStartupManager();
-  await promiseShutdownManager();
-
-  // Remove the startup state file and add legacy prefs to replace it.
-  AddonTestUtils.addonStartup.remove(false);
-
-  Preferences.set("extensions.xpiState", JSON.stringify({
-    "app-profile": {
-      [ID1]: {e: true, d: file1.persistentDescriptor, v: "0.1", mt: mt(file1)},
-      [ID2]: {e: false, d: file2.persistentDescriptor, v: "0.2", mt: mt(file2)},
-    }
-  }));
-
-  Preferences.set("extensions.bootstrappedAddons", JSON.stringify({
-    [ID1]: {
-      version: "0.1",
-      type: "extension",
-      descriptor: file1.persistentDescriptor,
-      hasEmbeddedWebExtension: true,
-    }
-  }));
-
-  await promiseStartupManager();
-
-  // Check the the state data is updated correctly.
-  let states = getXS();
-
-  let addon1 = states.findAddon(ID1);
-  ok(addon1.enabled, "Addon 1 should be enabled");
-  equal(addon1.version, "0.1", "Addon 1 has the correct version");
-  equal(addon1.mtime, mt(file1), "Addon 1 has the correct timestamp");
-  ok(addon1.hasEmbeddedWebExtension, "Addon 1 has an embedded WebExtension");
-
-  let addon2 = states.findAddon(ID2);
-  ok(!addon2.enabled, "Addon 2 should not be enabled");
-  equal(addon2.version, "0.2", "Addon 2 has the correct version");
-  equal(addon2.mtime, mt(file2), "Addon 2 has the correct timestamp");
-  ok(!addon2.hasEmbeddedWebExtension, "Addon 2 no embedded WebExtension");
-
-  // Check that legacy prefs and files have been removed.
-  ok(!Preferences.has("extensions.xpiState"), "No xpiState pref left behind");
-  ok(!Preferences.has("extensions.bootstrappedAddons"), "No bootstrappedAddons pref left behind");
-  ok(!Preferences.has("extensions.enabledAddons"), "No enabledAddons pref left behind");
-
-  let file = AddonTestUtils.profileDir.clone();
-  file.append("extensions.ini");
-  ok(!file.exists(), "No extensions.ini file left behind");
-
-  await promiseShutdownManager();
-});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -149,17 +149,16 @@ skip-if = os == "android"
 [test_json_updatecheck.js]
 [test_legacy.js]
 skip-if = !allow_legacy_extensions || appname == "thunderbird"
 [test_locale.js]
 [test_manifest.js]
 [test_manifest_locales.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
-[test_migrate_state_prefs.js]
 [test_moved_extension_metadata.js]
 [test_no_addons.js]
 [test_nodisable_hidden.js]
 [test_onPropertyChanged_appDisabled.js]
 [test_overrideblocklist.js]
 run-sequentially = Uses global XCurProcD dir.
 tags = blocklist
 [test_pass_symbol.js]