Bug 944006: Don't load XPIProviderUtils if no addons and no schemaVersion; r=unfocused
authorIrving Reid <irving@mozilla.com>
Mon, 02 Dec 2013 19:46:41 -0500
changeset 168026 bf901d895201c9316140dfe3d53b5402ab97237d
parent 168025 a7c9340f5a340fc34323148d01559acceae41bcf
child 168027 cea439ce0f0d7add541505e002ec11009a9d3e8e
push id4703
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 20:24:19 +0000
treeherdermozilla-aurora@20af7fbd96c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersunfocused
bugs944006
milestone28.0a1
Bug 944006: Don't load XPIProviderUtils if no addons and no schemaVersion; r=unfocused
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/XPIProviderUtils.js
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js
toolkit/mozapps/extensions/test/xpcshell/test_no_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_startup.js
toolkit/mozapps/extensions/test/xpcshell/test_upgrade.js
toolkit/mozapps/extensions/test/xpcshell/test_upgrade_strictcompat.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -1722,17 +1722,18 @@ var XPIProvider = {
   // The value of the minCompatiblePlatformVersion preference
   minCompatiblePlatformVersion: null,
   // A dictionary of the file descriptors for bootstrappable add-ons by ID
   bootstrappedAddons: {},
   // A dictionary of JS scopes of loaded bootstrappable add-ons by ID
   bootstrapScopes: {},
   // True if the platform could have activated extensions
   extensionsActive: false,
-
+  // File / directory state of installed add-ons
+  installStates: [],
   // True if all of the add-ons found during startup were installed in the
   // application install location
   allAppGlobal: true,
   // A string listing the enabled add-ons for annotating crash reports
   enabledAddons: null,
   // An array of add-on IDs of add-ons that were inactive during startup
   inactiveAddonIDs: [],
   // Keep track of startup phases for telemetry
@@ -3366,17 +3367,18 @@ var XPIProvider = {
     for (let location of knownLocations) {
       let addons = XPIDatabase.getAddonsInLocation(location);
       for (let aOldAddon of addons) {
         changed = removeMetadata(aOldAddon) || changed;
       }
     }
 
     // Cache the new install location states
-    let cache = JSON.stringify(this.getInstallLocationStates());
+    this.installStates = this.getInstallLocationStates();
+    let cache = JSON.stringify(this.installStates);
     Services.prefs.setCharPref(PREF_INSTALL_CACHE, cache);
     this.persistBootstrappedAddons();
 
     // Clear out any cached migration data.
     XPIDatabase.migrateData = null;
 
     return changed;
   },
@@ -3440,63 +3442,73 @@ var XPIProvider = {
     // This will be true if the previous session made changes that affect the
     // active state of add-ons but didn't commit them properly (normally due
     // to the application crashing)
     let hasPendingChanges = Prefs.getBoolPref(PREF_PENDING_OPERATIONS);
     if (hasPendingChanges) {
       updateReasons.push("hasPendingChanges");
     }
 
-    // If the schema appears to have changed then we should update the database
-    if (DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0)) {
-      updateReasons.push("schemaChanged");
-    }
-
     // If the application has changed then check for new distribution add-ons
     if (aAppChanged !== false &&
         Prefs.getBoolPref(PREF_INSTALL_DISTRO_ADDONS, true))
     {
       updated = this.installDistributionAddons(manifests);
       if (updated) {
         updateReasons.push("installDistributionAddons");
       }
     }
 
     // Telemetry probe added around getInstallLocationStates() to check perf
     let telemetryCaptureTime = Date.now();
-    let state = this.getInstallLocationStates();
+    this.installStates = this.getInstallLocationStates();
     let telemetry = Services.telemetry;
     telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(Date.now() - telemetryCaptureTime);
 
     // If the install directory state has changed then we must update the database
-    let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null);
+    let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, "[]");
     // For a little while, gather telemetry on whether the deep comparison
     // makes a difference
