Bug 1338280 - Avoid caching the database in Places services in tests. draft
authorKit Cambridge <kit@yakshaving.ninja>
Wed, 22 Feb 2017 13:54:40 -0800
changeset 488405 c2933bd01d9cf00ba23a506e84918a8b57f3d750
parent 488125 7abeac2f2d668554f0093fc0bdb1488f9a77d16e
child 488406 d30e2adc8d6ff44a8028fcdbbfea5965862e194c
push id46512
push userbmo:kit@mozilla.com
push dateThu, 23 Feb 2017 02:18:17 +0000
bugs1338280
milestone54.0a1
Bug 1338280 - Avoid caching the database in Places services in tests. This is a mechanical change to the annotation, favicon, bookmark, and history services that wraps the database handle in a getter. If the testing pref is set, the database is always accessed through `Database::GetDatabase`. This avoids keeping strong references to a database and allows its destructor to run. MozReview-Commit-ID: DZZ6KbxsbU5
toolkit/components/places/Database.h
toolkit/components/places/History.cpp
toolkit/components/places/History.h
toolkit/components/places/nsAnnotationService.cpp
toolkit/components/places/nsAnnotationService.h
toolkit/components/places/nsFaviconService.cpp
toolkit/components/places/nsFaviconService.h
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavBookmarks.h
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsNavHistory.h
--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -39,16 +39,22 @@
 #define TOPIC_PLACES_WILL_CLOSE_CONNECTION "places-will-close-connection"
 // Fired when the connection has gone, nothing will work from now on.
 #define TOPIC_PLACES_CONNECTION_CLOSED "places-connection-closed"
 
 // Simulate profile-before-change. This topic may only be used by
 // calling `observe` directly on the database. Used for testing only.
 #define TOPIC_SIMULATE_PLACES_SHUTDOWN "test-simulate-places-shutdown"
 
