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/notification_database.h"
9 #include "base/files/file_util.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "content/browser/notifications/notification_database_data_conversions.h"
13 #include "content/common/service_worker/service_worker_types.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/notification_database_data.h"
16 #include "storage/common/database/database_identifier.h"
17 #include "third_party/leveldatabase/env_chromium.h"
18 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
19 #include "third_party/leveldatabase/src/include/leveldb/db.h"
20 #include "third_party/leveldatabase/src/include/leveldb/env.h"
21 #include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
22 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
25 // Notification LevelDB database schema (in alphabetized order)
26 // =======================
28 // key: "DATA:" <origin identifier> '\x00' <notification_id>
29 // value: String containing the NotificationDatabaseDataProto protocol buffer
30 // in serialized form.
32 // key: "NEXT_NOTIFICATION_ID"
33 // value: Decimal string which fits into an int64_t.
38 // Keys of the fields defined in the database.
39 const char kNextNotificationIdKey
[] = "NEXT_NOTIFICATION_ID";
40 const char kDataKeyPrefix
[] = "DATA:";
42 // Separates the components of compound keys.
43 const char kKeySeparator
= '\x00';
45 // The first notification id which to be handed out by the database.
46 const int64_t kFirstNotificationId
= 1;
48 // Converts the LevelDB |status| to one of the notification database's values.
49 NotificationDatabase::Status
LevelDBStatusToStatus(
50 const leveldb::Status
& status
) {
52 return NotificationDatabase::STATUS_OK
;
53 else if (status
.IsNotFound())
54 return NotificationDatabase::STATUS_ERROR_NOT_FOUND
;
55 else if (status
.IsCorruption())
56 return NotificationDatabase::STATUS_ERROR_CORRUPTED
;
58 return NotificationDatabase::STATUS_ERROR_FAILED
;
61 // Creates a prefix for the data entries based on |origin|.
62 std::string
CreateDataPrefix(const GURL
& origin
) {
63 if (!origin
.is_valid())
64 return kDataKeyPrefix
;
66 return base::StringPrintf("%s%s%c",
68 storage::GetIdentifierFromOrigin(origin
).c_str(),
72 // Creates the compound data key in which notification data is stored.
73 std::string
CreateDataKey(const GURL
& origin
, int64_t notification_id
) {
74 DCHECK(origin
.is_valid());
75 return CreateDataPrefix(origin
) + base::Int64ToString(notification_id
);
78 // Deserializes data in |serialized_data| to |notification_database_data|.
79 // Will return if the deserialization was successful.
80 NotificationDatabase::Status
DeserializedNotificationData(
81 const std::string
& serialized_data
,
82 NotificationDatabaseData
* notification_database_data
) {
83 DCHECK(notification_database_data
);
84 if (DeserializeNotificationDatabaseData(serialized_data
,
85 notification_database_data
)) {
86 return NotificationDatabase::STATUS_OK
;
89 DLOG(ERROR
) << "Unable to deserialize a notification's data.";
90 return NotificationDatabase::STATUS_ERROR_CORRUPTED
;
95 NotificationDatabase::NotificationDatabase(const base::FilePath
& path
)
99 NotificationDatabase::~NotificationDatabase() {
100 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
103 NotificationDatabase::Status
NotificationDatabase::Open(
104 bool create_if_missing
) {
105 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
106 DCHECK_EQ(STATE_UNINITIALIZED
, state_
);
108 if (!create_if_missing
) {
109 if (IsInMemoryDatabase() ||
110 !base::PathExists(path_
) ||
111 base::IsDirectoryEmpty(path_
)) {
112 return NotificationDatabase::STATUS_ERROR_NOT_FOUND
;
116 filter_policy_
.reset(leveldb::NewBloomFilterPolicy(10));
118 leveldb::Options options
;
119 options
.create_if_missing
= create_if_missing
;
120 options
.paranoid_checks
= true;
121 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
122 if (IsInMemoryDatabase()) {
123 env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
124 options
.env
= env_
.get();
127 leveldb::DB
* db
= nullptr;
128 Status status
= LevelDBStatusToStatus(
129 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
));
130 if (status
!= STATUS_OK
)
133 state_
= STATE_INITIALIZED
;
136 return ReadNextNotificationId();
139 NotificationDatabase::Status
NotificationDatabase::ReadNotificationData(
140 int64_t notification_id
,
142 NotificationDatabaseData
* notification_database_data
) const {
143 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
144 DCHECK_EQ(STATE_INITIALIZED
, state_
);
145 DCHECK_GE(notification_id
, kFirstNotificationId
);
146 DCHECK(origin
.is_valid());
147 DCHECK(notification_database_data
);
149 std::string key
= CreateDataKey(origin
, notification_id
);
150 std::string serialized_data
;
152 Status status
= LevelDBStatusToStatus(
153 db_
->Get(leveldb::ReadOptions(), key
, &serialized_data
));
154 if (status
!= STATUS_OK
)
157 return DeserializedNotificationData(serialized_data
,
158 notification_database_data
);
161 NotificationDatabase::Status
162 NotificationDatabase::ReadAllNotificationData(
163 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
164 return ReadAllNotificationDataInternal(GURL() /* origin */,
165 kInvalidServiceWorkerRegistrationId
,
166 notification_data_vector
);
169 NotificationDatabase::Status
170 NotificationDatabase::ReadAllNotificationDataForOrigin(
172 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
173 return ReadAllNotificationDataInternal(origin
,
174 kInvalidServiceWorkerRegistrationId
,
175 notification_data_vector
);
178 NotificationDatabase::Status
179 NotificationDatabase::ReadAllNotificationDataForServiceWorkerRegistration(
181 int64_t service_worker_registration_id
,
182 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
183 return ReadAllNotificationDataInternal(origin
,
184 service_worker_registration_id
,
185 notification_data_vector
);
188 NotificationDatabase::Status
NotificationDatabase::WriteNotificationData(
190 const NotificationDatabaseData
& notification_database_data
,
191 int64_t* notification_id
) {
192 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
193 DCHECK_EQ(STATE_INITIALIZED
, state_
);
194 DCHECK(notification_id
);
195 DCHECK(origin
.is_valid());
197 DCHECK_GE(next_notification_id_
, kFirstNotificationId
);
199 NotificationDatabaseData storage_data
= notification_database_data
;
200 storage_data
.notification_id
= next_notification_id_
;
202 std::string serialized_data
;
203 if (!SerializeNotificationDatabaseData(storage_data
,
205 DLOG(ERROR
) << "Unable to serialize data for a notification belonging "
207 return STATUS_ERROR_FAILED
;
210 leveldb::WriteBatch batch
;
211 batch
.Put(CreateDataKey(origin
, next_notification_id_
), serialized_data
);
212 batch
.Put(kNextNotificationIdKey
,
213 base::Int64ToString(next_notification_id_
+ 1));
215 Status status
= LevelDBStatusToStatus(
216 db_
->Write(leveldb::WriteOptions(), &batch
));
217 if (status
!= STATUS_OK
)
220 *notification_id
= next_notification_id_
++;
224 NotificationDatabase::Status
NotificationDatabase::DeleteNotificationData(
225 int64_t notification_id
,
226 const GURL
& origin
) {
227 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
228 DCHECK_EQ(STATE_INITIALIZED
, state_
);
229 DCHECK_GE(notification_id
, kFirstNotificationId
);
230 DCHECK(origin
.is_valid());
232 std::string key
= CreateDataKey(origin
, notification_id
);
233 return LevelDBStatusToStatus(db_
->Delete(leveldb::WriteOptions(), key
));
236 NotificationDatabase::Status
237 NotificationDatabase::DeleteAllNotificationDataForOrigin(
239 std::set
<int64_t>* deleted_notification_set
) {
240 return DeleteAllNotificationDataInternal(origin
,
241 kInvalidServiceWorkerRegistrationId
,
242 deleted_notification_set
);
245 NotificationDatabase::Status
246 NotificationDatabase::DeleteAllNotificationDataForServiceWorkerRegistration(
248 int64_t service_worker_registration_id
,
249 std::set
<int64_t>* deleted_notification_set
) {
250 return DeleteAllNotificationDataInternal(origin
,
251 service_worker_registration_id
,
252 deleted_notification_set
);
255 NotificationDatabase::Status
NotificationDatabase::Destroy() {
256 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
258 leveldb::Options options
;
259 if (IsInMemoryDatabase()) {
261 return STATUS_OK
; // The database has not been initialized.
263 options
.env
= env_
.get();
266 state_
= STATE_DISABLED
;
269 return LevelDBStatusToStatus(
270 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), options
));
273 NotificationDatabase::Status
NotificationDatabase::ReadNextNotificationId() {
275 Status status
= LevelDBStatusToStatus(
276 db_
->Get(leveldb::ReadOptions(), kNextNotificationIdKey
, &value
));
278 if (status
== STATUS_ERROR_NOT_FOUND
) {
279 next_notification_id_
= kFirstNotificationId
;
283 if (status
!= STATUS_OK
)
286 if (!base::StringToInt64(value
, &next_notification_id_
) ||
287 next_notification_id_
< kFirstNotificationId
) {
288 return STATUS_ERROR_CORRUPTED
;
294 NotificationDatabase::Status
295 NotificationDatabase::ReadAllNotificationDataInternal(
297 int64_t service_worker_registration_id
,
298 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
299 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
300 DCHECK(notification_data_vector
);
302 const std::string prefix
= CreateDataPrefix(origin
);
304 leveldb::Slice
prefix_slice(prefix
);
306 NotificationDatabaseData notification_database_data
;
307 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(leveldb::ReadOptions()));
308 for (iter
->Seek(prefix_slice
); iter
->Valid(); iter
->Next()) {
309 if (!iter
->key().starts_with(prefix_slice
))
312 Status status
= DeserializedNotificationData(iter
->value().ToString(),
313 ¬ification_database_data
);
314 if (status
!= STATUS_OK
)
317 if (service_worker_registration_id
!= kInvalidServiceWorkerRegistrationId
&&
318 notification_database_data
.service_worker_registration_id
!=
319 service_worker_registration_id
) {
323 notification_data_vector
->push_back(notification_database_data
);
326 return LevelDBStatusToStatus(iter
->status());
329 NotificationDatabase::Status
330 NotificationDatabase::DeleteAllNotificationDataInternal(
332 int64_t service_worker_registration_id
,
333 std::set
<int64_t>* deleted_notification_set
) {
334 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
335 DCHECK(deleted_notification_set
);
336 DCHECK(origin
.is_valid());
338 const std::string prefix
= CreateDataPrefix(origin
);
340 leveldb::Slice
prefix_slice(prefix
);
341 leveldb::WriteBatch batch
;
343 NotificationDatabaseData notification_database_data
;
344 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(leveldb::ReadOptions()));
345 for (iter
->Seek(prefix_slice
); iter
->Valid(); iter
->Next()) {
346 if (!iter
->key().starts_with(prefix_slice
))
349 if (service_worker_registration_id
!= kInvalidServiceWorkerRegistrationId
) {
350 Status status
= DeserializedNotificationData(iter
->value().ToString(),
351 ¬ification_database_data
);
352 if (status
!= STATUS_OK
)
355 if (notification_database_data
.service_worker_registration_id
!=
356 service_worker_registration_id
) {
361 leveldb::Slice notification_id_slice
= iter
->key();
362 notification_id_slice
.remove_prefix(prefix_slice
.size());
364 int64_t notification_id
= 0;
365 if (!base::StringToInt64(notification_id_slice
.ToString(),
367 return STATUS_ERROR_CORRUPTED
;
370 deleted_notification_set
->insert(notification_id
);
371 batch
.Delete(iter
->key());
374 if (deleted_notification_set
->empty())
377 return LevelDBStatusToStatus(db_
->Write(leveldb::WriteOptions(), &batch
));
380 } // namespace content