merge m-c to fx-team
authorTim Taubert <ttaubert@mozilla.com>
Tue, 25 Sep 2012 09:48:00 +0200
changeset 114224 08d435dedc7fc19bfad3d31c62daec9013525c6d
parent 114220 654489f4be25f14be4a92e2ff26e650364558bc8 (current diff)
parent 114223 d3089bef49eaba3245d8b4c27ea386926cc0179e (diff)
child 114282 7c7639f797d0299c1d1c74c162e5b5721550422b
child 114330 38a87696db4f76b5c71736c086653ee9a3ad196a
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone18.0a1
first release with
nightly linux32
08d435dedc7f / 18.0a1 / 20120925030547 / files
nightly linux64
08d435dedc7f / 18.0a1 / 20120925030547 / files
nightly mac
08d435dedc7f / 18.0a1 / 20120925030547 / files
nightly win32
08d435dedc7f / 18.0a1 / 20120925030547 / files
nightly win64
08d435dedc7f / 18.0a1 / 20120925030547 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge m-c to fx-team
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -55,17 +55,18 @@ let DebuggerController = {
     DebuggerView.initializeKeys();
     DebuggerView.initializePanes();
     DebuggerView.initializeEditor(function() {
       DebuggerView.GlobalSearch.initialize();
       DebuggerView.Scripts.initialize();
       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();
     }.bind(this));
   },
 
   /**
    * Destroys the debugger view, disconnects the debugger client and cleans up
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -1,16 +1,17 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "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
 const GLOBAL_SEARCH_LINE_MAX_SIZE = 300; // chars
 const GLOBAL_SEARCH_ACTION_DELAY = 150; // ms
 
 const SEARCH_GLOBAL_FLAG = "!";
 const SEARCH_LINE_FLAG = ":";
@@ -98,18 +99,18 @@ let DebuggerView = {
    * Initializes UI properties for all the displayed panes.
    */
   initializePanes: function DV_initializePanes() {
     this._togglePanesButton.addEventListener("click", this._onTogglePanesButtonPressed);
 
     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 });
   },
 
   /**
    * Initializes the SourceEditor instance.
    *
    * @param function aCallback
    *        Called after the editor finishes initializing.
    */
@@ -168,79 +169,140 @@ let DebuggerView = {
     DebuggerController.Breakpoints.initialize();
     this.editor.focus();
   },
 
   /**
    * 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"));
     } else {
       let margin = parseInt(this._stackframesAndBreakpoints.getAttribute("width")) + 1;
       this._stackframesAndBreakpoints.style.marginLeft = -margin + "px";
       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"));
     } else {
       let margin = parseInt(this._variables.getAttribute("width")) + 1;
       this._variables.style.marginRight = -margin + "px";
       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();
+    });
   },
 
   /**
    * The cached global view elements.
    */
   _togglePanesButton: null,
   _stackframesAndBreakpoints: null,
   _stackframes: null,
