Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / notifications / platform_notification_context_impl.cc
bloba4c04a50c6680547eb8203ca7561d84aa6cc9377
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
3 // found in the LICENSE file.
5 #include "content/browser/notifications/platform_notification_context_impl.h"
7 #include "base/bind_helpers.h"
8 #include "base/files/file_util.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "content/browser/notifications/notification_database.h"
12 #include "content/browser/service_worker/service_worker_context_wrapper.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/notification_database_data.h"
16 using base::DoNothing;
18 namespace content {
20 // Name of the directory in the user's profile directory where the notification
21 // database files should be stored.
22 const base::FilePath::CharType kPlatformNotificationsDirectory[] =
23 FILE_PATH_LITERAL("Platform Notifications");
25 PlatformNotificationContextImpl::PlatformNotificationContextImpl(
26 const base::FilePath& path,
27 const scoped_refptr<ServiceWorkerContextWrapper>& service_worker_context)
28 : path_(path),
29 service_worker_context_(service_worker_context) {
30 DCHECK_CURRENTLY_ON(BrowserThread::UI);
33 PlatformNotificationContextImpl::~PlatformNotificationContextImpl() {
34 DCHECK_CURRENTLY_ON(BrowserThread::UI);
36 // If the database has been initialized, it must be deleted on the task runner
37 // thread as closing it may cause file I/O.
38 if (database_) {
39 DCHECK(task_runner_);
40 task_runner_->DeleteSoon(FROM_HERE, database_.release());
44 void PlatformNotificationContextImpl::Initialize() {
45 DCHECK_CURRENTLY_ON(BrowserThread::UI);
46 BrowserThread::PostTask(
47 BrowserThread::IO,
48 FROM_HERE,
49 base::Bind(&PlatformNotificationContextImpl::InitializeOnIO, this));
52 void PlatformNotificationContextImpl::InitializeOnIO() {
53 DCHECK_CURRENTLY_ON(BrowserThread::IO);
55 // |service_worker_context_| may be NULL in tests.
56 if (service_worker_context_)
57 service_worker_context_->AddObserver(this);
60 void PlatformNotificationContextImpl::Shutdown() {
61 DCHECK_CURRENTLY_ON(BrowserThread::UI);
62 BrowserThread::PostTask(
63 BrowserThread::IO,
64 FROM_HERE,
65 base::Bind(&PlatformNotificationContextImpl::ShutdownOnIO, this));
68 void PlatformNotificationContextImpl::ShutdownOnIO() {
69 DCHECK_CURRENTLY_ON(BrowserThread::IO);
71 // |service_worker_context_| may be NULL in tests.
72 if (service_worker_context_)
73 service_worker_context_->RemoveObserver(this);
76 void PlatformNotificationContextImpl::ReadNotificationData(
77 int64_t notification_id,
78 const GURL& origin,
79 const ReadResultCallback& callback) {
80 DCHECK_CURRENTLY_ON(BrowserThread::IO);
81 LazyInitialize(
82 base::Bind(&PlatformNotificationContextImpl::DoReadNotificationData,
83 this, notification_id, origin, callback),
84 base::Bind(callback, false /* success */, NotificationDatabaseData()));
87 void PlatformNotificationContextImpl::DoReadNotificationData(
88 int64_t notification_id,
89 const GURL& origin,
90 const ReadResultCallback& callback) {
91 DCHECK(task_runner_->RunsTasksOnCurrentThread());
93 NotificationDatabaseData database_data;
94 NotificationDatabase::Status status =
95 database_->ReadNotificationData(notification_id,
96 origin,
97 &database_data);
99 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.ReadResult",
100 status, NotificationDatabase::STATUS_COUNT);
102 if (status == NotificationDatabase::STATUS_OK) {
103 BrowserThread::PostTask(BrowserThread::IO,
104 FROM_HERE,
105 base::Bind(callback,
106 true /* success */,
107 database_data));
108 return;
111 // Blow away the database if reading data failed due to corruption.
112 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
113 DestroyDatabase();
115 BrowserThread::PostTask(
116 BrowserThread::IO,
117 FROM_HERE,
118 base::Bind(callback, false /* success */, NotificationDatabaseData()));
121 void PlatformNotificationContextImpl::WriteNotificationData(
122 const GURL& origin,
123 const NotificationDatabaseData& database_data,
124 const WriteResultCallback& callback) {
125 DCHECK_CURRENTLY_ON(BrowserThread::IO);
126 LazyInitialize(
127 base::Bind(&PlatformNotificationContextImpl::DoWriteNotificationData,
128 this, origin, database_data, callback),
129 base::Bind(callback, false /* success */, 0 /* notification_id */));
132 void PlatformNotificationContextImpl::DoWriteNotificationData(
133 const GURL& origin,
134 const NotificationDatabaseData& database_data,
135 const WriteResultCallback& callback) {
136 DCHECK(task_runner_->RunsTasksOnCurrentThread());
138 int64_t notification_id = 0;
139 NotificationDatabase::Status status =
140 database_->WriteNotificationData(origin,
141 database_data,
142 &notification_id);
144 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.WriteResult",
145 status, NotificationDatabase::STATUS_COUNT);
147 if (status == NotificationDatabase::STATUS_OK) {
148 DCHECK_GT(notification_id, 0);
149 BrowserThread::PostTask(BrowserThread::IO,
150 FROM_HERE,
151 base::Bind(callback,
152 true /* success */,
153 notification_id));
154 return;
157 // Blow away the database if writing data failed due to corruption.
158 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
159 DestroyDatabase();
161 BrowserThread::PostTask(
162 BrowserThread::IO,
163 FROM_HERE,
164 base::Bind(callback, false /* success */, 0 /* notification_id */));
167 void PlatformNotificationContextImpl::DeleteNotificationData(
168 int64_t notification_id,
169 const GURL& origin,
170 const DeleteResultCallback& callback) {
171 DCHECK_CURRENTLY_ON(BrowserThread::IO);
172 LazyInitialize(
173 base::Bind(&PlatformNotificationContextImpl::DoDeleteNotificationData,
174 this, notification_id, origin, callback),
175 base::Bind(callback, false /* success */));
178 void PlatformNotificationContextImpl::DoDeleteNotificationData(
179 int64_t notification_id,
180 const GURL& origin,
181 const DeleteResultCallback& callback) {
182 DCHECK(task_runner_->RunsTasksOnCurrentThread());
184 NotificationDatabase::Status status =
185 database_->DeleteNotificationData(notification_id, origin);
187 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.DeleteResult",
188 status, NotificationDatabase::STATUS_COUNT);
190 bool success = status == NotificationDatabase::STATUS_OK;
192 // Blow away the database if deleting data failed due to corruption. Following
193 // the contract of the delete methods, consider this to be a success as the
194 // caller's goal has been achieved: the data is gone.
195 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) {
196 DestroyDatabase();
197 success = true;
200 BrowserThread::PostTask(BrowserThread::IO,
201 FROM_HERE,
202 base::Bind(callback, success));
205 void PlatformNotificationContextImpl::OnRegistrationDeleted(
206 int64_t registration_id,
207 const GURL& pattern) {
208 DCHECK_CURRENTLY_ON(BrowserThread::IO);
209 LazyInitialize(
210 base::Bind(&PlatformNotificationContextImpl::
211 DoDeleteNotificationsForServiceWorkerRegistration,
212 this, pattern.GetOrigin(), registration_id),
213 base::Bind(&DoNothing));
216 void PlatformNotificationContextImpl::
217 DoDeleteNotificationsForServiceWorkerRegistration(
218 const GURL& origin,
219 int64_t service_worker_registration_id) {
220 DCHECK(task_runner_->RunsTasksOnCurrentThread());
222 std::set<int64_t> deleted_notifications_set;
223 NotificationDatabase::Status status =
224 database_->DeleteAllNotificationDataForServiceWorkerRegistration(
225 origin, service_worker_registration_id, &deleted_notifications_set);
227 UMA_HISTOGRAM_ENUMERATION(
228 "Notifications.Database.DeleteServiceWorkerRegistrationResult",
229 status, NotificationDatabase::STATUS_COUNT);
231 // Blow away the database if a corruption error occurred during the deletion.
232 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
233 DestroyDatabase();
235 // TODO(peter): Close the notifications in |deleted_notifications_set|.
238 void PlatformNotificationContextImpl::OnStorageWiped() {
239 DCHECK_CURRENTLY_ON(BrowserThread::IO);
240 LazyInitialize(
241 base::Bind(base::IgnoreResult(
242 &PlatformNotificationContextImpl::DestroyDatabase), this),
243 base::Bind(&DoNothing));
246 void PlatformNotificationContextImpl::LazyInitialize(
247 const base::Closure& success_closure,
248 const base::Closure& failure_closure) {
249 DCHECK_CURRENTLY_ON(BrowserThread::IO);
251 if (!task_runner_) {
252 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
253 base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken();
255 task_runner_ = pool->GetSequencedTaskRunner(token);
258 task_runner_->PostTask(
259 FROM_HERE,
260 base::Bind(&PlatformNotificationContextImpl::OpenDatabase,
261 this, success_closure, failure_closure));
264 void PlatformNotificationContextImpl::OpenDatabase(
265 const base::Closure& success_closure,
266 const base::Closure& failure_closure) {
267 DCHECK(task_runner_->RunsTasksOnCurrentThread());
269 if (database_) {
270 success_closure.Run();
271 return;
274 database_.reset(new NotificationDatabase(GetDatabasePath()));
275 NotificationDatabase::Status status =
276 database_->Open(true /* create_if_missing */);
278 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.OpenResult",
279 status, NotificationDatabase::STATUS_COUNT);
281 // When the database could not be opened due to corruption, destroy it, blow
282 // away the contents of the directory and try re-opening the database.
283 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) {
284 if (DestroyDatabase()) {
285 status = database_->Open(true /* create_if_missing */);
287 // TODO(peter): Record UMA on |status| for re-opening the database after
288 // corruption was detected.
292 if (status == NotificationDatabase::STATUS_OK) {
293 success_closure.Run();
294 return;
297 database_.reset();
299 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_closure);
302 bool PlatformNotificationContextImpl::DestroyDatabase() {
303 DCHECK(task_runner_->RunsTasksOnCurrentThread());
304 DCHECK(database_);
306 NotificationDatabase::Status status = database_->Destroy();
307 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.DestroyResult",
308 status, NotificationDatabase::STATUS_COUNT);
310 database_.reset();
312 // TODO(peter): Close any existing persistent notifications on the platform.
314 // Remove all files in the directory that the database was previously located
315 // in, to make sure that any left-over files are gone as well.
316 base::FilePath database_path = GetDatabasePath();
317 if (!database_path.empty())
318 return base::DeleteFile(database_path, true);
320 return true;
323 base::FilePath PlatformNotificationContextImpl::GetDatabasePath() const {
324 if (path_.empty())
325 return path_;
327 return path_.Append(kPlatformNotificationsDirectory);
330 void PlatformNotificationContextImpl::SetTaskRunnerForTesting(
331 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
332 task_runner_ = task_runner;
335 } // namespace content