storage_api.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/storage_api.h"
6
7#include <string>
8#include <vector>
9
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"
17
18namespace extensions {
19
20using content::BrowserThread;
21
22// SettingsFunction
23
24SettingsFunction::SettingsFunction()
25    : settings_namespace_(settings_namespace::INVALID),
26      tried_restoring_storage_(false) {}
27
28SettingsFunction::~SettingsFunction() {}
29
30bool 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 RunImpl().
36    return false;
37  }
38  return settings_namespace_string != "sync";
39}
40
41bool SettingsFunction::RunImpl() {
42  std::string settings_namespace_string;
43  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &settings_namespace_string));
44  args_->Remove(0, NULL);
45  settings_namespace_ =
46      settings_namespace::FromString(settings_namespace_string);
47  EXTENSION_FUNCTION_VALIDATE(
48      settings_namespace_ != settings_namespace::INVALID);
49
50  StorageFrontend* frontend = StorageFrontend::Get(browser_context());
51  if (!frontend->IsStorageEnabled(settings_namespace_)) {
52    error_ = base::StringPrintf(
53        "\"%s\" is not available in this instance of Chrome",
54        settings_namespace_string.c_str());
55    return false;
56  }
57
58  observers_ = frontend->GetObservers();
59  frontend->RunWithStorage(
60      GetExtension(),
61      settings_namespace_,
62      base::Bind(&SettingsFunction::AsyncRunWithStorage, this));
63  return true;
64}
65
66void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) {
67  bool success = RunWithStorage(storage);
68  BrowserThread::PostTask(
69      BrowserThread::UI,
70      FROM_HERE,
71      base::Bind(&SettingsFunction::SendResponse, this, success));
72}
73
74bool SettingsFunction::UseReadResult(ValueStore::ReadResult result,
75                                     ValueStore* storage) {
76  if (result->HasError())
77    return HandleError(result->error(), storage);
78
79  base::DictionaryValue* dict = new base::DictionaryValue();
80  dict->Swap(&result->settings());
81  SetResult(dict);
82  return true;
83}
84
85bool SettingsFunction::UseWriteResult(ValueStore::WriteResult result,
86                                      ValueStore* storage) {
87  if (result->HasError())
88    return HandleError(result->error(), storage);
89
90  if (!result->changes().empty()) {
91    observers_->Notify(
92        &SettingsObserver::OnSettingsChanged,
93        extension_id(),
94        settings_namespace_,
95        ValueStoreChange::ToJson(result->changes()));
96  }
97
98  return true;
99}
100
101bool SettingsFunction::HandleError(const ValueStore::Error& error,
102                                   ValueStore* storage) {
103  // If the method failed due to corruption, and we haven't tried to fix it, we
104  // can try to restore the storage and re-run it. Otherwise, the method has
105  // failed.
106  if (error.code == ValueStore::CORRUPTION && !tried_restoring_storage_) {
107    tried_restoring_storage_ = true;
108
109    // If the corruption is on a particular key, try to restore that key and
110    // re-run.
111    if (error.key.get() && storage->RestoreKey(*error.key))
112      return RunWithStorage(storage);
113
114    // If the full database is corrupted, try to restore the whole thing and
115    // re-run.
116    if (storage->Restore())
117      return RunWithStorage(storage);
118  }
119
120  error_ = error.message;
121  return false;
122}
123
124// Concrete settings functions
125
126namespace {
127
128// Adds all StringValues from a ListValue to a vector of strings.
129void AddAllStringValues(const base::ListValue& from,
130                        std::vector<std::string>* to) {
131  DCHECK(to->empty());
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);
137    }
138  }
139}
140
141// Gets the keys of a DictionaryValue.
142std::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());
146  }
147  return keys;
148}
149
150// Creates quota heuristics for settings modification.
151void 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)
156  };
157  heuristics->push_back(new QuotaService::TimedLimit(
158      longLimitConfig,
159      new QuotaLimitHeuristic::SingletonBucketMapper(),
160      "MAX_WRITE_OPERATIONS_PER_HOUR"));
161
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)
167  };
168  heuristics->push_back(new QuotaService::SustainedLimit(
169      base::TimeDelta::FromMinutes(10),
170      shortLimitConfig,
171      new QuotaLimitHeuristic::SingletonBucketMapper(),
172      "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE"));
173};
174
175}  // namespace
176
177bool StorageStorageAreaGetFunction::RunWithStorage(ValueStore* storage) {
178  base::Value* input = NULL;
179  EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input));
180
181  switch (input->GetType()) {
182    case base::Value::TYPE_NULL:
183      return UseReadResult(storage->Get(), storage);
184
185    case base::Value::TYPE_STRING: {
186      std::string as_string;
187      input->GetAsString(&as_string);
188      return UseReadResult(storage->Get(as_string), storage);
189    }
190
191    case base::Value::TYPE_LIST: {
192      std::vector<std::string> as_string_list;
193      AddAllStringValues(*static_cast<base::ListValue*>(input),
194                         &as_string_list);
195      return UseReadResult(storage->Get(as_string_list), storage);
196    }
197
198    case base::Value::TYPE_DICTIONARY: {
199      base::DictionaryValue* as_dict =
200          static_cast<base::DictionaryValue*>(input);
201      ValueStore::ReadResult result = storage->Get(GetKeys(*as_dict));
202      if (result->HasError()) {
203        return UseReadResult(result.Pass(), storage);
204      }
205
206      base::DictionaryValue* with_default_values = as_dict->DeepCopy();
207      with_default_values->MergeDictionary(&result->settings());
208      return UseReadResult(
209          ValueStore::MakeReadResult(make_scoped_ptr(with_default_values)),
210          storage);
211    }
212
213    default:
214      EXTENSION_FUNCTION_VALIDATE(false);
215      return false;
216  }
217}
218
219bool StorageStorageAreaGetBytesInUseFunction::RunWithStorage(
220    ValueStore* storage) {
221  base::Value* input = NULL;
222  EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input));
223
224  size_t bytes_in_use = 0;
225
226  switch (input->GetType()) {
227    case base::Value::TYPE_NULL:
228      bytes_in_use = storage->GetBytesInUse();
229      break;
230
231    case base::Value::TYPE_STRING: {
232      std::string as_string;
233      input->GetAsString(&as_string);
234      bytes_in_use = storage->GetBytesInUse(as_string);
235      break;
236    }
237
238    case base::Value::TYPE_LIST: {
239      std::vector<std::string> as_string_list;
240      AddAllStringValues(*static_cast<base::ListValue*>(input),
241                         &as_string_list);
242      bytes_in_use = storage->GetBytesInUse(as_string_list);
243      break;
244    }
245
246    default:
247      EXTENSION_FUNCTION_VALIDATE(false);
248      return false;
249  }
250
251  SetResult(new base::FundamentalValue(static_cast<int>(bytes_in_use)));
252  return true;
253}
254
255bool StorageStorageAreaSetFunction::RunWithStorage(ValueStore* storage) {
256  base::DictionaryValue* input = NULL;
257  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &input));
258  return UseWriteResult(storage->Set(ValueStore::DEFAULTS, *input), storage);
259}
260
261void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics(
262    QuotaLimitHeuristics* heuristics) const {
263  GetModificationQuotaLimitHeuristics(heuristics);
264}
265
266bool StorageStorageAreaRemoveFunction::RunWithStorage(ValueStore* storage) {
267  base::Value* input = NULL;
268  EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input));
269
270  switch (input->GetType()) {
271    case base::Value::TYPE_STRING: {
272      std::string as_string;
273      input->GetAsString(&as_string);
274      return UseWriteResult(storage->Remove(as_string), storage);
275    }
276
277    case base::Value::TYPE_LIST: {
278      std::vector<std::string> as_string_list;
279      AddAllStringValues(*static_cast<base::ListValue*>(input),
280                         &as_string_list);
281      return UseWriteResult(storage->Remove(as_string_list), storage);
282    }
283
284    default:
285      EXTENSION_FUNCTION_VALIDATE(false);
286      return false;
287  };
288}
289
290void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics(
291    QuotaLimitHeuristics* heuristics) const {
292  GetModificationQuotaLimitHeuristics(heuristics);
293}
294
295bool StorageStorageAreaClearFunction::RunWithStorage(ValueStore* storage) {
296  return UseWriteResult(storage->Clear(), storage);
297}
298
299void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics(
300    QuotaLimitHeuristics* heuristics) const {
301  GetModificationQuotaLimitHeuristics(heuristics);
302}
303
304}  // namespace extensions
305