summaryrefslogtreecommitdiffstats
path: root/content/browser/notifications/notification_database.cc
blob: 4d8b4f9ca9a94a00a49455b33b75c8c0d4298872 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/notifications/notification_database.h"

#include <string>

#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#include "third_party/leveldatabase/src/include/leveldb/env.h"
#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"

// Notification LevelDB database schema (in alphabetized order)
// =======================
//
// key: "NEXT_NOTIFICATION_ID"
// value: Decimal string which fits into an int64_t.
//

namespace content {
namespace {

// Keys of the fields defined in the database.
const char kNextNotificationIdKey[] = "NEXT_NOTIFICATION_ID";

// The first notification id which to be handed out by the database.
const int64_t kFirstNotificationId = 1;

// Converts the LevelDB |status| to one of the notification database's values.
NotificationDatabase::Status LevelDBStatusToStatus(
    const leveldb::Status& status) {
  if (status.ok())
    return NotificationDatabase::STATUS_OK;
  else if (status.IsNotFound())
    return NotificationDatabase::STATUS_ERROR_NOT_FOUND;
  else if (status.IsCorruption())
    return NotificationDatabase::STATUS_ERROR_CORRUPTED;

  return NotificationDatabase::STATUS_ERROR_FAILED;
}

}  // namespace

NotificationDatabase::NotificationDatabase(const base::FilePath& path)
    : path_(path),
      state_(STATE_UNINITIALIZED) {
  sequence_checker_.DetachFromSequence();
}

NotificationDatabase::~NotificationDatabase() {
  DCHECK(sequence_checker_.CalledOnValidSequencedThread());
}

NotificationDatabase::Status NotificationDatabase::Open(
    bool create_if_missing) {
  DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  DCHECK_EQ(STATE_UNINITIALIZED, state_);

  if (!create_if_missing) {
    if (IsInMemoryDatabase() ||
        !base::PathExists(path_) ||
        base::IsDirectoryEmpty(path_)) {
      return NotificationDatabase::STATUS_ERROR_NOT_FOUND;
    }
  }

  leveldb::Options options;
  options.create_if_missing = create_if_missing;
  options.paranoid_checks = true;
  if (IsInMemoryDatabase()) {
    env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
    options.env = env_.get();
  }

  leveldb::DB* db = nullptr;
  Status status = LevelDBStatusToStatus(
      leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db));
  if (status != STATUS_OK)
    return status;

  state_ = STATE_INITIALIZED;
  db_.reset(db);

  return STATUS_OK;
}

NotificationDatabase::Status NotificationDatabase::GetNextNotificationId(
    int64_t* notification_id) const {
  DCHECK(sequence_checker_.CalledOnValidSequencedThread());
  DCHECK_EQ(state_, STATE_INITIALIZED);
  DCHECK(notification_id);

  std::string value;
  Status status = LevelDBStatusToStatus(
      db_->Get(leveldb::ReadOptions(), kNextNotificationIdKey, &value));

  if (status == STATUS_ERROR_NOT_FOUND) {
    *notification_id = kFirstNotificationId;
    return STATUS_OK;
  }

  if (status != STATUS_OK)
    return status;

  int64_t next_notification_id;
  if (!base::StringToInt64(value, &next_notification_id) ||
      next_notification_id < kFirstNotificationId) {
    return STATUS_ERROR_CORRUPTED;
  }

  *notification_id = next_notification_id;
  return STATUS_OK;
}

NotificationDatabase::Status NotificationDatabase::Destroy() {
  DCHECK(sequence_checker_.CalledOnValidSequencedThread());

  leveldb::Options options;
  if (IsInMemoryDatabase()) {
    if (!env_)
      return STATUS_OK;  // The database has not been initialized.

    options.env = env_.get();
  }

  state_ = STATE_DISABLED;
  db_.reset();

  return LevelDBStatusToStatus(
      leveldb::DestroyDB(path_.AsUTF8Unsafe(), options));
}

void NotificationDatabase::WriteNextNotificationId(
    leveldb::WriteBatch* batch,
    int64_t next_notification_id) const {
  DCHECK_GE(next_notification_id, kFirstNotificationId);
  DCHECK(batch);

  batch->Put(kNextNotificationIdKey, base::Int64ToString(next_notification_id));
}

}  // namespace content