@@ -1620,16 +1682,18 @@ StackFramesView.prototype = {
    * @return object
    *         The newly created html node representing the added frame.
    */
   addFrame: function DVF_addFrame(aDepth, aFrameNameText, aFrameDetailsText) {
     // Make sure we don't duplicate anything.
     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");
     let frameDetails = document.createElement("label");
 
     // Create a list item to be added to the stackframes container.
     frame.id = "stackframe-" + aDepth;
     frame.className = "dbg-stackframe list-item";
--- a/browser/devtools/debugger/test/browser_dbg_pane-collapse.js
+++ b/browser/devtools/debugger/test/browser_dbg_pane-collapse.js
@@ -14,42 +14,62 @@ var gView = null;
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gDebuggee = aDebuggee;
     gPane = aPane;
     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");
   let togglePanesButton =
     gDebugger.document.getElementById("toggle-panes");
 
   let width = parseInt(stackframesAndBrekpoints.getAttribute("width"));
   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.");
 
   let margin = -(width + 1) + "px";
   is(width, gDebugger.Prefs.stackframesWidth,
     "The stackframes and breakpoints pane has an incorrect width after collapsing.");
   is(stackframesAndBrekpoints.style.marginLeft, margin,
@@ -57,17 +77,17 @@ function testPaneCollapse1() {
   ok(stackframesAndBrekpoints.hasAttribute("animated"),
     "The stackframes and breakpoints pane has an incorrect attribute after an animated collapsing.");
   ok(togglePanesButton.hasAttribute("stackframesAndBreakpointsHidden"),
     "The stackframes and breakpoints pane should not be visible after collapsing.");
 
   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.");
 
   is(width, gDebugger.Prefs.stackframesWidth,
     "The stackframes and breakpoints pane has an incorrect width after uncollapsing.");
   is(stackframesAndBrekpoints.style.marginLeft, "0px",
     "The stackframes and breakpoints pane has an incorrect left margin after uncollapsing.");
@@ -82,26 +102,26 @@ function testPaneCollapse2() {
     gDebugger.document.getElementById("variables");
   let togglePanesButton =
     gDebugger.document.getElementById("toggle-panes");
 
   let width = parseInt(variables.getAttribute("width"));
   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.");
 
   let margin = -(width + 1) + "px";
   is(width, gDebugger.Prefs.variablesWidth,
     "The variables pane has an incorrect width after collapsing.");
   is(variables.style.marginRight, margin,
@@ -109,17 +129,17 @@ function testPaneCollapse2() {
   ok(variables.hasAttribute("animated"),
     "The variables pane has an incorrect attribute after an animated collapsing.");
   ok(togglePanesButton.hasAttribute("variablesHidden"),
     "The variables pane should not be visible after collapsing.");
 
   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.");
 
   is(width, gDebugger.Prefs.variablesWidth,
     "The variables pane has an incorrect width after uncollapsing.");
   is(variables.style.marginRight, "0px",
     "The variables pane has an incorrect right margin after uncollapsing.");
--- a/browser/themes/gnomestripe/devtools/debugger.css
+++ b/browser/themes/gnomestripe/devtools/debugger.css
@@ -1,16 +1,16 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #body {
-  background: -moz-dialog;
+  background-color: white;
 }
 
 /**
  * Debugger content
  */
 
 #dbg-content {
   padding: 0;
--- a/browser/themes/pinstripe/devtools/debugger.css
+++ b/browser/themes/pinstripe/devtools/debugger.css
@@ -2,17 +2,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../shared.inc
 
 #body {
-  background: -moz-dialog;
+  background-color: white;
 }
 
 /**
  * Debugger content
  */
 
 #dbg-content {
   padding: 0;
--- a/browser/themes/winstripe/devtools/debugger.css
+++ b/browser/themes/winstripe/devtools/debugger.css
@@ -1,16 +1,16 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #body {
-  background: -moz-dialog;
+  background-color: white;
 }
 
 /**
  * Debugger content
  */
 
 #dbg-content {
   padding: 0;
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -2254,33 +2254,23 @@ var XPIProvider = {
    *         true to update add-ons appDisabled property when the application
    *         version has changed
    * @param  aOldAppVersion
    *         The version of the application last run with this profile or null
    *         if it is a new profile or the version is unknown
    * @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 = {};
 
     /**
      * Updates an add-on's metadata and determines if a restart of the
      * application is necessary. This is called when either the add-on's
      * install directory path or last modified time has changed.
@@ -2596,20 +2586,21 @@ var XPIProvider = {
 
       let newAddon = null;
       let sameVersion = false;
       // Check the updated manifests lists for the install location, If there
       // is no manifest for the add-on ID then newAddon will be undefined
       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;
 
       // Load the manifest if necessary and sanity check the add-on ID
       try {
         if (!newAddon) {
           // Load the manifest from the add-on.
@@ -2679,24 +2670,24 @@ var XPIProvider = {
       if (isDetectedInstall && newAddon.foreignInstall) {
         // If the add-on is a foreign install and is in a scope where add-ons
         // that were dropped in should default to disabled then disable it
         let disablingScopes = Prefs.getIntPref(PREF_EM_AUTO_DISABLED_SCOPES, 0);
         if (aInstallLocation.scope & disablingScopes)
           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
         if (!newAddon.active && newAddon.visible && !isAddonDisabled(newAddon)) {
           // If the add-on is softblocked then assume it is softDisabled
           if (newAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
             newAddon.softDisabled = true;
           else
@@ -2744,19 +2735,20 @@ var XPIProvider = {
 
         let installReason = BOOTSTRAP_REASONS.ADDON_INSTALL;
 
         // If we're hiding a bootstrapped add-on then call its uninstall method
         if (newAddon.id in oldBootstrappedAddons) {
           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 ?
                           BOOTSTRAP_REASONS.ADDON_UPGRADE :
                           BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
 
           let oldAddonFile = Cc["@mozilla.org/file/local;1"].
                              createInstance(Ci.nsIFile);
@@ -2853,18 +2845,18 @@ var XPIProvider = {
           }
         }, this);
       }
 
       // All the remaining add-ons in this install location must be new.
 
       // 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;
       }
     }, this);
 
     // The remaining locations that had add-ons installed in them no longer
     // have any add-ons installed in them, or the locations no longer exist.
@@ -2876,16 +2868,19 @@ var XPIProvider = {
         changed = removeMetadata(aLocation, aOldAddon) || changed;
       }, this);
     }, this);
 
     // Cache the new install location states
     let cache = JSON.stringify(this.getInstallLocationStates());
     Services.prefs.setCharPref(PREF_INSTALL_CACHE, cache);
 
+    // Clear out any cached migration data.
+    XPIDatabase.migrateData = null;
+
     return changed;
   },
 
   /**
    * Imports the xpinstall permissions from preferences into the permissions
    * manager for the user to change later.
    */
   importPermissions: function XPI_importPermissions() {
@@ -2938,44 +2933,48 @@ var XPIProvider = {
       this.importPermissions();
 
     // If the application version has changed then the database information
     // needs to be updated
     let updateDatabase = aAppChanged;
 
     // 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
     // to the application crashing)
     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
     // must update the database however if there are no add-ons then there is
     // no need to update the database.
     // Avoid using XPIDatabase.dbFileExists, as that code is lazy-loaded,
     // and we want to avoid loading it until absolutely necessary.
     let dbFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
@@ -3004,24 +3003,23 @@ var XPIProvider = {
     let transationBegun = false;
     try {
       let extensionListChanged = false;
       // If the database needs to be updated then open it and then update it
       // from the filesystem
       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);
         }
       }
 
       if (aAppChanged) {
         // When upgrading the app and using a custom skin make sure it is still
--- a/toolkit/mozapps/extensions/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/XPIProviderUtils.js
@@ -304,16 +304,20 @@ var XPIDatabase = {
   statementCache: {},
   // A cache of weak referenced DBAddonInternals so we can reuse objects where
   // possible
   addonCache: [],
   // The nested transaction count
   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: {
     _getDefaultLocale: "SELECT id, name, description, creator, homepageURL " +
                        "FROM locale WHERE id=:id",
     _getLocales: "SELECT addon_locale.locale, locale.id, locale.name, " +
                  "locale.description, locale.creator, locale.homepageURL " +
                  "FROM addon_locale JOIN locale ON " +
@@ -475,31 +479,38 @@ var XPIDatabase = {
 
     // Attempt to open the database
     try {
       connection = Services.storage.openUnsharedDatabase(aDBFile);
       this.dbfileExists = true;
     }
     catch (e) {
       ERROR("Failed to open database (1st attempt)", e);
-      try {
-        aDBFile.remove(true);
-      }
-      catch (e) {
-        ERROR("Failed to remove database that could not be opened", e);
+      // 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");
+        }
       }
-      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");
       }
     }
 
     connection.executeSimpleSQL("PRAGMA synchronous = FULL");
     connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE");
 
     return connection;
@@ -518,30 +529,30 @@ var XPIDatabase = {
     delete this.connection;
 
     if (!aForceOpen && !this.dbfileExists) {
       this.connection = null;
       return {};
     }
 
     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;
     if (schemaVersion != DB_SCHEMA) {
       // A non-zero schema version means that a schema has been successfully
       // created in the database in the past so we might be able to get useful
       // 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();
         try {
           if (this.dbfileExists)
             this.dbfile.remove(true);
 
           // Reopen an empty database
@@ -557,64 +568,62 @@ var XPIDatabase = {
       else {
         let dbSchema = 0;
         try {
           dbSchema = Services.prefs.getIntPref(PREF_DB_SCHEMA);
         } catch (e) {}
 
         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();
         }
         catch (e) {
           ERROR("Error processing file changes", e);
           this.rollbackTransaction();
         }
       }
     }
 
     // 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;
   },
 
   /**
    * A lazy getter for the database connection.
    */
   get connection() {
     this.openConnection(true);
     return this.connection;
@@ -629,50 +638,63 @@ var XPIDatabase = {
    * @return an array of persisitent descriptors for the directories
    */
   getActiveBundles: function XPIDB_getActiveBundles() {
     let bundles = [];
 
     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)
       bundles.push(XPIProvider.bootstrappedAddons[id].descriptor);
 
     return bundles;
   },
 
   /**
    * Retrieves migration data from the old extensions.rdf database.
    *
    * @return an object holding information about what add-ons were previously
    *         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);
 
         let location = getRDFProperty(ds, source, "installLocation");
         if (location) {
           if (!(location in migrateData))
             migrateData[location] = {};
           let id = source.ValueUTF8.substring(PREFIX_ITEM_URI.length);
@@ -704,16 +726,20 @@ var XPIDatabase = {
               appInfo.minVersion = getRDFProperty(ds, targetApp, "minVersion");
               appInfo.maxVersion = getRDFProperty(ds, targetApp, "maxVersion");
             }
             migrateData[location][id].targetApplications.push(appInfo);
           }
         }
       }
     }
+    catch (e) {
+      WARN("Error reading " + FILE_OLD_DATABASE, e);
+      migrateData = null;
+    }
 
     return migrateData;
   },
 
   /**
    * Retrieves migration data from a database that has an older or newer schema.
    *
    * @return an object holding information about what add-ons were previously
@@ -742,17 +768,17 @@ var XPIDatabase = {
         }
         else if (DB_BOOL_METADATA.indexOf(row.name) != -1) {
           props.push(row.name);
         }
       }
 
       if (reqCount < REQUIRED.length) {
         ERROR("Unable to read anything useful from the database");
-        return migrateData;
+        return null;
       }
       stmt.finalize();
 
       stmt = this.connection.createStatement("SELECT " + props.join(",") + " FROM addon");
       for (let row in resultRows(stmt)) {
         if (!(row.location in migrateData))
           migrateData[row.location] = {};
         let addonData = {
@@ -785,16 +811,17 @@ 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)
         taStmt.finalize();
       if (stmt)
         stmt.finalize();
     }
 
@@ -813,16 +840,21 @@ var XPIDatabase = {
       this.addonCache = [];
 
       if (this.transactionCount > 0) {
         ERROR(this.transactionCount + " outstanding transactions, rolling back.");
         while (this.transactionCount > 0)
           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;
 
       // Re-create the connection smart getter to allow the database to be
       // re-loaded during testing.
       this.__defineGetter__("connection", function() {
         this.openConnection(true);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon5@tests.mozilla.org</em:id>
+    <em:version>2.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 5</em:name>
+    <em:description>Test Description</em:description>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>2</em:minVersion>
+        <em:maxVersion>2</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon6@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 6</em:name>
+    <em:description>Test Description</em:description>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>2</em:minVersion>
+        <em:maxVersion>2</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
--- a/toolkit/mozapps/extensions/test/xpcshell/test_locked.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked.js
@@ -147,16 +147,19 @@ function run_test() {
   testserver = new HttpServer();
   testserver.registerDirectory("/addons/", do_get_file("addons"));
   testserver.registerDirectory("/data/", do_get_file("data"));
   testserver.start(4444);
 
   // 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",
                                "addon7@tests.mozilla.org",
                                "theme2@tests.mozilla.org"], function([a2, a3, a4,
                                                                       a7, t2]) {
     // Set up the initial state
     a2.userDisabled = true;
@@ -193,40 +196,45 @@ function run_test_1() {
                                "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);
@@ -235,30 +243,40 @@ function run_test_1() {
     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));
 
-    // 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",
                                  "addon2@tests.mozilla.org",
                                  "addon3@tests.mozilla.org",
                                  "addon4@tests.mozilla.org",
                                  "addon5@tests.mozilla.org",
                                  "addon6@tests.mozilla.org",
@@ -268,47 +286,52 @@ function run_test_1() {
                                                                         a4, a5, a6,
                                                                         a7, t1, t2]) {
       // Should be correctly recovered
       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));
 
       // Should be correctly recovered
       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));
 
       // The compatibility update won't be recovered but it should still be
       // active for this session
       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));
 
       // The compatibility update won't be recovered and with strict
       // compatibility it would not have been able to tell that it was
       // previously userDisabled. However, without strict compat, it wasn't
       // appDisabled, so it knows it must have been userDisabled.
       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);
@@ -318,69 +341,79 @@ function run_test_1() {
       do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
 
       // Should be correctly recovered
       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));
 
       // Should be correctly recovered
       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));
 
       // 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",
                                    "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);
@@ -389,21 +422,104 @@ function run_test_1() {
         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));
 
-        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();
+        });
       });
     });
   });
 }
new file mode 100644
--- /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();
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js
@@ -147,16 +147,19 @@ function run_test() {
   testserver = new HttpServer();
   testserver.registerDirectory("/addons/", do_get_file("addons"));
   testserver.registerDirectory("/data/", do_get_file("data"));
   testserver.start(4444);
 
   // 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",
                                "addon7@tests.mozilla.org",
                                "theme2@tests.mozilla.org"], function([a2, a3, a4,
                                                                       a7, t2]) {
     // Set up the initial state
     a2.userDisabled = true;
@@ -193,40 +196,45 @@ function run_test_1() {
                                "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);
@@ -235,30 +243,40 @@ function run_test_1() {
     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));
 
-    // 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",
                                  "addon2@tests.mozilla.org",
                                  "addon3@tests.mozilla.org",
                                  "addon4@tests.mozilla.org",
                                  "addon5@tests.mozilla.org",
                                  "addon6@tests.mozilla.org",
@@ -268,45 +286,50 @@ function run_test_1() {
                                                                         a4, a5, a6,
                                                                         a7, t1, t2]) {
       // Should be correctly recovered
       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));
 
       // Should be correctly recovered
       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));
 
       // The compatibility update won't be recovered but it should still be
       // active for this session
       do_check_neq(a3, null);
       do_check_true(a3.isActive);
       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
       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);
       do_check_false(a6.userDisabled);
       do_check_false(a6.appDisabled);
       do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
 
       do_check_neq(a7, null);
@@ -316,69 +339,79 @@ function run_test_1() {
       do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
 
       // Should be correctly recovered
       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));
 
       // Should be correctly recovered
       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));
 
       // 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",
                                    "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_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);
         do_check_false(a6.userDisabled);
         do_check_false(a6.appDisabled);
         do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
 
         do_check_neq(a7, null);
@@ -387,21 +420,104 @@ function run_test_1() {
         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));
 
-        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();
+        });
       });
     });
   });
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -175,16 +175,17 @@ skip-if = os == "android"
 [test_install_icons.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_install_strictcompat.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_locale.js]
 [test_locked.js]
+[test_locked2.js]
 [test_locked_strictcompat.js]
 [test_manifest.js]
 [test_migrate1.js]
 [test_migrate2.js]
 [test_migrate3.js]
 [test_migrate4.js]
 [test_migrate5.js]
 [test_migrateAddonRepository.js]