From 0f75d25e1a1f95ceb6aa55b0104712274bac7337 Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Mon, 24 Sep 2012 15:25:33 +0300 Subject: [PATCH 1/4] Bug 790650 - It may be a good idea to have the debugger start with collapsed panels, r=past --- .../devtools/debugger/debugger-controller.js | 3 +- browser/devtools/debugger/debugger-view.js | 108 ++++++++++++++---- .../test/browser_dbg_pane-collapse.js | 44 +++++-- .../themes/gnomestripe/devtools/debugger.css | 2 +- .../themes/pinstripe/devtools/debugger.css | 2 +- .../themes/winstripe/devtools/debugger.css | 2 +- 6 files changed, 123 insertions(+), 38 deletions(-) diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index 171cd0cb798a..ec12bab1d18f 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -60,7 +60,8 @@ let DebuggerController = { DebuggerView.StackFrames.initialize(); DebuggerView.Breakpoints.initialize(); DebuggerView.Properties.initialize(); - DebuggerView.showCloseButton(!this._isRemoteDebugger && !this._isChromeDebugger); + DebuggerView.toggleCloseButton(!this._isRemoteDebugger && + !this._isChromeDebugger); this.dispatchEvent("Debugger:Loaded"); this._connect(); diff --git a/browser/devtools/debugger/debugger-view.js b/browser/devtools/debugger/debugger-view.js index d082ef0162f6..79b254be1217 100644 --- a/browser/devtools/debugger/debugger-view.js +++ b/browser/devtools/debugger/debugger-view.js @@ -6,6 +6,7 @@ "use strict"; const BREAKPOINT_LINE_TOOLTIP_MAX_SIZE = 1000; // chars +const PANES_APPEARANCE_DELAY = 50; // ms const PROPERTY_VIEW_FLASH_DURATION = 400; // ms const GLOBAL_SEARCH_MATCH_FLASH_DURATION = 100; // ms const GLOBAL_SEARCH_URL_MAX_SIZE = 100; // chars @@ -103,8 +104,8 @@ let DebuggerView = { this._stackframesAndBreakpoints.setAttribute("width", Prefs.stackframesWidth); this._variables.setAttribute("width", Prefs.variablesWidth); - this.showStackframesAndBreakpointsPane(Prefs.stackframesPaneVisible); - this.showVariablesPane(Prefs.variablesPaneVisible); + this.toggleStackframesAndBreakpointsPane({ silent: true }); + this.toggleVariablesPane({ silent: true }); }, /** @@ -173,34 +174,42 @@ let DebuggerView = { * Called when the panes toggle button is clicked. */ _onTogglePanesButtonPressed: function DV__onTogglePanesButtonPressed() { - this.showStackframesAndBreakpointsPane( - this._togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), true); - - this.showVariablesPane( - this._togglePanesButton.getAttribute("variablesHidden"), true); + this.toggleStackframesAndBreakpointsPane({ + visible: !!this._togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), + animated: true + }); + this.toggleVariablesPane({ + visible: !!this._togglePanesButton.getAttribute("variablesHidden"), + animated: true + }); + this._onPanesToggle(); }, /** * Sets the close button hidden or visible. It's hidden by default. * @param boolean aVisibleFlag */ - showCloseButton: function DV_showCloseButton(aVisibleFlag) { + toggleCloseButton: function DV_toggleCloseButton(aVisibleFlag) { document.getElementById("close").setAttribute("hidden", !aVisibleFlag); }, /** * Sets the stackframes and breakpoints pane hidden or visible. - * @param boolean aVisibleFlag - * @param boolean aAnimatedFlag + * + * @param object aFlags [optional] + * An object containing some of the following booleans: + * - visible: true if the pane should be shown, false for hidden + * - animated: true to display an animation on toggle + * - silent: true to not update any designated prefs */ - showStackframesAndBreakpointsPane: - function DV_showStackframesAndBreakpointsPane(aVisibleFlag, aAnimatedFlag) { - if (aAnimatedFlag) { + toggleStackframesAndBreakpointsPane: + function DV_toggleStackframesAndBreakpointsPane(aFlags = {}) { + if (aFlags.animated) { this._stackframesAndBreakpoints.setAttribute("animated", ""); } else { this._stackframesAndBreakpoints.removeAttribute("animated"); } - if (aVisibleFlag) { + if (aFlags.visible) { this._stackframesAndBreakpoints.style.marginLeft = "0"; this._togglePanesButton.removeAttribute("stackframesAndBreakpointsHidden"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes")); @@ -210,22 +219,28 @@ let DebuggerView = { this._togglePanesButton.setAttribute("stackframesAndBreakpointsHidden", "true"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes")); } - Prefs.stackframesPaneVisible = aVisibleFlag; + if (!aFlags.silent) { + Prefs.stackframesPaneVisible = !!aFlags.visible; + } }, /** * Sets the variable spane hidden or visible. - * @param boolean aVisibleFlag - * @param boolean aAnimatedFlag + * + * @param object aFlags [optional] + * An object containing some of the following booleans: + * - visible: true if the pane should be shown, false for hidden + * - animated: true to display an animation on toggle + * - silent: true to not update any designated prefs */ - showVariablesPane: - function DV_showVariablesPane(aVisibleFlag, aAnimatedFlag) { - if (aAnimatedFlag) { + toggleVariablesPane: + function DV_toggleVariablesPane(aFlags = {}) { + if (aFlags.animated) { this._variables.setAttribute("animated", ""); } else { this._variables.removeAttribute("animated"); } - if (aVisibleFlag) { + if (aFlags.visible) { this._variables.style.marginRight = "0"; this._togglePanesButton.removeAttribute("variablesHidden"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes")); @@ -235,7 +250,54 @@ let DebuggerView = { this._togglePanesButton.setAttribute("variablesHidden", "true"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes")); } - Prefs.variablesPaneVisible = aVisibleFlag; + if (!aFlags.silent) { + Prefs.variablesPaneVisible = !!aFlags.visible; + } + }, + + /** + * Shows the stackframes, breakpoints and variable panes if currently hidden + * and the preferences dictate otherwise. + */ + showPanesIfAllowed: function DV_showPanesIfAllowed() { + // Try to keep animations as smooth as possible, so wait a few cycles. + window.setTimeout(function() { + let shown; + + if (Prefs.stackframesPaneVisible && + this._togglePanesButton.getAttribute("stackframesAndBreakpointsHidden")) { + this.toggleStackframesAndBreakpointsPane({ + visible: true, + animated: true, + silent: true + }); + shown = true; + } + if (Prefs.variablesPaneVisible && + this._togglePanesButton.getAttribute("variablesHidden")) { + this.toggleVariablesPane({ + visible: true, + animated: true, + silent: true + }); + shown = true; + } + if (shown) { + this._onPanesToggle(); + } + }.bind(this), PANES_APPEARANCE_DELAY); + }, + + /** + * Displaying the panes may have the effect of triggering scrollbars to + * appear in the source editor, which would render the currently highlighted + * line to appear behind them in some cases. + */ + _onPanesToggle: function DV__onPanesToggle() { + document.addEventListener("transitionend", function onEvent() { + document.removeEventListener("transitionend", onEvent); + DebuggerController.StackFrames.updateEditorLocation(); + }); }, /** @@ -1625,6 +1687,8 @@ StackFramesView.prototype = { if (document.getElementById("stackframe-" + aDepth)) { return null; } + // Stackframes are UI elements which benefit from visible panes. + DebuggerView.showPanesIfAllowed(); let frame = document.createElement("box"); let frameName = document.createElement("label"); diff --git a/browser/devtools/debugger/test/browser_dbg_pane-collapse.js b/browser/devtools/debugger/test/browser_dbg_pane-collapse.js index cb8aee13b5f5..ee6e92b9a4c7 100644 --- a/browser/devtools/debugger/test/browser_dbg_pane-collapse.js +++ b/browser/devtools/debugger/test/browser_dbg_pane-collapse.js @@ -19,12 +19,32 @@ function test() { gDebugger = gPane.contentWindow; gView = gDebugger.DebuggerView; + testPanesState(); + + gView.toggleStackframesAndBreakpointsPane({ visible: true }); + gView.toggleVariablesPane({ visible: true }); testPaneCollapse1(); testPaneCollapse2(); + closeDebuggerAndFinish(); }); } +function testPanesState() { + let togglePanesButton = + gDebugger.document.getElementById("toggle-panes"); + + ok(togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), + "The stackframes and breakpoints pane should initially be invisible."); + is(gDebugger.Prefs.stackframesPaneVisible, true, + "The stackframes and breakpoints pane should initially be preffed as visible."); + + ok(togglePanesButton.getAttribute("variablesHidden"), + "The stackframes and breakpoints pane should initially be invisible."); + is(gDebugger.Prefs.variablesPaneVisible, true, + "The stackframes and breakpoints pane should initially be preffed as visible."); +} + function testPaneCollapse1() { let stackframesAndBrekpoints = gDebugger.document.getElementById("stackframes+breakpoints"); @@ -35,16 +55,16 @@ function testPaneCollapse1() { is(width, gDebugger.Prefs.stackframesWidth, "The stackframes and breakpoints pane has an incorrect width."); is(stackframesAndBrekpoints.style.marginLeft, "0px", - "The stackframes and breakpoints pane has an incorrect initial left margin."); + "The stackframes and breakpoints pane has an incorrect left margin."); ok(!stackframesAndBrekpoints.hasAttribute("animated"), - "The stackframes and breakpoints pane has an incorrect initial animated attribute."); + "The stackframes and breakpoints pane has an incorrect animated attribute."); ok(!togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), - "The stackframes and breakpoints pane should initially be visible."); + "The stackframes and breakpoints pane should at this point be visible."); is(gDebugger.Prefs.stackframesPaneVisible, true, - "The stackframes and breakpoints pane should initially be visible."); + "The stackframes and breakpoints pane should at this point be visible."); - gView.showStackframesAndBreakpointsPane(false, true); + gView.toggleStackframesAndBreakpointsPane({ visible: false, animated: true }); is(gDebugger.Prefs.stackframesPaneVisible, false, "The stackframes and breakpoints pane should be hidden after collapsing."); @@ -62,7 +82,7 @@ function testPaneCollapse1() { is(gDebugger.Prefs.stackframesPaneVisible, false, "The stackframes and breakpoints pane should be hidden before uncollapsing."); - gView.showStackframesAndBreakpointsPane(true, false); + gView.toggleStackframesAndBreakpointsPane({ visible: true, animated: false }); is(gDebugger.Prefs.stackframesPaneVisible, true, "The stackframes and breakpoints pane should be visible after uncollapsing."); @@ -87,16 +107,16 @@ function testPaneCollapse2() { is(width, gDebugger.Prefs.variablesWidth, "The variables pane has an incorrect width."); is(variables.style.marginRight, "0px", - "The variables pane has an incorrect initial right margin."); + "The variables pane has an incorrect right margin."); ok(!variables.hasAttribute("animated"), - "The variables pane has an incorrect initial animated attribute."); + "The variables pane has an incorrect animated attribute."); ok(!togglePanesButton.getAttribute("variablesHidden"), - "The variables pane should initially be visible."); + "The variables pane should at this point be visible."); is(gDebugger.Prefs.variablesPaneVisible, true, - "The variables pane should initially be visible."); + "The variables pane should at this point be visible."); - gView.showVariablesPane(false, true); + gView.toggleVariablesPane({ visible: false, animated: true }); is(gDebugger.Prefs.variablesPaneVisible, false, "The variables pane should be hidden after collapsing."); @@ -114,7 +134,7 @@ function testPaneCollapse2() { is(gDebugger.Prefs.variablesPaneVisible, false, "The variables pane should be hidden before uncollapsing."); - gView.showVariablesPane(true, false); + gView.toggleVariablesPane({ visible: true, animated: false }); is(gDebugger.Prefs.variablesPaneVisible, true, "The variables pane should be visible after uncollapsing."); diff --git a/browser/themes/gnomestripe/devtools/debugger.css b/browser/themes/gnomestripe/devtools/debugger.css index 52f1dc901f9b..142f2a7dfdf5 100644 --- a/browser/themes/gnomestripe/devtools/debugger.css +++ b/browser/themes/gnomestripe/devtools/debugger.css @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #body { - background: -moz-dialog; + background-color: white; } /** diff --git a/browser/themes/pinstripe/devtools/debugger.css b/browser/themes/pinstripe/devtools/debugger.css index fc62901d439f..4de928e9c372 100644 --- a/browser/themes/pinstripe/devtools/debugger.css +++ b/browser/themes/pinstripe/devtools/debugger.css @@ -7,7 +7,7 @@ %include ../shared.inc #body { - background: -moz-dialog; + background-color: white; } /** diff --git a/browser/themes/winstripe/devtools/debugger.css b/browser/themes/winstripe/devtools/debugger.css index c46a1fc6799d..0eeb445e4f6e 100644 --- a/browser/themes/winstripe/devtools/debugger.css +++ b/browser/themes/winstripe/devtools/debugger.css @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #body { - background: -moz-dialog; + background-color: white; } /** From 11d67761721724a2311cdf5ea50a046d4d4bb98d Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Thu, 10 May 2012 12:49:36 -0700 Subject: [PATCH 2/4] Bug 671894 - Part 1: Add-ons Manager doesn't recover correctly in some cases when the database is locked. r=Unfocused --- toolkit/mozapps/extensions/XPIProvider.jsm | 49 ++-- .../mozapps/extensions/XPIProviderUtils.js | 172 ++++++----- .../test/addons/test_locked2_5/install.rdf | 23 ++ .../test/addons/test_locked2_6/install.rdf | 23 ++ .../extensions/test/xpcshell/test_locked.js | 130 ++++++++- .../extensions/test/xpcshell/test_locked2.js | 272 ++++++++++++++++++ .../test/xpcshell/test_locked_strictcompat.js | 130 ++++++++- .../extensions/test/xpcshell/xpcshell.ini | 1 + 8 files changed, 680 insertions(+), 120 deletions(-) create mode 100644 toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf create mode 100644 toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf create mode 100644 toolkit/mozapps/extensions/test/xpcshell/test_locked2.js diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index 66c150a504ee..bbf03dac1ab2 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 3c9cfede416f..aa9fb95f410c 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 000000000000..09655c2a673c --- /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 000000000000..75f110d2a1cc --- /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 c81613733fdb..dd00999031a0 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 000000000000..7bf65ba4fdde --- /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 12555c8bba0f..f24358d02b76 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 20ba274517bd..9b889bf86e0a 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] From d93057561e26686ef4d473f78ff32af902a09bfa Mon Sep 17 00:00:00 2001 From: Blair McBride Date: Tue, 25 Sep 2012 16:34:49 +1200 Subject: [PATCH 3/4] Bug 671894 - Part 2: Add-ons Manager doesn't recover correctly in some cases when the database is locked. r=Mossop --- toolkit/mozapps/extensions/XPIProvider.jsm | 15 ++- .../mozapps/extensions/XPIProviderUtils.js | 114 ++++++++++-------- 2 files changed, 75 insertions(+), 54 deletions(-) diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index bbf03dac1ab2..a585f316cba7 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -2740,9 +2740,10 @@ var XPIProvider = { let oldBootstrap = oldBootstrappedAddons[newAddon.id]; XPIProvider.bootstrappedAddons[newAddon.id] = oldBootstrap; - // If the old version is the same as the new version, don't call - // uninstall and install methods. - if (sameVersion) + // If the old version is the same as the new version, or we're + // recovering from a corrupt DB, don't call uninstall and install + // methods. + if (sameVersion || !isNewInstall) return false; installReason = Services.vc.compare(oldBootstrap.version, newAddon.version) < 0 ? @@ -2937,8 +2938,12 @@ var XPIProvider = { // Load the list of bootstrapped add-ons first so processFileChanges can // modify it - this.bootstrappedAddons = JSON.parse(Prefs.getCharPref(PREF_BOOTSTRAP_ADDONS, - "{}")); + try { + this.bootstrappedAddons = JSON.parse(Prefs.getCharPref(PREF_BOOTSTRAP_ADDONS, + "{}")); + } catch (e) { + WARN("Error parsing enabled bootstrapped extensions cache", e); + } // First install any new add-ons into the locations, if there are any // changes then we must update the database with the information in the diff --git a/toolkit/mozapps/extensions/XPIProviderUtils.js b/toolkit/mozapps/extensions/XPIProviderUtils.js index aa9fb95f410c..db87bf2f5d2f 100644 --- a/toolkit/mozapps/extensions/XPIProviderUtils.js +++ b/toolkit/mozapps/extensions/XPIProviderUtils.js @@ -534,10 +534,10 @@ var XPIDatabase = { } this.initialized = true; + this.migrateData = null; this.connection = this.openDatabaseFile(this.dbfile); - 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; @@ -578,7 +578,14 @@ var XPIDatabase = { } // At this point the database should be completely empty - this.createSchema(); + try { + this.createSchema(); + } + catch (e) { + // If creating the schema fails, then the database is unusable, + // fall back to an in-memory database. + this.connection = Services.storage.openSpecialDatabase("memory"); + } // If there is no migration data then load the list of add-on directories // that were active during the last run @@ -636,22 +643,23 @@ var XPIDatabase = { let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST], true); - let iniFactory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. - getService(Ci.nsIINIParserFactory); + if (!addonsList.exists()) + return null; try { - var parser = iniFactory.createINIParser(addonsList); + let iniFactory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"] + .getService(Ci.nsIINIParserFactory); + let parser = iniFactory.createINIParser(addonsList); + let keys = parser.getKeys("ExtensionDirs"); + + while (keys.hasMore()) + bundles.push(parser.getString("ExtensionDirs", keys.getNext())); } catch (e) { WARN("Failed to parse extensions.ini", e); return null; } - let keys = parser.getKeys("ExtensionDirs"); - - while (keys.hasMore()) - bundles.push(parser.getString("ExtensionDirs", keys.getNext())); - // Also include the list of active bootstrapped extensions for (let id in XPIProvider.bootstrappedAddons) bundles.push(XPIProvider.bootstrappedAddons[id].descriptor); @@ -666,59 +674,67 @@ var XPIDatabase = { * userDisabled and any updated compatibility information */ getMigrateDataFromRDF: function XPIDB_getMigrateDataFromRDF(aDbWasMissing) { - let migrateData = {}; // Migrate data from extensions.rdf let rdffile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_DATABASE], true); if (!rdffile.exists()) return null; - 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); + LOG("Migrating data from " + FILE_OLD_DATABASE); + let migrateData = {}; - 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: [] - } + try { + 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(); - let disabled = getRDFProperty(ds, source, "userDisabled"); - if (disabled == "true" || disabled == "needs-disable") - migrateData[location][id].userDisabled = true; + while (elements.hasMoreElements()) { + let source = elements.getNext().QueryInterface(Ci.nsIRDFResource); - 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 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: [] } - else { - appInfo.minVersion = getRDFProperty(ds, targetApp, "minVersion"); - appInfo.maxVersion = getRDFProperty(ds, targetApp, "maxVersion"); + + 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); } - migrateData[location][id].targetApplications.push(appInfo); } } } + catch (e) { + WARN("Error reading " + FILE_OLD_DATABASE, e); + migrateData = null; + } return migrateData; }, From 8df33f40816f911553761abadf7bb528fad3c7f8 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Tue, 25 Sep 2012 15:05:15 +0100 Subject: [PATCH 4/4] Bug 793584 - Update Makefile to use i386-redhat-linux-gnu.sup too; rs=ted DONTBUILD since NPOTDB --- build/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Makefile.in b/build/Makefile.in index ea19bbeb8c10..67121b8839d2 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -150,7 +150,7 @@ _VALGRIND_DIR = $(DEPTH)/_valgrind GARBAGE_DIRS += $(_VALGRIND_DIR) _VALGRIND_FILES = \ - $(topsrcdir)/build/valgrind/i686-redhat-linux-gnu.sup \ + $(topsrcdir)/build/valgrind/i386-redhat-linux-gnu.sup \ $(topsrcdir)/build/valgrind/x86_64-redhat-linux-gnu.sup \ $(NULL)