diff --git a/Providers/Profile.swift b/Providers/Profile.swift index d136290bb0ad..b76699185cbe 100644 --- a/Providers/Profile.swift +++ b/Providers/Profile.swift @@ -331,6 +331,10 @@ open class BrowserProfile: Profile { log.debug("Reopening profile.") isShutdown = false + if !places.isOpen && !RustFirefoxAccounts.shared.accountManager.hasAccount() { + places.migrateBookmarksIfNeeded(fromBrowserDB: db) + } + db.reopenIfClosed() _ = logins.reopenIfClosed() _ = places.reopenIfClosed() @@ -427,10 +431,9 @@ open class BrowserProfile: Profile { return self.legacyPlaces } - lazy var places: RustPlaces = { - let databasePath = URL(fileURLWithPath: (try! files.getAndEnsureDirectory()), isDirectory: true).appendingPathComponent("places.db").path - return RustPlaces(databasePath: databasePath) - }() + lazy var placesDbPath = URL(fileURLWithPath: (try! files.getAndEnsureDirectory()), isDirectory: true).appendingPathComponent("places.db").path + + lazy var places = RustPlaces(databasePath: placesDbPath) lazy var searchEngines: SearchEngines = { return SearchEngines(prefs: self.prefs, files: self.files) diff --git a/Storage/Rust/RustPlaces.swift b/Storage/Rust/RustPlaces.swift index fbd7c12ee49b..e1ca4958181a 100644 --- a/Storage/Rust/RustPlaces.swift +++ b/Storage/Rust/RustPlaces.swift @@ -120,6 +120,35 @@ public class RustPlaces { return deferred } + public func migrateBookmarksIfNeeded(fromBrowserDB browserDB: BrowserDB) { + // Since we use the existence of places.db as an indication that we've + // already migrated bookmarks, assert that places.db is not open here. + assert(!isOpen, "Shouldn't attempt to migrate bookmarks after opening Rust places.db") + + // We only need to migrate bookmarks here if the old browser.db file + // already exists AND the new Rust places.db file does NOT exist yet. + // This is to ensure that we only ever run this migration ONCE. In + // addition, it is the caller's (Profile.swift) responsibility to NOT + // use this migration API for users signed into a Firefox Account. + // Those users will automatically get all their bookmarks on next Sync. + guard FileManager.default.fileExists(atPath: browserDB.databasePath), + !FileManager.default.fileExists(atPath: databasePath) else { + return + } + + // Ensure that the old BrowserDB schema is up-to-date before migrating. + _ = browserDB.touch().value + + // Open the Rust places.db now for the first time. + _ = reopenIfClosed() + + do { + try api?.migrateBookmarksFromBrowserDb(path: browserDB.databasePath) + } catch let err as NSError { + Sentry.shared.sendWithStacktrace(message: "Error encountered while migrating bookmarks from BrowserDB", tag: SentryTag.rustPlaces, severity: .error, description: err.localizedDescription) + } + } + public func getBookmarksTree(rootGUID: GUID, recursive: Bool) -> Deferred> { return withReader { connection in return try connection.getBookmarksTree(rootGUID: rootGUID, recursive: recursive)