+// A pref that controls whether testing conveniences are enabled. If true,
+// the annotation, bookmark, favicon, and history services will not hold
+// strong references to the database singleton, allowing tests to completely
+// shut down and restart Places.
+#define PREF_DATABASE_TESTING_ENABLED "places.database.testing.enabled"
+
 class nsIRunnable;
 
 namespace mozilla {
 namespace places {
 
 enum JournalMode {
   // Default SQLite journal mode.
   JOURNAL_DELETE = 0
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -3,16 +3,17 @@
 /* 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 "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsXULAppAPI.h"
 
 #include "History.h"
 #include "nsNavHistory.h"
 #include "nsNavBookmarks.h"
@@ -1865,28 +1866,31 @@ StoreAndNotifyEmbedVisit(VisitData& aPla
 
 ////////////////////////////////////////////////////////////////////////////////
 //// History
 
 History* History::gService = nullptr;
 
 History::History()
   : mShuttingDown(false)
+  , mTestingEnabled(false)
   , mShutdownMutex("History::mShutdownMutex")
   , mObservers(VISIT_OBSERVERS_INITIAL_CACHE_LENGTH)
   , mRecentlyVisitedURIs(RECENTLY_VISITED_URIS_SIZE)
 {
   NS_ASSERTION(!gService, "Ruh-roh!  This service has already been created!");
   gService = this;
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   NS_WARNING_ASSERTION(os, "Observer service was not found!");
   if (os) {
     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, false);
   }
+
+  mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
 }
 
 History::~History()
 {
   UnregisterWeakMemoryReporter(this);
 
   gService = nullptr;
 
@@ -2288,26 +2292,40 @@ History::GetSingleton()
     NS_ENSURE_TRUE(gService, nullptr);
     gService->InitMemoryReporter();
   }
 
   NS_ADDREF(gService);
   return gService;
 }
 
+already_AddRefed<Database>
+History::GetDatabase()
+{
+  if (mShuttingDown) {
+    return nullptr;
+  }
+  RefPtr<Database> DB;
+  if (mTestingEnabled) {
+    DB = Database::GetDatabase();
+  } else {
+    if (!mDB) {
+      mDB = Database::GetDatabase();
+    }
+    DB = mDB;
+  }
+  return DB.forget();
+}
+
 mozIStorageConnection*
 History::GetDBConn()
 {
-  if (mShuttingDown)
-    return nullptr;
-  if (!mDB) {
-    mDB = Database::GetDatabase();
-    NS_ENSURE_TRUE(mDB, nullptr);
-  }
-  return mDB->MainConn();
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_TRUE(DB, nullptr);
+  return DB->MainConn();
 }
 
 void
 History::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Prevent other threads from scheduling uses of the DB while we mark
--- a/toolkit/components/places/History.h
+++ b/toolkit/components/places/History.h
@@ -105,27 +105,25 @@ public:
    * manager only.
    */
   static History* GetSingleton();
 
   template<int N>
   already_AddRefed<mozIStorageStatement>
   GetStatement(const char (&aQuery)[N])
   {
-    mozIStorageConnection* dbConn = GetDBConn();
-    NS_ENSURE_TRUE(dbConn, nullptr);
-    return mDB->GetStatement(aQuery);
+    RefPtr<mozilla::places::Database> DB = GetDatabase();
+    return DB->GetStatement(aQuery);
   }
 
   already_AddRefed<mozIStorageStatement>
   GetStatement(const nsACString& aQuery)
   {
-    mozIStorageConnection* dbConn = GetDBConn();
-    NS_ENSURE_TRUE(dbConn, nullptr);
-    return mDB->GetStatement(aQuery);
+    RefPtr<mozilla::places::Database> DB = GetDatabase();
+    return DB->GetStatement(aQuery);
   }
 
   bool IsShuttingDown() const {
     return mShuttingDown;
   }
   Mutex& GetShutdownMutex() {
     return mShutdownMutex;
   }
@@ -136,16 +134,18 @@ public:
    */
   void AppendToRecentlyVisitedURIs(nsIURI* aURI);
 
 private:
   virtual ~History();
 
   void InitMemoryReporter();
 
+  already_AddRefed<Database> GetDatabase();
+
   /**
    * Obtains a read-write database connection.
    */
   mozIStorageConnection* GetDBConn();
 
   /**
    * The database handle.  This is initialized lazily by the first call to
    * GetDBConn(), so never use it directly, or, if you really need, always
@@ -159,16 +159,18 @@ private:
    * Remove any memory references to tasks and do not take on any more.
    */
   void Shutdown();
 
   static History* gService;
 
   // Ensures new tasks aren't started on destruction.
   bool mShuttingDown;
+  // Controls whether we hold a strong reference to the database handle.
+  bool mTestingEnabled;
   // This mutex guards mShuttingDown. Code running in other threads that might
   // schedule tasks that use the database should grab it and check the value of
   // mShuttingDown. If we are already shutting down, the code must gracefully
   // avoid using the db. If we are not, the lock will prevent shutdown from
   // starting in an unexpected moment.
   Mutex mShutdownMutex;
 
   typedef nsTObserverArray<mozilla::dom::Link* > ObserverArray;
--- a/toolkit/components/places/nsAnnotationService.cpp
+++ b/toolkit/components/places/nsAnnotationService.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "mozilla/ArrayUtils.h"
+#include "mozilla/Preferences.h"
 
 #include "nsAnnotationService.h"
 #include "nsNavHistory.h"
 #include "nsPlacesTables.h"
 #include "nsPlacesIndexes.h"
 #include "nsPlacesMacros.h"
 #include "Helpers.h"
 
@@ -115,56 +116,77 @@ PLACES_FACTORY_SINGLETON_IMPLEMENTATION(
 NS_IMPL_ISUPPORTS(nsAnnotationService
 , nsIAnnotationService
 , nsIObserver
 , nsISupportsWeakReference
 )
 
 
 nsAnnotationService::nsAnnotationService()
-  : mHasSessionAnnotations(false)
+  : mTestingEnabled(false)
+  , mHasSessionAnnotations(false)
 {
   NS_ASSERTION(!gAnnotationService,
                "Attempting to create two instances of the service!");
   gAnnotationService = this;
+  mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
 }
 
 
 nsAnnotationService::~nsAnnotationService()
 {
   NS_ASSERTION(gAnnotationService == this,
                "Deleting a non-singleton instance of the service");
   if (gAnnotationService == this)
     gAnnotationService = nullptr;
 }
 
 
 nsresult
 nsAnnotationService::Init()
 {
-  mDB = Database::GetDatabase();
-  NS_ENSURE_STATE(mDB);
+  mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
 
   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
   if (obsSvc) {
     (void)obsSvc->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
   }
 
   return NS_OK;
 }
 
+
+already_AddRefed<Database>
+nsAnnotationService::GetDatabase()
+{
+  RefPtr<Database> DB;
+  if (mTestingEnabled) {
+    DB = Database::GetDatabase();
+  } else {
+    if (!mDB) {
+      mDB = Database::GetDatabase();
+    }
+    DB = mDB;
+  }
+  return DB.forget();
+}
+
+
 nsresult
 nsAnnotationService::SetAnnotationStringInternal(nsIURI* aURI,
                                                  int64_t aItemId,
                                                  const nsACString& aName,
                                                  const nsAString& aValue,
                                                  int32_t aFlags,
                                                  uint16_t aExpiration)
 {
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
   nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
                                    nsIAnnotationService::TYPE_STRING,
                                    statement);
   NS_ENSURE_SUCCESS(rv, rv);
   mozStorageStatementScoper scoper(statement);
 
   rv = statement->BindStringByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -388,17 +410,20 @@ nsAnnotationService::SetItemAnnotationSt
 nsresult
 nsAnnotationService::SetAnnotationInt32Internal(nsIURI* aURI,
                                                 int64_t aItemId,
                                                 const nsACString& aName,
                                                 int32_t aValue,
                                                 int32_t aFlags,
                                                 uint16_t aExpiration)
 {
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
   nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
                                    nsIAnnotationService::TYPE_INT32,
                                    statement);
   NS_ENSURE_SUCCESS(rv, rv);
   mozStorageStatementScoper scoper(statement);
 
   rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -459,17 +484,20 @@ nsAnnotationService::SetItemAnnotationIn
 nsresult
 nsAnnotationService::SetAnnotationInt64Internal(nsIURI* aURI,
                                                 int64_t aItemId,
                                                 const nsACString& aName,
                                                 int64_t aValue,
                                                 int32_t aFlags,
                                                 uint16_t aExpiration)
 {
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
   nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
                                    nsIAnnotationService::TYPE_INT64,
                                    statement);
   NS_ENSURE_SUCCESS(rv, rv);
   mozStorageStatementScoper scoper(statement);
 
   rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -530,17 +558,20 @@ nsAnnotationService::SetItemAnnotationIn
 nsresult
 nsAnnotationService::SetAnnotationDoubleInternal(nsIURI* aURI,
                                                  int64_t aItemId,
                                                  const nsACString& aName,
                                                  double aValue,
                                                  int32_t aFlags,
                                                  uint16_t aExpiration)
 {
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
   nsCOMPtr<mozIStorageStatement> statement;
   nsresult rv = StartSetAnnotation(aURI, aItemId, aName, aFlags, aExpiration,
                                    nsIAnnotationService::TYPE_DOUBLE,
                                    statement);
   NS_ENSURE_SUCCESS(rv, rv);
   mozStorageStatementScoper scoper(statement);
 
   rv = statement->BindDoubleByName(NS_LITERAL_CSTRING("content"), aValue);
@@ -988,17 +1019,20 @@ nsAnnotationService::GetPagesWithAnnotat
   return NS_OK;
 }
 
 
 nsresult
 nsAnnotationService::GetPagesWithAnnotationCOMArray(const nsACString& aName,
                                                     nsCOMArray<nsIURI>* _results)
 {
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT h.url "
     "FROM moz_anno_attributes n "
     "JOIN moz_annos a ON n.id = a.anno_attribute_id "
     "JOIN moz_places h ON h.id = a.place_id "
     "WHERE n.name = :anno_name"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
@@ -1068,17 +1102,20 @@ nsAnnotationService::GetAnnotationsWithN
 {
   NS_ENSURE_ARG(!aName.IsEmpty());
   NS_ENSURE_ARG_POINTER(_annotations);
 
   *_count = 0;
   *_annotations = nullptr;
   nsCOMArray<mozIAnnotatedResult> annotations;
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT h.guid, h.url, -1, a.type, a.content "
     "FROM moz_anno_attributes n "
     "JOIN moz_annos a ON n.id = a.anno_attribute_id "
     "JOIN moz_places h ON h.id = a.place_id "
     "WHERE n.name = :anno_name "
     "UNION ALL "
     "SELECT b.guid, h.url, b.id, a.type, a.content "
     "FROM moz_anno_attributes n "
@@ -1159,17 +1196,20 @@ nsAnnotationService::GetAnnotationsWithN
   return NS_OK;
 }
 
 
 nsresult
 nsAnnotationService::GetItemsWithAnnotationTArray(const nsACString& aName,
                                                   nsTArray<int64_t>* _results)
 {
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT a.item_id "
     "FROM moz_anno_attributes n "
     "JOIN moz_items_annos a ON n.id = a.anno_attribute_id "
     "WHERE n.name = :anno_name"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
@@ -1231,28 +1271,31 @@ nsAnnotationService::GetPageAnnotationNa
 
 nsresult
 nsAnnotationService::GetAnnotationNamesTArray(nsIURI* aURI,
                                               int64_t aItemId,
                                               nsTArray<nsCString>* _result)
 {
   _result->Clear();
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   bool isItemAnnotation = (aItemId > 0);
   nsCOMPtr<mozIStorageStatement> statement;
   if (isItemAnnotation) {
-    statement = mDB->GetStatement(
+    statement = DB->GetStatement(
       "SELECT n.name "
       "FROM moz_anno_attributes n "
       "JOIN moz_items_annos a ON a.anno_attribute_id = n.id "
       "WHERE a.item_id = :item_id"
     );
   }
   else {
-    statement = mDB->GetStatement(
+    statement = DB->GetStatement(
       "SELECT n.name "
       "FROM moz_anno_attributes n "
       "JOIN moz_annos a ON a.anno_attribute_id = n.id "
       "JOIN moz_places h ON h.id = a.place_id "
       "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
     );
   }
   NS_ENSURE_STATE(statement);
@@ -1356,28 +1399,31 @@ nsAnnotationService::ItemHasAnnotation(i
  *       delete the last item of a given name, that item really should go away.
  *       It will be cleaned up by expiration.
  */
 nsresult
 nsAnnotationService::RemoveAnnotationInternal(nsIURI* aURI,
                                               int64_t aItemId,
                                               const nsACString& aName)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   bool isItemAnnotation = (aItemId > 0);
   nsCOMPtr<mozIStorageStatement> statement;
   if (isItemAnnotation) {
-    statement = mDB->GetStatement(
+    statement = DB->GetStatement(
       "DELETE FROM moz_items_annos "
       "WHERE item_id = :item_id "
         "AND anno_attribute_id = "
           "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
     );
   }
   else {
-    statement = mDB->GetStatement(
+    statement = DB->GetStatement(
       "DELETE FROM moz_annos "
       "WHERE place_id = "
           "(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
         "AND anno_attribute_id = "
           "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name)"
     );
   }
   NS_ENSURE_STATE(statement);
@@ -1431,18 +1477,21 @@ nsAnnotationService::RemoveItemAnnotatio
 }
 
 
 NS_IMETHODIMP
 nsAnnotationService::RemovePageAnnotations(nsIURI* aURI)
 {
   NS_ENSURE_ARG(aURI);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Should this be precompiled or a getter?
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
     "DELETE FROM moz_annos WHERE place_id = "
       "(SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url)"
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   nsresult rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1458,18 +1507,21 @@ nsAnnotationService::RemovePageAnnotatio
 
 
 NS_IMETHODIMP
 nsAnnotationService::RemoveItemAnnotations(int64_t aItemId,
                                            uint16_t aSource)
 {
   NS_ENSURE_ARG_MIN(aItemId, 1);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Should this be precompiled or a getter?
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
     "DELETE FROM moz_items_annos WHERE item_id = :item_id"
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1491,19 +1543,22 @@ nsAnnotationService::RemoveItemAnnotatio
 NS_IMETHODIMP
 nsAnnotationService::CopyPageAnnotations(nsIURI* aSourceURI,
                                          nsIURI* aDestURI,
                                          bool aOverwriteDest)
 {
   NS_ENSURE_ARG(aSourceURI);
   NS_ENSURE_ARG(aDestURI);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
 
-  nsCOMPtr<mozIStorageStatement> sourceStmt = mDB->GetStatement(
+  mozStorageTransaction transaction(DB->MainConn(), false);
+
+  nsCOMPtr<mozIStorageStatement> sourceStmt = DB->GetStatement(
     "SELECT h.id, n.id, n.name, a2.id "
     "FROM moz_places h "
     "JOIN moz_annos a ON a.place_id = h.id "
     "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
     "LEFT JOIN moz_annos a2 ON a2.place_id = "
       "(SELECT id FROM moz_places WHERE url_hash = hash(:dest_url) AND url = :dest_url) "
                           "AND a2.anno_attribute_id = n.id "
     "WHERE url = :source_url"
@@ -1511,17 +1566,17 @@ nsAnnotationService::CopyPageAnnotations
   NS_ENSURE_STATE(sourceStmt);
   mozStorageStatementScoper sourceScoper(sourceStmt);
 
   nsresult rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("source_url"), aSourceURI);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = URIBinder::Bind(sourceStmt, NS_LITERAL_CSTRING("dest_url"), aDestURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<mozIStorageStatement> copyStmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> copyStmt = DB->GetStatement(
     "INSERT INTO moz_annos "
     "(place_id, anno_attribute_id, content, flags, expiration, "
      "type, dateAdded, lastModified) "
     "SELECT (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url), "
            "anno_attribute_id, content, flags, expiration, type, "
            ":date, :date "
     "FROM moz_annos "
     "WHERE place_id = :page_id "
@@ -1574,36 +1629,39 @@ NS_IMETHODIMP
 nsAnnotationService::CopyItemAnnotations(int64_t aSourceItemId,
                                          int64_t aDestItemId,
                                          bool aOverwriteDest,
                                          uint16_t aSource)
 {
   NS_ENSURE_ARG_MIN(aSourceItemId, 1);
   NS_ENSURE_ARG_MIN(aDestItemId, 1);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
 
-  nsCOMPtr<mozIStorageStatement> sourceStmt = mDB->GetStatement(
+  mozStorageTransaction transaction(DB->MainConn(), false);
+
+  nsCOMPtr<mozIStorageStatement> sourceStmt = DB->GetStatement(
     "SELECT n.id, n.name, a2.id "
     "FROM moz_bookmarks b "
     "JOIN moz_items_annos a ON a.item_id = b.id "
     "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
     "LEFT JOIN moz_items_annos a2 ON a2.item_id = :dest_item_id "
                                 "AND a2.anno_attribute_id = n.id "
     "WHERE b.id = :source_item_id"
   );
   NS_ENSURE_STATE(sourceStmt);
   mozStorageStatementScoper sourceScoper(sourceStmt);
 
   nsresult rv = sourceStmt->BindInt64ByName(NS_LITERAL_CSTRING("source_item_id"), aSourceItemId);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = sourceStmt->BindInt64ByName(NS_LITERAL_CSTRING("dest_item_id"), aDestItemId);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<mozIStorageStatement> copyStmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> copyStmt = DB->GetStatement(
       "INSERT OR REPLACE INTO moz_items_annos "
       "(item_id, anno_attribute_id, content, flags, expiration, "
        "type, dateAdded, lastModified) "
       "SELECT :dest_item_id, anno_attribute_id, content, flags, expiration, "
              "type, :date, :date "
       "FROM moz_items_annos "
       "WHERE item_id = :source_item_id "
       "AND anno_attribute_id = :name_id"
@@ -1695,31 +1753,34 @@ nsAnnotationService::GetObservers(uint32
 }
 
 nsresult
 nsAnnotationService::HasAnnotationInternal(nsIURI* aURI,
                                            int64_t aItemId,
                                            const nsACString& aName,
                                            bool* _hasAnno)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   bool isItemAnnotation = (aItemId > 0);
   nsCOMPtr<mozIStorageStatement> stmt;
   if (isItemAnnotation) {
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "SELECT b.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
              "a.id, a.dateAdded "
       "FROM moz_bookmarks b "
       "LEFT JOIN moz_items_annos a ON a.item_id = b.id "
                                  "AND a.anno_attribute_id = nameid "
       "WHERE b.id = :item_id"
     );
   }
   else {
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "SELECT h.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
              "a.id, a.dateAdded "
       "FROM moz_places h "
       "LEFT JOIN moz_annos a ON a.place_id = h.id "
                            "AND a.anno_attribute_id = nameid "
       "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
     );
@@ -1764,30 +1825,33 @@ nsAnnotationService::HasAnnotationIntern
  */
 
 nsresult
 nsAnnotationService::StartGetAnnotation(nsIURI* aURI,
                                         int64_t aItemId,
                                         const nsACString& aName,
                                         nsCOMPtr<mozIStorageStatement>& aStatement)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   bool isItemAnnotation = (aItemId > 0);
 
   if (isItemAnnotation) {
-    aStatement = mDB->GetStatement(
+    aStatement = DB->GetStatement(
       "SELECT a.id, a.item_id, :anno_name, a.content, a.flags, "
              "a.expiration, a.type "
       "FROM moz_anno_attributes n "
       "JOIN moz_items_annos a ON a.anno_attribute_id = n.id "
       "WHERE a.item_id = :item_id "
       "AND n.name = :anno_name"
     );
   }
   else {
-    aStatement = mDB->GetStatement(
+    aStatement = DB->GetStatement(
       "SELECT a.id, a.place_id, :anno_name, a.content, a.flags, "
              "a.expiration, a.type "
       "FROM moz_anno_attributes n "
       "JOIN moz_annos a ON n.id = a.anno_attribute_id "
       "JOIN moz_places h ON h.id = a.place_id "
       "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url "
         "AND n.name = :anno_name"
     );
@@ -1831,24 +1895,27 @@ nsresult
 nsAnnotationService::StartSetAnnotation(nsIURI* aURI,
                                         int64_t aItemId,
                                         const nsACString& aName,
                                         int32_t aFlags,
                                         uint16_t aExpiration,
                                         uint16_t aType,
                                         nsCOMPtr<mozIStorageStatement>& aStatement)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   bool isItemAnnotation = (aItemId > 0);
 
   if (aExpiration == EXPIRE_SESSION) {
     mHasSessionAnnotations = true;
   }
 
   // Ensure the annotation name exists.
-  nsCOMPtr<mozIStorageStatement> addNameStmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> addNameStmt = DB->GetStatement(
     "INSERT OR IGNORE INTO moz_anno_attributes (name) VALUES (:anno_name)"
   );
   NS_ENSURE_STATE(addNameStmt);
   mozStorageStatementScoper scoper(addNameStmt);
 
   nsresult rv = addNameStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aName);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = addNameStmt->Execute();
@@ -1859,28 +1926,28 @@ nsAnnotationService::StartSetAnnotation(
   // - we should not allow setting annotations on invalid URIs or itemIds.
   // This query will tell us:
   // - whether the item or page exists.
   // - whether the annotation already exists.
   // - the nameID associated with the annotation name.
   // - the id and dateAdded of the old annotation, if it exists.
   nsCOMPtr<mozIStorageStatement> stmt;
   if (isItemAnnotation) {
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "SELECT b.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
              "a.id, a.dateAdded "
       "FROM moz_bookmarks b "
       "LEFT JOIN moz_items_annos a ON a.item_id = b.id "
                                  "AND a.anno_attribute_id = nameid "
       "WHERE b.id = :item_id"
     );
   }
   else {
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "SELECT h.id, "
              "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) AS nameid, "
              "a.id, a.dateAdded "
       "FROM moz_places h "
       "LEFT JOIN moz_annos a ON a.place_id = h.id "
                            "AND a.anno_attribute_id = nameid "
       "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
     );
@@ -1906,26 +1973,26 @@ nsAnnotationService::StartSetAnnotation(
   }
 
   int64_t fkId = stmt->AsInt64(0);
   int64_t nameID = stmt->AsInt64(1);
   int64_t oldAnnoId = stmt->AsInt64(2);
   int64_t oldAnnoDate = stmt->AsInt64(3);
 
   if (isItemAnnotation) {
-    aStatement = mDB->GetStatement(
+    aStatement = DB->GetStatement(
       "INSERT OR REPLACE INTO moz_items_annos "
         "(id, item_id, anno_attribute_id, content, flags, "
          "expiration, type, dateAdded, lastModified) "
       "VALUES (:id, :fk, :name_id, :content, :flags, "
       ":expiration, :type, :date_added, :last_modified)"
     );
   }
   else {
-    aStatement = mDB->GetStatement(
+    aStatement = DB->GetStatement(
       "INSERT OR REPLACE INTO moz_annos "
         "(id, place_id, anno_attribute_id, content, flags, "
          "expiration, type, dateAdded, lastModified) "
       "VALUES (:id, :fk, :name_id, :content, :flags, "
       ":expiration, :type, :date_added, :last_modified)"
     );
   }
   NS_ENSURE_STATE(aStatement);
@@ -1971,41 +2038,44 @@ nsAnnotationService::StartSetAnnotation(
 
 NS_IMETHODIMP
 nsAnnotationService::Observe(nsISupports *aSubject,
                              const char *aTopic,
                              const char16_t *aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) {
     // Remove all session annotations, if any.
     if (mHasSessionAnnotations) {
-      nsCOMPtr<mozIStorageAsyncStatement> pageAnnoStmt = mDB->GetAsyncStatement(
+      nsCOMPtr<mozIStorageAsyncStatement> pageAnnoStmt = DB->GetAsyncStatement(
         "DELETE FROM moz_annos WHERE expiration = :expire_session"
       );
       NS_ENSURE_STATE(pageAnnoStmt);
       nsresult rv = pageAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"),
                                                   EXPIRE_SESSION);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsCOMPtr<mozIStorageAsyncStatement> itemAnnoStmt = mDB->GetAsyncStatement(
+      nsCOMPtr<mozIStorageAsyncStatement> itemAnnoStmt = DB->GetAsyncStatement(
         "DELETE FROM moz_items_annos WHERE expiration = :expire_session"
       );
       NS_ENSURE_STATE(itemAnnoStmt);
       rv = itemAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expire_session"),
                                          EXPIRE_SESSION);
       NS_ENSURE_SUCCESS(rv, rv);
 
       mozIStorageBaseStatement *stmts[] = {
         pageAnnoStmt.get()
       , itemAnnoStmt.get()
       };
 
       nsCOMPtr<mozIStoragePendingStatement> ps;
-      rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
+      rv = DB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
                                          getter_AddRefs(ps));
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
--- a/toolkit/components/places/nsAnnotationService.h
+++ b/toolkit/components/places/nsAnnotationService.h
@@ -80,16 +80,17 @@ public:
     return gAnnotationService;
   }
 
 private:
   ~nsAnnotationService();
 
 protected:
   RefPtr<mozilla::places::Database> mDB;
+  bool mTestingEnabled;
 
   nsCOMArray<nsIAnnotationObserver> mObservers;
   bool mHasSessionAnnotations;
 
   static nsAnnotationService* gAnnotationService;
 
   static const int kAnnoIndex_ID;
   static const int kAnnoIndex_PageOrItem;
@@ -143,16 +144,18 @@ protected:
                                        double aValue,
                                        int32_t aFlags,
                                        uint16_t aExpiration);
 
   nsresult RemoveAnnotationInternal(nsIURI* aURI,
                                     int64_t aItemId,
                                     const nsACString& aName);
 
+  already_AddRefed<Database> GetDatabase();
+
 public:
   nsresult GetPagesWithAnnotationCOMArray(const nsACString& aName,
                                           nsCOMArray<nsIURI>* _results);
   nsresult GetItemsWithAnnotationTArray(const nsACString& aName,
                                         nsTArray<int64_t>* _result);
   nsresult GetAnnotationNamesTArray(nsIURI* aURI,
                                     int64_t aItemId,
                                     nsTArray<nsCString>* _result);
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -75,17 +75,18 @@ NS_IMPL_CLASSINFO(nsFaviconService, null
 NS_IMPL_ISUPPORTS_CI(
   nsFaviconService
 , nsIFaviconService
 , mozIAsyncFavicons
 , nsITimerCallback
 )
 
 nsFaviconService::nsFaviconService()
-  : mFailedFaviconSerial(0)
+  : mTestingEnabled(false)
+  , mFailedFaviconSerial(0)
   , mFailedFavicons(MAX_FAILED_FAVICONS / 2)
   , mUnassociatedIcons(UNASSOCIATED_FAVICONS_LENGTH)
 {
   NS_ASSERTION(!gFaviconService,
                "Attempting to create two instances of the service!");
   gFaviconService = this;
 }
 
@@ -97,49 +98,66 @@ nsFaviconService::~nsFaviconService()
   if (gFaviconService == this)
     gFaviconService = nullptr;
 }
 
 
 nsresult
 nsFaviconService::Init()
 {
-  mDB = Database::GetDatabase();
-  NS_ENSURE_STATE(mDB);
+  mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
 
   mExpireUnassociatedIconsTimer = do_CreateInstance("@mozilla.org/timer;1");
   NS_ENSURE_STATE(mExpireUnassociatedIconsTimer);
 
   return NS_OK;
 }
 
+already_AddRefed<Database>
+nsFaviconService::GetDatabase()
+{
+  RefPtr<Database> DB;
+  if (mTestingEnabled) {
+    DB = Database::GetDatabase();
+  } else {
+    if (!mDB) {
+      mDB = Database::GetDatabase();
+    }
+    DB = mDB;
+  }
+  return DB.forget();
+}
+
 NS_IMETHODIMP
 nsFaviconService::ExpireAllFavicons()
 {
-  nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = mDB->GetAsyncStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = DB->GetAsyncStatement(
     "UPDATE moz_places "
     "SET favicon_id = NULL "
     "WHERE favicon_id NOT NULL"
   );
   NS_ENSURE_STATE(unlinkIconsStmt);
-  nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = mDB->GetAsyncStatement(
+  nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = DB->GetAsyncStatement(
     "DELETE FROM moz_favicons WHERE id NOT IN ("
       "SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL "
     ")"
   );
   NS_ENSURE_STATE(removeIconsStmt);
 
   mozIStorageBaseStatement* stmts[] = {
     unlinkIconsStmt.get()
   , removeIconsStmt.get()
   };
   nsCOMPtr<mozIStoragePendingStatement> ps;
   RefPtr<ExpireFaviconsStatementCallbackNotifier> callback =
     new ExpireFaviconsStatementCallbackNotifier();
-  nsresult rv = mDB->MainConn()->ExecuteAsync(
+  nsresult rv = DB->MainConn()->ExecuteAsync(
     stmts, ArrayLength(stmts), callback, getter_AddRefs(ps)
   );
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -281,17 +299,17 @@ nsFaviconService::SetAndFetchFaviconForP
   }
 
   RefPtr<AsyncFetchAndSetIconForPage> event =
     new AsyncFetchAndSetIconForPage(icon, page, loadPrivate,
                                     aCallback, aLoadingPrincipal);
 
   // Get the target thread and start the work.
   // DB will be updated and observers notified when data has finished loading.
-  RefPtr<Database> DB = Database::GetDatabase();
+  RefPtr<Database> DB = GetDatabase();
   NS_ENSURE_STATE(DB);
   DB->DispatchToAsyncThread(event);
 
   // Return this event to the caller to allow aborting an eventual fetch.
   event.forget(_canceler);
 
   return NS_OK;
 }
@@ -352,17 +370,17 @@ nsFaviconService::ReplaceFaviconData(nsI
     iconData->mimeType.Assign(aMimeType);
     iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen);
   }
 
   // If the database contains an icon at the given url, we will update the
   // database immediately so that the associated pages are kept in sync.
   // Otherwise, do nothing and let the icon be picked up from the memory hash.
   RefPtr<AsyncReplaceFaviconData> event = new AsyncReplaceFaviconData(*iconData);
-  RefPtr<Database> DB = Database::GetDatabase();
+  RefPtr<Database> DB = GetDatabase();
   NS_ENSURE_STATE(DB);
   DB->DispatchToAsyncThread(event);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFaviconService::ReplaceFaviconDataFromDataURL(nsIURI* aFaviconURI,
@@ -468,17 +486,17 @@ nsFaviconService::GetFaviconURLForPage(n
 
   nsAutoCString pageSpec;
   nsresult rv = aPageURI->GetSpec(pageSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<AsyncGetFaviconURLForPage> event =
     new AsyncGetFaviconURLForPage(pageSpec, aCallback);
 
-  RefPtr<Database> DB = Database::GetDatabase();
+  RefPtr<Database> DB = GetDatabase();
   NS_ENSURE_STATE(DB);
   DB->DispatchToAsyncThread(event);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI,
@@ -489,17 +507,17 @@ nsFaviconService::GetFaviconDataForPage(
   NS_ENSURE_ARG(aCallback);
 
   nsAutoCString pageSpec;
   nsresult rv = aPageURI->GetSpec(pageSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<AsyncGetFaviconDataForPage> event =
     new AsyncGetFaviconDataForPage(pageSpec, aCallback);
-  RefPtr<Database> DB = Database::GetDatabase();
+  RefPtr<Database> DB = GetDatabase();
   NS_ENSURE_STATE(DB);
   DB->DispatchToAsyncThread(event);
 
   return NS_OK;
 }
 
 nsresult
 nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI,
@@ -666,18 +684,21 @@ nsFaviconService::OptimizeFaviconImage(c
 
   return NS_OK;
 }
 
 nsresult
 nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI,
                                       mozIStorageStatementCallback *aCallback)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback");
-  nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+  nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
     "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url"
   );
   NS_ENSURE_STATE(stmt);
 
   // Ignore the ref part of the URI before querying the database because
   // we may have added a media fragment for rendering purposes.
 
   nsAutoCString faviconURI;
--- a/toolkit/components/places/nsFaviconService.h
+++ b/toolkit/components/places/nsFaviconService.h
@@ -115,30 +115,34 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIFAVICONSERVICE
   NS_DECL_MOZIASYNCFAVICONS
   NS_DECL_NSITIMERCALLBACK
 
 private:
   ~nsFaviconService();
 
+  already_AddRefed<mozilla::places::Database> GetDatabase();
+
   RefPtr<mozilla::places::Database> mDB;
 
   nsCOMPtr<nsITimer> mExpireUnassociatedIconsTimer;
 
   static nsFaviconService* gFaviconService;
 
   /**
    * A cached URI for the default icon. We return this a lot, and don't want to
    * re-parse and normalize our unchanging string many times.  Important: do
    * not return this directly; use Clone() since callers may change the object
    * they get back. May be null, in which case it needs initialization.
    */
   nsCOMPtr<nsIURI> mDefaultIcon;
 
+  bool mTestingEnabled;
+
   uint32_t mFailedFaviconSerial;
   nsDataHashtable<nsCStringHashKey, uint32_t> mFailedFavicons;
 
   // This class needs access to the icons cache.
   friend class mozilla::places::AsyncReplaceFaviconData;
   nsTHashtable<UnassociatedIconHashKey> mUnassociatedIcons;
 };
 
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -158,16 +158,17 @@ NeedsTombstone(const BookmarkData& aBook
 nsNavBookmarks::nsNavBookmarks()
   : mItemCount(0)
   , mRoot(0)
   , mMenuRoot(0)
   , mTagsRoot(0)
   , mUnfiledRoot(0)
   , mToolbarRoot(0)
   , mMobileRoot(0)
+  , mTestingEnabled(false)
   , mCanNotify(false)
   , mCacheObservers("bookmark-observers")
   , mBatching(false)
 {
   NS_ASSERTION(!gBookmarksService,
                "Attempting to create two instances of the service!");
   gBookmarksService = this;
 }
@@ -200,18 +201,17 @@ nsNavBookmarks::StoreLastInsertedId(cons
   MOZ_ASSERT(aTable.EqualsLiteral("moz_bookmarks"));
   sLastInsertedItemId = aLastInsertedId;
 }
 
 
 nsresult
 nsNavBookmarks::Init()
 {
-  mDB = Database::GetDatabase();
-  NS_ENSURE_STATE(mDB);
+  ReadTestingEnabled();
 
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
     (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true);
   }
 
   nsresult rv = ReadRoots();
@@ -231,21 +231,44 @@ nsNavBookmarks::Init()
   NS_ENSURE_STATE(history);
   history->AddObserver(this, true);
 
   // DO NOT PUT STUFF HERE that can fail. See observer comment above.
 
   return NS_OK;
 }
 
+already_AddRefed<Database>
+nsNavBookmarks::GetDatabase()
+{
+  RefPtr<Database> DB;
+  if (mTestingEnabled) {
+    DB = Database::GetDatabase();
+  } else {
+    if (!mDB) {
+      mDB = Database::GetDatabase();
+    }
+    DB = mDB;
+  }
+  return DB.forget();
+}
+
+void
+nsNavBookmarks::ReadTestingEnabled() {
+  mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
+}
+
 nsresult
 nsNavBookmarks::ReadRoots()
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
+  nsresult rv = DB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT guid, id FROM moz_bookmarks WHERE guid IN ( "
       "'root________', 'menu________', 'toolbar_____', "
       "'tags________', 'unfiled_____', 'mobile______' )"
   ), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasResult;
   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
@@ -286,17 +309,20 @@ nsNavBookmarks::ReadRoots()
 // nsNavBookmarks::IsBookmarkedInDatabase
 //
 //    This checks to see if the specified place_id is actually bookmarked.
 
 nsresult
 nsNavBookmarks::IsBookmarkedInDatabase(int64_t aPlaceId,
                                        bool* aIsBookmarked)
 {
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT 1 FROM moz_bookmarks WHERE fk = :page_id"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = stmt->ExecuteStep(aIsBookmarked);
@@ -309,17 +335,20 @@ nsresult
 nsNavBookmarks::AdjustIndices(int64_t aFolderId,
                               int32_t aStartIndex,
                               int32_t aEndIndex,
                               int32_t aDelta)
 {
   NS_ASSERTION(aStartIndex >= 0 && aEndIndex <= INT32_MAX &&
                aStartIndex <= aEndIndex, "Bad indices");
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "UPDATE moz_bookmarks SET position = position + :delta "
       "WHERE parent = :parent "
         "AND position BETWEEN :from_index AND :to_index"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
@@ -343,17 +372,20 @@ nsNavBookmarks::AdjustSeparatorsSyncCoun
                                             int32_t aStartIndex,
                                             int64_t aSyncChangeDelta)
 {
   MOZ_ASSERT(aStartIndex >= 0, "Bad start position");
   if (!aSyncChangeDelta) {
     return NS_OK;
   }
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "UPDATE moz_bookmarks SET syncChangeCounter = syncChangeCounter + :delta "
       "WHERE parent = :parent AND position >= :start_index "
       "AND type = :item_type "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("delta"), aSyncChangeDelta);
@@ -435,17 +467,20 @@ nsNavBookmarks::InsertBookmarkInDB(int64
                                    int64_t* _itemId,
                                    nsACString& _guid)
 {
   // Check for a valid itemId.
   MOZ_ASSERT(_itemId && (*_itemId == -1 || *_itemId > 0));
   // Check for a valid placeId.
   MOZ_ASSERT(aPlaceId && (aPlaceId == -1 || aPlaceId > 0));
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "INSERT INTO moz_bookmarks "
       "(id, fk, type, parent, position, title, "
        "dateAdded, lastModified, guid, syncStatus, syncChangeCounter) "
     "VALUES (:item_id, :page_id, :item_type, :parent, :item_index, "
             ":item_title, :date_added, :last_modified, "
             ":item_guid, :sync_status, :change_counter)"
   );
   NS_ENSURE_STATE(stmt);
@@ -592,17 +627,20 @@ nsNavBookmarks::InsertBookmark(int64_t a
 {
   NS_ENSURE_ARG(aURI);
   NS_ENSURE_ARG_POINTER(aNewBookmarkId);
   NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
 
   if (!aGUID.IsEmpty() && !IsValidGUID(aGUID))
     return NS_ERROR_INVALID_ARG;
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
   int64_t placeId;
   nsAutoCString placeGuid;
   nsresult rv = history->GetOrCreateIdForPage(aURI, &placeId, placeGuid);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -689,34 +727,37 @@ nsNavBookmarks::RemoveItem(int64_t aItem
     js::ProfileEntry::Category::OTHER);
 
   NS_ENSURE_ARG(!IsRoot(aItemId));
 
   BookmarkData bookmark;
   nsresult rv = FetchItemInfo(aItemId, bookmark);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
 
   // First, if not a tag, remove item annotations.
   bool isUntagging = bookmark.grandParentId == mTagsRoot;
   if (bookmark.parentId != mTagsRoot && !isUntagging) {
     nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
     NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
     rv = annosvc->RemoveItemAnnotations(bookmark.id, aSource);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (bookmark.type == TYPE_FOLDER) {
     // Remove all of the folder's children.
     rv = RemoveFolderChildren(bookmark.id, aSource);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "DELETE FROM moz_bookmarks WHERE id = :item_id"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = stmt->Execute();
@@ -861,17 +902,20 @@ nsNavBookmarks::CreateContainerWithID(in
 
   // Get the correct index for insertion.  This also ensures the parent exists.
   int32_t index, folderCount;
   int64_t grandParentId;
   nsAutoCString folderGuid;
   nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
 
   if (*aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
       *aIndex >= folderCount) {
     index = folderCount;
   } else {
     index = *aIndex;
     // Create space for the insertion.
     rv = AdjustIndices(aParent, index, INT32_MAX, 1);
@@ -919,17 +963,20 @@ nsNavBookmarks::InsertSeparator(int64_t 
 
   // Get the correct index for insertion.  This also ensures the parent exists.
   int32_t index, folderCount;
   int64_t grandParentId;
   nsAutoCString folderGuid;
   nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
 
   if (aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
       aIndex >= folderCount) {
     index = folderCount;
   }
   else {
     index = aIndex;
     // Create space for the insertion.
@@ -960,17 +1007,20 @@ nsNavBookmarks::InsertSeparator(int64_t 
 
 
 nsresult
 nsNavBookmarks::GetLastChildId(int64_t aFolderId, int64_t* aItemId)
 {
   NS_ASSERTION(aFolderId > 0, "Invalid folder id");
   *aItemId = -1;
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT id FROM moz_bookmarks WHERE parent = :parent "
     "ORDER BY position DESC LIMIT 1"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -991,25 +1041,28 @@ nsNavBookmarks::GetIdForItemAt(int64_t a
                                int32_t aIndex,
                                int64_t* aItemId)
 {
   NS_ENSURE_ARG_MIN(aFolder, 1);
   NS_ENSURE_ARG_POINTER(aItemId);
 
   *aItemId = -1;
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsresult rv;
   if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
     // Get last item within aFolder.
     rv = GetLastChildId(aFolder, aItemId);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     // Get the item in aFolder with position aIndex.
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "SELECT id, fk, type FROM moz_bookmarks "
       "WHERE parent = :parent AND position = :item_index"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolder);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1047,21 +1100,24 @@ nsNavBookmarks::GetRemoveFolderTransacti
   NS_ADDREF(*aResult = rft);
   return NS_OK;
 }
 
 
 nsresult
 nsNavBookmarks::GetDescendantFolders(int64_t aFolderId,
                                      nsTArray<int64_t>& aDescendantFoldersArray) {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsresult rv;
   // New descendant folders will be added from this index on.
   uint32_t startIndex = aDescendantFoldersArray.Length();
   {
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "SELECT id "
       "FROM moz_bookmarks "
       "WHERE parent = :parent "
       "AND type = :item_type "
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
@@ -1091,27 +1147,30 @@ nsNavBookmarks::GetDescendantFolders(int
 }
 
 
 nsresult
 nsNavBookmarks::GetDescendantChildren(int64_t aFolderId,
                                       const nsACString& aFolderGuid,
                                       int64_t aGrandParentId,
                                       nsTArray<BookmarkData>& aFolderChildrenArray) {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // New children will be added from this index on.
   uint32_t startIndex = aFolderChildrenArray.Length();
   nsresult rv;
   {
     // Collect children informations.
     // Select all children of a given folder, sorted by position.
     // This is a LEFT JOIN because not all bookmarks types have a place.
     // We construct a result where the first columns exactly match
     // kGetInfoIndex_* order, and additionally contains columns for position,
     // item_child, and folder_child from moz_bookmarks.
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
              "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
              "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
              "b.guid, b.position, b.type, b.fk, b.syncStatus "
       "FROM moz_bookmarks b "
       "LEFT JOIN moz_places h ON b.fk = h.id "
       "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
       "WHERE b.parent = :parent "
@@ -1202,34 +1261,37 @@ nsNavBookmarks::RemoveFolderChildren(int
     if (child.type == TYPE_FOLDER) {
       foldersToRemove.Append(',');
       foldersToRemove.AppendInt(child.id);
     }
   }
 
   int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Delete items from the database now.
-  mozStorageTransaction transaction(mDB->MainConn(), false);
-
-  nsCOMPtr<mozIStorageStatement> deleteStatement = mDB->GetStatement(
+  mozStorageTransaction transaction(DB->MainConn(), false);
+
+  nsCOMPtr<mozIStorageStatement> deleteStatement = DB->GetStatement(
     NS_LITERAL_CSTRING(
       "DELETE FROM moz_bookmarks "
       "WHERE parent IN (:parent") + foldersToRemove + NS_LITERAL_CSTRING(")")
   );
   NS_ENSURE_STATE(deleteStatement);
   mozStorageStatementScoper deleteStatementScoper(deleteStatement);
 
   rv = deleteStatement->BindInt64ByName(NS_LITERAL_CSTRING("parent"), folder.id);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = deleteStatement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Clean up orphan items annotations.
-  rv = mDB->MainConn()->ExecuteSimpleSQL(
+  rv = DB->MainConn()->ExecuteSimpleSQL(
     NS_LITERAL_CSTRING(
       "DELETE FROM moz_items_annos "
       "WHERE id IN ("
         "SELECT a.id from moz_items_annos a "
         "LEFT JOIN moz_bookmarks b ON a.item_id = b.id "
         "WHERE b.id ISNULL)"));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1335,17 +1397,20 @@ nsNavBookmarks::MoveItem(int64_t aItemId
   NS_ENSURE_ARG(!IsRoot(aItemId));
   NS_ENSURE_ARG_MIN(aItemId, 1);
   NS_ENSURE_ARG_MIN(aNewParent, 1);
   // -1 is append, but no other negative number is allowed.
   NS_ENSURE_ARG_MIN(aIndex, -1);
   // Disallow making an item its own parent.
   NS_ENSURE_ARG(aItemId != aNewParent);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
 
   BookmarkData bookmark;
   nsresult rv = FetchItemInfo(aItemId, bookmark);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // if parent and index are the same, nothing to do
   if (bookmark.parentId == aNewParent && bookmark.position == aIndex)
     return NS_OK;
@@ -1424,17 +1489,17 @@ nsNavBookmarks::MoveItem(int64_t aItemId
     rv = AdjustIndices(aNewParent, newIndex, INT32_MAX, 1);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
 
   {
     // Update parent and position.
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "UPDATE moz_bookmarks SET parent = :parent, position = :item_index "
       "WHERE id = :item_id "
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aNewParent);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1504,18 +1569,21 @@ nsNavBookmarks::MoveItem(int64_t aItemId
                                aSource));
   return NS_OK;
 }
 
 nsresult
 nsNavBookmarks::FetchItemInfo(int64_t aItemId,
                               BookmarkData& _bookmark)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // LEFT JOIN since not all bookmarks have an associated place.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, "
            "b.dateAdded, b.lastModified, b.guid, t.guid, t.parent, "
            "b.syncStatus "
     "FROM moz_bookmarks b "
     "LEFT JOIN moz_bookmarks t ON t.id = b.parent "
     "LEFT JOIN moz_places h ON h.id = b.fk "
     "WHERE b.id = :item_id"
   );
@@ -1580,29 +1648,32 @@ nsNavBookmarks::FetchItemInfo(int64_t aI
 nsresult
 nsNavBookmarks::SetItemDateInternal(enum BookmarkDate aDateType,
                                     int64_t aSyncChangeDelta,
                                     int64_t aItemId,
                                     PRTime aValue)
 {
   aValue = RoundToMilliseconds(aValue);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsCOMPtr<mozIStorageStatement> stmt;
   if (aDateType == DATE_ADDED) {
     // lastModified is set to the same value as dateAdded.  We do this for
     // performance reasons, since it will allow us to use an index to sort items
     // by date.
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "UPDATE moz_bookmarks SET dateAdded = :date, lastModified = :date, "
        "syncChangeCounter = syncChangeCounter + :delta "
       "WHERE id = :item_id"
     );
   }
   else {
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "UPDATE moz_bookmarks SET lastModified = :date, "
        "syncChangeCounter = syncChangeCounter + :delta "
       "WHERE id = :item_id"
     );
   }
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
@@ -1638,17 +1709,20 @@ nsNavBookmarks::SetItemDateAdded(int64_t
 
   // Round here so that we notify with the right value.
   bookmark.dateAdded = RoundToMilliseconds(aDateAdded);
 
   if (isTagging) {
     // If we're changing a tag, bump the change counter for all tagged
     // bookmarks. We use a separate code path to avoid a transaction for
     // non-tags.
-    mozStorageTransaction transaction(mDB->MainConn(), false);
+    RefPtr<Database> DB = GetDatabase();
+    NS_ENSURE_STATE(DB);
+
+    mozStorageTransaction transaction(DB->MainConn(), false);
 
     rv = SetItemDateInternal(DATE_ADDED, syncChangeDelta, bookmark.id,
                              bookmark.dateAdded);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = AddSyncChangesForBookmarksWithURL(bookmark.url, syncChangeDelta);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1708,17 +1782,20 @@ nsNavBookmarks::SetItemLastModified(int6
 
   // Round here so that we notify with the right value.
   bookmark.lastModified = RoundToMilliseconds(aLastModified);
 
   if (isTagging) {
     // If we're changing a tag, bump the change counter for all tagged
     // bookmarks. We use a separate code path to avoid a transaction for
     // non-tags.
-    mozStorageTransaction transaction(mDB->MainConn(), false);
+    RefPtr<Database> DB = GetDatabase();
+    NS_ENSURE_STATE(DB);
+
+    mozStorageTransaction transaction(DB->MainConn(), false);
 
     rv = SetItemDateInternal(LAST_MODIFIED, syncChangeDelta, bookmark.id,
                              bookmark.lastModified);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = AddSyncChangesForBookmarksWithURL(bookmark.url, syncChangeDelta);
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1784,17 +1861,20 @@ nsresult
 nsNavBookmarks::AddSyncChangesForBookmarksWithURI(nsIURI* aURI,
                                                   int64_t aSyncChangeDelta)
 {
   if (NS_WARN_IF(!aURI) || !aSyncChangeDelta) {
     // Ignore sync changes for invalid URIs.
     return NS_OK;
   }
 
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
    "UPDATE moz_bookmarks SET "
     "syncChangeCounter = syncChangeCounter + :delta "
    "WHERE type = :type AND "
          "fk = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND "
                "url = :url)"
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
@@ -1815,17 +1895,20 @@ nsNavBookmarks::AddSyncChangesForBookmar
 nsresult
 nsNavBookmarks::AddSyncChangesForBookmarksInFolder(int64_t aFolderId,
                                                    int64_t aSyncChangeDelta)
 {
   if (!aSyncChangeDelta) {
     return NS_OK;
   }
 
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
    "UPDATE moz_bookmarks SET "
     "syncChangeCounter = syncChangeCounter + :delta "
     "WHERE type = :type AND "
           "fk = (SELECT fk FROM moz_bookmarks WHERE parent = :parent)"
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
@@ -1846,17 +1929,21 @@ nsNavBookmarks::AddSyncChangesForBookmar
 
 
 nsresult
 nsNavBookmarks::InsertTombstone(const BookmarkData& aBookmark)
 {
   if (!NeedsTombstone(aBookmark)) {
     return NS_OK;
   }
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "INSERT INTO moz_bookmarks_deleted (guid, dateRemoved) "
     "VALUES (:guid, :date_removed)"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
                                            aBookmark.guid);
@@ -1874,33 +1961,36 @@ nsNavBookmarks::InsertTombstone(const Bo
 
 nsresult
 nsNavBookmarks::InsertTombstones(const nsTArray<TombstoneData>& aTombstones)
 {
   if (aTombstones.IsEmpty()) {
     return NS_OK;
   }
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   size_t maxRowsPerChunk = SQLITE_MAX_VARIABLE_NUMBER / 2;
   for (uint32_t startIndex = 0; startIndex < aTombstones.Length(); startIndex += maxRowsPerChunk) {
     size_t rowsPerChunk = std::min(maxRowsPerChunk, aTombstones.Length() - startIndex);
 
     // Build a query to insert all tombstones in a single statement, chunking to
     // avoid the SQLite bound parameter limit.
     nsAutoCString tombstonesToInsert;
     tombstonesToInsert.AppendLiteral("VALUES (?, ?)");
     for (uint32_t i = 1; i < rowsPerChunk; ++i) {
       tombstonesToInsert.AppendLiteral(", (?, ?)");
     }
 #ifdef DEBUG
     MOZ_ASSERT(tombstonesToInsert.CountChar('?') == rowsPerChunk * 2,
                "Expected one binding param per column for each tombstone");
 #endif
 
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_deleted "
         "(guid, dateRemoved) ") +
       tombstonesToInsert
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
     uint32_t paramIndex = 0;
@@ -1919,33 +2009,39 @@ nsNavBookmarks::InsertTombstones(const n
 
   return NS_OK;
 }
 
 
 nsresult
 nsNavBookmarks::RemoveTombstone(const nsACString& aGUID)
 {
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "DELETE FROM moz_bookmarks_deleted WHERE guid = :guid"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return stmt->Execute();
 }
 
 
 nsresult
 nsNavBookmarks::PreventSyncReparenting(const BookmarkData& aBookmark)
 {
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "DELETE FROM moz_items_annos WHERE "
       "item_id = :item_id AND "
       "anno_attribute_id = (SELECT id FROM moz_anno_attributes "
                            "WHERE name = :orphan_anno)"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
@@ -1977,17 +2073,20 @@ nsNavBookmarks::SetItemTitle(int64_t aIt
 
   nsAutoCString title;
   TruncateTitle(aTitle, title);
 
   if (isChangingTagFolder) {
     // If we're changing the title of a tag folder, bump the change counter
     // for all tagged bookmarks. We use a separate code path to avoid a
     // transaction for non-tags.
-    mozStorageTransaction transaction(mDB->MainConn(), false);
+    RefPtr<Database> DB = GetDatabase();
+    NS_ENSURE_STATE(DB);
+
+    mozStorageTransaction transaction(DB->MainConn(), false);
 
     rv = SetItemTitleInternal(bookmark, aTitle, syncChangeDelta);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = AddSyncChangesForBookmarksInFolder(bookmark.id, syncChangeDelta);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = transaction.Commit();
@@ -2014,17 +2113,20 @@ nsNavBookmarks::SetItemTitle(int64_t aIt
 }
 
 
 nsresult
 nsNavBookmarks::SetItemTitleInternal(BookmarkData& aBookmark,
                                      const nsACString& aTitle,
                                      int64_t aSyncChangeDelta)
 {
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
     "UPDATE moz_bookmarks SET "
      "title = :item_title, lastModified = :date, "
      "syncChangeCounter = syncChangeCounter + :delta "
     "WHERE id = :item_id"
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
@@ -2135,22 +2237,25 @@ nsresult
 nsNavBookmarks::QueryFolderChildren(
   int64_t aFolderId,
   nsNavHistoryQueryOptions* aOptions,
   nsCOMArray<nsNavHistoryResultNode>* aChildren)
 {
   NS_ENSURE_ARG_POINTER(aOptions);
   NS_ENSURE_ARG_POINTER(aChildren);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Select all children of a given folder, sorted by position.
   // This is a LEFT JOIN because not all bookmarks types have a place.
   // We construct a result where the first columns exactly match those returned
   // by mDBGetURLPageInfo, and additionally contains columns for position,
   // item_child, and folder_child from moz_bookmarks.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
            "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
            "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
            "b.guid, b.position, b.type, b.fk "
     "FROM moz_bookmarks b "
     "LEFT JOIN moz_places h ON b.fk = h.id "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE b.parent = :parent "
@@ -2273,22 +2378,25 @@ nsresult
 nsNavBookmarks::QueryFolderChildrenAsync(
   nsNavHistoryFolderResultNode* aNode,
   int64_t aFolderId,
   mozIStoragePendingStatement** _pendingStmt)
 {
   NS_ENSURE_ARG_POINTER(aNode);
   NS_ENSURE_ARG_POINTER(_pendingStmt);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Select all children of a given folder, sorted by position.
   // This is a LEFT JOIN because not all bookmarks types have a place.
   // We construct a result where the first columns exactly match those returned
   // by mDBGetURLPageInfo, and additionally contains columns for position,
   // item_child, and folder_child from moz_bookmarks.
-  nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+  nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
            "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
            "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
            "b.guid, b.position, b.type, b.fk "
     "FROM moz_bookmarks b "
     "LEFT JOIN moz_places h ON b.fk = h.id "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE b.parent = :parent "
@@ -2312,19 +2420,22 @@ nsresult
 nsNavBookmarks::FetchFolderInfo(int64_t aFolderId,
                                 int32_t* _folderCount,
                                 nsACString& _guid,
                                 int64_t* _parentId)
 {
   *_folderCount = 0;
   *_parentId = -1;
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // This query has to always return results, so it can't be written as a join,
   // though a left join of 2 subqueries would have the same cost.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT count(*), "
             "(SELECT guid FROM moz_bookmarks WHERE id = :parent), "
             "(SELECT parent FROM moz_bookmarks WHERE id = :parent) "
     "FROM moz_bookmarks "
     "WHERE parent = :parent"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
@@ -2358,17 +2469,20 @@ nsNavBookmarks::FetchFolderInfo(int64_t 
 
 
 NS_IMETHODIMP
 nsNavBookmarks::IsBookmarked(nsIURI* aURI, bool* aBookmarked)
 {
   NS_ENSURE_ARG(aURI);
   NS_ENSURE_ARG_POINTER(aBookmarked);
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT 1 FROM moz_bookmarks b "
     "JOIN moz_places h ON b.fk = h.id "
     "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
@@ -2394,16 +2508,19 @@ nsNavBookmarks::GetBookmarkedURIFor(nsIU
   nsAutoCString placeGuid;
   nsresult rv = history->GetIdForPage(aURI, &placeId, placeGuid);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!placeId) {
     // This URI is unknown, just return null.
     return NS_OK;
   }
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Check if a bookmark exists in the redirects chain for this URI.
   // The query will also check if the page is directly bookmarked, and return
   // the first found bookmark in case.  The check is directly on moz_bookmarks
   // without special filtering.
   // The next query finds the bookmarked ancestors in a redirects chain.
   // It won't go further than 3 levels of redirects (a->b->c->your_place_id).
   // To make this path 100% correct (up to any level) we would need either:
   //  - A separate hash, build through recursive querying of the database.
@@ -2442,17 +2559,17 @@ nsNavBookmarks::GetBookmarkedURIFor(nsIU
       "LIMIT 1 "
     ")",
     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
   );
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(query);
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(query);
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId);
   NS_ENSURE_SUCCESS(rv, rv);
   bool hasBookmarkedOrigin;
   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasBookmarkedOrigin)) &&
       hasBookmarkedOrigin) {
@@ -2475,31 +2592,34 @@ nsNavBookmarks::ChangeBookmarkURI(int64_
   NS_ENSURE_ARG_MIN(aBookmarkId, 1);
   NS_ENSURE_ARG(aNewURI);
 
   BookmarkData bookmark;
   nsresult rv = FetchItemInfo(aBookmarkId, bookmark);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_ARG(bookmark.type == TYPE_BOOKMARK);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
 
   bool isTagging = bookmark.grandParentId == mTagsRoot;
   int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
   int64_t newPlaceId;
   nsAutoCString newPlaceGuid;
   rv = history->GetOrCreateIdForPage(aNewURI, &newPlaceId, newPlaceGuid);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!newPlaceId)
     return NS_ERROR_INVALID_ARG;
 
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
     "UPDATE moz_bookmarks SET "
      "fk = :page_id, lastModified = :date, "
      "syncChangeCounter = syncChangeCounter + :delta "
     "WHERE id = :item_id "
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
@@ -2577,20 +2697,23 @@ nsNavBookmarks::GetFolderIdForItem(int64
 
 nsresult
 nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI* aURI,
                                            nsTArray<int64_t>& aResult,
                                            bool aSkipTags)
 {
   NS_ENSURE_ARG(aURI);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Double ordering covers possible lastModified ties, that could happen when
   // importing, syncing or due to extensions.
   // Note: not using a JOIN is cheaper in this case.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "/* do not warn (bug 1175249) */ "
     "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
     "FROM moz_bookmarks b "
     "JOIN moz_bookmarks t on t.id = b.parent "
     "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
     "ORDER BY b.lastModified DESC, b.id DESC "
   );
   NS_ENSURE_STATE(stmt);
@@ -2621,20 +2744,23 @@ nsNavBookmarks::GetBookmarkIdsForURITArr
 }
 
 nsresult
 nsNavBookmarks::GetBookmarksForURI(nsIURI* aURI,
                                    nsTArray<BookmarkData>& aBookmarks)
 {
   NS_ENSURE_ARG(aURI);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Double ordering covers possible lastModified ties, that could happen when
   // importing, syncing or due to extensions.
   // Note: not using a JOIN is cheaper in this case.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "/* do not warn (bug 1175249) */ "
     "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent, b.syncStatus "
     "FROM moz_bookmarks b "
     "JOIN moz_bookmarks t on t.id = b.parent "
     "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) "
     "ORDER BY b.lastModified DESC, b.id DESC "
   );
   NS_ENSURE_STATE(stmt);
@@ -2742,20 +2868,23 @@ nsNavBookmarks::SetItemIndex(int64_t aIt
   int64_t grandParentId;
   nsAutoCString folderGuid;
   rv = FetchFolderInfo(bookmark.parentId, &folderCount, folderGuid, &grandParentId);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(aNewIndex < folderCount, NS_ERROR_INVALID_ARG);
   // Check the parent's guid is the expected one.
   MOZ_ASSERT(bookmark.parentGuid == folderGuid);
 
-  mozStorageTransaction transaction(mDB->MainConn(), false);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false);
 
   {
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "UPDATE moz_bookmarks SET "
        "position = :item_index "
       "WHERE id = :item_id"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
@@ -2767,17 +2896,17 @@ nsNavBookmarks::SetItemIndex(int64_t aIt
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
 
   {
     // Sync stores child indices in the parent's record, so we only need to
     // bump the parent's change counter.
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "UPDATE moz_bookmarks SET "
        "syncChangeCounter = syncChangeCounter + :delta "
       "WHERE id = :parent_id"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent_id"),
@@ -2831,21 +2960,24 @@ nsNavBookmarks::SetKeywordForBookmark(in
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), bookmark.url);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Shortcuts are always lowercased internally.
   nsAutoString keyword(aUserCasedKeyword);
   ToLowerCase(keyword);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // The same URI can be associated to more than one keyword, provided the post
   // data differs.  Check if there are already keywords associated to this uri.
   nsTArray<nsString> oldKeywords;
   {
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "SELECT keyword FROM moz_keywords WHERE place_id = :place_id"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("place_id"), bookmark.placeId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool hasMore;
@@ -2860,21 +2992,21 @@ nsNavBookmarks::SetKeywordForBookmark(in
   // Trying to remove a non-existent keyword is a no-op.
   if (keyword.IsEmpty() && oldKeywords.Length() == 0) {
     return NS_OK;
   }
 
   int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource);
 
   if (keyword.IsEmpty()) {
-    mozStorageTransaction removeTxn(mDB->MainConn(), false);
+    mozStorageTransaction removeTxn(DB->MainConn(), false);
 
     // We are removing the existing keywords.
     for (uint32_t i = 0; i < oldKeywords.Length(); ++i) {
-      nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+      nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
         "DELETE FROM moz_keywords WHERE keyword = :old_keyword"
       );
       NS_ENSURE_STATE(stmt);
       mozStorageStatementScoper scoper(stmt);
       rv = stmt->BindStringByName(NS_LITERAL_CSTRING("old_keyword"),
                                   oldKeywords[i]);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = stmt->Execute();
@@ -2890,17 +3022,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
       nsAutoCString changedIds;
       changedIds.AppendInt(bookmarks[0].id);
       for (uint32_t i = 1; i < bookmarks.Length(); ++i) {
         changedIds.Append(',');
         changedIds.AppendInt(bookmarks[i].id);
       }
       // Update the sync change counter for all bookmarks with the removed
       // keyword.
-      nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+      nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
         NS_LITERAL_CSTRING(
         "UPDATE moz_bookmarks SET "
          "syncChangeCounter = syncChangeCounter + :delta "
         "WHERE id IN (") + changedIds + NS_LITERAL_CSTRING(")")
       );
       NS_ENSURE_STATE(stmt);
       mozStorageStatementScoper scoper(stmt);
 
@@ -2934,17 +3066,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
     return NS_OK;
   }
 
   // A keyword can only be associated to a single URI.  Check if the requested
   // keyword was already associated, in such a case we will need to notify about
   // the change.
   nsCOMPtr<nsIURI> oldUri;
   {
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       "SELECT url "
       "FROM moz_keywords "
       "JOIN moz_places h ON h.id = place_id "
       "WHERE keyword = :keyword"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
     rv = stmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword);
@@ -2958,17 +3090,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
       rv = NS_NewURI(getter_AddRefs(oldUri), spec);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // If another uri is using the new keyword, we must update the keyword entry.
   // Note we cannot use INSERT OR REPLACE cause it wouldn't invoke the delete
   // trigger.
-  mozStorageTransaction updateTxn(mDB->MainConn(), false);
+  mozStorageTransaction updateTxn(DB->MainConn(), false);
 
   nsCOMPtr<mozIStorageStatement> stmt;
   if (oldUri) {
     // In both cases, notify about the change.
     nsTArray<BookmarkData> bookmarks;
     rv = GetBookmarksForURI(oldUri, bookmarks);
     NS_ENSURE_SUCCESS(rv, rv);
     for (uint32_t i = 0; i < bookmarks.Length(); ++i) {
@@ -2982,23 +3114,23 @@ nsNavBookmarks::SetKeywordForBookmark(in
                                      TYPE_BOOKMARK,
                                      bookmarks[i].parentId,
                                      bookmarks[i].guid,
                                      bookmarks[i].parentGuid,
                                      EmptyCString(),
                                      aSource));
     }
 
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "UPDATE moz_keywords SET place_id = :place_id WHERE keyword = :keyword"
     );
     NS_ENSURE_STATE(stmt);
   }
   else {
-    stmt = mDB->GetStatement(
+    stmt = DB->GetStatement(
       "INSERT INTO moz_keywords (keyword, place_id) "
       "VALUES (:keyword, :place_id)"
     );
   }
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("place_id"), bookmark.placeId);
@@ -3016,17 +3148,17 @@ nsNavBookmarks::SetKeywordForBookmark(in
     // Build a query to update all bookmarks in a single statement.
     nsAutoCString changedIds;
     changedIds.AppendInt(bookmarks[0].id);
     for (uint32_t i = 1; i < bookmarks.Length(); ++i) {
       changedIds.Append(',');
       changedIds.AppendInt(bookmarks[i].id);
     }
     // Update the sync change counter for all bookmarks with the new keyword.
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+    nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
       NS_LITERAL_CSTRING(
       "UPDATE moz_bookmarks SET "
        "syncChangeCounter = syncChangeCounter + :delta "
       "WHERE id IN (") + changedIds + NS_LITERAL_CSTRING(")")
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
@@ -3062,19 +3194,22 @@ nsNavBookmarks::SetKeywordForBookmark(in
 
 
 NS_IMETHODIMP
 nsNavBookmarks::GetKeywordForBookmark(int64_t aBookmarkId, nsAString& aKeyword)
 {
   NS_ENSURE_ARG_MIN(aBookmarkId, 1);
   aKeyword.Truncate(0);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // We can have multiple keywords for the same uri, here we'll just return the
   // last created one.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
     "SELECT k.keyword "
     "FROM moz_bookmarks b "
     "JOIN moz_keywords k ON k.place_id = b.fk "
     "WHERE b.id = :item_id "
     "ORDER BY k.ROWID DESC "
     "LIMIT 1"
   ));
   NS_ENSURE_STATE(stmt);
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -464,26 +464,32 @@ private:
 
     int64_t mID;
     uint16_t mSource;
     MOZ_INIT_OUTSIDE_CTOR int64_t mParent;
     nsCString mTitle;
     MOZ_INIT_OUTSIDE_CTOR int32_t mIndex;
   };
 
+  bool mTestingEnabled;
+
   // Used to enable and disable the observer notifications.
   bool mCanNotify;
   nsCategoryCache<nsINavBookmarkObserver> mCacheObservers;
 
   // Tracks whether we are in batch mode.
   // Note: this is only tracking bookmarks batches, not history ones.
   bool mBatching;
 
   /**
    * This function must be called every time a bookmark is removed.
    *
    * @param aURI
    *        Uri to test.
    */
   nsresult UpdateKeywordsForRemovedBookmark(const BookmarkData& aBookmark);
+
+  already_AddRefed<mozilla::places::Database> GetDatabase();
+
+  void ReadTestingEnabled();
 };
 
 #endif // nsNavBookmarks_h_
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -275,16 +275,17 @@ PLACES_FACTORY_SINGLETON_IMPLEMENTATION(
 nsNavHistory::nsNavHistory()
   : mBatchLevel(0)
   , mBatchDBTransaction(nullptr)
   , mCachedNow(0)
   , mRecentTyped(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
   , mRecentLink(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
   , mRecentBookmark(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
   , mEmbedVisits(EMBED_VISITS_INITIAL_CACHE_LENGTH)
+  , mTestingEnabled(false)
   , mHistoryEnabled(true)
   , mNumVisitsForFrecency(10)
   , mTagsFolder(-1)
   , mDaysOfHistory(-1)
   , mLastCachedStartOfDay(INT64_MAX)
   , mLastCachedEndOfDay(0)
   , mCanNotify(true)
   , mCacheObservers("history-observers")
@@ -304,21 +305,19 @@ nsNavHistory::~nsNavHistory()
   if (gHistoryService == this)
     gHistoryService = nullptr;
 }
 
 
 nsresult
 nsNavHistory::Init()
 {
+  ReadTestingEnabled();
   LoadPrefs();
 
-  mDB = Database::GetDatabase();
-  NS_ENSURE_STATE(mDB);
-
   /*****************************************************************************
    *** IMPORTANT NOTICE!
    ***
    *** Nothing after these add observer calls should return anything but NS_OK.
    *** If a failure code is returned, this nsNavHistory object will be held onto
    *** by the observer service and the preference service.
    ****************************************************************************/
 
@@ -335,21 +334,43 @@ nsNavHistory::Init()
   }
 
   // Don't add code that can fail here! Do it up above, before we add our
   // observers.
 
   return NS_OK;
 }
 
+void
+nsNavHistory::ReadTestingEnabled() {
+  mTestingEnabled = Preferences::GetBool(PREF_DATABASE_TESTING_ENABLED, false);
+}
+
+already_AddRefed<Database>
+nsNavHistory::GetDatabase()
+{
+  RefPtr<Database> DB;
+  if (mTestingEnabled) {
+    DB = Database::GetDatabase();
+  } else {
+    if (!mDB) {
+      mDB = Database::GetDatabase();
+    }
+    DB = mDB;
+  }
+  return DB.forget();
+}
+
 NS_IMETHODIMP
 nsNavHistory::GetDatabaseStatus(uint16_t *aDatabaseStatus)
 {
   NS_ENSURE_ARG_POINTER(aDatabaseStatus);
-  *aDatabaseStatus = mDB->GetDatabaseStatus();
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+  *aDatabaseStatus = DB->GetDatabaseStatus();
   return NS_OK;
 }
 
 uint32_t
 nsNavHistory::GetRecentFlags(nsIURI *aURI)
 {
   uint32_t result = 0;
   nsAutoCString spec;
@@ -370,17 +391,20 @@ nsNavHistory::GetRecentFlags(nsIURI *aUR
 
 nsresult
 nsNavHistory::GetIdForPage(nsIURI* aURI,
                            int64_t* _pageId,
                            nsCString& _GUID)
 {
   *_pageId = 0;
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT id, url, title, rev_host, visit_count, guid "
     "FROM moz_places "
     "WHERE url_hash = hash(:page_url) AND url = :page_url "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
@@ -407,18 +431,21 @@ nsNavHistory::GetOrCreateIdForPage(nsIUR
 {
   nsresult rv = GetIdForPage(aURI, _pageId, _GUID);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (*_pageId != 0) {
     return NS_OK;
   }
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Create a new hidden, untyped and unvisited entry.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "INSERT INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid) "
     "VALUES (:page_url, hash(:page_url), :rev_host, :hidden, :frecency, :guid) "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -630,21 +657,24 @@ nsNavHistory::StoreLastInsertedId(const 
 
 int32_t
 nsNavHistory::GetDaysOfHistory() {
   MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread");
 
   if (mDaysOfHistory != -1)
     return mDaysOfHistory;
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_TRUE(DB, 0);
+
   // SQLite doesn't have a CEIL() function, so we must do that later.
   // We should also take into account timers resolution, that may be as bad as
   // 16ms on Windows, so in some cases the difference may be 0, if the
   // check is done near the visit.  Thus remember to check for NULL separately.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT CAST(( "
         "strftime('%s','now','localtime','utc') - "
         "(SELECT MIN(visit_date)/1000000 FROM moz_historyvisits) "
       ") AS DOUBLE) "
     "/86400, "
     "strftime('%s','now','localtime','+1 day','start of day','utc') * 1000000"
   );
   NS_ENSURE_TRUE(stmt, 0);
@@ -1061,17 +1091,20 @@ nsNavHistory::invalidateFrecencies(const
     invalidFrecenciesSQLFragment.AppendLiteral("AND id IN(");
     invalidFrecenciesSQLFragment.Append(aPlaceIdsQueryString);
     invalidFrecenciesSQLFragment.Append(')');
   }
   RefPtr<InvalidateAllFrecenciesCallback> cb =
     aPlaceIdsQueryString.IsEmpty() ? new InvalidateAllFrecenciesCallback()
                                    : nullptr;
 
-  nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
     invalidFrecenciesSQLFragment
   );
   NS_ENSURE_STATE(stmt);
 
   nsCOMPtr<mozIStoragePendingStatement> ps;
   nsresult rv = stmt->ExecuteAsync(cb, getter_AddRefs(ps));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1129,21 +1162,23 @@ nsNavHistory::CanAddURI(nsIURI* aURI, bo
   // Default to false.
   *canAdd = false;
 
   // If history is disabled, don't add any entry.
   if (IsHistoryDisabled()) {
     return NS_OK;
   }
 
+  RefPtr<Database> DB = GetDatabase();
+
   // If the url length is over a threshold, don't add it.
   nsCString spec;
   nsresult rv = aURI->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
-  if (!mDB || spec.Length() > mDB->MaxUrlLength()) {
+  if (!DB || spec.Length() > DB->MaxUrlLength()) {
     return NS_OK;
   }
 
   nsAutoCString scheme;
   rv = aURI->GetScheme(scheme);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // first check the most common cases (HTTP, HTTPS) to allow in to avoid most
@@ -2216,24 +2251,27 @@ nsNavHistory::GetQueryResults(nsNavHisto
 
   nsCString queryString;
   bool paramsPresent = false;
   nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
   nsresult rv = ConstructQueryString(aQueries, aOptions, queryString,
                                      paramsPresent, addParams);
   NS_ENSURE_SUCCESS(rv,rv);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // create statement
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(queryString);
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(queryString);
 #ifdef DEBUG
   if (!statement) {
     nsAutoCString lastErrorString;
-    (void)mDB->MainConn()->GetLastErrorString(lastErrorString);
+    (void)DB->MainConn()->GetLastErrorString(lastErrorString);
     int32_t lastError = 0;
-    (void)mDB->MainConn()->GetLastError(&lastError);
+    (void)DB->MainConn()->GetLastError(&lastError);
     printf("Places failed to create a statement from this query:\n%s\nStorage error (%d): %s\n",
            queryString.get(), lastError, lastErrorString.get());
   }
 #endif
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   if (paramsPresent) {
@@ -2327,18 +2365,21 @@ nsNavHistory::GetObservers(uint32_t* _co
 
   return NS_OK;
 }
 
 // See RunInBatchMode
 nsresult
 nsNavHistory::BeginUpdateBatch()
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   if (mBatchLevel++ == 0) {
-    mBatchDBTransaction = new mozStorageTransaction(mDB->MainConn(), false,
+    mBatchDBTransaction = new mozStorageTransaction(DB->MainConn(), false,
                                                     mozIStorageConnection::TRANSACTION_DEFERRED,
                                                     true);
 
     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                      nsINavHistoryObserver, OnBeginUpdateBatch());
   }
   return NS_OK;
 }
@@ -2396,22 +2437,25 @@ nsNavHistory::GetHistoryDisabled(bool *_
 
 nsresult
 nsNavHistory::RemovePagesInternal(const nsCString& aPlaceIdsQueryString)
 {
   // Return early if there is nothing to delete.
   if (aPlaceIdsQueryString.IsEmpty())
     return NS_OK;
 
-  mozStorageTransaction transaction(mDB->MainConn(), false,
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  mozStorageTransaction transaction(DB->MainConn(), false,
                                     mozIStorageConnection::TRANSACTION_DEFERRED,
                                     true);
 
   // Delete all visits for the specified place ids.
-  nsresult rv = mDB->MainConn()->ExecuteSimpleSQL(
+  nsresult rv = DB->MainConn()->ExecuteSimpleSQL(
     NS_LITERAL_CSTRING(
       "DELETE FROM moz_historyvisits WHERE place_id IN (") +
         aPlaceIdsQueryString +
         NS_LITERAL_CSTRING(")")
   );
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = CleanupPlacesOnVisitsDelete(aPlaceIdsQueryString);
@@ -2436,18 +2480,21 @@ nsNavHistory::RemovePagesInternal(const 
  */
 nsresult
 nsNavHistory::CleanupPlacesOnVisitsDelete(const nsCString& aPlaceIdsQueryString)
 {
   // Return early if there is nothing to delete.
   if (aPlaceIdsQueryString.IsEmpty())
     return NS_OK;
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Collect about-to-be-deleted URIs to notify onDeleteURI.
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
     "SELECT h.id, h.url, h.guid, "
            "(SUBSTR(h.url, 1, 6) <> 'place:' "
            " AND NOT EXISTS (SELECT b.id FROM moz_bookmarks b "
                             "WHERE b.fk = h.id LIMIT 1)) as whole_entry "
     "FROM moz_places h "
     "WHERE h.id IN ( ") + aPlaceIdsQueryString + NS_LITERAL_CSTRING(")")
   );
   NS_ENSURE_STATE(stmt);
@@ -2486,28 +2533,28 @@ nsNavHistory::CleanupPlacesOnVisitsDelet
                        OnDeleteVisits(uri, 0, guid, nsINavHistoryObserver::REASON_DELETED, 0));
     }
   }
 
   // if the entry is not bookmarked and is not a place: uri
   // then we can remove it from moz_places.
   // Note that we do NOT delete favicons. Any unreferenced favicons will be
   // deleted next time the browser is shut down.
-  nsresult rv = mDB->MainConn()->ExecuteSimpleSQL(
+  nsresult rv = DB->MainConn()->ExecuteSimpleSQL(
     NS_LITERAL_CSTRING(
       "DELETE FROM moz_places WHERE id IN ( "
         ) + filteredPlaceIds + NS_LITERAL_CSTRING(
       ") "
     )
   );
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Hosts accumulated during the places delete are updated through a trigger
   // (see nsPlacesTriggers.h).
-  rv = mDB->MainConn()->ExecuteSimpleSQL(
+  rv = DB->MainConn()->ExecuteSimpleSQL(
     NS_LITERAL_CSTRING("DELETE FROM moz_updatehosts_temp")
   );
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Invalidate frecencies of touched places, since they need recalculation.
   rv = invalidateFrecencies(aPlaceIdsQueryString);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2637,18 +2684,21 @@ nsNavHistory::RemovePagesFromHost(const 
 
   // build condition string based on host selection type
   nsAutoCString conditionString;
   if (aEntireDomain)
     conditionString.AssignLiteral("rev_host >= ?1 AND rev_host < ?2 ");
   else
     conditionString.AssignLiteral("rev_host = ?1 ");
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // create statement depending on delete type
-  nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> statement = DB->GetStatement(
     NS_LITERAL_CSTRING("SELECT id FROM moz_places WHERE ") + conditionString
   );
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   rv = statement->BindStringByIndex(0, revHostDot);
   NS_ENSURE_SUCCESS(rv, rv);
   if (aEntireDomain) {
@@ -2692,19 +2742,22 @@ NS_IMETHODIMP
 nsNavHistory::RemovePagesByTimeframe(PRTime aBeginTime, PRTime aEndTime)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
 
   nsresult rv;
   // build a list of place ids to delete
   nsCString deletePlaceIdsQueryString;
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // we only need to know if a place has a visit into the given timeframe
   // this query is faster than actually selecting in moz_historyvisits
-  nsCOMPtr<mozIStorageStatement> selectByTime = mDB->GetStatement(
+  nsCOMPtr<mozIStorageStatement> selectByTime = DB->GetStatement(
     "SELECT h.id FROM moz_places h WHERE "
       "EXISTS "
         "(SELECT id FROM moz_historyvisits v WHERE v.place_id = h.id "
           "AND v.visit_date >= :from_date AND v.visit_date <= :to_date LIMIT 1)"
   );
   NS_ENSURE_STATE(selectByTime);
   mozStorageStatementScoper selectByTimeScoper(selectByTime);
 
@@ -2807,17 +2860,20 @@ nsNavHistory::GetPageTitle(nsIURI* aURI,
 {
   PLACES_WARN_DEPRECATED();
 
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
   NS_ENSURE_ARG(aURI);
 
   aTitle.Truncate(0);
 
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     "SELECT id, url, title, rev_host, visit_count, guid "
     "FROM moz_places "
     "WHERE url_hash = hash(:page_url) AND url = :page_url "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
@@ -2847,19 +2903,20 @@ nsNavHistory::GetDatabaseConnection(mozI
 {
   return GetDBConnection(_DBConnection);
 }
 
 
 NS_IMETHODIMP
 nsNavHistory::GetExpectedDatabasePageSize(int32_t* _expectedPageSize)
 {
-  NS_ENSURE_STATE(mDB);
-  NS_ENSURE_STATE(mDB->MainConn());
-  return mDB->MainConn()->GetDefaultPageSize(_expectedPageSize);
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+  NS_ENSURE_STATE(DB->MainConn());
+  return DB->MainConn()->GetDefaultPageSize(_expectedPageSize);
 }
 
 
 NS_IMETHODIMP
 nsNavHistory::OnBeginVacuum(bool* _vacuumGranted)
 {
   // TODO: Check if we have to deny the vacuum in some heavy-load case.
   // We could maybe want to do that during batches?
@@ -2878,27 +2935,31 @@ nsNavHistory::OnEndVacuum(bool aSucceede
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsPIPlacesDatabase
 
 NS_IMETHODIMP
 nsNavHistory::GetDBConnection(mozIStorageConnection **_DBConnection)
 {
   NS_ENSURE_ARG_POINTER(_DBConnection);
-  RefPtr<mozIStorageConnection> connection = mDB->MainConn();
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+  RefPtr<mozIStorageConnection> connection = DB->MainConn();
   connection.forget(_DBConnection);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavHistory::GetShutdownClient(nsIAsyncShutdownClient **_shutdownClient)
 {
   NS_ENSURE_ARG_POINTER(_shutdownClient);
-  RefPtr<nsIAsyncShutdownClient> client = mDB->GetClientsShutdown();
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+  RefPtr<nsIAsyncShutdownClient> client = DB->GetClientsShutdown();
   MOZ_ASSERT(client);
   client.forget(_shutdownClient);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavHistory::AsyncExecuteLegacyQueries(nsINavHistoryQuery** aQueries,
@@ -2926,26 +2987,29 @@ nsNavHistory::AsyncExecuteLegacyQueries(
 
   nsCString queryString;
   bool paramsPresent = false;
   nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
   nsresult rv = ConstructQueryString(queries, options, queryString,
                                      paramsPresent, addParams);
   NS_ENSURE_SUCCESS(rv,rv);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsCOMPtr<mozIStorageAsyncStatement> statement =
-    mDB->GetAsyncStatement(queryString);
+    DB->GetAsyncStatement(queryString);
   NS_ENSURE_STATE(statement);
 
 #ifdef DEBUG
   if (NS_FAILED(rv)) {
     nsAutoCString lastErrorString;
-    (void)mDB->MainConn()->GetLastErrorString(lastErrorString);
+    (void)DB->MainConn()->GetLastErrorString(lastErrorString);
     int32_t lastError = 0;
-    (void)mDB->MainConn()->GetLastError(&lastError);
+    (void)DB->MainConn()->GetLastError(&lastError);
     printf("Places failed to create a statement from this query:\n%s\nStorage error (%d): %s\n",
            queryString.get(), lastError, lastErrorString.get());
   }
 #endif
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (paramsPresent) {
     // bind parameters
@@ -3003,17 +3067,19 @@ nsNavHistory::Observe(nsISupports *aSubj
                     const char16_t *aData)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
   if (strcmp(aTopic, TOPIC_PROFILE_TEARDOWN) == 0 ||
       strcmp(aTopic, TOPIC_PROFILE_CHANGE) == 0 ||
       strcmp(aTopic, TOPIC_SIMULATE_PLACES_SHUTDOWN) == 0) {
     // These notifications are used by tests to simulate a Places shutdown.
     // They should just be forwarded to the Database handle.
-    mDB->Observe(aSubject, aTopic, aData);
+    RefPtr<Database> DB = GetDatabase();
+    NS_ENSURE_STATE(DB);
+    DB->Observe(aSubject, aTopic, aData);
   }
 
   else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) {
     // Don't even try to notify observers from this point on, the category
     // cache would init services that could try to use our APIs.
     mCanNotify = false;
     mObservers.Clear();
   }
@@ -3099,54 +3165,57 @@ public:
 nsresult
 nsNavHistory::DecayFrecency()
 {
   nsresult rv = FixInvalidFrecencies();
   NS_ENSURE_SUCCESS(rv, rv);
 
   float decayRate = Preferences::GetFloat(PREF_FREC_DECAY_RATE, PREF_FREC_DECAY_RATE_DEF);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   // Globally decay places frecency rankings to estimate reduced frecency
   // values of pages that haven't been visited for a while, i.e., they do
   // not get an updated frecency.  A scaling factor of .975 results in .5 the
   // original value after 28 days.
   // When changing the scaling factor, ensure that the barrier in
   // moz_places_afterupdate_frecency_trigger still ignores these changes.
-  nsCOMPtr<mozIStorageAsyncStatement> decayFrecency = mDB->GetAsyncStatement(
+  nsCOMPtr<mozIStorageAsyncStatement> decayFrecency = DB->GetAsyncStatement(
     "UPDATE moz_places SET frecency = ROUND(frecency * :decay_rate) "
     "WHERE frecency > 0"
   );
   NS_ENSURE_STATE(decayFrecency);
 
   rv = decayFrecency->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
                                        static_cast<double>(decayRate));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Decay potentially unused adaptive entries (e.g. those that are at 1)
   // to allow better chances for new entries that will start at 1.
-  nsCOMPtr<mozIStorageAsyncStatement> decayAdaptive = mDB->GetAsyncStatement(
+  nsCOMPtr<mozIStorageAsyncStatement> decayAdaptive = DB->GetAsyncStatement(
     "UPDATE moz_inputhistory SET use_count = use_count * .975"
   );
   NS_ENSURE_STATE(decayAdaptive);
 
   // Delete any adaptive entries that won't help in ordering anymore.
-  nsCOMPtr<mozIStorageAsyncStatement> deleteAdaptive = mDB->GetAsyncStatement(
+  nsCOMPtr<mozIStorageAsyncStatement> deleteAdaptive = DB->GetAsyncStatement(
     "DELETE FROM moz_inputhistory WHERE use_count < .01"
   );
   NS_ENSURE_STATE(deleteAdaptive);
 
   mozIStorageBaseStatement *stmts[] = {
     decayFrecency.get(),
     decayAdaptive.get(),
     deleteAdaptive.get()
   };
   nsCOMPtr<mozIStoragePendingStatement> ps;
   RefPtr<DecayFrecencyCallback> cb = new DecayFrecencyCallback();
-  rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
-                                     getter_AddRefs(ps));
+  rv = DB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
+                                    getter_AddRefs(ps));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 
 // Query stuff *****************************************************************
 
@@ -4000,39 +4069,42 @@ nsresult
 nsNavHistory::VisitIdToResultNode(int64_t visitId,
                                   nsNavHistoryQueryOptions* aOptions,
                                   nsNavHistoryResultNode** aResult)
 {
   nsAutoCString tagsFragment;
   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
                      true, tagsFragment);
 
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsCOMPtr<mozIStorageStatement> statement;
   switch (aOptions->ResultType())
   {
     case nsNavHistoryQueryOptions::RESULTS_AS_VISIT:
     case nsNavHistoryQueryOptions::RESULTS_AS_FULL_VISIT:
       // visit query - want exact visit time
       // Should match kGetInfoIndex_* (see GetQueryResults)
-      statement = mDB->GetStatement(NS_LITERAL_CSTRING(
+      statement = DB->GetStatement(NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
                "v.visit_date, f.url, null, null, null, null, "
                ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
               "v.id, v.from_visit, v.visit_type "
         "FROM moz_places h "
         "JOIN moz_historyvisits v ON h.id = v.place_id "
         "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE v.id = :visit_id ")
       );
       break;
 
     case nsNavHistoryQueryOptions::RESULTS_AS_URI:
       // URL results - want last visit time
       // Should match kGetInfoIndex_* (see GetQueryResults)
-      statement = mDB->GetStatement(NS_LITERAL_CSTRING(
+      statement = DB->GetStatement(NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
                "h.last_visit_date, f.url, null, null, null, null, "
                ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
               "null, null, null "
         "FROM moz_places h "
         "JOIN moz_historyvisits v ON h.id = v.place_id "
         "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE v.id = :visit_id ")
@@ -4064,21 +4136,24 @@ nsNavHistory::VisitIdToResultNode(int64_
 
   return RowToResult(row, aOptions, aResult);
 }
 
 nsresult
 nsNavHistory::BookmarkIdToResultNode(int64_t aBookmarkId, nsNavHistoryQueryOptions* aOptions,
                                      nsNavHistoryResultNode** aResult)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsAutoCString tagsFragment;
   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
                      true, tagsFragment);
   // Should match kGetInfoIndex_*
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
       "SELECT b.fk, h.url, COALESCE(b.title, h.title), "
              "h.rev_host, h.visit_count, h.last_visit_date, f.url, b.id, "
              "b.dateAdded, b.lastModified, b.parent, "
              ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
              "null, null, null, b.guid, b.position, b.type, b.fk "
       "FROM moz_bookmarks b "
       "JOIN moz_places h ON b.fk = h.id "
       "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
@@ -4105,21 +4180,24 @@ nsNavHistory::BookmarkIdToResultNode(int
   return RowToResult(row, aOptions, aResult);
 }
 
 nsresult
 nsNavHistory::URIToResultNode(nsIURI* aURI,
                               nsNavHistoryQueryOptions* aOptions,
                               nsNavHistoryResultNode** aResult)
 {
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
   nsAutoCString tagsFragment;
   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
                      true, tagsFragment);
   // Should match kGetInfoIndex_*
-  nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
+  nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(NS_LITERAL_CSTRING(
     "SELECT h.id, :page_url, COALESCE(b.title, h.title), "
            "h.rev_host, h.visit_count, h.last_visit_date, f.url, "
            "b.id, b.dateAdded, b.lastModified, b.parent, "
            ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
            "null, null, null, b.guid, b.position, b.type, b.fk "
     "FROM moz_places h "
     "LEFT JOIN moz_bookmarks b ON b.fk = h.id "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
@@ -4350,28 +4428,31 @@ void ParseSearchTermsFromQueries(const n
 }
 
 } // namespace
 
 
 nsresult
 nsNavHistory::UpdateFrecency(int64_t aPlaceId)
 {
-  nsCOMPtr<mozIStorageAsyncStatement> updateFrecencyStmt = mDB->GetAsyncStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageAsyncStatement> updateFrecencyStmt = DB->GetAsyncStatement(
     "UPDATE moz_places "
     "SET frecency = NOTIFY_FRECENCY("
       "CALCULATE_FRECENCY(:page_id), url, guid, hidden, last_visit_date"
     ") "
     "WHERE id = :page_id"
   );
   NS_ENSURE_STATE(updateFrecencyStmt);
   nsresult rv = updateFrecencyStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
                                                     aPlaceId);
   NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<mozIStorageAsyncStatement> updateHiddenStmt = mDB->GetAsyncStatement(
+  nsCOMPtr<mozIStorageAsyncStatement> updateHiddenStmt = DB->GetAsyncStatement(
     "UPDATE moz_places "
     "SET hidden = 0 "
     "WHERE id = :page_id AND frecency <> 0"
   );
   NS_ENSURE_STATE(updateHiddenStmt);
   rv = updateHiddenStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
                                          aPlaceId);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -4379,18 +4460,18 @@ nsNavHistory::UpdateFrecency(int64_t aPl
   mozIStorageBaseStatement *stmts[] = {
     updateFrecencyStmt.get()
   , updateHiddenStmt.get()
   };
 
   RefPtr<AsyncStatementCallbackNotifier> cb =
     new AsyncStatementCallbackNotifier(TOPIC_FRECENCY_UPDATED);
   nsCOMPtr<mozIStoragePendingStatement> ps;
-  rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
-                                     getter_AddRefs(ps));
+  rv = DB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb,
+                                    getter_AddRefs(ps));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 
 namespace {
 
@@ -4415,17 +4496,20 @@ public:
   }
 };
 
 } // namespace
 
 nsresult
 nsNavHistory::FixInvalidFrecencies()
 {
-  nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
     "UPDATE moz_places "
     "SET frecency = CALCULATE_FRECENCY(id) "
     "WHERE frecency < 0"
   );
   NS_ENSURE_STATE(stmt);
 
   RefPtr<FixInvalidFrecenciesCallback> callback =
     new FixInvalidFrecenciesCallback();
@@ -4437,17 +4521,20 @@ nsNavHistory::FixInvalidFrecencies()
 
 
 #ifdef MOZ_XUL
 
 nsresult
 nsNavHistory::AutoCompleteFeedback(int32_t aIndex,
                                    nsIAutoCompleteController *aController)
 {
-  nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
+  RefPtr<Database> DB = GetDatabase();
+  NS_ENSURE_STATE(DB);
+
+  nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
     "INSERT OR REPLACE INTO moz_inputhistory "
     // use_count will asymptotically approach the max of 10.
     "SELECT h.id, IFNULL(i.input, :input_text), IFNULL(i.use_count, 0) * .9 + 1 "
     "FROM moz_places h "
     "LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = :input_text "
     "WHERE url_hash = hash(:page_url) AND url = :page_url "
   );
   NS_ENSURE_STATE(stmt);
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -594,16 +594,20 @@ protected:
                             const nsACString& url);
   void ExpireNonrecentEvents(RecentEventHash* hashTable);
 
 #ifdef MOZ_XUL
   nsresult AutoCompleteFeedback(int32_t aIndex,
                                 nsIAutoCompleteController *aController);
 #endif
 
+  already_AddRefed<mozilla::places::Database> GetDatabase();
+
+  bool mTestingEnabled;
+
   // Whether history is enabled or not.
   // Will mimic value of the places.history.enabled preference.
   bool mHistoryEnabled;
 
   // Frecency preferences.
   int32_t mNumVisitsForFrecency;
   int32_t mFirstBucketCutoffInDays;
   int32_t mSecondBucketCutoffInDays;
@@ -637,16 +641,18 @@ protected:
 
   int32_t mDaysOfHistory;
   int64_t mLastCachedStartOfDay;
   int64_t mLastCachedEndOfDay;
 
   // Used to enable and disable the observer notifications
   bool mCanNotify;
   nsCategoryCache<nsINavHistoryObserver> mCacheObservers;
+
+  void ReadTestingEnabled();
 };
 
 
 #define PLACES_URI_PREFIX "place:"
 
 /* Returns true if the given URI represents a history query. */
 inline bool IsQueryURI(const nsCString &uri)
 {