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;
}
/**
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)
diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm
index 66c150a504ee..a585f316cba7 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
@@ -2749,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 ?
@@ -2858,8 +2850,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 +2873,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;
},
@@ -2943,14 +2938,18 @@ 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
// 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 +2957,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 +3008,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..db87bf2f5d2f 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");
}
}
@@ -523,10 +534,10 @@ var XPIDatabase = {
}
this.initialized = true;
+ this.migrateData = null;
this.connection = this.openDatabaseFile(this.dbfile);
- let 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,31 @@ 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();
+ 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
+ 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 +610,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;
},
/**
@@ -634,14 +643,22 @@ 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);
- let parser = iniFactory.createINIParser(addonsList);
+ if (!addonsList.exists())
+ return null;
- let keys = parser.getKeys("ExtensionDirs");
+ try {
+ 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()));
+ while (keys.hasMore())
+ bundles.push(parser.getString("ExtensionDirs", keys.getNext()));
+ }
+ catch (e) {
+ WARN("Failed to parse extensions.ini", e);
+ return null;
+ }
// Also include the list of active bootstrapped extensions
for (let id in XPIProvider.bootstrappedAddons)
@@ -657,17 +674,22 @@ 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()) {
- LOG("Migrating data from extensions.rdf");
+ if (!rdffile.exists())
+ return null;
+
+ LOG("Migrating data from " + FILE_OLD_DATABASE);
+ let migrateData = {};
+
+ 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();
+
while (elements.hasMoreElements()) {
let source = elements.getNext().QueryInterface(Ci.nsIRDFResource);
@@ -709,6 +731,10 @@ var XPIDatabase = {
}
}
}
+ catch (e) {
+ WARN("Error reading " + FILE_OLD_DATABASE, e);
+ migrateData = null;
+ }
return migrateData;
},
@@ -747,7 +773,7 @@ var XPIDatabase = {
if (reqCount < REQUIRED.length) {
ERROR("Unable to read anything useful from the database");
- return migrateData;
+ return null;
}
stmt.finalize();
@@ -790,6 +816,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 +845,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]