diff options
Diffstat (limited to 'base/prefs')
27 files changed, 2933 insertions, 0 deletions
diff --git a/base/prefs/DEPS b/base/prefs/DEPS new file mode 100644 index 0000000..ab1b336 --- /dev/null +++ b/base/prefs/DEPS @@ -0,0 +1,18 @@ +include_rules = [ + # Will move to base/ + "+chrome/common/important_file_writer.h", +] + +specific_include_rules = { + '.*_[a-z]+test\.cc': [ + "!chrome/common/chrome_notification_types.h", + "!chrome/common/chrome_paths.h", + "!chrome/common/important_file_writer.h", + "!chrome/common/pref_names.h", + "!chrome/test/base/testing_pref_service.h", + "!content/public/browser/notification_details.h", + "!content/public/browser/notification_source.h", + "!content/public/browser/notification_types.h", + "!content/public/test/mock_notification_observer.h", + ], +} diff --git a/base/prefs/README b/base/prefs/README new file mode 100644 index 0000000..a49604e --- /dev/null +++ b/base/prefs/README @@ -0,0 +1,10 @@ +Prefs is a general-purpose key-value store for application preferences. + +At the moment, src/base/prefs has a bunch of dependencies back to +src/chrome, and thus should only be used by code under src/chrome. + +We are working to remove these dependencies; once we do so, +src/base/prefs will be available as a separate module, 'base_prefs', +from src/base/base.gyp. + +Until that happens, please do not try to reuse outside of chrome/. diff --git a/base/prefs/default_pref_store.cc b/base/prefs/default_pref_store.cc new file mode 100644 index 0000000..c031221 --- /dev/null +++ b/base/prefs/default_pref_store.cc @@ -0,0 +1,26 @@ +// 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 "base/prefs/default_pref_store.h" + +using base::Value; + +DefaultPrefStore::DefaultPrefStore() {} + +void DefaultPrefStore::SetDefaultValue(const std::string& key, Value* value) { + CHECK(GetValue(key, NULL) == READ_NO_VALUE); + SetValue(key, value); +} + +void DefaultPrefStore::RemoveDefaultValue(const std::string& key) { + CHECK(GetValue(key, NULL) == READ_OK); + RemoveValue(key); +} + +base::Value::Type DefaultPrefStore::GetType(const std::string& key) const { + const Value* value; + return GetValue(key, &value) == READ_OK ? value->GetType() : Value::TYPE_NULL; +} + +DefaultPrefStore::~DefaultPrefStore() {} diff --git a/base/prefs/default_pref_store.h b/base/prefs/default_pref_store.h new file mode 100644 index 0000000..90d83f0 --- /dev/null +++ b/base/prefs/default_pref_store.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef BASE_PREFS_DEFAULT_PREF_STORE_H_ +#define BASE_PREFS_DEFAULT_PREF_STORE_H_ + +#include <string> + +#include "base/prefs/value_map_pref_store.h" +#include "base/values.h" + +// This PrefStore keeps track of default preference values set when a +// preference is registered with the PrefService. +class DefaultPrefStore : public ValueMapPrefStore { + public: + DefaultPrefStore(); + + // Stores a new |value| for |key|. Assumes ownership of |value|. + void SetDefaultValue(const std::string& key, Value* value); + + // Removes the value for |key|. + void RemoveDefaultValue(const std::string& key); + + // Returns the registered type for |key| or Value::TYPE_NULL if the |key| + // has not been registered. + base::Value::Type GetType(const std::string& key) const; + + protected: + virtual ~DefaultPrefStore(); + + private: + DISALLOW_COPY_AND_ASSIGN(DefaultPrefStore); +}; + +#endif // BASE_PREFS_DEFAULT_PREF_STORE_H_ diff --git a/base/prefs/json_pref_store.cc b/base/prefs/json_pref_store.cc new file mode 100644 index 0000000..18dc0b7 --- /dev/null +++ b/base/prefs/json_pref_store.cc @@ -0,0 +1,344 @@ +// 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 "base/prefs/json_pref_store.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/json/json_string_value_serializer.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop_proxy.h" +#include "base/values.h" + +namespace { + +// Some extensions we'll tack on to copies of the Preferences files. +const FilePath::CharType* kBadExtension = FILE_PATH_LITERAL("bad"); + +// Differentiates file loading between origin thread and passed +// (aka file) thread. +class FileThreadDeserializer + : public base::RefCountedThreadSafe<FileThreadDeserializer> { + public: + FileThreadDeserializer(JsonPrefStore* delegate, + base::MessageLoopProxy* file_loop_proxy) + : no_dir_(false), + error_(PersistentPrefStore::PREF_READ_ERROR_NONE), + delegate_(delegate), + file_loop_proxy_(file_loop_proxy), + origin_loop_proxy_(base::MessageLoopProxy::current()) { + } + + void Start(const FilePath& path) { + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + file_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&FileThreadDeserializer::ReadFileAndReport, + this, path)); + } + + // Deserializes JSON on the file thread. + void ReadFileAndReport(const FilePath& path) { + DCHECK(file_loop_proxy_->BelongsToCurrentThread()); + + value_.reset(DoReading(path, &error_, &no_dir_)); + + origin_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this)); + } + + // Reports deserialization result on the origin thread. + void ReportOnOriginThread() { + DCHECK(origin_loop_proxy_->BelongsToCurrentThread()); + delegate_->OnFileRead(value_.release(), error_, no_dir_); + } + + static Value* DoReading(const FilePath& path, + PersistentPrefStore::PrefReadError* error, + bool* no_dir) { + int error_code; + std::string error_msg; + JSONFileValueSerializer serializer(path); + Value* value = serializer.Deserialize(&error_code, &error_msg); + HandleErrors(value, path, error_code, error_msg, error); + *no_dir = !file_util::PathExists(path.DirName()); + return value; + } + + static void HandleErrors(const Value* value, + const FilePath& path, + int error_code, + const std::string& error_msg, + PersistentPrefStore::PrefReadError* error); + + private: + friend class base::RefCountedThreadSafe<FileThreadDeserializer>; + ~FileThreadDeserializer() {} + + bool no_dir_; + PersistentPrefStore::PrefReadError error_; + scoped_ptr<Value> value_; + scoped_refptr<JsonPrefStore> delegate_; + scoped_refptr<base::MessageLoopProxy> file_loop_proxy_; + scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_; +}; + +// static +void FileThreadDeserializer::HandleErrors( + const Value* value, + const FilePath& path, + int error_code, + const std::string& error_msg, + PersistentPrefStore::PrefReadError* error) { + *error = PersistentPrefStore::PREF_READ_ERROR_NONE; + if (!value) { + DVLOG(1) << "Error while loading JSON file: " << error_msg; + switch (error_code) { + case JSONFileValueSerializer::JSON_ACCESS_DENIED: + *error = PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED; + break; + case JSONFileValueSerializer::JSON_CANNOT_READ_FILE: + *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER; + break; + case JSONFileValueSerializer::JSON_FILE_LOCKED: + *error = PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED; + break; + case JSONFileValueSerializer::JSON_NO_SUCH_FILE: + *error = PersistentPrefStore::PREF_READ_ERROR_NO_FILE; + break; + default: + *error = PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE; + // JSON errors indicate file corruption of some sort. + // Since the file is corrupt, move it to the side and continue with + // empty preferences. This will result in them losing their settings. + // We keep the old file for possible support and debugging assistance + // as well as to detect if they're seeing these errors repeatedly. + // TODO(erikkay) Instead, use the last known good file. + FilePath bad = path.ReplaceExtension(kBadExtension); + + // If they've ever had a parse error before, put them in another bucket. + // TODO(erikkay) if we keep this error checking for very long, we may + // want to differentiate between recent and long ago errors. + if (file_util::PathExists(bad)) + *error = PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT; + file_util::Move(path, bad); + break; + } + } else if (!value->IsType(Value::TYPE_DICTIONARY)) { + *error = PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE; + } +} + +} // namespace + +JsonPrefStore::JsonPrefStore(const FilePath& filename, + base::MessageLoopProxy* file_message_loop_proxy) + : path_(filename), + file_message_loop_proxy_(file_message_loop_proxy), + prefs_(new DictionaryValue()), + read_only_(false), + writer_(filename, file_message_loop_proxy), + error_delegate_(NULL), + initialized_(false), + read_error_(PREF_READ_ERROR_OTHER) { +} + +PrefStore::ReadResult JsonPrefStore::GetValue(const std::string& key, + const Value** result) const { + Value* tmp = NULL; + if (prefs_->Get(key, &tmp)) { + if (result) + *result = tmp; + return READ_OK; + } + return READ_NO_VALUE; +} + +void JsonPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +size_t JsonPrefStore::NumberOfObservers() const { + return observers_.size(); +} + +bool JsonPrefStore::IsInitializationComplete() const { + return initialized_; +} + +PrefStore::ReadResult JsonPrefStore::GetMutableValue(const std::string& key, + Value** result) { + return prefs_->Get(key, result) ? READ_OK : READ_NO_VALUE; +} + +void JsonPrefStore::SetValue(const std::string& key, Value* value) { + DCHECK(value); + scoped_ptr<Value> new_value(value); + Value* old_value = NULL; + prefs_->Get(key, &old_value); + if (!old_value || !value->Equals(old_value)) { + prefs_->Set(key, new_value.release()); + ReportValueChanged(key); + } +} + +void JsonPrefStore::SetValueSilently(const std::string& key, Value* value) { + DCHECK(value); + scoped_ptr<Value> new_value(value); + Value* old_value = NULL; + prefs_->Get(key, &old_value); + if (!old_value || !value->Equals(old_value)) { + prefs_->Set(key, new_value.release()); + if (!read_only_) + writer_.ScheduleWrite(this); + } +} + +void JsonPrefStore::RemoveValue(const std::string& key) { + if (prefs_->Remove(key, NULL)) + ReportValueChanged(key); +} + +void JsonPrefStore::MarkNeedsEmptyValue(const std::string& key) { + keys_need_empty_value_.insert(key); +} + +bool JsonPrefStore::ReadOnly() const { + return read_only_; +} + +PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const { + return read_error_; +} + +PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() { + if (path_.empty()) { + OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); + return PREF_READ_ERROR_FILE_NOT_SPECIFIED; + } + + PrefReadError error; + bool no_dir; + Value* value = FileThreadDeserializer::DoReading(path_, &error, &no_dir); + OnFileRead(value, error, no_dir); + return error; +} + +void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) { + initialized_ = false; + error_delegate_.reset(error_delegate); + if (path_.empty()) { + OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false); + return; + } + + // Start async reading of the preferences file. It will delete itself + // in the end. + scoped_refptr<FileThreadDeserializer> deserializer( + new FileThreadDeserializer(this, file_message_loop_proxy_.get())); + deserializer->Start(path_); +} + +void JsonPrefStore::CommitPendingWrite() { + if (writer_.HasPendingWrite() && !read_only_) + writer_.DoScheduledWrite(); +} + +void JsonPrefStore::ReportValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); + if (!read_only_) + writer_.ScheduleWrite(this); +} + +void JsonPrefStore::OnFileRead(Value* value_owned, + PersistentPrefStore::PrefReadError error, + bool no_dir) { + scoped_ptr<Value> value(value_owned); + read_error_ = error; + + if (no_dir) { + FOR_EACH_OBSERVER(PrefStore::Observer, + observers_, + OnInitializationCompleted(false)); + return; + } + + initialized_ = true; + + switch (error) { + case PREF_READ_ERROR_ACCESS_DENIED: + case PREF_READ_ERROR_FILE_OTHER: + case PREF_READ_ERROR_FILE_LOCKED: + case PREF_READ_ERROR_JSON_TYPE: + case PREF_READ_ERROR_FILE_NOT_SPECIFIED: + read_only_ = true; + break; + case PREF_READ_ERROR_NONE: + DCHECK(value.get()); + prefs_.reset(static_cast<DictionaryValue*>(value.release())); + break; + case PREF_READ_ERROR_NO_FILE: + // If the file just doesn't exist, maybe this is first run. In any case + // there's no harm in writing out default prefs in this case. + break; + case PREF_READ_ERROR_JSON_PARSE: + case PREF_READ_ERROR_JSON_REPEAT: + break; + default: + NOTREACHED() << "Unknown error: " << error; + } + + if (error_delegate_.get() && error != PREF_READ_ERROR_NONE) + error_delegate_->OnError(error); + + FOR_EACH_OBSERVER(PrefStore::Observer, + observers_, + OnInitializationCompleted(true)); +} + +JsonPrefStore::~JsonPrefStore() { + CommitPendingWrite(); +} + +bool JsonPrefStore::SerializeData(std::string* output) { + // TODO(tc): Do we want to prune webkit preferences that match the default + // value? + JSONStringValueSerializer serializer(output); + serializer.set_pretty_print(true); + scoped_ptr<DictionaryValue> copy(prefs_->DeepCopyWithoutEmptyChildren()); + + // Iterates |keys_need_empty_value_| and if the key exists in |prefs_|, + // ensure its empty ListValue or DictonaryValue is preserved. + for (std::set<std::string>::const_iterator + it = keys_need_empty_value_.begin(); + it != keys_need_empty_value_.end(); + ++it) { + const std::string& key = *it; + + base::Value* value = NULL; + if (!prefs_->Get(key, &value)) + continue; + + if (value->IsType(base::Value::TYPE_LIST)) { + const base::ListValue* list = NULL; + if (value->GetAsList(&list) && list->empty()) + copy->Set(key, new base::ListValue); + } else if (value->IsType(base::Value::TYPE_DICTIONARY)) { + const base::DictionaryValue* dict = NULL; + if (value->GetAsDictionary(&dict) && dict->empty()) + copy->Set(key, new base::DictionaryValue); + } + } + + return serializer.Serialize(*(copy.get())); +} diff --git a/base/prefs/json_pref_store.h b/base/prefs/json_pref_store.h new file mode 100644 index 0000000..3ed1ffb1 --- /dev/null +++ b/base/prefs/json_pref_store.h @@ -0,0 +1,93 @@ +// 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. + +#ifndef BASE_PREFS_JSON_PREF_STORE_H_ +#define BASE_PREFS_JSON_PREF_STORE_H_ + +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "base/prefs/persistent_pref_store.h" +#include "chrome/common/important_file_writer.h" + +namespace base { +class DictionaryValue; +class MessageLoopProxy; +class Value; +} + +class FilePath; + +// A writable PrefStore implementation that is used for user preferences. +class JsonPrefStore : public PersistentPrefStore, + public ImportantFileWriter::DataSerializer { + public: + // |file_message_loop_proxy| is the MessageLoopProxy for a thread on which + // file I/O can be done. + JsonPrefStore(const FilePath& pref_filename, + base::MessageLoopProxy* file_message_loop_proxy); + + // PrefStore overrides: + virtual ReadResult GetValue(const std::string& key, + const base::Value** result) const OVERRIDE; + virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE; + virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE; + virtual size_t NumberOfObservers() const OVERRIDE; + virtual bool IsInitializationComplete() const OVERRIDE; + + // PersistentPrefStore overrides: + virtual ReadResult GetMutableValue(const std::string& key, + base::Value** result) OVERRIDE; + virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE; + virtual void SetValueSilently(const std::string& key, + base::Value* value) OVERRIDE; + virtual void RemoveValue(const std::string& key) OVERRIDE; + virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE; + virtual bool ReadOnly() const OVERRIDE; + virtual PrefReadError GetReadError() const OVERRIDE; + virtual PrefReadError ReadPrefs() OVERRIDE; + virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE; + virtual void CommitPendingWrite() OVERRIDE; + virtual void ReportValueChanged(const std::string& key) OVERRIDE; + + // This method is called after JSON file has been read. Method takes + // ownership of the |value| pointer. Note, this method is used with + // asynchronous file reading, so class exposes it only for the internal needs. + // (read: do not call it manually). + void OnFileRead(base::Value* value_owned, PrefReadError error, bool no_dir); + + private: + virtual ~JsonPrefStore(); + + // ImportantFileWriter::DataSerializer overrides: + virtual bool SerializeData(std::string* output) OVERRIDE; + + FilePath path_; + scoped_refptr<base::MessageLoopProxy> file_message_loop_proxy_; + + scoped_ptr<base::DictionaryValue> prefs_; + + bool read_only_; + + // Helper for safely writing pref data. + ImportantFileWriter writer_; + + ObserverList<PrefStore::Observer, true> observers_; + + scoped_ptr<ReadErrorDelegate> error_delegate_; + + bool initialized_; + PrefReadError read_error_; + + std::set<std::string> keys_need_empty_value_; + + DISALLOW_COPY_AND_ASSIGN(JsonPrefStore); +}; + +#endif // BASE_PREFS_JSON_PREF_STORE_H_ diff --git a/base/prefs/json_pref_store_unittest.cc b/base/prefs/json_pref_store_unittest.cc new file mode 100644 index 0000000..14723ab --- /dev/null +++ b/base/prefs/json_pref_store_unittest.cc @@ -0,0 +1,293 @@ +// 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 "base/file_util.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "base/path_service.h" +#include "base/prefs/json_pref_store.h" +#include "base/scoped_temp_dir.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/pref_names.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class MockPrefStoreObserver : public PrefStore::Observer { + public: + MOCK_METHOD1(OnPrefValueChanged, void (const std::string&)); + MOCK_METHOD1(OnInitializationCompleted, void (bool)); +}; + +class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate { + public: + MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError)); +}; + +} // namespace + +class JsonPrefStoreTest : public testing::Test { + protected: + virtual void SetUp() { + message_loop_proxy_ = base::MessageLoopProxy::current(); + + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_)); + data_dir_ = data_dir_.AppendASCII("pref_service"); + ASSERT_TRUE(file_util::PathExists(data_dir_)); + } + + // The path to temporary directory used to contain the test operations. + ScopedTempDir temp_dir_; + // The path to the directory where the test data is stored. + FilePath data_dir_; + // A message loop that we can use as the file thread message loop. + MessageLoop message_loop_; + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; +}; + +// Test fallback behavior for a nonexistent file. +TEST_F(JsonPrefStoreTest, NonExistentFile) { + FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); + ASSERT_FALSE(file_util::PathExists(bogus_input_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(bogus_input_file, message_loop_proxy_.get()); + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); +} + +// Test fallback behavior for an invalid file. +TEST_F(JsonPrefStoreTest, InvalidFile) { + FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json"); + FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json"); + ASSERT_TRUE(file_util::CopyFile(invalid_file_original, invalid_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(invalid_file, message_loop_proxy_.get()); + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); + + // The file should have been moved aside. + EXPECT_FALSE(file_util::PathExists(invalid_file)); + FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad"); + EXPECT_TRUE(file_util::PathExists(moved_aside)); + EXPECT_TRUE(file_util::TextContentsEqual(invalid_file_original, + moved_aside)); +} + +// This function is used to avoid code duplication while testing synchronous and +// asynchronous version of the JsonPrefStore loading. +void RunBasicJsonPrefStoreTest(JsonPrefStore *pref_store, + const FilePath& output_file, + const FilePath& golden_output_file) { + const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs"; + const char kMaxTabs[] = "tabs.max_tabs"; + const char kLongIntPref[] = "long_int.pref"; + + std::string cnn("http://www.cnn.com"); + + const Value* actual; + EXPECT_EQ(PrefStore::READ_OK, + pref_store->GetValue(prefs::kHomePage, &actual)); + std::string string_value; + EXPECT_TRUE(actual->GetAsString(&string_value)); + EXPECT_EQ(cnn, string_value); + + const char kSomeDirectory[] = "some_directory"; + + EXPECT_EQ(PrefStore::READ_OK, pref_store->GetValue(kSomeDirectory, &actual)); + FilePath::StringType path; + EXPECT_TRUE(actual->GetAsString(&path)); + EXPECT_EQ(FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path); + FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/")); + + pref_store->SetValue(kSomeDirectory, + Value::CreateStringValue(some_path.value())); + EXPECT_EQ(PrefStore::READ_OK, pref_store->GetValue(kSomeDirectory, &actual)); + EXPECT_TRUE(actual->GetAsString(&path)); + EXPECT_EQ(some_path.value(), path); + + // Test reading some other data types from sub-dictionaries. + EXPECT_EQ(PrefStore::READ_OK, + pref_store->GetValue(kNewWindowsInTabs, &actual)); + bool boolean = false; + EXPECT_TRUE(actual->GetAsBoolean(&boolean)); + EXPECT_TRUE(boolean); + + pref_store->SetValue(kNewWindowsInTabs, + Value::CreateBooleanValue(false)); + EXPECT_EQ(PrefStore::READ_OK, + pref_store->GetValue(kNewWindowsInTabs, &actual)); + EXPECT_TRUE(actual->GetAsBoolean(&boolean)); + EXPECT_FALSE(boolean); + + EXPECT_EQ(PrefStore::READ_OK, pref_store->GetValue(kMaxTabs, &actual)); + int integer = 0; + EXPECT_TRUE(actual->GetAsInteger(&integer)); + EXPECT_EQ(20, integer); + pref_store->SetValue(kMaxTabs, Value::CreateIntegerValue(10)); + EXPECT_EQ(PrefStore::READ_OK, pref_store->GetValue(kMaxTabs, &actual)); + EXPECT_TRUE(actual->GetAsInteger(&integer)); + EXPECT_EQ(10, integer); + + pref_store->SetValue(kLongIntPref, + Value::CreateStringValue( + base::Int64ToString(214748364842LL))); + EXPECT_EQ(PrefStore::READ_OK, pref_store->GetValue(kLongIntPref, &actual)); + EXPECT_TRUE(actual->GetAsString(&string_value)); + int64 value; + base::StringToInt64(string_value, &value); + EXPECT_EQ(214748364842LL, value); + + // Serialize and compare to expected output. + ASSERT_TRUE(file_util::PathExists(golden_output_file)); + pref_store->CommitPendingWrite(); + MessageLoop::current()->RunAllPending(); + EXPECT_TRUE(file_util::TextContentsEqual(golden_output_file, output_file)); + ASSERT_TRUE(file_util::Delete(output_file, false)); +} + +TEST_F(JsonPrefStoreTest, Basic) { + ASSERT_TRUE(file_util::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("write.json"))); + + // Test that the persistent value can be loaded. + FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_TRUE(file_util::PathExists(input_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, message_loop_proxy_.get()); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + ASSERT_FALSE(pref_store->ReadOnly()); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest(pref_store, + input_file, + data_dir_.AppendASCII("write.golden.json")); +} + +TEST_F(JsonPrefStoreTest, BasicAsync) { + ASSERT_TRUE(file_util::CopyFile(data_dir_.AppendASCII("read.json"), + temp_dir_.path().AppendASCII("write.json"))); + + // Test that the persistent value can be loaded. + FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_TRUE(file_util::PathExists(input_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, message_loop_proxy_.get()); + + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate; + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + EXPECT_CALL(*mock_error_delegate, + OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + message_loop_.RunAllPending(); + pref_store->RemoveObserver(&mock_observer); + + ASSERT_FALSE(pref_store->ReadOnly()); + + // The JSON file looks like this: + // { + // "homepage": "http://www.cnn.com", + // "some_directory": "/usr/local/", + // "tabs": { + // "new_windows_in_tabs": true, + // "max_tabs": 20 + // } + // } + + RunBasicJsonPrefStoreTest(pref_store, + input_file, + data_dir_.AppendASCII("write.golden.json")); +} + +// Tests asynchronous reading of the file when there is no file. +TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) { + FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); + ASSERT_FALSE(file_util::PathExists(bogus_input_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(bogus_input_file, message_loop_proxy_.get()); + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate; + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + EXPECT_CALL(*mock_error_delegate, + OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1); + message_loop_.RunAllPending(); + pref_store->RemoveObserver(&mock_observer); + + EXPECT_FALSE(pref_store->ReadOnly()); +} + +TEST_F(JsonPrefStoreTest, NeedsEmptyValue) { + FilePath pref_file = temp_dir_.path().AppendASCII("write.json"); + + ASSERT_TRUE(file_util::CopyFile( + data_dir_.AppendASCII("read.need_empty_value.json"), + pref_file)); + + // Test that the persistent value can be loaded. + ASSERT_TRUE(file_util::PathExists(pref_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(pref_file, message_loop_proxy_.get()); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + ASSERT_FALSE(pref_store->ReadOnly()); + + // The JSON file looks like this: + // { + // "list": [ 1 ], + // "list_needs_empty_value": [ 2 ], + // "dict": { + // "dummy": true, + // }, + // "dict_needs_empty_value": { + // "dummy": true, + // }, + // } + + // Set flag to preserve empty values for the following keys. + pref_store->MarkNeedsEmptyValue("list_needs_empty_value"); + pref_store->MarkNeedsEmptyValue("dict_needs_empty_value"); + + // Set all keys to empty values. + pref_store->SetValue("list", new base::ListValue); + pref_store->SetValue("list_needs_empty_value", new base::ListValue); + pref_store->SetValue("dict", new base::DictionaryValue); + pref_store->SetValue("dict_needs_empty_value", new base::DictionaryValue); + + // Write to file. + pref_store->CommitPendingWrite(); + MessageLoop::current()->RunAllPending(); + + // Compare to expected output. + FilePath golden_output_file = + data_dir_.AppendASCII("write.golden.need_empty_value.json"); + ASSERT_TRUE(file_util::PathExists(golden_output_file)); + EXPECT_TRUE(file_util::TextContentsEqual(golden_output_file, pref_file)); +} diff --git a/base/prefs/overlay_user_pref_store.cc b/base/prefs/overlay_user_pref_store.cc new file mode 100644 index 0000000..26ee45e --- /dev/null +++ b/base/prefs/overlay_user_pref_store.cc @@ -0,0 +1,186 @@ +// 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 "base/prefs/overlay_user_pref_store.h" + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" + +OverlayUserPrefStore::OverlayUserPrefStore( + PersistentPrefStore* underlay) + : underlay_(underlay) { + underlay_->AddObserver(this); +} + +bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const { + return overlay_.GetValue(key, NULL); +} + +void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void OverlayUserPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +size_t OverlayUserPrefStore::NumberOfObservers() const { + return observers_.size(); +} + +bool OverlayUserPrefStore::IsInitializationComplete() const { + return underlay_->IsInitializationComplete(); +} + +PrefStore::ReadResult OverlayUserPrefStore::GetValue( + const std::string& key, + const Value** result) const { + // If the |key| shall NOT be stored in the overlay store, there must not + // be an entry. + DCHECK(ShallBeStoredInOverlay(key) || !overlay_.GetValue(key, NULL)); + + if (overlay_.GetValue(key, result)) + return READ_OK; + return underlay_->GetValue(GetUnderlayKey(key), result); +} + +PrefStore::ReadResult OverlayUserPrefStore::GetMutableValue( + const std::string& key, + Value** result) { + if (!ShallBeStoredInOverlay(key)) + return underlay_->GetMutableValue(GetUnderlayKey(key), result); + + if (overlay_.GetValue(key, result)) + return READ_OK; + + // Try to create copy of underlay if the overlay does not contain a value. + Value* underlay_value = NULL; + PrefStore::ReadResult read_result = + underlay_->GetMutableValue(GetUnderlayKey(key), &underlay_value); + if (read_result != READ_OK) + return read_result; + + *result = underlay_value->DeepCopy(); + overlay_.SetValue(key, *result); + return READ_OK; +} + +void OverlayUserPrefStore::SetValue(const std::string& key, + Value* value) { + if (!ShallBeStoredInOverlay(key)) { + underlay_->SetValue(GetUnderlayKey(key), value); + return; + } + + if (overlay_.SetValue(key, value)) + ReportValueChanged(key); +} + +void OverlayUserPrefStore::SetValueSilently(const std::string& key, + Value* value) { + if (!ShallBeStoredInOverlay(key)) { + underlay_->SetValueSilently(GetUnderlayKey(key), value); + return; + } + + overlay_.SetValue(key, value); +} + +void OverlayUserPrefStore::RemoveValue(const std::string& key) { + if (!ShallBeStoredInOverlay(key)) { + underlay_->RemoveValue(GetUnderlayKey(key)); + return; + } + + if (overlay_.RemoveValue(key)) + ReportValueChanged(key); +} + +void OverlayUserPrefStore::MarkNeedsEmptyValue(const std::string& key) { + if (!ShallBeStoredInOverlay(key)) + underlay_->MarkNeedsEmptyValue(key); +} + +bool OverlayUserPrefStore::ReadOnly() const { + return false; +} + +PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const { + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() { + // We do not read intentionally. + OnInitializationCompleted(true); + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +void OverlayUserPrefStore::ReadPrefsAsync( + ReadErrorDelegate* error_delegate_raw) { + scoped_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw); + // We do not read intentionally. + OnInitializationCompleted(true); +} + +void OverlayUserPrefStore::CommitPendingWrite() { + underlay_->CommitPendingWrite(); + // We do not write our content intentionally. +} + +void OverlayUserPrefStore::ReportValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); +} + +void OverlayUserPrefStore::OnPrefValueChanged(const std::string& key) { + if (!overlay_.GetValue(GetOverlayKey(key), NULL)) + ReportValueChanged(GetOverlayKey(key)); +} + +void OverlayUserPrefStore::OnInitializationCompleted(bool succeeded) { + FOR_EACH_OBSERVER(PrefStore::Observer, observers_, + OnInitializationCompleted(succeeded)); +} + +void OverlayUserPrefStore::RegisterOverlayPref(const std::string& key) { + RegisterOverlayPref(key, key); +} + +void OverlayUserPrefStore::RegisterOverlayPref( + const std::string& overlay_key, + const std::string& underlay_key) { + DCHECK(!overlay_key.empty()) << "Overlay key is empty"; + DCHECK(overlay_to_underlay_names_map_.find(overlay_key) == + overlay_to_underlay_names_map_.end()) << + "Overlay key already registered"; + DCHECK(!underlay_key.empty()) << "Underlay key is empty"; + DCHECK(underlay_to_overlay_names_map_.find(underlay_key) == + underlay_to_overlay_names_map_.end()) << + "Underlay key already registered"; + overlay_to_underlay_names_map_[overlay_key] = underlay_key; + underlay_to_overlay_names_map_[underlay_key] = overlay_key; +} + +OverlayUserPrefStore::~OverlayUserPrefStore() { + underlay_->RemoveObserver(this); +} + +const std::string& OverlayUserPrefStore::GetOverlayKey( + const std::string& underlay_key) const { + NamesMap::const_iterator i = + underlay_to_overlay_names_map_.find(underlay_key); + return i != underlay_to_overlay_names_map_.end() ? i->second : underlay_key; +} + +const std::string& OverlayUserPrefStore::GetUnderlayKey( + const std::string& overlay_key) const { + NamesMap::const_iterator i = + overlay_to_underlay_names_map_.find(overlay_key); + return i != overlay_to_underlay_names_map_.end() ? i->second : overlay_key; +} + +bool OverlayUserPrefStore::ShallBeStoredInOverlay( + const std::string& key) const { + return overlay_to_underlay_names_map_.find(key) != + overlay_to_underlay_names_map_.end(); +} diff --git a/base/prefs/overlay_user_pref_store.h b/base/prefs/overlay_user_pref_store.h new file mode 100644 index 0000000..5c33c79 --- /dev/null +++ b/base/prefs/overlay_user_pref_store.h @@ -0,0 +1,84 @@ +// 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. + +#ifndef BASE_PREFS_OVERLAY_USER_PREF_STORE_H_ +#define BASE_PREFS_OVERLAY_USER_PREF_STORE_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "base/prefs/persistent_pref_store.h" +#include "base/prefs/pref_value_map.h" + +// PersistentPrefStore that directs all write operations into an in-memory +// PrefValueMap. Read operations are first answered by the PrefValueMap. +// If the PrefValueMap does not contain a value for the requested key, +// the look-up is passed on to an underlying PersistentPrefStore |underlay_|. +class OverlayUserPrefStore : public PersistentPrefStore, + public PrefStore::Observer { + public: + explicit OverlayUserPrefStore(PersistentPrefStore* underlay); + + // Returns true if a value has been set for the |key| in this + // OverlayUserPrefStore, i.e. if it potentially overrides a value + // from the |underlay_|. + virtual bool IsSetInOverlay(const std::string& key) const; + + // Methods of PrefStore. + virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE; + virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE; + virtual size_t NumberOfObservers() const OVERRIDE; + virtual bool IsInitializationComplete() const OVERRIDE; + virtual ReadResult GetValue(const std::string& key, + const base::Value** result) const OVERRIDE; + + // Methods of PersistentPrefStore. + virtual ReadResult GetMutableValue(const std::string& key, + base::Value** result) OVERRIDE; + virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE; + virtual void SetValueSilently(const std::string& key, + base::Value* value) OVERRIDE; + virtual void RemoveValue(const std::string& key) OVERRIDE; + virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE; + virtual bool ReadOnly() const OVERRIDE; + virtual PrefReadError GetReadError() const OVERRIDE; + virtual PrefReadError ReadPrefs() OVERRIDE; + virtual void ReadPrefsAsync(ReadErrorDelegate* delegate) OVERRIDE; + virtual void CommitPendingWrite() OVERRIDE; + virtual void ReportValueChanged(const std::string& key) OVERRIDE; + + // Methods of PrefStore::Observer. + virtual void OnPrefValueChanged(const std::string& key) OVERRIDE; + virtual void OnInitializationCompleted(bool succeeded) OVERRIDE; + + void RegisterOverlayPref(const std::string& key); + void RegisterOverlayPref(const std::string& overlay_key, + const std::string& underlay_key); + + protected: + virtual ~OverlayUserPrefStore(); + + private: + typedef std::map<std::string, std::string> NamesMap; + + const std::string& GetOverlayKey(const std::string& underlay_key) const; + const std::string& GetUnderlayKey(const std::string& overlay_key) const; + + // Returns true if |key| corresponds to a preference that shall be stored in + // an in-memory PrefStore that is not persisted to disk. + bool ShallBeStoredInOverlay(const std::string& key) const; + + ObserverList<PrefStore::Observer, true> observers_; + PrefValueMap overlay_; + scoped_refptr<PersistentPrefStore> underlay_; + NamesMap overlay_to_underlay_names_map_; + NamesMap underlay_to_overlay_names_map_; + + DISALLOW_COPY_AND_ASSIGN(OverlayUserPrefStore); +}; + +#endif // BASE_PREFS_OVERLAY_USER_PREF_STORE_H_ diff --git a/base/prefs/overlay_user_pref_store_unittest.cc b/base/prefs/overlay_user_pref_store_unittest.cc new file mode 100644 index 0000000..85b937f --- /dev/null +++ b/base/prefs/overlay_user_pref_store_unittest.cc @@ -0,0 +1,282 @@ +// 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 "base/prefs/overlay_user_pref_store.h" +#include "base/prefs/pref_store_observer_mock.h" +#include "base/prefs/testing_pref_store.h" +#include "base/values.h" +#include "chrome/common/pref_names.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Mock; +using ::testing::StrEq; + +namespace { + +const char* overlay_key = prefs::kBrowserWindowPlacement; +const char* regular_key = prefs::kShowBookmarkBar; +// With the removal of the kWebKitGlobalXXX prefs, we'll no longer have real +// prefs using the overlay pref store, so make up keys here. +const char* mapped_overlay_key = "test.per_tab.javascript_enabled"; +const char* mapped_underlay_key = "test.per_profile.javascript_enabled"; + +} // namespace + +class OverlayUserPrefStoreTest : public testing::Test { + protected: + OverlayUserPrefStoreTest() + : underlay_(new TestingPrefStore()), + overlay_(new OverlayUserPrefStore(underlay_.get())) { + overlay_->RegisterOverlayPref(overlay_key); + overlay_->RegisterOverlayPref(mapped_overlay_key, mapped_underlay_key); + } + + virtual ~OverlayUserPrefStoreTest() {} + + scoped_refptr<TestingPrefStore> underlay_; + scoped_refptr<OverlayUserPrefStore> overlay_; +}; + +TEST_F(OverlayUserPrefStoreTest, Observer) { + PrefStoreObserverMock obs; + overlay_->AddObserver(&obs); + + // Check that underlay first value is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); + underlay_->SetValue(overlay_key, Value::CreateIntegerValue(42)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that underlay overwriting is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); + underlay_->SetValue(overlay_key, Value::CreateIntegerValue(43)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that overwriting change in overlay is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); + overlay_->SetValue(overlay_key, Value::CreateIntegerValue(44)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that hidden underlay change is not reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0); + underlay_->SetValue(overlay_key, Value::CreateIntegerValue(45)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that overlay remove is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); + overlay_->RemoveValue(overlay_key); + Mock::VerifyAndClearExpectations(&obs); + + // Check that underlay remove is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1); + underlay_->RemoveValue(overlay_key); + Mock::VerifyAndClearExpectations(&obs); + + // Check respecting of silence. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0); + overlay_->SetValueSilently(overlay_key, Value::CreateIntegerValue(46)); + Mock::VerifyAndClearExpectations(&obs); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0); + underlay_->SetValue(overlay_key, Value::CreateIntegerValue(47)); + overlay_->SetValue(overlay_key, Value::CreateIntegerValue(48)); + Mock::VerifyAndClearExpectations(&obs); +} + +TEST_F(OverlayUserPrefStoreTest, GetAndSet) { + const Value* value = NULL; + EXPECT_EQ(PrefStore::READ_NO_VALUE, + overlay_->GetValue(overlay_key, &value)); + EXPECT_EQ(PrefStore::READ_NO_VALUE, + underlay_->GetValue(overlay_key, &value)); + + underlay_->SetValue(overlay_key, Value::CreateIntegerValue(42)); + + // Value shines through: + EXPECT_EQ(PrefStore::READ_OK, overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + EXPECT_EQ(PrefStore::READ_OK, underlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + overlay_->SetValue(overlay_key, Value::CreateIntegerValue(43)); + + EXPECT_EQ(PrefStore::READ_OK, overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + EXPECT_EQ(PrefStore::READ_OK, underlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + overlay_->RemoveValue(overlay_key); + + // Value shines through: + EXPECT_EQ(PrefStore::READ_OK, overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + EXPECT_EQ(PrefStore::READ_OK, underlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); +} + +// Check that GetMutableValue does not return the dictionary of the underlay. +TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) { + underlay_->SetValue(overlay_key, new DictionaryValue); + + Value* modify = NULL; + EXPECT_EQ(PrefStore::READ_OK, + overlay_->GetMutableValue(overlay_key, &modify)); + ASSERT_TRUE(modify); + ASSERT_TRUE(modify->IsType(Value::TYPE_DICTIONARY)); + static_cast<DictionaryValue*>(modify)->SetInteger(overlay_key, 42); + + Value* original_in_underlay = NULL; + EXPECT_EQ(PrefStore::READ_OK, + underlay_->GetMutableValue(overlay_key, &original_in_underlay)); + ASSERT_TRUE(original_in_underlay); + ASSERT_TRUE(original_in_underlay->IsType(Value::TYPE_DICTIONARY)); + EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty()); + + Value* modified = NULL; + EXPECT_EQ(PrefStore::READ_OK, + overlay_->GetMutableValue(overlay_key, &modified)); + ASSERT_TRUE(modified); + ASSERT_TRUE(modified->IsType(Value::TYPE_DICTIONARY)); + EXPECT_TRUE(Value::Equals(modify, static_cast<DictionaryValue*>(modified))); +} + +// Here we consider a global preference that is not overlayed. +TEST_F(OverlayUserPrefStoreTest, GlobalPref) { + PrefStoreObserverMock obs; + overlay_->AddObserver(&obs); + + const Value* value = NULL; + + // Check that underlay first value is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); + underlay_->SetValue(regular_key, Value::CreateIntegerValue(42)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that underlay overwriting is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); + underlay_->SetValue(regular_key, Value::CreateIntegerValue(43)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that we get this value from the overlay + EXPECT_EQ(PrefStore::READ_OK, overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + // Check that overwriting change in overlay is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); + overlay_->SetValue(regular_key, Value::CreateIntegerValue(44)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that we get this value from the overlay and the underlay. + EXPECT_EQ(PrefStore::READ_OK, overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); + EXPECT_EQ(PrefStore::READ_OK, underlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); + + // Check that overlay remove is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1); + overlay_->RemoveValue(regular_key); + Mock::VerifyAndClearExpectations(&obs); + + // Check that value was removed from overlay and underlay + EXPECT_EQ(PrefStore::READ_NO_VALUE, overlay_->GetValue(regular_key, &value)); + EXPECT_EQ(PrefStore::READ_NO_VALUE, underlay_->GetValue(regular_key, &value)); + + // Check respecting of silence. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0); + overlay_->SetValueSilently(regular_key, Value::CreateIntegerValue(46)); + Mock::VerifyAndClearExpectations(&obs); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0); + underlay_->SetValue(regular_key, Value::CreateIntegerValue(47)); + overlay_->SetValue(regular_key, Value::CreateIntegerValue(48)); + Mock::VerifyAndClearExpectations(&obs); +} + +// Check that names mapping works correctly. +TEST_F(OverlayUserPrefStoreTest, NamesMapping) { + PrefStoreObserverMock obs; + overlay_->AddObserver(&obs); + + const Value* value = NULL; + + // Check that if there is no override in the overlay, changing underlay value + // is reported as changing an overlay value. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); + underlay_->SetValue(mapped_underlay_key, Value::CreateIntegerValue(42)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that underlay overwriting is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); + underlay_->SetValue(mapped_underlay_key, Value::CreateIntegerValue(43)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that we get this value from the overlay with both keys + EXPECT_EQ(PrefStore::READ_OK, overlay_->GetValue(mapped_overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + // In this case, overlay reads directly from the underlay. + EXPECT_EQ(PrefStore::READ_OK, + overlay_->GetValue(mapped_underlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + // Check that overwriting change in overlay is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); + overlay_->SetValue(mapped_overlay_key, Value::CreateIntegerValue(44)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that we get an overriden value from overlay, while reading the + // value from underlay still holds an old value. + EXPECT_EQ(PrefStore::READ_OK, overlay_->GetValue(mapped_overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); + EXPECT_EQ(PrefStore::READ_OK, + overlay_->GetValue(mapped_underlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + EXPECT_EQ(PrefStore::READ_OK, + underlay_->GetValue(mapped_underlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + // Check that hidden underlay change is not reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0); + underlay_->SetValue(mapped_underlay_key, Value::CreateIntegerValue(45)); + Mock::VerifyAndClearExpectations(&obs); + + // Check that overlay remove is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); + overlay_->RemoveValue(mapped_overlay_key); + Mock::VerifyAndClearExpectations(&obs); + + // Check that underlay remove is reported. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1); + underlay_->RemoveValue(mapped_underlay_key); + Mock::VerifyAndClearExpectations(&obs); + + // Check that value was removed. + EXPECT_EQ(PrefStore::READ_NO_VALUE, + overlay_->GetValue(mapped_overlay_key, &value)); + EXPECT_EQ(PrefStore::READ_NO_VALUE, + overlay_->GetValue(mapped_underlay_key, &value)); + + // Check respecting of silence. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0); + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0); + overlay_->SetValueSilently(mapped_overlay_key, Value::CreateIntegerValue(46)); + Mock::VerifyAndClearExpectations(&obs); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0); + EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0); + underlay_->SetValue(mapped_underlay_key, Value::CreateIntegerValue(47)); + overlay_->SetValue(mapped_overlay_key, Value::CreateIntegerValue(48)); + Mock::VerifyAndClearExpectations(&obs); +} diff --git a/base/prefs/persistent_pref_store.h b/base/prefs/persistent_pref_store.h new file mode 100644 index 0000000..9dd0ff9 --- /dev/null +++ b/base/prefs/persistent_pref_store.h @@ -0,0 +1,94 @@ +// 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. + +#ifndef BASE_PREFS_PERSISTENT_PREF_STORE_H_ +#define BASE_PREFS_PERSISTENT_PREF_STORE_H_ + +#include <string> + +#include "base/prefs/pref_store.h" + +// This interface is complementary to the PrefStore interface, declaring +// additional functionality that adds support for setting values and persisting +// the data to some backing store. +class PersistentPrefStore : public PrefStore { + public: + // Unique integer code for each type of error so we can report them + // distinctly in a histogram. + // NOTE: Don't change the order here as it will change the server's meaning + // of the histogram. + enum PrefReadError { + PREF_READ_ERROR_NONE = 0, + PREF_READ_ERROR_JSON_PARSE, + PREF_READ_ERROR_JSON_TYPE, + PREF_READ_ERROR_ACCESS_DENIED, + PREF_READ_ERROR_FILE_OTHER, + PREF_READ_ERROR_FILE_LOCKED, + PREF_READ_ERROR_NO_FILE, + PREF_READ_ERROR_JSON_REPEAT, + PREF_READ_ERROR_OTHER, + PREF_READ_ERROR_FILE_NOT_SPECIFIED, + PREF_READ_ERROR_MAX_ENUM + }; + + class ReadErrorDelegate { + public: + virtual ~ReadErrorDelegate() {} + + virtual void OnError(PrefReadError error) = 0; + }; + + // Equivalent to PrefStore::GetValue but returns a mutable value. + virtual ReadResult GetMutableValue(const std::string& key, + base::Value** result) = 0; + + // Triggers a value changed notification. This function needs to be called + // if one retrieves a list or dictionary with GetMutableValue and change its + // value. SetValue takes care of notifications itself. Note that + // ReportValueChanged will trigger notifications even if nothing has changed. + virtual void ReportValueChanged(const std::string& key) = 0; + + // Sets a |value| for |key| in the store. Assumes ownership of |value|, which + // must be non-NULL. + virtual void SetValue(const std::string& key, base::Value* value) = 0; + + // Same as SetValue, but doesn't generate notifications. This is used by + // PrefService::GetMutableUserPref() in order to put empty entries + // into the user pref store. Using SetValue is not an option since existing + // tests rely on the number of notifications generated. + virtual void SetValueSilently(const std::string& key, base::Value* value) = 0; + + // Removes the value for |key|. + virtual void RemoveValue(const std::string& key) = 0; + + // Marks that the |key| with empty ListValue/DictionaryValue needs to be + // persisted. + virtual void MarkNeedsEmptyValue(const std::string& key) = 0; + + // Whether the store is in a pseudo-read-only mode where changes are not + // actually persisted to disk. This happens in some cases when there are + // read errors during startup. + virtual bool ReadOnly() const = 0; + + // Gets the read error. Only valid if IsInitializationComplete() returns true. + virtual PrefReadError GetReadError() const = 0; + + // Reads the preferences from disk. Notifies observers via + // "PrefStore::OnInitializationCompleted" when done. + virtual PrefReadError ReadPrefs() = 0; + + // Reads the preferences from disk asynchronously. Notifies observers via + // "PrefStore::OnInitializationCompleted" when done. Also it fires + // |error_delegate| if it is not NULL and reading error has occurred. + // Owns |error_delegate|. + virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0; + + // Lands any pending writes to disk. + virtual void CommitPendingWrite() = 0; + + protected: + virtual ~PersistentPrefStore() {} +}; + +#endif // BASE_PREFS_PERSISTENT_PREF_STORE_H_ diff --git a/base/prefs/pref_notifier.h b/base/prefs/pref_notifier.h new file mode 100644 index 0000000..e0df260 --- /dev/null +++ b/base/prefs/pref_notifier.h @@ -0,0 +1,26 @@ +// 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 BASE_PREFS_PREF_NOTIFIER_H_ +#define BASE_PREFS_PREF_NOTIFIER_H_ + +#include <string> + +// Delegate interface used by PrefValueStore to notify its owner about changes +// to the preference values. +// TODO(mnissler, danno): Move this declaration to pref_value_store.h once we've +// cleaned up all public uses of this interface. +class PrefNotifier { + public: + virtual ~PrefNotifier() {} + + // Sends out a change notification for the preference identified by + // |pref_name|. + virtual void OnPreferenceChanged(const std::string& pref_name) = 0; + + // Broadcasts the intialization completed notification. + virtual void OnInitializationCompleted(bool succeeded) = 0; +}; + +#endif // BASE_PREFS_PREF_NOTIFIER_H_ diff --git a/base/prefs/pref_store.cc b/base/prefs/pref_store.cc new file mode 100644 index 0000000..0521654 --- /dev/null +++ b/base/prefs/pref_store.cc @@ -0,0 +1,13 @@ +// 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 "base/prefs/pref_store.h" + +size_t PrefStore::NumberOfObservers() const { + return 0; +} + +bool PrefStore::IsInitializationComplete() const { + return true; +} diff --git a/base/prefs/pref_store.h b/base/prefs/pref_store.h new file mode 100644 index 0000000..c54fb4e --- /dev/null +++ b/base/prefs/pref_store.h @@ -0,0 +1,72 @@ +// 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. + +#ifndef BASE_PREFS_PREF_STORE_H_ +#define BASE_PREFS_PREF_STORE_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" + +namespace base { +class Value; +} + +// This is an abstract interface for reading and writing from/to a persistent +// preference store, used by PrefService. An implementation using a JSON file +// can be found in JsonPrefStore, while an implementation without any backing +// store for testing can be found in TestingPrefStore. Furthermore, there is +// CommandLinePrefStore, which bridges command line options to preferences and +// ConfigurationPolicyPrefStore, which is used for hooking up configuration +// policy with the preference subsystem. +class PrefStore : public base::RefCounted<PrefStore> { + public: + // Observer interface for monitoring PrefStore. + class Observer { + public: + // Called when the value for the given |key| in the store changes. + virtual void OnPrefValueChanged(const std::string& key) = 0; + // Notification about the PrefStore being fully initialized. + virtual void OnInitializationCompleted(bool succeeded) = 0; + + protected: + virtual ~Observer() {} + }; + + // Return values for GetValue(). + enum ReadResult { + // Value found and returned. + READ_OK, + // No value present, but skip other pref stores and use default. + READ_USE_DEFAULT, + // No value present. + READ_NO_VALUE, + }; + + PrefStore() {} + + // Add and remove observers. + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual size_t NumberOfObservers() const; + + // Whether the store has completed all asynchronous initialization. + virtual bool IsInitializationComplete() const; + + // Get the value for a given preference |key| and stores it in |*result|. + // |*result| is only modified if the return value is READ_OK and if |result| + // is not NULL. Ownership of the |*result| value remains with the PrefStore. + virtual ReadResult GetValue(const std::string& key, + const base::Value** result) const = 0; + + protected: + friend class base::RefCounted<PrefStore>; + virtual ~PrefStore() {} + + private: + DISALLOW_COPY_AND_ASSIGN(PrefStore); +}; + +#endif // BASE_PREFS_PREF_STORE_H_ diff --git a/base/prefs/pref_store_observer_mock.cc b/base/prefs/pref_store_observer_mock.cc new file mode 100644 index 0000000..0970e63 --- /dev/null +++ b/base/prefs/pref_store_observer_mock.cc @@ -0,0 +1,9 @@ +// 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 "base/prefs/pref_store_observer_mock.h" + +PrefStoreObserverMock::PrefStoreObserverMock() {} + +PrefStoreObserverMock::~PrefStoreObserverMock() {} diff --git a/base/prefs/pref_store_observer_mock.h b/base/prefs/pref_store_observer_mock.h new file mode 100644 index 0000000..8252c3b --- /dev/null +++ b/base/prefs/pref_store_observer_mock.h @@ -0,0 +1,25 @@ +// 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 BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_ +#define BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_ + +#include "base/basictypes.h" +#include "base/prefs/pref_store.h" +#include "testing/gmock/include/gmock/gmock.h" + +// A gmock-ified implementation of PrefStore::Observer. +class PrefStoreObserverMock : public PrefStore::Observer { + public: + PrefStoreObserverMock(); + virtual ~PrefStoreObserverMock(); + + MOCK_METHOD1(OnPrefValueChanged, void(const std::string&)); + MOCK_METHOD1(OnInitializationCompleted, void(bool)); + + private: + DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock); +}; + +#endif // BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_ diff --git a/base/prefs/pref_value_map.cc b/base/prefs/pref_value_map.cc new file mode 100644 index 0000000..48e8fa3 --- /dev/null +++ b/base/prefs/pref_value_map.cc @@ -0,0 +1,151 @@ +// 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 "base/prefs/pref_value_map.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "base/values.h" + +PrefValueMap::PrefValueMap() {} + +PrefValueMap::~PrefValueMap() { + Clear(); +} + +bool PrefValueMap::GetValue(const std::string& key, const Value** value) const { + const Map::const_iterator entry = prefs_.find(key); + if (entry != prefs_.end()) { + if (value) + *value = entry->second; + return true; + } + + return false; +} + +bool PrefValueMap::GetValue(const std::string& key, Value** value) { + const Map::const_iterator entry = prefs_.find(key); + if (entry != prefs_.end()) { + if (value) + *value = entry->second; + return true; + } + + return false; +} + +bool PrefValueMap::SetValue(const std::string& key, Value* value) { + DCHECK(value); + scoped_ptr<Value> value_ptr(value); + const Map::iterator entry = prefs_.find(key); + if (entry != prefs_.end()) { + if (Value::Equals(entry->second, value)) + return false; + delete entry->second; + entry->second = value_ptr.release(); + } else { + prefs_[key] = value_ptr.release(); + } + + return true; +} + +bool PrefValueMap::RemoveValue(const std::string& key) { + const Map::iterator entry = prefs_.find(key); + if (entry != prefs_.end()) { + delete entry->second; + prefs_.erase(entry); + return true; + } + + return false; +} + +void PrefValueMap::Clear() { + STLDeleteValues(&prefs_); + prefs_.clear(); +} + +void PrefValueMap::Swap(PrefValueMap* other) { + prefs_.swap(other->prefs_); +} + +PrefValueMap::iterator PrefValueMap::begin() { + return prefs_.begin(); +} + +PrefValueMap::iterator PrefValueMap::end() { + return prefs_.end(); +} + +PrefValueMap::const_iterator PrefValueMap::begin() const { + return prefs_.begin(); +} + +PrefValueMap::const_iterator PrefValueMap::end() const { + return prefs_.end(); +} + +bool PrefValueMap::GetBoolean(const std::string& key, + bool* value) const { + const Value* stored_value = NULL; + return GetValue(key, &stored_value) && stored_value->GetAsBoolean(value); +} + +void PrefValueMap::SetBoolean(const std::string& key, bool value) { + SetValue(key, Value::CreateBooleanValue(value)); +} + +bool PrefValueMap::GetString(const std::string& key, + std::string* value) const { + const Value* stored_value = NULL; + return GetValue(key, &stored_value) && stored_value->GetAsString(value); +} + +void PrefValueMap::SetString(const std::string& key, + const std::string& value) { + SetValue(key, Value::CreateStringValue(value)); +} + +bool PrefValueMap::GetInteger(const std::string& key, int* value) const { + const Value* stored_value = NULL; + return GetValue(key, &stored_value) && stored_value->GetAsInteger(value); +} + +void PrefValueMap::SetInteger(const std::string& key, const int value) { + SetValue(key, Value::CreateIntegerValue(value)); +} + +void PrefValueMap::GetDifferingKeys( + const PrefValueMap* other, + std::vector<std::string>* differing_keys) const { + differing_keys->clear(); + + // Walk over the maps in lockstep, adding everything that is different. + Map::const_iterator this_pref(prefs_.begin()); + Map::const_iterator other_pref(other->prefs_.begin()); + while (this_pref != prefs_.end() && other_pref != other->prefs_.end()) { + const int diff = this_pref->first.compare(other_pref->first); + if (diff == 0) { + if (!this_pref->second->Equals(other_pref->second)) + differing_keys->push_back(this_pref->first); + ++this_pref; + ++other_pref; + } else if (diff < 0) { + differing_keys->push_back(this_pref->first); + ++this_pref; + } else if (diff > 0) { + differing_keys->push_back(other_pref->first); + ++other_pref; + } + } + + // Add the remaining entries. + for ( ; this_pref != prefs_.end(); ++this_pref) + differing_keys->push_back(this_pref->first); + for ( ; other_pref != other->prefs_.end(); ++other_pref) + differing_keys->push_back(other_pref->first); +} diff --git a/base/prefs/pref_value_map.h b/base/prefs/pref_value_map.h new file mode 100644 index 0000000..9c846dc --- /dev/null +++ b/base/prefs/pref_value_map.h @@ -0,0 +1,87 @@ +// 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 BASE_PREFS_PREF_VALUE_MAP_H_ +#define BASE_PREFS_PREF_VALUE_MAP_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" + +namespace base { +class Value; +} + +// A generic string to value map used by the PrefStore implementations. +class PrefValueMap { + public: + typedef std::map<std::string, base::Value*>::iterator iterator; + typedef std::map<std::string, base::Value*>::const_iterator const_iterator; + + PrefValueMap(); + virtual ~PrefValueMap(); + + // Gets the value for |key| and stores it in |value|. Ownership remains with + // the map. Returns true if a value is present. If not, |value| is not + // touched. + bool GetValue(const std::string& key, const base::Value** value) const; + bool GetValue(const std::string& key, base::Value** value); + + // Sets a new |value| for |key|. Takes ownership of |value|, which must be + // non-NULL. Returns true if the value changed. + bool SetValue(const std::string& key, base::Value* value); + + // Removes the value for |key| from the map. Returns true if a value was + // removed. + bool RemoveValue(const std::string& key); + + // Clears the map. + void Clear(); + + // Swaps the contents of two maps. + void Swap(PrefValueMap* other); + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + // Gets a boolean value for |key| and stores it in |value|. Returns true if + // the value was found and of the proper type. + bool GetBoolean(const std::string& key, bool* value) const; + + // Sets the value for |key| to the boolean |value|. + void SetBoolean(const std::string& key, bool value); + + // Gets a string value for |key| and stores it in |value|. Returns true if + // the value was found and of the proper type. + bool GetString(const std::string& key, std::string* value) const; + + // Sets the value for |key| to the string |value|. + void SetString(const std::string& key, const std::string& value); + + // Gets an int value for |key| and stores it in |value|. Returns true if + // the value was found and of the proper type. + bool GetInteger(const std::string& key, int* value) const; + + // Sets the value for |key| to the int |value|. + void SetInteger(const std::string& key, const int value); + + // Compares this value map against |other| and stores all key names that have + // different values in |differing_keys|. This includes keys that are present + // only in one of the maps. + void GetDifferingKeys(const PrefValueMap* other, + std::vector<std::string>* differing_keys) const; + + private: + typedef std::map<std::string, base::Value*> Map; + + Map prefs_; + + DISALLOW_COPY_AND_ASSIGN(PrefValueMap); +}; + +#endif // BASE_PREFS_PREF_VALUE_MAP_H_ diff --git a/base/prefs/pref_value_map_unittest.cc b/base/prefs/pref_value_map_unittest.cc new file mode 100644 index 0000000..b406db8 --- /dev/null +++ b/base/prefs/pref_value_map_unittest.cc @@ -0,0 +1,111 @@ +// 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 "base/prefs/pref_value_map.h" +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest.h" + +class PrefValueMapTest : public testing::Test { +}; + +TEST_F(PrefValueMapTest, SetValue) { + PrefValueMap map; + const Value* result = NULL; + EXPECT_FALSE(map.GetValue("key", &result)); + EXPECT_FALSE(result); + + EXPECT_TRUE(map.SetValue("key", Value::CreateStringValue("test"))); + EXPECT_FALSE(map.SetValue("key", Value::CreateStringValue("test"))); + EXPECT_TRUE(map.SetValue("key", Value::CreateStringValue("hi mom!"))); + + EXPECT_TRUE(map.GetValue("key", &result)); + EXPECT_TRUE(StringValue("hi mom!").Equals(result)); +} + +TEST_F(PrefValueMapTest, GetAndSetIntegerValue) { + PrefValueMap map; + ASSERT_TRUE(map.SetValue("key", Value::CreateIntegerValue(5))); + + int int_value = 0; + EXPECT_TRUE(map.GetInteger("key", &int_value)); + EXPECT_EQ(5, int_value); + + map.SetInteger("key", -14); + EXPECT_TRUE(map.GetInteger("key", &int_value)); + EXPECT_EQ(-14, int_value); +} + +TEST_F(PrefValueMapTest, RemoveValue) { + PrefValueMap map; + EXPECT_FALSE(map.RemoveValue("key")); + + EXPECT_TRUE(map.SetValue("key", Value::CreateStringValue("test"))); + EXPECT_TRUE(map.GetValue("key", NULL)); + + EXPECT_TRUE(map.RemoveValue("key")); + EXPECT_FALSE(map.GetValue("key", NULL)); + + EXPECT_FALSE(map.RemoveValue("key")); +} + +TEST_F(PrefValueMapTest, Clear) { + PrefValueMap map; + EXPECT_TRUE(map.SetValue("key", Value::CreateStringValue("test"))); + EXPECT_TRUE(map.GetValue("key", NULL)); + + map.Clear(); + + EXPECT_FALSE(map.GetValue("key", NULL)); +} + +TEST_F(PrefValueMapTest, GetDifferingKeys) { + PrefValueMap reference; + EXPECT_TRUE(reference.SetValue("b", Value::CreateStringValue("test"))); + EXPECT_TRUE(reference.SetValue("c", Value::CreateStringValue("test"))); + EXPECT_TRUE(reference.SetValue("e", Value::CreateStringValue("test"))); + + PrefValueMap check; + std::vector<std::string> differing_paths; + std::vector<std::string> expected_differing_paths; + + reference.GetDifferingKeys(&check, &differing_paths); + expected_differing_paths.push_back("b"); + expected_differing_paths.push_back("c"); + expected_differing_paths.push_back("e"); + EXPECT_EQ(expected_differing_paths, differing_paths); + + EXPECT_TRUE(check.SetValue("a", Value::CreateStringValue("test"))); + EXPECT_TRUE(check.SetValue("c", Value::CreateStringValue("test"))); + EXPECT_TRUE(check.SetValue("d", Value::CreateStringValue("test"))); + + reference.GetDifferingKeys(&check, &differing_paths); + expected_differing_paths.clear(); + expected_differing_paths.push_back("a"); + expected_differing_paths.push_back("b"); + expected_differing_paths.push_back("d"); + expected_differing_paths.push_back("e"); + EXPECT_EQ(expected_differing_paths, differing_paths); +} + +TEST_F(PrefValueMapTest, SwapTwoMaps) { + PrefValueMap first_map; + EXPECT_TRUE(first_map.SetValue("a", Value::CreateStringValue("test"))); + EXPECT_TRUE(first_map.SetValue("b", Value::CreateStringValue("test"))); + EXPECT_TRUE(first_map.SetValue("c", Value::CreateStringValue("test"))); + + PrefValueMap second_map; + EXPECT_TRUE(second_map.SetValue("d", Value::CreateStringValue("test"))); + EXPECT_TRUE(second_map.SetValue("e", Value::CreateStringValue("test"))); + EXPECT_TRUE(second_map.SetValue("f", Value::CreateStringValue("test"))); + + first_map.Swap(&second_map); + + EXPECT_TRUE(first_map.GetValue("d", NULL)); + EXPECT_TRUE(first_map.GetValue("e", NULL)); + EXPECT_TRUE(first_map.GetValue("f", NULL)); + + EXPECT_TRUE(second_map.GetValue("a", NULL)); + EXPECT_TRUE(second_map.GetValue("b", NULL)); + EXPECT_TRUE(second_map.GetValue("c", NULL)); +} diff --git a/base/prefs/public/pref_change_registrar.cc b/base/prefs/public/pref_change_registrar.cc new file mode 100644 index 0000000..e7a9791 --- /dev/null +++ b/base/prefs/public/pref_change_registrar.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2010 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 "base/prefs/public/pref_change_registrar.h" + +#include "base/logging.h" +#include "base/prefs/public/pref_service_base.h" + +PrefChangeRegistrar::PrefChangeRegistrar() : service_(NULL) {} + +PrefChangeRegistrar::~PrefChangeRegistrar() { + // If you see an invalid memory access in this destructor, this + // PrefChangeRegistrar might be subscribed to an OffTheRecordProfileImpl that + // has been destroyed. This should not happen any more but be warned. + // Feel free to contact battre@chromium.org in case this happens. + RemoveAll(); +} + +void PrefChangeRegistrar::Init(PrefServiceBase* service) { + DCHECK(IsEmpty() || service_ == service); + service_ = service; +} + +void PrefChangeRegistrar::Add(const char* path, + content::NotificationObserver* obs) { + if (!service_) { + NOTREACHED(); + return; + } + ObserverRegistration registration(path, obs); + if (observers_.find(registration) != observers_.end()) { + NOTREACHED(); + return; + } + observers_.insert(registration); + service_->AddPrefObserver(path, obs); +} + +void PrefChangeRegistrar::Remove(const char* path, + content::NotificationObserver* obs) { + if (!service_) { + NOTREACHED(); + return; + } + ObserverRegistration registration(path, obs); + std::set<ObserverRegistration>::iterator it = + observers_.find(registration); + if (it == observers_.end()) { + NOTREACHED(); + return; + } + service_->RemovePrefObserver(it->first.c_str(), it->second); + observers_.erase(it); +} + +void PrefChangeRegistrar::RemoveAll() { + if (service_) { + for (std::set<ObserverRegistration>::const_iterator it = observers_.begin(); + it != observers_.end(); ++it) { + service_->RemovePrefObserver(it->first.c_str(), it->second); + } + observers_.clear(); + } +} + +bool PrefChangeRegistrar::IsEmpty() const { + return observers_.empty(); +} + +bool PrefChangeRegistrar::IsObserved(const std::string& pref) { + for (std::set<ObserverRegistration>::const_iterator it = observers_.begin(); + it != observers_.end(); ++it) { + if (it->first == pref) + return true; + } + return false; +} + +bool PrefChangeRegistrar::IsManaged() { + for (std::set<ObserverRegistration>::const_iterator it = observers_.begin(); + it != observers_.end(); ++it) { + const PrefServiceBase::Preference* pref = + service_->FindPreference(it->first.c_str()); + if (pref && pref->IsManaged()) + return true; + } + return false; +} diff --git a/base/prefs/public/pref_change_registrar.h b/base/prefs/public/pref_change_registrar.h new file mode 100644 index 0000000..97743e2 --- /dev/null +++ b/base/prefs/public/pref_change_registrar.h @@ -0,0 +1,66 @@ +// Copyright (c) 2010 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 BASE_PREFS_PUBLIC_PREF_CHANGE_REGISTRAR_H_ +#define BASE_PREFS_PUBLIC_PREF_CHANGE_REGISTRAR_H_ + +#include <set> +#include <string> + +#include "base/basictypes.h" + +class PrefServiceBase; + +namespace content { +class NotificationObserver; +} + +// Automatically manages the registration of one or more pref change observers +// with a PrefStore. Functions much like NotificationRegistrar, but specifically +// manages observers of preference changes. When the Registrar is destroyed, +// all registered observers are automatically unregistered with the PrefStore. +class PrefChangeRegistrar { + public: + PrefChangeRegistrar(); + virtual ~PrefChangeRegistrar(); + + // Must be called before adding or removing observers. Can be called more + // than once as long as the value of |service| doesn't change. + void Init(PrefServiceBase* service); + + // Adds an pref observer for the specified pref |path| and |obs| observer + // object. All registered observers will be automatically unregistered + // when the registrar's destructor is called unless the observer has been + // explicitly removed by a call to Remove beforehand. + void Add(const char* path, + content::NotificationObserver* obs); + + // Removes a preference observer that has previously been added with a call to + // Add. + void Remove(const char* path, + content::NotificationObserver* obs); + + // Removes all observers that have been previously added with a call to Add. + void RemoveAll(); + + // Returns true if no pref observers are registered. + bool IsEmpty() const; + + // Check whether |pref| is in the set of preferences being observed. + bool IsObserved(const std::string& pref); + + // Check whether any of the observed preferences has the managed bit set. + bool IsManaged(); + + private: + typedef std::pair<std::string, content::NotificationObserver*> + ObserverRegistration; + + std::set<ObserverRegistration> observers_; + PrefServiceBase* service_; + + DISALLOW_COPY_AND_ASSIGN(PrefChangeRegistrar); +}; + +#endif // BASE_PREFS_PUBLIC_PREF_CHANGE_REGISTRAR_H_ diff --git a/base/prefs/public/pref_change_registrar_unittest.cc b/base/prefs/public/pref_change_registrar_unittest.cc new file mode 100644 index 0000000..491d7db --- /dev/null +++ b/base/prefs/public/pref_change_registrar_unittest.cc @@ -0,0 +1,207 @@ +// Copyright (c) 2010 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 "base/prefs/public/pref_change_registrar.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/testing_pref_service.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/notification_types.h" +#include "content/public/test/mock_notification_observer.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; +using testing::Eq; + +namespace { + +// A mock provider that allows us to capture pref observer changes. +class MockPrefService : public TestingPrefService { + public: + MockPrefService() {} + virtual ~MockPrefService() {} + + MOCK_METHOD2(AddPrefObserver, + void(const char*, content::NotificationObserver*)); + MOCK_METHOD2(RemovePrefObserver, + void(const char*, content::NotificationObserver*)); +}; + +} // namespace + +class PrefChangeRegistrarTest : public testing::Test { + public: + PrefChangeRegistrarTest() {} + virtual ~PrefChangeRegistrarTest() {} + + protected: + virtual void SetUp(); + + content::NotificationObserver* observer() const { return observer_.get(); } + MockPrefService* service() const { return service_.get(); } + + private: + scoped_ptr<MockPrefService> service_; + scoped_ptr<content::MockNotificationObserver> observer_; +}; + +void PrefChangeRegistrarTest::SetUp() { + service_.reset(new MockPrefService()); + observer_.reset(new content::MockNotificationObserver()); +} + +TEST_F(PrefChangeRegistrarTest, AddAndRemove) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + // Test adding. + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), observer())); + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.2")), observer())); + registrar.Add("test.pref.1", observer()); + registrar.Add("test.pref.2", observer()); + EXPECT_FALSE(registrar.IsEmpty()); + + // Test removing. + Mock::VerifyAndClearExpectations(service()); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.1")), observer())); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.2")), observer())); + registrar.Remove("test.pref.1", observer()); + registrar.Remove("test.pref.2", observer()); + EXPECT_TRUE(registrar.IsEmpty()); + + // Explicitly check the expectations now to make sure that the Removes + // worked (rather than the registrar destructor doing the work). + Mock::VerifyAndClearExpectations(service()); +} + +TEST_F(PrefChangeRegistrarTest, AutoRemove) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + // Setup of auto-remove. + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), observer())); + registrar.Add("test.pref.1", observer()); + Mock::VerifyAndClearExpectations(service()); + EXPECT_FALSE(registrar.IsEmpty()); + + // Test auto-removing. + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.1")), observer())); +} + +TEST_F(PrefChangeRegistrarTest, RemoveAll) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), observer())); + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.2")), observer())); + registrar.Add("test.pref.1", observer()); + registrar.Add("test.pref.2", observer()); + Mock::VerifyAndClearExpectations(service()); + + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.1")), observer())); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.2")), observer())); + registrar.RemoveAll(); + EXPECT_TRUE(registrar.IsEmpty()); + + // Explicitly check the expectations now to make sure that the RemoveAll + // worked (rather than the registrar destructor doing the work). + Mock::VerifyAndClearExpectations(service()); +} + +class ObserveSetOfPreferencesTest : public testing::Test { + public: + virtual void SetUp() { + pref_service_.reset(new TestingPrefService); + pref_service_->RegisterStringPref(prefs::kHomePage, + "http://google.com", + PrefService::UNSYNCABLE_PREF); + pref_service_->RegisterBooleanPref(prefs::kHomePageIsNewTabPage, + false, + PrefService::UNSYNCABLE_PREF); + pref_service_->RegisterStringPref(prefs::kApplicationLocale, + "", + PrefService::UNSYNCABLE_PREF); + } + + PrefChangeRegistrar* CreatePrefChangeRegistrar( + content::NotificationObserver* observer) { + PrefChangeRegistrar* pref_set = new PrefChangeRegistrar(); + pref_set->Init(pref_service_.get()); + pref_set->Add(prefs::kHomePage, observer); + pref_set->Add(prefs::kHomePageIsNewTabPage, observer); + return pref_set; + } + + scoped_ptr<TestingPrefService> pref_service_; +}; + +TEST_F(ObserveSetOfPreferencesTest, IsObserved) { + scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar(NULL)); + EXPECT_TRUE(pref_set->IsObserved(prefs::kHomePage)); + EXPECT_TRUE(pref_set->IsObserved(prefs::kHomePageIsNewTabPage)); + EXPECT_FALSE(pref_set->IsObserved(prefs::kApplicationLocale)); +} + +TEST_F(ObserveSetOfPreferencesTest, IsManaged) { + scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar(NULL)); + EXPECT_FALSE(pref_set->IsManaged()); + pref_service_->SetManagedPref(prefs::kHomePage, + Value::CreateStringValue("http://crbug.com")); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->SetManagedPref(prefs::kHomePageIsNewTabPage, + Value::CreateBooleanValue(true)); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->RemoveManagedPref(prefs::kHomePage); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->RemoveManagedPref(prefs::kHomePageIsNewTabPage); + EXPECT_FALSE(pref_set->IsManaged()); +} + +MATCHER_P(PrefNameDetails, name, "details references named preference") { + std::string* pstr = + reinterpret_cast<const content::Details<std::string>&>(arg).ptr(); + return pstr && *pstr == name; +} + +TEST_F(ObserveSetOfPreferencesTest, Observe) { + using testing::_; + using testing::Mock; + + content::MockNotificationObserver observer; + scoped_ptr<PrefChangeRegistrar> pref_set( + CreatePrefChangeRegistrar(&observer)); + + EXPECT_CALL(observer, + Observe(int(chrome::NOTIFICATION_PREF_CHANGED), + content::Source<PrefService>(pref_service_.get()), + PrefNameDetails(prefs::kHomePage))); + pref_service_->SetUserPref(prefs::kHomePage, + Value::CreateStringValue("http://crbug.com")); + Mock::VerifyAndClearExpectations(&observer); + + EXPECT_CALL(observer, + Observe(int(chrome::NOTIFICATION_PREF_CHANGED), + content::Source<PrefService>(pref_service_.get()), + PrefNameDetails(prefs::kHomePageIsNewTabPage))); + pref_service_->SetUserPref(prefs::kHomePageIsNewTabPage, + Value::CreateBooleanValue(true)); + Mock::VerifyAndClearExpectations(&observer); + + EXPECT_CALL(observer, Observe(_, _, _)).Times(0); + pref_service_->SetUserPref(prefs::kApplicationLocale, + Value::CreateStringValue("en_US.utf8")); + Mock::VerifyAndClearExpectations(&observer); +} diff --git a/base/prefs/public/pref_service_base.h b/base/prefs/public/pref_service_base.h new file mode 100644 index 0000000..aa5b3fd --- /dev/null +++ b/base/prefs/public/pref_service_base.h @@ -0,0 +1,272 @@ +// 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. + +// This is the base interface for a preference services that provides +// a way to access the application's current preferences. +// +// This base interface assumes all preferences are local. See +// SyncablePrefServiceBase for the interface to a preference service +// that stores preferences that can be synced. +// +// Chromium settings and storage represent user-selected preferences and +// information and MUST not be extracted, overwritten or modified except +// through Chromium defined APIs. + +#ifndef BASE_PREFS_PUBLIC_PREF_SERVICE_BASE_H_ +#define BASE_PREFS_PUBLIC_PREF_SERVICE_BASE_H_ + +#include "base/values.h" + +namespace content { +class BrowserContext; +class NotificationObserver; +} + +namespace subtle { +class PrefMemberBase; +} + +class FilePath; +class Profile; +class TabContents; + +class PrefServiceBase { + public: + // Retrieves a PrefServiceBase for the given context. + static PrefServiceBase* FromBrowserContext(content::BrowserContext* context); + + virtual ~PrefServiceBase() {} + + // Enum used when registering preferences to determine if it should be synced + // or not. This is only used for profile prefs, not local state prefs. + // See the Register*Pref methods for profile prefs below. + enum PrefSyncStatus { + UNSYNCABLE_PREF, + SYNCABLE_PREF + }; + + // Interface to a single preference. + class Preference { + public: + virtual ~Preference() {} + + // Returns the name of the Preference (i.e., the key, e.g., + // browser.window_placement). + virtual const std::string name() const = 0; + + // Returns the registered type of the preference. + virtual base::Value::Type GetType() const = 0; + + // Returns the value of the Preference, falling back to the registered + // default value if no other has been set. + virtual const base::Value* GetValue() const = 0; + + // Returns the value recommended by the admin, if any. + virtual const base::Value* GetRecommendedValue() const = 0; + + // Returns true if the Preference is managed, i.e. set by an admin policy. + // Since managed prefs have the highest priority, this also indicates + // whether the pref is actually being controlled by the policy setting. + virtual bool IsManaged() const = 0; + + // Returns true if the Preference is recommended, i.e. set by an admin + // policy but the user is allowed to change it. + virtual bool IsRecommended() const = 0; + + // Returns true if the Preference has a value set by an extension, even if + // that value is being overridden by a higher-priority source. + virtual bool HasExtensionSetting() const = 0; + + // Returns true if the Preference has a user setting, even if that value is + // being overridden by a higher-priority source. + virtual bool HasUserSetting() const = 0; + + // Returns true if the Preference value is currently being controlled by an + // extension, and not by any higher-priority source. + virtual bool IsExtensionControlled() const = 0; + + // Returns true if the Preference value is currently being controlled by a + // user setting, and not by any higher-priority source. + virtual bool IsUserControlled() const = 0; + + // Returns true if the Preference is currently using its default value, + // and has not been set by any higher-priority source (even with the same + // value). + virtual bool IsDefaultValue() const = 0; + + // Returns true if the user can change the Preference value, which is the + // case if no higher-priority source than the user store controls the + // Preference. + virtual bool IsUserModifiable() const = 0; + + // Returns true if an extension can change the Preference value, which is + // the case if no higher-priority source than the extension store controls + // the Preference. + virtual bool IsExtensionModifiable() const = 0; + }; + + // Returns true if the preference for the given preference name is available + // and is managed. + virtual bool IsManagedPreference(const char* pref_name) const = 0; + + // Returns |true| if a preference with the given name is available and its + // value can be changed by the user. + virtual bool IsUserModifiablePreference(const char* pref_name) const = 0; + + // Make the PrefService aware of a pref. + // TODO(zea): split local state and profile prefs into their own subclasses. + // ---------- Local state prefs ---------- + virtual void RegisterBooleanPref(const char* path, + bool default_value) = 0; + virtual void RegisterIntegerPref(const char* path, + int default_value) = 0; + virtual void RegisterDoublePref(const char* path, + double default_value) = 0; + virtual void RegisterStringPref(const char* path, + const std::string& default_value) = 0; + virtual void RegisterFilePathPref(const char* path, + const FilePath& default_value) = 0; + virtual void RegisterListPref(const char* path) = 0; + virtual void RegisterDictionaryPref(const char* path) = 0; + // These take ownership of the default_value: + virtual void RegisterListPref(const char* path, + base::ListValue* default_value) = 0; + virtual void RegisterDictionaryPref( + const char* path, base::DictionaryValue* default_value) = 0; + // These variants use a default value from the locale dll instead. + virtual void RegisterLocalizedBooleanPref( + const char* path, int locale_default_message_id) = 0; + virtual void RegisterLocalizedIntegerPref( + const char* path, int locale_default_message_id) = 0; + virtual void RegisterLocalizedDoublePref( + const char* path, int locale_default_message_id) = 0; + virtual void RegisterLocalizedStringPref( + const char* path, int locale_default_message_id) = 0; + virtual void RegisterInt64Pref(const char* path, + int64 default_value) = 0; + + // ---------- Profile prefs ---------- + // Profile prefs must specify whether the pref should be synchronized across + // machines or not (see PrefSyncStatus enum above). + virtual void RegisterBooleanPref(const char* path, + bool default_value, + PrefSyncStatus sync_status) = 0; + virtual void RegisterIntegerPref(const char* path, + int default_value, + PrefSyncStatus sync_status) = 0; + virtual void RegisterDoublePref(const char* path, + double default_value, + PrefSyncStatus sync_status) = 0; + virtual void RegisterStringPref(const char* path, + const std::string& default_value, + PrefSyncStatus sync_status) = 0; + virtual void RegisterFilePathPref(const char* path, + const FilePath& default_value, + PrefSyncStatus sync_status) = 0; + virtual void RegisterListPref(const char* path, + PrefSyncStatus sync_status) = 0; + virtual void RegisterDictionaryPref(const char* path, + PrefSyncStatus sync_status) = 0; + // These take ownership of the default_value: + virtual void RegisterListPref(const char* path, + base::ListValue* default_value, + PrefSyncStatus sync_status) = 0; + virtual void RegisterDictionaryPref(const char* path, + base::DictionaryValue* default_value, + PrefSyncStatus sync_status) = 0; + // These variants use a default value from the locale dll instead. + virtual void RegisterLocalizedBooleanPref( + const char* path, + int locale_default_message_id, + PrefSyncStatus sync_status) = 0; + virtual void RegisterLocalizedIntegerPref( + const char* path, + int locale_default_message_id, + PrefSyncStatus sync_status) = 0; + virtual void RegisterLocalizedDoublePref( + const char* path, + int locale_default_message_id, + PrefSyncStatus sync_status) = 0; + virtual void RegisterLocalizedStringPref( + const char* path, + int locale_default_message_id, + PrefSyncStatus sync_status) = 0; + virtual void RegisterInt64Pref(const char* path, + int64 default_value, + PrefSyncStatus sync_status) = 0; + virtual void RegisterUint64Pref(const char* path, + uint64 default_value, + PrefSyncStatus sync_status) = 0; + // Unregisters a preference. + virtual void UnregisterPreference(const char* path) = 0; + + // Look up a preference. Returns NULL if the preference is not + // registered. + virtual const Preference* FindPreference(const char* pref_name) const = 0; + + // If the path is valid and the value at the end of the path matches the type + // specified, it will return the specified value. Otherwise, the default + // value (set when the pref was registered) will be returned. + virtual bool GetBoolean(const char* path) const = 0; + virtual int GetInteger(const char* path) const = 0; + virtual double GetDouble(const char* path) const = 0; + virtual std::string GetString(const char* path) const = 0; + virtual FilePath GetFilePath(const char* path) const = 0; + + // Returns the branch if it exists, or the registered default value otherwise. + // Note that |path| must point to a registered preference. In that case, these + // functions will never return NULL. + virtual const base::DictionaryValue* GetDictionary( + const char* path) const = 0; + virtual const base::ListValue* GetList(const char* path) const = 0; + + // Removes a user pref and restores the pref to its default value. + virtual void ClearPref(const char* path) = 0; + + // If the path is valid (i.e., registered), update the pref value in the user + // prefs. + // To set the value of dictionary or list values in the pref tree use + // Set(), but to modify the value of a dictionary or list use either + // ListPrefUpdate or DictionaryPrefUpdate from scoped_user_pref_update.h. + virtual void Set(const char* path, const base::Value& value) = 0; + virtual void SetBoolean(const char* path, bool value) = 0; + virtual void SetInteger(const char* path, int value) = 0; + virtual void SetDouble(const char* path, double value) = 0; + virtual void SetString(const char* path, const std::string& value) = 0; + virtual void SetFilePath(const char* path, const FilePath& value) = 0; + + // Int64 helper methods that actually store the given value as a string. + // Note that if obtaining the named value via GetDictionary or GetList, the + // Value type will be TYPE_STRING. + virtual void SetInt64(const char* path, int64 value) = 0; + virtual int64 GetInt64(const char* path) const = 0; + + // As above, but for unsigned values. + virtual void SetUint64(const char* path, uint64 value) = 0; + virtual uint64 GetUint64(const char* path) const = 0; + + protected: + // Registration of pref change observers must be done using the + // PrefChangeRegistrar, which is declared as a friend here to grant it + // access to the otherwise protected members Add/RemovePrefObserver. + // PrefMember registers for preferences changes notification directly to + // avoid the storage overhead of the registrar, so its base class must be + // declared as a friend, too. + friend class PrefChangeRegistrar; + friend class subtle::PrefMemberBase; + + // These are protected so they can only be accessed by the friend + // classes listed above. + // + // If the pref at the given path changes, we call the observer's Observe + // method with PREF_CHANGED. Note that observers should not call these methods + // directly but rather use a PrefChangeRegistrar to make sure the observer + // gets cleaned up properly. + virtual void AddPrefObserver(const char* path, + content::NotificationObserver* obs) = 0; + virtual void RemovePrefObserver(const char* path, + content::NotificationObserver* obs) = 0; +}; + +#endif // BASE_PREFS_PUBLIC_PREF_SERVICE_BASE_H_ diff --git a/base/prefs/testing_pref_store.cc b/base/prefs/testing_pref_store.cc new file mode 100644 index 0000000..f7c7830 --- /dev/null +++ b/base/prefs/testing_pref_store.cc @@ -0,0 +1,135 @@ +// 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 "base/prefs/testing_pref_store.h" + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" + +TestingPrefStore::TestingPrefStore() + : read_only_(true), + init_complete_(false) { +} + +PrefStore::ReadResult TestingPrefStore::GetValue(const std::string& key, + const Value** value) const { + return prefs_.GetValue(key, value) ? READ_OK : READ_NO_VALUE; +} + +PrefStore::ReadResult TestingPrefStore::GetMutableValue(const std::string& key, + Value** value) { + return prefs_.GetValue(key, value) ? READ_OK : READ_NO_VALUE; +} + +void TestingPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void TestingPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +size_t TestingPrefStore::NumberOfObservers() const { + return observers_.size(); +} + +bool TestingPrefStore::IsInitializationComplete() const { + return init_complete_; +} + +void TestingPrefStore::SetValue(const std::string& key, Value* value) { + if (prefs_.SetValue(key, value)) + NotifyPrefValueChanged(key); +} + +void TestingPrefStore::SetValueSilently(const std::string& key, Value* value) { + prefs_.SetValue(key, value); +} + +void TestingPrefStore::RemoveValue(const std::string& key) { + if (prefs_.RemoveValue(key)) + NotifyPrefValueChanged(key); +} + +void TestingPrefStore::MarkNeedsEmptyValue(const std::string& key) { +} + +bool TestingPrefStore::ReadOnly() const { + return read_only_; +} + +PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const { + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() { + NotifyInitializationCompleted(); + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate_raw) { + scoped_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw); + NotifyInitializationCompleted(); +} + +void TestingPrefStore::SetInitializationCompleted() { + init_complete_ = true; + NotifyInitializationCompleted(); +} + +void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void TestingPrefStore::NotifyInitializationCompleted() { + FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true)); +} + +void TestingPrefStore::ReportValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void TestingPrefStore::SetString(const std::string& key, + const std::string& value) { + SetValue(key, Value::CreateStringValue(value)); +} + +void TestingPrefStore::SetInteger(const std::string& key, int value) { + SetValue(key, Value::CreateIntegerValue(value)); +} + +void TestingPrefStore::SetBoolean(const std::string& key, bool value) { + SetValue(key, Value::CreateBooleanValue(value)); +} + +bool TestingPrefStore::GetString(const std::string& key, + std::string* value) const { + const Value* stored_value; + if (!prefs_.GetValue(key, &stored_value) || !stored_value) + return false; + + return stored_value->GetAsString(value); +} + +bool TestingPrefStore::GetInteger(const std::string& key, int* value) const { + const Value* stored_value; + if (!prefs_.GetValue(key, &stored_value) || !stored_value) + return false; + + return stored_value->GetAsInteger(value); +} + +bool TestingPrefStore::GetBoolean(const std::string& key, bool* value) const { + const Value* stored_value; + if (!prefs_.GetValue(key, &stored_value) || !stored_value) + return false; + + return stored_value->GetAsBoolean(value); +} + +void TestingPrefStore::set_read_only(bool read_only) { + read_only_ = read_only; +} + +TestingPrefStore::~TestingPrefStore() {} diff --git a/base/prefs/testing_pref_store.h b/base/prefs/testing_pref_store.h new file mode 100644 index 0000000..07e1401 --- /dev/null +++ b/base/prefs/testing_pref_store.h @@ -0,0 +1,84 @@ +// 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. + +#ifndef BASE_PREFS_TESTING_PREF_STORE_H_ +#define BASE_PREFS_TESTING_PREF_STORE_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/observer_list.h" +#include "base/prefs/persistent_pref_store.h" +#include "base/prefs/pref_value_map.h" + +// |TestingPrefStore| is a preference store implementation that allows tests to +// explicitly manipulate the contents of the store, triggering notifications +// where appropriate. +class TestingPrefStore : public PersistentPrefStore { + public: + TestingPrefStore(); + + // Overriden from PrefStore. + virtual ReadResult GetValue(const std::string& key, + const base::Value** result) const OVERRIDE; + virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE; + virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE; + virtual size_t NumberOfObservers() const OVERRIDE; + virtual bool IsInitializationComplete() const OVERRIDE; + + // PersistentPrefStore overrides: + virtual ReadResult GetMutableValue(const std::string& key, + base::Value** result) OVERRIDE; + virtual void ReportValueChanged(const std::string& key) OVERRIDE; + virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE; + virtual void SetValueSilently(const std::string& key, + base::Value* value) OVERRIDE; + virtual void RemoveValue(const std::string& key) OVERRIDE; + virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE; + virtual bool ReadOnly() const OVERRIDE; + virtual PrefReadError GetReadError() const OVERRIDE; + virtual PersistentPrefStore::PrefReadError ReadPrefs() OVERRIDE; + virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE; + virtual void CommitPendingWrite() OVERRIDE {} + + // Marks the store as having completed initialization. + void SetInitializationCompleted(); + + // Used for tests to trigger notifications explicitly. + void NotifyPrefValueChanged(const std::string& key); + void NotifyInitializationCompleted(); + + // Some convenience getters/setters. + void SetString(const std::string& key, const std::string& value); + void SetInteger(const std::string& key, int value); + void SetBoolean(const std::string& key, bool value); + + bool GetString(const std::string& key, std::string* value) const; + bool GetInteger(const std::string& key, int* value) const; + bool GetBoolean(const std::string& key, bool* value) const; + + // Getter and Setter methods for setting and getting the state of the + // |TestingPrefStore|. + virtual void set_read_only(bool read_only); + + protected: + virtual ~TestingPrefStore(); + + private: + // Stores the preference values. + PrefValueMap prefs_; + + // Flag that indicates if the PrefStore is read-only + bool read_only_; + + // Whether initialization has been completed. + bool init_complete_; + + ObserverList<PrefStore::Observer, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(TestingPrefStore); +}; + +#endif // BASE_PREFS_TESTING_PREF_STORE_H_ diff --git a/base/prefs/value_map_pref_store.cc b/base/prefs/value_map_pref_store.cc new file mode 100644 index 0000000..dc33384 --- /dev/null +++ b/base/prefs/value_map_pref_store.cc @@ -0,0 +1,61 @@ +// 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 "base/prefs/value_map_pref_store.h" + +#include <algorithm> + +#include "base/stl_util.h" +#include "base/values.h" + +ValueMapPrefStore::ValueMapPrefStore() {} + +PrefStore::ReadResult ValueMapPrefStore::GetValue(const std::string& key, + const Value** value) const { + return prefs_.GetValue(key, value) ? READ_OK : READ_NO_VALUE; +} + +void ValueMapPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void ValueMapPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +size_t ValueMapPrefStore::NumberOfObservers() const { + return observers_.size(); +} + +ValueMapPrefStore::iterator ValueMapPrefStore::begin() { + return prefs_.begin(); +} + +ValueMapPrefStore::iterator ValueMapPrefStore::end() { + return prefs_.end(); +} + +ValueMapPrefStore::const_iterator ValueMapPrefStore::begin() const { + return prefs_.begin(); +} + +ValueMapPrefStore::const_iterator ValueMapPrefStore::end() const { + return prefs_.end(); +} + +ValueMapPrefStore::~ValueMapPrefStore() {} + +void ValueMapPrefStore::SetValue(const std::string& key, Value* value) { + if (prefs_.SetValue(key, value)) + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void ValueMapPrefStore::RemoveValue(const std::string& key) { + if (prefs_.RemoveValue(key)) + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void ValueMapPrefStore::NotifyInitializationCompleted() { + FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true)); +} diff --git a/base/prefs/value_map_pref_store.h b/base/prefs/value_map_pref_store.h new file mode 100644 index 0000000..64ff265 --- /dev/null +++ b/base/prefs/value_map_pref_store.h @@ -0,0 +1,59 @@ +// 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. + +#ifndef BASE_PREFS_VALUE_MAP_PREF_STORE_H_ +#define BASE_PREFS_VALUE_MAP_PREF_STORE_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/observer_list.h" +#include "base/prefs/pref_store.h" +#include "base/prefs/pref_value_map.h" + +// A basic PrefStore implementation that uses a simple name-value map for +// storing the preference values. +class ValueMapPrefStore : public PrefStore { + public: + typedef std::map<std::string, base::Value*>::iterator iterator; + typedef std::map<std::string, base::Value*>::const_iterator const_iterator; + + ValueMapPrefStore(); + + // PrefStore overrides: + virtual ReadResult GetValue(const std::string& key, + const base::Value** value) const OVERRIDE; + virtual void AddObserver(PrefStore::Observer* observer) OVERRIDE; + virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE; + virtual size_t NumberOfObservers() const OVERRIDE; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + + protected: + virtual ~ValueMapPrefStore(); + + // Store a |value| for |key| in the store. Also generates an notification if + // the value changed. Assumes ownership of |value|, which must be non-NULL. + void SetValue(const std::string& key, base::Value* value); + + // Remove the value for |key| from the store. Sends a notification if there + // was a value to be removed. + void RemoveValue(const std::string& key); + + // Notify observers about the initialization completed event. + void NotifyInitializationCompleted(); + + private: + PrefValueMap prefs_; + + ObserverList<PrefStore::Observer, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(ValueMapPrefStore); +}; + +#endif // BASE_PREFS_VALUE_MAP_PREF_STORE_H_ |