This source file includes following definitions.
- Allocate
- Free
- QuotaExceededError
- used_total_
- GetBytesInUse
- GetBytesInUse
- GetBytesInUse
- Get
- Get
- Get
- Remove
- Remove
- Clear
- Restore
- RestoreKey
- CalculateUsage
#include "extensions/browser/api/storage/settings_storage_quota_enforcer.h"
#include "base/bind.h"
#include "base/json/json_writer.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "extensions/browser/value_store/value_store_util.h"
#include "extensions/common/extension_api.h"
namespace util = value_store_util;
namespace extensions {
namespace {
enum Resource {
QUOTA_BYTES,
QUOTA_BYTES_PER_ITEM,
MAX_ITEMS
};
void Allocate(
const std::string& key,
const base::Value& value,
size_t* used_total,
std::map<std::string, size_t>* used_per_setting) {
std::string value_as_json;
base::JSONWriter::Write(&value, &value_as_json);
size_t new_size = key.size() + value_as_json.size();
size_t existing_size = (*used_per_setting)[key];
*used_total += (new_size - existing_size);
(*used_per_setting)[key] = new_size;
}
void Free(
size_t* used_total,
std::map<std::string, size_t>* used_per_setting,
const std::string& key) {
*used_total -= (*used_per_setting)[key];
used_per_setting->erase(key);
}
scoped_ptr<ValueStore::Error> QuotaExceededError(Resource resource,
scoped_ptr<std::string> key) {
const char* name = NULL;
switch (resource) {
case QUOTA_BYTES:
name = "QUOTA_BYTES";
UMA_HISTOGRAM_COUNTS_100(
"Extensions.SettingsQuotaExceeded.TotalBytes", 1);
break;
case QUOTA_BYTES_PER_ITEM:
name = "QUOTA_BYTES_PER_ITEM";
UMA_HISTOGRAM_COUNTS_100(
"Extensions.SettingsQuotaExceeded.BytesPerSetting", 1);
break;
case MAX_ITEMS:
name = "MAX_ITEMS";
UMA_HISTOGRAM_COUNTS_100(
"Extensions.SettingsQuotaExceeded.KeyCount", 1);
break;
}
CHECK(name);
return make_scoped_ptr(new ValueStore::Error(
ValueStore::QUOTA_EXCEEDED,
base::StringPrintf("%s quota exceeded", name),
key.Pass()));
}
}
SettingsStorageQuotaEnforcer::SettingsStorageQuotaEnforcer(
const Limits& limits, ValueStore* delegate)
: limits_(limits), delegate_(delegate), used_total_(0) {
CalculateUsage();
}
SettingsStorageQuotaEnforcer::~SettingsStorageQuotaEnforcer() {}
size_t SettingsStorageQuotaEnforcer::GetBytesInUse(const std::string& key) {
std::map<std::string, size_t>::iterator maybe_used =
used_per_setting_.find(key);
return maybe_used == used_per_setting_.end() ? 0u : maybe_used->second;
}
size_t SettingsStorageQuotaEnforcer::GetBytesInUse(
const std::vector<std::string>& keys) {
size_t used = 0;
for (std::vector<std::string>::const_iterator it = keys.begin();
it != keys.end(); ++it) {
used += GetBytesInUse(*it);
}
return used;
}
size_t SettingsStorageQuotaEnforcer::GetBytesInUse() {
return used_total_;
}
ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
const std::string& key) {
return delegate_->Get(key);
}
ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
const std::vector<std::string>& keys) {
return delegate_->Get(keys);
}
ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get() {
return delegate_->Get();
}
ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set(
WriteOptions options, const std::string& key, const base::Value& value) {
size_t new_used_total = used_total_;
std::map<std::string, size_t> new_used_per_setting = used_per_setting_;
Allocate(key, value, &new_used_total, &new_used_per_setting);
if (!(options & IGNORE_QUOTA)) {
if (new_used_total > limits_.quota_bytes) {
return MakeWriteResult(
QuotaExceededError(QUOTA_BYTES, util::NewKey(key)));
}
if (new_used_per_setting[key] > limits_.quota_bytes_per_item) {
return MakeWriteResult(
QuotaExceededError(QUOTA_BYTES_PER_ITEM, util::NewKey(key)));
}
if (new_used_per_setting.size() > limits_.max_items)
return MakeWriteResult(QuotaExceededError(MAX_ITEMS, util::NewKey(key)));
}
WriteResult result = delegate_->Set(options, key, value);
if (result->HasError()) {
return result.Pass();
}
used_total_ = new_used_total;
used_per_setting_.swap(new_used_per_setting);
return result.Pass();
}
ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set(
WriteOptions options, const base::DictionaryValue& values) {
size_t new_used_total = used_total_;
std::map<std::string, size_t> new_used_per_setting = used_per_setting_;
for (base::DictionaryValue::Iterator it(values); !it.IsAtEnd();
it.Advance()) {
Allocate(it.key(), it.value(), &new_used_total, &new_used_per_setting);
if (!(options & IGNORE_QUOTA) &&
new_used_per_setting[it.key()] > limits_.quota_bytes_per_item) {
return MakeWriteResult(
QuotaExceededError(QUOTA_BYTES_PER_ITEM, util::NewKey(it.key())));
}
}
if (!(options & IGNORE_QUOTA)) {
if (new_used_total > limits_.quota_bytes)
return MakeWriteResult(QuotaExceededError(QUOTA_BYTES, util::NoKey()));
if (new_used_per_setting.size() > limits_.max_items)
return MakeWriteResult(QuotaExceededError(MAX_ITEMS, util::NoKey()));
}
WriteResult result = delegate_->Set(options, values);
if (result->HasError()) {
return result.Pass();
}
used_total_ = new_used_total;
used_per_setting_ = new_used_per_setting;
return result.Pass();
}
ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove(
const std::string& key) {
WriteResult result = delegate_->Remove(key);
if (result->HasError()) {
return result.Pass();
}
Free(&used_total_, &used_per_setting_, key);
return result.Pass();
}
ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove(
const std::vector<std::string>& keys) {
WriteResult result = delegate_->Remove(keys);
if (result->HasError()) {
return result.Pass();
}
for (std::vector<std::string>::const_iterator it = keys.begin();
it != keys.end(); ++it) {
Free(&used_total_, &used_per_setting_, *it);
}
return result.Pass();
}
ValueStore::WriteResult SettingsStorageQuotaEnforcer::Clear() {
WriteResult result = delegate_->Clear();
if (result->HasError()) {
return result.Pass();
}
used_per_setting_.clear();
used_total_ = 0;
return result.Pass();
}
bool SettingsStorageQuotaEnforcer::Restore() {
if (!delegate_->Restore()) {
used_per_setting_.clear();
used_total_ = 0u;
return false;
}
CalculateUsage();
return true;
}
bool SettingsStorageQuotaEnforcer::RestoreKey(const std::string& key) {
if (!delegate_->RestoreKey(key))
return false;
ReadResult result = Get(key);
if (!result->settings().HasKey(key) && ContainsKey(used_per_setting_, key))
Free(&used_total_, &used_per_setting_, key);
return true;
}
void SettingsStorageQuotaEnforcer::CalculateUsage() {
ReadResult maybe_settings = delegate_->Get();
if (maybe_settings->HasError()) {
if (maybe_settings->error().code == ValueStore::CORRUPTION &&
delegate_->Restore()) {
maybe_settings = delegate_->Get();
} else {
LOG(WARNING) << "Failed to get settings for quota:"
<< maybe_settings->error().message;
return;
}
}
for (base::DictionaryValue::Iterator it(maybe_settings->settings());
!it.IsAtEnd();
it.Advance()) {
Allocate(it.key(), it.value(), &used_total_, &used_per_setting_);
}
}
}