Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / content / browser / notifications / platform_notification_context_impl.cc
blobdb6f1361a16d6ca41bf25abf7a2883ee0e552741
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::
122 ReadAllNotificationDataForServiceWorkerRegistration(
123 const GURL& origin,
124 int64_t service_worker_registration_id,
125 const ReadAllResultCallback& callback) {
126 DCHECK_CURRENTLY_ON(BrowserThread::IO);
127 LazyInitialize(
128 base::Bind(&PlatformNotificationContextImpl::
129 DoReadAllNotificationDataForServiceWorkerRegistration,
130 this, origin, service_worker_registration_id, callback),
131 base::Bind(callback,
132 false /* success */,
133 std::vector<NotificationDatabaseData>()));
136 void PlatformNotificationContextImpl::
137 DoReadAllNotificationDataForServiceWorkerRegistration(
138 const GURL& origin,
139 int64_t service_worker_registration_id,
140 const ReadAllResultCallback& callback) {
141 DCHECK(task_runner_->RunsTasksOnCurrentThread());
143 std::vector<NotificationDatabaseData> notification_datas;
145 NotificationDatabase::Status status =
146 database_->ReadAllNotificationDataForServiceWorkerRegistration(
147 origin, service_worker_registration_id, &notification_datas);
149 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.ReadForServiceWorkerResult",
150 status, NotificationDatabase::STATUS_COUNT);
152 if (status == NotificationDatabase::STATUS_OK) {
153 BrowserThread::PostTask(BrowserThread::IO,
154 FROM_HERE,
155 base::Bind(callback,
156 true /* success */,
157 notification_datas));
158 return;
161 // Blow away the database if reading data failed due to corruption.
162 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
163 DestroyDatabase();
165 BrowserThread::PostTask(
166 BrowserThread::IO,
167 FROM_HERE,
168 base::Bind(callback,
169 false /* success */,
170 std::vector<NotificationDatabaseData>()));
173 void PlatformNotificationContextImpl::WriteNotificationData(
174 const GURL& origin,
175 const NotificationDatabaseData& database_data,
176 const WriteResultCallback& callback) {
177 DCHECK_CURRENTLY_ON(BrowserThread::IO);
178 LazyInitialize(
179 base::Bind(&PlatformNotificationContextImpl::DoWriteNotificationData,
180 this, origin, database_data, callback),
181 base::Bind(callback, false /* success */, 0 /* notification_id */));
184 void PlatformNotificationContextImpl::DoWriteNotificationData(
185 const GURL& origin,
186 const NotificationDatabaseData& database_data,
187 const WriteResultCallback& callback) {
188 DCHECK(task_runner_->RunsTasksOnCurrentThread());
190 int64_t notification_id = 0;
191 NotificationDatabase::Status status =
192 database_->WriteNotificationData(origin,
193 database_data,
194 &notification_id);
196 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.WriteResult",
197 status, NotificationDatabase::STATUS_COUNT);
199 if (status == NotificationDatabase::STATUS_OK) {
200 DCHECK_GT(notification_id, 0);
201 BrowserThread::PostTask(BrowserThread::IO,
202 FROM_HERE,
203 base::Bind(callback,
204 true /* success */,
205 notification_id));
206 return;
209 // Blow away the database if writing data failed due to corruption.
210 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
211 DestroyDatabase();
213 BrowserThread::PostTask(
214 BrowserThread::IO,
215 FROM_HERE,
216 base::Bind(callback, false /* success */, 0 /* notification_id */));
219 void PlatformNotificationContextImpl::DeleteNotificationData(
220 int64_t notification_id,
221 const GURL& origin,
222 const DeleteResultCallback& callback) {
223 DCHECK_CURRENTLY_ON(BrowserThread::IO);
224 LazyInitialize(
225 base::Bind(&PlatformNotificationContextImpl::DoDeleteNotificationData,
226 this, notification_id, origin, callback),
227 base::Bind(callback, false /* success */));
230 void PlatformNotificationContextImpl::DoDeleteNotificationData(
231 int64_t notification_id,
232 const GURL& origin,
233 const DeleteResultCallback& callback) {
234 DCHECK(task_runner_->RunsTasksOnCurrentThread());
236 NotificationDatabase::Status status =
237 database_->DeleteNotificationData(notification_id, origin);
239 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.DeleteResult",
240 status, NotificationDatabase::STATUS_COUNT);
242 bool success = status == NotificationDatabase::STATUS_OK;
244 // Blow away the database if deleting data failed due to corruption. Following
245 // the contract of the delete methods, consider this to be a success as the
246 // caller's goal has been achieved: the data is gone.
247 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) {
248 DestroyDatabase();
249 success = true;
252 BrowserThread::PostTask(BrowserThread::IO,
253 FROM_HERE,
254 base::Bind(callback, success));
257 void PlatformNotificationContextImpl::OnRegistrationDeleted(
258 int64_t registration_id,
259 const GURL& pattern) {
260 DCHECK_CURRENTLY_ON(BrowserThread::IO);
261 LazyInitialize(
262 base::Bind(&PlatformNotificationContextImpl::
263 DoDeleteNotificationsForServiceWorkerRegistration,
264 this, pattern.GetOrigin(), registration_id),
265 base::Bind(&DoNothing));
268 void PlatformNotificationContextImpl::
269 DoDeleteNotificationsForServiceWorkerRegistration(
270 const GURL& origin,
271 int64_t service_worker_registration_id) {
272 DCHECK(task_runner_->RunsTasksOnCurrentThread());
274 std::set<int64_t> deleted_notifications_set;
275 NotificationDatabase::Status status =
276 database_->DeleteAllNotificationDataForServiceWorkerRegistration(
277 origin, service_worker_registration_id, &deleted_notifications_set);
279 UMA_HISTOGRAM_ENUMERATION(
280 "Notifications.Database.DeleteServiceWorkerRegistrationResult",
281 status, NotificationDatabase::STATUS_COUNT);
283 // Blow away the database if a corruption error occurred during the deletion.
284 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED)
285 DestroyDatabase();
287 // TODO(peter): Close the notifications in |deleted_notifications_set|.
290 void PlatformNotificationContextImpl::OnStorageWiped() {
291 DCHECK_CURRENTLY_ON(BrowserThread::IO);
292 LazyInitialize(
293 base::Bind(base::IgnoreResult(
294 &PlatformNotificationContextImpl::DestroyDatabase), this),
295 base::Bind(&DoNothing));
298 void PlatformNotificationContextImpl::LazyInitialize(
299 const base::Closure& success_closure,
300 const base::Closure& failure_closure) {
301 DCHECK_CURRENTLY_ON(BrowserThread::IO);
303 if (!task_runner_) {
304 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
305 base::SequencedWorkerPool::SequenceToken token = pool->GetSequenceToken();
307 task_runner_ = pool->GetSequencedTaskRunner(token);
310 task_runner_->PostTask(
311 FROM_HERE,
312 base::Bind(&PlatformNotificationContextImpl::OpenDatabase,
313 this, success_closure, failure_closure));
316 void PlatformNotificationContextImpl::OpenDatabase(
317 const base::Closure& success_closure,
318 const base::Closure& failure_closure) {
319 DCHECK(task_runner_->RunsTasksOnCurrentThread());
321 if (database_) {
322 success_closure.Run();
323 return;
326 database_.reset(new NotificationDatabase(GetDatabasePath()));
327 NotificationDatabase::Status status =
328 database_->Open(true /* create_if_missing */);
330 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.OpenResult",
331 status, NotificationDatabase::STATUS_COUNT);
333 // When the database could not be opened due to corruption, destroy it, blow
334 // away the contents of the directory and try re-opening the database.
335 if (status == NotificationDatabase::STATUS_ERROR_CORRUPTED) {
336 if (DestroyDatabase()) {
337 status = database_->Open(true /* create_if_missing */);
339 UMA_HISTOGRAM_ENUMERATION(
340 "Notifications.Database.OpenAfterCorruptionResult",
341 status, NotificationDatabase::STATUS_COUNT);
345 if (status == NotificationDatabase::STATUS_OK) {
346 success_closure.Run();
347 return;
350 database_.reset();
352 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, failure_closure);
355 bool PlatformNotificationContextImpl::DestroyDatabase() {
356 DCHECK(task_runner_->RunsTasksOnCurrentThread());
357 DCHECK(database_);
359 NotificationDatabase::Status status = database_->Destroy();
360 UMA_HISTOGRAM_ENUMERATION("Notifications.Database.DestroyResult",
361 status, NotificationDatabase::STATUS_COUNT);
363 database_.reset();
365 // TODO(peter): Close any existing persistent notifications on the platform.
367 // Remove all files in the directory that the database was previously located
368 // in, to make sure that any left-over files are gone as well.
369 base::FilePath database_path = GetDatabasePath();
370 if (!database_path.empty())
371 return base::DeleteFile(database_path, true);
373 return true;
376 base::FilePath PlatformNotificationContextImpl::GetDatabasePath() const {
377 if (path_.empty())
378 return path_;
380 return path_.Append(kPlatformNotificationsDirectory);
383 void PlatformNotificationContextImpl::SetTaskRunnerForTesting(
384 const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
385 task_runner_ = task_runner;
388 } // namespace content