From 35820dbe1e4b18a219b7cd09d1b27c963bdcae3c Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Tue, 8 Nov 2011 16:02:49 -0800 Subject: [PATCH] Bug 697246: Defer creating the extensions database until it is actually necessary. r=Unfocused --- toolkit/mozapps/extensions/XPIProvider.jsm | 118 +++++++++++++----- .../test/xpcshell/test_bootstrap.js | 8 ++ .../extensions/test/xpcshell/test_startup.js | 8 ++ 3 files changed, 105 insertions(+), 29 deletions(-) diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index f6f44f326fda..bbfee92bc355 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -2942,16 +2942,18 @@ var XPIProvider = { let state = this.getInstallLocationStates(); - // If the database exists then the previous file cache can be trusted - // otherwise the database needs to be recreated - let dbFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true); - updateDatabase |= !dbFile.exists(); if (!updateDatabase) { // If the state has changed then we must update the database let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null); updateDatabase |= cache != JSON.stringify(state); } + // 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. + if (!XPIDatabase.dbfileExists) + updateDatabase = state.length > 0; + if (!updateDatabase) { let bootstrapDescriptors = [this.bootstrappedAddons[b].descriptor for (b in this.bootstrappedAddons)]; @@ -2963,7 +2965,7 @@ var XPIProvider = { bootstrapDescriptors.splice(pos, 1); } }); - + if (bootstrapDescriptors.length > 0) { WARN("Bootstrap state is invalid (missing add-ons: " + bootstrapDescriptors.toSource() + ")"); updateDatabase = true; @@ -2977,7 +2979,7 @@ var XPIProvider = { // If the database needs to be updated then open it and then update it // from the filesystem if (updateDatabase || hasPendingChanges) { - let migrateData = XPIDatabase.openConnection(false); + let migrateData = XPIDatabase.openConnection(false, true); try { extensionListChanged = this.processFileChanges(state, manifests, @@ -4022,6 +4024,8 @@ var XPIDatabase = { addonCache: [], // The nested transaction count transactionCount: 0, + // The database file + dbfile: FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true), // The statements used by the database statements: { @@ -4113,6 +4117,11 @@ var XPIDatabase = { rollbackSavepoint: "ROLLBACK TO SAVEPOINT 'default'" }, + get dbfileExists() { + delete this.dbfileExists; + return this.dbfileExists = this.dbfile.exists(); + }, + /** * Begins a new transaction in the database. Transactions may be nested. Data * written by an inner transaction may be rolled back on its own. Rolling back @@ -4176,6 +4185,7 @@ var XPIDatabase = { // Attempt to open the database try { connection = Services.storage.openUnsharedDatabase(aDBFile); + this.dbfileExists = true; } catch (e) { ERROR("Failed to open database (1st attempt)", e); @@ -4213,12 +4223,17 @@ var XPIDatabase = { * @return the migration data from the database if it was an old schema or * null otherwise. */ - openConnection: function XPIDB_openConnection(aRebuildOnError) { - this.initialized = true; - let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true); + openConnection: function XPIDB_openConnection(aRebuildOnError, aForceOpen) { delete this.connection; - this.connection = this.openDatabaseFile(dbfile); + if (!aForceOpen && !this.dbfileExists) { + this.connection = null; + return {}; + } + + this.initialized = true; + + this.connection = this.openDatabaseFile(this.dbfile); let migrateData = null; // If the database was corrupt or missing then the new blank database will @@ -4235,11 +4250,11 @@ var XPIDatabase = { // Delete the existing database this.connection.close(); try { - if (dbfile.exists()) - dbfile.remove(true); + if (this.dbfileExists) + this.dbfile.remove(true); // Reopen an empty database - this.connection = this.openDatabaseFile(dbfile); + this.connection = this.openDatabaseFile(this.dbfile); } catch (e) { ERROR("Failed to remove old database", e); @@ -4250,7 +4265,6 @@ var XPIDatabase = { } else if (Prefs.getIntPref(PREF_DB_SCHEMA, 0) == 0) { // Only migrate data from the RDF if we haven't done it before - LOG("Migrating data from extensions.rdf"); migrateData = this.getMigrateDataFromRDF(); } @@ -4350,6 +4364,7 @@ var XPIDatabase = { // Migrate data from extensions.rdf let rdffile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_DATABASE], true); if (rdffile.exists()) { + LOG("Migrating data from extensions.rdf"); let ds = gRDF.GetDataSourceBlocking(Services.io.newFileURI(rdffile).spec); let root = Cc["@mozilla.org/rdf/container;1"]. createInstance(Ci.nsIRDFContainer); @@ -4963,6 +4978,9 @@ var XPIDatabase = { * @return an array of names of install locations */ getInstallLocations: function XPIDB_getInstallLocations() { + if (!this.connection) + return []; + let stmt = this.getStatement("getInstallLocations"); return [row.location for each (row in resultRows(stmt))]; @@ -4976,6 +4994,9 @@ var XPIDatabase = { * @return an array of DBAddonInternals */ getAddonsInLocation: function XPIDB_getAddonsInLocation(aLocation) { + if (!this.connection) + return []; + let stmt = this.getStatement("getAddonsInLocation"); stmt.params.location = aLocation; @@ -4994,6 +5015,11 @@ var XPIDatabase = { * A callback to pass the DBAddonInternal to */ getAddonInLocation: function XPIDB_getAddonInLocation(aId, aLocation, aCallback) { + if (!this.connection) { + aCallback(null); + return; + } + let stmt = this.getStatement("getAddonInLocation"); stmt.params.id = aId; @@ -5020,6 +5046,11 @@ var XPIDatabase = { * A callback to pass the DBAddonInternal to */ getVisibleAddonForID: function XPIDB_getVisibleAddonForID(aId, aCallback) { + if (!this.connection) { + aCallback(null); + return; + } + let stmt = this.getStatement("getVisibleAddonForID"); stmt.params.id = aId; @@ -5045,6 +5076,11 @@ var XPIDatabase = { * A callback to pass the array of DBAddonInternals to */ getVisibleAddons: function XPIDB_getVisibleAddons(aTypes, aCallback) { + if (!this.connection) { + aCallback([]); + return; + } + let stmt = null; if (!aTypes || aTypes.length == 0) { stmt = this.getStatement("getVisibleAddons"); @@ -5076,6 +5112,9 @@ var XPIDatabase = { * @return an array of DBAddonInternals */ getAddonsByType: function XPIDB_getAddonsByType(aType) { + if (!this.connection) + return []; + let stmt = this.getStatement("getAddonsByType"); stmt.params.type = aType; @@ -5090,6 +5129,9 @@ var XPIDatabase = { * @return a DBAddonInternal */ getVisibleAddonForInternalName: function XPIDB_getVisibleAddonForInternalName(aInternalName) { + if (!this.connection) + return null; + let stmt = this.getStatement("getVisibleAddonForInternalName"); let addon = null; @@ -5112,6 +5154,11 @@ var XPIDatabase = { */ getVisibleAddonsWithPendingOperations: function XPIDB_getVisibleAddonsWithPendingOperations(aTypes, aCallback) { + if (!this.connection) { + aCallback([]); + return; + } + let stmt = null; if (!aTypes || aTypes.length == 0) { stmt = this.getStatement("getVisibleAddonsWithPendingOperations"); @@ -5143,6 +5190,9 @@ var XPIDatabase = { * @return an array of DBAddonInternals */ getAddons: function XPIDB_getAddons() { + if (!this.connection) + return []; + let stmt = this.getStatement("getAddons"); return [this.makeAddonFromRow(row) for each (row in resultRows(stmt))];; @@ -5157,6 +5207,10 @@ var XPIDatabase = { * The file descriptor of the add-on */ addAddonMetadata: function XPIDB_addAddonMetadata(aAddon, aDescriptor) { + // If there is no DB yet then forcibly create one + if (!this.connection) + this.openConnection(false, true); + this.beginTransaction(); var self = this; @@ -5430,28 +5484,34 @@ var XPIDatabase = { let enabledAddons = []; let text = "[ExtensionDirs]\r\n"; let count = 0; + let stmt; - let stmt = this.getStatement("getActiveAddons"); + if (this.connection) { + stmt = this.getStatement("getActiveAddons"); - for (let row in resultRows(stmt)) { - text += "Extension" + (count++) + "=" + row.descriptor + "\r\n"; - enabledAddons.push(row.id + ":" + row.version); + for (let row in resultRows(stmt)) { + 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"; - if (Prefs.getBoolPref(PREF_EM_DSS_ENABLED)) { - stmt = this.getStatement("getThemes"); - } - else { - stmt = this.getStatement("getActiveTheme"); - stmt.params.internalName = XPIProvider.selectedSkin; - } - count = 0; - for (let row in resultRows(stmt)) { - text += "Extension" + (count++) + "=" + row.descriptor + "\r\n"; - enabledAddons.push(row.id + ":" + row.version); + + if (this.connection) { + if (Prefs.getBoolPref(PREF_EM_DSS_ENABLED)) { + stmt = this.getStatement("getThemes"); + } + else { + stmt = this.getStatement("getActiveTheme"); + stmt.params.internalName = XPIProvider.selectedSkin; + } + count = 0; + for (let row in resultRows(stmt)) { + text += "Extension" + (count++) + "=" + row.descriptor + "\r\n"; + enabledAddons.push(row.id + ":" + row.version); + } } var fos = FileUtils.openSafeFileOutputStream(addonsList); diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js index d5cdf2a70fc8..6adc0520a0f5 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js @@ -125,6 +125,10 @@ function run_test() { startupManager(); + let file = gProfD.clone(); + file.append("extensions.sqlite"); + do_check_false(file.exists()); + run_test_1(); } @@ -167,6 +171,10 @@ function run_test_1() { } function check_test_1() { + let file = gProfD.clone(); + file.append("extensions.sqlite"); + do_check_true(file.exists()); + AddonManager.getAllInstalls(function(installs) { // There should be no active installs now since the install completed and // doesn't require a restart. diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js index 71f41b6775f8..3e4f112a1826 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js @@ -132,6 +132,10 @@ function run_test() { check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []); check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []); + let file = gProfD.clone(); + file.append("extensions.sqlite"); + do_check_false(file.exists()); + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org", @@ -183,6 +187,10 @@ function run_test_1() { check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []); do_check_true(gCachePurged); + let file = gProfD.clone(); + file.append("extensions.sqlite"); + do_check_true(file.exists()); + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org",