// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/api/storage/storage_api.h" #include #include #include "base/bind.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "chrome/browser/extensions/api/storage/settings_frontend.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/storage.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/quota_service.h" namespace extensions { using content::BrowserThread; // SettingsFunction SettingsFunction::SettingsFunction() : settings_namespace_(settings_namespace::INVALID) {} SettingsFunction::~SettingsFunction() {} bool SettingsFunction::ShouldSkipQuotaLimiting() const { // Only apply quota if this is for sync storage. std::string settings_namespace_string; if (!args_->GetString(0, &settings_namespace_string)) { // This should be EXTENSION_FUNCTION_VALIDATE(false) but there is no way // to signify that from this function. It will be caught in RunImpl(). return false; } return settings_namespace_string != "sync"; } bool SettingsFunction::RunImpl() { std::string settings_namespace_string; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &settings_namespace_string)); args_->Remove(0, NULL); settings_namespace_ = settings_namespace::FromString(settings_namespace_string); EXTENSION_FUNCTION_VALIDATE( settings_namespace_ != settings_namespace::INVALID); SettingsFrontend* frontend = GetProfile()->GetExtensionService()->settings_frontend(); if (!frontend->IsStorageEnabled(settings_namespace_)) { error_ = base::StringPrintf( "\"%s\" is not available in this instance of Chrome", settings_namespace_string.c_str()); return false; } observers_ = frontend->GetObservers(); frontend->RunWithStorage( extension_id(), settings_namespace_, base::Bind(&SettingsFunction::AsyncRunWithStorage, this)); return true; } void SettingsFunction::AsyncRunWithStorage(ValueStore* storage) { bool success = RunWithStorage(storage); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&SettingsFunction::SendResponse, this, success)); } bool SettingsFunction::UseReadResult(ValueStore::ReadResult read_result) { if (read_result->HasError()) { error_ = read_result->error().message; return false; } base::DictionaryValue* result = new base::DictionaryValue(); result->Swap(&read_result->settings()); SetResult(result); return true; } bool SettingsFunction::UseWriteResult(ValueStore::WriteResult result) { if (result->HasError()) { error_ = result->error().message; return false; } if (!result->changes().empty()) { observers_->Notify( &SettingsObserver::OnSettingsChanged, extension_id(), settings_namespace_, ValueStoreChange::ToJson(result->changes())); } return true; } // Concrete settings functions namespace { // Adds all StringValues from a ListValue to a vector of strings. void AddAllStringValues(const base::ListValue& from, std::vector* to) { DCHECK(to->empty()); std::string as_string; for (base::ListValue::const_iterator it = from.begin(); it != from.end(); ++it) { if ((*it)->GetAsString(&as_string)) { to->push_back(as_string); } } } // Gets the keys of a DictionaryValue. std::vector GetKeys(const base::DictionaryValue& dict) { std::vector keys; for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) { keys.push_back(it.key()); } return keys; } // Creates quota heuristics for settings modification. void GetModificationQuotaLimitHeuristics(QuotaLimitHeuristics* heuristics) { QuotaLimitHeuristic::Config longLimitConfig = { // See storage.json for current value. api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR, base::TimeDelta::FromHours(1) }; heuristics->push_back(new QuotaService::TimedLimit( longLimitConfig, new QuotaLimitHeuristic::SingletonBucketMapper(), "MAX_WRITE_OPERATIONS_PER_HOUR")); // A max of 10 operations per minute, sustained over 10 minutes. QuotaLimitHeuristic::Config shortLimitConfig = { // See storage.json for current value. api::storage::sync::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE, base::TimeDelta::FromMinutes(1) }; heuristics->push_back(new QuotaService::SustainedLimit( base::TimeDelta::FromMinutes(10), shortLimitConfig, new QuotaLimitHeuristic::SingletonBucketMapper(), "MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE")); }; } // namespace bool StorageStorageAreaGetFunction::RunWithStorage(ValueStore* storage) { base::Value* input = NULL; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); switch (input->GetType()) { case base::Value::TYPE_NULL: return UseReadResult(storage->Get()); case base::Value::TYPE_STRING: { std::string as_string; input->GetAsString(&as_string); return UseReadResult(storage->Get(as_string)); } case base::Value::TYPE_LIST: { std::vector as_string_list; AddAllStringValues(*static_cast(input), &as_string_list); return UseReadResult(storage->Get(as_string_list)); } case base::Value::TYPE_DICTIONARY: { base::DictionaryValue* as_dict = static_cast(input); ValueStore::ReadResult result = storage->Get(GetKeys(*as_dict)); if (result->HasError()) { return UseReadResult(result.Pass()); } base::DictionaryValue* with_default_values = as_dict->DeepCopy(); with_default_values->MergeDictionary(&result->settings()); return UseReadResult( ValueStore::MakeReadResult(make_scoped_ptr(with_default_values))); } default: EXTENSION_FUNCTION_VALIDATE(false); return false; } } bool StorageStorageAreaGetBytesInUseFunction::RunWithStorage( ValueStore* storage) { base::Value* input = NULL; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); size_t bytes_in_use = 0; switch (input->GetType()) { case base::Value::TYPE_NULL: bytes_in_use = storage->GetBytesInUse(); break; case base::Value::TYPE_STRING: { std::string as_string; input->GetAsString(&as_string); bytes_in_use = storage->GetBytesInUse(as_string); break; } case base::Value::TYPE_LIST: { std::vector as_string_list; AddAllStringValues(*static_cast(input), &as_string_list); bytes_in_use = storage->GetBytesInUse(as_string_list); break; } default: EXTENSION_FUNCTION_VALIDATE(false); return false; } SetResult(new base::FundamentalValue(static_cast(bytes_in_use))); return true; } bool StorageStorageAreaSetFunction::RunWithStorage(ValueStore* storage) { base::DictionaryValue* input = NULL; EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &input)); return UseWriteResult(storage->Set(ValueStore::DEFAULTS, *input)); } void StorageStorageAreaSetFunction::GetQuotaLimitHeuristics( QuotaLimitHeuristics* heuristics) const { GetModificationQuotaLimitHeuristics(heuristics); } bool StorageStorageAreaRemoveFunction::RunWithStorage(ValueStore* storage) { base::Value* input = NULL; EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &input)); switch (input->GetType()) { case base::Value::TYPE_STRING: { std::string as_string; input->GetAsString(&as_string); return UseWriteResult(storage->Remove(as_string)); } case base::Value::TYPE_LIST: { std::vector as_string_list; AddAllStringValues(*static_cast(input), &as_string_list); return UseWriteResult(storage->Remove(as_string_list)); } default: EXTENSION_FUNCTION_VALIDATE(false); return false; }; } void StorageStorageAreaRemoveFunction::GetQuotaLimitHeuristics( QuotaLimitHeuristics* heuristics) const { GetModificationQuotaLimitHeuristics(heuristics); } bool StorageStorageAreaClearFunction::RunWithStorage(ValueStore* storage) { return UseWriteResult(storage->Clear()); } void StorageStorageAreaClearFunction::GetQuotaLimitHeuristics( QuotaLimitHeuristics* heuristics) const { GetModificationQuotaLimitHeuristics(heuristics); } } // namespace extensions