diff --git a/toolkit/components/places/src/nsNavBookmarks.cpp b/toolkit/components/places/src/nsNavBookmarks.cpp index e63c6bd7f86..7699ccb27d2 100644 --- a/toolkit/components/places/src/nsNavBookmarks.cpp +++ b/toolkit/components/places/src/nsNavBookmarks.cpp @@ -2280,6 +2280,168 @@ nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver *aObserver) return mObservers.RemoveWeakElement(aObserver); } +/** + * Called by the History service when shutting down + */ +nsresult +nsNavBookmarks::OnQuit() +{ + // get bookmarks file + nsCOMPtr bookmarksFile; + nsresult rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE, + getter_AddRefs(bookmarksFile)); + NS_ENSURE_SUCCESS(rv, rv); + + // create if it doesn't exist + PRBool exists; + rv = bookmarksFile->Exists(&exists); + if (NS_FAILED(rv) || !exists) { + rv = bookmarksFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600); + NS_ASSERTION(rv, "Unable to create bookmarks.html!"); + return rv; + } + + // export bookmarks.html + rv = ExportBookmarksHTML(bookmarksFile); + NS_ENSURE_SUCCESS(rv, rv); + + // archive if needed + nsCOMPtr prefServ(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr bookmarksPrefs; + rv = prefServ->GetBranch("browser.bookmarks.", getter_AddRefs(bookmarksPrefs)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 numberOfBackups; + rv = bookmarksPrefs->GetIntPref("max_backups", &numberOfBackups); + if (NS_FAILED(rv)) + numberOfBackups = 5; + + if (numberOfBackups > 0) { + rv = ArchiveBookmarksFile(numberOfBackups, PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +/** + * ArchiveBookmarksFile() + * + * Creates a dated backup once a day in /bookmarkbackups + * + * PRInt32 numberOfBackups - the maximum number of backups to keep + * + * PRBool forceArchive - forces creating an archive even if one was + * already created that day (overwrites) + */ +nsresult +nsNavBookmarks::ArchiveBookmarksFile(PRInt32 numberOfBackups, + PRBool forceArchive) +{ + nsCOMPtr bookmarksBackupDir; + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(bookmarksBackupDir)); + NS_ENSURE_SUCCESS(rv, rv); + + nsDependentCString dirName("bookmarkbackups"); + rv = bookmarksBackupDir->AppendNative(dirName); + NS_ENSURE_SUCCESS(rv, rv); + + PRBool exists; + rv = bookmarksBackupDir->Exists(&exists); + if (NS_FAILED(rv) || !exists) { + rv = bookmarksBackupDir->Create(nsIFile::DIRECTORY_TYPE, 0700); + + // if there's no backup folder, there's no backup, fail + NS_ENSURE_SUCCESS(rv, rv); + } + + // construct the new leafname + PRTime now64 = PR_Now(); + PRExplodedTime nowInfo; + PR_ExplodeTime(now64, PR_LocalTimeParameters, &nowInfo); + PR_NormalizeTime(&nowInfo, PR_LocalTimeParameters); + + char timeString[128]; + + PR_FormatTime(timeString, 128, "bookmarks-%Y-%m-%d.html", &nowInfo); + + nsCAutoString backupFilenameCString(timeString); + nsAutoString backupFilenameString = NS_ConvertUTF8toUTF16(backupFilenameCString); + + nsCOMPtr backupFile; + if (forceArchive) { + // if we have a backup from today, nuke it + nsCOMPtr currentBackup; + rv = bookmarksBackupDir->Clone(getter_AddRefs(currentBackup)); + NS_ENSURE_SUCCESS(rv, rv); + rv = currentBackup->Append(backupFilenameString); + NS_ENSURE_SUCCESS(rv, rv); + rv = currentBackup->Exists(&exists); + if (!NS_FAILED(rv) && exists) { + rv = currentBackup->Remove(PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + } + } else { + nsCOMPtr existingBackups; + rv = bookmarksBackupDir->GetDirectoryEntries(getter_AddRefs(existingBackups)); + NS_ENSURE_SUCCESS(rv, rv); + + nsStringArray backupFileNames; + + PRBool hasMoreElements = PR_FALSE; + PRBool hasCurrentBackup = PR_FALSE; + + while (NS_SUCCEEDED(existingBackups->HasMoreElements(&hasMoreElements)) && + hasMoreElements) + { + rv = existingBackups->GetNext(getter_AddRefs(backupFile)); + NS_ENSURE_SUCCESS(rv, rv); + nsAutoString backupName; + rv = backupFile->GetLeafName(backupName); + NS_ENSURE_SUCCESS(rv, rv); + + // the backup for today exists, do not create later + if (backupName == backupFilenameString) { + hasCurrentBackup = PR_TRUE; + continue; + } + + // mark the rest for possible removal + if (Substring(backupName, 0, 10) == NS_LITERAL_STRING("bookmarks-")) + backupFileNames.AppendString(backupName); + } + + if (numberOfBackups > 0 && backupFileNames.Count() >= numberOfBackups) { + PRInt32 numberOfBackupsToDelete = backupFileNames.Count() - numberOfBackups + 1; + backupFileNames.Sort(); + + while (numberOfBackupsToDelete--) { + (void)bookmarksBackupDir->Clone(getter_AddRefs(backupFile)); + (void)backupFile->Append(*backupFileNames[0]); + (void)backupFile->Remove(PR_FALSE); + backupFileNames.RemoveStringAt(0); + } + } + + if (hasCurrentBackup) + return NS_OK; + } + + nsCOMPtr bookmarksFile; + rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE, + getter_AddRefs(bookmarksFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = bookmarksFile->CopyTo(bookmarksBackupDir, backupFilenameString); + // at least dump something out in case this fails in a debug build + NS_ENSURE_SUCCESS(rv, rv); + + return rv; +} + + // nsNavBookmarks::nsINavHistoryObserver NS_IMETHODIMP diff --git a/toolkit/components/places/src/nsNavBookmarks.h b/toolkit/components/places/src/nsNavBookmarks.h index 05f0c90b78e..553ff6bec9c 100644 --- a/toolkit/components/places/src/nsNavBookmarks.h +++ b/toolkit/components/places/src/nsNavBookmarks.h @@ -108,6 +108,10 @@ public: static const PRInt32 kGetFolderInfoIndex_Title; static const PRInt32 kGetFolderInfoIndex_Type; + // Called by History service when quitting. + nsresult OnQuit(); + nsresult ArchiveBookmarksFile(PRInt32 aNumberOfBackups, PRBool aForceArchive); + private: static nsNavBookmarks *sInstance; diff --git a/toolkit/components/places/src/nsNavHistory.cpp b/toolkit/components/places/src/nsNavHistory.cpp index 0880cad44b5..9b2f9c3cf8b 100644 --- a/toolkit/components/places/src/nsNavHistory.cpp +++ b/toolkit/components/places/src/nsNavHistory.cpp @@ -2899,6 +2899,11 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic, // notify expiring system that we're quitting, it may want to do stuff mExpire.OnQuit(); + + // notify the bookmarks service we're quitting + nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); + NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); + (void)bookmarks->OnQuit(); } else if (nsCRT::strcmp(aTopic, gXpcomShutdown) == 0) { nsresult rv; nsCOMPtr observerService =