• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/api/storage/settings_storage_quota_enforcer.h"
6 
7 #include "base/bind.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/stl_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "extensions/browser/value_store/value_store_util.h"
15 #include "extensions/common/extension_api.h"
16 
17 namespace util = value_store_util;
18 
19 namespace extensions {
20 
21 namespace {
22 
23 // Resources there are a quota for.
24 enum Resource {
25   QUOTA_BYTES,
26   QUOTA_BYTES_PER_ITEM,
27   MAX_ITEMS
28 };
29 
30 // Allocates a setting in a record of total and per-setting usage.
Allocate(const std::string & key,const base::Value & value,size_t * used_total,std::map<std::string,size_t> * used_per_setting)31 void Allocate(
32     const std::string& key,
33     const base::Value& value,
34     size_t* used_total,
35     std::map<std::string, size_t>* used_per_setting) {
36   // Calculate the setting size based on its JSON serialization size.
37   // TODO(kalman): Does this work with different encodings?
38   // TODO(kalman): This is duplicating work that the leveldb delegate
39   // implementation is about to do, and it would be nice to avoid this.
40   std::string value_as_json;
41   base::JSONWriter::Write(&value, &value_as_json);
42   size_t new_size = key.size() + value_as_json.size();
43   size_t existing_size = (*used_per_setting)[key];
44 
45   *used_total += (new_size - existing_size);
46   (*used_per_setting)[key] = new_size;
47 }
48 
49 // Frees the allocation of a setting in a record of total and per-setting usage.
Free(size_t * used_total,std::map<std::string,size_t> * used_per_setting,const std::string & key)50 void Free(
51     size_t* used_total,
52     std::map<std::string, size_t>* used_per_setting,
53     const std::string& key) {
54   *used_total -= (*used_per_setting)[key];
55   used_per_setting->erase(key);
56 }
57 
QuotaExceededError(Resource resource,scoped_ptr<std::string> key)58 scoped_ptr<ValueStore::Error> QuotaExceededError(Resource resource,
59                                                  scoped_ptr<std::string> key) {
60   const char* name = NULL;
61   // TODO(kalman): These hisograms are both silly and untracked. Fix.
62   switch (resource) {
63     case QUOTA_BYTES:
64       name = "QUOTA_BYTES";
65       UMA_HISTOGRAM_COUNTS_100(
66           "Extensions.SettingsQuotaExceeded.TotalBytes", 1);
67       break;
68     case QUOTA_BYTES_PER_ITEM:
69       name = "QUOTA_BYTES_PER_ITEM";
70       UMA_HISTOGRAM_COUNTS_100(
71           "Extensions.SettingsQuotaExceeded.BytesPerSetting", 1);
72       break;
73     case MAX_ITEMS:
74       name = "MAX_ITEMS";
75       UMA_HISTOGRAM_COUNTS_100(
76           "Extensions.SettingsQuotaExceeded.KeyCount", 1);
77       break;
78   }
79   CHECK(name);
80   return make_scoped_ptr(new ValueStore::Error(
81       ValueStore::QUOTA_EXCEEDED,
82       base::StringPrintf("%s quota exceeded", name),
83       key.Pass()));
84 }
85 
86 }  // namespace
87 
SettingsStorageQuotaEnforcer(const Limits & limits,ValueStore * delegate)88 SettingsStorageQuotaEnforcer::SettingsStorageQuotaEnforcer(
89     const Limits& limits, ValueStore* delegate)
90     : limits_(limits), delegate_(delegate), used_total_(0) {
91   CalculateUsage();
92 }
93 
~SettingsStorageQuotaEnforcer()94 SettingsStorageQuotaEnforcer::~SettingsStorageQuotaEnforcer() {}
95 
GetBytesInUse(const std::string & key)96 size_t SettingsStorageQuotaEnforcer::GetBytesInUse(const std::string& key) {
97   std::map<std::string, size_t>::iterator maybe_used =
98       used_per_setting_.find(key);
99   return maybe_used == used_per_setting_.end() ? 0u : maybe_used->second;
100 }
101 
GetBytesInUse(const std::vector<std::string> & keys)102 size_t SettingsStorageQuotaEnforcer::GetBytesInUse(
103     const std::vector<std::string>& keys) {
104   size_t used = 0;
105   for (std::vector<std::string>::const_iterator it = keys.begin();
106       it != keys.end(); ++it) {
107     used += GetBytesInUse(*it);
108   }
109   return used;
110 }
111 
GetBytesInUse()112 size_t SettingsStorageQuotaEnforcer::GetBytesInUse() {
113   // All ValueStore implementations rely on GetBytesInUse being
114   // implemented here.
115   return used_total_;
116 }
117 
Get(const std::string & key)118 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
119     const std::string& key) {
120   return delegate_->Get(key);
121 }
122 
Get(const std::vector<std::string> & keys)123 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
124     const std::vector<std::string>& keys) {
125   return delegate_->Get(keys);
126 }
127 
Get()128 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get() {
129   return delegate_->Get();
130 }
131 
Set(WriteOptions options,const std::string & key,const base::Value & value)132 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set(
133     WriteOptions options, const std::string& key, const base::Value& value) {
134   size_t new_used_total = used_total_;
135   std::map<std::string, size_t> new_used_per_setting = used_per_setting_;
136   Allocate(key, value, &new_used_total, &new_used_per_setting);
137 
138   if (!(options & IGNORE_QUOTA)) {
139     if (new_used_total > limits_.quota_bytes) {
140       return MakeWriteResult(
141           QuotaExceededError(QUOTA_BYTES, util::NewKey(key)));
142     }
143     if (new_used_per_setting[key] > limits_.quota_bytes_per_item) {
144       return MakeWriteResult(
145           QuotaExceededError(QUOTA_BYTES_PER_ITEM, util::NewKey(key)));
146     }
147     if (new_used_per_setting.size() > limits_.max_items)
148       return MakeWriteResult(QuotaExceededError(MAX_ITEMS, util::NewKey(key)));
149   }
150 
151   WriteResult result = delegate_->Set(options, key, value);
152   if (result->HasError()) {
153     return result.Pass();
154   }
155 
156   used_total_ = new_used_total;
157   used_per_setting_.swap(new_used_per_setting);
158   return result.Pass();
159 }
160 
Set(WriteOptions options,const base::DictionaryValue & values)161 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Set(
162     WriteOptions options, const base::DictionaryValue& values) {
163   size_t new_used_total = used_total_;
164   std::map<std::string, size_t> new_used_per_setting = used_per_setting_;
165   for (base::DictionaryValue::Iterator it(values); !it.IsAtEnd();
166        it.Advance()) {
167     Allocate(it.key(), it.value(), &new_used_total, &new_used_per_setting);
168 
169     if (!(options & IGNORE_QUOTA) &&
170         new_used_per_setting[it.key()] > limits_.quota_bytes_per_item) {
171       return MakeWriteResult(
172           QuotaExceededError(QUOTA_BYTES_PER_ITEM, util::NewKey(it.key())));
173     }
174   }
175 
176   if (!(options & IGNORE_QUOTA)) {
177     if (new_used_total > limits_.quota_bytes)
178       return MakeWriteResult(QuotaExceededError(QUOTA_BYTES, util::NoKey()));
179     if (new_used_per_setting.size() > limits_.max_items)
180       return MakeWriteResult(QuotaExceededError(MAX_ITEMS, util::NoKey()));
181   }
182 
183   WriteResult result = delegate_->Set(options, values);
184   if (result->HasError()) {
185     return result.Pass();
186   }
187 
188   used_total_ = new_used_total;
189   used_per_setting_ = new_used_per_setting;
190   return result.Pass();
191 }
192 
Remove(const std::string & key)193 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove(
194     const std::string& key) {
195   WriteResult result = delegate_->Remove(key);
196   if (result->HasError()) {
197     return result.Pass();
198   }
199   Free(&used_total_, &used_per_setting_, key);
200   return result.Pass();
201 }
202 
Remove(const std::vector<std::string> & keys)203 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove(
204     const std::vector<std::string>& keys) {
205   WriteResult result = delegate_->Remove(keys);
206   if (result->HasError()) {
207     return result.Pass();
208   }
209 
210   for (std::vector<std::string>::const_iterator it = keys.begin();
211       it != keys.end(); ++it) {
212     Free(&used_total_, &used_per_setting_, *it);
213   }
214   return result.Pass();
215 }
216 
Clear()217 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Clear() {
218   WriteResult result = delegate_->Clear();
219   if (result->HasError()) {
220     return result.Pass();
221   }
222 
223   used_per_setting_.clear();
224   used_total_ = 0;
225   return result.Pass();
226 }
227 
Restore()228 bool SettingsStorageQuotaEnforcer::Restore() {
229   if (!delegate_->Restore()) {
230     // If we failed, we can't calculate the usage - that's okay, though, because
231     // next time we Restore() (if it succeeds) we will recalculate usage anyway.
232     // So reset storage counters now to free up resources.
233     used_per_setting_.clear();
234     used_total_ = 0u;
235     return false;
236   }
237   CalculateUsage();
238   return true;
239 }
240 
RestoreKey(const std::string & key)241 bool SettingsStorageQuotaEnforcer::RestoreKey(const std::string& key) {
242   if (!delegate_->RestoreKey(key))
243     return false;
244 
245   ReadResult result = Get(key);
246   // If the key was deleted as a result of the Restore() call, free it.
247   if (!result->settings().HasKey(key) && ContainsKey(used_per_setting_, key))
248     Free(&used_total_, &used_per_setting_, key);
249   return true;
250 }
251 
CalculateUsage()252 void SettingsStorageQuotaEnforcer::CalculateUsage() {
253   ReadResult maybe_settings = delegate_->Get();
254   if (maybe_settings->HasError()) {
255     // Try to restore the database if it's corrupt.
256     if (maybe_settings->error().code == ValueStore::CORRUPTION &&
257         delegate_->Restore()) {
258       maybe_settings = delegate_->Get();
259     } else {
260       LOG(WARNING) << "Failed to get settings for quota:"
261                    << maybe_settings->error().message;
262       return;
263     }
264   }
265 
266   for (base::DictionaryValue::Iterator it(maybe_settings->settings());
267        !it.IsAtEnd();
268        it.Advance()) {
269     Allocate(it.key(), it.value(), &used_total_, &used_per_setting_);
270   }
271 }
272 
273 }  // namespace extensions
274