LCOV - code coverage report
Current view: top level - toolkit/components/places - Database.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 231 1358 17.0 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "mozilla/ArrayUtils.h"
       6             : #include "mozilla/Attributes.h"
       7             : #include "mozilla/DebugOnly.h"
       8             : #include "mozilla/ScopeExit.h"
       9             : #include "mozilla/JSONWriter.h"
      10             : 
      11             : #include "Database.h"
      12             : 
      13             : #include "nsIAnnotationService.h"
      14             : #include "nsIInterfaceRequestorUtils.h"
      15             : #include "nsIFile.h"
      16             : #include "nsIWritablePropertyBag2.h"
      17             : 
      18             : #include "nsNavBookmarks.h"
      19             : #include "nsNavHistory.h"
      20             : #include "nsPlacesTables.h"
      21             : #include "nsPlacesIndexes.h"
      22             : #include "nsPlacesTriggers.h"
      23             : #include "nsPlacesMacros.h"
      24             : #include "nsVariant.h"
      25             : #include "SQLFunctions.h"
      26             : #include "Helpers.h"
      27             : #include "nsFaviconService.h"
      28             : 
      29             : #include "nsAppDirectoryServiceDefs.h"
      30             : #include "nsDirectoryServiceUtils.h"
      31             : #include "prenv.h"
      32             : #include "prsystem.h"
      33             : #include "nsPrintfCString.h"
      34             : #include "mozilla/Preferences.h"
      35             : #include "mozilla/Services.h"
      36             : #include "mozilla/Unused.h"
      37             : #include "prtime.h"
      38             : 
      39             : #include "nsXULAppAPI.h"
      40             : 
      41             : // Time between corrupt database backups.
      42             : #define RECENT_BACKUP_TIME_MICROSEC (int64_t)86400 * PR_USEC_PER_SEC // 24H
      43             : 
      44             : // Filename of the database.
      45             : #define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
      46             : // Filename of the icons database.
      47             : #define DATABASE_FAVICONS_FILENAME NS_LITERAL_STRING("favicons.sqlite")
      48             : 
      49             : // Set to the database file name when it was found corrupt by a previous
      50             : // maintenance run.
      51             : #define PREF_FORCE_DATABASE_REPLACEMENT "places.database.replaceDatabaseOnStartup"
      52             : 
      53             : // Whether on corruption we should try to fix the database by cloning it.
      54             : #define PREF_DATABASE_CLONEONCORRUPTION "places.database.cloneOnCorruption"
      55             : 
      56             : // Set to specify the size of the places database growth increments in kibibytes
      57             : #define PREF_GROWTH_INCREMENT_KIB "places.database.growthIncrementKiB"
      58             : 
      59             : // Set to disable the default robust storage and use volatile, in-memory
      60             : // storage without robust transaction flushing guarantees. This makes
      61             : // SQLite use much less I/O at the cost of losing data when things crash.
      62             : // The pref is only honored if an environment variable is set. The env
      63             : // variable is intentionally named something scary to help prevent someone
      64             : // from thinking it is a useful performance optimization they should enable.
      65             : #define PREF_DISABLE_DURABILITY "places.database.disableDurability"
      66             : #define ENV_ALLOW_CORRUPTION "ALLOW_PLACES_DATABASE_TO_LOSE_DATA_AND_BECOME_CORRUPT"
      67             : 
      68             : // The maximum url length we can store in history.
      69             : // We do not add to history URLs longer than this value.
      70             : #define PREF_HISTORY_MAXURLLEN "places.history.maxUrlLength"
      71             : // This number is mostly a guess based on various facts:
      72             : // * IE didn't support urls longer than 2083 chars
      73             : // * Sitemaps protocol used to support a maximum of 2048 chars
      74             : // * Various SEO guides suggest to not go over 2000 chars
      75             : // * Various apps/services are known to have issues over 2000 chars
      76             : // * RFC 2616 - HTTP/1.1 suggests being cautious about depending
      77             : //   on URI lengths above 255 bytes
      78             : #define PREF_HISTORY_MAXURLLEN_DEFAULT 2000
      79             : 
      80             : #define PREF_MIGRATE_V48_FRECENCIES "places.database.migrateV48Frecencies"
      81             : 
      82             : // Maximum size for the WAL file.
      83             : // For performance reasons this should be as large as possible, so that more
      84             : // transactions can fit into it, and the checkpoint cost is paid less often.
      85             : // At the same time, since we use synchronous = NORMAL, an fsync happens only
      86             : // at checkpoint time, so we don't want the WAL to grow too much and risk to
      87             : // lose all the contained transactions on a crash.
      88             : #define DATABASE_MAX_WAL_BYTES 2048000
      89             : 
      90             : // Since exceeding the journal limit will cause a truncate, we allow a slightly
      91             : // larger limit than DATABASE_MAX_WAL_BYTES to reduce the number of truncates.
      92             : // This is the number of bytes the journal can grow over the maximum wal size
      93             : // before being truncated.
      94             : #define DATABASE_JOURNAL_OVERHEAD_BYTES 2048000
      95             : 
      96             : #define BYTES_PER_KIBIBYTE 1024
      97             : 
      98             : // How much time Sqlite can wait before returning a SQLITE_BUSY error.
      99             : #define DATABASE_BUSY_TIMEOUT_MS 100
     100             : 
     101             : // Old Sync GUID annotation.
     102             : #define SYNCGUID_ANNO NS_LITERAL_CSTRING("sync/guid")
     103             : 
     104             : // Places string bundle, contains internationalized bookmark root names.
     105             : #define PLACES_BUNDLE "chrome://places/locale/places.properties"
     106             : 
     107             : // Livemarks annotations.
     108             : #define LMANNO_FEEDURI "livemark/feedURI"
     109             : #define LMANNO_SITEURI "livemark/siteURI"
     110             : 
     111             : // This is no longer used & obsolete except for during migration.
     112             : // Note: it may still be found in older places databases.
     113             : #define MOBILE_ROOT_ANNO "mobile/bookmarksRoot"
     114             : 
     115             : // This annotation is no longer used & is obsolete, but here for migration.
     116             : #define LAST_USED_ANNO NS_LITERAL_CSTRING("bookmarkPropertiesDialog/folderLastUsed")
     117             : // This is key in the meta table that the LAST_USED_ANNO is migrated to.
     118             : #define LAST_USED_FOLDERS_META_KEY NS_LITERAL_CSTRING("places/bookmarks/edit/lastusedfolder")
     119             : 
     120             : // We use a fixed title for the mobile root to avoid marking the database as
     121             : // corrupt if we can't look up the localized title in the string bundle. Sync
     122             : // sets the title to the localized version when it creates the left pane query.
     123             : #define MOBILE_ROOT_TITLE "mobile"
     124             : 
     125             : using namespace mozilla;
     126             : 
     127             : namespace mozilla {
     128             : namespace places {
     129             : 
     130             : namespace {
     131             : 
     132             : ////////////////////////////////////////////////////////////////////////////////
     133             : //// Helpers
     134             : 
     135             : 
     136             : /**
     137             :  * Get the filename for a corrupt database.
     138             :  */
     139           0 : nsString getCorruptFilename(const nsString& aDbFilename) {
     140           0 :   return aDbFilename + NS_LITERAL_STRING(".corrupt");
     141             : }
     142             : /**
     143             :  * Get the filename for a recover database.
     144             :  */
     145           0 : nsString getRecoverFilename(const nsString& aDbFilename) {
     146           0 :   return aDbFilename + NS_LITERAL_STRING(".recover");
     147             : }
     148             : 
     149             : /**
     150             :  * Checks whether exists a corrupt database file created not longer than
     151             :  * RECENT_BACKUP_TIME_MICROSEC ago.
     152             :  */
     153             : bool
     154           0 : isRecentCorruptFile(const nsCOMPtr<nsIFile>& aCorruptFile)
     155             : {
     156           0 :   MOZ_ASSERT(NS_IsMainThread());
     157           0 :   bool fileExists = false;
     158           0 :   if (NS_FAILED(aCorruptFile->Exists(&fileExists)) || !fileExists) {
     159             :     return false;
     160             :   }
     161           0 :   PRTime lastMod = 0;
     162           0 :   if (NS_FAILED(aCorruptFile->GetLastModifiedTime(&lastMod)) ||
     163           0 :       lastMod <= 0 ||
     164           0 :       (PR_Now() - lastMod) > RECENT_BACKUP_TIME_MICROSEC) {
     165             :     return false;
     166             :   }
     167           0 :   return true;
     168             : }
     169             : 
     170             : /**
     171             :  * Sets the connection journal mode to one of the JOURNAL_* types.
     172             :  *
     173             :  * @param aDBConn
     174             :  *        The database connection.
     175             :  * @param aJournalMode
     176             :  *        One of the JOURNAL_* types.
     177             :  * @returns the current journal mode.
     178             :  * @note this may return a different journal mode than the required one, since
     179             :  *       setting it may fail.
     180             :  */
     181             : enum JournalMode
     182           2 : SetJournalMode(nsCOMPtr<mozIStorageConnection>& aDBConn,
     183             :                enum JournalMode aJournalMode)
     184             : {
     185           2 :   MOZ_ASSERT(NS_IsMainThread());
     186           1 :   nsAutoCString journalMode;
     187           1 :   switch (aJournalMode) {
     188             :     default:
     189           0 :       MOZ_FALLTHROUGH_ASSERT("Trying to set an unknown journal mode.");
     190             :       // Fall through to the default DELETE journal.
     191             :     case JOURNAL_DELETE:
     192           0 :       journalMode.AssignLiteral("delete");
     193           0 :       break;
     194             :     case JOURNAL_TRUNCATE:
     195           0 :       journalMode.AssignLiteral("truncate");
     196           0 :       break;
     197             :     case JOURNAL_MEMORY:
     198           0 :       journalMode.AssignLiteral("memory");
     199           0 :       break;
     200             :     case JOURNAL_WAL:
     201           0 :       journalMode.AssignLiteral("wal");
     202           0 :       break;
     203             :   }
     204             : 
     205           0 :   nsCOMPtr<mozIStorageStatement> statement;
     206           0 :   nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = ");
     207           0 :   query.Append(journalMode);
     208           0 :   aDBConn->CreateStatement(query, getter_AddRefs(statement));
     209           2 :   NS_ENSURE_TRUE(statement, JOURNAL_DELETE);
     210             : 
     211           0 :   bool hasResult = false;
     212           4 :   if (NS_SUCCEEDED(statement->ExecuteStep(&hasResult)) && hasResult &&
     213           2 :       NS_SUCCEEDED(statement->GetUTF8String(0, journalMode))) {
     214           0 :     if (journalMode.EqualsLiteral("delete")) {
     215             :       return JOURNAL_DELETE;
     216             :     }
     217           0 :     if (journalMode.EqualsLiteral("truncate")) {
     218             :       return JOURNAL_TRUNCATE;
     219             :     }
     220           1 :     if (journalMode.EqualsLiteral("memory")) {
     221             :       return JOURNAL_MEMORY;
     222             :     }
     223           2 :     if (journalMode.EqualsLiteral("wal")) {
     224             :       return JOURNAL_WAL;
     225             :     }
     226           0 :     MOZ_ASSERT(false, "Got an unknown journal mode.");
     227             :   }
     228             : 
     229             :   return JOURNAL_DELETE;
     230             : }
     231             : 
     232             : nsresult
     233           5 : CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
     234             :            const nsCString& aRootName, const nsCString& aGuid,
     235             :            const nsCString& titleString, const int32_t position, int64_t& newId)
     236             : {
     237           0 :   MOZ_ASSERT(NS_IsMainThread());
     238             : 
     239             :   // A single creation timestamp for all roots so that the root folder's
     240             :   // last modification time isn't earlier than its childrens' creation time.
     241             :   static PRTime timestamp = 0;
     242           5 :   if (!timestamp)
     243           1 :     timestamp = RoundedPRNow();
     244             : 
     245             :   // Create a new bookmark folder for the root.
     246          10 :   nsCOMPtr<mozIStorageStatement> stmt;
     247          15 :   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     248             :     "INSERT INTO moz_bookmarks "
     249             :       "(type, position, title, dateAdded, lastModified, guid, parent, "
     250             :        "syncChangeCounter, syncStatus) "
     251             :     "VALUES (:item_type, :item_position, :item_title,"
     252             :             ":date_added, :last_modified, :guid, "
     253             :             "IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0), "
     254             :             "1, :sync_status)"
     255           0 :   ), getter_AddRefs(stmt));
     256           0 :   if (NS_FAILED(rv)) return rv;
     257             : 
     258           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
     259           0 :                              nsINavBookmarksService::TYPE_FOLDER);
     260           0 :   if (NS_FAILED(rv)) return rv;
     261           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"), position);
     262           0 :   if (NS_FAILED(rv)) return rv;
     263           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
     264           0 :                                   titleString);
     265           0 :   if (NS_FAILED(rv)) return rv;
     266           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), timestamp);
     267           0 :   if (NS_FAILED(rv)) return rv;
     268           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), timestamp);
     269           0 :   if (NS_FAILED(rv)) return rv;
     270           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGuid);
     271           5 :   if (NS_FAILED(rv)) return rv;
     272           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("sync_status"),
     273          15 :                              nsINavBookmarksService::SYNC_STATUS_NEW);
     274           5 :   if (NS_FAILED(rv)) return rv;
     275           5 :   rv = stmt->Execute();
     276           5 :   if (NS_FAILED(rv)) return rv;
     277             : 
     278           5 :   newId = nsNavBookmarks::sLastInsertedItemId;
     279             :   return NS_OK;
     280             : }
     281             : 
     282             : nsresult
     283           1 : SetupDurability(nsCOMPtr<mozIStorageConnection>& aDBConn, int32_t aDBPageSize) {
     284             :   nsresult rv;
     285           1 :   if (PR_GetEnv(ENV_ALLOW_CORRUPTION) &&
     286           0 :       Preferences::GetBool(PREF_DISABLE_DURABILITY, false)) {
     287             :     // Volatile storage was requested. Use the in-memory journal (no
     288             :     // filesystem I/O) and don't sync the filesystem after writing.
     289           0 :     SetJournalMode(aDBConn, JOURNAL_MEMORY);
     290           0 :     rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     291           0 :       "PRAGMA synchronous = OFF"));
     292           0 :     NS_ENSURE_SUCCESS(rv, rv);
     293             :   } else {
     294             :     // Be sure to set journal mode after page_size.  WAL would prevent the change
     295             :     // otherwise.
     296           0 :     if (JOURNAL_WAL == SetJournalMode(aDBConn, JOURNAL_WAL)) {
     297             :       // Set the WAL journal size limit.
     298             :       int32_t checkpointPages =
     299           2 :         static_cast<int32_t>(DATABASE_MAX_WAL_BYTES / aDBPageSize);
     300           4 :       nsAutoCString checkpointPragma("PRAGMA wal_autocheckpoint = ");
     301           2 :       checkpointPragma.AppendInt(checkpointPages);
     302           2 :       rv = aDBConn->ExecuteSimpleSQL(checkpointPragma);
     303           1 :       NS_ENSURE_SUCCESS(rv, rv);
     304             :     }
     305             :     else {
     306             :       // Ignore errors, if we fail here the database could be considered corrupt
     307             :       // and we won't be able to go on, even if it's just matter of a bogus file
     308             :       // system.  The default mode (DELETE) will be fine in such a case.
     309           0 :       (void)SetJournalMode(aDBConn, JOURNAL_TRUNCATE);
     310             : 
     311             :       // Set synchronous to FULL to ensure maximum data integrity, even in
     312             :       // case of crashes or unclean shutdowns.
     313           0 :       rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     314           0 :           "PRAGMA synchronous = FULL"));
     315           0 :       NS_ENSURE_SUCCESS(rv, rv);
     316             :     }
     317             :   }
     318             : 
     319             :   // The journal is usually free to grow for performance reasons, but it never
     320             :   // shrinks back.  Since the space taken may be problematic, limit its size.
     321           2 :   nsAutoCString journalSizePragma("PRAGMA journal_size_limit = ");
     322           0 :   journalSizePragma.AppendInt(DATABASE_MAX_WAL_BYTES + DATABASE_JOURNAL_OVERHEAD_BYTES);
     323           0 :   (void)aDBConn->ExecuteSimpleSQL(journalSizePragma);
     324             : 
     325             :   // Grow places in |growthIncrementKiB| increments to limit fragmentation on disk.
     326             :   // By default, it's 5 MB.
     327             :   int32_t growthIncrementKiB =
     328           2 :     Preferences::GetInt(PREF_GROWTH_INCREMENT_KIB, 5 * BYTES_PER_KIBIBYTE);
     329           2 :   if (growthIncrementKiB > 0) {
     330           0 :     (void)aDBConn->SetGrowthIncrement(growthIncrementKiB * BYTES_PER_KIBIBYTE, EmptyCString());
     331             :   }
     332           2 :   return NS_OK;
     333             : }
     334             : 
     335             : nsresult
     336           0 : AttachDatabase(nsCOMPtr<mozIStorageConnection>& aDBConn,
     337             :                const nsACString& aPath,
     338             :                const nsACString& aName) {
     339           0 :   nsCOMPtr<mozIStorageStatement> stmt;
     340           0 :   nsresult rv = aDBConn->CreateStatement(
     341           0 :     NS_LITERAL_CSTRING("ATTACH DATABASE :path AS ") + aName,
     342           4 :     getter_AddRefs(stmt));
     343           1 :   NS_ENSURE_SUCCESS(rv, rv);
     344           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("path"), aPath);
     345           0 :   NS_ENSURE_SUCCESS(rv, rv);
     346           0 :   rv = stmt->Execute();
     347           1 :   NS_ENSURE_SUCCESS(rv, rv);
     348             : 
     349             :   // The journal limit must be set apart for each database.
     350           1 :   nsAutoCString journalSizePragma("PRAGMA favicons.journal_size_limit = ");
     351           1 :   journalSizePragma.AppendInt(DATABASE_MAX_WAL_BYTES + DATABASE_JOURNAL_OVERHEAD_BYTES);
     352           1 :   Unused << aDBConn->ExecuteSimpleSQL(journalSizePragma);
     353             : 
     354           1 :   return NS_OK;
     355             : }
     356             : 
     357             : } // namespace
     358             : 
     359             : ////////////////////////////////////////////////////////////////////////////////
     360             : //// Database
     361             : 
     362          69 : PLACES_FACTORY_SINGLETON_IMPLEMENTATION(Database, gDatabase)
     363             : 
     364         135 : NS_IMPL_ISUPPORTS(Database
     365             : , nsIObserver
     366             : , nsISupportsWeakReference
     367             : )
     368             : 
     369           1 : Database::Database()
     370             :   : mMainThreadStatements(mMainConn)
     371             :   , mMainThreadAsyncStatements(mMainConn)
     372             :   , mAsyncThreadStatements(mMainConn)
     373             :   , mDBPageSize(0)
     374             :   , mDatabaseStatus(nsINavHistoryService::DATABASE_STATUS_OK)
     375             :   , mClosed(false)
     376             :   , mShouldConvertIconPayloads(false)
     377             :   , mShouldVacuumIcons(false)
     378           1 :   , mClientsShutdown(new ClientsShutdownBlocker())
     379           1 :   , mConnectionShutdown(new ConnectionShutdownBlocker(this))
     380             :   , mMaxUrlLength(0)
     381             :   , mCacheObservers(TOPIC_PLACES_INIT_COMPLETE)
     382             :   , mRootId(-1)
     383             :   , mMenuRootId(-1)
     384             :   , mTagsRootId(-1)
     385             :   , mUnfiledRootId(-1)
     386             :   , mToolbarRootId(-1)
     387           0 :   , mMobileRootId(-1)
     388             : {
     389           1 :   MOZ_ASSERT(!XRE_IsContentProcess(),
     390             :              "Cannot instantiate Places in the content process");
     391             :   // Attempting to create two instances of the service?
     392           1 :   MOZ_ASSERT(!gDatabase);
     393           0 :   gDatabase = this;
     394           0 : }
     395             : 
     396             : already_AddRefed<nsIAsyncShutdownClient>
     397           1 : Database::GetProfileChangeTeardownPhase()
     398             : {
     399           2 :   nsCOMPtr<nsIAsyncShutdownService> asyncShutdownSvc = services::GetAsyncShutdown();
     400           0 :   MOZ_ASSERT(asyncShutdownSvc);
     401           0 :   if (NS_WARN_IF(!asyncShutdownSvc)) {
     402             :     return nullptr;
     403             :   }
     404             : 
     405             :   // Consumers of Places should shutdown before us, at profile-change-teardown.
     406           2 :   nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
     407           1 :   DebugOnly<nsresult> rv = asyncShutdownSvc->
     408           0 :     GetProfileChangeTeardown(getter_AddRefs(shutdownPhase));
     409           1 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
     410           0 :   return shutdownPhase.forget();
     411             : }
     412             : 
     413             : already_AddRefed<nsIAsyncShutdownClient>
     414           1 : Database::GetProfileBeforeChangePhase()
     415             : {
     416           2 :   nsCOMPtr<nsIAsyncShutdownService> asyncShutdownSvc = services::GetAsyncShutdown();
     417           0 :   MOZ_ASSERT(asyncShutdownSvc);
     418           0 :   if (NS_WARN_IF(!asyncShutdownSvc)) {
     419             :     return nullptr;
     420             :   }
     421             : 
     422             :   // Consumers of Places should shutdown before us, at profile-change-teardown.
     423           2 :   nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
     424           0 :   DebugOnly<nsresult> rv = asyncShutdownSvc->
     425           4 :     GetProfileBeforeChange(getter_AddRefs(shutdownPhase));
     426           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
     427           1 :   return shutdownPhase.forget();
     428             : }
     429             : 
     430           0 : Database::~Database()
     431             : {
     432           0 : }
     433             : 
     434             : bool
     435           0 : Database::IsShutdownStarted() const
     436             : {
     437          64 :   if (!mConnectionShutdown) {
     438             :     // We have already broken the cycle between `this` and `mConnectionShutdown`.
     439             :     return true;
     440             :   }
     441           0 :   return mConnectionShutdown->IsStarted();
     442             : }
     443             : 
     444             : already_AddRefed<mozIStorageAsyncStatement>
     445           0 : Database::GetAsyncStatement(const nsACString& aQuery)
     446             : {
     447           1 :   if (IsShutdownStarted() || NS_FAILED(EnsureConnection())) {
     448             :     return nullptr;
     449             :   }
     450             : 
     451           1 :   MOZ_ASSERT(NS_IsMainThread());
     452           0 :   return mMainThreadAsyncStatements.GetCachedStatement(aQuery);
     453             : }
     454             : 
     455             : already_AddRefed<mozIStorageStatement>
     456           0 : Database::GetStatement(const nsACString& aQuery)
     457             : {
     458          30 :   if (IsShutdownStarted()) {
     459             :     return nullptr;
     460             :   }
     461          30 :   if (NS_IsMainThread()) {
     462           2 :     if (NS_FAILED(EnsureConnection())) {
     463             :       return nullptr;
     464             :     }
     465           2 :     return mMainThreadStatements.GetCachedStatement(aQuery);
     466             :   }
     467             :   // In the async case, the connection must have been started on the main-thread
     468             :   // already.
     469          56 :   MOZ_ASSERT(mMainConn);
     470           0 :   return mAsyncThreadStatements.GetCachedStatement(aQuery);
     471             : }
     472             : 
     473             : already_AddRefed<nsIAsyncShutdownClient>
     474           1 : Database::GetClientsShutdown()
     475             : {
     476           0 :   if (mClientsShutdown)
     477           1 :     return mClientsShutdown->GetClient();
     478             :   return nullptr;
     479             : }
     480             : 
     481             : already_AddRefed<nsIAsyncShutdownClient>
     482           3 : Database::GetConnectionShutdown()
     483             : {
     484           6 :   if (mConnectionShutdown)
     485           0 :     return mConnectionShutdown->GetClient();
     486             :   return nullptr;
     487             : }
     488             : 
     489             : // static
     490             : already_AddRefed<Database>
     491          34 : Database::GetDatabase()
     492             : {
     493          34 :   if (PlacesShutdownBlocker::IsStarted()) {
     494             :     return nullptr;
     495             :   }
     496           0 :   return GetSingleton();
     497             : }
     498             : 
     499             : nsresult
     500           1 : Database::Init()
     501             : {
     502           1 :   MOZ_ASSERT(NS_IsMainThread());
     503             : 
     504             :   // DO NOT FAIL HERE, otherwise we would never break the cycle between this
     505             :   // object and the shutdown blockers, causing unexpected leaks.
     506             : 
     507             :   {
     508             :     // First of all Places clients should block profile-change-teardown.
     509           2 :     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileChangeTeardownPhase();
     510           0 :     MOZ_ASSERT(shutdownPhase);
     511           0 :     if (shutdownPhase) {
     512           3 :       DebugOnly<nsresult> rv = shutdownPhase->AddBlocker(
     513           2 :         static_cast<nsIAsyncShutdownBlocker*>(mClientsShutdown.get()),
     514           1 :         NS_LITERAL_STRING(__FILE__),
     515             :         __LINE__,
     516           7 :         NS_LITERAL_STRING(""));
     517           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     518             :     }
     519             :   }
     520             : 
     521             :   {
     522             :     // Then connection closing should block profile-before-change.
     523           2 :     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileBeforeChangePhase();
     524           0 :     MOZ_ASSERT(shutdownPhase);
     525           0 :     if (shutdownPhase) {
     526           3 :       DebugOnly<nsresult> rv = shutdownPhase->AddBlocker(
     527           2 :         static_cast<nsIAsyncShutdownBlocker*>(mConnectionShutdown.get()),
     528           1 :         NS_LITERAL_STRING(__FILE__),
     529             :         __LINE__,
     530           0 :         NS_LITERAL_STRING(""));
     531           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     532             :     }
     533             :   }
     534             : 
     535             :   // Finally observe profile shutdown notifications.
     536           2 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     537           1 :   if (os) {
     538           0 :     (void)os->AddObserver(this, TOPIC_PROFILE_CHANGE_TEARDOWN, true);
     539             :   }
     540           1 :   return NS_OK;
     541             : }
     542             : 
     543             : nsresult
     544          31 : Database::EnsureConnection()
     545             : {
     546             :   // Run this only once.
     547          32 :   if (mMainConn ||
     548          63 :       mDatabaseStatus == nsINavHistoryService::DATABASE_STATUS_LOCKED) {
     549             :     return NS_OK;
     550             :   }
     551             :   // Don't try to create a database too late.
     552           1 :   if (IsShutdownStarted()) {
     553             :     return NS_ERROR_FAILURE;
     554             :   }
     555             : 
     556           1 :   MOZ_ASSERT(NS_IsMainThread(),
     557             :              "Database initialization must happen on the main-thread");
     558             : 
     559             :   {
     560           0 :     bool initSucceeded = false;
     561           1 :     auto notify = MakeScopeExit([&] () {
     562             :       // If the database connection cannot be opened, it may just be locked
     563             :       // by third parties.  Set a locked state.
     564           0 :       if (!initSucceeded) {
     565           1 :         mMainConn = nullptr;
     566           0 :         mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_LOCKED;
     567             :       }
     568             :       // Notify at the next tick, to avoid re-entrancy problems.
     569           2 :       NS_DispatchToMainThread(
     570           0 :         NewRunnableMethod("places::Database::EnsureConnection()",
     571             :                           this, &Database::NotifyConnectionInitalized)
     572           1 :       );
     573           0 :     });
     574             : 
     575             :     nsCOMPtr<mozIStorageService> storage =
     576           0 :       do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
     577           1 :     NS_ENSURE_STATE(storage);
     578             : 
     579           0 :     nsCOMPtr<nsIFile> profileDir;
     580           0 :     nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     581           0 :                                          getter_AddRefs(profileDir));
     582           0 :     NS_ENSURE_SUCCESS(rv, rv);
     583             : 
     584           0 :     nsCOMPtr<nsIFile> databaseFile;
     585           0 :     rv = profileDir->Clone(getter_AddRefs(databaseFile));
     586           1 :     NS_ENSURE_SUCCESS(rv, rv);
     587           0 :     rv = databaseFile->Append(DATABASE_FILENAME);
     588           0 :     NS_ENSURE_SUCCESS(rv, rv);
     589           0 :     bool databaseExisted = false;
     590           0 :     rv = databaseFile->Exists(&databaseExisted);
     591           1 :     NS_ENSURE_SUCCESS(rv, rv);
     592             : 
     593           2 :     nsAutoString corruptDbName;
     594           0 :     if (NS_SUCCEEDED(Preferences::GetString(PREF_FORCE_DATABASE_REPLACEMENT,
     595           1 :                                             corruptDbName)) &&
     596           0 :         !corruptDbName.IsEmpty()) {
     597             :       // If this pref is set, maintenance required a database replacement, due to
     598             :       // integrity corruption.
     599             :       // Be sure to clear the pref to avoid handling it more than once.
     600           0 :       (void)Preferences::ClearUser(PREF_FORCE_DATABASE_REPLACEMENT);
     601             : 
     602             :       // The database is corrupt, backup and replace it with a new one.
     603           0 :       nsCOMPtr<nsIFile> fileToBeReplaced;
     604           0 :       bool fileExists = false;
     605           0 :       if (NS_SUCCEEDED(profileDir->Clone(getter_AddRefs(fileToBeReplaced))) &&
     606           0 :           NS_SUCCEEDED(fileToBeReplaced->Exists(&fileExists)) &&
     607             :          fileExists) {
     608           0 :         rv = BackupAndReplaceDatabaseFile(storage, corruptDbName, true, false);
     609           0 :         NS_ENSURE_SUCCESS(rv, rv);
     610             :       }
     611             :     }
     612             : 
     613             :     // Open the database file.  If it does not exist a new one will be created.
     614             :     // Use an unshared connection, it will consume more memory but avoid shared
     615             :     // cache contentions across threads.
     616           0 :     rv = storage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
     617           1 :     if (NS_SUCCEEDED(rv) && !databaseExisted) {
     618           1 :       mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CREATE;
     619             :     }
     620           0 :     else if (rv == NS_ERROR_FILE_CORRUPTED) {
     621             :       // The database is corrupt, backup and replace it with a new one.
     622           0 :       rv = BackupAndReplaceDatabaseFile(storage, DATABASE_FILENAME, true, true);
     623             :       // Fallback to catch-all handler.
     624             :     }
     625           0 :     NS_ENSURE_SUCCESS(rv, rv);
     626             : 
     627             :     // Initialize the database schema.  In case of failure the existing schema is
     628             :     // is corrupt or incoherent, thus the database should be replaced.
     629           0 :     bool databaseMigrated = false;
     630           1 :     rv = SetupDatabaseConnection(storage);
     631           1 :     bool shouldTryToCloneDb = true;
     632           0 :     if (NS_SUCCEEDED(rv)) {
     633             :       // Failing to initialize the schema may indicate a corruption.
     634           0 :       rv = InitSchema(&databaseMigrated);
     635           0 :       if (NS_FAILED(rv)) {
     636             :         // Cloning the db on a schema migration may not be a good idea, since we
     637             :         // may end up cloning the schema problems.
     638           0 :         shouldTryToCloneDb = false;
     639           0 :         if (rv == NS_ERROR_STORAGE_BUSY ||
     640           0 :             rv == NS_ERROR_FILE_IS_LOCKED ||
     641           0 :             rv == NS_ERROR_FILE_NO_DEVICE_SPACE ||
     642           0 :             rv == NS_ERROR_OUT_OF_MEMORY) {
     643             :           // The database is not corrupt, though some migration step failed.
     644             :           // This may be caused by concurrent use of sync and async Storage APIs
     645             :           // or by a system issue.
     646             :           // The best we can do is trying again. If it should still fail, Places
     647             :           // won't work properly and will be handled as LOCKED.
     648           0 :           rv = InitSchema(&databaseMigrated);
     649           0 :           if (NS_FAILED(rv)) {
     650           0 :             rv = NS_ERROR_FILE_IS_LOCKED;
     651             :           }
     652             :         } else {
     653             :           rv = NS_ERROR_FILE_CORRUPTED;
     654             :         }
     655             :       }
     656             :     }
     657           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     658           0 :       if (rv != NS_ERROR_FILE_IS_LOCKED) {
     659           0 :         mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
     660             :       }
     661             :       // Some errors may not indicate a database corruption, for those cases we
     662             :       // just bail out without throwing away a possibly valid places.sqlite.
     663           0 :       if (rv == NS_ERROR_FILE_CORRUPTED) {
     664             :         // Since we don't know which database is corrupt, we must replace both.
     665           0 :         rv = BackupAndReplaceDatabaseFile(storage, DATABASE_FAVICONS_FILENAME, false, false);
     666           0 :         NS_ENSURE_SUCCESS(rv, rv);
     667           0 :         rv = BackupAndReplaceDatabaseFile(storage, DATABASE_FILENAME, shouldTryToCloneDb, true);
     668           0 :         NS_ENSURE_SUCCESS(rv, rv);
     669             :         // Try to initialize the new database again.
     670           0 :         rv = SetupDatabaseConnection(storage);
     671           0 :         NS_ENSURE_SUCCESS(rv, rv);
     672           0 :         rv = InitSchema(&databaseMigrated);
     673             :       }
     674             :       // Bail out if we couldn't fix the database.
     675           0 :       NS_ENSURE_SUCCESS(rv, rv);
     676             :     }
     677             : 
     678           1 :     if (databaseMigrated) {
     679           0 :       mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_UPGRADED;
     680             :     }
     681             : 
     682             :     // Initialize here all the items that are not part of the on-disk database,
     683             :     // like views, temp triggers or temp tables.  The database should not be
     684             :     // considered corrupt if any of the following fails.
     685             : 
     686           0 :     rv = InitTempEntities();
     687           1 :     NS_ENSURE_SUCCESS(rv, rv);
     688             : 
     689           1 :     rv = CheckRoots();
     690           1 :     NS_ENSURE_SUCCESS(rv, rv);
     691             : 
     692           0 :     initSucceeded = true;
     693             :   }
     694           0 :   return NS_OK;
     695             : }
     696             : 
     697             : nsresult
     698           0 : Database::NotifyConnectionInitalized()
     699             : {
     700           1 :   MOZ_ASSERT(NS_IsMainThread());
     701             :   // Notify about Places initialization.
     702           0 :   nsCOMArray<nsIObserver> entries;
     703           0 :   mCacheObservers.GetEntries(entries);
     704           4 :   for (int32_t idx = 0; idx < entries.Count(); ++idx) {
     705           0 :     MOZ_ALWAYS_SUCCEEDS(entries[idx]->Observe(nullptr, TOPIC_PLACES_INIT_COMPLETE, nullptr));
     706             :   }
     707           2 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     708           1 :   if (obs) {
     709           0 :     MOZ_ALWAYS_SUCCEEDS(obs->NotifyObservers(nullptr, TOPIC_PLACES_INIT_COMPLETE, nullptr));
     710             :   }
     711           0 :   return NS_OK;
     712             : }
     713             : 
     714             : nsresult
     715           0 : Database::EnsureFaviconsDatabaseAttached(const nsCOMPtr<mozIStorageService>& aStorage)
     716             : {
     717           0 :   MOZ_ASSERT(NS_IsMainThread());
     718             : 
     719           0 :   nsCOMPtr<nsIFile> databaseFile;
     720           0 :   NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(databaseFile));
     721           1 :   NS_ENSURE_STATE(databaseFile);
     722           0 :   nsresult rv = databaseFile->Append(DATABASE_FAVICONS_FILENAME);
     723           0 :   NS_ENSURE_SUCCESS(rv, rv);
     724           0 :   nsString iconsPath;
     725           0 :   rv = databaseFile->GetPath(iconsPath);
     726           1 :   NS_ENSURE_SUCCESS(rv, rv);
     727             : 
     728           1 :   bool fileExists = false;
     729           0 :   if (NS_SUCCEEDED(databaseFile->Exists(&fileExists)) && fileExists) {
     730           0 :     return AttachDatabase(mMainConn, NS_ConvertUTF16toUTF8(iconsPath),
     731           0 :                           NS_LITERAL_CSTRING("favicons"));
     732             :   }
     733             : 
     734             :   // Open the database file, this will also create it.
     735           0 :   nsCOMPtr<mozIStorageConnection> conn;
     736           3 :   rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(conn));
     737           1 :   NS_ENSURE_SUCCESS(rv, rv);
     738             : 
     739             :   {
     740             :     // Ensure we'll close the connection when done.
     741           1 :     auto cleanup = MakeScopeExit([&] () {
     742             :       // We cannot use AsyncClose() here, because by the time we try to ATTACH
     743             :       // this database, its transaction could be still be running and that would
     744             :       // cause the ATTACH query to fail.
     745           0 :       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(conn->Close()));
     746           3 :     });
     747             : 
     748             :     // Enable incremental vacuum for this database. Since it will contain even
     749             :     // large blobs and can be cleared with history, it's worth to have it.
     750             :     // Note that it will be necessary to manually use PRAGMA incremental_vacuum.
     751           0 :     rv = conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     752             :       "PRAGMA auto_vacuum = INCREMENTAL"
     753           0 :     ));
     754           0 :     NS_ENSURE_SUCCESS(rv, rv);
     755             : 
     756             :     int32_t defaultPageSize;
     757           1 :     rv = conn->GetDefaultPageSize(&defaultPageSize);
     758           0 :     NS_ENSURE_SUCCESS(rv, rv);
     759           0 :     rv = SetupDurability(conn, defaultPageSize);
     760           0 :     NS_ENSURE_SUCCESS(rv, rv);
     761             : 
     762             :     // We are going to update the database, so everything from now on should be
     763             :     // in a transaction for performances.
     764           0 :     mozStorageTransaction transaction(conn, false);
     765           0 :     rv = conn->ExecuteSimpleSQL(CREATE_MOZ_ICONS);
     766           0 :     NS_ENSURE_SUCCESS(rv, rv);
     767           0 :     rv = conn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ICONS_ICONURLHASH);
     768           0 :     NS_ENSURE_SUCCESS(rv, rv);
     769           0 :     rv = conn->ExecuteSimpleSQL(CREATE_MOZ_PAGES_W_ICONS);
     770           0 :     NS_ENSURE_SUCCESS(rv, rv);
     771           3 :     rv = conn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PAGES_W_ICONS_ICONURLHASH);
     772           1 :     NS_ENSURE_SUCCESS(rv, rv);
     773           3 :     rv = conn->ExecuteSimpleSQL(CREATE_MOZ_ICONS_TO_PAGES);
     774           1 :     NS_ENSURE_SUCCESS(rv, rv);
     775           0 :     rv = transaction.Commit();
     776           0 :     NS_ENSURE_SUCCESS(rv, rv);
     777             : 
     778             :     // The scope exit will take care of closing the connection.
     779             :   }
     780             : 
     781           2 :   rv = AttachDatabase(mMainConn, NS_ConvertUTF16toUTF8(iconsPath),
     782           4 :                       NS_LITERAL_CSTRING("favicons"));
     783           1 :   NS_ENSURE_SUCCESS(rv, rv);
     784             : 
     785             :   return NS_OK;
     786             : }
     787             : 
     788             : 
     789             : nsresult
     790           0 : Database::BackupAndReplaceDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage,
     791             :                                        const nsString& aDbFilename,
     792             :                                        bool aTryToClone,
     793             :                                        bool aReopenConnection)
     794             : {
     795           0 :   MOZ_ASSERT(NS_IsMainThread());
     796             : 
     797           0 :   if (aDbFilename.Equals(DATABASE_FILENAME)) {
     798           0 :     mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
     799             :   } else {
     800             :     // Due to OS file lockings, attached databases can't be cloned properly,
     801             :     // otherwise trying to reattach them later would fail.
     802             :     aTryToClone = false;
     803             :   }
     804             : 
     805           0 :   nsCOMPtr<nsIFile> profDir;
     806           0 :   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     807           0 :                                        getter_AddRefs(profDir));
     808           0 :   NS_ENSURE_SUCCESS(rv, rv);
     809           0 :   nsCOMPtr<nsIFile> databaseFile;
     810           0 :   rv = profDir->Clone(getter_AddRefs(databaseFile));
     811           0 :   NS_ENSURE_SUCCESS(rv, rv);
     812           0 :   rv = databaseFile->Append(aDbFilename);
     813           0 :   NS_ENSURE_SUCCESS(rv, rv);
     814             : 
     815             :   // If we already failed in the last 24 hours avoid to create another corrupt file,
     816             :   // since doing so, in some situation, could cause us to create a new corrupt
     817             :   // file at every try to access any Places service.  That is bad because it
     818             :   // would quickly fill the user's disk space without any notice.
     819           0 :   nsCOMPtr<nsIFile> corruptFile;
     820           0 :   rv = profDir->Clone(getter_AddRefs(corruptFile));
     821           0 :   NS_ENSURE_SUCCESS(rv, rv);
     822           0 :   nsString corruptFilename = getCorruptFilename(aDbFilename);
     823           0 :   rv = corruptFile->Append(corruptFilename);
     824           0 :   NS_ENSURE_SUCCESS(rv, rv);
     825           0 :   if (!isRecentCorruptFile(corruptFile)) {
     826             :     // Ensure we never create more than one corrupt file.
     827           0 :     nsCOMPtr<nsIFile> corruptFile;
     828           0 :     rv = profDir->Clone(getter_AddRefs(corruptFile));
     829           0 :     NS_ENSURE_SUCCESS(rv, rv);
     830           0 :     nsString corruptFilename = getCorruptFilename(aDbFilename);
     831           0 :     rv = corruptFile->Append(corruptFilename);
     832           0 :     NS_ENSURE_SUCCESS(rv, rv);
     833           0 :     rv = corruptFile->Remove(false);
     834           0 :     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
     835           0 :                          rv != NS_ERROR_FILE_NOT_FOUND) {
     836             :       return rv;
     837             :     }
     838             : 
     839           0 :     nsCOMPtr<nsIFile> backup;
     840           0 :     Unused << aStorage->BackupDatabaseFile(databaseFile, corruptFilename,
     841           0 :                                            profDir, getter_AddRefs(backup));
     842             :   }
     843             : 
     844             :   // If anything fails from this point on, we have a stale connection or
     845             :   // database file, and there's not much more we can do.
     846             :   // The only thing we can try to do is to replace the database on the next
     847             :   // startup, and report the problem through telemetry.
     848             :   {
     849             :     enum eCorruptDBReplaceStage : int8_t {
     850             :       stage_closing = 0,
     851             :       stage_removing,
     852             :       stage_reopening,
     853             :       stage_replaced,
     854             :       stage_cloning,
     855             :       stage_cloned
     856             :     };
     857           0 :     eCorruptDBReplaceStage stage = stage_closing;
     858           0 :     auto guard = MakeScopeExit([&]() {
     859           0 :       if (stage != stage_replaced) {
     860             :         // Reaching this point means the database is corrupt and we failed to
     861             :         // replace it.  For this session part of the application related to
     862             :         // bookmarks and history will misbehave.  The frontend may show a
     863             :         // "locked" notification to the user though.
     864             :         // Set up a pref to try replacing the database at the next startup.
     865           0 :         Preferences::SetString(PREF_FORCE_DATABASE_REPLACEMENT, aDbFilename);
     866             :       }
     867             :       // Report the corruption through telemetry.
     868           0 :       Telemetry::Accumulate(Telemetry::PLACES_DATABASE_CORRUPTION_HANDLING_STAGE,
     869           0 :                             static_cast<int8_t>(stage));
     870           0 :     });
     871             : 
     872             :     // Close database connection if open.
     873           0 :     if (mMainConn) {
     874           0 :       rv = mMainConn->SpinningSynchronousClose();
     875           0 :       NS_ENSURE_SUCCESS(rv, rv);
     876           0 :       mMainConn = nullptr;
     877             :     }
     878             : 
     879             :     // Remove the broken database.
     880           0 :     stage = stage_removing;
     881           0 :     rv = databaseFile->Remove(false);
     882           0 :     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
     883           0 :                          rv != NS_ERROR_FILE_NOT_FOUND) {
     884             :       return rv;
     885             :     }
     886             : 
     887             :     // Create a new database file and try to clone tables from the corrupt one.
     888           0 :     bool cloned = false;
     889           0 :     if (aTryToClone && Preferences::GetBool(PREF_DATABASE_CLONEONCORRUPTION, true)) {
     890           0 :       stage = stage_cloning;
     891           0 :       rv = TryToCloneTablesFromCorruptDatabase(aStorage, databaseFile);
     892           0 :       if (NS_SUCCEEDED(rv)) {
     893             :         // If we cloned successfully, we should not consider the database
     894             :         // corrupt anymore, otherwise we could reimport default bookmarks.
     895           0 :         mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_OK;
     896           0 :         cloned = true;
     897             :       }
     898             :     }
     899             : 
     900           0 :     if (aReopenConnection) {
     901             :       // Use an unshared connection, it will consume more memory but avoid shared
     902             :       // cache contentions across threads.
     903           0 :       stage = stage_reopening;
     904           0 :       rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
     905           0 :       NS_ENSURE_SUCCESS(rv, rv);
     906             :     }
     907             : 
     908           0 :     stage = cloned ? stage_cloned : stage_replaced;
     909             :   }
     910             : 
     911           0 :   return NS_OK;
     912             : }
     913             : 
     914             : nsresult
     915           0 : Database::TryToCloneTablesFromCorruptDatabase(const nsCOMPtr<mozIStorageService>& aStorage,
     916             :                                               const nsCOMPtr<nsIFile>& aDatabaseFile)
     917             : {
     918           0 :   MOZ_ASSERT(NS_IsMainThread());
     919             : 
     920           0 :   nsAutoString filename;
     921           0 :   nsresult rv = aDatabaseFile->GetLeafName(filename);
     922             : 
     923           0 :   nsCOMPtr<nsIFile> corruptFile;
     924           0 :   rv = aDatabaseFile->Clone(getter_AddRefs(corruptFile));
     925           0 :   NS_ENSURE_SUCCESS(rv, rv);
     926           0 :   rv = corruptFile->SetLeafName(getCorruptFilename(filename));
     927           0 :   NS_ENSURE_SUCCESS(rv, rv);
     928           0 :   nsAutoString path;
     929           0 :   rv = corruptFile->GetPath(path);
     930           0 :   NS_ENSURE_SUCCESS(rv, rv);
     931             : 
     932           0 :   nsCOMPtr<nsIFile> recoverFile;
     933           0 :   rv = aDatabaseFile->Clone(getter_AddRefs(recoverFile));
     934           0 :   NS_ENSURE_SUCCESS(rv, rv);
     935           0 :   rv = recoverFile->SetLeafName(getRecoverFilename(filename));
     936           0 :   NS_ENSURE_SUCCESS(rv, rv);
     937             :   // Ensure there's no previous recover file.
     938           0 :   rv = recoverFile->Remove(false);
     939           0 :   if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
     940           0 :                        rv != NS_ERROR_FILE_NOT_FOUND) {
     941             :     return rv;
     942             :   }
     943             : 
     944           0 :   nsCOMPtr<mozIStorageConnection> conn;
     945           0 :   auto guard = MakeScopeExit([&]() {
     946           0 :     if (conn) {
     947           0 :       Unused << conn->Close();
     948             :     }
     949           0 :     Unused << recoverFile->Remove(false);
     950           0 :   });
     951             : 
     952           0 :   rv = aStorage->OpenUnsharedDatabase(recoverFile, getter_AddRefs(conn));
     953           0 :   NS_ENSURE_SUCCESS(rv, rv);
     954           0 :   rv = AttachDatabase(conn, NS_ConvertUTF16toUTF8(path),
     955           0 :                       NS_LITERAL_CSTRING("corrupt"));
     956           0 :   NS_ENSURE_SUCCESS(rv, rv);
     957             : 
     958           0 :   mozStorageTransaction transaction(conn, false);
     959             : 
     960             :   // Copy the schema version.
     961           0 :   nsCOMPtr<mozIStorageStatement> stmt;
     962           0 :   (void)conn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA corrupt.user_version"),
     963           0 :                               getter_AddRefs(stmt));
     964           0 :   NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
     965             :   bool hasResult;
     966           0 :   rv = stmt->ExecuteStep(&hasResult);
     967           0 :   NS_ENSURE_SUCCESS(rv, rv);
     968           0 :   int32_t schemaVersion = stmt->AsInt32(0);
     969           0 :   rv = conn->SetSchemaVersion(schemaVersion);
     970           0 :   NS_ENSURE_SUCCESS(rv, rv);
     971             : 
     972             :   // Recreate the tables.
     973           0 :   rv = conn->CreateStatement(NS_LITERAL_CSTRING(
     974             :     "SELECT name, sql FROM corrupt.sqlite_master "
     975             :     "WHERE type = 'table' AND name BETWEEN 'moz_' AND 'moza'"
     976           0 :   ), getter_AddRefs(stmt));
     977           0 :   NS_ENSURE_SUCCESS(rv, rv);
     978           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     979           0 :     nsAutoCString name;
     980           0 :     rv = stmt->GetUTF8String(0, name);
     981           0 :     NS_ENSURE_SUCCESS(rv, rv);
     982           0 :     nsAutoCString query;
     983           0 :     rv = stmt->GetUTF8String(1, query);
     984           0 :     NS_ENSURE_SUCCESS(rv, rv);
     985           0 :     rv = conn->ExecuteSimpleSQL(query);
     986           0 :     NS_ENSURE_SUCCESS(rv, rv);
     987             :     // Copy the table contents.
     988           0 :     rv = conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("INSERT INTO main.") +
     989           0 :      name + NS_LITERAL_CSTRING(" SELECT * FROM corrupt.") + name);
     990           0 :     if (NS_FAILED(rv)) {
     991           0 :       rv = conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("INSERT INTO main.") +
     992           0 :             name + NS_LITERAL_CSTRING(" SELECT * FROM corrupt.") + name +
     993           0 :             NS_LITERAL_CSTRING(" ORDER BY rowid DESC"));
     994             :     }
     995           0 :     NS_ENSURE_SUCCESS(rv, rv);
     996             :   }
     997             : 
     998             :   // Recreate the indices.  Doing this after data addition is faster.
     999           0 :   rv = conn->CreateStatement(NS_LITERAL_CSTRING(
    1000             :     "SELECT sql FROM corrupt.sqlite_master "
    1001             :     "WHERE type <> 'table' AND name BETWEEN 'moz_' AND 'moza'"
    1002           0 :   ), getter_AddRefs(stmt));
    1003           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1004           0 :   hasResult = false;
    1005           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1006           0 :     nsAutoCString query;
    1007           0 :     rv = stmt->GetUTF8String(0, query);
    1008           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1009           0 :     rv = conn->ExecuteSimpleSQL(query);
    1010           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1011             :   }
    1012           0 :   rv = stmt->Finalize();
    1013           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1014             : 
    1015           0 :   rv = transaction.Commit();
    1016           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1017             : 
    1018           0 :   Unused << conn->Close();
    1019           0 :   conn = nullptr;
    1020           0 :   rv = recoverFile->RenameTo(nullptr, filename);
    1021           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1022           0 :   Unused << corruptFile->Remove(false);
    1023             : 
    1024           0 :   guard.release();
    1025           0 :   return NS_OK;
    1026             : }
    1027             : 
    1028             : nsresult
    1029           1 : Database::SetupDatabaseConnection(nsCOMPtr<mozIStorageService>& aStorage)
    1030             : {
    1031           0 :   MOZ_ASSERT(NS_IsMainThread());
    1032             : 
    1033             :   // Using immediate transactions allows the main connection to retry writes
    1034             :   // that fail with `SQLITE_BUSY` because a cloned connection has locked the
    1035             :   // database for writing.
    1036           1 :   nsresult rv = mMainConn->SetDefaultTransactionType(
    1037           1 :     mozIStorageConnection::TRANSACTION_IMMEDIATE);
    1038           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1039             : 
    1040             :   // WARNING: any statement executed before setting the journal mode must be
    1041             :   // finalized, since SQLite doesn't allow changing the journal mode if there
    1042             :   // is any outstanding statement.
    1043             : 
    1044             :   {
    1045             :     // Get the page size.  This may be different than the default if the
    1046             :     // database file already existed with a different page size.
    1047           0 :     nsCOMPtr<mozIStorageStatement> statement;
    1048           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1049             :       MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"
    1050           0 :     ), getter_AddRefs(statement));
    1051           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1052           1 :     bool hasResult = false;
    1053           1 :     rv = statement->ExecuteStep(&hasResult);
    1054           0 :     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_FILE_CORRUPTED);
    1055           1 :     rv = statement->GetInt32(0, &mDBPageSize);
    1056           0 :     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && mDBPageSize > 0, NS_ERROR_FILE_CORRUPTED);
    1057             :   }
    1058             : 
    1059             :   // Ensure that temp tables are held in memory, not on disk.
    1060           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1061             :     MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY")
    1062           0 :   );
    1063           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1064             : 
    1065           1 :   rv = SetupDurability(mMainConn, mDBPageSize);
    1066           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1067             : 
    1068           1 :   nsAutoCString busyTimeoutPragma("PRAGMA busy_timeout = ");
    1069           0 :   busyTimeoutPragma.AppendInt(DATABASE_BUSY_TIMEOUT_MS);
    1070           0 :   (void)mMainConn->ExecuteSimpleSQL(busyTimeoutPragma);
    1071             : 
    1072             :   // Enable FOREIGN KEY support. This is a strict requirement.
    1073           2 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1074             :     MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA foreign_keys = ON")
    1075           3 :   );
    1076           1 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_FILE_CORRUPTED);
    1077             : #ifdef DEBUG
    1078             :   {
    1079             :     // There are a few cases where setting foreign_keys doesn't work:
    1080             :     //  * in the middle of a multi-statement transaction
    1081             :     //  * if the SQLite library in use doesn't support them
    1082             :     // Since we need foreign_keys, let's at least assert in debug mode.
    1083           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    1084           3 :     mMainConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys"),
    1085           5 :                             getter_AddRefs(stmt));
    1086           1 :     bool hasResult = false;
    1087           1 :     if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1088           1 :       int32_t fkState = stmt->AsInt32(0);
    1089           0 :       MOZ_ASSERT(fkState, "Foreign keys should be enabled");
    1090             :     }
    1091             :   }
    1092             : #endif
    1093             : 
    1094             :   // Attach the favicons database to the main connection.
    1095           0 :   rv = EnsureFaviconsDatabaseAttached(aStorage);
    1096           0 :   if (NS_FAILED(rv)) {
    1097             :     // The favicons database may be corrupt. Try to replace and reattach it.
    1098           0 :     nsCOMPtr<nsIFile> iconsFile;
    1099           0 :     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    1100           0 :                                 getter_AddRefs(iconsFile));
    1101           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1102           0 :     rv = iconsFile->Append(DATABASE_FAVICONS_FILENAME);
    1103           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1104           0 :     rv = iconsFile->Remove(false);
    1105           0 :     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
    1106           0 :                          rv != NS_ERROR_FILE_NOT_FOUND) {
    1107             :       return rv;
    1108             :     }
    1109           0 :     rv = EnsureFaviconsDatabaseAttached(aStorage);
    1110           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1111             :   }
    1112             : 
    1113             :   // Create favicons temp entities.
    1114           3 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_ICONS_AFTERINSERT_TRIGGER);
    1115           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1116             : 
    1117             :   // We use our functions during migration, so initialize them now.
    1118           1 :   rv = InitFunctions();
    1119           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1120             : 
    1121             :   return NS_OK;
    1122             : }
    1123             : 
    1124             : nsresult
    1125           1 : Database::InitSchema(bool* aDatabaseMigrated)
    1126             : {
    1127           0 :   MOZ_ASSERT(NS_IsMainThread());
    1128           0 :   *aDatabaseMigrated = false;
    1129             : 
    1130             :   // Get the database schema version.
    1131             :   int32_t currentSchemaVersion;
    1132           1 :   nsresult rv = mMainConn->GetSchemaVersion(&currentSchemaVersion);
    1133           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1134           1 :   bool databaseInitialized = currentSchemaVersion > 0;
    1135             : 
    1136           1 :   if (databaseInitialized && currentSchemaVersion == DATABASE_SCHEMA_VERSION) {
    1137             :     // The database is up to date and ready to go.
    1138             :     return NS_OK;
    1139             :   }
    1140             : 
    1141           1 :   auto guard = MakeScopeExit([&]() {
    1142             :     // This runs at the end of the migration, out of the transaction,
    1143             :     // regardless of its success.
    1144           0 :     if (mShouldVacuumIcons) {
    1145           0 :       mShouldVacuumIcons = false;
    1146           0 :       MOZ_ALWAYS_SUCCEEDS(mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1147             :         "VACUUM favicons"
    1148             :       )));
    1149             :     }
    1150           1 :     if (mShouldConvertIconPayloads) {
    1151           0 :       mShouldConvertIconPayloads = false;
    1152           0 :       nsFaviconService::ConvertUnsupportedPayloads(mMainConn);
    1153             :     }
    1154           1 :     MigrateV48Frecencies();
    1155           0 :   });
    1156             : 
    1157             :   // We are going to update the database, so everything from now on should be in
    1158             :   // a transaction for performances.
    1159           3 :   mozStorageTransaction transaction(mMainConn, false);
    1160             : 
    1161           1 :   if (databaseInitialized) {
    1162             :     // Migration How-to:
    1163             :     //
    1164             :     // 1. increment PLACES_SCHEMA_VERSION.
    1165             :     // 2. implement a method that performs upgrade to your version from the
    1166             :     //    previous one.
    1167             :     //
    1168             :     // NOTE: The downgrade process is pretty much complicated by the fact old
    1169             :     //       versions cannot know what a new version is going to implement.
    1170             :     //       The only thing we will do for downgrades is setting back the schema
    1171             :     //       version, so that next upgrades will run again the migration step.
    1172             : 
    1173           0 :     if (currentSchemaVersion < DATABASE_SCHEMA_VERSION) {
    1174           0 :       *aDatabaseMigrated = true;
    1175             : 
    1176           0 :       if (currentSchemaVersion < 30) {
    1177             :         // These are versions older than Firefox 45 that are not supported
    1178             :         // anymore.  In this case it's safer to just replace the database.
    1179             :         // Note that Firefox 45 is the ESR release before the latest one (52),
    1180             :         // and Firefox 48 is a watershed release, so any version older than 48
    1181             :         // will first have to go through it.
    1182             :         return NS_ERROR_FILE_CORRUPTED;
    1183             :       }
    1184             : 
    1185             :       // Firefox 45 ESR uses schema version 30.
    1186             : 
    1187           0 :       if (currentSchemaVersion < 31) {
    1188           0 :         rv = MigrateV31Up();
    1189           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1190             :       }
    1191             : 
    1192             :       // Firefox 48 uses schema version 31.
    1193             : 
    1194           0 :       if (currentSchemaVersion < 32) {
    1195           0 :         rv = MigrateV32Up();
    1196           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1197             :       }
    1198             : 
    1199             :       // Firefox 49 uses schema version 32.
    1200             : 
    1201           0 :       if (currentSchemaVersion < 33) {
    1202           0 :         rv = MigrateV33Up();
    1203           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1204             :       }
    1205             : 
    1206             :       // Firefox 50 uses schema version 33.
    1207             : 
    1208           0 :       if (currentSchemaVersion < 34) {
    1209           0 :         rv = MigrateV34Up();
    1210           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1211             :       }
    1212             : 
    1213             :       // Firefox 51 uses schema version 34.
    1214             : 
    1215           0 :       if (currentSchemaVersion < 35) {
    1216           0 :         rv = MigrateV35Up();
    1217           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1218             :       }
    1219             : 
    1220             :       // Firefox 52 uses schema version 35.
    1221             : 
    1222           0 :       if (currentSchemaVersion < 36) {
    1223           0 :         rv = MigrateV36Up();
    1224           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1225             :       }
    1226             : 
    1227           0 :       if (currentSchemaVersion < 37) {
    1228           0 :         rv = MigrateV37Up();
    1229           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1230             :       }
    1231             : 
    1232             :       // Firefox 55 uses schema version 37.
    1233             : 
    1234           0 :       if (currentSchemaVersion < 38) {
    1235           0 :         rv = MigrateV38Up();
    1236           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1237             :       }
    1238             : 
    1239             :       // Firefox 56 uses schema version 38.
    1240             : 
    1241           0 :       if (currentSchemaVersion < 39) {
    1242           0 :         rv = MigrateV39Up();
    1243           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1244             :       }
    1245             : 
    1246             :       // Firefox 57 uses schema version 39.
    1247             : 
    1248           0 :       if (currentSchemaVersion < 40) {
    1249           0 :         rv = MigrateV40Up();
    1250           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1251             :       }
    1252             : 
    1253           0 :       if (currentSchemaVersion < 41) {
    1254           0 :         rv = MigrateV41Up();
    1255           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1256             :       }
    1257             : 
    1258             :       // Firefox 58 uses schema version 41.
    1259             : 
    1260           0 :       if (currentSchemaVersion < 42) {
    1261           0 :         rv = MigrateV42Up();
    1262           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1263             :       }
    1264             : 
    1265           0 :       if (currentSchemaVersion < 43) {
    1266           0 :         rv = MigrateV43Up();
    1267           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1268             :       }
    1269             : 
    1270             :       // Firefox 60 uses schema version 43.
    1271             : 
    1272           0 :       if (currentSchemaVersion < 44) {
    1273           0 :         rv = MigrateV44Up();
    1274           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1275             :       }
    1276             : 
    1277           0 :       if (currentSchemaVersion < 45) {
    1278           0 :         rv = MigrateV45Up();
    1279           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1280             :       }
    1281             : 
    1282           0 :       if (currentSchemaVersion < 46) {
    1283           0 :         rv = MigrateV46Up();
    1284           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1285             :       }
    1286             : 
    1287           0 :       if (currentSchemaVersion < 47) {
    1288           0 :         rv = MigrateV47Up();
    1289           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1290             :       }
    1291             : 
    1292             :       // Firefox 61 uses schema version 47.
    1293             : 
    1294           0 :       if (currentSchemaVersion < 48) {
    1295           0 :         rv = MigrateV48Up();
    1296           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1297             :       }
    1298             : 
    1299           0 :       if (currentSchemaVersion < 49) {
    1300           0 :         rv = MigrateV49Up();
    1301           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1302             :       }
    1303             : 
    1304           0 :       if (currentSchemaVersion < 50) {
    1305           0 :         rv = MigrateV50Up();
    1306           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1307             :       }
    1308             : 
    1309           0 :       if (currentSchemaVersion < 51) {
    1310           0 :         rv = MigrateV51Up();
    1311           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1312             :       }
    1313             : 
    1314             :       // Firefox 62 uses schema version 51.
    1315             : 
    1316             :       // Schema Upgrades must add migration code here.
    1317             :       // >>> IMPORTANT! <<<
    1318             :       // NEVER MIX UP SYNC AND ASYNC EXECUTION IN MIGRATORS, YOU MAY LOCK THE
    1319             :       // CONNECTION AND CAUSE FURTHER STEPS TO FAIL.
    1320             :       // In case, set a bool and do the async work in the ScopeExit guard just
    1321             :       // before the migration steps.
    1322             :     }
    1323             :   }
    1324             :   else {
    1325             :     // This is a new database, so we have to create all the tables and indices.
    1326             : 
    1327             :     // moz_origins.
    1328           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ORIGINS);
    1329           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1330             : 
    1331             :     // moz_places.
    1332           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES);
    1333           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1334           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL_HASH);
    1335           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1336           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_REVHOST);
    1337           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1338           3 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_VISITCOUNT);
    1339           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1340           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
    1341           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1342           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_LASTVISITDATE);
    1343           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1344           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
    1345           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1346           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_ORIGIN_ID);
    1347           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1348             : 
    1349             :     // moz_historyvisits.
    1350           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS);
    1351           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1352           3 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_PLACEDATE);
    1353           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1354           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_FROMVISIT);
    1355           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1356           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_VISITDATE);
    1357           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1358             : 
    1359             :     // moz_inputhistory.
    1360           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_INPUTHISTORY);
    1361           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1362             : 
    1363             :     // moz_bookmarks.
    1364           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS);
    1365           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1366           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_DELETED);
    1367           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1368           3 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACETYPE);
    1369           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1370           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PARENTPOSITION);
    1371           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1372           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
    1373           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1374           3 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_DATEADDED);
    1375           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1376           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
    1377           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1378             : 
    1379             :     // moz_keywords.
    1380           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS);
    1381           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1382           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_KEYWORDS_PLACEPOSTDATA);
    1383           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1384             : 
    1385             :     // moz_anno_attributes.
    1386           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNO_ATTRIBUTES);
    1387           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1388             : 
    1389             :     // moz_annos.
    1390           3 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNOS);
    1391           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1392           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
    1393           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1394             : 
    1395             :     // moz_items_annos.
    1396           3 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ITEMS_ANNOS);
    1397           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1398           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
    1399           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1400             : 
    1401             :     // moz_meta.
    1402           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_META);
    1403           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1404             : 
    1405             :     // The bookmarks roots get initialized in CheckRoots().
    1406             :   }
    1407             : 
    1408             :   // Set the schema version to the current one.
    1409           1 :   rv = mMainConn->SetSchemaVersion(DATABASE_SCHEMA_VERSION);
    1410           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1411             : 
    1412           1 :   rv = transaction.Commit();
    1413           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1414             : 
    1415             :   // ANY FAILURE IN THIS METHOD WILL CAUSE US TO MARK THE DATABASE AS CORRUPT
    1416             :   // AND TRY TO REPLACE IT.
    1417             :   // DO NOT PUT HERE ANYTHING THAT IS NOT RELATED TO INITIALIZATION OR MODIFYING
    1418             :   // THE DISK DATABASE.
    1419             : 
    1420             :   return NS_OK;
    1421             : }
    1422             : 
    1423             : nsresult
    1424           0 : Database::CheckRoots()
    1425             : {
    1426           1 :   MOZ_ASSERT(NS_IsMainThread());
    1427             : 
    1428             :   // If the database has just been created, skip straight to the part where
    1429             :   // we create the roots.
    1430           1 :   if (mDatabaseStatus == nsINavHistoryService::DATABASE_STATUS_CREATE) {
    1431           1 :     return EnsureBookmarkRoots(0);
    1432             :   }
    1433             : 
    1434           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1435           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1436             :     "SELECT guid, id, position FROM moz_bookmarks WHERE guid IN ( "
    1437             :       "'" ROOT_GUID "', '" MENU_ROOT_GUID "', '" TOOLBAR_ROOT_GUID "', "
    1438             :       "'" TAGS_ROOT_GUID "', '" UNFILED_ROOT_GUID "', '" MOBILE_ROOT_GUID "' )"
    1439           0 :     ), getter_AddRefs(stmt));
    1440           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1441             : 
    1442             :   bool hasResult;
    1443           0 :   nsAutoCString guid;
    1444           0 :   int32_t maxPosition = 0;
    1445           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1446           0 :     rv = stmt->GetUTF8String(0, guid);
    1447           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1448             : 
    1449           0 :     if (guid.EqualsLiteral(ROOT_GUID)) {
    1450           0 :       mRootId = stmt->AsInt64(1);
    1451             :     }
    1452             :     else {
    1453           0 :       maxPosition = std::max(stmt->AsInt32(2), maxPosition);
    1454             : 
    1455           0 :       if (guid.EqualsLiteral(MENU_ROOT_GUID)) {
    1456           0 :         mMenuRootId = stmt->AsInt64(1);
    1457             :       }
    1458           0 :       else if (guid.EqualsLiteral(TOOLBAR_ROOT_GUID)) {
    1459           0 :         mToolbarRootId = stmt->AsInt64(1);
    1460             :       }
    1461           0 :       else if (guid.EqualsLiteral(TAGS_ROOT_GUID)) {
    1462           0 :         mTagsRootId = stmt->AsInt64(1);
    1463             :       }
    1464           0 :       else if (guid.EqualsLiteral(UNFILED_ROOT_GUID)) {
    1465           0 :         mUnfiledRootId = stmt->AsInt64(1);
    1466             :       }
    1467           0 :       else if (guid.EqualsLiteral(MOBILE_ROOT_GUID)) {
    1468           0 :         mMobileRootId = stmt->AsInt64(1);
    1469             :       }
    1470             :     }
    1471             :   }
    1472             : 
    1473           0 :   rv = EnsureBookmarkRoots(maxPosition + 1);
    1474           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1475             : 
    1476             :   return NS_OK;
    1477             : }
    1478             : 
    1479             : nsresult
    1480           1 : Database::EnsureBookmarkRoots(const int32_t startPosition)
    1481             : {
    1482           0 :   MOZ_ASSERT(NS_IsMainThread());
    1483             : 
    1484             :   nsresult rv;
    1485             : 
    1486             :   // Note: If the root is missing, we recreate it but we don't fix any
    1487             :   // remaining built-in folder parent ids. We leave these to a maintenance task,
    1488             :   // so that we're not needing to do extra checks on startup.
    1489             : 
    1490           1 :   if (mRootId < 1) {
    1491             :     // The first root's title is an empty string.
    1492           0 :     rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("places"),
    1493           0 :                     NS_LITERAL_CSTRING("root________"), EmptyCString(),
    1494           0 :                     0, mRootId);
    1495             : 
    1496           0 :     if (NS_FAILED(rv)) return rv;
    1497             :   }
    1498             : 
    1499           1 :   int32_t position = startPosition;
    1500             : 
    1501             :   // For the other roots, the UI doesn't rely on the value in the database, so
    1502             :   // just set it to something simple to make it easier for humans to read.
    1503           0 :   if (mMenuRootId < 1) {
    1504           0 :     rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("menu"),
    1505           0 :                     NS_LITERAL_CSTRING("menu________"), NS_LITERAL_CSTRING("menu"),
    1506           7 :                     position, mMenuRootId);
    1507           1 :     if (NS_FAILED(rv)) return rv;
    1508           0 :     position++;
    1509             :   }
    1510             : 
    1511           0 :   if (mToolbarRootId < 1) {
    1512           0 :     rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("toolbar"),
    1513           0 :                     NS_LITERAL_CSTRING("toolbar_____"), NS_LITERAL_CSTRING("toolbar"),
    1514           7 :                     position, mToolbarRootId);
    1515           1 :     if (NS_FAILED(rv)) return rv;
    1516           0 :     position++;
    1517             :   }
    1518             : 
    1519           0 :   if (mTagsRootId < 1) {
    1520           0 :     rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("tags"),
    1521           0 :                     NS_LITERAL_CSTRING("tags________"), NS_LITERAL_CSTRING("tags"),
    1522           7 :                     position, mTagsRootId);
    1523           1 :     if (NS_FAILED(rv)) return rv;
    1524           1 :     position++;
    1525             :   }
    1526             : 
    1527           0 :   if (mUnfiledRootId < 1) {
    1528           1 :     if (NS_FAILED(rv)) return rv;
    1529           0 :     rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"),
    1530           0 :                     NS_LITERAL_CSTRING("unfiled_____"), NS_LITERAL_CSTRING("unfiled"),
    1531           7 :                     position, mUnfiledRootId);
    1532           0 :     if (NS_FAILED(rv)) return rv;
    1533             :     position++;
    1534             :   }
    1535             : 
    1536           0 :   if (mMobileRootId < 1) {
    1537           1 :     int64_t mobileRootId = CreateMobileRoot();
    1538           0 :     if (mobileRootId <= 0) return NS_ERROR_FAILURE;
    1539             :     {
    1540           0 :       nsCOMPtr<mozIStorageStatement> mobileRootSyncStatusStmt;
    1541           0 :       rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1542             :         "UPDATE moz_bookmarks SET syncStatus = :sync_status WHERE id = :id"
    1543           5 :       ), getter_AddRefs(mobileRootSyncStatusStmt));
    1544           0 :       if (NS_FAILED(rv)) return rv;
    1545             : 
    1546           1 :       rv = mobileRootSyncStatusStmt->BindInt32ByName(
    1547           0 :         NS_LITERAL_CSTRING("sync_status"),
    1548             :         nsINavBookmarksService::SYNC_STATUS_NEW
    1549           3 :       );
    1550           1 :       if (NS_FAILED(rv)) return rv;
    1551           2 :       rv = mobileRootSyncStatusStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
    1552           3 :                                                      mobileRootId);
    1553           1 :       if (NS_FAILED(rv)) return rv;
    1554             : 
    1555           0 :       rv = mobileRootSyncStatusStmt->Execute();
    1556           1 :       NS_ENSURE_SUCCESS(rv, rv);
    1557             : 
    1558           1 :       mMobileRootId = mobileRootId;
    1559             :     }
    1560             :   }
    1561             : 
    1562             :   return NS_OK;
    1563             : }
    1564             : 
    1565             : nsresult
    1566           0 : Database::InitFunctions()
    1567             : {
    1568           0 :   MOZ_ASSERT(NS_IsMainThread());
    1569             : 
    1570           0 :   nsresult rv = GetUnreversedHostFunction::create(mMainConn);
    1571           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1572           0 :   rv = MatchAutoCompleteFunction::create(mMainConn);
    1573           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1574           0 :   rv = CalculateFrecencyFunction::create(mMainConn);
    1575           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1576           0 :   rv = GenerateGUIDFunction::create(mMainConn);
    1577           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1578           0 :   rv = IsValidGUIDFunction::create(mMainConn);
    1579           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1580           0 :   rv = FixupURLFunction::create(mMainConn);
    1581           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1582           0 :   rv = FrecencyNotificationFunction::create(mMainConn);
    1583           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1584           0 :   rv = StoreLastInsertedIdFunction::create(mMainConn);
    1585           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1586           0 :   rv = HashFunction::create(mMainConn);
    1587           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1588           0 :   rv = GetQueryParamFunction::create(mMainConn);
    1589           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1590           2 :   rv = GetPrefixFunction::create(mMainConn);
    1591           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1592           2 :   rv = GetHostAndPortFunction::create(mMainConn);
    1593           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1594           0 :   rv = StripPrefixAndUserinfoFunction::create(mMainConn);
    1595           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1596           0 :   rv = IsFrecencyDecayingFunction::create(mMainConn);
    1597           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1598           0 :   rv = SqrtFunction::create(mMainConn);
    1599           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1600             : 
    1601             :   return NS_OK;
    1602             : }
    1603             : 
    1604             : nsresult
    1605           0 : Database::InitTempEntities()
    1606             : {
    1607           0 :   MOZ_ASSERT(NS_IsMainThread());
    1608             : 
    1609           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERINSERT_TRIGGER);
    1610           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1611           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERDELETE_TRIGGER);
    1612           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1613             : 
    1614             :   // Add the triggers that update the moz_origins table as necessary.
    1615           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEORIGINSINSERT_TEMP);
    1616           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1617           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEORIGINSINSERT_AFTERDELETE_TRIGGER);
    1618           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1619           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERINSERT_TRIGGER);
    1620           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1621           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEORIGINSDELETE_TEMP);
    1622           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1623           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEORIGINSDELETE_AFTERDELETE_TRIGGER);
    1624           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1625           3 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERDELETE_TRIGGER);
    1626           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1627           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_FRECENCY_TRIGGER);
    1628           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1629             : 
    1630           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_BOOKMARKS_FOREIGNCOUNT_AFTERDELETE_TRIGGER);
    1631           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1632           3 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_BOOKMARKS_FOREIGNCOUNT_AFTERINSERT_TRIGGER);
    1633           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1634           3 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_BOOKMARKS_FOREIGNCOUNT_AFTERUPDATE_TRIGGER);
    1635           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1636             : 
    1637           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_KEYWORDS_FOREIGNCOUNT_AFTERDELETE_TRIGGER);
    1638           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1639           3 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_KEYWORDS_FOREIGNCOUNT_AFTERINSERT_TRIGGER);
    1640           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1641           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_KEYWORDS_FOREIGNCOUNT_AFTERUPDATE_TRIGGER);
    1642           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1643             : 
    1644             :   return NS_OK;
    1645             : }
    1646             : 
    1647             : nsresult
    1648           0 : Database::MigrateV31Up() {
    1649           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1650             :     "DROP TABLE IF EXISTS moz_bookmarks_roots"
    1651           0 :   ));
    1652           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1653             : 
    1654             :   return NS_OK;
    1655             : }
    1656             : 
    1657             : nsresult
    1658           0 : Database::MigrateV32Up() {
    1659             :   // Remove some old and no more used Places preferences that may be confusing
    1660             :   // for the user.
    1661           0 :   mozilla::Unused << Preferences::ClearUser("places.history.expiration.transient_optimal_database_size");
    1662           0 :   mozilla::Unused << Preferences::ClearUser("places.last_vacuum");
    1663           0 :   mozilla::Unused << Preferences::ClearUser("browser.history_expire_sites");
    1664           0 :   mozilla::Unused << Preferences::ClearUser("browser.history_expire_days.mirror");
    1665           0 :   mozilla::Unused << Preferences::ClearUser("browser.history_expire_days_min");
    1666             : 
    1667             :   // For performance reasons we want to remove too long urls from history.
    1668             :   // We cannot use the moz_places triggers here, cause they are defined only
    1669             :   // after the schema migration.  Thus we need to collect the hosts that need to
    1670             :   // be updated first.
    1671           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1672             :     "CREATE TEMP TABLE moz_migrate_v32_temp ("
    1673             :       "host TEXT PRIMARY KEY "
    1674             :     ") WITHOUT ROWID "
    1675           0 :   ));
    1676           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1677             :   {
    1678           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    1679           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1680             :       "INSERT OR IGNORE INTO moz_migrate_v32_temp (host) "
    1681             :         "SELECT fixup_url(get_unreversed_host(rev_host)) "
    1682             :         "FROM moz_places WHERE LENGTH(url) > :maxlen AND foreign_count = 0"
    1683           0 :     ), getter_AddRefs(stmt));
    1684           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1685           0 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("maxlen"), MaxUrlLength());
    1686           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1687           0 :     rv = stmt->Execute();
    1688           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1689             :   }
    1690             :   // Now remove the pages with a long url.
    1691             :   {
    1692           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    1693           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1694             :       "DELETE FROM moz_places WHERE LENGTH(url) > :maxlen AND foreign_count = 0"
    1695           0 :     ), getter_AddRefs(stmt));
    1696           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1697           0 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("maxlen"), MaxUrlLength());
    1698           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1699           0 :     rv = stmt->Execute();
    1700           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1701             :   }
    1702             : 
    1703             :   // Expire orphan visits and update moz_hosts.
    1704             :   // These may be a bit more expensive and are not critical for the DB
    1705             :   // functionality, so we execute them asynchronously.
    1706           0 :   nsCOMPtr<mozIStorageAsyncStatement> expireOrphansStmt;
    1707           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1708             :     "DELETE FROM moz_historyvisits "
    1709             :     "WHERE NOT EXISTS (SELECT 1 FROM moz_places WHERE id = place_id)"
    1710           0 :   ), getter_AddRefs(expireOrphansStmt));
    1711           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1712           0 :   nsCOMPtr<mozIStorageAsyncStatement> deleteHostsStmt;
    1713           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1714             :     "DELETE FROM moz_hosts "
    1715             :     "WHERE host IN (SELECT host FROM moz_migrate_v32_temp) "
    1716             :       "AND NOT EXISTS("
    1717             :         "SELECT 1 FROM moz_places "
    1718             :           "WHERE rev_host = get_unreversed_host(host || '.') || '.' "
    1719             :              "OR rev_host = get_unreversed_host(host || '.') || '.www.' "
    1720             :       "); "
    1721           0 :   ), getter_AddRefs(deleteHostsStmt));
    1722           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1723             : 
    1724             : #define HOST_TO_REVHOST_PREDICATE \
    1725             :   "rev_host = get_unreversed_host(host || '.') || '.' " \
    1726             :   "OR rev_host = get_unreversed_host(host || '.') || '.www.'"
    1727             : #define HOSTS_PREFIX_PRIORITY_FRAGMENT \
    1728             :   "SELECT CASE " \
    1729             :     "WHEN ( " \
    1730             :       "SELECT round(avg(substr(url,1,12) = 'https://www.')) FROM moz_places h " \
    1731             :       "WHERE (" HOST_TO_REVHOST_PREDICATE ") AND +h.typed = 1 " \
    1732             :     ") THEN 'https://www.' " \
    1733             :     "WHEN ( " \
    1734             :       "SELECT round(avg(substr(url,1,8) = 'https://')) FROM moz_places h " \
    1735             :       "WHERE (" HOST_TO_REVHOST_PREDICATE ") AND +h.typed = 1 " \
    1736             :     ") THEN 'https://' " \
    1737             :     "WHEN 1 = ( " \
    1738             :       "SELECT min(substr(url,1,4) = 'ftp:') FROM moz_places h " \
    1739             :       "WHERE (" HOST_TO_REVHOST_PREDICATE ") AND +h.typed = 1 " \
    1740             :     ") THEN 'ftp://' " \
    1741             :     "WHEN ( " \
    1742             :       "SELECT round(avg(substr(url,1,11) = 'http://www.')) FROM moz_places h " \
    1743             :       "WHERE (" HOST_TO_REVHOST_PREDICATE ") AND +h.typed = 1 " \
    1744             :     ") THEN 'www.' " \
    1745             :   "END "
    1746             : 
    1747           0 :   nsCOMPtr<mozIStorageAsyncStatement> updateHostsStmt;
    1748           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1749             :     "UPDATE moz_hosts "
    1750             :     "SET prefix = (" HOSTS_PREFIX_PRIORITY_FRAGMENT ") "
    1751             :     "WHERE host IN (SELECT host FROM moz_migrate_v32_temp) "
    1752           0 :   ), getter_AddRefs(updateHostsStmt));
    1753           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1754             : 
    1755             : #undef HOST_TO_REVHOST_PREDICATE
    1756             : #undef HOSTS_PREFIX_PRIORITY_FRAGMENT
    1757             : 
    1758           0 :   nsCOMPtr<mozIStorageAsyncStatement> dropTableStmt;
    1759           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1760             :     "DROP TABLE IF EXISTS moz_migrate_v32_temp"
    1761           0 :   ), getter_AddRefs(dropTableStmt));
    1762           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1763             : 
    1764             :   mozIStorageBaseStatement *stmts[] = {
    1765             :     expireOrphansStmt,
    1766             :     deleteHostsStmt,
    1767             :     updateHostsStmt,
    1768             :     dropTableStmt
    1769           0 :   };
    1770           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    1771           0 :   rv = mMainConn->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
    1772           0 :                                getter_AddRefs(ps));
    1773           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1774             : 
    1775             :   return NS_OK;
    1776             : }
    1777             : 
    1778             : nsresult
    1779           0 : Database::MigrateV33Up() {
    1780           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1781             :     "DROP INDEX IF EXISTS moz_places_url_uniqueindex"
    1782           0 :   ));
    1783           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1784             : 
    1785             :   // Add an url_hash column to moz_places.
    1786           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1787           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1788             :     "SELECT url_hash FROM moz_places"
    1789           0 :   ), getter_AddRefs(stmt));
    1790           0 :   if (NS_FAILED(rv)) {
    1791           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1792             :       "ALTER TABLE moz_places ADD COLUMN url_hash INTEGER DEFAULT 0 NOT NULL"
    1793           0 :     ));
    1794           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1795             :   }
    1796             : 
    1797           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1798             :     "UPDATE moz_places SET url_hash = hash(url) WHERE url_hash = 0"
    1799           0 :   ));
    1800           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1801             : 
    1802             :   // Create an index on url_hash.
    1803           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL_HASH);
    1804           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1805             : 
    1806             :   return NS_OK;
    1807             : }
    1808             : 
    1809             : nsresult
    1810           0 : Database::MigrateV34Up() {
    1811           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1812             :     "DELETE FROM moz_keywords WHERE id IN ( "
    1813             :       "SELECT id FROM moz_keywords k "
    1814             :       "WHERE NOT EXISTS (SELECT 1 FROM moz_places h WHERE k.place_id = h.id) "
    1815             :     ")"
    1816           0 :   ));
    1817           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1818             : 
    1819             :   return NS_OK;
    1820             : }
    1821             : 
    1822             : nsresult
    1823           0 : Database::MigrateV35Up() {
    1824           0 :   int64_t mobileRootId = CreateMobileRoot();
    1825           0 :   if (mobileRootId <= 0)  {
    1826             :     // Either the schema is broken or there isn't any root. The latter can
    1827             :     // happen if a consumer, for example Thunderbird, never used bookmarks.
    1828             :     // If there are no roots, this migration should not run.
    1829           0 :     nsCOMPtr<mozIStorageStatement> checkRootsStmt;
    1830           0 :     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1831             :       "SELECT id FROM moz_bookmarks WHERE parent = 0"
    1832           0 :     ), getter_AddRefs(checkRootsStmt));
    1833           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1834           0 :     bool hasResult = false;
    1835           0 :     rv = checkRootsStmt->ExecuteStep(&hasResult);
    1836           0 :     if (NS_SUCCEEDED(rv) && !hasResult) {
    1837             :       return NS_OK;
    1838             :     }
    1839           0 :     return NS_ERROR_FAILURE;
    1840             :   }
    1841             : 
    1842             :   // At this point, we should have no more than two folders with the mobile
    1843             :   // bookmarks anno: the new root, and the old folder if one exists. If, for
    1844             :   // some reason, we have multiple folders with the anno, we append their
    1845             :   // children to the new root.
    1846           0 :   nsTArray<int64_t> folderIds;
    1847           0 :   nsresult rv = GetItemsWithAnno(NS_LITERAL_CSTRING(MOBILE_ROOT_ANNO),
    1848             :                                  nsINavBookmarksService::TYPE_FOLDER,
    1849           0 :                                  folderIds);
    1850           0 :   if (NS_FAILED(rv)) return rv;
    1851             : 
    1852           0 :   for (uint32_t i = 0; i < folderIds.Length(); ++i) {
    1853           0 :     if (folderIds[i] == mobileRootId) {
    1854             :       // Ignore the new mobile root. We'll remove this anno from the root in
    1855             :       // bug 1306445.
    1856           0 :       continue;
    1857             :     }
    1858             : 
    1859             :     // Append the folder's children to the new root.
    1860           0 :     nsCOMPtr<mozIStorageStatement> moveStmt;
    1861           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1862             :       "UPDATE moz_bookmarks "
    1863             :       "SET parent = :root_id, "
    1864             :           "position = position + IFNULL("
    1865             :             "(SELECT MAX(position) + 1 FROM moz_bookmarks "
    1866             :              "WHERE parent = :root_id), 0)"
    1867             :       "WHERE parent = :folder_id"
    1868           0 :     ), getter_AddRefs(moveStmt));
    1869           0 :     if (NS_FAILED(rv)) return rv;
    1870             : 
    1871           0 :     rv = moveStmt->BindInt64ByName(NS_LITERAL_CSTRING("root_id"),
    1872           0 :                                    mobileRootId);
    1873           0 :     if (NS_FAILED(rv)) return rv;
    1874           0 :     rv = moveStmt->BindInt64ByName(NS_LITERAL_CSTRING("folder_id"),
    1875           0 :                                    folderIds[i]);
    1876           0 :     if (NS_FAILED(rv)) return rv;
    1877             : 
    1878           0 :     rv = moveStmt->Execute();
    1879           0 :     if (NS_FAILED(rv)) return rv;
    1880             : 
    1881             :     // Delete the old folder.
    1882           0 :     rv = DeleteBookmarkItem(folderIds[i]);
    1883           0 :     if (NS_FAILED(rv)) return rv;
    1884             :   }
    1885             : 
    1886             :   return NS_OK;
    1887             : }
    1888             : 
    1889             : nsresult
    1890           0 : Database::MigrateV36Up() {
    1891             :   // Add sync status and change counter tracking columns for bookmarks.
    1892           0 :   nsCOMPtr<mozIStorageStatement> syncStatusStmt;
    1893           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1894             :     "SELECT syncStatus FROM moz_bookmarks"
    1895           0 :   ), getter_AddRefs(syncStatusStmt));
    1896           0 :   if (NS_FAILED(rv)) {
    1897             :     // We default to SYNC_STATUS_UNKNOWN = 0 for existing bookmarks, matching
    1898             :     // the bookmark restore behavior. If Sync is set up, we'll update the status
    1899             :     // to SYNC_STATUS_NORMAL = 2 before the first post-migration sync.
    1900           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1901             :       "ALTER TABLE moz_bookmarks "
    1902             :       "ADD COLUMN syncStatus INTEGER DEFAULT 0 NOT NULL"
    1903           0 :     ));
    1904           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1905             :   }
    1906             : 
    1907           0 :   nsCOMPtr<mozIStorageStatement> syncChangeCounterStmt;
    1908           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1909             :     "SELECT syncChangeCounter FROM moz_bookmarks"
    1910           0 :   ), getter_AddRefs(syncChangeCounterStmt));
    1911           0 :   if (NS_FAILED(rv)) {
    1912             :     // The change counter starts at 1 for all local bookmarks. It's incremented
    1913             :     // for each modification that should trigger a sync, and decremented after
    1914             :     // the modified bookmark is uploaded to the server.
    1915           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1916             :       "ALTER TABLE moz_bookmarks "
    1917           0 :       "ADD COLUMN syncChangeCounter INTEGER DEFAULT 1 NOT NULL"));
    1918           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1919             :   }
    1920             : 
    1921           0 :   nsCOMPtr<mozIStorageStatement> tombstoneTableStmt;
    1922           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1923             :     "SELECT 1 FROM moz_bookmarks_deleted"
    1924           0 :   ), getter_AddRefs(tombstoneTableStmt));
    1925           0 :   if (NS_FAILED(rv)) {
    1926           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_DELETED);
    1927           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1928             :   }
    1929             : 
    1930             :   return NS_OK;
    1931             : }
    1932             : 
    1933             : nsresult
    1934           0 : Database::MigrateV37Up() {
    1935             :   // Move favicons to the new database.
    1936             :   // For now we retain the old moz_favicons table, but we empty it.
    1937             :   // This allows for a "safer" downgrade, even if icons will be lost in the
    1938             :   // process. In a couple versions we shall drop moz_favicons completely.
    1939             : 
    1940             :   // First, check if the old favicons table still exists.
    1941           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1942           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1943             :     "SELECT url FROM moz_favicons"
    1944           0 :   ), getter_AddRefs(stmt));
    1945           0 :   if (NS_FAILED(rv)) {
    1946             :     // The table has already been removed, nothing to do.
    1947             :     return NS_OK;
    1948             :   }
    1949             : 
    1950             :   // The new table accepts only png or svg payloads, so we set a valid width
    1951             :   // only for them, the mime-type for the others.  Later we will asynchronously
    1952             :   // try to convert the unsupported payloads, or remove them.
    1953             : 
    1954             :   // Add pages.
    1955           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1956             :     "INSERT INTO moz_pages_w_icons (page_url, page_url_hash) "
    1957             :     "SELECT h.url, hash(h.url) "
    1958             :     "FROM moz_places h "
    1959             :     "JOIN moz_favicons f ON f.id = h.favicon_id"
    1960           0 :   ));
    1961           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1962             :   // Set icons as expired, so we will replace them with proper versions at the
    1963             :   // first load.
    1964             :   // Note: we use a peculiarity of Sqlite here, where the column affinity
    1965             :   // is not enforced, thanks to that we can store a string in an integer column.
    1966           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1967             :     "INSERT INTO moz_icons (icon_url, fixed_icon_url_hash, width, data) "
    1968             :       "SELECT url, hash(fixup_url(url)), "
    1969             :              "(CASE WHEN mime_type = 'image/png' THEN 16 "
    1970             :                    "WHEN mime_type = 'image/svg+xml' THEN 65535 "
    1971             :                    "ELSE mime_type END), "
    1972             :              "data FROM moz_favicons "
    1973             :              "WHERE LENGTH(data) > 0 "
    1974           0 :   ));
    1975           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1976             :   // Create relations.
    1977           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1978             :     "INSERT OR IGNORE INTO moz_icons_to_pages (page_id, icon_id) "
    1979             :       "SELECT (SELECT id FROM moz_pages_w_icons "
    1980             :               "WHERE page_url_hash = h.url_hash "
    1981             :                 "AND page_url = h.url), "
    1982             :              "(SELECT id FROM moz_icons "
    1983             :               "WHERE fixed_icon_url_hash = hash(fixup_url(f.url)) "
    1984             :                 "AND icon_url = f.url) "
    1985             :       "FROM moz_favicons f "
    1986             :       "JOIN moz_places h on f.id = h.favicon_id"
    1987           0 :   ));
    1988           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1989             :   // Remove old favicons and relations.
    1990           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1991             :     "DELETE FROM moz_favicons"
    1992           0 :   ));
    1993           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1994             : 
    1995           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1996             :     "UPDATE moz_places SET favicon_id = NULL"
    1997           0 :   ));
    1998           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1999             : 
    2000             :   // The async favicons conversion will happen at the end of the normal schema
    2001             :   // migration.
    2002           0 :   mShouldConvertIconPayloads = true;
    2003             : 
    2004           0 :   return NS_OK;
    2005             : }
    2006             : 
    2007             : nsresult
    2008           0 : Database::MigrateV38Up()
    2009             : {
    2010           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2011           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2012             :     "SELECT description, preview_image_url FROM moz_places"
    2013           0 :   ), getter_AddRefs(stmt));
    2014           0 :   if (NS_FAILED(rv)) {
    2015           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2016             :       "ALTER TABLE moz_places ADD COLUMN description TEXT"
    2017           0 :     ));
    2018           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2019             : 
    2020           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2021             :       "ALTER TABLE moz_places ADD COLUMN preview_image_url TEXT"
    2022           0 :     ));
    2023           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2024             :   }
    2025             : 
    2026             :   return NS_OK;
    2027             : }
    2028             : 
    2029             : nsresult
    2030           0 : Database::MigrateV39Up() {
    2031             :   // Create an index on dateAdded.
    2032           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_DATEADDED);
    2033           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2034             : 
    2035             :   return NS_OK;
    2036             : }
    2037             : 
    2038             : nsresult
    2039           0 : Database::MigrateV40Up() {
    2040             :   // We are changing the hashing function to crop the hashed text to a maximum
    2041             :   // length, thus we must recalculate the hashes.
    2042             :   // Due to this, on downgrade some of these may not match, it should be limited
    2043             :   // to unicode and very long urls though.
    2044           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2045             :     "UPDATE moz_places "
    2046             :     "SET url_hash = hash(url) "
    2047           0 :     "WHERE url_hash <> hash(url)"));
    2048           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2049           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2050             :     "UPDATE moz_icons "
    2051             :     "SET fixed_icon_url_hash = hash(fixup_url(icon_url)) "
    2052           0 :     "WHERE fixed_icon_url_hash <> hash(fixup_url(icon_url))"));
    2053           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2054           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2055             :     "UPDATE moz_pages_w_icons "
    2056             :     "SET page_url_hash = hash(page_url) "
    2057           0 :     "WHERE page_url_hash <> hash(page_url)"));
    2058           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2059             :   return NS_OK;
    2060             : }
    2061             : 
    2062             : nsresult
    2063           0 : Database::MigrateV41Up() {
    2064             :   // Remove old favicons entities.
    2065           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2066           0 :     "DROP INDEX IF EXISTS moz_places_faviconindex"));
    2067           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2068           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2069           0 :     "DROP TABLE IF EXISTS moz_favicons"));
    2070           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2071             :   return NS_OK;
    2072             : }
    2073             : 
    2074             : nsresult
    2075           0 : Database::MigrateV42Up() {
    2076             :   // auto_vacuum of the favicons database was broken, we may have to set it again.
    2077           0 :   int32_t vacuum = 0;
    2078             :   {
    2079           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    2080           0 :     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2081             :       "PRAGMA favicons.auto_vacuum"
    2082           0 :     ), getter_AddRefs(stmt));
    2083           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2084           0 :     bool hasResult = false;
    2085           0 :     if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    2086           0 :       vacuum = stmt->AsInt32(0);
    2087             :     }
    2088             :   }
    2089           0 :   if (vacuum != 2) {
    2090           0 :     nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2091           0 :       "PRAGMA favicons.auto_vacuum = INCREMENTAL"));
    2092           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2093             :     // For the change to be effective, we must vacuum the database.
    2094           0 :     mShouldVacuumIcons = true;
    2095             :   }
    2096             :   return NS_OK;
    2097             : }
    2098             : 
    2099             : nsresult
    2100           0 : Database::MigrateV43Up() {
    2101             :   // moz_keywords doesn't properly disallow multiple keyword for the same URI
    2102             :   // because for postData NULL != NULL. We should use an empty string instead.
    2103             : 
    2104             :   // To avoid constraint failures, we must first remove duplicate keywords.
    2105             :   // This may cause a dataloss, but the only alternative would be to modify the
    2106             :   // related url, and that's far more complex.
    2107           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2108             :     "DELETE FROM moz_keywords "
    2109             :     "WHERE post_data ISNULL "
    2110             :       "AND id NOT IN ( "
    2111             :         "SELECT MAX(id) "
    2112             :         "FROM moz_keywords "
    2113             :         "WHERE post_data ISNULL "
    2114             :         "GROUP BY place_id "
    2115             :       ")"
    2116           0 :   ));
    2117           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2118             :   // We must recalculate foreign_count for all the touched places.
    2119           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2120             :     "UPDATE moz_places "
    2121             :     "SET foreign_count = (SELECT count(*) FROM moz_bookmarks WHERE fk = moz_places.id) + "
    2122             :                         "(SELECT count(*) FROM moz_keywords WHERE place_id = moz_places.id) "
    2123             :     "WHERE id IN (SELECT DISTINCT place_id FROM moz_keywords) "
    2124           0 :   ));
    2125           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2126           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2127             :     "UPDATE moz_keywords "
    2128             :     "SET post_data = '' "
    2129             :     "WHERE post_data ISNULL "
    2130           0 :   ));
    2131           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2132             : 
    2133             :   return NS_OK;
    2134             : }
    2135             : 
    2136             : nsresult
    2137           0 : Database::MigrateV44Up() {
    2138             :   // We need to remove any non-builtin roots and their descendants.
    2139             : 
    2140             :   // Install a temp trigger to clean up linked tables when the main
    2141             :   // bookmarks are deleted.
    2142           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2143             :     "CREATE TEMP TRIGGER moz_migrate_bookmarks_trigger "
    2144             :     "AFTER DELETE ON moz_bookmarks FOR EACH ROW "
    2145             :     "BEGIN "
    2146             :       // Insert tombstones.
    2147             :       "INSERT OR IGNORE INTO moz_bookmarks_deleted (guid, dateRemoved) "
    2148             :         "VALUES (OLD.guid, strftime('%s', 'now', 'localtime', 'utc') * 1000); "
    2149             :       // Remove old annotations for the bookmarks.
    2150             :       "DELETE FROM moz_items_annos "
    2151             :         "WHERE item_id = OLD.id; "
    2152             :       // Decrease the foreign_count in moz_places.
    2153             :       "UPDATE moz_places "
    2154             :         "SET foreign_count = foreign_count - 1 "
    2155             :         "WHERE id = OLD.fk; "
    2156             :     "END "
    2157           0 :   ));
    2158           0 :   if (NS_FAILED(rv)) return rv;
    2159             : 
    2160             :   // This trigger listens for moz_places deletes, and updates moz_annos and
    2161             :   // moz_keywords accordingly.
    2162           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2163             :     "CREATE TEMP TRIGGER moz_migrate_annos_trigger "
    2164             :     "AFTER UPDATE ON moz_places FOR EACH ROW "
    2165             :     // Only remove from moz_places if we don't have any remaining keywords pointing to
    2166             :     // this place, and it hasn't been visited. Note: orphan keywords are tidied up below.
    2167             :     "WHEN NEW.visit_count = 0 AND "
    2168             :       " NEW.foreign_count = (SELECT COUNT(*) FROM moz_keywords WHERE place_id = NEW.id) "
    2169             :     "BEGIN "
    2170             :       // No more references to the place, so we can delete the place itself.
    2171             :       "DELETE FROM moz_places "
    2172             :         "WHERE id = NEW.id; "
    2173             :       // Delete annotations relating to the place.
    2174             :       "DELETE FROM moz_annos "
    2175             :         "WHERE place_id = NEW.id; "
    2176             :       // Delete keywords relating to the place.
    2177             :       "DELETE FROM moz_keywords "
    2178             :         "WHERE place_id = NEW.id; "
    2179             :     "END "
    2180           0 :   ));
    2181           0 :   if (NS_FAILED(rv)) return rv;
    2182             : 
    2183             :   // Listens to moz_keyword deletions, to ensure moz_places gets the
    2184             :   // foreign_count updated corrrectly.
    2185           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2186             :     "CREATE TEMP TRIGGER moz_migrate_keyword_trigger "
    2187             :     "AFTER DELETE ON moz_keywords FOR EACH ROW "
    2188             :     "BEGIN "
    2189             :       // If we remove a keyword, then reduce the foreign_count.
    2190             :       "UPDATE moz_places "
    2191             :         "SET foreign_count = foreign_count - 1 "
    2192             :           "WHERE id = OLD.place_id; "
    2193             :     "END "
    2194           0 :   ));
    2195           0 :   if (NS_FAILED(rv)) return rv;
    2196             : 
    2197             :   // First of all, find the non-builtin roots.
    2198           0 :   nsCOMPtr<mozIStorageStatement> deleteStmt;
    2199           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2200             :     "WITH RECURSIVE "
    2201             :     "itemsToRemove(id, guid) AS ( "
    2202             :       "SELECT b.id, b.guid FROM moz_bookmarks b "
    2203             :       "JOIN moz_bookmarks p ON b.parent = p.id "
    2204             :       "WHERE p.guid = 'root________' AND "
    2205             :         "b.guid NOT IN ('menu________', 'toolbar_____', 'tags________', 'unfiled_____', 'mobile______') "
    2206             :       "UNION ALL "
    2207             :       "SELECT b.id, b.guid FROM moz_bookmarks b "
    2208             :       "JOIN itemsToRemove d ON d.id = b.parent "
    2209             :     ") "
    2210             :     "DELETE FROM moz_bookmarks "
    2211             :       "WHERE id IN (SELECT id FROM itemsToRemove) "
    2212           0 :   ), getter_AddRefs(deleteStmt));
    2213           0 :   if (NS_FAILED(rv)) return rv;
    2214             : 
    2215           0 :   rv = deleteStmt->Execute();
    2216           0 :   if (NS_FAILED(rv)) return rv;
    2217             : 
    2218             :   // Before we remove the triggers, check for keywords attached to places which
    2219             :   // no longer have a bookmark to them. We do this before removing the triggers,
    2220             :   // so that we can make use of the keyword trigger to update the counts in
    2221             :   // moz_places.
    2222           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2223             :     "DELETE FROM moz_keywords WHERE place_id IN ( "
    2224             :       "SELECT h.id FROM moz_keywords k "
    2225             :       "JOIN moz_places h ON h.id = k.place_id "
    2226             :       "GROUP BY place_id HAVING h.foreign_count = count(*) "
    2227             :     ")"
    2228           0 :   ));
    2229           0 :   if (NS_FAILED(rv)) return rv;
    2230             : 
    2231             :   // Now remove the temp triggers.
    2232           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2233             :     "DROP TRIGGER moz_migrate_bookmarks_trigger "
    2234           0 :   ));
    2235           0 :   if (NS_FAILED(rv)) return rv;
    2236           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2237             :     "DROP TRIGGER moz_migrate_annos_trigger "
    2238           0 :   ));
    2239           0 :   if (NS_FAILED(rv)) return rv;
    2240           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2241             :     "DROP TRIGGER moz_migrate_keyword_trigger "
    2242           0 :   ));
    2243           0 :   if (NS_FAILED(rv)) return rv;
    2244             : 
    2245             :   // Cleanup any orphan annotation attributes.
    2246           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2247             :     "DELETE FROM moz_anno_attributes WHERE id IN ( "
    2248             :       "SELECT id FROM moz_anno_attributes n "
    2249             :       "EXCEPT "
    2250             :       "SELECT DISTINCT anno_attribute_id FROM moz_annos "
    2251             :       "EXCEPT "
    2252             :       "SELECT DISTINCT anno_attribute_id FROM moz_items_annos "
    2253             :     ")"
    2254           0 :   ));
    2255           0 :   if (NS_FAILED(rv)) return rv;
    2256             : 
    2257           0 :   return NS_OK;
    2258             : }
    2259             : 
    2260             : nsresult
    2261           0 : Database::MigrateV45Up() {
    2262           0 :   nsCOMPtr<mozIStorageStatement> metaTableStmt;
    2263           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2264             :     "SELECT 1 FROM moz_meta"
    2265           0 :   ), getter_AddRefs(metaTableStmt));
    2266           0 :   if (NS_FAILED(rv)) {
    2267           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_META);
    2268           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2269             :   }
    2270             :   return NS_OK;
    2271             : }
    2272             : 
    2273             : nsresult
    2274           0 : Database::MigrateV46Up() {
    2275             :   // Convert the existing queries. For simplicity we assume the user didn't
    2276             :   // edit these queries, and just do a 1:1 conversion.
    2277           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2278             :     "UPDATE moz_places "
    2279             :       "SET url = IFNULL('place:tag=' || ( "
    2280             :         "SELECT title FROM moz_bookmarks "
    2281             :         "WHERE id = CAST(get_query_param(substr(url, 7), 'folder') AS INT) "
    2282             :       "), url) "
    2283             :     "WHERE url_hash BETWEEN hash('place', 'prefix_lo') AND "
    2284             :                            "hash('place', 'prefix_hi') "
    2285             :       "AND url LIKE '%type=7%' "
    2286             :       "AND EXISTS(SELECT 1 FROM moz_bookmarks "
    2287             :           "WHERE id = CAST(get_query_param(substr(url, 7), 'folder') AS INT)) "
    2288           0 :   ));
    2289             : 
    2290             :   // Recalculate hashes for all tag queries.
    2291           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2292             :     "UPDATE moz_places SET url_hash = hash(url) "
    2293             :     "WHERE url_hash BETWEEN hash('place', 'prefix_lo') AND "
    2294             :                            "hash('place', 'prefix_hi') "
    2295             :       "AND url LIKE '%tag=%' "
    2296           0 :   ));
    2297           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2298             : 
    2299             :   // Update Sync fields for all tag queries.
    2300           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2301             :     "UPDATE moz_bookmarks SET syncChangeCounter = syncChangeCounter + 1 "
    2302             :     "WHERE fk IN ( "
    2303             :       "SELECT id FROM moz_places "
    2304             :       "WHERE url_hash BETWEEN hash('place', 'prefix_lo') AND "
    2305             :                              "hash('place', 'prefix_hi') "
    2306             :         "AND url LIKE '%tag=%' "
    2307             :     ") "
    2308           0 :   ));
    2309           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2310             :   return NS_OK;
    2311             : }
    2312             : 
    2313             : nsresult
    2314           0 : Database::MigrateV47Up() {
    2315             :   // v46 may have mistakenly set some url to NULL, we must fix those.
    2316             :   // Since the original url was an invalid query, we replace NULLs with an
    2317             :   // empty query.
    2318           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2319             :     "UPDATE moz_places "
    2320             :     "SET url = 'place:excludeItems=1', url_hash = hash('place:excludeItems=1') "
    2321             :     "WHERE url ISNULL "
    2322           0 :   ));
    2323           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2324             :   // Update Sync fields for these queries.
    2325           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2326             :     "UPDATE moz_bookmarks SET syncChangeCounter = syncChangeCounter + 1 "
    2327             :     "WHERE fk IN ( "
    2328             :       "SELECT id FROM moz_places "
    2329             :       "WHERE url_hash = hash('place:excludeItems=1') "
    2330             :         "AND url = 'place:excludeItems=1' "
    2331             :     ") "
    2332           0 :   ));
    2333           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2334             :   return NS_OK;
    2335             : }
    2336             : 
    2337             : nsresult
    2338           0 : Database::MigrateV48Up() {
    2339             :   // Create and populate moz_origins.
    2340           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2341           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2342             :     "SELECT * FROM moz_origins; "
    2343           0 :   ), getter_AddRefs(stmt));
    2344           0 :   if (NS_FAILED(rv)) {
    2345           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ORIGINS);
    2346           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2347             :   }
    2348           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2349             :     "INSERT OR IGNORE INTO moz_origins (prefix, host, frecency) "
    2350             :     "SELECT get_prefix(url), get_host_and_port(url), -1 "
    2351             :     "FROM moz_places; "
    2352           0 :   ));
    2353           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2354             : 
    2355             :   // Add and populate moz_places.origin_id.
    2356           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2357             :     "SELECT origin_id FROM moz_places; "
    2358           0 :   ), getter_AddRefs(stmt));
    2359           0 :   if (NS_FAILED(rv)) {
    2360           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2361             :       "ALTER TABLE moz_places "
    2362             :       "ADD COLUMN origin_id INTEGER REFERENCES moz_origins(id); "
    2363           0 :     ));
    2364           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2365             :   }
    2366           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_ORIGIN_ID);
    2367           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2368           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2369             :     "UPDATE moz_places "
    2370             :     "SET origin_id = ( "
    2371             :       "SELECT id FROM moz_origins "
    2372             :       "WHERE prefix = get_prefix(url) AND host = get_host_and_port(url) "
    2373             :     "); "
    2374           0 :   ));
    2375           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2376             : 
    2377             :   // Setting this pref will cause InitSchema to begin async migration of
    2378             :   // frecencies to moz_origins.  The reason we don't defer the other steps
    2379             :   // above, like we do this one here, is because we want to make sure that the
    2380             :   // main data in moz_origins, prefix and host, are coherent in relation to
    2381             :   // moz_places.
    2382           0 :   Unused << Preferences::SetBool(PREF_MIGRATE_V48_FRECENCIES, true);
    2383             : 
    2384             :   // From this point on, nobody should use moz_hosts again.  Empty it so that we
    2385             :   // don't leak the user's history, but don't remove it yet so that the user can
    2386             :   // downgrade.
    2387           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2388             :     "DELETE FROM moz_hosts; "
    2389           0 :   ));
    2390           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2391             : 
    2392             :   return NS_OK;
    2393             : }
    2394             : 
    2395             : namespace {
    2396             : 
    2397           0 : class MigrateV48FrecenciesRunnable final : public Runnable
    2398             : {
    2399             : public:
    2400             :   NS_DECL_NSIRUNNABLE
    2401             :   explicit MigrateV48FrecenciesRunnable(mozIStorageConnection* aDBConn);
    2402             : private:
    2403             :   nsCOMPtr<mozIStorageConnection> mDBConn;
    2404             : };
    2405             : 
    2406           0 : MigrateV48FrecenciesRunnable::MigrateV48FrecenciesRunnable(mozIStorageConnection* aDBConn)
    2407             :   : Runnable("places::MigrateV48FrecenciesRunnable")
    2408           0 :   , mDBConn(aDBConn)
    2409             : {
    2410           0 : }
    2411             : 
    2412             : NS_IMETHODIMP
    2413           0 : MigrateV48FrecenciesRunnable::Run()
    2414             : {
    2415           0 :   if (NS_IsMainThread()) {
    2416             :     // Migration done.  Clear the pref.
    2417           0 :     Unused << Preferences::ClearUser(PREF_MIGRATE_V48_FRECENCIES);
    2418           0 :     return NS_OK;
    2419             :   }
    2420             : 
    2421             :   // We do the work in chunks, or the wal journal may grow too much.
    2422           0 :   nsCOMPtr<mozIStorageStatement> updateStmt;
    2423           0 :   nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2424             :     "UPDATE moz_origins "
    2425             :     "SET frecency = ( "
    2426             :       "SELECT MAX(frecency) "
    2427             :       "FROM moz_places "
    2428             :       "WHERE moz_places.origin_id = moz_origins.id "
    2429             :     ") "
    2430             :     "WHERE rowid IN ( "
    2431             :       "SELECT rowid "
    2432             :       "FROM moz_origins "
    2433             :       "WHERE frecency = -1 "
    2434             :       "LIMIT 400 "
    2435             :     ") "
    2436           0 :   ));
    2437           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2438             : 
    2439           0 :   nsCOMPtr<mozIStorageStatement> selectStmt;
    2440           0 :   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
    2441             :     "SELECT id FROM moz_origins WHERE frecency = -1 "
    2442           0 :   ), getter_AddRefs(selectStmt));
    2443           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2444           0 :   bool hasResult = false;
    2445           0 :   rv = selectStmt->ExecuteStep(&hasResult);
    2446           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2447           0 :   if (hasResult) {
    2448             :     // There are more results to handle. Re-dispatch to the same thread for the
    2449             :     // next chunk.
    2450           0 :     return NS_DispatchToCurrentThread(this);
    2451             :   }
    2452             : 
    2453             :   // Re-dispatch to the main-thread to flip the migration pref.
    2454           0 :   return NS_DispatchToMainThread(this);
    2455             : }
    2456             : 
    2457             : } // namespace
    2458             : 
    2459             : void
    2460           0 : Database::MigrateV48Frecencies()
    2461             : {
    2462           1 :   MOZ_ASSERT(NS_IsMainThread());
    2463             : 
    2464           1 :   if (!Preferences::GetBool(PREF_MIGRATE_V48_FRECENCIES)) {
    2465           0 :     return;
    2466             :   }
    2467             : 
    2468             :   RefPtr<MigrateV48FrecenciesRunnable> runnable =
    2469           0 :     new MigrateV48FrecenciesRunnable(mMainConn);
    2470           0 :   nsCOMPtr<nsIEventTarget> target = do_GetInterface(mMainConn);
    2471           0 :   MOZ_ASSERT(target);
    2472           0 :   Unused << target->Dispatch(runnable, NS_DISPATCH_NORMAL);
    2473             : }
    2474             : 
    2475             : nsresult
    2476           0 : Database::MigrateV49Up() {
    2477             :   // Calculate initial frecency stats, which should have been done as part of
    2478             :   // the v48 migration but wasn't.
    2479           0 :   nsNavHistory *navHistory = nsNavHistory::GetHistoryService();
    2480           0 :   NS_ENSURE_STATE(navHistory);
    2481           0 :   nsresult rv = navHistory->RecalculateFrecencyStats(nullptr);
    2482           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2483             : 
    2484             :   // These hidden preferences were added along with the v48 migration as part of
    2485             :   // the frecency stats implementation but are now replaced with entries in the
    2486             :   // moz_meta table.
    2487           0 :   Unused << Preferences::ClearUser("places.frecency.stats.count");
    2488           0 :   Unused << Preferences::ClearUser("places.frecency.stats.sum");
    2489           0 :   Unused << Preferences::ClearUser("places.frecency.stats.sumOfSquares");
    2490             : 
    2491           0 :   return NS_OK;
    2492             : }
    2493             : 
    2494             : nsresult
    2495           0 : Database::MigrateV50Up() {
    2496             :   // Convert the existing queries. We don't have REGEX available, so the simplest
    2497             :   // thing to do is to pull the urls out, and process them manually.
    2498           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2499           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2500             :     "SELECT id, url FROM moz_places "
    2501             :     "WHERE url_hash BETWEEN hash('place', 'prefix_lo') AND "
    2502             :                            "hash('place', 'prefix_hi') "
    2503             :       "AND url LIKE '%folder=%' "
    2504           0 :   ), getter_AddRefs(stmt));
    2505           0 :   if (NS_FAILED(rv)) return rv;
    2506             : 
    2507           0 :   AutoTArray<Pair<int64_t, nsCString>, 32> placeURLs;
    2508             : 
    2509           0 :   bool hasMore = false;
    2510           0 :   nsCString url;
    2511           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    2512             :     int64_t placeId;
    2513           0 :     rv = stmt->GetInt64(0, &placeId);
    2514           0 :     if (NS_FAILED(rv)) return rv;
    2515           0 :     rv = stmt->GetUTF8String(1, url);
    2516           0 :     if (NS_FAILED(rv)) return rv;
    2517             : 
    2518           0 :     if (!placeURLs.AppendElement(MakePair(placeId, url))) {
    2519             :       return NS_ERROR_OUT_OF_MEMORY;
    2520             :     }
    2521             :   }
    2522             : 
    2523           0 :   if (placeURLs.IsEmpty()) {
    2524             :     return NS_OK;
    2525             :   }
    2526             : 
    2527             :   int64_t placeId;
    2528           0 :   for (uint32_t i = 0; i < placeURLs.Length(); ++i) {
    2529           0 :     placeId = placeURLs[i].first();
    2530           0 :     url = placeURLs[i].second();
    2531             : 
    2532           0 :     rv = ConvertOldStyleQuery(url);
    2533             :     // Something bad happened, and we can't convert it, so just continue.
    2534           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2535           0 :       continue;
    2536             :     }
    2537             : 
    2538           0 :     nsCOMPtr<mozIStorageStatement> updateStmt;
    2539           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2540             :       "UPDATE moz_places "
    2541             :       "SET url = :url, url_hash = hash(:url) "
    2542             :       "WHERE id = :placeId "
    2543           0 :     ), getter_AddRefs(updateStmt));
    2544           0 :     if (NS_FAILED(rv)) return rv;
    2545             : 
    2546           0 :     rv = URIBinder::Bind(updateStmt, NS_LITERAL_CSTRING("url"), url);
    2547           0 :     if (NS_FAILED(rv)) return rv;
    2548           0 :     rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("placeId"), placeId);
    2549           0 :     if (NS_FAILED(rv)) return rv;
    2550             : 
    2551           0 :     rv = updateStmt->Execute();
    2552           0 :     if (NS_FAILED(rv)) return rv;
    2553             : 
    2554             :     // Update Sync fields for these queries.
    2555           0 :     nsCOMPtr<mozIStorageStatement> syncStmt;
    2556           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2557             :       "UPDATE moz_bookmarks SET syncChangeCounter = syncChangeCounter + 1 "
    2558             :       "WHERE fk = :placeId "
    2559           0 :     ), getter_AddRefs(syncStmt));
    2560           0 :     if (NS_FAILED(rv)) return rv;
    2561             : 
    2562           0 :     rv = syncStmt->BindInt64ByName(NS_LITERAL_CSTRING("placeId"), placeId);
    2563           0 :     if (NS_FAILED(rv)) return rv;
    2564             : 
    2565           0 :     rv = syncStmt->Execute();
    2566           0 :     if (NS_FAILED(rv)) return rv;
    2567             :   }
    2568             : 
    2569             :   return NS_OK;
    2570             : }
    2571             : 
    2572             : 
    2573           0 : struct StringWriteFunc : public JSONWriteFunc
    2574             : {
    2575             :   nsCString& mCString;
    2576           0 :   explicit StringWriteFunc(nsCString& aCString) : mCString(aCString)
    2577             :   {
    2578             :   }
    2579           0 :   void Write(const char* aStr) override { mCString.Append(aStr); }
    2580             : };
    2581             : 
    2582             : nsresult
    2583           0 : Database::MigrateV51Up()
    2584             : {
    2585           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2586           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2587             :     "SELECT b.guid FROM moz_anno_attributes n "
    2588             :     "JOIN moz_items_annos a ON n.id = a.anno_attribute_id "
    2589             :     "JOIN moz_bookmarks b ON a.item_id = b.id "
    2590             :     "WHERE n.name = :anno_name ORDER BY a.content DESC"
    2591           0 :   ), getter_AddRefs(stmt));
    2592           0 :   if (NS_FAILED(rv)) {
    2593           0 :     MOZ_ASSERT(false, "Should succeed unless item annotations table has been removed");
    2594             :     return NS_OK;
    2595             :   };
    2596             : 
    2597           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
    2598           0 :                                   LAST_USED_ANNO);
    2599           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2600             : 
    2601           0 :   nsAutoCString json;
    2602           0 :   JSONWriter jw{ MakeUnique<StringWriteFunc>(json) };
    2603           0 :   jw.StartArrayProperty(nullptr, JSONWriter::SingleLineStyle);
    2604             : 
    2605           0 :   bool hasAtLeastOne = false;
    2606           0 :   bool hasMore = false;
    2607             :   uint32_t length;
    2608           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    2609           0 :     hasAtLeastOne = true;
    2610           0 :     jw.StringElement(stmt->AsSharedUTF8String(0, &length));
    2611             :   }
    2612           0 :   jw.EndArray();
    2613             : 
    2614             :   // If we don't have any, just abort early and save the extra work.
    2615           0 :   if (!hasAtLeastOne) {
    2616             :     return NS_OK;
    2617             :   }
    2618             : 
    2619           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2620             :     "INSERT OR REPLACE INTO moz_meta "
    2621             :     "VALUES (:key, :value) "
    2622           0 :   ), getter_AddRefs(stmt));
    2623           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2624             : 
    2625           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("key"),
    2626           0 :                                   LAST_USED_FOLDERS_META_KEY);
    2627           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2628           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("value"), json);
    2629           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2630           0 :   rv = stmt->Execute();
    2631           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2632             : 
    2633             :   // Clean up the now redundant annotations.
    2634           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2635             :     "DELETE FROM moz_items_annos WHERE anno_attribute_id = "
    2636             :       "(SELECT id FROM moz_anno_attributes WHERE name = :anno_name) "
    2637           0 :   ), getter_AddRefs(stmt));
    2638           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2639           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), LAST_USED_ANNO);
    2640           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2641           0 :   rv = stmt->Execute();
    2642           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2643             : 
    2644           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2645             :     "DELETE FROM moz_anno_attributes WHERE name = :anno_name "
    2646           0 :   ), getter_AddRefs(stmt));
    2647           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2648           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),  LAST_USED_ANNO);
    2649           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2650           0 :   rv = stmt->Execute();
    2651           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2652             : 
    2653             :   return NS_OK;
    2654             : }
    2655             : 
    2656             : 
    2657             : nsresult
    2658           0 : Database::ConvertOldStyleQuery(nsCString& aURL)
    2659             : {
    2660           0 :   AutoTArray<QueryKeyValuePair, 8> tokens;
    2661           0 :   nsresult rv = TokenizeQueryString(aURL, &tokens);
    2662           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2663             : 
    2664           0 :   AutoTArray<QueryKeyValuePair, 8> newTokens;
    2665           0 :   bool invalid = false;
    2666           0 :   nsAutoCString guid;
    2667             : 
    2668           0 :   for (uint32_t j = 0; j < tokens.Length(); ++j) {
    2669           0 :     const QueryKeyValuePair& kvp = tokens[j];
    2670             : 
    2671           0 :     if (!kvp.key.EqualsLiteral("folder")) {
    2672           0 :       if (!newTokens.AppendElement(kvp)) {
    2673             :         return NS_ERROR_OUT_OF_MEMORY;
    2674             :       }
    2675             :       continue;
    2676             :     }
    2677             : 
    2678           0 :     int64_t itemId = kvp.value.ToInteger(&rv);
    2679           0 :     if (NS_SUCCEEDED(rv)) {
    2680             :       // We have the folder's ID, now to find its GUID.
    2681           0 :       nsCOMPtr<mozIStorageStatement> stmt;
    2682           0 :       nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2683             :         "SELECT guid FROM moz_bookmarks "
    2684             :         "WHERE id = :itemId "
    2685           0 :       ), getter_AddRefs(stmt));
    2686           0 :       if (NS_FAILED(rv)) return rv;
    2687             : 
    2688           0 :       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("itemId"), itemId);
    2689           0 :       if (NS_FAILED(rv)) return rv;
    2690             : 
    2691           0 :       bool hasMore = false;
    2692           0 :       if (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    2693           0 :         rv = stmt->GetUTF8String(0, guid);
    2694           0 :         if (NS_FAILED(rv)) return rv;
    2695             :       }
    2696           0 :     } else if (kvp.value.EqualsLiteral("PLACES_ROOT")) {
    2697           0 :       guid = NS_LITERAL_CSTRING(ROOT_GUID);
    2698           0 :     } else if (kvp.value.EqualsLiteral("BOOKMARKS_MENU")) {
    2699           0 :       guid = NS_LITERAL_CSTRING(MENU_ROOT_GUID);
    2700           0 :     } else if (kvp.value.EqualsLiteral("TAGS")) {
    2701           0 :       guid = NS_LITERAL_CSTRING(TAGS_ROOT_GUID);
    2702           0 :     } else if (kvp.value.EqualsLiteral("UNFILED_BOOKMARKS")) {
    2703           0 :       guid = NS_LITERAL_CSTRING(UNFILED_ROOT_GUID);
    2704           0 :     } else if (kvp.value.EqualsLiteral("TOOLBAR")) {
    2705           0 :       guid = NS_LITERAL_CSTRING(TOOLBAR_ROOT_GUID);
    2706           0 :     } else if (kvp.value.EqualsLiteral("MOBILE_BOOKMARKS")) {
    2707           0 :       guid = NS_LITERAL_CSTRING(MOBILE_ROOT_GUID);
    2708             :     }
    2709             : 
    2710             :     QueryKeyValuePair* newPair;
    2711           0 :     if (guid.IsEmpty()) {
    2712             :       // This is invalid, so we'll change this key/value pair to something else
    2713             :       // so that the query remains a valid url.
    2714           0 :       newPair = new QueryKeyValuePair(NS_LITERAL_CSTRING("invalidOldParentId"), kvp.value);
    2715           0 :       invalid = true;
    2716             :     } else {
    2717           0 :       newPair = new QueryKeyValuePair(NS_LITERAL_CSTRING("parent"), guid);
    2718             :     }
    2719           0 :     if (!newTokens.AppendElement(*newPair)) {
    2720             :       return NS_ERROR_OUT_OF_MEMORY;
    2721             :     }
    2722           0 :     delete newPair;
    2723             :   }
    2724             : 
    2725           0 :   if (invalid) {
    2726             :     // One or more of the folders don't exist, replace with an empty query.
    2727           0 :     newTokens.AppendElement(QueryKeyValuePair(NS_LITERAL_CSTRING("excludeItems"),
    2728           0 :                                               NS_LITERAL_CSTRING("1")));
    2729             :   }
    2730             : 
    2731           0 :   TokensToQueryString(newTokens, aURL);
    2732           0 :   return NS_OK;
    2733             : }
    2734             : 
    2735             : nsresult
    2736           0 : Database::GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType,
    2737             :                            nsTArray<int64_t>& aItemIds)
    2738             : {
    2739           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2740           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2741             :     "SELECT b.id FROM moz_items_annos a "
    2742             :     "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
    2743             :     "JOIN moz_bookmarks b ON b.id = a.item_id "
    2744             :     "WHERE n.name = :anno_name AND "
    2745             :           "b.type = :item_type"
    2746           0 :   ), getter_AddRefs(stmt));
    2747           0 :   if (NS_FAILED(rv)) return rv;
    2748             : 
    2749           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aAnnoName);
    2750           0 :   if (NS_FAILED(rv)) return rv;
    2751           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_type"), aItemType);
    2752           0 :   if (NS_FAILED(rv)) return rv;
    2753             : 
    2754           0 :   bool hasMore = false;
    2755           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    2756             :     int64_t itemId;
    2757           0 :     rv = stmt->GetInt64(0, &itemId);
    2758           0 :     if (NS_FAILED(rv)) return rv;
    2759           0 :     aItemIds.AppendElement(itemId);
    2760             :   }
    2761             : 
    2762             :   return NS_OK;
    2763             : }
    2764             : 
    2765             : nsresult
    2766           0 : Database::DeleteBookmarkItem(int32_t aItemId)
    2767             : {
    2768             :   // Delete the old bookmark.
    2769           0 :   nsCOMPtr<mozIStorageStatement> deleteStmt;
    2770           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2771             :     "DELETE FROM moz_bookmarks WHERE id = :item_id"
    2772           0 :   ), getter_AddRefs(deleteStmt));
    2773           0 :   if (NS_FAILED(rv)) return rv;
    2774             : 
    2775           0 :   rv = deleteStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
    2776           0 :                                    aItemId);
    2777           0 :   if (NS_FAILED(rv)) return rv;
    2778             : 
    2779           0 :   rv = deleteStmt->Execute();
    2780           0 :   if (NS_FAILED(rv)) return rv;
    2781             : 
    2782             :   // Clean up orphan annotations.
    2783           0 :   nsCOMPtr<mozIStorageStatement> removeAnnosStmt;
    2784           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2785             :     "DELETE FROM moz_items_annos WHERE item_id = :item_id"
    2786           0 :   ), getter_AddRefs(removeAnnosStmt));
    2787           0 :   if (NS_FAILED(rv)) return rv;
    2788             : 
    2789           0 :   rv = removeAnnosStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
    2790           0 :                                         aItemId);
    2791           0 :   if (NS_FAILED(rv)) return rv;
    2792             : 
    2793           0 :   rv = removeAnnosStmt->Execute();
    2794           0 :   if (NS_FAILED(rv)) return rv;
    2795             : 
    2796           0 :   return NS_OK;
    2797             : }
    2798             : 
    2799             : int64_t
    2800           0 : Database::CreateMobileRoot()
    2801             : {
    2802           1 :   MOZ_ASSERT(NS_IsMainThread());
    2803             : 
    2804             :   // Create the mobile root, ignoring conflicts if one already exists (for
    2805             :   // example, if the user downgraded to an earlier release channel).
    2806           2 :   nsCOMPtr<mozIStorageStatement> createStmt;
    2807           3 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2808             :     "INSERT OR IGNORE INTO moz_bookmarks "
    2809             :       "(type, title, dateAdded, lastModified, guid, position, parent) "
    2810             :     "SELECT :item_type, :item_title, :timestamp, :timestamp, :guid, "
    2811             :       "IFNULL((SELECT MAX(position) + 1 FROM moz_bookmarks p WHERE p.parent = b.id), 0), b.id "
    2812             :     "FROM moz_bookmarks b WHERE b.parent = 0"
    2813           0 :   ), getter_AddRefs(createStmt));
    2814           1 :   if (NS_FAILED(rv)) return -1;
    2815             : 
    2816           0 :   rv = createStmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
    2817           3 :                                    nsINavBookmarksService::TYPE_FOLDER);
    2818           0 :   if (NS_FAILED(rv)) return -1;
    2819           0 :   rv = createStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
    2820           0 :                                         NS_LITERAL_CSTRING(MOBILE_ROOT_TITLE));
    2821           0 :   if (NS_FAILED(rv)) return -1;
    2822           0 :   rv = createStmt->BindInt64ByName(NS_LITERAL_CSTRING("timestamp"),
    2823           3 :                                    RoundedPRNow());
    2824           1 :   if (NS_FAILED(rv)) return -1;
    2825           0 :   rv = createStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
    2826           6 :                                         NS_LITERAL_CSTRING(MOBILE_ROOT_GUID));
    2827           0 :   if (NS_FAILED(rv)) return -1;
    2828             : 
    2829           0 :   rv = createStmt->Execute();
    2830           0 :   if (NS_FAILED(rv)) return -1;
    2831             : 
    2832             :   // Find the mobile root ID. We can't use the last inserted ID because the
    2833             :   // root might already exist, and we ignore on conflict.
    2834           0 :   nsCOMPtr<mozIStorageStatement> findIdStmt;
    2835           3 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2836             :     "SELECT id FROM moz_bookmarks WHERE guid = :guid"
    2837           0 :   ), getter_AddRefs(findIdStmt));
    2838           0 :   if (NS_FAILED(rv)) return -1;
    2839             : 
    2840           0 :   rv = findIdStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
    2841           6 :                                         NS_LITERAL_CSTRING(MOBILE_ROOT_GUID));
    2842           1 :   if (NS_FAILED(rv)) return -1;
    2843             : 
    2844           0 :   bool hasResult = false;
    2845           0 :   rv = findIdStmt->ExecuteStep(&hasResult);
    2846           1 :   if (NS_FAILED(rv) || !hasResult) return -1;
    2847             : 
    2848             :   int64_t rootId;
    2849           1 :   rv = findIdStmt->GetInt64(0, &rootId);
    2850           1 :   if (NS_FAILED(rv)) return -1;
    2851             : 
    2852           0 :   return rootId;
    2853             : }
    2854             : 
    2855             : void
    2856           0 : Database::Shutdown()
    2857             : {
    2858             :   // As the last step in the shutdown path, finalize the database handle.
    2859           0 :   MOZ_ASSERT(NS_IsMainThread());
    2860           0 :   MOZ_ASSERT(!mClosed);
    2861             : 
    2862             :   // Break cycles with the shutdown blockers.
    2863           0 :   mClientsShutdown = nullptr;
    2864           0 :   nsCOMPtr<mozIStorageCompletionCallback> connectionShutdown = mConnectionShutdown.forget();
    2865             : 
    2866           0 :   if (!mMainConn) {
    2867             :     // The connection has never been initialized. Just mark it as closed.
    2868           0 :     mClosed = true;
    2869           0 :     (void)connectionShutdown->Complete(NS_OK, nullptr);
    2870           0 :     return;
    2871             :   }
    2872             : 
    2873             : #ifdef DEBUG
    2874             :   {
    2875             :     bool hasResult;
    2876           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    2877             : 
    2878             :     // Sanity check for missing guids.
    2879           0 :     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2880             :       "SELECT 1 "
    2881             :       "FROM moz_places "
    2882             :       "WHERE guid IS NULL "
    2883           0 :     ), getter_AddRefs(stmt));
    2884           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2885           0 :     rv = stmt->ExecuteStep(&hasResult);
    2886           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2887           0 :     MOZ_ASSERT(!hasResult, "Found a page without a GUID!");
    2888           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2889             :       "SELECT 1 "
    2890             :       "FROM moz_bookmarks "
    2891             :       "WHERE guid IS NULL "
    2892           0 :     ), getter_AddRefs(stmt));
    2893           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2894           0 :     rv = stmt->ExecuteStep(&hasResult);
    2895           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2896           0 :     MOZ_ASSERT(!hasResult, "Found a bookmark without a GUID!");
    2897             : 
    2898             :     // Sanity check for unrounded dateAdded and lastModified values (bug
    2899             :     // 1107308).
    2900           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2901             :         "SELECT 1 "
    2902             :         "FROM moz_bookmarks "
    2903             :         "WHERE dateAdded % 1000 > 0 OR lastModified % 1000 > 0 LIMIT 1"
    2904           0 :       ), getter_AddRefs(stmt));
    2905           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2906           0 :     rv = stmt->ExecuteStep(&hasResult);
    2907           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2908           0 :     MOZ_ASSERT(!hasResult, "Found unrounded dates!");
    2909             : 
    2910             :     // Sanity check url_hash
    2911           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2912             :       "SELECT 1 FROM moz_places WHERE url_hash = 0"
    2913           0 :     ), getter_AddRefs(stmt));
    2914           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2915           0 :     rv = stmt->ExecuteStep(&hasResult);
    2916           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2917           0 :     MOZ_ASSERT(!hasResult, "Found a place without a hash!");
    2918             : 
    2919             :     // Sanity check unique urls
    2920           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2921             :       "SELECT 1 FROM moz_places GROUP BY url HAVING count(*) > 1 "
    2922           0 :     ), getter_AddRefs(stmt));
    2923           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2924           0 :     rv = stmt->ExecuteStep(&hasResult);
    2925           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2926           0 :     MOZ_ASSERT(!hasResult, "Found a duplicate url!");
    2927             : 
    2928             :     // Sanity check NULL urls
    2929           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2930             :       "SELECT 1 FROM moz_places WHERE url ISNULL "
    2931           0 :     ), getter_AddRefs(stmt));
    2932           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2933           0 :     rv = stmt->ExecuteStep(&hasResult);
    2934           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2935           0 :     MOZ_ASSERT(!hasResult, "Found a NULL url!");
    2936             :   }
    2937             : #endif
    2938             : 
    2939           0 :   mMainThreadStatements.FinalizeStatements();
    2940           0 :   mMainThreadAsyncStatements.FinalizeStatements();
    2941             : 
    2942             :   RefPtr< FinalizeStatementCacheProxy<mozIStorageStatement> > event =
    2943             :     new FinalizeStatementCacheProxy<mozIStorageStatement>(
    2944             :           mAsyncThreadStatements,
    2945             :           NS_ISUPPORTS_CAST(nsIObserver*, this)
    2946           0 :         );
    2947           0 :   DispatchToAsyncThread(event);
    2948             : 
    2949           0 :   mClosed = true;
    2950             : 
    2951             :   // Execute PRAGMA optimized as last step, this will ensure proper database
    2952             :   // performance across restarts.
    2953           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    2954             :   MOZ_ALWAYS_SUCCEEDS(mMainConn->ExecuteSimpleSQLAsync(NS_LITERAL_CSTRING(
    2955             :     "PRAGMA optimize(0x02)"
    2956             :   ), nullptr, getter_AddRefs(ps)));
    2957             : 
    2958             :   (void)mMainConn->AsyncClose(connectionShutdown);
    2959             :   mMainConn = nullptr;
    2960             : }
    2961             : 
    2962             : ////////////////////////////////////////////////////////////////////////////////
    2963             : //// nsIObserver
    2964             : 
    2965             : NS_IMETHODIMP
    2966             : Database::Observe(nsISupports *aSubject,
    2967             :                   const char *aTopic,
    2968             :                   const char16_t *aData)
    2969             : {
    2970             :   MOZ_ASSERT(NS_IsMainThread());
    2971             :   if (strcmp(aTopic, TOPIC_PROFILE_CHANGE_TEARDOWN) == 0) {
    2972             :     // Tests simulating shutdown may cause multiple notifications.
    2973             :     if (IsShutdownStarted()) {
    2974             :       return NS_OK;
    2975             :     }
    2976             : 
    2977             :     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    2978             :     NS_ENSURE_STATE(os);
    2979             : 
    2980             :     // If shutdown happens in the same mainthread loop as init, observers could
    2981             :     // handle the places-init-complete notification after xpcom-shutdown, when
    2982             :     // the connection does not exist anymore.  Removing those observers would
    2983             :     // be less expensive but may cause their RemoveObserver calls to throw.
    2984             :     // Thus notify the topic now, so they stop listening for it.
    2985             :     nsCOMPtr<nsISimpleEnumerator> e;
    2986             :     if (NS_SUCCEEDED(os->EnumerateObservers(TOPIC_PLACES_INIT_COMPLETE,
    2987             :                      getter_AddRefs(e))) && e) {
    2988             :       bool hasMore = false;
    2989             :       while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
    2990             :         nsCOMPtr<nsISupports> supports;
    2991             :         if (NS_SUCCEEDED(e->GetNext(getter_AddRefs(supports)))) {
    2992             :           nsCOMPtr<nsIObserver> observer = do_QueryInterface(supports);
    2993             :           (void)observer->Observe(observer, TOPIC_PLACES_INIT_COMPLETE, nullptr);
    2994             :         }
    2995             :       }
    2996             :     }
    2997             : 
    2998             :     // Notify all Places users that we are about to shutdown.
    2999             :     (void)os->NotifyObservers(nullptr, TOPIC_PLACES_SHUTDOWN, nullptr);
    3000             :   } else if (strcmp(aTopic, TOPIC_SIMULATE_PLACES_SHUTDOWN) == 0) {
    3001             :     // This notification is (and must be) only used by tests that are trying
    3002             :     // to simulate Places shutdown out of the normal shutdown path.
    3003             : 
    3004             :     // Tests simulating shutdown may cause re-entrance.
    3005             :     if (IsShutdownStarted()) {
    3006             :       return NS_OK;
    3007             :     }
    3008             : 
    3009             :     // We are simulating a shutdown, so invoke the shutdown blockers,
    3010             :     // wait for them, then proceed with connection shutdown.
    3011             :     // Since we are already going through shutdown, but it's not the real one,
    3012             :     // we won't need to block the real one anymore, so we can unblock it.
    3013             :     {
    3014             :       nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileChangeTeardownPhase();
    3015             :       if (shutdownPhase) {
    3016             :         shutdownPhase->RemoveBlocker(mClientsShutdown.get());
    3017             :       }
    3018             :       (void)mClientsShutdown->BlockShutdown(nullptr);
    3019             :     }
    3020             : 
    3021             :     // Spin the events loop until the clients are done.
    3022             :     // Note, this is just for tests, specifically test_clearHistory_shutdown.js
    3023             :     SpinEventLoopUntil([&]() {
    3024             :       return mClientsShutdown->State() == PlacesShutdownBlocker::States::RECEIVED_DONE;
    3025             :     });
    3026             : 
    3027             :     {
    3028             :       nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileBeforeChangePhase();
    3029             :       if (shutdownPhase) {
    3030             :         shutdownPhase->RemoveBlocker(mConnectionShutdown.get());
    3031             :       }
    3032             :       (void)mConnectionShutdown->BlockShutdown(nullptr);
    3033             :     }
    3034             :   }
    3035             :   return NS_OK;
    3036             : }
    3037             : 
    3038             : uint32_t
    3039             : Database::MaxUrlLength() {
    3040             :   MOZ_ASSERT(NS_IsMainThread());
    3041             :   if (!mMaxUrlLength) {
    3042             :     mMaxUrlLength = Preferences::GetInt(PREF_HISTORY_MAXURLLEN,
    3043             :                                         PREF_HISTORY_MAXURLLEN_DEFAULT);
    3044             :     if (mMaxUrlLength < 255 || mMaxUrlLength > INT32_MAX) {
    3045             :       mMaxUrlLength = PREF_HISTORY_MAXURLLEN_DEFAULT;
    3046             :     }
    3047             :   }
    3048             :   return mMaxUrlLength;
    3049             : }
    3050             : 
    3051             : 
    3052             : 
    3053             : } // namespace places
    3054             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952