PageRenderTime 68ms CodeModel.GetById 41ms RepoModel.GetById 0ms app.codeStats 0ms

/content/browser/notifications/notification_database.cc

https://gitlab.com/hkratz/chromium
C++ | 377 lines | 283 code | 73 blank | 21 comment | 44 complexity | 64c78d7d1565cafdd27dfa0a23b7d239 MD5 | raw file
  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.
  4. #include "content/browser/notifications/notification_database.h"
  5. #include <string>
  6. #include "base/files/file_util.h"
  7. #include "base/strings/string_number_conversions.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "content/browser/notifications/notification_database_data_conversions.h"
  10. #include "content/common/service_worker/service_worker_types.h"
  11. #include "content/public/browser/browser_thread.h"
  12. #include "content/public/browser/notification_database_data.h"
  13. #include "storage/common/database/database_identifier.h"
  14. #include "third_party/leveldatabase/env_chromium.h"
  15. #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
  16. #include "third_party/leveldatabase/src/include/leveldb/db.h"
  17. #include "third_party/leveldatabase/src/include/leveldb/env.h"
  18. #include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
  19. #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
  20. #include "url/gurl.h"
  21. // Notification LevelDB database schema (in alphabetized order)
  22. // =======================
  23. //
  24. // key: "DATA:" <origin identifier> '\x00' <notification_id>
  25. // value: String containing the NotificationDatabaseDataProto protocol buffer
  26. // in serialized form.
  27. //
  28. // key: "NEXT_NOTIFICATION_ID"
  29. // value: Decimal string which fits into an int64_t.
  30. namespace content {
  31. namespace {
  32. // Keys of the fields defined in the database.
  33. const char kNextNotificationIdKey[] = "NEXT_NOTIFICATION_ID";
  34. const char kDataKeyPrefix[] = "DATA:";
  35. // Separates the components of compound keys.
  36. const char kKeySeparator = '\x00';
  37. // The first notification id which to be handed out by the database.
  38. const int64_t kFirstNotificationId = 1;
  39. // Converts the LevelDB |status| to one of the notification database's values.
  40. NotificationDatabase::Status LevelDBStatusToStatus(
  41. const leveldb::Status& status) {
  42. if (status.ok())
  43. return NotificationDatabase::STATUS_OK;
  44. else if (status.IsNotFound())
  45. return NotificationDatabase::STATUS_ERROR_NOT_FOUND;
  46. else if (status.IsCorruption())
  47. return NotificationDatabase::STATUS_ERROR_CORRUPTED;
  48. else if (status.IsIOError())
  49. return NotificationDatabase::STATUS_IO_ERROR;
  50. else if (status.IsNotSupportedError())
  51. return NotificationDatabase::STATUS_NOT_SUPPORTED;
  52. // TODO(cmumford): Once leveldb 1.19 is released add IsInvalidArgument().
  53. return NotificationDatabase::STATUS_ERROR_FAILED;
  54. }
  55. // Creates a prefix for the data entries based on |origin|.
  56. std::string CreateDataPrefix(const GURL& origin) {
  57. if (!origin.is_valid())
  58. return kDataKeyPrefix;
  59. return base::StringPrintf("%s%s%c", kDataKeyPrefix,
  60. storage::GetIdentifierFromOrigin(origin).c_str(),
  61. kKeySeparator);
  62. }
  63. // Creates the compound data key in which notification data is stored.
  64. std::string CreateDataKey(const GURL& origin, int64_t notification_id) {
  65. DCHECK(origin.is_valid());
  66. return CreateDataPrefix(origin) + base::Int64ToString(notification_id);
  67. }
  68. // Deserializes data in |serialized_data| to |notification_database_data|.
  69. // Will return if the deserialization was successful.
  70. NotificationDatabase::Status DeserializedNotificationData(
  71. const std::string& serialized_data,
  72. NotificationDatabaseData* notification_database_data) {
  73. DCHECK(notification_database_data);
  74. if (DeserializeNotificationDatabaseData(serialized_data,
  75. notification_database_data)) {
  76. return NotificationDatabase::STATUS_OK;
  77. }
  78. DLOG(ERROR) << "Unable to deserialize a notification's data.";
  79. return NotificationDatabase::STATUS_ERROR_CORRUPTED;
  80. }
  81. } // namespace
  82. NotificationDatabase::NotificationDatabase(const base::FilePath& path)
  83. : path_(path) {}
  84. NotificationDatabase::~NotificationDatabase() {
  85. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  86. }
  87. NotificationDatabase::Status NotificationDatabase::Open(
  88. bool create_if_missing) {
  89. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  90. DCHECK_EQ(STATE_UNINITIALIZED, state_);
  91. if (!create_if_missing) {
  92. if (IsInMemoryDatabase() || !base::PathExists(path_) ||
  93. base::IsDirectoryEmpty(path_)) {
  94. return NotificationDatabase::STATUS_ERROR_NOT_FOUND;
  95. }
  96. }
  97. filter_policy_.reset(leveldb::NewBloomFilterPolicy(10));
  98. leveldb::Options options;
  99. options.create_if_missing = create_if_missing;
  100. options.paranoid_checks = true;
  101. options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
  102. options.filter_policy = filter_policy_.get();
  103. if (IsInMemoryDatabase()) {
  104. env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
  105. options.env = env_.get();
  106. }
  107. leveldb::DB* db = nullptr;
  108. Status status = LevelDBStatusToStatus(
  109. leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
  110. if (status != STATUS_OK)
  111. return status;
  112. state_ = STATE_INITIALIZED;
  113. db_.reset(db);
  114. return ReadNextNotificationId();
  115. }
  116. NotificationDatabase::Status NotificationDatabase::ReadNotificationData(
  117. int64_t notification_id,
  118. const GURL& origin,
  119. NotificationDatabaseData* notification_database_data) const {
  120. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  121. DCHECK_EQ(STATE_INITIALIZED, state_);
  122. DCHECK_GE(notification_id, kFirstNotificationId);
  123. DCHECK(origin.is_valid());
  124. DCHECK(notification_database_data);
  125. std::string key = CreateDataKey(origin, notification_id);
  126. std::string serialized_data;
  127. Status status = LevelDBStatusToStatus(
  128. db_->Get(leveldb::ReadOptions(), key, &serialized_data));
  129. if (status != STATUS_OK)
  130. return status;
  131. return DeserializedNotificationData(serialized_data,
  132. notification_database_data);
  133. }
  134. NotificationDatabase::Status NotificationDatabase::ReadAllNotificationData(
  135. std::vector<NotificationDatabaseData>* notification_data_vector) const {
  136. return ReadAllNotificationDataInternal(GURL() /* origin */,
  137. kInvalidServiceWorkerRegistrationId,
  138. notification_data_vector);
  139. }
  140. NotificationDatabase::Status
  141. NotificationDatabase::ReadAllNotificationDataForOrigin(
  142. const GURL& origin,
  143. std::vector<NotificationDatabaseData>* notification_data_vector) const {
  144. return ReadAllNotificationDataInternal(
  145. origin, kInvalidServiceWorkerRegistrationId, notification_data_vector);
  146. }
  147. NotificationDatabase::Status
  148. NotificationDatabase::ReadAllNotificationDataForServiceWorkerRegistration(
  149. const GURL& origin,
  150. int64_t service_worker_registration_id,
  151. std::vector<NotificationDatabaseData>* notification_data_vector) const {
  152. return ReadAllNotificationDataInternal(origin, service_worker_registration_id,
  153. notification_data_vector);
  154. }
  155. NotificationDatabase::Status NotificationDatabase::WriteNotificationData(
  156. const GURL& origin,
  157. const NotificationDatabaseData& notification_database_data,
  158. int64_t* notification_id) {
  159. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  160. DCHECK_EQ(STATE_INITIALIZED, state_);
  161. DCHECK(notification_id);
  162. DCHECK(origin.is_valid());
  163. DCHECK_GE(next_notification_id_, kFirstNotificationId);
  164. NotificationDatabaseData storage_data = notification_database_data;
  165. storage_data.notification_id = next_notification_id_;
  166. std::string serialized_data;
  167. if (!SerializeNotificationDatabaseData(storage_data, &serialized_data)) {
  168. DLOG(ERROR) << "Unable to serialize data for a notification belonging "
  169. << "to: " << origin;
  170. return STATUS_ERROR_FAILED;
  171. }
  172. leveldb::WriteBatch batch;
  173. batch.Put(CreateDataKey(origin, next_notification_id_), serialized_data);
  174. batch.Put(kNextNotificationIdKey,
  175. base::Int64ToString(next_notification_id_ + 1));
  176. Status status =
  177. LevelDBStatusToStatus(db_->Write(leveldb::WriteOptions(), &batch));
  178. if (status != STATUS_OK)
  179. return status;
  180. *notification_id = next_notification_id_++;
  181. return STATUS_OK;
  182. }
  183. NotificationDatabase::Status NotificationDatabase::DeleteNotificationData(
  184. int64_t notification_id,
  185. const GURL& origin) {
  186. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  187. DCHECK_EQ(STATE_INITIALIZED, state_);
  188. DCHECK_GE(notification_id, kFirstNotificationId);
  189. DCHECK(origin.is_valid());
  190. std::string key = CreateDataKey(origin, notification_id);
  191. return LevelDBStatusToStatus(db_->Delete(leveldb::WriteOptions(), key));
  192. }
  193. NotificationDatabase::Status
  194. NotificationDatabase::DeleteAllNotificationDataForOrigin(
  195. const GURL& origin,
  196. std::set<int64_t>* deleted_notification_set) {
  197. return DeleteAllNotificationDataInternal(
  198. origin, kInvalidServiceWorkerRegistrationId, deleted_notification_set);
  199. }
  200. NotificationDatabase::Status
  201. NotificationDatabase::DeleteAllNotificationDataForServiceWorkerRegistration(
  202. const GURL& origin,
  203. int64_t service_worker_registration_id,
  204. std::set<int64_t>* deleted_notification_set) {
  205. return DeleteAllNotificationDataInternal(
  206. origin, service_worker_registration_id, deleted_notification_set);
  207. }
  208. NotificationDatabase::Status NotificationDatabase::Destroy() {
  209. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  210. leveldb::Options options;
  211. if (IsInMemoryDatabase()) {
  212. if (!env_)
  213. return STATUS_OK; // The database has not been initialized.
  214. options.env = env_.get();
  215. }
  216. state_ = STATE_DISABLED;
  217. db_.reset();
  218. return LevelDBStatusToStatus(
  219. leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
  220. }
  221. NotificationDatabase::Status NotificationDatabase::ReadNextNotificationId() {
  222. std::string value;
  223. Status status = LevelDBStatusToStatus(
  224. db_->Get(leveldb::ReadOptions(), kNextNotificationIdKey, &value));
  225. if (status == STATUS_ERROR_NOT_FOUND) {
  226. next_notification_id_ = kFirstNotificationId;
  227. return STATUS_OK;
  228. }
  229. if (status != STATUS_OK)
  230. return status;
  231. if (!base::StringToInt64(value, &next_notification_id_) ||
  232. next_notification_id_ < kFirstNotificationId) {
  233. return STATUS_ERROR_CORRUPTED;
  234. }
  235. return STATUS_OK;
  236. }
  237. NotificationDatabase::Status
  238. NotificationDatabase::ReadAllNotificationDataInternal(
  239. const GURL& origin,
  240. int64_t service_worker_registration_id,
  241. std::vector<NotificationDatabaseData>* notification_data_vector) const {
  242. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  243. DCHECK(notification_data_vector);
  244. const std::string prefix = CreateDataPrefix(origin);
  245. leveldb::Slice prefix_slice(prefix);
  246. NotificationDatabaseData notification_database_data;
  247. scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
  248. for (iter->Seek(prefix_slice); iter->Valid(); iter->Next()) {
  249. if (!iter->key().starts_with(prefix_slice))
  250. break;
  251. Status status = DeserializedNotificationData(iter->value().ToString(),
  252. &notification_database_data);
  253. if (status != STATUS_OK)
  254. return status;
  255. if (service_worker_registration_id != kInvalidServiceWorkerRegistrationId &&
  256. notification_database_data.service_worker_registration_id !=
  257. service_worker_registration_id) {
  258. continue;
  259. }
  260. notification_data_vector->push_back(notification_database_data);
  261. }
  262. return LevelDBStatusToStatus(iter->status());
  263. }
  264. NotificationDatabase::Status
  265. NotificationDatabase::DeleteAllNotificationDataInternal(
  266. const GURL& origin,
  267. int64_t service_worker_registration_id,
  268. std::set<int64_t>* deleted_notification_set) {
  269. DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  270. DCHECK(deleted_notification_set);
  271. DCHECK(origin.is_valid());
  272. const std::string prefix = CreateDataPrefix(origin);
  273. leveldb::Slice prefix_slice(prefix);
  274. leveldb::WriteBatch batch;
  275. NotificationDatabaseData notification_database_data;
  276. scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
  277. for (iter->Seek(prefix_slice); iter->Valid(); iter->Next()) {
  278. if (!iter->key().starts_with(prefix_slice))
  279. break;
  280. if (service_worker_registration_id != kInvalidServiceWorkerRegistrationId) {
  281. Status status = DeserializedNotificationData(iter->value().ToString(),
  282. &notification_database_data);
  283. if (status != STATUS_OK)
  284. return status;
  285. if (notification_database_data.service_worker_registration_id !=
  286. service_worker_registration_id) {
  287. continue;
  288. }
  289. }
  290. leveldb::Slice notification_id_slice = iter->key();
  291. notification_id_slice.remove_prefix(prefix_slice.size());
  292. int64_t notification_id = 0;
  293. if (!base::StringToInt64(notification_id_slice.ToString(),
  294. &notification_id)) {
  295. return STATUS_ERROR_CORRUPTED;
  296. }
  297. deleted_notification_set->insert(notification_id);
  298. batch.Delete(iter->key());
  299. }
  300. if (deleted_notification_set->empty())
  301. return STATUS_OK;
  302. return LevelDBStatusToStatus(db_->Write(leveldb::WriteOptions(), &batch));
  303. }
  304. } // namespace content