1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/api/storage/settings_storage_quota_enforcer.h"
8 #include "base/json/json_writer.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/stringprintf.h"
13 #include "chrome/browser/value_store/value_store_util.h"
14 #include "chrome/common/extensions/api/extension_api.h"
16 namespace util = value_store_util;
18 namespace extensions {
22 // Resources there are a quota for.
29 // Allocates a setting in a record of total and per-setting usage.
31 const std::string& key,
34 std::map<std::string, size_t>* used_per_setting) {
35 // Calculate the setting size based on its JSON serialization size.
36 // TODO(kalman): Does this work with different encodings?
37 // TODO(kalman): This is duplicating work that the leveldb delegate
38 // implementation is about to do, and it would be nice to avoid this.
39 std::string value_as_json;
40 base::JSONWriter::Write(&value, &value_as_json);
41 size_t new_size = key.size() + value_as_json.size();
42 size_t existing_size = (*used_per_setting)[key];
44 *used_total += (new_size - existing_size);
45 (*used_per_setting)[key] = new_size;
48 // Frees the allocation of a setting in a record of total and per-setting usage.
51 std::map<std::string, size_t>* used_per_setting,
52 const std::string& key) {
53 *used_total -= (*used_per_setting)[key];
54 used_per_setting->erase(key);
57 scoped_ptr<ValueStore::Error> QuotaExceededError(Resource resource,
58 scoped_ptr<std::string> key) {
59 const char* name = NULL;
60 // TODO(kalman): These hisograms are both silly and untracked. Fix.
64 UMA_HISTOGRAM_COUNTS_100(
65 "Extensions.SettingsQuotaExceeded.TotalBytes", 1);
67 case QUOTA_BYTES_PER_ITEM:
68 name = "QUOTA_BYTES_PER_ITEM";
69 UMA_HISTOGRAM_COUNTS_100(
70 "Extensions.SettingsQuotaExceeded.BytesPerSetting", 1);
74 UMA_HISTOGRAM_COUNTS_100(
75 "Extensions.SettingsQuotaExceeded.KeyCount", 1);
79 return make_scoped_ptr(new ValueStore::Error(
80 ValueStore::QUOTA_EXCEEDED,
81 base::StringPrintf("%s quota exceeded", name),
87 SettingsStorageQuotaEnforcer::SettingsStorageQuotaEnforcer(
88 const Limits& limits, ValueStore* delegate)
89 : limits_(limits), delegate_(delegate), used_total_(0) {
90 ReadResult maybe_settings = delegate_->Get();
91 if (maybe_settings->HasError()) {
92 LOG(WARNING) << "Failed to get initial settings for quota: " <<
93 maybe_settings->error().message;
97 for (base::DictionaryValue::Iterator it(maybe_settings->settings());
98 !it.IsAtEnd(); it.Advance()) {
99 Allocate(it.key(), it.value(), &used_total_, &used_per_setting_);
103 SettingsStorageQuotaEnforcer::~SettingsStorageQuotaEnforcer() {}
105 size_t SettingsStorageQuotaEnforcer::GetBytesInUse(const std::string& key) {
106 std::map<std::string, size_t>::iterator maybe_used =
107 used_per_setting_.find(key);
108 return maybe_used == used_per_setting_.end() ? 0u : maybe_used->second;
111 size_t SettingsStorageQuotaEnforcer::GetBytesInUse(
112 const std::vector<std::string>& keys) {
114 for (std::vector<std::string>::const_iterator it = keys.begin();
115 it != keys.end(); ++it) {
116 used += GetBytesInUse(*it);
121 size_t SettingsStorageQuotaEnforcer::GetBytesInUse() {
122 // All ValueStore implementations rely on GetBytesInUse being
127 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
128 const std::string& key) {
129 return delegate_->Get(key);
132 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
133 const std::vector<std::string>& keys) {
134 return delegate_->Get(keys);
137 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get() {
138 return delegate_->Get();
141 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set(
142 WriteOptions options, const std::string& key, const Value& value) {
143 size_t new_used_total = used_total_;
144 std::map<std::string, size_t> new_used_per_setting = used_per_setting_;
145 Allocate(key, value, &new_used_total, &new_used_per_setting);
147 if (!(options & IGNORE_QUOTA)) {
148 if (new_used_total > limits_.quota_bytes) {
149 return MakeWriteResult(
150 QuotaExceededError(QUOTA_BYTES, util::NewKey(key)));
152 if (new_used_per_setting[key] > limits_.quota_bytes_per_item) {
153 return MakeWriteResult(
154 QuotaExceededError(QUOTA_BYTES_PER_ITEM, util::NewKey(key)));
156 if (new_used_per_setting.size() > limits_.max_items)
157 return MakeWriteResult(QuotaExceededError(MAX_ITEMS, util::NewKey(key)));
160 WriteResult result = delegate_->Set(options, key, value);
161 if (result->HasError()) {
162 return result.Pass();
165 used_total_ = new_used_total;
166 used_per_setting_.swap(new_used_per_setting);
167 return result.Pass();
170 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set(
171 WriteOptions options, const base::DictionaryValue& values) {
172 size_t new_used_total = used_total_;
173 std::map<std::string, size_t> new_used_per_setting = used_per_setting_;
174 for (base::DictionaryValue::Iterator it(values); !it.IsAtEnd();
176 Allocate(it.key(), it.value(), &new_used_total, &new_used_per_setting);
178 if (!(options & IGNORE_QUOTA) &&
179 new_used_per_setting[it.key()] > limits_.quota_bytes_per_item) {
180 return MakeWriteResult(
181 QuotaExceededError(QUOTA_BYTES_PER_ITEM, util::NewKey(it.key())));
185 if (!(options & IGNORE_QUOTA)) {
186 if (new_used_total > limits_.quota_bytes)
187 return MakeWriteResult(QuotaExceededError(QUOTA_BYTES, util::NoKey()));
188 if (new_used_per_setting.size() > limits_.max_items)
189 return MakeWriteResult(QuotaExceededError(MAX_ITEMS, util::NoKey()));
192 WriteResult result = delegate_->Set(options, values);
193 if (result->HasError()) {
194 return result.Pass();
197 used_total_ = new_used_total;
198 used_per_setting_ = new_used_per_setting;
199 return result.Pass();
202 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove(
203 const std::string& key) {
204 WriteResult result = delegate_->Remove(key);
205 if (result->HasError()) {
206 return result.Pass();
208 Free(&used_total_, &used_per_setting_, key);
209 return result.Pass();
212 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove(
213 const std::vector<std::string>& keys) {
214 WriteResult result = delegate_->Remove(keys);
215 if (result->HasError()) {
216 return result.Pass();
219 for (std::vector<std::string>::const_iterator it = keys.begin();
220 it != keys.end(); ++it) {
221 Free(&used_total_, &used_per_setting_, *it);
223 return result.Pass();
226 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Clear() {
227 WriteResult result = delegate_->Clear();
228 if (result->HasError()) {
229 return result.Pass();
232 while (!used_per_setting_.empty()) {
233 Free(&used_total_, &used_per_setting_, used_per_setting_.begin()->first);
235 return result.Pass();
238 } // namespace extensions