Home | History | Annotate | Download | only in storage
      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 
     18 namespace extensions {
     19 
     20 using content::BrowserThread;
     21 
     22 // SettingsFunction
     23 
     24 SettingsFunction::SettingsFunction()
     25     : settings_namespace_(settings_namespace::INVALID),
     26       tried_restoring_storage_(false) {}
     27 
     28 SettingsFunction::~SettingsFunction() {}
     29 
     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().
     36     return false;
     37   }
     38   return settings_namespace_string != "sync";
     39 }
     40 
     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);
     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 
     65 void 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 
     73 ExtensionFunction::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 
     84 ExtensionFunction::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 
    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
    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 
    126 namespace {
    127 
    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) {
    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.
    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());
    146   }
    147   return keys;
    148 }
    149 
    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)
    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 
    177 ExtensionFunction::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 
    220 ExtensionFunction::ResponseValue
    221 StorageStorageAreaGetBytesInUseFunction::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 
    256 ExtensionFunction::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 
    264 void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics(
    265     QuotaLimitHeuristics* heuristics) const {
    266   GetModificationQuotaLimitHeuristics(heuristics);
    267 }
    268 
    269 ExtensionFunction::ResponseValue
    270 StorageStorageAreaRemoveFunction::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 
    294 void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics(
    295     QuotaLimitHeuristics* heuristics) const {
    296   GetModificationQuotaLimitHeuristics(heuristics);
    297 }
    298 
    299 ExtensionFunction::ResponseValue
    300 StorageStorageAreaClearFunction::RunWithStorage(ValueStore* storage) {
    301   return UseWriteResult(storage->Clear(), storage);
    302 }
    303 
    304 void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics(
    305     QuotaLimitHeuristics* heuristics) const {
    306   GetModificationQuotaLimitHeuristics(heuristics);
    307 }
    308 
    309 }  // namespace extensions
    310