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()) {
91 observers_
->Notify(FROM_HERE
, &SettingsObserver::OnSettingsChanged
,
92 extension_id(), settings_namespace_
,
93 ValueStoreChange::ToJson(result
->changes()));
99 ExtensionFunction::ResponseValue
SettingsFunction::HandleError(
100 const ValueStore::Error
& error
,
101 ValueStore
* storage
) {
102 // If the method failed due to corruption, and we haven't tried to fix it, we
103 // can try to restore the storage and re-run it. Otherwise, the method has
105 if (error
.code
== ValueStore::CORRUPTION
&& !tried_restoring_storage_
) {
106 tried_restoring_storage_
= true;
108 // If the corruption is on a particular key, try to restore that key and
110 if (error
.key
.get() && storage
->RestoreKey(*error
.key
))
111 return RunWithStorage(storage
);
113 // If the full database is corrupted, try to restore the whole thing and
115 if (storage
->Restore())
116 return RunWithStorage(storage
);
119 return Error(error
.message
);
122 // Concrete settings functions
126 // Adds all StringValues from a ListValue to a vector of strings.
127 void AddAllStringValues(const base::ListValue
& from
,
128 std::vector
<std::string
>* to
) {
130 std::string as_string
;
131 for (base::ListValue::const_iterator it
= from
.begin();
132 it
!= from
.end(); ++it
) {
133 if ((*it
)->GetAsString(&as_string
)) {
134 to
->push_back(as_string
);
139 // Gets the keys of a DictionaryValue.
140 std::vector
<std::string
> GetKeys(const base::DictionaryValue
& dict
) {
141 std::vector
<std::string
> keys
;
142 for (base::DictionaryValue::Iterator
it(dict
); !it
.IsAtEnd(); it
.Advance()) {
143 keys
.push_back(it
.key());
148 // Creates quota heuristics for settings modification.
149 void GetModificationQuotaLimitHeuristics(QuotaLimitHeuristics
* heuristics
) {
150 // See storage.json for the current value of these limits.
151 QuotaLimitHeuristic::Config short_limit_config
= {
152 api::storage::sync::MAX_WRITE_OPERATIONS_PER_MINUTE
,
153 base::TimeDelta::FromMinutes(1)};
154 QuotaLimitHeuristic::Config long_limit_config
= {
155 api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR
,
156 base::TimeDelta::FromHours(1)};
157 heuristics
->push_back(new QuotaService::TimedLimit(
158 short_limit_config
, new QuotaLimitHeuristic::SingletonBucketMapper(),
159 "MAX_WRITE_OPERATIONS_PER_MINUTE"));
160 heuristics
->push_back(new QuotaService::TimedLimit(
161 long_limit_config
, new QuotaLimitHeuristic::SingletonBucketMapper(),
162 "MAX_WRITE_OPERATIONS_PER_HOUR"));
167 ExtensionFunction::ResponseValue
StorageStorageAreaGetFunction::RunWithStorage(
168 ValueStore
* storage
) {
169 base::Value
* input
= NULL
;
170 if (!args_
->Get(0, &input
))
173 switch (input
->GetType()) {
174 case base::Value::TYPE_NULL
:
175 return UseReadResult(storage
->Get(), storage
);
177 case base::Value::TYPE_STRING
: {
178 std::string as_string
;
179 input
->GetAsString(&as_string
);
180 return UseReadResult(storage
->Get(as_string
), storage
);
183 case base::Value::TYPE_LIST
: {
184 std::vector
<std::string
> as_string_list
;
185 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
187 return UseReadResult(storage
->Get(as_string_list
), storage
);
190 case base::Value::TYPE_DICTIONARY
: {
191 base::DictionaryValue
* as_dict
=
192 static_cast<base::DictionaryValue
*>(input
);
193 ValueStore::ReadResult result
= storage
->Get(GetKeys(*as_dict
));
194 if (result
->HasError()) {
195 return UseReadResult(result
.Pass(), storage
);
198 base::DictionaryValue
* with_default_values
= as_dict
->DeepCopy();
199 with_default_values
->MergeDictionary(&result
->settings());
200 return UseReadResult(
201 ValueStore::MakeReadResult(make_scoped_ptr(with_default_values
)),
210 ExtensionFunction::ResponseValue
211 StorageStorageAreaGetBytesInUseFunction::RunWithStorage(ValueStore
* storage
) {
212 base::Value
* input
= NULL
;
213 if (!args_
->Get(0, &input
))
216 size_t bytes_in_use
= 0;
218 switch (input
->GetType()) {
219 case base::Value::TYPE_NULL
:
220 bytes_in_use
= storage
->GetBytesInUse();
223 case base::Value::TYPE_STRING
: {
224 std::string as_string
;
225 input
->GetAsString(&as_string
);
226 bytes_in_use
= storage
->GetBytesInUse(as_string
);
230 case base::Value::TYPE_LIST
: {
231 std::vector
<std::string
> as_string_list
;
232 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
234 bytes_in_use
= storage
->GetBytesInUse(as_string_list
);
243 new base::FundamentalValue(static_cast<int>(bytes_in_use
)));
246 ExtensionFunction::ResponseValue
StorageStorageAreaSetFunction::RunWithStorage(
247 ValueStore
* storage
) {
248 base::DictionaryValue
* input
= NULL
;
249 if (!args_
->GetDictionary(0, &input
))
251 return UseWriteResult(storage
->Set(ValueStore::DEFAULTS
, *input
), storage
);
254 void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics(
255 QuotaLimitHeuristics
* heuristics
) const {
256 GetModificationQuotaLimitHeuristics(heuristics
);
259 ExtensionFunction::ResponseValue
260 StorageStorageAreaRemoveFunction::RunWithStorage(ValueStore
* storage
) {
261 base::Value
* input
= NULL
;
262 if (!args_
->Get(0, &input
))
265 switch (input
->GetType()) {
266 case base::Value::TYPE_STRING
: {
267 std::string as_string
;
268 input
->GetAsString(&as_string
);
269 return UseWriteResult(storage
->Remove(as_string
), storage
);
272 case base::Value::TYPE_LIST
: {
273 std::vector
<std::string
> as_string_list
;
274 AddAllStringValues(*static_cast<base::ListValue
*>(input
),
276 return UseWriteResult(storage
->Remove(as_string_list
), storage
);
284 void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics(
285 QuotaLimitHeuristics
* heuristics
) const {
286 GetModificationQuotaLimitHeuristics(heuristics
);
289 ExtensionFunction::ResponseValue
290 StorageStorageAreaClearFunction::RunWithStorage(ValueStore
* storage
) {
291 return UseWriteResult(storage
->Clear(), storage
);
294 void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics(
295 QuotaLimitHeuristics
* heuristics
) const {
296 GetModificationQuotaLimitHeuristics(heuristics
);
299 } // namespace extensions