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

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

Issue 2878443002: Address a race condition in displaying notifications
Patch Set: Address a race condition in displaying notifications Created 3 years, 7 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/bind_helpers.h" 7 #include "base/bind_helpers.h"
8 #include "base/files/file_util.h" 8 #include "base/files/file_util.h"
9 #include "base/memory/ptr_util.h" 9 #include "base/memory/ptr_util.h"
10 #include "base/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
11 #include "base/stl_util.h" 11 #include "base/stl_util.h"
12 #include "base/threading/sequenced_worker_pool.h" 12 #include "base/threading/sequenced_worker_pool.h"
13 #include "base/time/default_clock.h"
13 #include "content/browser/notifications/blink_notification_service_impl.h" 14 #include "content/browser/notifications/blink_notification_service_impl.h"
14 #include "content/browser/notifications/notification_database.h" 15 #include "content/browser/notifications/notification_database.h"
15 #include "content/browser/service_worker/service_worker_context_wrapper.h" 16 #include "content/browser/service_worker/service_worker_context_wrapper.h"
16 #include "content/public/browser/browser_context.h" 17 #include "content/public/browser/browser_context.h"
17 #include "content/public/browser/browser_thread.h" 18 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/content_browser_client.h" 19 #include "content/public/browser/content_browser_client.h"
19 #include "content/public/browser/notification_database_data.h" 20 #include "content/public/browser/notification_database_data.h"
20 #include "content/public/browser/platform_notification_service.h" 21 #include "content/public/browser/platform_notification_service.h"
21 22
22 using base::DoNothing; 23 using base::DoNothing;
23 24
24 namespace content { 25 namespace content {
25 namespace { 26 namespace {
26 27
28 // Grace period, in seconds, during which we avoid deleting recently shown
29 // notifications from the database.
30 constexpr double kNotificationDisplayGraceSeconds = 5.0;
31
27 // Name of the directory in the user's profile directory where the notification 32 // Name of the directory in the user's profile directory where the notification
28 // database files should be stored. 33 // database files should be stored.
29 const base::FilePath::CharType kPlatformNotificationsDirectory[] = 34 const base::FilePath::CharType kPlatformNotificationsDirectory[] =
30 FILE_PATH_LITERAL("Platform Notifications"); 35 FILE_PATH_LITERAL("Platform Notifications");
31 36
32 } // namespace 37 } // namespace
33 38
34 PlatformNotificationContextImpl::PlatformNotificationContextImpl( 39 PlatformNotificationContextImpl::PlatformNotificationContextImpl(
35 const base::FilePath& path, 40 const base::FilePath& path,
36 BrowserContext* browser_context, 41 BrowserContext* browser_context,
37 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context) 42 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
38 : path_(path), 43 : path_(path),
39 browser_context_(browser_context), 44 browser_context_(browser_context),
40 service_worker_context_(service_worker_context), 45 service_worker_context_(service_worker_context),
41 notification_id_generator_(browser_context) { 46 notification_id_generator_(browser_context),
47 clock_(base::MakeUnique<base::DefaultClock>()) {
42 DCHECK_CURRENTLY_ON(BrowserThread::UI); 48 DCHECK_CURRENTLY_ON(BrowserThread::UI);
43 } 49 }
44 50
45 PlatformNotificationContextImpl::~PlatformNotificationContextImpl() { 51 PlatformNotificationContextImpl::~PlatformNotificationContextImpl() {
46 DCHECK_CURRENTLY_ON(BrowserThread::UI); 52 DCHECK_CURRENTLY_ON(BrowserThread::UI);
47 53
48 // If the database has been initialized, it must be deleted on the task runner 54 // If the database has been initialized, it must be deleted on the task runner
49 // thread as closing it may cause file I/O. 55 // thread as closing it may cause file I/O.
50 if (database_) { 56 if (database_) {
51 DCHECK(task_runner_); 57 DCHECK(task_runner_);
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 void PlatformNotificationContextImpl:: 271 void PlatformNotificationContextImpl::
266 DoReadAllNotificationDataForServiceWorkerRegistration( 272 DoReadAllNotificationDataForServiceWorkerRegistration(
267 const GURL& origin, 273 const GURL& origin,
268 int64_t service_worker_registration_id, 274 int64_t service_worker_registration_id,
269 const ReadAllResultCallback& callback, 275 const ReadAllResultCallback& callback,
270 std::unique_ptr<std::set<std::string>> displayed_notifications, 276 std::unique_ptr<std::set<std::string>> displayed_notifications,
271 bool supports_synchronization) { 277 bool supports_synchronization) {
272 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 278 DCHECK(task_runner_->RunsTasksOnCurrentThread());
273 DCHECK(displayed_notifications); 279 DCHECK(displayed_notifications);
274 280
281 base::Time current_time = clock_->Now();
282
283 // Prune the list of recently shown notifications to those that were displayed
284 // in the past |kNotificationDisplayGraceSeconds| seconds.
285 auto iter = recent_notifications_.begin();
286 while (iter != recent_notifications_.end()) {
287 base::TimeDelta display_time = current_time - iter->second;
288 if (display_time.InSecondsF() > kNotificationDisplayGraceSeconds)
289 iter = recent_notifications_.erase(iter);
290 else
291 iter++;
292 }
293
275 std::vector<NotificationDatabaseData> notification_datas; 294 std::vector<NotificationDatabaseData> notification_datas;
276 295
277 NotificationDatabase::Status status = 296 NotificationDatabase::Status status =
278 database_->ReadAllNotificationDataForServiceWorkerRegistration( 297 database_->ReadAllNotificationDataForServiceWorkerRegistration(
279 origin, service_worker_registration_id, &notification_datas); 298 origin, service_worker_registration_id, &notification_datas);
280 299
281 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.ReadForServiceWorkerResult", 300 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.ReadForServiceWorkerResult",
282 status, NotificationDatabase::STATUS_COUNT); 301 status, NotificationDatabase::STATUS_COUNT);
283 302
284 std::vector<std::string> obsolete_notifications; 303 std::vector<std::string> obsolete_notifications;
285 304
286 if (status == NotificationDatabase::STATUS_OK) { 305 if (status == NotificationDatabase::STATUS_OK) {
287 if (supports_synchronization) { 306 if (supports_synchronization) {
288 for (auto it = notification_datas.begin(); 307 for (auto it = notification_datas.begin();
289 it != notification_datas.end();) { 308 it != notification_datas.end();) {
290 // The database is only used for persistent notifications. 309 // The database is only used for persistent notifications.
291 DCHECK(NotificationIdGenerator::IsPersistentNotification( 310 DCHECK(NotificationIdGenerator::IsPersistentNotification(
292 it->notification_id)); 311 it->notification_id));
293 if (displayed_notifications->count(it->notification_id)) { 312
313 const bool notification_exists =
314 displayed_notifications->count(it->notification_id) ||
315 recent_notifications_.count(it->notification_id);
316
317 if (notification_exists) {
294 ++it; 318 ++it;
295 } else { 319 } else {
296 obsolete_notifications.push_back(it->notification_id); 320 obsolete_notifications.push_back(it->notification_id);
297 it = notification_datas.erase(it); 321 it = notification_datas.erase(it);
298 } 322 }
299 } 323 }
300 } 324 }
301 325
302 BrowserThread::PostTask( 326 BrowserThread::PostTask(
303 BrowserThread::IO, FROM_HERE, 327 BrowserThread::IO, FROM_HERE,
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 origin, database_data.notification_data.tag, 392 origin, database_data.notification_data.tag,
369 database_->GetNextPersistentNotificationId()); 393 database_->GetNextPersistentNotificationId());
370 394
371 NotificationDatabase::Status status = 395 NotificationDatabase::Status status =
372 database_->WriteNotificationData(origin, write_database_data); 396 database_->WriteNotificationData(origin, write_database_data);
373 397
374 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.WriteResult", status, 398 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.WriteResult", status,
375 NotificationDatabase::STATUS_COUNT); 399 NotificationDatabase::STATUS_COUNT);
376 400
377 if (status == NotificationDatabase::STATUS_OK) { 401 if (status == NotificationDatabase::STATUS_OK) {
402 recent_notifications_[write_database_data.notification_id] = clock_->Now();
403
378 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 404 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
379 base::Bind(callback, true /* success */, 405 base::Bind(callback, true /* success */,
380 write_database_data.notification_id)); 406 write_database_data.notification_id));
381 407
382 return; 408 return;
383 } 409 }
384 410
385 // Blow away the database if writing data failed due to corruption. 411 // Blow away the database if writing data failed due to corruption.
386 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) 412 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
387 DestroyDatabase(); 413 DestroyDatabase();
388 414
389 BrowserThread::PostTask( 415 BrowserThread::PostTask(
390 BrowserThread::IO, FROM_HERE, 416 BrowserThread::IO, FROM_HERE,
391 base::Bind(callback, false /* success */, "" /* notification_id */)); 417 base::Bind(callback, false /* success */, "" /* notification_id */));
392 } 418 }
393 419
394 void PlatformNotificationContextImpl::DeleteNotificationData( 420 void PlatformNotificationContextImpl::DeleteNotificationData(
awdf 2017/05/11 10:38:53 Just to check my understanding - this DeleteNotifi
395 const std::string& notification_id, 421 const std::string& notification_id,
396 const GURL& origin, 422 const GURL& origin,
397 const DeleteResultCallback& callback) { 423 const DeleteResultCallback& callback) {
398 DCHECK_CURRENTLY_ON(BrowserThread::IO); 424 DCHECK_CURRENTLY_ON(BrowserThread::IO);
399 425
400 LazyInitialize( 426 LazyInitialize(
401 base::Bind(&PlatformNotificationContextImpl::DoDeleteNotificationData, 427 base::Bind(&PlatformNotificationContextImpl::DoDeleteNotificationData,
402 this, notification_id, origin, callback), 428 this, notification_id, origin, callback),
403 base::Bind(callback, false /* success */)); 429 base::Bind(callback, false /* success */));
404 } 430 }
405 431
406 void PlatformNotificationContextImpl::DoDeleteNotificationData( 432 void PlatformNotificationContextImpl::DoDeleteNotificationData(
407 const std::string& notification_id, 433 const std::string& notification_id,
408 const GURL& origin, 434 const GURL& origin,
409 const DeleteResultCallback& callback) { 435 const DeleteResultCallback& callback) {
410 DCHECK(task_runner_->RunsTasksOnCurrentThread()); 436 DCHECK(task_runner_->RunsTasksOnCurrentThread());
411 437
412 NotificationDatabase::Status status = 438 NotificationDatabase::Status status =
413 database_->DeleteNotificationData(notification_id, origin); 439 database_->DeleteNotificationData(notification_id, origin);
414 440
415 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.DeleteResult", status, 441 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.DeleteResult", status,
416 NotificationDatabase::STATUS_COUNT); 442 NotificationDatabase::STATUS_COUNT);
417 443
444 recent_notifications_.erase(notification_id);
445
418 bool success = status == NotificationDatabase::STATUS_OK; 446 bool success = status == NotificationDatabase::STATUS_OK;
419 447
420 // Blow away the database if deleting data failed due to corruption. Following 448 // Blow away the database if deleting data failed due to corruption. Following
421 // the contract of the delete methods, consider this to be a success as the 449 // the contract of the delete methods, consider this to be a success as the
422 // caller's goal has been achieved: the data is gone. 450 // caller's goal has been achieved: the data is gone.
423 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) { 451 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) {
424 DestroyDatabase(); 452 DestroyDatabase();
425 success = true; 453 success = true;
426 } 454 }
427 455
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
568 596
569 return path_.Append(kPlatformNotificationsDirectory); 597 return path_.Append(kPlatformNotificationsDirectory);
570 } 598 }
571 599
572 void PlatformNotificationContextImpl::SetTaskRunnerForTesting( 600 void PlatformNotificationContextImpl::SetTaskRunnerForTesting(
573 const scoped_refptr<base::SequencedTaskRunner>& task_runner) { 601 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
574 task_runner_ = task_runner; 602 task_runner_ = task_runner;
575 } 603 }
576 604
577 } // namespace content 605 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698