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 options
.filter_policy
= filter_policy_
.get();
123 if (IsInMemoryDatabase()) {
124 env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
125 options
.env
= env_
.get();
128 leveldb::DB
* db
= nullptr;
129 Status status
= LevelDBStatusToStatus(
130 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
));
131 if (status
!= STATUS_OK
)
134 state_
= STATE_INITIALIZED
;
137 return ReadNextNotificationId();
140 NotificationDatabase::Status
NotificationDatabase::ReadNotificationData(
141 int64_t notification_id
,
143 NotificationDatabaseData
* notification_database_data
) const {
144 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
145 DCHECK_EQ(STATE_INITIALIZED
, state_
);
146 DCHECK_GE(notification_id
, kFirstNotificationId
);
147 DCHECK(origin
.is_valid());
148 DCHECK(notification_database_data
);
150 std::string key
= CreateDataKey(origin
, notification_id
);
151 std::string serialized_data
;
153 Status status
= LevelDBStatusToStatus(
154 db_
->Get(leveldb::ReadOptions(), key
, &serialized_data
));
155 if (status
!= STATUS_OK
)
158 return DeserializedNotificationData(serialized_data
,
159 notification_database_data
);
162 NotificationDatabase::Status
163 NotificationDatabase::ReadAllNotificationData(
164 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
165 return ReadAllNotificationDataInternal(GURL() /* origin */,
166 kInvalidServiceWorkerRegistrationId
,
167 notification_data_vector
);
170 NotificationDatabase::Status
171 NotificationDatabase::ReadAllNotificationDataForOrigin(
173 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
174 return ReadAllNotificationDataInternal(origin
,
175 kInvalidServiceWorkerRegistrationId
,
176 notification_data_vector
);
179 NotificationDatabase::Status
180 NotificationDatabase::ReadAllNotificationDataForServiceWorkerRegistration(
182 int64_t service_worker_registration_id
,
183 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
184 return ReadAllNotificationDataInternal(origin
,
185 service_worker_registration_id
,
186 notification_data_vector
);
189 NotificationDatabase::Status
NotificationDatabase::WriteNotificationData(
191 const NotificationDatabaseData
& notification_database_data
,
192 int64_t* notification_id
) {
193 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
194 DCHECK_EQ(STATE_INITIALIZED
, state_
);
195 DCHECK(notification_id
);
196 DCHECK(origin
.is_valid());
198 DCHECK_GE(next_notification_id_
, kFirstNotificationId
);
200 NotificationDatabaseData storage_data
= notification_database_data
;
201 storage_data
.notification_id
= next_notification_id_
;
203 std::string serialized_data
;
204 if (!SerializeNotificationDatabaseData(storage_data
,
206 DLOG(ERROR
) << "Unable to serialize data for a notification belonging "
208 return STATUS_ERROR_FAILED
;
211 leveldb::WriteBatch batch
;
212 batch
.Put(CreateDataKey(origin
, next_notification_id_
), serialized_data
);
213 batch
.Put(kNextNotificationIdKey
,
214 base::Int64ToString(next_notification_id_
+ 1));
216 Status status
= LevelDBStatusToStatus(
217 db_
->Write(leveldb::WriteOptions(), &batch
));
218 if (status
!= STATUS_OK
)
221 *notification_id
= next_notification_id_
++;
225 NotificationDatabase::Status
NotificationDatabase::DeleteNotificationData(
226 int64_t notification_id
,
227 const GURL
& origin
) {
228 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
229 DCHECK_EQ(STATE_INITIALIZED
, state_
);
230 DCHECK_GE(notification_id
, kFirstNotificationId
);
231 DCHECK(origin
.is_valid());
233 std::string key
= CreateDataKey(origin
, notification_id
);
234 return LevelDBStatusToStatus(db_
->Delete(leveldb::WriteOptions(), key
));
237 NotificationDatabase::Status
238 NotificationDatabase::DeleteAllNotificationDataForOrigin(
240 std::set
<int64_t>* deleted_notification_set
) {
241 return DeleteAllNotificationDataInternal(origin
,
242 kInvalidServiceWorkerRegistrationId
,
243 deleted_notification_set
);
246 NotificationDatabase::Status
247 NotificationDatabase::DeleteAllNotificationDataForServiceWorkerRegistration(
249 int64_t service_worker_registration_id
,
250 std::set
<int64_t>* deleted_notification_set
) {
251 return DeleteAllNotificationDataInternal(origin
,
252 service_worker_registration_id
,
253 deleted_notification_set
);
256 NotificationDatabase::Status
NotificationDatabase::Destroy() {
257 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
259 leveldb::Options options
;
260 if (IsInMemoryDatabase()) {
262 return STATUS_OK
; // The database has not been initialized.
264 options
.env
= env_
.get();
267 state_
= STATE_DISABLED
;
270 return LevelDBStatusToStatus(
271 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), options
));
274 NotificationDatabase::Status
NotificationDatabase::ReadNextNotificationId() {
276 Status status
= LevelDBStatusToStatus(
277 db_
->Get(leveldb::ReadOptions(), kNextNotificationIdKey
, &value
));
279 if (status
== STATUS_ERROR_NOT_FOUND
) {
280 next_notification_id_
= kFirstNotificationId
;
284 if (status
!= STATUS_OK
)
287 if (!base::StringToInt64(value
, &next_notification_id_
) ||
288 next_notification_id_
< kFirstNotificationId
) {
289 return STATUS_ERROR_CORRUPTED
;
295 NotificationDatabase::Status
296 NotificationDatabase::ReadAllNotificationDataInternal(
298 int64_t service_worker_registration_id
,
299 std::vector
<NotificationDatabaseData
>* notification_data_vector
) const {
300 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
301 DCHECK(notification_data_vector
);
303 const std::string prefix
= CreateDataPrefix(origin
);
305 leveldb::Slice
prefix_slice(prefix
);
307 NotificationDatabaseData notification_database_data
;
308 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(leveldb::ReadOptions()));
309 for (iter
->Seek(prefix_slice
); iter
->Valid(); iter
->Next()) {
310 if (!iter
->key().starts_with(prefix_slice
))
313 Status status
= DeserializedNotificationData(iter
->value().ToString(),
314 ¬ification_database_data
);
315 if (status
!= STATUS_OK
)
318 if (service_worker_registration_id
!= kInvalidServiceWorkerRegistrationId
&&
319 notification_database_data
.service_worker_registration_id
!=
320 service_worker_registration_id
) {
324 notification_data_vector
->push_back(notification_database_data
);
327 return LevelDBStatusToStatus(iter
->status());
330 NotificationDatabase::Status
331 NotificationDatabase::DeleteAllNotificationDataInternal(
333 int64_t service_worker_registration_id
,
334 std::set
<int64_t>* deleted_notification_set
) {
335 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
336 DCHECK(deleted_notification_set
);
337 DCHECK(origin
.is_valid());
339 const std::string prefix
= CreateDataPrefix(origin
);
341 leveldb::Slice
prefix_slice(prefix
);
342 leveldb::WriteBatch batch
;
344 NotificationDatabaseData notification_database_data
;
345 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(leveldb::ReadOptions()));
346 for (iter
->Seek(prefix_slice
); iter
->Valid(); iter
->Next()) {
347 if (!iter
->key().starts_with(prefix_slice
))
350 if (service_worker_registration_id
!= kInvalidServiceWorkerRegistrationId
) {
351 Status status
= DeserializedNotificationData(iter
->value().ToString(),
352 ¬ification_database_data
);
353 if (status
!= STATUS_OK
)
356 if (notification_database_data
.service_worker_registration_id
!=
357 service_worker_registration_id
) {
362 leveldb::Slice notification_id_slice
= iter
->key();
363 notification_id_slice
.remove_prefix(prefix_slice
.size());
365 int64_t notification_id
= 0;
366 if (!base::StringToInt64(notification_id_slice
.ToString(),
368 return STATUS_ERROR_CORRUPTED
;
371 deleted_notification_set
->insert(notification_id
);
372 batch
.Delete(iter
->key());
375 if (deleted_notification_set
->empty())
378 return LevelDBStatusToStatus(db_
->Write(leveldb::WriteOptions(), &batch
));
381 } // namespace content