diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index 66c150a504e..bbf03dac1ab 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -2259,23 +2259,13 @@ var XPIProvider = { * @param aOldPlatformVersion * The version of the platform last run with this profile or null * if it is a new profile or the version is unknown - * @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 a boolean indicating if a change requiring flushing the caches was * detected */ processFileChanges: function XPI_processFileChanges(aState, aManifests, aUpdateCompatibility, aOldAppVersion, - aOldPlatformVersion, - aMigrateData, - aActiveBundles) { + aOldPlatformVersion) { let visibleAddons = {}; let oldBootstrappedAddons = this.bootstrappedAddons; this.bootstrappedAddons = {}; @@ -2601,10 +2591,11 @@ var XPIProvider = { if (aInstallLocation.name in aManifests) newAddon = aManifests[aInstallLocation.name][aId]; - // If we aren't recovering from a corrupt database or we don't have - // migration data for this add-on then this must be a new install. - let isNewInstall = !aActiveBundles && !aMigrateData; - + // If we had staged data for this add-on or we aren't recovering from a + // corrupt database and we don't have migration data for this add-on then + // this must be a new install. + let isNewInstall = (!!newAddon) || (!XPIDatabase.activeBundles && !aMigrateData); + // If it's a new install and we haven't yet loaded the manifest then it // must be something dropped directly into the install location let isDetectedInstall = isNewInstall && !newAddon; @@ -2684,14 +2675,14 @@ var XPIProvider = { newAddon.userDisabled = true; } - if (aActiveBundles) { - // If we have a list of what add-ons should be marked as active then use - // it to guess at migration data + // If we have a list of what add-ons should be marked as active then use + // it to guess at migration data. + if (!isNewInstall && XPIDatabase.activeBundles) { // For themes we know which is active by the current skin setting if (newAddon.type == "theme") newAddon.active = newAddon.internalName == XPIProvider.currentSkin; else - newAddon.active = aActiveBundles.indexOf(aAddonState.descriptor) != -1; + newAddon.active = XPIDatabase.activeBundles.indexOf(aAddonState.descriptor) != -1; // If the add-on wasn't active and it isn't already disabled in some way // then it was probably either softDisabled or userDisabled @@ -2858,8 +2849,8 @@ var XPIProvider = { // Get the migration data for this install location. let locMigrateData = {}; - if (aMigrateData && installLocation.name in aMigrateData) - locMigrateData = aMigrateData[installLocation.name]; + if (XPIDatabase.migrateData && installLocation.name in XPIDatabase.migrateData) + locMigrateData = XPIDatabase.migrateData[installLocation.name]; for (let id in addonStates) { changed = addMetadata(installLocation, id, addonStates[id], locMigrateData[id]) || changed; @@ -2881,6 +2872,9 @@ var XPIProvider = { let cache = JSON.stringify(this.getInstallLocationStates()); Services.prefs.setCharPref(PREF_INSTALL_CACHE, cache); + // Clear out any cached migration data. + XPIDatabase.migrateData = null; + return changed; }, @@ -2950,7 +2944,7 @@ var XPIProvider = { // changes then we must update the database with the information in the // install locations let manifests = {}; - updateDatabase = this.processPendingFileChanges(manifests) | updateDatabase; + updateDatabase = this.processPendingFileChanges(manifests) || updateDatabase; // 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 @@ -2958,19 +2952,19 @@ var XPIProvider = { let hasPendingChanges = Prefs.getBoolPref(PREF_PENDING_OPERATIONS); // If the schema appears to have changed then we should update the database - updateDatabase |= DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0); + updateDatabase = updateDatabase || DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0); // If the application has changed then check for new distribution add-ons if (aAppChanged !== false && Prefs.getBoolPref(PREF_INSTALL_DISTRO_ADDONS, true)) - updateDatabase = this.installDistributionAddons(manifests) | updateDatabase; + updateDatabase = this.installDistributionAddons(manifests) || updateDatabase; let state = this.getInstallLocationStates(); 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); + updateDatabase = cache != JSON.stringify(state); } // If the database doesn't exist and there are add-ons installed then we @@ -3009,14 +3003,13 @@ var XPIProvider = { if (updateDatabase || hasPendingChanges) { XPIDatabase.beginTransaction(); transationBegun = true; - let migrateData = XPIDatabase.openConnection(false, true); + XPIDatabase.openConnection(false, true); try { extensionListChanged = this.processFileChanges(state, manifests, aAppChanged, aOldAppVersion, - aOldPlatformVersion, - migrateData, null); + aOldPlatformVersion); } catch (e) { ERROR("Error processing file changes", e); diff --git a/toolkit/mozapps/extensions/XPIProviderUtils.js b/toolkit/mozapps/extensions/XPIProviderUtils.js index 3c9cfede416..aa9fb95f410 100644 --- a/toolkit/mozapps/extensions/XPIProviderUtils.js +++ b/toolkit/mozapps/extensions/XPIProviderUtils.js @@ -309,6 +309,10 @@ var XPIDatabase = { transactionCount: 0, // The database file dbfile: FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true), + // Migration data loaded from an old version of the database. + migrateData: null, + // Active add-on directories loaded from extensions.ini and prefs at startup. + activeBundles: null, // The statements used by the database statements: { @@ -480,21 +484,28 @@ var XPIDatabase = { } catch (e) { ERROR("Failed to open database (1st attempt)", e); - try { - aDBFile.remove(true); + // If the database was locked for some reason then assume it still + // has some good data and we should try to load it the next time around. + if (e.result != Cr.NS_ERROR_STORAGE_BUSY) { + try { + aDBFile.remove(true); + } + catch (e) { + ERROR("Failed to remove database that could not be opened", e); + } + try { + connection = Services.storage.openUnsharedDatabase(aDBFile); + } + catch (e) { + ERROR("Failed to open database (2nd attempt)", e); + + // If we have got here there seems to be no way to open the real + // database, instead open a temporary memory database so things will + // work for this session. + return Services.storage.openSpecialDatabase("memory"); + } } - catch (e) { - ERROR("Failed to remove database that could not be opened", e); - } - try { - connection = Services.storage.openUnsharedDatabase(aDBFile); - } - catch (e) { - ERROR("Failed to open database (2nd attempt)", e); - - // If we have got here there seems to be no way to open the real - // database, instead open a temporary memory database so things will - // work for this session + else { return Services.storage.openSpecialDatabase("memory"); } } @@ -526,7 +537,7 @@ var XPIDatabase = { this.connection = this.openDatabaseFile(this.dbfile); - let migrateData = null; + this.migrateData = null; // If the database was corrupt or missing then the new blank database will // have a schema version of 0. let schemaVersion = this.connection.schemaVersion; @@ -536,7 +547,7 @@ var XPIDatabase = { // information from it if (schemaVersion != 0) { LOG("Migrating data from schema " + schemaVersion); - migrateData = this.getMigrateDataFromDatabase(); + this.migrateData = this.getMigrateDataFromDatabase(); // Delete the existing database this.connection.close(); @@ -562,21 +573,24 @@ var XPIDatabase = { if (dbSchema == 0) { // Only migrate data from the RDF if we haven't done it before - migrateData = this.getMigrateDataFromRDF(); + this.migrateData = this.getMigrateDataFromRDF(); } } // At this point the database should be completely empty this.createSchema(); + // 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) { - let activeBundles = this.getActiveBundles(); WARN("Rebuilding add-ons database from installed extensions."); this.beginTransaction(); try { let state = XPIProvider.getInstallLocationStates(); - XPIProvider.processFileChanges(state, {}, false, undefined, undefined, - migrateData, activeBundles) + XPIProvider.processFileChanges(state, {}, false); // Make sure to update the active add-ons and add-ons list on shutdown Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true); this.commitTransaction(); @@ -589,27 +603,15 @@ var XPIDatabase = { } // If the database connection has a file open then it has the right schema - // by now so make sure the preferences reflect that. If not then there is - // an in-memory database open which means a problem opening and deleting the - // real database, clear the schema preference to force trying to load the - // database on the next startup + // by now so make sure the preferences reflect that. if (this.connection.databaseFile) { Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA); + Services.prefs.savePrefFile(null); } - else { - try { - Services.prefs.clearUserPref(PREF_DB_SCHEMA); - } - catch (e) { - // The preference may not be defined - } - } - Services.prefs.savePrefFile(null); // Begin any pending transactions for (let i = 0; i < this.transactionCount; i++) this.connection.executeSimpleSQL("SAVEPOINT 'default'"); - return migrateData; }, /** @@ -636,7 +638,14 @@ var XPIDatabase = { let iniFactory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. getService(Ci.nsIINIParserFactory); - let parser = iniFactory.createINIParser(addonsList); + + try { + var parser = iniFactory.createINIParser(addonsList); + } + catch (e) { + WARN("Failed to parse extensions.ini", e); + return null; + } let keys = parser.getKeys("ExtensionDirs"); @@ -661,51 +670,52 @@ 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); - root.Init(ds, gRDF.GetResource(RDFURI_ITEM_ROOT)); - let elements = root.GetElements(); - while (elements.hasMoreElements()) { - let source = elements.getNext().QueryInterface(Ci.nsIRDFResource); + if (!rdffile.exists()) + return null; - let location = getRDFProperty(ds, source, "installLocation"); - if (location) { - if (!(location in migrateData)) - migrateData[location] = {}; - let id = source.ValueUTF8.substring(PREFIX_ITEM_URI.length); - migrateData[location][id] = { - version: getRDFProperty(ds, source, "version"), - userDisabled: false, - targetApplications: [] + 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); + root.Init(ds, gRDF.GetResource(RDFURI_ITEM_ROOT)); + let elements = root.GetElements(); + while (elements.hasMoreElements()) { + let source = elements.getNext().QueryInterface(Ci.nsIRDFResource); + + let location = getRDFProperty(ds, source, "installLocation"); + if (location) { + if (!(location in migrateData)) + migrateData[location] = {}; + let id = source.ValueUTF8.substring(PREFIX_ITEM_URI.length); + migrateData[location][id] = { + version: getRDFProperty(ds, source, "version"), + userDisabled: false, + targetApplications: [] + } + + let disabled = getRDFProperty(ds, source, "userDisabled"); + if (disabled == "true" || disabled == "needs-disable") + migrateData[location][id].userDisabled = true; + + let targetApps = ds.GetTargets(source, EM_R("targetApplication"), + true); + while (targetApps.hasMoreElements()) { + let targetApp = targetApps.getNext() + .QueryInterface(Ci.nsIRDFResource); + let appInfo = { + id: getRDFProperty(ds, targetApp, "id") + }; + + let minVersion = getRDFProperty(ds, targetApp, "updatedMinVersion"); + if (minVersion) { + appInfo.minVersion = minVersion; + appInfo.maxVersion = getRDFProperty(ds, targetApp, "updatedMaxVersion"); } - - let disabled = getRDFProperty(ds, source, "userDisabled"); - if (disabled == "true" || disabled == "needs-disable") - migrateData[location][id].userDisabled = true; - - let targetApps = ds.GetTargets(source, EM_R("targetApplication"), - true); - while (targetApps.hasMoreElements()) { - let targetApp = targetApps.getNext() - .QueryInterface(Ci.nsIRDFResource); - let appInfo = { - id: getRDFProperty(ds, targetApp, "id") - }; - - let minVersion = getRDFProperty(ds, targetApp, "updatedMinVersion"); - if (minVersion) { - appInfo.minVersion = minVersion; - appInfo.maxVersion = getRDFProperty(ds, targetApp, "updatedMaxVersion"); - } - else { - appInfo.minVersion = getRDFProperty(ds, targetApp, "minVersion"); - appInfo.maxVersion = getRDFProperty(ds, targetApp, "maxVersion"); - } - migrateData[location][id].targetApplications.push(appInfo); + else { + appInfo.minVersion = getRDFProperty(ds, targetApp, "minVersion"); + appInfo.maxVersion = getRDFProperty(ds, targetApp, "maxVersion"); } + migrateData[location][id].targetApplications.push(appInfo); } } } @@ -747,7 +757,7 @@ var XPIDatabase = { if (reqCount < REQUIRED.length) { ERROR("Unable to read anything useful from the database"); - return migrateData; + return null; } stmt.finalize(); @@ -790,6 +800,7 @@ var XPIDatabase = { catch (e) { // An error here means the schema is too different to read ERROR("Error migrating data", e); + return null; } finally { if (taStmt) @@ -818,6 +829,11 @@ var XPIDatabase = { this.rollbackTransaction(); } + // If we are running with an in-memory database then force a new + // extensions.ini to be written to disk on the next startup + if (!this.connection.databaseFile) + Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true); + this.initialized = false; let connection = this.connection; delete this.connection; diff --git a/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf b/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf new file mode 100644 index 00000000000..09655c2a673 --- /dev/null +++ b/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf @@ -0,0 +1,23 @@ + + + + + + addon5@tests.mozilla.org + 2.0 + + + Test 5 + Test Description + + + + xpcshell@tests.mozilla.org + 2 + 2 + + + + + diff --git a/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf b/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf new file mode 100644 index 00000000000..75f110d2a1c --- /dev/null +++ b/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf @@ -0,0 +1,23 @@ + + + + + + addon6@tests.mozilla.org + 1.0 + + + Test 6 + Test Description + + + + xpcshell@tests.mozilla.org + 2 + 2 + + + + + diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_locked.js b/toolkit/mozapps/extensions/test/xpcshell/test_locked.js index c81613733fd..dd00999031a 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_locked.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked.js @@ -152,6 +152,9 @@ function run_test() { // Startup the profile and setup the initial state startupManager(); + // New profile so new add-ons are ignored + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", @@ -198,30 +201,35 @@ function run_test_1() { do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_true(a5.isActive); do_check_false(a5.userDisabled); do_check_false(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); @@ -240,20 +248,30 @@ function run_test_1() { do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - // After restarting the database won't be open so lock the file for writing - restartManager(); + // After shutting down the database won't be open so we can lock it + shutdownManager(); var dbfile = gProfD.clone(); dbfile.append("extensions.sqlite"); - var fstream = AM_Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(AM_Ci.nsIFileOutputStream); - fstream.init(dbfile, FileUtils.MODE_TRUNCATE | FileUtils.MODE_WRONLY, FileUtils.PERMS_FILE, 0); + let connection = Services.storage.openUnsharedDatabase(dbfile); + connection.executeSimpleSQL("PRAGMA synchronous = FULL"); + connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); + // Force the DB to become locked + connection.beginTransactionAs(connection.TRANSACTION_EXCLUSIVE); + connection.commitTransaction(); + + startupManager(false); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); // Accessing the add-ons should open and recover the database AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", @@ -273,6 +291,7 @@ function run_test_1() { do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); // Should be correctly recovered do_check_neq(a2, null); @@ -280,6 +299,7 @@ function run_test_1() { do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); // The compatibility update won't be recovered but it should still be // active for this session @@ -288,6 +308,7 @@ function run_test_1() { do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); // The compatibility update won't be recovered and with strict // compatibility it would not have been able to tell that it was @@ -298,12 +319,14 @@ function run_test_1() { do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_true(a5.isActive); do_check_false(a5.userDisabled); do_check_false(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); @@ -323,6 +346,7 @@ function run_test_1() { do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); // Should be correctly recovered do_check_neq(t2, null); @@ -330,12 +354,16 @@ function run_test_1() { do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); // Restarting will actually apply changes to extensions.ini which will // then be put into the in-memory database when we next fail to load the // real thing restartManager(); + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org", @@ -352,30 +380,35 @@ function run_test_1() { do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_true(a5.isActive); do_check_false(a5.userDisabled); do_check_false(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); @@ -394,15 +427,98 @@ function run_test_1() { do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - fstream.close(); - end_test(); + connection.close(); + + // After allowing access to the original DB things should go back to as + // they were previously + restartManager(); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + + 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", + "theme1@tests.mozilla.org", + "theme2@tests.mozilla.org"], function([a1, a2, a3, + a4, a5, a6, + a7, t1, t2]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_true(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_true(a3.isActive); + do_check_false(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_neq(a4, null); + do_check_false(a4.isActive); + do_check_true(a4.userDisabled); + do_check_false(a4.appDisabled); + do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); + + do_check_neq(a5, null); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(a7, null); + do_check_false(a7.isActive); + do_check_true(a7.userDisabled); + do_check_false(a7.appDisabled); + do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(t1, null); + do_check_false(t1.isActive); + do_check_true(t1.userDisabled); + do_check_false(t1.appDisabled); + do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); + + do_check_neq(t2, null); + do_check_true(t2.isActive); + do_check_false(t2.userDisabled); + do_check_false(t2.appDisabled); + do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); + + end_test(); + }); }); }); }); diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_locked2.js b/toolkit/mozapps/extensions/test/xpcshell/test_locked2.js new file mode 100644 index 00000000000..7bf65ba4fdd --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked2.js @@ -0,0 +1,272 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Checks that we handle a locked database when there are extension changes +// in progress + +// Will be left alone +var addon1 = { + id: "addon1@tests.mozilla.org", + version: "1.0", + name: "Test 1", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +// Will be enabled +var addon2 = { + id: "addon2@tests.mozilla.org", + version: "1.0", + name: "Test 2", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +// Will be disabled +var addon3 = { + id: "addon3@tests.mozilla.org", + version: "1.0", + name: "Test 3", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +// Will be uninstalled +var addon4 = { + id: "addon4@tests.mozilla.org", + version: "1.0", + name: "Test 4", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + + +// Will be updated +var addon5 = { + id: "addon5@tests.mozilla.org", + version: "1.0", + name: "Test 5", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2"); + + writeInstallRDFForExtension(addon1, profileDir); + writeInstallRDFForExtension(addon2, profileDir); + writeInstallRDFForExtension(addon3, profileDir); + writeInstallRDFForExtension(addon4, profileDir); + writeInstallRDFForExtension(addon5, profileDir); + + // Make it look like add-on 5 was installed some time in the past so the update is + // detected + setExtensionModifiedTime(getFileForAddon(profileDir, addon5.id), Date.now() - (60000)); + + // Startup the profile and setup the initial state + startupManager(); + + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []); + + AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) { + a2.userDisabled = true; + + restartManager(); + + 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]) { + a2.userDisabled = false; + a3.userDisabled = true; + a4.uninstall(); + + installAllFiles([do_get_addon("test_locked2_5"), + do_get_addon("test_locked2_6")], function() { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_false(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_ENABLE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_true(a3.isActive); + do_check_true(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_neq(a4, null); + do_check_true(a4.isActive); + do_check_false(a4.userDisabled); + do_check_false(a4.appDisabled); + do_check_eq(a4.pendingOperations, AddonManager.PENDING_UNINSTALL); + do_check_true(isExtensionInAddonsList(profileDir, a4.id)); + + do_check_neq(a5, null); + do_check_eq(a5.version, "1.0"); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_UPGRADE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + // After shutting down the database won't be open so we can lock it + shutdownManager(); + var dbfile = gProfD.clone(); + dbfile.append("extensions.sqlite"); + let connection = Services.storage.openUnsharedDatabase(dbfile); + connection.executeSimpleSQL("PRAGMA synchronous = FULL"); + connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); + // Force the DB to become locked + connection.beginTransactionAs(connection.TRANSACTION_EXCLUSIVE); + connection.commitTransaction(); + + startupManager(false); + + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []); + + 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"], + function([a1, a2, a3, a4, a5, a6]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_true(a2.isActive); + do_check_false(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_false(a3.isActive); + do_check_true(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_eq(a4, null); + + do_check_neq(a5, null); + do_check_eq(a5.version, "2.0"); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a6.id)); + + connection.close(); + + // After allowing access to the original DB things should still be + // applied correctly + restartManager(); + + // These things happened when we had no access to the database so + // they are seen as external changes when we get the database back :( + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon6@tests.mozilla.org"]); + check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon4@tests.mozilla.org"]); + + 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"], + function([a1, a2, a3, a4, a5, a6]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_true(a2.isActive); + do_check_false(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_false(a3.isActive); + do_check_true(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_eq(a4, null); + + do_check_neq(a5, null); + do_check_eq(a5.version, "2.0"); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a6.id)); + + end_test(); + }); + }); + }); + }); + }); +} + +function end_test() { + do_test_finished(); +} diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js b/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js index 12555c8bba0..f24358d02b7 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js @@ -152,6 +152,9 @@ function run_test() { // Startup the profile and setup the initial state startupManager(); + // New profile so new add-ons are ignored + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", @@ -198,30 +201,35 @@ function run_test_1() { do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_false(a5.isActive); do_check_false(a5.userDisabled); do_check_true(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); @@ -240,20 +248,30 @@ function run_test_1() { do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - // After restarting the database won't be open so lock the file for writing - restartManager(); + // After shutting down the database won't be open so we can lock it + shutdownManager(); var dbfile = gProfD.clone(); dbfile.append("extensions.sqlite"); - var fstream = AM_Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(AM_Ci.nsIFileOutputStream); - fstream.init(dbfile, FileUtils.MODE_TRUNCATE | FileUtils.MODE_WRONLY, FileUtils.PERMS_FILE, 0); + let connection = Services.storage.openUnsharedDatabase(dbfile); + connection.executeSimpleSQL("PRAGMA synchronous = FULL"); + connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); + // Force the DB to become locked + connection.beginTransactionAs(connection.TRANSACTION_EXCLUSIVE); + connection.commitTransaction(); + + startupManager(false); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); // Accessing the add-ons should open and recover the database AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", @@ -273,6 +291,7 @@ function run_test_1() { do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); // Should be correctly recovered do_check_neq(a2, null); @@ -280,6 +299,7 @@ function run_test_1() { do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); // The compatibility update won't be recovered but it should still be // active for this session @@ -288,6 +308,7 @@ function run_test_1() { do_check_false(a3.userDisabled); do_check_true(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); // The compatibility update won't be recovered and it will not have been // able to tell that it was previously userDisabled @@ -296,12 +317,14 @@ function run_test_1() { do_check_false(a4.userDisabled); do_check_true(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_false(a5.isActive); do_check_false(a5.userDisabled); do_check_true(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); @@ -321,6 +344,7 @@ function run_test_1() { do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); // Should be correctly recovered do_check_neq(t2, null); @@ -328,12 +352,16 @@ function run_test_1() { do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); // Restarting will actually apply changes to extensions.ini which will // then be put into the in-memory database when we next fail to load the // real thing restartManager(); + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org", @@ -350,30 +378,35 @@ function run_test_1() { do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_false(a3.isActive); do_check_false(a3.userDisabled); do_check_true(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_false(a4.userDisabled); do_check_true(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_false(a5.isActive); do_check_false(a5.userDisabled); do_check_true(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); @@ -392,15 +425,98 @@ function run_test_1() { do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - fstream.close(); - end_test(); + connection.close(); + + // After allowing access to the original DB things should go back to as + // they were previously + restartManager(); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + + 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", + "theme1@tests.mozilla.org", + "theme2@tests.mozilla.org"], function([a1, a2, a3, + a4, a5, a6, + a7, t1, t2]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_true(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_true(a3.isActive); + do_check_false(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_neq(a4, null); + do_check_false(a4.isActive); + do_check_true(a4.userDisabled); + do_check_false(a4.appDisabled); + do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); + + do_check_neq(a5, null); + do_check_false(a5.isActive); + do_check_false(a5.userDisabled); + do_check_true(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(a7, null); + do_check_false(a7.isActive); + do_check_true(a7.userDisabled); + do_check_false(a7.appDisabled); + do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(t1, null); + do_check_false(t1.isActive); + do_check_true(t1.userDisabled); + do_check_false(t1.appDisabled); + do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); + + do_check_neq(t2, null); + do_check_true(t2.isActive); + do_check_false(t2.userDisabled); + do_check_false(t2.appDisabled); + do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); + + end_test(); + }); }); }); }); diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini index 20ba274517b..9b889bf86e0 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini @@ -180,6 +180,7 @@ skip-if = os == "android" skip-if = os == "android" [test_locale.js] [test_locked.js] +[test_locked2.js] [test_locked_strictcompat.js] [test_manifest.js] [test_migrate1.js]