Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / notifications / notification_database.cc
blobb3b805f4048a3a01e30039e37a4878621b83d395
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"
7 #include <string>
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"
23 #include "url/gurl.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.
35 namespace content {
36 namespace {
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) {
51 if (status.ok())
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",
67 kDataKeyPrefix,
68 storage::GetIdentifierFromOrigin(origin).c_str(),
69 kKeySeparator);
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;
93 } // namespace
95 NotificationDatabase::NotificationDatabase(const base::FilePath& path)
96 : path_(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)
131 return status;
133 state_ = STATE_INITIALIZED;
134 db_.reset(db);
136 return ReadNextNotificationId();
139 NotificationDatabase::Status NotificationDatabase::ReadNotificationData(
140 int64_t notification_id,
141 const GURL& origin,
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)
155 return status;
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(
171 const GURL& origin,
172 std::vector<NotificationDatabaseData>* notification_data_vector) const {
173 return ReadAllNotificationDataInternal(origin,
174 kInvalidServiceWorkerRegistrationId,
175 notification_data_vector);
178 NotificationDatabase::Status
179 NotificationDatabase::ReadAllNotificationDataForServiceWorkerRegistration(
180 const GURL& origin,
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(
189 const GURL& origin,
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,
204 &serialized_data)) {
205 DLOG(ERROR) << "Unable to serialize data for a notification belonging "
206 << "to: " << origin;
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)
218 return status;
220 *notification_id = next_notification_id_++;
221 return STATUS_OK;
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(
238 const GURL& origin,
239 std::set<int64_t>* deleted_notification_set) {
240 return DeleteAllNotificationDataInternal(origin,
241 kInvalidServiceWorkerRegistrationId,
242 deleted_notification_set);
245 NotificationDatabase::Status
246 NotificationDatabase::DeleteAllNotificationDataForServiceWorkerRegistration(
247 const GURL& origin,
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()) {
260 if (!env_)
261 return STATUS_OK; // The database has not been initialized.
263 options.env = env_.get();
266 state_ = STATE_DISABLED;
267 db_.reset();
269 return LevelDBStatusToStatus(
270 leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
273 NotificationDatabase::Status NotificationDatabase::ReadNextNotificationId() {
274 std::string value;
275 Status status = LevelDBStatusToStatus(
276 db_->Get(leveldb::ReadOptions(), kNextNotificationIdKey, &value));
278 if (status == STATUS_ERROR_NOT_FOUND) {
279 next_notification_id_ = kFirstNotificationId;
280 return STATUS_OK;
283 if (status != STATUS_OK)
284 return status;
286 if (!base::StringToInt64(value, &next_notification_id_) ||
287 next_notification_id_ < kFirstNotificationId) {
288 return STATUS_ERROR_CORRUPTED;
291 return STATUS_OK;
294 NotificationDatabase::Status
295 NotificationDatabase::ReadAllNotificationDataInternal(
296 const GURL& origin,
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))
310 break;
312 Status status = DeserializedNotificationData(iter->value().ToString(),
313 &notification_database_data);
314 if (status != STATUS_OK)
315 return status;
317 if (service_worker_registration_id != kInvalidServiceWorkerRegistrationId &&
318 notification_database_data.service_worker_registration_id !=
319 service_worker_registration_id) {
320 continue;
323 notification_data_vector->push_back(notification_database_data);
326 return LevelDBStatusToStatus(iter->status());
329 NotificationDatabase::Status
330 NotificationDatabase::DeleteAllNotificationDataInternal(
331 const GURL& origin,
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))
347 break;
349 if (service_worker_registration_id != kInvalidServiceWorkerRegistrationId) {
350 Status status = DeserializedNotificationData(iter->value().ToString(),
351 &notification_database_data);
352 if (status != STATUS_OK)
353 return status;
355 if (notification_database_data.service_worker_registration_id !=
356 service_worker_registration_id) {
357 continue;
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(),
366 &notification_id)) {
367 return STATUS_ERROR_CORRUPTED;
370 deleted_notification_set->insert(notification_id);
371 batch.Delete(iter->key());
374 if (deleted_notification_set->empty())
375 return STATUS_OK;
377 return LevelDBStatusToStatus(db_->Write(leveldb::WriteOptions(), &batch));
380 } // namespace content