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