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.
5 #include "extensions/browser/api/storage/storage_api.h"
10 #include "base/bind.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/values.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "extensions/browser/api/storage/storage_frontend.h"
15 #include "extensions/browser/quota_service.h"
16 #include "extensions/common/api/storage.h"
18 namespace extensions
{
20 using content::BrowserThread
;
24 SettingsFunction::SettingsFunction()
25 : settings_namespace_(settings_namespace::INVALID
),
26 tried_restoring_storage_(false) {}
28 SettingsFunction::~SettingsFunction() {}
30 bool SettingsFunction::ShouldSkipQuotaLimiting() const {
31 // Only apply quota if this is for sync storage.
32 std::string settings_namespace_string
;
33 if (!args_
->GetString(0, &settings_namespace_string
)) {
34 // This should be EXTENSION_FUNCTION_VALIDATE(false) but there is no way
35 // to signify that from this function. It will be caught in Run().
38 return settings_namespace_string
!= "sync";
41 ExtensionFunction::ResponseAction
SettingsFunction::Run() {
42 std::string settings_namespace_string
;
43 EXTENSION_FUNCTION_VALIDATE(args_
->GetString(0, &settings_namespace_string
));
44 args_
->Remove(0, NULL
);
46 settings_namespace::FromString(settings_namespace_string
);
47 EXTENSION_FUNCTION_VALIDATE(settings_namespace_
!=
48 settings_namespace::INVALID
);
50 StorageFrontend
* frontend
= StorageFrontend::Get(browser_context());
51 if (!frontend
->IsStorageEnabled(settings_namespace_
)) {
52 return RespondNow(Error(
53 base::StringPrintf("\"%s\" is not available in this instance of Chrome",
54 settings_namespace_string
.c_str())));
57 observers_
= frontend
->GetObservers();
58 frontend
->RunWithStorage(
61 base::Bind(&SettingsFunction::AsyncRunWithStorage
, this));
62 return RespondLater();
65 void SettingsFunction::AsyncRunWithStorage(ValueStore
* storage
) {
66 ResponseValue response
= RunWithStorage(storage
);
67 BrowserThread::PostTask(
70 base::Bind(&SettingsFunction::Respond
, this, base::Passed(&response
)));
73 ExtensionFunction::ResponseValue
SettingsFunction::UseReadResult(
74 ValueStore::ReadResult result
,
75 ValueStore
* storage
) {
76 if (result
->HasError())
77 return HandleError(result
->error(), storage
);
79 base::DictionaryValue
* dict
= new base::DictionaryValue();
80 dict
->Swap(&result
->settings());
81 return OneArgument(dict
);
84 ExtensionFunction::ResponseValue
SettingsFunction::UseWriteResult(
85 ValueStore::WriteResult result
,
86 ValueStore
* storage
) {
87 if (result
->HasError())
88 return HandleError(result
->error(), storage
);
90 if (!result
->changes().empty()) {
92 &SettingsObserver::OnSettingsChanged
,
95 ValueStoreChange::ToJson(result
->changes()));
101 ExtensionFunction::ResponseValue
SettingsFunction::HandleError(
102 const ValueStore::Error
& error
,
103 ValueStore
* storage
) {
104 // If the method failed due to corruption, and we haven't tried to fix it, we
105 // can try to restore the storage and re-run it. Otherwise, the method has
107 if (error
.code
== ValueStore::CORRUPTION
&& !tried_restoring_storage_
) {
108 tried_restoring_storage_
= true;
110 // If the corruption is on a particular key, try to restore that key and
112 if (error
.key
.get() && storage
->RestoreKey(*error
.key
))
113 return RunWithStorage(storage
);
115 // If the full database is corrupted, try to restore the whole thing and
117 if (storage
->Restore())
118 return RunWithStorage(storage
);
121 return Error(error
.message
);
124 // Concrete settings functions
128 // Adds all StringValues from a ListValue to a vector of strings.
129 void AddAllStringValues(const base::ListValue
& from
,
130 std::vector
<std::string
>* to
) {
132 std::string as_string
;
133 for (base::ListValue::const_iterator it
= from
.begin();
134 it
!= from
.end(); ++it
) {
135 if ((*it
)->GetAsString(&as_string
)) {
136 to
->push_back(as_string
);
141 // Gets the keys of a DictionaryValue.
142 std::vector
<std::string
> GetKeys(const base::DictionaryValue
& dict
) {
143 std::vector
<std::string
> keys
;
144 for (base::DictionaryValue::Iterator
it(dict
); !it
.IsAtEnd(); it
.Advance()) {
145 keys
.push_back(it
.key());
150 // Creates quota heuristics for settings modification.
151 void GetModificationQuotaLimitHeuristics(QuotaLimitHeuristics
* heuristics
) {
152 QuotaLimitHeuristic::Config longLimitConfig
= {
153 // See storage.json for current value.
154 core_api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR
,
155 base::TimeDelta::FromHours(1)
157 heuristics
->push_back(new QuotaService::TimedLimit(
159 new QuotaLimitHeuristic::SingletonBucketMapper(),
160 "MAX_WRITE_OPERATIONS_PER_HOUR"));
162 // A max of 10 operations per minute, sustained over 10 minutes.
163 QuotaLimitHeuristic::Config shortLimitConfig
= {
164 // See storage.json for current value.
165 core_api::storage::sync::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE
,
166 base::TimeDelta::FromMinutes(1)
168 heuristics
->push_back(new QuotaService::SustainedLimit(
169 base::TimeDelta::FromMinutes(10),
171 new QuotaLimitHeuristic::SingletonBucketMapper(),
172 "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE"));
177 ExtensionFunction::ResponseValue
StorageStorageAreaGetFunction::RunWithStorage(
178 ValueStore
* storage
) {
179 base::Value
* input
= NULL
;
180 if (!args_
->Get(0, &input
))
183 switch (input
->GetType()) {
184 case base::Value::TYPE_NULL
:
185 return UseReadResult(storage
->Get(), storage
);
187 case base::Value::TYPE_STRING
: {
188 std::string as_string
;
189 input
->GetAsString(&as_string
);
190 return UseReadResult(storage
->Get(as_string
), storage
);
193 case base::Value::TYPE_LIST
: {
194 std::vector
<std::string
> as_string_list
;
195 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
197 return UseReadResult(storage
->Get(as_string_list
), storage
);
200 case base::Value::TYPE_DICTIONARY
: {
201 base::DictionaryValue
* as_dict
=
202 static_cast<base::DictionaryValue
*>(input
);
203 ValueStore::ReadResult result
= storage
->Get(GetKeys(*as_dict
));
204 if (result
->HasError()) {
205 return UseReadResult(result
.Pass(), storage
);
208 base::DictionaryValue
* with_default_values
= as_dict
->DeepCopy();
209 with_default_values
->MergeDictionary(&result
->settings());
210 return UseReadResult(
211 ValueStore::MakeReadResult(make_scoped_ptr(with_default_values
)),
220 ExtensionFunction::ResponseValue
221 StorageStorageAreaGetBytesInUseFunction::RunWithStorage(ValueStore
* storage
) {
222 base::Value
* input
= NULL
;
223 if (!args_
->Get(0, &input
))
226 size_t bytes_in_use
= 0;
228 switch (input
->GetType()) {
229 case base::Value::TYPE_NULL
:
230 bytes_in_use
= storage
->GetBytesInUse();
233 case base::Value::TYPE_STRING
: {
234 std::string as_string
;
235 input
->GetAsString(&as_string
);
236 bytes_in_use
= storage
->GetBytesInUse(as_string
);
240 case base::Value::TYPE_LIST
: {
241 std::vector
<std::string
> as_string_list
;
242 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
244 bytes_in_use
= storage
->GetBytesInUse(as_string_list
);
253 new base::FundamentalValue(static_cast<int>(bytes_in_use
)));
256 ExtensionFunction::ResponseValue
StorageStorageAreaSetFunction::RunWithStorage(
257 ValueStore
* storage
) {
258 base::DictionaryValue
* input
= NULL
;
259 if (!args_
->GetDictionary(0, &input
))
261 return UseWriteResult(storage
->Set(ValueStore::DEFAULTS
, *input
), storage
);
264 void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics(
265 QuotaLimitHeuristics
* heuristics
) const {
266 GetModificationQuotaLimitHeuristics(heuristics
);
269 ExtensionFunction::ResponseValue
270 StorageStorageAreaRemoveFunction::RunWithStorage(ValueStore
* storage
) {
271 base::Value
* input
= NULL
;
272 if (!args_
->Get(0, &input
))
275 switch (input
->GetType()) {
276 case base::Value::TYPE_STRING
: {
277 std::string as_string
;
278 input
->GetAsString(&as_string
);
279 return UseWriteResult(storage
->Remove(as_string
), storage
);
282 case base::Value::TYPE_LIST
: {
283 std::vector
<std::string
> as_string_list
;
284 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
286 return UseWriteResult(storage
->Remove(as_string_list
), storage
);
294 void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics(
295 QuotaLimitHeuristics
* heuristics
) const {
296 GetModificationQuotaLimitHeuristics(heuristics
);
299 ExtensionFunction::ResponseValue
300 StorageStorageAreaClearFunction::RunWithStorage(ValueStore
* storage
) {
301 return UseWriteResult(storage
->Clear(), storage
);
304 void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics(
305 QuotaLimitHeuristics
* heuristics
) const {
306 GetModificationQuotaLimitHeuristics(heuristics
);
309 } // namespace extensions