blob: d0abeac3982f3d8d93528e285c4b2c94f4451fba [file] [log] [blame]
// Copyright (c) 2011 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 "chrome/browser/extensions/app_notification_storage.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/sys_string_conversions.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/common/extensions/extension.h"
#include "content/browser/browser_thread.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
using base::JSONReader;
using base::JSONWriter;
// A concrete implementation of the AppNotificationStorage interface, using
// LevelDb for backing storage.
class LevelDbAppNotificationStorage : public AppNotificationStorage {
public:
explicit LevelDbAppNotificationStorage(const FilePath& path);
virtual ~LevelDbAppNotificationStorage();
// Implementing the AppNotificationStorage interface.
virtual bool GetExtensionIds(std::set<std::string>* result) OVERRIDE;
virtual bool Get(const std::string& extension_id,
AppNotificationList* result) OVERRIDE;
virtual bool Set(const std::string& extension_id,
const AppNotificationList& list) OVERRIDE;
virtual bool Delete(const std::string& extension_id) OVERRIDE;
private:
// If |db_| is NULL, attempt to open it. You should pass true for
// |create_if_missing| if you need to write data into the database and want to
// ensure it has been created after this call.
bool OpenDbIfNeeded(bool create_if_missing);
// The path where the database will reside.
FilePath path_;
// This should be used for all read operations on the db.
leveldb::ReadOptions read_options_;
// The leveldb database object - might be NULL if there was nothing found on
// disk at |path_| and OpenDbIfNeeded hasn't been called with
// create_if_missing=true yet.
scoped_ptr<leveldb::DB> db_;
DISALLOW_COPY_AND_ASSIGN(LevelDbAppNotificationStorage);
};
// static
AppNotificationStorage* AppNotificationStorage::Create(
const FilePath& path) {
return new LevelDbAppNotificationStorage(path);
}
AppNotificationStorage::~AppNotificationStorage() {}
namespace {
void AppNotificationListToJSON(const AppNotificationList& list,
std::string* result) {
ListValue list_value;
AppNotificationList::const_iterator i;
for (i = list.begin(); i != list.end(); ++i) {
DictionaryValue* dictionary = new DictionaryValue();
(*i)->ToDictionaryValue(dictionary);
list_value.Append(dictionary);
}
JSONWriter::Write(&list_value, false /* pretty_print */, result);
}
bool JSONToAppNotificationList(const std::string& json,
AppNotificationList* list) {
CHECK(list);
scoped_ptr<Value> value(JSONReader::Read(json,
false /* allow_trailing_comma */));
if (!value.get() || value->GetType() != Value::TYPE_LIST)
return false;
ListValue* list_value = static_cast<ListValue*>(value.get());
for (size_t i = 0; i < list_value->GetSize(); i++) {
Value* item = NULL;
if (!list_value->Get(i, &item) || !item ||
item->GetType() != Value::TYPE_DICTIONARY)
return false;
DictionaryValue* dictionary = static_cast<DictionaryValue*>(item);
AppNotification* notification =
AppNotification::FromDictionaryValue(*dictionary);
if (!notification)
return false;
list->push_back(linked_ptr<AppNotification>(notification));
}
return true;
}
void LogLevelDbError(tracked_objects::Location location,
const leveldb::Status& status) {
LOG(ERROR) << "AppNotificationStorage database error at "
<< location.ToString() << " status:" << status.ToString();
}
} // namespace
LevelDbAppNotificationStorage::LevelDbAppNotificationStorage(
const FilePath& path) : path_(path) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
read_options_.verify_checksums = true;
}
LevelDbAppNotificationStorage::~LevelDbAppNotificationStorage() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
}
bool LevelDbAppNotificationStorage::GetExtensionIds(
std::set<std::string>* result) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
CHECK(result);
if (!OpenDbIfNeeded(false))
return false;
if (!db_.get())
return true;
scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options_));
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
std::string key = iter->key().ToString();
if (Extension::IdIsValid(key))
result->insert(key);
}
return true;
}
bool LevelDbAppNotificationStorage::Get(const std::string& extension_id,
AppNotificationList* result) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
CHECK(result);
if (!OpenDbIfNeeded(false))
return false;
if (!db_.get())
return true;
std::string json;
leveldb::Status status = db_->Get(read_options_, extension_id, &json);
if (status.IsNotFound()) {
return true;
} else if (!status.ok()) {
LogLevelDbError(FROM_HERE, status);
return false;
}
return JSONToAppNotificationList(json, result);
}
bool LevelDbAppNotificationStorage::Set(const std::string& extension_id,
const AppNotificationList& list) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (!OpenDbIfNeeded(true))
return false;
CHECK(db_.get());
std::string json;
AppNotificationListToJSON(list, &json);
leveldb::Status status = db_->Put(leveldb::WriteOptions(),
extension_id,
json);
if (!status.ok()) {
LogLevelDbError(FROM_HERE, status);
return false;
}
return true;
}
bool LevelDbAppNotificationStorage::Delete(const std::string& extension_id) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (!OpenDbIfNeeded(false))
return false;
if (!db_.get())
return true; // The database doesn't exist on disk.
// Leveldb does not consider it an error if the key to delete isn't present,
// so we don't bother checking that first.
leveldb::Status status = db_->Delete(leveldb::WriteOptions(), extension_id);
if (!status.ok()) {
LogLevelDbError(FROM_HERE, status);
return false;
}
return true;
}
bool LevelDbAppNotificationStorage::OpenDbIfNeeded(bool create_if_missing) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (db_.get())
return true;
// If the file doesn't exist and the caller doesn't want it created, just
// return early.
if (!create_if_missing && !file_util::PathExists(path_))
return true;
#if defined(OS_POSIX)
std::string os_path = path_.value();
#elif defined(OS_WIN)
std::string os_path = base::SysWideToUTF8(path_.value());
#endif
leveldb::Options options;
options.create_if_missing = true;
leveldb::DB* db = NULL;
leveldb::Status status = leveldb::DB::Open(options, os_path, &db);
if (!status.ok()) {
LogLevelDbError(FROM_HERE, status);
return false;
}
db_.reset(db);
return true;
}