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