1// Copyright 2014 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
5#include "extensions/browser/value_store/value_store_frontend.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "content/public/browser/browser_thread.h"
12#include "extensions/browser/value_store/leveldb_value_store.h"
13
14using content::BrowserThread;
15
16class ValueStoreFrontend::Backend : public base::RefCountedThreadSafe<Backend> {
17 public:
18  Backend() : storage_(NULL) {}
19
20  void Init(const base::FilePath& db_path) {
21    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
22    DCHECK(!storage_);
23    TRACE_EVENT0("ValueStoreFrontend::Backend", "Init");
24    db_path_ = db_path;
25    storage_ = new LeveldbValueStore(db_path);
26  }
27
28  // This variant is useful for testing (using a mock ValueStore).
29  void InitWithStore(scoped_ptr<ValueStore> storage) {
30    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
31    DCHECK(!storage_);
32    storage_ = storage.release();
33  }
34
35  void Get(const std::string& key,
36           const ValueStoreFrontend::ReadCallback& callback) {
37    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
38    ValueStore::ReadResult result = storage_->Get(key);
39
40    // Extract the value from the ReadResult and pass ownership of it to the
41    // callback.
42    scoped_ptr<base::Value> value;
43    if (!result->HasError()) {
44      result->settings().RemoveWithoutPathExpansion(key, &value);
45    } else {
46      LOG(WARNING) << "Reading " << key << " from " << db_path_.value()
47                   << " failed: " << result->error().message;
48    }
49
50    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
51        base::Bind(&ValueStoreFrontend::Backend::RunCallback,
52                   this, callback, base::Passed(&value)));
53  }
54
55  void Set(const std::string& key, scoped_ptr<base::Value> value) {
56    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
57    // We don't need the old value, so skip generating changes.
58    ValueStore::WriteResult result = storage_->Set(
59        ValueStore::IGNORE_QUOTA | ValueStore::NO_GENERATE_CHANGES,
60        key,
61        *value.get());
62    LOG_IF(ERROR, result->HasError()) << "Error while writing " << key << " to "
63                                      << db_path_.value();
64  }
65
66  void Remove(const std::string& key) {
67    DCHECK_CURRENTLY_ON(BrowserThread::FILE);
68    storage_->Remove(key);
69  }
70
71 private:
72  friend class base::RefCountedThreadSafe<Backend>;
73
74  virtual ~Backend() {
75    if (BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
76      delete storage_;
77    } else {
78      BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, storage_);
79    }
80  }
81
82  void RunCallback(const ValueStoreFrontend::ReadCallback& callback,
83                   scoped_ptr<base::Value> value) {
84    DCHECK_CURRENTLY_ON(BrowserThread::UI);
85    callback.Run(value.Pass());
86  }
87
88  // The actual ValueStore that handles persisting the data to disk. Used
89  // exclusively on the FILE thread.
90  ValueStore* storage_;
91
92  base::FilePath db_path_;
93
94  DISALLOW_COPY_AND_ASSIGN(Backend);
95};
96
97ValueStoreFrontend::ValueStoreFrontend()
98    : backend_(new Backend()) {
99}
100
101ValueStoreFrontend::ValueStoreFrontend(const base::FilePath& db_path)
102    : backend_(new Backend()) {
103  Init(db_path);
104}
105
106ValueStoreFrontend::ValueStoreFrontend(scoped_ptr<ValueStore> value_store)
107    : backend_(new Backend()) {
108  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
109      base::Bind(&ValueStoreFrontend::Backend::InitWithStore,
110                 backend_, base::Passed(&value_store)));
111}
112
113ValueStoreFrontend::~ValueStoreFrontend() {
114  DCHECK(CalledOnValidThread());
115}
116
117void ValueStoreFrontend::Init(const base::FilePath& db_path) {
118  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
119      base::Bind(&ValueStoreFrontend::Backend::Init,
120                 backend_, db_path));
121}
122
123void ValueStoreFrontend::Get(const std::string& key,
124                             const ReadCallback& callback) {
125  DCHECK(CalledOnValidThread());
126
127  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
128      base::Bind(&ValueStoreFrontend::Backend::Get,
129                 backend_, key, callback));
130}
131
132void ValueStoreFrontend::Set(const std::string& key,
133                             scoped_ptr<base::Value> value) {
134  DCHECK(CalledOnValidThread());
135
136  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
137      base::Bind(&ValueStoreFrontend::Backend::Set,
138                 backend_, key, base::Passed(&value)));
139}
140
141void ValueStoreFrontend::Remove(const std::string& key) {
142  DCHECK(CalledOnValidThread());
143
144  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
145      base::Bind(&ValueStoreFrontend::Backend::Remove,
146                 backend_, key));
147}
148