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 Run().
36    return false;
37  }
38  return settings_namespace_string != "sync";
39}
40
41ExtensionFunction::ResponseAction SettingsFunction::Run() {
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(settings_namespace_ !=
48                              settings_namespace::INVALID);
49
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())));
55  }
56
57  observers_ = frontend->GetObservers();
58  frontend->RunWithStorage(
59      extension(),
60      settings_namespace_,
61      base::Bind(&SettingsFunction::AsyncRunWithStorage, this));
62  return RespondLater();
63}
64
65void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) {
66  ResponseValue response = RunWithStorage(storage);
67  BrowserThread::PostTask(
68      BrowserThread::UI,
69      FROM_HERE,
70      base::Bind(&SettingsFunction::Respond, this, base::Passed(&response)));
71}
72
73ExtensionFunction::ResponseValue SettingsFunction::UseReadResult(
74    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  return OneArgument(dict);
82}
83
84ExtensionFunction::ResponseValue SettingsFunction::UseWriteResult(
85    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 NoArguments();
99}
100
101ExtensionFunction::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
106  // failed.
107  if (error.code == ValueStore::CORRUPTION && !tried_restoring_storage_) {
108    tried_restoring_storage_ = true;
109
110    // If the corruption is on a particular key, try to restore that key and
111    // re-run.
112    if (error.key.get() && storage->RestoreKey(*error.key))
113      return RunWithStorage(storage);
114
115    // If the full database is corrupted, try to restore the whole thing and
116    // re-run.
117    if (storage->Restore())
118      return RunWithStorage(storage);
119  }
120
121  return Error(error.message);
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
177ExtensionFunction::ResponseValue StorageStorageAreaGetFunction::RunWithStorage(
178    ValueStore* storage) {
179  base::Value* input = NULL;
180  if (!args_->Get(0, &input))
181    return BadMessage();
182
183  switch (input->GetType()) {
184    case base::Value::TYPE_NULL:
185      return UseReadResult(storage->Get(), storage);
186
187    case base::Value::TYPE_STRING: {
188      std::string as_string;
189      input->GetAsString(&as_string);
190      return UseReadResult(storage->Get(as_string), storage);
191    }
192
193    case base::Value::TYPE_LIST: {
194      std::vector<std::string> as_string_list;
195      AddAllStringValues(*static_cast<base::ListValue*>(input),
196                         &as_string_list);
197      return UseReadResult(storage->Get(as_string_list), storage);
198    }
199
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);
206      }
207
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)),
212          storage);
213    }
214
215    default:
216      return BadMessage();
217  }
218}
219
220ExtensionFunction::ResponseValue
221StorageStorageAreaGetBytesInUseFunction::RunWithStorage(ValueStore* storage) {
222  base::Value* input = NULL;
223  if (!args_->Get(0, &input))
224    return BadMessage();
225
226  size_t bytes_in_use = 0;
227
228  switch (input->GetType()) {
229    case base::Value::TYPE_NULL:
230      bytes_in_use = storage->GetBytesInUse();
231      break;
232
233    case base::Value::TYPE_STRING: {
234      std::string as_string;
235      input->GetAsString(&as_string);
236      bytes_in_use = storage->GetBytesInUse(as_string);
237      break;
238    }
239
240    case base::Value::TYPE_LIST: {
241      std::vector<std::string> as_string_list;
242      AddAllStringValues(*static_cast<base::ListValue*>(input),
243                         &as_string_list);
244      bytes_in_use = storage->GetBytesInUse(as_string_list);
245      break;
246    }
247
248    default:
249      return BadMessage();
250  }
251
252  return OneArgument(
253      new base::FundamentalValue(static_cast<int>(bytes_in_use)));
254}
255
256ExtensionFunction::ResponseValue StorageStorageAreaSetFunction::RunWithStorage(
257    ValueStore* storage) {
258  base::DictionaryValue* input = NULL;
259  if (!args_->GetDictionary(0, &input))
260    return BadMessage();
261  return UseWriteResult(storage->Set(ValueStore::DEFAULTS, *input), storage);
262}
263
264void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics(
265    QuotaLimitHeuristics* heuristics) const {
266  GetModificationQuotaLimitHeuristics(heuristics);
267}
268
269ExtensionFunction::ResponseValue
270StorageStorageAreaRemoveFunction::RunWithStorage(ValueStore* storage) {
271  base::Value* input = NULL;
272  if (!args_->Get(0, &input))
273    return BadMessage();
274
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);
280    }
281
282    case base::Value::TYPE_LIST: {
283      std::vector<std::string> as_string_list;
284      AddAllStringValues(*static_cast<base::ListValue*>(input),
285                         &as_string_list);
286      return UseWriteResult(storage->Remove(as_string_list), storage);
287    }
288
289    default:
290      return BadMessage();
291  };
292}
293
294void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics(
295    QuotaLimitHeuristics* heuristics) const {
296  GetModificationQuotaLimitHeuristics(heuristics);
297}
298
299ExtensionFunction::ResponseValue
300StorageStorageAreaClearFunction::RunWithStorage(ValueStore* storage) {
301  return UseWriteResult(storage->Clear(), storage);
302}
303
304void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics(
305    QuotaLimitHeuristics* heuristics) const {
306  GetModificationQuotaLimitHeuristics(heuristics);
307}
308
309}  // namespace extensions
310