Chromium Code Reviews| Index: content/browser/notifications/platform_notification_context_impl.cc |
| diff --git a/content/browser/notifications/platform_notification_context_impl.cc b/content/browser/notifications/platform_notification_context_impl.cc |
| index df744ea6b839c8d6d72cdb955dc6b9430df25aec..f891ca63c4633f21eda37f11ab247cfddfb66082 100644 |
| --- a/content/browser/notifications/platform_notification_context_impl.cc |
| +++ b/content/browser/notifications/platform_notification_context_impl.cc |
| @@ -4,6 +4,7 @@ |
| #include "content/browser/notifications/platform_notification_context_impl.h" |
| +#include "base/files/file_util.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "content/browser/notifications/notification_database.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.h" |
| @@ -18,6 +19,15 @@ void EmptyFailureCallback() {} |
| } // namespace |
| +// Defines the behavior to apply when a corrupt database is being opened. |
| +enum class PlatformNotificationContextImpl::CorruptionBehavior { |
| + // Destroy the entire database and start over with an empty one. |
| + DESTROY_AND_START_OVER, |
| + |
| + // Abort the operation and invoke the failure callback. |
| + FAIL_OPERATION, |
|
cmumford
2015/03/19 22:12:27
I can't really think of a time where you'd want to
Peter Beverloo
2015/03/20 14:31:26
Done.
Note that this CL used this to avoid gettin
|
| +}; |
| + |
| // Name of the directory in the user's profile directory where the notification |
| // database files should be stored. |
| const base::FilePath::CharType kPlatformNotificationsDirectory[] = |
| @@ -91,6 +101,8 @@ void PlatformNotificationContextImpl::DoReadNotificationData( |
| origin, |
| &database_data); |
| + // TODO(peter): Record UMA on |status| for reading from the database. |
| + |
| if (status == NotificationDatabase::STATUS_OK) { |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| @@ -100,8 +112,9 @@ void PlatformNotificationContextImpl::DoReadNotificationData( |
| return; |
| } |
| - // TODO(peter): Record UMA on |status| for reading from the database. |
| - // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED. |
| + // Blow away the database if reading data failed due to corruption. |
| + if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) |
| + DestroyDatabase(); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| @@ -132,9 +145,10 @@ void PlatformNotificationContextImpl::DoWriteNotificationData( |
| database_data, |
| ¬ification_id); |
| - DCHECK_GT(notification_id, 0); |
| + // TODO(peter): Record UMA on |status| for reading from the database. |
| if (status == NotificationDatabase::STATUS_OK) { |
| + DCHECK_GT(notification_id, 0); |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| base::Bind(callback, |
| @@ -143,8 +157,9 @@ void PlatformNotificationContextImpl::DoWriteNotificationData( |
| return; |
| } |
| - // TODO(peter): Record UMA on |status| for reading from the database. |
| - // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED. |
| + // Blow away the database if writing data failed due to corruption. |
| + if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) |
| + DestroyDatabase(); |
| BrowserThread::PostTask( |
| BrowserThread::IO, |
| @@ -172,10 +187,17 @@ void PlatformNotificationContextImpl::DoDeleteNotificationData( |
| NotificationDatabase::Status status = |
| database_->DeleteNotificationData(notification_id, origin); |
| - const bool success = status == NotificationDatabase::STATUS_OK; |
| - |
| // TODO(peter): Record UMA on |status| for reading from the database. |
| - // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED. |
| + |
| + bool success = status == NotificationDatabase::STATUS_OK; |
| + |
| + // Blow away the database if reading data failed due to corruption. Following |
| + // the contract of the delete methods, consider this to be a success as the |
| + // caller's goal has been achieved: the data is gone. |
| + if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) { |
| + DestroyDatabase(); |
| + success = true; |
| + } |
| BrowserThread::PostTask(BrowserThread::IO, |
| FROM_HERE, |
| @@ -200,13 +222,26 @@ void PlatformNotificationContextImpl:: |
| DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| std::set<int64_t> deleted_notifications_set; |
| - database_->DeleteAllNotificationDataForServiceWorkerRegistration( |
| - origin, service_worker_registration_id, &deleted_notifications_set); |
| + NotificationDatabase::Status status = |
| + database_->DeleteAllNotificationDataForServiceWorkerRegistration( |
| + origin, service_worker_registration_id, &deleted_notifications_set); |
| // TODO(peter): Record UMA on status for deleting from the database. |
| + |
| + // Blow away the database if a corruption error occurred during the deletion. |
| + if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) |
| + DestroyDatabase(); |
| + |
| // TODO(peter): Close the notifications in |deleted_notifications_set|. |
| } |
| +void PlatformNotificationContextImpl::OnStorageWiped() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + LazyInitialize( |
| + base::Bind(&PlatformNotificationContextImpl::DestroyDatabase, this), |
| + base::Bind(&EmptyFailureCallback)); |
| +} |
| + |
| void PlatformNotificationContextImpl::LazyInitialize( |
| const base::Closure& success_closure, |
| const base::Closure& failure_closure) { |
| @@ -222,12 +257,14 @@ void PlatformNotificationContextImpl::LazyInitialize( |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::Bind(&PlatformNotificationContextImpl::OpenDatabase, |
| - this, success_closure, failure_closure)); |
| + this, success_closure, failure_closure, |
| + CorruptionBehavior::DESTROY_AND_START_OVER)); |
| } |
| void PlatformNotificationContextImpl::OpenDatabase( |
| const base::Closure& success_closure, |
| - const base::Closure& failure_closure) { |
| + const base::Closure& failure_closure, |
| + CorruptionBehavior corruption_behavior) { |
| DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| if (database_) { |
| @@ -236,24 +273,50 @@ void PlatformNotificationContextImpl::OpenDatabase( |
| } |
| database_.reset(new NotificationDatabase(GetDatabasePath())); |
| - |
| - // TODO(peter): Record UMA on |status| for opening the database. |
| - // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED. |
| - |
| NotificationDatabase::Status status = |
| database_->Open(true /* create_if_missing */); |
| + // TODO(peter): Record UMA on |status| for opening the database. |
| + |
| if (status == NotificationDatabase::STATUS_OK) { |
| success_closure.Run(); |
| return; |
| } |
| - // TODO(peter): Properly handle failures when opening the database. |
| + // When the database could not be opened due to corruption and the corruption |
| + // behavior is set to DESTROY_AND_START_OVER, blow away the database and retry |
| + // opening it with the same success and failure callbacks. |
| + if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED && |
| + corruption_behavior == CorruptionBehavior::DESTROY_AND_START_OVER) { |
| + DestroyDatabase(); |
| + OpenDatabase(success_closure, |
|
cmumford
2015/03/19 22:12:27
If I'm reading this correctly the user of this fun
Peter Beverloo
2015/03/20 14:31:26
Indeed. The TODO on line 317 covers this - when th
|
| + failure_closure, |
| + CorruptionBehavior::FAIL_OPERATION); |
| + return; |
| + } |
| + |
| database_.reset(); |
|
cmumford
2015/03/19 22:12:27
Do you want to reset this ptr above before calling
Peter Beverloo
2015/03/20 14:31:26
Currently DestroyDatabase() calls NotificationData
|
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_closure); |
| } |
| +void PlatformNotificationContextImpl::DestroyDatabase() { |
|
cmumford
2015/03/19 22:12:27
You should return a status code here, and check it
Peter Beverloo
2015/03/20 14:31:26
That's an interesting case, I hadn't considered th
|
| + DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| + DCHECK(database_); |
| + |
| + // TODO(peter): Record UMA on the status code of the Destroy() call. |
| + database_->Destroy(); |
| + database_.reset(); |
| + |
| + // Remove all files in the directory that the database was previously located |
| + // in, to make sure that any left-over files are gone as well. |
| + base::FilePath database_path = GetDatabasePath(); |
| + if (!database_path.empty()) |
| + base::DeleteFile(database_path, true); |
| + |
| + // TODO(peter): Close any existing persistent notifications on the platform. |
| +} |
| + |
| base::FilePath PlatformNotificationContextImpl::GetDatabasePath() const { |
| if (path_.empty()) |
| return path_; |