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