diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-16 05:00:20 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-16 05:00:20 +0000 |
commit | 4bbe629b332d548c48bb6474602a7ed438d8226b (patch) | |
tree | 145584b855760afa677da5d49a7aee7c03305c06 | |
parent | fb16d7f9b150ad6893e1db386707252b0dbdeab2 (diff) | |
download | chromium_src-4bbe629b332d548c48bb6474602a7ed438d8226b.zip chromium_src-4bbe629b332d548c48bb6474602a7ed438d8226b.tar.gz chromium_src-4bbe629b332d548c48bb6474602a7ed438d8226b.tar.bz2 |
Add support for enforcing a quota on extension settings.
BUG=96709
TEST=unit test added
Review URL: http://codereview.chromium.org/7908007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@101456 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/extensions/extension_settings_quota_unittest.cc | 462 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_settings_storage_quota_enforcer.cc | 168 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_settings_storage_quota_enforcer.h | 55 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 |
5 files changed, 688 insertions, 0 deletions
diff --git a/chrome/browser/extensions/extension_settings_quota_unittest.cc b/chrome/browser/extensions/extension_settings_quota_unittest.cc new file mode 100644 index 0000000..7329b6c --- /dev/null +++ b/chrome/browser/extensions/extension_settings_quota_unittest.cc @@ -0,0 +1,462 @@ +// Copyright (c) 2011 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 "testing/gtest/include/gtest/gtest.h" + +#include "base/json/json_writer.h" +#include "base/values.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/extension_settings.h" +#include "chrome/browser/extensions/extension_settings_storage_cache.h" +#include "chrome/browser/extensions/extension_settings_storage_quota_enforcer.h" +#include "chrome/browser/extensions/extension_settings_noop_storage.h" + +class ExtensionSettingsQuotaUnittest : public testing::Test { + public: + ExtensionSettingsQuotaUnittest() + : byte_value_1_(Value::CreateIntegerValue(1)), + byte_value_16_(Value::CreateStringValue("sixteen bytes.")), + byte_value_256_(new ListValue()), + delegate_( + new ExtensionSettingsStorageCache( + new ExtensionSettingsNoopStorage())) { + for (int i = 1; i < 89; ++i) { + byte_value_256_->Append(Value::CreateIntegerValue(i)); + } + ValidateByteValues(); + } + + void ValidateByteValues() { + std::string validate_sizes; + base::JSONWriter::Write(byte_value_1_.get(), false, &validate_sizes); + ASSERT_EQ(1u, validate_sizes.size()); + base::JSONWriter::Write(byte_value_16_.get(), false, &validate_sizes); + ASSERT_EQ(16u, validate_sizes.size()); + base::JSONWriter::Write(byte_value_256_.get(), false, &validate_sizes); + ASSERT_EQ(256u, validate_sizes.size()); + } + + virtual void TearDown() OVERRIDE { + ASSERT_TRUE(storage_.get() != NULL); + } + + protected: + // Creates |storage_|. Must only be called once. + void CreateStorage(size_t quota_bytes, size_t max_keys) { + ASSERT_TRUE(storage_.get() == NULL); + storage_.reset( + new ExtensionSettingsStorageQuotaEnforcer( + quota_bytes, max_keys, delegate_)); + } + + // Returns whether the settings in |storage_| and |delegate_| are the same as + // |settings|. + bool SettingsEqual(const DictionaryValue& settings) { + return settings.Equals(storage_->Get().GetSettings()) && + settings.Equals(delegate_->Get().GetSettings()); + } + + // Values with different serialized sizes. + scoped_ptr<Value> byte_value_1_; + scoped_ptr<Value> byte_value_16_; + scoped_ptr<ListValue> byte_value_256_; + + // Quota enforcing storage area being tested. + scoped_ptr<ExtensionSettingsStorageQuotaEnforcer> storage_; + + // In-memory storage area being delegated to. Always owned by |storage_|. + ExtensionSettingsStorageCache* delegate_; +}; + +TEST_F(ExtensionSettingsQuotaUnittest, ZeroQuotaBytes) { + DictionaryValue empty; + CreateStorage(0, UINT_MAX); + + EXPECT_TRUE(storage_->Set("a", *byte_value_1_).HasError()); + EXPECT_FALSE(storage_->Remove("a").HasError()); + EXPECT_FALSE(storage_->Remove("b").HasError()); + EXPECT_TRUE(SettingsEqual(empty)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, KeySizeTakenIntoAccount) { + DictionaryValue empty; + CreateStorage(8u, UINT_MAX); + EXPECT_TRUE(storage_->Set("Really long key", *byte_value_1_).HasError()); + EXPECT_TRUE(SettingsEqual(empty)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, SmallByteQuota) { + DictionaryValue settings; + CreateStorage(8u, UINT_MAX); + + EXPECT_FALSE(storage_->Set("a", *byte_value_1_).HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set("b", *byte_value_16_).HasError()); + EXPECT_TRUE(storage_->Set("c", *byte_value_256_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, MediumByteQuota) { + DictionaryValue settings; + CreateStorage(40, UINT_MAX); + + DictionaryValue to_set; + to_set.Set("a", byte_value_1_->DeepCopy()); + to_set.Set("b", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(to_set).HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able to set value to other under-quota value. + to_set.Set("a", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(to_set).HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set("c", *byte_value_256_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, ZeroMaxKeys) { + DictionaryValue empty; + CreateStorage(UINT_MAX, 0); + + EXPECT_TRUE(storage_->Set("a", *byte_value_1_).HasError()); + EXPECT_FALSE(storage_->Remove("a").HasError()); + EXPECT_FALSE(storage_->Remove("b").HasError()); + EXPECT_TRUE(SettingsEqual(empty)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, SmallMaxKeys) { + DictionaryValue settings; + CreateStorage(UINT_MAX, 1); + + EXPECT_FALSE(storage_->Set("a", *byte_value_1_).HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able to set existing key to other value without going over quota. + EXPECT_FALSE(storage_->Set("a", *byte_value_16_).HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set("b", *byte_value_16_).HasError()); + EXPECT_TRUE(storage_->Set("c", *byte_value_256_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, MediumMaxKeys) { + DictionaryValue settings; + CreateStorage(UINT_MAX, 2); + + DictionaryValue to_set; + to_set.Set("a", byte_value_1_->DeepCopy()); + to_set.Set("b", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(to_set).HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able to set existing keys to other values without going over + // quota. + to_set.Set("a", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(to_set).HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set("c", *byte_value_256_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, RemovingExistingSettings) { + DictionaryValue settings; + CreateStorage(266, 2); + + storage_->Set("b", *byte_value_16_); + settings.Set("b", byte_value_16_->DeepCopy()); + // Not enough quota. + storage_->Set("c", *byte_value_256_); + EXPECT_TRUE(SettingsEqual(settings)); + + // Try again with "b" removed, enough quota. + EXPECT_FALSE(storage_->Remove("b").HasError()); + settings.Remove("b", NULL); + EXPECT_FALSE(storage_->Set("c", *byte_value_256_).HasError()); + settings.Set("c", byte_value_256_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Enough byte quota but max keys not high enough. + EXPECT_FALSE(storage_->Set("a", *byte_value_1_).HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set("b", *byte_value_1_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Back under max keys. + EXPECT_FALSE(storage_->Remove("a").HasError()); + settings.Remove("a", NULL); + EXPECT_FALSE(storage_->Set("b", *byte_value_1_).HasError()); + settings.Set("b", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, RemovingNonexistentSettings) { + DictionaryValue settings; + CreateStorage(36, 3); + + // Max out bytes. + DictionaryValue to_set; + to_set.Set("b1", byte_value_16_->DeepCopy()); + to_set.Set("b2", byte_value_16_->DeepCopy()); + EXPECT_TRUE(to_set.Equals(storage_->Set(to_set).GetSettings())); + settings.Set("b1", byte_value_16_->DeepCopy()); + settings.Set("b2", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set("a", *byte_value_1_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Remove some settings that don't exist. + std::vector<std::string> to_remove; + to_remove.push_back("a1"); + to_remove.push_back("a2"); + EXPECT_FALSE(storage_->Remove(to_remove).HasError()); + EXPECT_FALSE(storage_->Remove("b").HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Still no quota. + EXPECT_TRUE(storage_->Set("a", *byte_value_1_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Max out key count. + to_set.Clear(); + to_set.Set("b1", byte_value_1_->DeepCopy()); + to_set.Set("b2", byte_value_1_->DeepCopy()); + EXPECT_TRUE(to_set.Equals(storage_->Set(to_set).GetSettings())); + settings.Set("b1", byte_value_1_->DeepCopy()); + settings.Set("b2", byte_value_1_->DeepCopy()); + storage_->Set("b3", *byte_value_1_); + settings.Set("b3", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Remove some settings that don't exist. + to_remove.clear(); + to_remove.push_back("a1"); + to_remove.push_back("a2"); + EXPECT_FALSE(storage_->Remove(to_remove).HasError()); + EXPECT_FALSE(storage_->Remove("b").HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Still no quota. + EXPECT_TRUE(storage_->Set("a", *byte_value_1_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, Clear) { + DictionaryValue settings; + CreateStorage(40, 5); + + // Test running out of byte quota. + DictionaryValue to_set; + to_set.Set("a", byte_value_16_->DeepCopy()); + to_set.Set("b", byte_value_16_->DeepCopy()); + EXPECT_FALSE(storage_->Set(to_set).HasError()); + EXPECT_TRUE(storage_->Set("c", *byte_value_16_).HasError()); + + EXPECT_FALSE(storage_->Clear().HasError()); + + // (repeat) + EXPECT_FALSE(storage_->Set(to_set).HasError()); + EXPECT_TRUE(storage_->Set("c", *byte_value_16_).HasError()); + + // Test reaching max keys. + storage_->Clear(); + to_set.Clear(); + to_set.Set("a", byte_value_1_->DeepCopy()); + to_set.Set("b", byte_value_1_->DeepCopy()); + to_set.Set("c", byte_value_1_->DeepCopy()); + to_set.Set("d", byte_value_1_->DeepCopy()); + to_set.Set("e", byte_value_1_->DeepCopy()); + EXPECT_FALSE(storage_->Set(to_set).HasError()); + EXPECT_TRUE(storage_->Set("f", *byte_value_1_).HasError()); + + storage_->Clear(); + + // (repeat) + EXPECT_FALSE(storage_->Set(to_set).HasError()); + EXPECT_TRUE(storage_->Set("f", *byte_value_1_).HasError()); +} + +TEST_F(ExtensionSettingsQuotaUnittest, ChangingUsedBytesWithSet) { + DictionaryValue settings; + CreateStorage(20, UINT_MAX); + + // Change a setting to make it go over quota. + storage_->Set("a", *byte_value_16_); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set("a", *byte_value_256_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Change a setting to reduce usage and room for another setting. + EXPECT_TRUE(storage_->Set("foobar", *byte_value_1_).HasError()); + storage_->Set("a", *byte_value_1_); + settings.Set("a", byte_value_1_->DeepCopy()); + + EXPECT_FALSE(storage_->Set("foobar", *byte_value_1_).HasError()); + settings.Set("foobar", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, SetsOnlyEntirelyCompletedWithByteQuota) { + DictionaryValue settings; + CreateStorage(40, UINT_MAX); + + storage_->Set("a", *byte_value_16_); + settings.Set("a", byte_value_16_->DeepCopy()); + + // The entire change is over quota. + DictionaryValue to_set; + to_set.Set("b", byte_value_16_->DeepCopy()); + to_set.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(to_set).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // The entire change is over quota, but quota reduced in existing key. + to_set.Set("a", byte_value_1_->DeepCopy()); + EXPECT_FALSE(storage_->Set(to_set).HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + settings.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, SetsOnlyEntireCompletedWithMaxKeys) { + DictionaryValue settings; + CreateStorage(UINT_MAX, 2); + + storage_->Set("a", *byte_value_1_); + settings.Set("a", byte_value_1_->DeepCopy()); + + DictionaryValue to_set; + to_set.Set("b", byte_value_16_->DeepCopy()); + to_set.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(storage_->Set(to_set).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, WithInitialDataAndByteQuota) { + DictionaryValue settings; + delegate_->Set("a", *byte_value_256_); + settings.Set("a", byte_value_256_->DeepCopy()); + + CreateStorage(280, UINT_MAX); + EXPECT_TRUE(SettingsEqual(settings)); + + // Add some data. + EXPECT_FALSE(storage_->Set("b", *byte_value_16_).HasError()); + settings.Set("b", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Not enough quota. + EXPECT_TRUE(storage_->Set("c", *byte_value_16_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Reduce usage of original setting so that "c" can fit. + EXPECT_FALSE(storage_->Set("a", *byte_value_16_).HasError()); + settings.Set("a", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_FALSE(storage_->Set("c", *byte_value_16_).HasError()); + settings.Set("c", byte_value_16_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Remove to free up some more data. + EXPECT_TRUE(storage_->Set("d", *byte_value_256_).HasError()); + + std::vector<std::string> to_remove; + to_remove.push_back("a"); + to_remove.push_back("b"); + storage_->Remove(to_remove); + settings.Remove("a", NULL); + settings.Remove("b", NULL); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_FALSE(storage_->Set("d", *byte_value_256_).HasError()); + settings.Set("d", byte_value_256_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, WithInitialDataAndMaxKeys) { + DictionaryValue settings; + delegate_->Set("a", *byte_value_1_); + settings.Set("a", byte_value_1_->DeepCopy()); + CreateStorage(UINT_MAX, 2); + + EXPECT_FALSE(storage_->Set("b", *byte_value_1_).HasError()); + settings.Set("b", byte_value_1_->DeepCopy()); + + EXPECT_TRUE(storage_->Set("c", *byte_value_1_).HasError()); + + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, InitiallyOverByteQuota) { + DictionaryValue settings; + settings.Set("a", byte_value_16_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + settings.Set("c", byte_value_16_->DeepCopy()); + delegate_->Set(settings); + + CreateStorage(40, UINT_MAX); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_TRUE(storage_->Set("d", *byte_value_16_).HasError()); + + // Take under quota by reducing size of an existing setting + EXPECT_FALSE(storage_->Set("a", *byte_value_1_).HasError()); + settings.Set("a", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able set another small setting. + EXPECT_FALSE(storage_->Set("d", *byte_value_1_).HasError()); + settings.Set("d", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); +} + +TEST_F(ExtensionSettingsQuotaUnittest, InitiallyOverMaxKeys) { + DictionaryValue settings; + settings.Set("a", byte_value_16_->DeepCopy()); + settings.Set("b", byte_value_16_->DeepCopy()); + settings.Set("c", byte_value_16_->DeepCopy()); + delegate_->Set(settings); + + CreateStorage(UINT_MAX, 2); + EXPECT_TRUE(SettingsEqual(settings)); + + // Can't set either an existing or new setting. + EXPECT_TRUE(storage_->Set("d", *byte_value_16_).HasError()); + EXPECT_TRUE(storage_->Set("a", *byte_value_1_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Should be able after removing 2. + storage_->Remove("a"); + settings.Remove("a", NULL); + storage_->Remove("b"); + settings.Remove("b", NULL); + EXPECT_TRUE(SettingsEqual(settings)); + + EXPECT_FALSE(storage_->Set("e", *byte_value_1_).HasError()); + settings.Set("e", byte_value_1_->DeepCopy()); + EXPECT_TRUE(SettingsEqual(settings)); + + // Still can't set any. + EXPECT_TRUE(storage_->Set("d", *byte_value_16_).HasError()); + EXPECT_TRUE(storage_->Set("a", *byte_value_1_).HasError()); + EXPECT_TRUE(SettingsEqual(settings)); +} diff --git a/chrome/browser/extensions/extension_settings_storage_quota_enforcer.cc b/chrome/browser/extensions/extension_settings_storage_quota_enforcer.cc new file mode 100644 index 0000000..9f56a53 --- /dev/null +++ b/chrome/browser/extensions/extension_settings_storage_quota_enforcer.cc @@ -0,0 +1,168 @@ +// Copyright (c) 2011 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/extension_settings_storage_quota_enforcer.h" + +#include "base/bind.h" +#include "base/json/json_writer.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/task.h" + +namespace { + +const char* kExceededQuotaErrorMessage = "Quota exceeded"; + +// Allocates a setting in a record of total and per-setting usage. +void Allocate( + const std::string& key, + const Value& value, + size_t* used_total, + std::map<std::string, size_t>* used_per_setting) { + // Calculate the setting size based on its JSON serialization size. + // TODO(kalman): Does this work with different encodings? + // TODO(kalman): This is duplicating work that the leveldb delegate + // implementation is about to do, and it would be nice to avoid this. + std::string value_as_json; + base::JSONWriter::Write(&value, false, &value_as_json); + size_t new_size = key.size() + value_as_json.size(); + size_t existing_size = (*used_per_setting)[key]; + + *used_total += (new_size - existing_size); + (*used_per_setting)[key] = new_size; +} + +// Frees the allocation of a setting in a record of total and per-setting usage. +void Free( + size_t* used_total, + std::map<std::string, size_t>* used_per_setting, + const std::string& key) { + *used_total -= (*used_per_setting)[key]; + used_per_setting->erase(key); +} + +} // namespace + +ExtensionSettingsStorageQuotaEnforcer::ExtensionSettingsStorageQuotaEnforcer( + size_t quota_bytes, size_t max_keys, ExtensionSettingsStorage* delegate) + : quota_bytes_(quota_bytes), + max_keys_(max_keys), + delegate_(delegate), + used_total_(0) { + Result maybe_initial_settings = delegate->Get(); + if (maybe_initial_settings.HasError()) { + LOG(WARNING) << "Failed to get initial settings for quota: " << + maybe_initial_settings.GetError(); + return; + } + + DictionaryValue* initial_settings = maybe_initial_settings.GetSettings(); + for (DictionaryValue::key_iterator it = initial_settings->begin_keys(); + it != initial_settings->end_keys(); ++it) { + Value *value; + initial_settings->GetWithoutPathExpansion(*it, &value); + Allocate(*it, *value, &used_total_, &used_per_setting_); + } +} + +ExtensionSettingsStorageQuotaEnforcer::~ExtensionSettingsStorageQuotaEnforcer( + ) {} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Get( + const std::string& key) { + return delegate_->Get(key); +} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Get( + const std::vector<std::string>& keys) { + return delegate_->Get(keys); +} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Get() { + return delegate_->Get(); +} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Set( + const std::string& key, const Value& value) { + size_t new_used_total = used_total_; + std::map<std::string, size_t> new_used_per_setting = used_per_setting_; + Allocate(key, value, &new_used_total, &new_used_per_setting); + + if (new_used_total > quota_bytes_ || + new_used_per_setting.size() > max_keys_) { + return Result(kExceededQuotaErrorMessage); + } + + Result result = delegate_->Set(key, value); + if (result.HasError()) { + return result; + } + + used_total_ = new_used_total; + used_per_setting_.swap(new_used_per_setting); + return result; +} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Set( + const DictionaryValue& values) { + size_t new_used_total = used_total_; + std::map<std::string, size_t> new_used_per_setting = used_per_setting_; + for (DictionaryValue::key_iterator it = values.begin_keys(); + it != values.end_keys(); ++it) { + Value* value; + values.GetWithoutPathExpansion(*it, &value); + Allocate(*it, *value, &new_used_total, &new_used_per_setting); + } + + if (new_used_total > quota_bytes_ || + new_used_per_setting.size() > max_keys_) { + return Result(kExceededQuotaErrorMessage); + } + + Result result = delegate_->Set(values); + if (result.HasError()) { + return result; + } + + used_total_ = new_used_total; + used_per_setting_ = new_used_per_setting; + return result; +} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Remove( + const std::string& key) { + Result result = delegate_->Remove(key); + if (result.HasError()) { + return result; + } + Free(&used_total_, &used_per_setting_, key); + return result; +} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Remove( + const std::vector<std::string>& keys) { + Result result = delegate_->Remove(keys); + if (result.HasError()) { + return result; + } + + for (std::vector<std::string>::const_iterator it = keys.begin(); + it != keys.end(); ++it) { + Free(&used_total_, &used_per_setting_, *it); + } + return result; +} + +ExtensionSettingsStorage::Result ExtensionSettingsStorageQuotaEnforcer::Clear( + ) { + Result result = delegate_->Clear(); + if (result.HasError()) { + return result; + } + + while (!used_per_setting_.empty()) { + Free(&used_total_, &used_per_setting_, used_per_setting_.begin()->first); + } + return result; +} diff --git a/chrome/browser/extensions/extension_settings_storage_quota_enforcer.h b/chrome/browser/extensions/extension_settings_storage_quota_enforcer.h new file mode 100644 index 0000000..beb498b --- /dev/null +++ b/chrome/browser/extensions/extension_settings_storage_quota_enforcer.h @@ -0,0 +1,55 @@ +// Copyright (c) 2011 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ +#pragma once + +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/extensions/extension_settings_storage.h" + +// Enforces a quota size in bytes and a maximum number of setting keys for a +// delegate storage area. +class ExtensionSettingsStorageQuotaEnforcer : public ExtensionSettingsStorage { + public: + ExtensionSettingsStorageQuotaEnforcer( + size_t quota_bytes, + size_t max_keys, + // Ownership taken. + ExtensionSettingsStorage* delegate); + + virtual ~ExtensionSettingsStorageQuotaEnforcer(); + + // ExtensionSettingsStorage implementation. + virtual Result Get(const std::string& key) OVERRIDE; + virtual Result Get(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Get() OVERRIDE; + virtual Result Set(const std::string& key, const Value& value) OVERRIDE; + virtual Result Set(const DictionaryValue& settings) OVERRIDE; + virtual Result Remove(const std::string& key) OVERRIDE; + virtual Result Remove(const std::vector<std::string>& keys) OVERRIDE; + virtual Result Clear() OVERRIDE; + + private: + // The storage quota in bytes. + size_t const quota_bytes_; + + // The maximum number of settings keys allowed. + size_t const max_keys_; + + // The delegate storage area. + ExtensionSettingsStorage* const delegate_; + + // Total size of the settings currently being used. Includes both settings + // keys and their JSON-encoded values. + size_t used_total_; + + // Map of key to size of that key, including the key itself. + std::map<std::string, size_t> used_per_setting_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionSettingsStorageQuotaEnforcer); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_SETTINGS_STORAGE_QUOTA_ENFORCER_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 596d842..f525762 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1085,6 +1085,8 @@ 'browser/extensions/extension_settings_storage.h', 'browser/extensions/extension_settings_storage_cache.cc', 'browser/extensions/extension_settings_storage_cache.h', + 'browser/extensions/extension_settings_storage_quota_enforcer.cc', + 'browser/extensions/extension_settings_storage_quota_enforcer.h', 'browser/extensions/extension_settings_leveldb_storage.cc', 'browser/extensions/extension_settings_leveldb_storage.h', 'browser/extensions/extension_service.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index b60b2fa..c0d8a8d 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2310,6 +2310,7 @@ 'browser/extensions/extension_settings_cached_leveldb_storage_unittest.cc', 'browser/extensions/extension_settings_cached_noop_storage_unittest.cc', 'browser/extensions/extension_settings_leveldb_storage_unittest.cc', + 'browser/extensions/extension_settings_quota_unittest.cc', 'browser/extensions/extension_settings_storage_unittest.h', 'browser/extensions/extension_settings_storage_unittest.cc', 'browser/extensions/extension_sidebar_apitest.cc', |