- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / storage / settings_storage_quota_enforcer.cc
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.
4
5 #include "chrome/browser/extensions/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/strings/stringprintf.h"
13 #include "chrome/browser/value_store/value_store_util.h"
14 #include "chrome/common/extensions/api/extension_api.h"
15
16 namespace util = value_store_util;
17
18 namespace extensions {
19
20 namespace {
21
22 // Resources there are a quota for.
23 enum Resource {
24   QUOTA_BYTES,
25   QUOTA_BYTES_PER_ITEM,
26   MAX_ITEMS
27 };
28
29 // Allocates a setting in a record of total and per-setting usage.
30 void Allocate(
31     const std::string& key,
32     const Value& value,
33     size_t* used_total,
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];
43
44   *used_total += (new_size - existing_size);
45   (*used_per_setting)[key] = new_size;
46 }
47
48 // Frees the allocation of a setting in a record of total and per-setting usage.
49 void Free(
50     size_t* used_total,
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);
55 }
56
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.
61   switch (resource) {
62     case QUOTA_BYTES:
63       name = "QUOTA_BYTES";
64       UMA_HISTOGRAM_COUNTS_100(
65           "Extensions.SettingsQuotaExceeded.TotalBytes", 1);
66       break;
67     case QUOTA_BYTES_PER_ITEM:
68       name = "QUOTA_BYTES_PER_ITEM";
69       UMA_HISTOGRAM_COUNTS_100(
70           "Extensions.SettingsQuotaExceeded.BytesPerSetting", 1);
71       break;
72     case MAX_ITEMS:
73       name = "MAX_ITEMS";
74       UMA_HISTOGRAM_COUNTS_100(
75           "Extensions.SettingsQuotaExceeded.KeyCount", 1);
76       break;
77   }
78   CHECK(name);
79   return make_scoped_ptr(new ValueStore::Error(
80       ValueStore::QUOTA_EXCEEDED,
81       base::StringPrintf("%s quota exceeded", name),
82       key.Pass()));
83 }
84
85 }  // namespace
86
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;
94     return;
95   }
96
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_);
100   }
101 }
102
103 SettingsStorageQuotaEnforcer::~SettingsStorageQuotaEnforcer() {}
104
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;
109 }
110
111 size_t SettingsStorageQuotaEnforcer::GetBytesInUse(
112     const std::vector<std::string>& keys) {
113   size_t used = 0;
114   for (std::vector<std::string>::const_iterator it = keys.begin();
115       it != keys.end(); ++it) {
116     used += GetBytesInUse(*it);
117   }
118   return used;
119 }
120
121 size_t SettingsStorageQuotaEnforcer::GetBytesInUse() {
122   // All ValueStore implementations rely on GetBytesInUse being
123   // implemented here.
124   return used_total_;
125 }
126
127 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
128     const std::string& key) {
129   return delegate_->Get(key);
130 }
131
132 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get(
133     const std::vector<std::string>& keys) {
134   return delegate_->Get(keys);
135 }
136
137 ValueStore::ReadResult SettingsStorageQuotaEnforcer::Get() {
138   return delegate_->Get();
139 }
140
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);
146
147   if (!(options & IGNORE_QUOTA)) {
148     if (new_used_total > limits_.quota_bytes) {
149       return MakeWriteResult(
150           QuotaExceededError(QUOTA_BYTES, util::NewKey(key)));
151     }
152     if (new_used_per_setting[key] > limits_.quota_bytes_per_item) {
153       return MakeWriteResult(
154           QuotaExceededError(QUOTA_BYTES_PER_ITEM, util::NewKey(key)));
155     }
156     if (new_used_per_setting.size() > limits_.max_items)
157       return MakeWriteResult(QuotaExceededError(MAX_ITEMS, util::NewKey(key)));
158   }
159
160   WriteResult result = delegate_->Set(options, key, value);
161   if (result->HasError()) {
162     return result.Pass();
163   }
164
165   used_total_ = new_used_total;
166   used_per_setting_.swap(new_used_per_setting);
167   return result.Pass();
168 }
169
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();
175        it.Advance()) {
176     Allocate(it.key(), it.value(), &new_used_total, &new_used_per_setting);
177
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())));
182     }
183   }
184
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()));
190   }
191
192   WriteResult result = delegate_->Set(options, values);
193   if (result->HasError()) {
194     return result.Pass();
195   }
196
197   used_total_ = new_used_total;
198   used_per_setting_ = new_used_per_setting;
199   return result.Pass();
200 }
201
202 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Remove(
203     const std::string& key) {
204   WriteResult result = delegate_->Remove(key);
205   if (result->HasError()) {
206     return result.Pass();
207   }
208   Free(&used_total_, &used_per_setting_, key);
209   return result.Pass();
210 }
211
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();
217   }
218
219   for (std::vector<std::string>::const_iterator it = keys.begin();
220       it != keys.end(); ++it) {
221     Free(&used_total_, &used_per_setting_, *it);
222   }
223   return result.Pass();
224 }
225
226 ValueStore::WriteResult SettingsStorageQuotaEnforcer::Clear() {
227   WriteResult result = delegate_->Clear();
228   if (result->HasError()) {
229     return result.Pass();
230   }
231
232   while (!used_per_setting_.empty()) {
233     Free(&used_total_, &used_per_setting_, used_per_setting_.begin()->first);
234   }
235   return result.Pass();
236 }
237
238 }  // namespace extensions