-    if (cache != JSON.stringify(state)) {
-      if (directoryStateDiffers(state, cache)) {
+    let newState = JSON.stringify(this.installStates);
+    if (cache != newState) {
+      LOG("Directory state JSON differs: cache " + cache + " state " + newState);
+      if (directoryStateDiffers(this.installStates, cache)) {
         updateReasons.push("directoryState");
       }
       else {
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_state_badCompare", 1);
       }
     }
 
+    // If the schema appears to have changed then we should update the database
+    if (DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0)) {
+      // If we don't have any add-ons, just update the pref, since we don't need to
+      // write the database
+      if (this.installStates.length == 0) {
+        LOG("Empty XPI database, setting schema version preference to " + DB_SCHEMA);
+        Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA);
+      }
+      else {
+        updateReasons.push("schemaChanged");
+      }
+    }
+
     // If the database doesn't exist and there are add-ons installed then we
     // must update the database however if there are no add-ons then there is
     // no need to update the database.
     let dbFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
-    if (!dbFile.exists() && state.length > 0) {
+    if (!dbFile.exists() && this.installStates.length > 0) {
       updateReasons.push("needNewDatabase");
     }
 
     if (updateReasons.length == 0) {
       let bootstrapDescriptors = [this.bootstrappedAddons[b].descriptor
                                   for (b in this.bootstrappedAddons)];
 
-      state.forEach(function(aInstallLocationState) {
+      this.installStates.forEach(function(aInstallLocationState) {
         for (let id in aInstallLocationState.addons) {
           let pos = bootstrapDescriptors.indexOf(aInstallLocationState.addons[id].descriptor);
           if (pos != -1)
             bootstrapDescriptors.splice(pos, 1);
         }
       });
 
       if (bootstrapDescriptors.length > 0) {
@@ -3509,17 +3521,17 @@ var XPIProvider = {
     try {
       let extensionListChanged = false;
       // If the database needs to be updated then open it and then update it
       // from the filesystem
       if (updateReasons.length > 0) {
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_load_reasons", updateReasons);
         XPIDatabase.syncLoadDB(false);
         try {
-          extensionListChanged = this.processFileChanges(state, manifests,
+          extensionListChanged = this.processFileChanges(this.installStates, manifests,
                                                          aAppChanged,
                                                          aOldAppVersion,
                                                          aOldPlatformVersion);
         }
         catch (e) {
           ERROR("Failed to process extension changes at startup", e);
         }
       }
@@ -3561,17 +3573,17 @@ var XPIProvider = {
     }
     catch (e) {
       ERROR("Error during startup file checks", e);
     }
 
     // Check that the add-ons list still exists
     let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
                                        true);
-    if (addonsList.exists() == (state.length == 0)) {
+    if (addonsList.exists() == (this.installStates.length == 0)) {
       LOG("Add-ons list is invalid, rebuilding");
       XPIDatabase.writeAddonsList();
     }
 
     return false;
   },
 
   /**
--- a/toolkit/mozapps/extensions/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/XPIProviderUtils.js
@@ -611,16 +611,19 @@ this.XPIDatabase = {
       if (inputAddons.schemaVersion != DB_SCHEMA) {
         // Handle mismatched JSON schema version. For now, we assume
         // compatibility for JSON data, though we throw away any fields we
         // don't know about (bug 902956)
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError",
                                                 "schemaMismatch-" + inputAddons.schemaVersion);
         LOG("JSON schema mismatch: expected " + DB_SCHEMA +
             ", actual " + inputAddons.schemaVersion);
+        // When we rev the schema of the JSON database, we need to make sure we
+        // force the DB to save so that the DB_SCHEMA value in the JSON file and
+        // the preference are updated.
       }
       // If we got here, we probably have good data
       // Make AddonInternal instances from the loaded data and save them
       let addonDB = new Map();
       for (let loadedAddon of inputAddons.addons) {
         let newAddon = new DBAddonInternal(loadedAddon);
         addonDB.set(newAddon._key, newAddon);
       };
@@ -651,16 +654,17 @@ this.XPIDatabase = {
    * Upgrade database from earlier (sqlite or RDF) version if available
    */
   upgradeDB: function(aRebuildOnError) {
     let upgradeTimer = AddonManagerPrivate.simpleTimer("XPIDB_upgradeDB_MS");
     try {
       let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA);
       if (schemaVersion <= LAST_SQLITE_DB_SCHEMA) {
         // we should have an older SQLITE database
+        LOG("Attempting to upgrade from SQLITE database");
         this.migrateData = this.getMigrateDataFromSQLITE();
       }
       else {
         // we've upgraded before but the JSON file is gone, fall through
         // and rebuild from scratch
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "dbMissing");
       }
     }
@@ -751,50 +755,58 @@ this.XPIDatabase = {
    *         A boolean indicating whether add-on information should be loaded
    *         from the install locations if the database needs to be rebuilt.
    *         (if false, caller is XPIProvider.checkForChanges() which will rebuild)
    */
   rebuildDatabase: function XIPDB_rebuildDatabase(aRebuildOnError) {
     this.addonDB = new Map();
     this.initialized = true;
 
+    if (XPIProvider.installStates && XPIProvider.installStates.length == 0) {
+      // No extensions installed, so we're done
+      LOG("Rebuilding XPI database with no extensions");
+      return;
+    }
+
     // If there is no migration data then load the list of add-on directories
     // that were active during the last run
     if (!this.migrateData)
       this.activeBundles = this.getActiveBundles();
 
     if (aRebuildOnError) {
       WARN("Rebuilding add-ons database from installed extensions.");
       try {
-        let state = XPIProvider.getInstallLocationStates();
-        XPIProvider.processFileChanges(state, {}, false);
+        XPIProvider.processFileChanges(XPIProvider.installStates, {}, false);
       }
       catch (e) {
         ERROR("Failed to rebuild XPI database from installed extensions", e);
       }
-      // Make to update the active add-ons and add-ons list on shutdown
+      // Make sure to update the active add-ons and add-ons list on shutdown
       Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
     }
   },
 
   /**
    * Gets the list of file descriptors of active extension directories or XPI
    * files from the add-ons list. This must be loaded from disk since the
    * directory service gives no easy way to get both directly. This list doesn't
    * include themes as preferences already say which theme is currently active
    *
    * @return an array of persistent descriptors for the directories
    */
   getActiveBundles: function XPIDB_getActiveBundles() {
     let bundles = [];
 
+    // non-bootstrapped extensions
     let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST],
                                        true);
 
     if (!addonsList.exists())
+      // XXX Irving believes this is broken in the case where there is no
+      // extensions.ini but there are bootstrap extensions (e.g. Android)
       return null;
 
     try {
       let iniFactory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]
                          .getService(Ci.nsIINIParserFactory);
       let parser = iniFactory.createINIParser(addonsList);
       let keys = parser.getKeys("ExtensionDirs");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -372,20 +372,18 @@ let gXPISaveError = null;
  *         application has changed version since the last run. If not passed it
  *         defaults to true
  */
 function startupManager(aAppChanged) {
   if (gInternalManager)
     do_throw("Test attempt to startup manager that was already started.");
 
   if (aAppChanged || aAppChanged === undefined) {
-    var file = gProfD.clone();
-    file.append("extensions.ini");
-    if (file.exists())
-      file.remove(true);
+    if (gExtensionsINI.exists())
+      gExtensionsINI.remove(true);
   }
 
   gInternalManager = AM_Cc["@mozilla.org/addons/integration;1"].
                      getService(AM_Ci.nsIObserver).
                      QueryInterface(AM_Ci.nsITimerCallback);
 
   gInternalManager.observe(null, "addons-startup", null);
 
@@ -468,24 +466,22 @@ function loadAddonsList() {
     return dirs;
   }
 
   gAddonsList = {
     extensions: [],
     themes: []
   };
 
-  var file = gProfD.clone();
-  file.append("extensions.ini");
-  if (!file.exists())
+  if (!gExtensionsINI.exists())
     return;
 
   var factory = AM_Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
                 getService(AM_Ci.nsIINIParserFactory);
-  var parser = factory.createINIParser(file);
+  var parser = factory.createINIParser(gExtensionsINI);
   gAddonsList.extensions = readDirectories("ExtensionDirs");
   gAddonsList.themes = readDirectories("ThemeDirs");
 }
 
 function isItemInAddonsList(aType, aDir, aId) {
   var path = aDir.clone();
   path.append(aId);
   var xpiPath = aDir.clone();
@@ -1209,16 +1205,24 @@ if ("nsIWindowsRegKey" in AM_Ci) {
   registrar.registerFactory(Components.ID("{0478de5b-0f38-4edb-851d-4c99f1ed8eba}"),
                             "Mock Windows Registry Implementation",
                             "@mozilla.org/windows-registry-key;1", WinRegFactory);
 }
 
 // Get the profile directory for tests to use.
 const gProfD = do_get_profile();
 
+const EXTENSIONS_DB = "extensions.json";
+let gExtensionsJSON = gProfD.clone();
+gExtensionsJSON.append(EXTENSIONS_DB);
+
+const EXTENSIONS_INI = "extensions.ini";
+let gExtensionsINI = gProfD.clone();
+gExtensionsINI.append(EXTENSIONS_INI);
+
 // Enable more extensive EM logging
 Services.prefs.setBoolPref("extensions.logging.enabled", true);
 
 // By default only load extensions from the profile install location
 Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_PROFILE);
 
 // By default don't disable add-ons from any scope
 Services.prefs.setIntPref("extensions.autoDisableScopes", 0);
@@ -1394,20 +1398,16 @@ function do_exception_wrap(func) {
       func.apply(null, arguments);
     }
     catch(e) {
       do_report_unexpected_exception(e);
     }
   };
 }
 
-const EXTENSIONS_DB = "extensions.json";
-let gExtensionsJSON = gProfD.clone();
-gExtensionsJSON.append(EXTENSIONS_DB);
-
 /**
  * Change the schema version of the JSON extensions database
  */
 function changeXPIDBVersion(aNewVersion) {
   let jData = loadJSON(gExtensionsJSON);
   jData.schemaVersion = aNewVersion;
   saveJSON(jData, gExtensionsJSON);
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
@@ -162,19 +162,17 @@ function run_test() {
   do_test_pending();
 
   resetPrefs();
 
   startupManager();
 
   do_check_false(gExtensionsJSON.exists());
 
-  let file = gProfD.clone();
-  file.leafName = "extensions.ini";
-  do_check_false(file.exists());
+  do_check_false(gExtensionsINI.exists());
 
   do_check_bootstrappedPref(run_test_1);
 }
 
 // Tests that installing doesn't require a restart
 function run_test_1() {
   prepare_test({ }, [
     "onNewInstall"
@@ -218,19 +216,17 @@ function run_test_1() {
       // startup should not have been called yet.
       do_check_eq(getActiveVersion(), -1);
     });
     install.install();
   });
 }
 
 function check_test_1(installSyncGUID) {
-  let file = gProfD.clone();
-  file.leafName = "extensions.ini";
-  do_check_false(file.exists());
+  do_check_false(gExtensionsINI.exists());
 
   AddonManager.getAllInstalls(function(installs) {
     // There should be no active installs now since the install completed and
     // doesn't require a restart.
     do_check_eq(installs.length, 0);
 
     AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
       do_check_neq(b1, null);
@@ -308,19 +304,17 @@ function run_test_3() {
   do_check_eq(getShutdownNewVersion(), 0);
   startupManager(false);
   do_check_eq(getInstalledVersion(), 1);
   do_check_eq(getActiveVersion(), 0);
   do_check_eq(getShutdownReason(), ADDON_DISABLE);
   do_check_eq(getShutdownNewVersion(), 0);
   do_check_not_in_crash_annotation("bootstrap1@tests.mozilla.org", "1.0");
 
-  let file = gProfD.clone();
-  file.append("extensions.ini");
-  do_check_false(file.exists());
+  do_check_false(gExtensionsINI.exists());
 
   AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
     do_check_neq(b1, null);
     do_check_eq(b1.version, "1.0");
     do_check_false(b1.appDisabled);
     do_check_true(b1.userDisabled);
     do_check_false(b1.isActive);
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js
@@ -138,22 +138,22 @@ var PluginHostFactory = {
       throw Components.results.NS_ERROR_NO_AGGREGATION;
     return PluginHost.QueryInterface(iid);
   }
 };
 
 // Don't need the full interface, attempts to call other methods will just
 // throw which is just fine
 var WindowWatcher = {
-  openWindow: function(parent, url, name, features, arguments) {
+  openWindow: function(parent, url, name, features, windowArguments) {
     // Should be called to list the newly blocklisted items
     do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
 
     if (gNotificationCheck) {
-      var args = arguments.wrappedJSObject;
+      var args = windowArguments.wrappedJSObject;
       gNotificationCheck(args);
     }
 
     //run the code after the blocklist is closed
     Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
 
     // Call the next test after the blocklist has finished up
     do_timeout(0, gTestCheck);
@@ -200,20 +200,22 @@ function create_addon(addon) {
                    "    </em:targetApplication>\n" +
                    "    <em:name>" + addon.name + "</em:name>\n" +
                    "  </Description>\n" +
                    "</RDF>\n";
   var target = gProfD.clone();
   target.append("extensions");
   target.append(addon.id);
   target.append("install.rdf");
-  target.create(target.NORMAL_FILE_TYPE, 0644);
+  target.create(target.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
   var stream = Cc["@mozilla.org/network/file-output-stream;1"].
                createInstance(Ci.nsIFileOutputStream);
-  stream.init(target, 0x04 | 0x08 | 0x20, 0664, 0); // write, create, truncate
+  stream.init(target,
+              FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+              FileUtils.PERMS_FILE, 0);
   stream.write(installrdf, installrdf.length);
   stream.close();
 }
 
 function load_blocklist(file) {
   Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/" + file);
   var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
                   getService(Ci.nsITimerCallback);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_no_addons.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test startup and restart when no add-ons are installed
+// bug 944006
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+// Load XPI Provider to get schema version ID
+let XPIScope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
+const DB_SCHEMA = XPIScope.DB_SCHEMA;
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+  // Kick off the task-based tests...
+  run_next_test();
+}
+
+// Test for a preference to either exist with a specified value, or not exist at all
+function checkPending() {
+  try {
+    do_check_false(Services.prefs.getBoolPref("extensions.pendingOperations"));
+  }
+  catch (e) {
+    // OK
+  }
+}
+
+function checkString(aPref, aValue) {
+  try {
+    do_check_eq(Services.prefs.getCharPref(aPref), aValue)
+  }
+  catch (e) {
+    //OK
+  }
+}
+
+// Make sure all our extension state is empty/nonexistent
+function check_empty_state() {
+  do_check_false(gExtensionsJSON.exists());
+  do_check_false(gExtensionsINI.exists());
+
+  do_check_eq(Services.prefs.getIntPref("extensions.databaseSchema"), DB_SCHEMA);
+
+  checkString("extensions.bootstrappedAddons", "{}");
+  checkString("extensions.installCache", "[]");
+  checkPending();
+}
+
+// After first run with no add-ons, we expect:
+// no extensions.json is created
+// no extensions.ini
+// database schema version preference is set
+// bootstrap add-ons preference is not found
+// add-on directory state preference is an empty array
+// no pending operations
+add_task(function first_run() {
+  startupManager();
+  check_empty_state();
+  yield true;
+});
+
+// Now do something that causes a DB load, and re-check
+function trigger_db_load() {
+  let addonDefer = Promise.defer();
+  AddonManager.getAddonsByTypes(['extension'], addonDefer.resolve);
+  let addonList = yield addonDefer.promise;
+
+  do_check_eq(addonList.length, 0);
+  check_empty_state();
+
+  yield true;
+};
+add_task(trigger_db_load);
+
+// Now restart the manager and check again
+add_task(function restart_and_recheck() {
+  restartManager();
+  check_empty_state();
+  yield true;
+});
+
+// and reload the DB again
+add_task(trigger_db_load);
+
+// When we start up with no DB and an old database schema, we should update the
+// schema number but not create a database
+add_task(function upgrade_schema_version() {
+  shutdownManager();
+  Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+  startupManager();
+  do_check_eq(Services.prefs.getIntPref("extensions.databaseSchema"), DB_SCHEMA);
+  check_empty_state();
+});
+
--- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
@@ -127,22 +127,19 @@ function run_test() {
 
   startupManager();
   check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
 
-  let file = gProfD.clone();
-  file.append("extensions.json");
-  do_check_false(file.exists());
+  do_check_false(gExtensionsJSON.exists());
 
-  file.leafName = "extensions.ini";
-  do_check_false(file.exists());
+  do_check_false(gExtensionsINI.exists());
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org",
                                "addon6@tests.mozilla.org",
                                "addon7@tests.mozilla.org"],
@@ -185,20 +182,18 @@ function run_test_1() {
                                       "addon2@tests.mozilla.org",
                                       "addon3@tests.mozilla.org"]);
   check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
   do_check_true(gCachePurged);
 
-  let file = gProfD.clone();
-  file.append = "extensions.ini";
-  do_print("Checking for " + file.path);
-  do_check_true(file.exists());
+  do_print("Checking for " + gExtensionsINI.path);
+  do_check_true(gExtensionsINI.exists());
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org",
                                "addon6@tests.mozilla.org",
                                "addon7@tests.mozilla.org"],
@@ -297,19 +292,17 @@ function run_test_2() {
   restartManager();
   check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
   check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon3@tests.mozilla.org"]);
   check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
   check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
   do_check_true(gCachePurged);
 
-  var file = gProfD.clone();
-  file.append("extensions.ini");
-  do_check_true(file.exists());
+  do_check_true(gExtensionsINI.exists());
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org"],
                                function([a1, a2, a3, a4, a5]) {
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_upgrade.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_upgrade.js
@@ -174,19 +174,17 @@ function run_test_3() {
       maxVersion: "3"
     }],
     name: "Test Addon 4",
   }, globalDir);
   setExtensionModifiedTime(dest, gInstallTime);
 
   // Simulates a simple Build ID change, the platform deletes extensions.ini
   // whenever the application is changed.
-  var file = gProfD.clone();
-  file.append("extensions.ini");
-  file.remove(true);
+  gExtensionsINI.remove(true);
   restartManager();
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org"],
                                function([a1, a2, a3, a4]) {
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_upgrade_strictcompat.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_upgrade_strictcompat.js
@@ -177,19 +177,17 @@ function run_test_3() {
       maxVersion: "3"
     }],
     name: "Test Addon 4",
   }, globalDir);
   setExtensionModifiedTime(dest, gInstallTime);
 
   // Simulates a simple Build ID change, the platform deletes extensions.ini
   // whenever the application is changed.
-  var file = gProfD.clone();
-  file.append("extensions.ini");
-  file.remove(true);
+  gExtensionsINI.remove(true);
   restartManager();
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org"],
                                function([a1, a2, a3, a4]) {
 
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -200,16 +200,17 @@ run-sequentially = Uses hardcoded ports 
 skip-if = os == "android"
 [test_migrate1.js]
 [test_migrate2.js]
 [test_migrate3.js]
 [test_migrate4.js]
 [test_migrate5.js]
 [test_migrateAddonRepository.js]
 [test_migrate_max_version.js]
+[test_no_addons.js]
 [test_onPropertyChanged_appDisabled.js]
 [test_permissions.js]
 [test_permissions_prefs.js]
 [test_plugins.js]
 [test_pluginchange.js]
 [test_pluginBlocklistCtp.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"