Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(328)

Side by Side Diff: content/browser/notifications/platform_notification_context_impl.cc

Issue 1024463006: Destroy the notification database when corruption occurs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@n-db-SWContextObserver
Patch Set: another test Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/notifications/platform_notification_context_impl.h" 5 #include "content/browser/notifications/platform_notification_context_impl.h"
6 6
7 #include "base/files/file_util.h"
7 #include "base/threading/sequenced_worker_pool.h" 8 #include "base/threading/sequenced_worker_pool.h"
8 #include "content/browser/notifications/notification_database.h" 9 #include "content/browser/notifications/notification_database.h"
9 #include "content/browser/service_worker/service_worker_context_wrapper.h" 10 #include "content/browser/service_worker/service_worker_context_wrapper.h"
10 #include "content/public/browser/browser_thread.h" 11 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/notification_database_data.h" 12 #include "content/public/browser/notification_database_data.h"
12 13
13 namespace content { 14 namespace content {
14 namespace { 15 namespace {
15 16
16 // Used as a failure callback there is no further action to be made. 17 // Used as a failure callback there is no further action to be made.
17 void EmptyFailureCallback() {} 18 void EmptyFailureCallback() {}
18 19
19 } // namespace 20 } // namespace
20 21
22 // Defines the behavior to apply when a corrupt database is being opened.
23 enum class PlatformNotificationContextImpl::CorruptionBehavior {
24 // Destroy the entire database and start over with an empty one.
25 DESTROY_AND_START_OVER,
26
27 // Abort the operation and invoke the failure callback.
28 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
29 };
30
21 // Name of the directory in the user's profile directory where the notification 31 // Name of the directory in the user's profile directory where the notification
22 // database files should be stored. 32 // database files should be stored.
23 const base::FilePath::CharType kPlatformNotificationsDirectory[] = 33 const base::FilePath::CharType kPlatformNotificationsDirectory[] =
24 FILE_PATH_LITERAL("Platform Notifications"); 34 FILE_PATH_LITERAL("Platform Notifications");
25 35
26 PlatformNotificationContextImpl::PlatformNotificationContextImpl( 36 PlatformNotificationContextImpl::PlatformNotificationContextImpl(
27 const base::FilePath& path, 37 const base::FilePath& path,
28 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) 38 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
29 : path_(path), 39 : path_(path),
30 service_worker_context_(service_worker_context) { 40 service_worker_context_(service_worker_context) {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 const GURL& origin, 94 const GURL& origin,
85 const ReadResultCallback& callback) { 95 const ReadResultCallback& callback) {
86 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 96 DCHECK(task_runner_->RunsTasksOnCurrentThread());
87 97
88 NotificationDatabaseData database_data; 98 NotificationDatabaseData database_data;
89 NotificationDatabase::Status status = 99 NotificationDatabase::Status status =
90 database_->ReadNotificationData(notification_id, 100 database_->ReadNotificationData(notification_id,
91 origin, 101 origin,
92 &database_data); 102 &database_data);
93 103
104 // TODO(peter): Record UMA on |status| for reading from the database.
105
94 if (status == NotificationDatabase::STATUS_OK) { 106 if (status == NotificationDatabase::STATUS_OK) {
95 BrowserThread::PostTask(BrowserThread::IO, 107 BrowserThread::PostTask(BrowserThread::IO,
96 FROM_HERE, 108 FROM_HERE,
97 base::Bind(callback, 109 base::Bind(callback,
98 true /* success */, 110 true /* success */,
99 database_data)); 111 database_data));
100 return; 112 return;
101 } 113 }
102 114
103 // TODO(peter): Record UMA on |status| for reading from the database. 115 // Blow away the database if reading data failed due to corruption.
104 // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED. 116 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
117 DestroyDatabase();
105 118
106 BrowserThread::PostTask( 119 BrowserThread::PostTask(
107 BrowserThread::IO, 120 BrowserThread::IO,
108 FROM_HERE, 121 FROM_HERE,
109 base::Bind(callback, false /* success */, NotificationDatabaseData())); 122 base::Bind(callback, false /* success */, NotificationDatabaseData()));
110 } 123 }
111 124
112 void PlatformNotificationContextImpl::WriteNotificationData( 125 void PlatformNotificationContextImpl::WriteNotificationData(
113 const GURL& origin, 126 const GURL& origin,
114 const NotificationDatabaseData& database_data, 127 const NotificationDatabaseData& database_data,
(...skipping 10 matching lines...) Expand all
125 const NotificationDatabaseData& database_data, 138 const NotificationDatabaseData& database_data,
126 const WriteResultCallback& callback) { 139 const WriteResultCallback& callback) {
127 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 140 DCHECK(task_runner_->RunsTasksOnCurrentThread());
128 141
129 int64_t notification_id = 0; 142 int64_t notification_id = 0;
130 NotificationDatabase::Status status = 143 NotificationDatabase::Status status =
131 database_->WriteNotificationData(origin, 144 database_->WriteNotificationData(origin,
132 database_data, 145 database_data,
133 &notification_id); 146 &notification_id);
134 147
135 DCHECK_GT(notification_id, 0); 148 // TODO(peter): Record UMA on |status| for reading from the database.
136 149
137 if (status == NotificationDatabase::STATUS_OK) { 150 if (status == NotificationDatabase::STATUS_OK) {
151 DCHECK_GT(notification_id, 0);
138 BrowserThread::PostTask(BrowserThread::IO, 152 BrowserThread::PostTask(BrowserThread::IO,
139 FROM_HERE, 153 FROM_HERE,
140 base::Bind(callback, 154 base::Bind(callback,
141 true /* success */, 155 true /* success */,
142 notification_id)); 156 notification_id));
143 return; 157 return;
144 } 158 }
145 159
146 // TODO(peter): Record UMA on |status| for reading from the database. 160 // Blow away the database if writing data failed due to corruption.
147 // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED. 161 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
162 DestroyDatabase();
148 163
149 BrowserThread::PostTask( 164 BrowserThread::PostTask(
150 BrowserThread::IO, 165 BrowserThread::IO,
151 FROM_HERE, 166 FROM_HERE,
152 base::Bind(callback, false /* success */, 0 /* notification_id */)); 167 base::Bind(callback, false /* success */, 0 /* notification_id */));
153 } 168 }
154 169
155 void PlatformNotificationContextImpl::DeleteNotificationData( 170 void PlatformNotificationContextImpl::DeleteNotificationData(
156 int64_t notification_id, 171 int64_t notification_id,
157 const GURL& origin, 172 const GURL& origin,
158 const DeleteResultCallback& callback) { 173 const DeleteResultCallback& callback) {
159 DCHECK_CURRENTLY_ON(BrowserThread::IO); 174 DCHECK_CURRENTLY_ON(BrowserThread::IO);
160 LazyInitialize( 175 LazyInitialize(
161 base::Bind(&PlatformNotificationContextImpl::DoDeleteNotificationData, 176 base::Bind(&PlatformNotificationContextImpl::DoDeleteNotificationData,
162 this, notification_id, origin, callback), 177 this, notification_id, origin, callback),
163 base::Bind(callback, false /* success */)); 178 base::Bind(callback, false /* success */));
164 } 179 }
165 180
166 void PlatformNotificationContextImpl::DoDeleteNotificationData( 181 void PlatformNotificationContextImpl::DoDeleteNotificationData(
167 int64_t notification_id, 182 int64_t notification_id,
168 const GURL& origin, 183 const GURL& origin,
169 const DeleteResultCallback& callback) { 184 const DeleteResultCallback& callback) {
170 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 185 DCHECK(task_runner_->RunsTasksOnCurrentThread());
171 186
172 NotificationDatabase::Status status = 187 NotificationDatabase::Status status =
173 database_->DeleteNotificationData(notification_id, origin); 188 database_->DeleteNotificationData(notification_id, origin);
174 189
175 const bool success = status == NotificationDatabase::STATUS_OK; 190 // TODO(peter): Record UMA on |status| for reading from the database.
176 191
177 // TODO(peter): Record UMA on |status| for reading from the database. 192 bool success = status == NotificationDatabase::STATUS_OK;
178 // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED. 193
194 // Blow away the database if reading data failed due to corruption. Following
195 // the contract of the delete methods, consider this to be a success as the
196 // caller's goal has been achieved: the data is gone.
197 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) {
198 DestroyDatabase();
199 success = true;
200 }
179 201
180 BrowserThread::PostTask(BrowserThread::IO, 202 BrowserThread::PostTask(BrowserThread::IO,
181 FROM_HERE, 203 FROM_HERE,
182 base::Bind(callback, success)); 204 base::Bind(callback, success));
183 } 205 }
184 206
185 void PlatformNotificationContextImpl::OnRegistrationDeleted( 207 void PlatformNotificationContextImpl::OnRegistrationDeleted(
186 int64_t registration_id, 208 int64_t registration_id,
187 const GURL& pattern) { 209 const GURL& pattern) {
188 DCHECK_CURRENTLY_ON(BrowserThread::IO); 210 DCHECK_CURRENTLY_ON(BrowserThread::IO);
189 LazyInitialize( 211 LazyInitialize(
190 base::Bind(&PlatformNotificationContextImpl:: 212 base::Bind(&PlatformNotificationContextImpl::
191 DoDeleteNotificationsForServiceWorkerRegistration, 213 DoDeleteNotificationsForServiceWorkerRegistration,
192 this, pattern.GetOrigin(), registration_id), 214 this, pattern.GetOrigin(), registration_id),
193 base::Bind(&EmptyFailureCallback)); 215 base::Bind(&EmptyFailureCallback));
194 } 216 }
195 217
196 void PlatformNotificationContextImpl:: 218 void PlatformNotificationContextImpl::
197 DoDeleteNotificationsForServiceWorkerRegistration( 219 DoDeleteNotificationsForServiceWorkerRegistration(
198 const GURL& origin, 220 const GURL& origin,
199 int64_t service_worker_registration_id) { 221 int64_t service_worker_registration_id) {
200 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 222 DCHECK(task_runner_->RunsTasksOnCurrentThread());
201 223
202 std::set<int64_t> deleted_notifications_set; 224 std::set<int64_t> deleted_notifications_set;
203 database_->DeleteAllNotificationDataForServiceWorkerRegistration( 225 NotificationDatabase::Status status =
204 origin, service_worker_registration_id, &deleted_notifications_set); 226 database_->DeleteAllNotificationDataForServiceWorkerRegistration(
227 origin, service_worker_registration_id, &deleted_notifications_set);
205 228
206 // TODO(peter): Record UMA on status for deleting from the database. 229 // TODO(peter): Record UMA on status for deleting from the database.
230
231 // Blow away the database if a corruption error occurred during the deletion.
232 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
233 DestroyDatabase();
234
207 // TODO(peter): Close the notifications in |deleted_notifications_set|. 235 // TODO(peter): Close the notifications in |deleted_notifications_set|.
208 } 236 }
209 237
238 void PlatformNotificationContextImpl::OnStorageWiped() {
239 DCHECK_CURRENTLY_ON(BrowserThread::IO);
240 LazyInitialize(
241 base::Bind(&PlatformNotificationContextImpl::DestroyDatabase, this),
242 base::Bind(&EmptyFailureCallback));
243 }
244
210 void PlatformNotificationContextImpl::LazyInitialize( 245 void PlatformNotificationContextImpl::LazyInitialize(
211 const base::Closure& success_closure, 246 const base::Closure& success_closure,
212 const base::Closure& failure_closure) { 247 const base::Closure& failure_closure) {
213 DCHECK_CURRENTLY_ON(BrowserThread::IO); 248 DCHECK_CURRENTLY_ON(BrowserThread::IO);
214 249
215 if (!task_runner_) { 250 if (!task_runner_) {
216 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); 251 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
217 base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken(); 252 base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken();
218 253
219 task_runner_ = pool->GetSequencedTaskRunner(token); 254 task_runner_ = pool->GetSequencedTaskRunner(token);
220 } 255 }
221 256
222 task_runner_->PostTask( 257 task_runner_->PostTask(
223 FROM_HERE, 258 FROM_HERE,
224 base::Bind(&PlatformNotificationContextImpl::OpenDatabase, 259 base::Bind(&PlatformNotificationContextImpl::OpenDatabase,
225 this, success_closure, failure_closure)); 260 this, success_closure, failure_closure,
261 CorruptionBehavior::DESTROY_AND_START_OVER));
226 } 262 }
227 263
228 void PlatformNotificationContextImpl::OpenDatabase( 264 void PlatformNotificationContextImpl::OpenDatabase(
229 const base::Closure& success_closure, 265 const base::Closure& success_closure,
230 const base::Closure& failure_closure) { 266 const base::Closure& failure_closure,
267 CorruptionBehavior corruption_behavior) {
231 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 268 DCHECK(task_runner_->RunsTasksOnCurrentThread());
232 269
233 if (database_) { 270 if (database_) {
234 success_closure.Run(); 271 success_closure.Run();
235 return; 272 return;
236 } 273 }
237 274
238 database_.reset(new NotificationDatabase(GetDatabasePath())); 275 database_.reset(new NotificationDatabase(GetDatabasePath()));
276 NotificationDatabase::Status status =
277 database_->Open(true /* create_if_missing */);
239 278
240 // TODO(peter): Record UMA on |status| for opening the database. 279 // TODO(peter): Record UMA on |status| for opening the database.
241 // TODO(peter): Do the DeleteAndStartOver dance for STATUS_ERROR_CORRUPTED.
242
243 NotificationDatabase::Status status =
244 database_->Open(true /* create_if_missing */);
245 280
246 if (status == NotificationDatabase::STATUS_OK) { 281 if (status == NotificationDatabase::STATUS_OK) {
247 success_closure.Run(); 282 success_closure.Run();
248 return; 283 return;
249 } 284 }
250 285
251 // TODO(peter): Properly handle failures when opening the database. 286 // When the database could not be opened due to corruption and the corruption
287 // behavior is set to DESTROY_AND_START_OVER, blow away the database and retry
288 // opening it with the same success and failure callbacks.
289 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED &&
290 corruption_behavior == CorruptionBehavior::DESTROY_AND_START_OVER) {
291 DestroyDatabase();
292 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
293 failure_closure,
294 CorruptionBehavior::FAIL_OPERATION);
295 return;
296 }
297
252 database_.reset(); 298 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
253 299
254 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_closure); 300 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_closure);
255 } 301 }
256 302
303 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
304 DCHECK(task_runner_->RunsTasksOnCurrentThread());
305 DCHECK(database_);
306
307 // TODO(peter): Record UMA on the status code of the Destroy() call.
308 database_->Destroy();
309 database_.reset();
310
311 // Remove all files in the directory that the database was previously located
312 // in, to make sure that any left-over files are gone as well.
313 base::FilePath database_path = GetDatabasePath();
314 if (!database_path.empty())
315 base::DeleteFile(database_path, true);
316
317 // TODO(peter): Close any existing persistent notifications on the platform.
318 }
319
257 base::FilePath PlatformNotificationContextImpl::GetDatabasePath() const { 320 base::FilePath PlatformNotificationContextImpl::GetDatabasePath() const {
258 if (path_.empty()) 321 if (path_.empty())
259 return path_; 322 return path_;
260 323
261 return path_.Append(kPlatformNotificationsDirectory); 324 return path_.Append(kPlatformNotificationsDirectory);
262 } 325 }
263 326
264 void PlatformNotificationContextImpl::SetTaskRunnerForTesting( 327 void PlatformNotificationContextImpl::SetTaskRunnerForTesting(
265 const scoped_refptr<base::SequencedTaskRunner>& task_runner) { 328 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
266 task_runner_ = task_runner; 329 task_runner_ = task_runner;
267 } 330 }
268 331
269 } // namespace content 332 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698