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 // See storage.json for the current value of these limits.
153 QuotaLimitHeuristic::Config short_limit_config
= {
154 core_api::storage::sync::MAX_WRITE_OPERATIONS_PER_MINUTE
,
155 base::TimeDelta::FromMinutes(1)};
156 QuotaLimitHeuristic::Config long_limit_config
= {
157 core_api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR
,
158 base::TimeDelta::FromHours(1)};
159 heuristics
->push_back(new QuotaService::TimedLimit(
160 short_limit_config
, new QuotaLimitHeuristic::SingletonBucketMapper(),
161 "MAX_WRITE_OPERATIONS_PER_MINUTE"));
162 heuristics
->push_back(new QuotaService::TimedLimit(
163 long_limit_config
, new QuotaLimitHeuristic::SingletonBucketMapper(),
164 "MAX_WRITE_OPERATIONS_PER_HOUR"));
169 ExtensionFunction::ResponseValue
StorageStorageAreaGetFunction::RunWithStorage(
170 ValueStore
* storage
) {
171 base::Value
* input
= NULL
;
172 if (!args_
->Get(0, &input
))
175 switch (input
->GetType()) {
176 case base::Value::TYPE_NULL
:
177 return UseReadResult(storage
->Get(), storage
);
179 case base::Value::TYPE_STRING
: {
180 std::string as_string
;
181 input
->GetAsString(&as_string
);
182 return UseReadResult(storage
->Get(as_string
), storage
);
185 case base::Value::TYPE_LIST
: {
186 std::vector
<std::string
> as_string_list
;
187 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
189 return UseReadResult(storage
->Get(as_string_list
), storage
);
192 case base::Value::TYPE_DICTIONARY
: {
193 base::DictionaryValue
* as_dict
=
194 static_cast<base::DictionaryValue
*>(input
);
195 ValueStore::ReadResult result
= storage
->Get(GetKeys(*as_dict
));
196 if (result
->HasError()) {
197 return UseReadResult(result
.Pass(), storage
);
200 base::DictionaryValue
* with_default_values
= as_dict
->DeepCopy();
201 with_default_values
->MergeDictionary(&result
->settings());
202 return UseReadResult(
203 ValueStore::MakeReadResult(make_scoped_ptr(with_default_values
)),
212 ExtensionFunction::ResponseValue
213 StorageStorageAreaGetBytesInUseFunction::RunWithStorage(ValueStore
* storage
) {
214 base::Value
* input
= NULL
;
215 if (!args_
->Get(0, &input
))
218 size_t bytes_in_use
= 0;
220 switch (input
->GetType()) {
221 case base::Value::TYPE_NULL
:
222 bytes_in_use
= storage
->GetBytesInUse();
225 case base::Value::TYPE_STRING
: {
226 std::string as_string
;
227 input
->GetAsString(&as_string
);
228 bytes_in_use
= storage
->GetBytesInUse(as_string
);
232 case base::Value::TYPE_LIST
: {
233 std::vector
<std::string
> as_string_list
;
234 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
236 bytes_in_use
= storage
->GetBytesInUse(as_string_list
);
245 new base::FundamentalValue(static_cast<int>(bytes_in_use
)));
248 ExtensionFunction::ResponseValue
StorageStorageAreaSetFunction::RunWithStorage(
249 ValueStore
* storage
) {
250 base::DictionaryValue
* input
= NULL
;
251 if (!args_
->GetDictionary(0, &input
))
253 return UseWriteResult(storage
->Set(ValueStore::DEFAULTS
, *input
), storage
);
256 void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics(
257 QuotaLimitHeuristics
* heuristics
) const {
258 GetModificationQuotaLimitHeuristics(heuristics
);
261 ExtensionFunction::ResponseValue
262 StorageStorageAreaRemoveFunction::RunWithStorage(ValueStore
* storage
) {
263 base::Value
* input
= NULL
;
264 if (!args_
->Get(0, &input
))
267 switch (input
->GetType()) {
268 case base::Value::TYPE_STRING
: {
269 std::string as_string
;
270 input
->GetAsString(&as_string
);
271 return UseWriteResult(storage
->Remove(as_string
), storage
);
274 case base::Value::TYPE_LIST
: {
275 std::vector
<std::string
> as_string_list
;
276 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
278 return UseWriteResult(storage
->Remove(as_string_list
), storage
);
286 void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics(
287 QuotaLimitHeuristics
* heuristics
) const {
288 GetModificationQuotaLimitHeuristics(heuristics
);
291 ExtensionFunction::ResponseValue
292 StorageStorageAreaClearFunction::RunWithStorage(ValueStore
* storage
) {
293 return UseWriteResult(storage
->Clear(), storage
);
296 void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics(
297 QuotaLimitHeuristics
* heuristics
) const {
298 GetModificationQuotaLimitHeuristics(heuristics
);
301 } // namespace extensions