diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-05-09 14:22:11 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2016-05-09 15:11:45 +0000 |
commit | 2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c (patch) | |
tree | e75f511546c5fd1a173e87c1f9fb11d7ac8d1af3 /chromium/components/prefs | |
parent | a4f3d46271c57e8155ba912df46a05559d14726e (diff) | |
download | qtwebengine-chromium-2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c.tar.gz |
BASELINE: Update Chromium to 51.0.2704.41
Also adds in all smaller components by reversing logic for exclusion.
Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/components/prefs')
57 files changed, 9377 insertions, 0 deletions
diff --git a/chromium/components/prefs/BUILD.gn b/chromium/components/prefs/BUILD.gn new file mode 100644 index 00000000000..fdbb5cf5d5e --- /dev/null +++ b/chromium/components/prefs/BUILD.gn @@ -0,0 +1,106 @@ +# Copyright 2015 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. + +component("prefs") { + sources = [ + "default_pref_store.cc", + "default_pref_store.h", + "json_pref_store.cc", + "json_pref_store.h", + "overlay_user_pref_store.cc", + "overlay_user_pref_store.h", + "pref_change_registrar.cc", + "pref_change_registrar.h", + "pref_member.cc", + "pref_member.h", + "pref_notifier_impl.cc", + "pref_notifier_impl.h", + "pref_registry.cc", + "pref_registry.h", + "pref_registry_simple.cc", + "pref_registry_simple.h", + "pref_service.cc", + "pref_service.h", + "pref_service_factory.cc", + "pref_service_factory.h", + "pref_store.cc", + "pref_store.h", + "pref_value_map.cc", + "pref_value_map.h", + "pref_value_store.cc", + "pref_value_store.h", + "scoped_user_pref_update.cc", + "scoped_user_pref_update.h", + "value_map_pref_store.cc", + "value_map_pref_store.h", + ] + if (!is_ios) { + sources += [ + "base_prefs_export.h", + "persistent_pref_store.h", + "pref_filter.h", + "pref_notifier.h", + "pref_observer.h", + "writeable_pref_store.h", + ] + } + + defines = [ "COMPONENTS_PREFS_IMPLEMENTATION" ] + + deps = [ + "//base", + ] + + if (!is_debug) { + configs -= [ "//build/config/compiler:default_optimization" ] + configs += [ "//build/config/compiler:optimize_max" ] + } +} + +source_set("test_support") { + testonly = true + sources = [ + "mock_pref_change_callback.cc", + "mock_pref_change_callback.h", + "pref_store_observer_mock.cc", + "pref_store_observer_mock.h", + "testing_pref_service.cc", + "testing_pref_service.h", + "testing_pref_store.cc", + "testing_pref_store.h", + ] + + public_deps = [ + ":prefs", + ] + deps = [ + "//base", + "//testing/gmock", + "//testing/gtest", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "default_pref_store_unittest.cc", + "json_pref_store_unittest.cc", + "overlay_user_pref_store_unittest.cc", + "pref_change_registrar_unittest.cc", + "pref_member_unittest.cc", + "pref_notifier_impl_unittest.cc", + "pref_service_unittest.cc", + "pref_value_map_unittest.cc", + "pref_value_store_unittest.cc", + "scoped_user_pref_update_unittest.cc", + ] + + deps = [ + ":test_support", + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] +} diff --git a/chromium/components/prefs/OWNERS b/chromium/components/prefs/OWNERS new file mode 100644 index 00000000000..2d870381cb7 --- /dev/null +++ b/chromium/components/prefs/OWNERS @@ -0,0 +1,4 @@ +battre@chromium.org +bauerb@chromium.org +gab@chromium.org +pam@chromium.org diff --git a/chromium/components/prefs/base_prefs_export.h b/chromium/components/prefs/base_prefs_export.h new file mode 100644 index 00000000000..e047ad99ab2 --- /dev/null +++ b/chromium/components/prefs/base_prefs_export.h @@ -0,0 +1,29 @@ +// 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 COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_ +#define COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(COMPONENTS_PREFS_IMPLEMENTATION) +#define COMPONENTS_PREFS_EXPORT __declspec(dllexport) +#else +#define COMPONENTS_PREFS_EXPORT __declspec(dllimport) +#endif // defined(COMPONENTS_PREFS_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(COMPONENTS_PREFS_IMPLEMENTATION) +#define COMPONENTS_PREFS_EXPORT __attribute__((visibility("default"))) +#else +#define COMPONENTS_PREFS_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define COMPONENTS_PREFS_EXPORT +#endif + +#endif // COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_ diff --git a/chromium/components/prefs/default_pref_store.cc b/chromium/components/prefs/default_pref_store.cc new file mode 100644 index 00000000000..79867e95774 --- /dev/null +++ b/chromium/components/prefs/default_pref_store.cc @@ -0,0 +1,56 @@ +// 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 "components/prefs/default_pref_store.h" + +#include <utility> + +#include "base/logging.h" + +using base::Value; + +DefaultPrefStore::DefaultPrefStore() {} + +bool DefaultPrefStore::GetValue(const std::string& key, + const Value** result) const { + return prefs_.GetValue(key, result); +} + +void DefaultPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void DefaultPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool DefaultPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +void DefaultPrefStore::SetDefaultValue(const std::string& key, + scoped_ptr<Value> value) { + DCHECK(!GetValue(key, NULL)); + prefs_.SetValue(key, std::move(value)); +} + +void DefaultPrefStore::ReplaceDefaultValue(const std::string& key, + scoped_ptr<Value> value) { + const Value* old_value = NULL; + GetValue(key, &old_value); + bool notify = !old_value->Equals(value.get()); + prefs_.SetValue(key, std::move(value)); + if (notify) + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +DefaultPrefStore::const_iterator DefaultPrefStore::begin() const { + return prefs_.begin(); +} + +DefaultPrefStore::const_iterator DefaultPrefStore::end() const { + return prefs_.end(); +} + +DefaultPrefStore::~DefaultPrefStore() {} diff --git a/chromium/components/prefs/default_pref_store.h b/chromium/components/prefs/default_pref_store.h new file mode 100644 index 00000000000..2e338899bea --- /dev/null +++ b/chromium/components/prefs/default_pref_store.h @@ -0,0 +1,53 @@ +// 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 COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_ +#define COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_ + +#include <string> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/values.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_store.h" +#include "components/prefs/pref_value_map.h" + +// Used within a PrefRegistry to keep track of default preference values. +class COMPONENTS_PREFS_EXPORT DefaultPrefStore : public PrefStore { + public: + typedef PrefValueMap::const_iterator const_iterator; + + DefaultPrefStore(); + + // PrefStore implementation: + bool GetValue(const std::string& key, + const base::Value** result) const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + + // Sets a |value| for |key|. Should only be called if a value has not been + // set yet; otherwise call ReplaceDefaultValue(). + void SetDefaultValue(const std::string& key, scoped_ptr<base::Value> value); + + // Replaces the the value for |key| with a new value. Should only be called + // if a value has alreday been set; otherwise call SetDefaultValue(). + void ReplaceDefaultValue(const std::string& key, + scoped_ptr<base::Value> value); + + const_iterator begin() const; + const_iterator end() const; + + private: + ~DefaultPrefStore() override; + + PrefValueMap prefs_; + + base::ObserverList<PrefStore::Observer, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(DefaultPrefStore); +}; + +#endif // COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_ diff --git a/chromium/components/prefs/default_pref_store_unittest.cc b/chromium/components/prefs/default_pref_store_unittest.cc new file mode 100644 index 00000000000..06cabc01b14 --- /dev/null +++ b/chromium/components/prefs/default_pref_store_unittest.cc @@ -0,0 +1,70 @@ +// 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/macros.h" +#include "components/prefs/default_pref_store.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::StringValue; +using base::Value; + +namespace { + +class MockPrefStoreObserver : public PrefStore::Observer { + public: + explicit MockPrefStoreObserver(DefaultPrefStore* pref_store); + ~MockPrefStoreObserver() override; + + int change_count() { + return change_count_; + } + + // PrefStore::Observer implementation: + void OnPrefValueChanged(const std::string& key) override; + void OnInitializationCompleted(bool succeeded) override {} + + private: + DefaultPrefStore* pref_store_; + + int change_count_; + + DISALLOW_COPY_AND_ASSIGN(MockPrefStoreObserver); +}; + +MockPrefStoreObserver::MockPrefStoreObserver(DefaultPrefStore* pref_store) + : pref_store_(pref_store), change_count_(0) { + pref_store_->AddObserver(this); +} + +MockPrefStoreObserver::~MockPrefStoreObserver() { + pref_store_->RemoveObserver(this); +} + +void MockPrefStoreObserver::OnPrefValueChanged(const std::string& key) { + change_count_++; +} + +} // namespace + +TEST(DefaultPrefStoreTest, NotifyPrefValueChanged) { + scoped_refptr<DefaultPrefStore> pref_store(new DefaultPrefStore); + MockPrefStoreObserver observer(pref_store.get()); + std::string kPrefKey("pref_key"); + + // Setting a default value shouldn't send a change notification. + pref_store->SetDefaultValue(kPrefKey, + scoped_ptr<Value>(new StringValue("foo"))); + EXPECT_EQ(0, observer.change_count()); + + // Replacing the default value should send a change notification... + pref_store->ReplaceDefaultValue(kPrefKey, + scoped_ptr<Value>(new StringValue("bar"))); + EXPECT_EQ(1, observer.change_count()); + + // But only if the value actually changed. + pref_store->ReplaceDefaultValue(kPrefKey, + scoped_ptr<Value>(new StringValue("bar"))); + EXPECT_EQ(1, observer.change_count()); +} + diff --git a/chromium/components/prefs/json_pref_store.cc b/chromium/components/prefs/json_pref_store.cc new file mode 100644 index 00000000000..ba7cb913ebd --- /dev/null +++ b/chromium/components/prefs/json_pref_store.cc @@ -0,0 +1,541 @@ +// 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 "components/prefs/json_pref_store.h" + +#include <stddef.h> + +#include <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/json/json_string_value_serializer.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/metrics/histogram.h" +#include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/task_runner_util.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/time/default_clock.h" +#include "base/values.h" +#include "components/prefs/pref_filter.h" + +// Result returned from internal read tasks. +struct JsonPrefStore::ReadResult { + public: + ReadResult(); + ~ReadResult(); + + scoped_ptr<base::Value> value; + PrefReadError error; + bool no_dir; + + private: + DISALLOW_COPY_AND_ASSIGN(ReadResult); +}; + +JsonPrefStore::ReadResult::ReadResult() + : error(PersistentPrefStore::PREF_READ_ERROR_NONE), no_dir(false) { +} + +JsonPrefStore::ReadResult::~ReadResult() { +} + +namespace { + +// Some extensions we'll tack on to copies of the Preferences files. +const base::FilePath::CharType kBadExtension[] = FILE_PATH_LITERAL("bad"); + +PersistentPrefStore::PrefReadError HandleReadErrors( + const base::Value* value, + const base::FilePath& path, + int error_code, + const std::string& error_msg) { + if (!value) { + DVLOG(1) << "Error while loading JSON file: " << error_msg + << ", file: " << path.value(); + switch (error_code) { + case JSONFileValueDeserializer::JSON_ACCESS_DENIED: + return PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED; + case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE: + return PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER; + case JSONFileValueDeserializer::JSON_FILE_LOCKED: + return PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED; + case JSONFileValueDeserializer::JSON_NO_SUCH_FILE: + return PersistentPrefStore::PREF_READ_ERROR_NO_FILE; + default: + // 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. + base::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. + bool bad_existed = base::PathExists(bad); + base::Move(path, bad); + return bad_existed ? PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT + : PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE; + } + } + if (!value->IsType(base::Value::TYPE_DICTIONARY)) + return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE; + return PersistentPrefStore::PREF_READ_ERROR_NONE; +} + +// Records a sample for |size| in the Settings.JsonDataReadSizeKilobytes +// histogram suffixed with the base name of the JSON file under |path|. +void RecordJsonDataSizeHistogram(const base::FilePath& path, size_t size) { + std::string spaceless_basename; + base::ReplaceChars(path.BaseName().MaybeAsASCII(), " ", "_", + &spaceless_basename); + + // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS + // macro adapted to allow for a dynamically suffixed histogram name. + // Note: The factory creates and owns the histogram. + base::HistogramBase* histogram = base::Histogram::FactoryGet( + "Settings.JsonDataReadSizeKilobytes." + spaceless_basename, 1, 10000, 50, + base::HistogramBase::kUmaTargetedHistogramFlag); + histogram->Add(static_cast<int>(size) / 1024); +} + +scoped_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk( + const base::FilePath& path, + const base::FilePath& alternate_path) { + if (!base::PathExists(path) && !alternate_path.empty() && + base::PathExists(alternate_path)) { + base::Move(alternate_path, path); + } + + int error_code; + std::string error_msg; + scoped_ptr<JsonPrefStore::ReadResult> read_result( + new JsonPrefStore::ReadResult); + JSONFileValueDeserializer deserializer(path); + read_result->value = deserializer.Deserialize(&error_code, &error_msg); + read_result->error = + HandleReadErrors(read_result->value.get(), path, error_code, error_msg); + read_result->no_dir = !base::PathExists(path.DirName()); + + if (read_result->error == PersistentPrefStore::PREF_READ_ERROR_NONE) + RecordJsonDataSizeHistogram(path, deserializer.get_last_read_size()); + + return read_result; +} + +} // namespace + +// static +scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile( + const base::FilePath& filename, + base::SequencedWorkerPool* worker_pool) { + std::string token("json_pref_store-"); + token.append(filename.AsUTF8Unsafe()); + return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( + worker_pool->GetNamedSequenceToken(token), + base::SequencedWorkerPool::BLOCK_SHUTDOWN); +} + +JsonPrefStore::JsonPrefStore( + const base::FilePath& pref_filename, + const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner, + scoped_ptr<PrefFilter> pref_filter) + : JsonPrefStore(pref_filename, + base::FilePath(), + sequenced_task_runner, + std::move(pref_filter)) {} + +JsonPrefStore::JsonPrefStore( + const base::FilePath& pref_filename, + const base::FilePath& pref_alternate_filename, + const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner, + scoped_ptr<PrefFilter> pref_filter) + : path_(pref_filename), + alternate_path_(pref_alternate_filename), + sequenced_task_runner_(sequenced_task_runner), + prefs_(new base::DictionaryValue()), + read_only_(false), + writer_(pref_filename, sequenced_task_runner), + pref_filter_(std::move(pref_filter)), + initialized_(false), + filtering_in_progress_(false), + pending_lossy_write_(false), + read_error_(PREF_READ_ERROR_NONE), + write_count_histogram_(writer_.commit_interval(), path_) { + DCHECK(!path_.empty()); +} + +bool JsonPrefStore::GetValue(const std::string& key, + const base::Value** result) const { + DCHECK(CalledOnValidThread()); + + base::Value* tmp = nullptr; + if (!prefs_->Get(key, &tmp)) + return false; + + if (result) + *result = tmp; + return true; +} + +void JsonPrefStore::AddObserver(PrefStore::Observer* observer) { + DCHECK(CalledOnValidThread()); + + observers_.AddObserver(observer); +} + +void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) { + DCHECK(CalledOnValidThread()); + + observers_.RemoveObserver(observer); +} + +bool JsonPrefStore::HasObservers() const { + DCHECK(CalledOnValidThread()); + + return observers_.might_have_observers(); +} + +bool JsonPrefStore::IsInitializationComplete() const { + DCHECK(CalledOnValidThread()); + + return initialized_; +} + +bool JsonPrefStore::GetMutableValue(const std::string& key, + base::Value** result) { + DCHECK(CalledOnValidThread()); + + return prefs_->Get(key, result); +} + +void JsonPrefStore::SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + DCHECK(CalledOnValidThread()); + + DCHECK(value); + base::Value* old_value = nullptr; + prefs_->Get(key, &old_value); + if (!old_value || !value->Equals(old_value)) { + prefs_->Set(key, std::move(value)); + ReportValueChanged(key, flags); + } +} + +void JsonPrefStore::SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + DCHECK(CalledOnValidThread()); + + DCHECK(value); + base::Value* old_value = nullptr; + prefs_->Get(key, &old_value); + if (!old_value || !value->Equals(old_value)) { + prefs_->Set(key, std::move(value)); + ScheduleWrite(flags); + } +} + +void JsonPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + DCHECK(CalledOnValidThread()); + + if (prefs_->RemovePath(key, nullptr)) + ReportValueChanged(key, flags); +} + +void JsonPrefStore::RemoveValueSilently(const std::string& key, + uint32_t flags) { + DCHECK(CalledOnValidThread()); + + prefs_->RemovePath(key, nullptr); + ScheduleWrite(flags); +} + +bool JsonPrefStore::ReadOnly() const { + DCHECK(CalledOnValidThread()); + + return read_only_; +} + +PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const { + DCHECK(CalledOnValidThread()); + + return read_error_; +} + +PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() { + DCHECK(CalledOnValidThread()); + + OnFileRead(ReadPrefsFromDisk(path_, alternate_path_)); + return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE + : read_error_; +} + +void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { + DCHECK(CalledOnValidThread()); + + initialized_ = false; + error_delegate_.reset(error_delegate); + + // Weakly binds the read task so that it doesn't kick in during shutdown. + base::PostTaskAndReplyWithResult( + sequenced_task_runner_.get(), + FROM_HERE, + base::Bind(&ReadPrefsFromDisk, path_, alternate_path_), + base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr())); +} + +void JsonPrefStore::CommitPendingWrite() { + DCHECK(CalledOnValidThread()); + + // Schedule a write for any lossy writes that are outstanding to ensure that + // they get flushed when this function is called. + SchedulePendingLossyWrites(); + + if (writer_.HasPendingWrite() && !read_only_) + writer_.DoScheduledWrite(); +} + +void JsonPrefStore::SchedulePendingLossyWrites() { + if (pending_lossy_write_) + writer_.ScheduleWrite(this); +} + +void JsonPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) { + DCHECK(CalledOnValidThread()); + + if (pref_filter_) + pref_filter_->FilterUpdate(key); + + FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); + + ScheduleWrite(flags); +} + +void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback( + const base::Closure& on_next_successful_write) { + DCHECK(CalledOnValidThread()); + + writer_.RegisterOnNextSuccessfulWriteCallback(on_next_successful_write); +} + +void JsonPrefStore::ClearMutableValues() { + NOTIMPLEMENTED(); +} + +void JsonPrefStore::OnFileRead(scoped_ptr<ReadResult> read_result) { + DCHECK(CalledOnValidThread()); + + DCHECK(read_result); + + scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue); + + read_error_ = read_result->error; + + bool initialization_successful = !read_result->no_dir; + + if (initialization_successful) { + switch (read_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(read_result->value.get()); + unfiltered_prefs.reset( + static_cast<base::DictionaryValue*>(read_result->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. + case PREF_READ_ERROR_JSON_PARSE: + case PREF_READ_ERROR_JSON_REPEAT: + break; + case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE: + // This is a special error code to be returned by ReadPrefs when it + // can't complete synchronously, it should never be returned by the read + // operation itself. + case PREF_READ_ERROR_MAX_ENUM: + NOTREACHED(); + break; + } + } + + if (pref_filter_) { + filtering_in_progress_ = true; + const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback( + base::Bind( + &JsonPrefStore::FinalizeFileRead, AsWeakPtr(), + initialization_successful)); + pref_filter_->FilterOnLoad(post_filter_on_load_callback, + std::move(unfiltered_prefs)); + } else { + FinalizeFileRead(initialization_successful, std::move(unfiltered_prefs), + false); + } +} + +JsonPrefStore::~JsonPrefStore() { + CommitPendingWrite(); +} + +bool JsonPrefStore::SerializeData(std::string* output) { + DCHECK(CalledOnValidThread()); + + pending_lossy_write_ = false; + + write_count_histogram_.RecordWriteOccured(); + + if (pref_filter_) + pref_filter_->FilterSerializeData(prefs_.get()); + + JSONStringValueSerializer serializer(output); + // Not pretty-printing prefs shrinks pref file size by ~30%. To obtain + // readable prefs for debugging purposes, you can dump your prefs into any + // command-line or online JSON pretty printing tool. + serializer.set_pretty_print(false); + return serializer.Serialize(*prefs_); +} + +void JsonPrefStore::FinalizeFileRead(bool initialization_successful, + scoped_ptr<base::DictionaryValue> prefs, + bool schedule_write) { + DCHECK(CalledOnValidThread()); + + filtering_in_progress_ = false; + + if (!initialization_successful) { + FOR_EACH_OBSERVER(PrefStore::Observer, + observers_, + OnInitializationCompleted(false)); + return; + } + + prefs_ = std::move(prefs); + + initialized_ = true; + + if (schedule_write) + ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS); + + if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE) + error_delegate_->OnError(read_error_); + + FOR_EACH_OBSERVER(PrefStore::Observer, + observers_, + OnInitializationCompleted(true)); + + return; +} + +void JsonPrefStore::ScheduleWrite(uint32_t flags) { + if (read_only_) + return; + + if (flags & LOSSY_PREF_WRITE_FLAG) + pending_lossy_write_ = true; + else + writer_.ScheduleWrite(this); +} + +// NOTE: This value should NOT be changed without renaming the histogram +// otherwise it will create incompatible buckets. +const int32_t + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins = 5; + +JsonPrefStore::WriteCountHistogram::WriteCountHistogram( + const base::TimeDelta& commit_interval, + const base::FilePath& path) + : WriteCountHistogram(commit_interval, + path, + scoped_ptr<base::Clock>(new base::DefaultClock)) { +} + +JsonPrefStore::WriteCountHistogram::WriteCountHistogram( + const base::TimeDelta& commit_interval, + const base::FilePath& path, + scoped_ptr<base::Clock> clock) + : commit_interval_(commit_interval), + path_(path), + clock_(clock.release()), + report_interval_( + base::TimeDelta::FromMinutes(kHistogramWriteReportIntervalMins)), + last_report_time_(clock_->Now()), + writes_since_last_report_(0) { +} + +JsonPrefStore::WriteCountHistogram::~WriteCountHistogram() { + ReportOutstandingWrites(); +} + +void JsonPrefStore::WriteCountHistogram::RecordWriteOccured() { + ReportOutstandingWrites(); + + ++writes_since_last_report_; +} + +void JsonPrefStore::WriteCountHistogram::ReportOutstandingWrites() { + base::Time current_time = clock_->Now(); + base::TimeDelta time_since_last_report = current_time - last_report_time_; + + if (time_since_last_report <= report_interval_) + return; + + // If the time since the last report exceeds the report interval, report all + // the writes since the last report. They must have all occurred in the same + // report interval. + base::HistogramBase* histogram = GetHistogram(); + histogram->Add(writes_since_last_report_); + + // There may be several report intervals that elapsed that don't have any + // writes in them. Report these too. + int64_t total_num_intervals_elapsed = + (time_since_last_report / report_interval_); + for (int64_t i = 0; i < total_num_intervals_elapsed - 1; ++i) + histogram->Add(0); + + writes_since_last_report_ = 0; + last_report_time_ += total_num_intervals_elapsed * report_interval_; +} + +base::HistogramBase* JsonPrefStore::WriteCountHistogram::GetHistogram() { + std::string spaceless_basename; + base::ReplaceChars(path_.BaseName().MaybeAsASCII(), " ", "_", + &spaceless_basename); + std::string histogram_name = + "Settings.JsonDataWriteCount." + spaceless_basename; + + // The min value for a histogram is 1. The max value is the maximum number of + // writes that can occur in the window being recorded. The number of buckets + // used is the max value (plus the underflow/overflow buckets). + int32_t min_value = 1; + int32_t max_value = report_interval_ / commit_interval_; + int32_t num_buckets = max_value + 1; + + // NOTE: These values should NOT be changed without renaming the histogram + // otherwise it will create incompatible buckets. + DCHECK_EQ(30, max_value); + DCHECK_EQ(31, num_buckets); + + // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS + // macro adapted to allow for a dynamically suffixed histogram name. + // Note: The factory creates and owns the histogram. + base::HistogramBase* histogram = base::Histogram::FactoryGet( + histogram_name, min_value, max_value, num_buckets, + base::HistogramBase::kUmaTargetedHistogramFlag); + return histogram; +} diff --git a/chromium/components/prefs/json_pref_store.h b/chromium/components/prefs/json_pref_store.h new file mode 100644 index 00000000000..ff6e1f22000 --- /dev/null +++ b/chromium/components/prefs/json_pref_store.h @@ -0,0 +1,231 @@ +// 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 COMPONENTS_PREFS_JSON_PREF_STORE_H_ +#define COMPONENTS_PREFS_JSON_PREF_STORE_H_ + +#include <stdint.h> + +#include <set> +#include <string> + +#include "base/callback_forward.h" +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/files/important_file_writer.h" +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/threading/non_thread_safe.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/persistent_pref_store.h" + +class PrefFilter; + +namespace base { +class Clock; +class DictionaryValue; +class FilePath; +class HistogramBase; +class JsonPrefStoreLossyWriteTest; +class SequencedTaskRunner; +class SequencedWorkerPool; +class Value; +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestBasic); +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod); +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods); +FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps); +} + +// A writable PrefStore implementation that is used for user preferences. +class COMPONENTS_PREFS_EXPORT JsonPrefStore + : public PersistentPrefStore, + public base::ImportantFileWriter::DataSerializer, + public base::SupportsWeakPtr<JsonPrefStore>, + public base::NonThreadSafe { + public: + struct ReadResult; + + // Returns instance of SequencedTaskRunner which guarantees that file + // operations on the same file will be executed in sequenced order. + static scoped_refptr<base::SequencedTaskRunner> GetTaskRunnerForFile( + const base::FilePath& pref_filename, + base::SequencedWorkerPool* worker_pool); + + // Same as the constructor below with no alternate filename. + JsonPrefStore( + const base::FilePath& pref_filename, + const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner, + scoped_ptr<PrefFilter> pref_filter); + + // |sequenced_task_runner| must be a shutdown-blocking task runner, ideally + // created by the GetTaskRunnerForFile() method above. + // |pref_filename| is the path to the file to read prefs from. + // |pref_alternate_filename| is the path to an alternate file which the + // desired prefs may have previously been written to. If |pref_filename| + // doesn't exist and |pref_alternate_filename| does, |pref_alternate_filename| + // will be moved to |pref_filename| before the read occurs. + JsonPrefStore( + const base::FilePath& pref_filename, + const base::FilePath& pref_alternate_filename, + const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner, + scoped_ptr<PrefFilter> pref_filter); + + // PrefStore overrides: + bool GetValue(const std::string& key, + const base::Value** result) const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + bool IsInitializationComplete() const override; + + // PersistentPrefStore overrides: + bool GetMutableValue(const std::string& key, base::Value** result) override; + void SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + void SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool ReadOnly() const override; + PrefReadError GetReadError() const override; + // Note this method may be asynchronous if this instance has a |pref_filter_| + // in which case it will return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE. + // See details in pref_filter.h. + PrefReadError ReadPrefs() override; + void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; + void CommitPendingWrite() override; + void SchedulePendingLossyWrites() override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + + // Just like RemoveValue(), but doesn't notify observers. Used when doing some + // cleanup that shouldn't otherwise alert observers. + void RemoveValueSilently(const std::string& key, uint32_t flags); + + // Registers |on_next_successful_write| to be called once, on the next + // successful write event of |writer_|. + void RegisterOnNextSuccessfulWriteCallback( + const base::Closure& on_next_successful_write); + + void ClearMutableValues() override; + + private: + // Represents a histogram for recording the number of writes to the pref file + // that occur every kHistogramWriteReportIntervalInMins minutes. + class COMPONENTS_PREFS_EXPORT WriteCountHistogram { + public: + static const int32_t kHistogramWriteReportIntervalMins; + + WriteCountHistogram(const base::TimeDelta& commit_interval, + const base::FilePath& path); + // Constructor for testing. |clock| is a test Clock that is used to retrieve + // the time. + WriteCountHistogram(const base::TimeDelta& commit_interval, + const base::FilePath& path, + scoped_ptr<base::Clock> clock); + ~WriteCountHistogram(); + + // Record that a write has occured. + void RecordWriteOccured(); + + // Reports writes (that have not yet been reported) in all of the recorded + // intervals that have elapsed up until current time. + void ReportOutstandingWrites(); + + base::HistogramBase* GetHistogram(); + + private: + // The minimum interval at which writes can occur. + const base::TimeDelta commit_interval_; + + // The path to the file. + const base::FilePath path_; + + // Clock which is used to retrieve the current time. + scoped_ptr<base::Clock> clock_; + + // The interval at which to report write counts. + const base::TimeDelta report_interval_; + + // The time at which the last histogram value was reported for the number + // of write counts. + base::Time last_report_time_; + + // The number of writes that have occured since the last write count was + // reported. + uint32_t writes_since_last_report_; + + DISALLOW_COPY_AND_ASSIGN(WriteCountHistogram); + }; + + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestBasic); + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestSinglePeriod); + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestMultiplePeriods); + FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest, + WriteCountHistogramTestPeriodWithGaps); + friend class base::JsonPrefStoreLossyWriteTest; + + ~JsonPrefStore() override; + + // This method is called after the JSON file has been read. It then hands + // |value| (or an empty dictionary in some read error cases) to the + // |pref_filter| if one is set. It also gives a callback pointing at + // FinalizeFileRead() to that |pref_filter_| which is then responsible for + // invoking it when done. If there is no |pref_filter_|, FinalizeFileRead() + // is invoked directly. + void OnFileRead(scoped_ptr<ReadResult> read_result); + + // ImportantFileWriter::DataSerializer overrides: + bool SerializeData(std::string* output) override; + + // This method is called after the JSON file has been read and the result has + // potentially been intercepted and modified by |pref_filter_|. + // |initialization_successful| is pre-determined by OnFileRead() and should + // be used when reporting OnInitializationCompleted(). + // |schedule_write| indicates whether a write should be immediately scheduled + // (typically because the |pref_filter_| has already altered the |prefs|) -- + // this will be ignored if this store is read-only. + void FinalizeFileRead(bool initialization_successful, + scoped_ptr<base::DictionaryValue> prefs, + bool schedule_write); + + // Schedule a write with the file writer as long as |flags| doesn't contain + // WriteablePrefStore::LOSSY_PREF_WRITE_FLAG. + void ScheduleWrite(uint32_t flags); + + const base::FilePath path_; + const base::FilePath alternate_path_; + const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; + + scoped_ptr<base::DictionaryValue> prefs_; + + bool read_only_; + + // Helper for safely writing pref data. + base::ImportantFileWriter writer_; + + scoped_ptr<PrefFilter> pref_filter_; + base::ObserverList<PrefStore::Observer, true> observers_; + + scoped_ptr<ReadErrorDelegate> error_delegate_; + + bool initialized_; + bool filtering_in_progress_; + bool pending_lossy_write_; + PrefReadError read_error_; + + std::set<std::string> keys_need_empty_value_; + + WriteCountHistogram write_count_histogram_; + + DISALLOW_COPY_AND_ASSIGN(JsonPrefStore); +}; + +#endif // COMPONENTS_PREFS_JSON_PREF_STORE_H_ diff --git a/chromium/components/prefs/json_pref_store_unittest.cc b/chromium/components/prefs/json_pref_store_unittest.cc new file mode 100644 index 00000000000..621b895a0e2 --- /dev/null +++ b/chromium/components/prefs/json_pref_store_unittest.cc @@ -0,0 +1,962 @@ +// 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 "components/prefs/json_pref_store.h" + +#include <stdint.h> + +#include <utility> + +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram_samples.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/histogram_tester.h" +#include "base/test/simple_test_clock.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/thread.h" +#include "base/values.h" +#include "components/prefs/pref_filter.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +const char kHomePage[] = "homepage"; + +const char kReadJson[] = + "{\n" + " \"homepage\": \"http://www.cnn.com\",\n" + " \"some_directory\": \"/usr/local/\",\n" + " \"tabs\": {\n" + " \"new_windows_in_tabs\": true,\n" + " \"max_tabs\": 20\n" + " }\n" + "}"; + +const char kInvalidJson[] = "!@#$%^&"; + +// Expected output for tests using RunBasicJsonPrefStoreTest(). +const char kWriteGolden[] = + "{\"homepage\":\"http://www.cnn.com\"," + "\"long_int\":{\"pref\":\"214748364842\"}," + "\"some_directory\":\"/usr/sbin/\"," + "\"tabs\":{\"max_tabs\":10,\"new_windows_in_tabs\":false}}"; + +// Set the time on the given SimpleTestClock to the given time in minutes. +void SetCurrentTimeInMinutes(double minutes, base::SimpleTestClock* clock) { + const int32_t kBaseTimeMins = 100; + clock->SetNow(base::Time::FromDoubleT((kBaseTimeMins + minutes) * 60)); +} + +// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on +// to the |prefs| until explicitly asked to release them. +class InterceptingPrefFilter : public PrefFilter { + public: + InterceptingPrefFilter(); + ~InterceptingPrefFilter() override; + + // PrefFilter implementation: + void FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + scoped_ptr<base::DictionaryValue> pref_store_contents) override; + void FilterUpdate(const std::string& path) override {} + void FilterSerializeData( + base::DictionaryValue* pref_store_contents) override {} + + bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; } + + // Finalize an intercepted read, handing |intercepted_prefs_| back to its + // JsonPrefStore. + void ReleasePrefs(); + + private: + PostFilterOnLoadCallback post_filter_on_load_callback_; + scoped_ptr<base::DictionaryValue> intercepted_prefs_; + + DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter); +}; + +InterceptingPrefFilter::InterceptingPrefFilter() {} +InterceptingPrefFilter::~InterceptingPrefFilter() {} + +void InterceptingPrefFilter::FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + scoped_ptr<base::DictionaryValue> pref_store_contents) { + post_filter_on_load_callback_ = post_filter_on_load_callback; + intercepted_prefs_ = std::move(pref_store_contents); +} + +void InterceptingPrefFilter::ReleasePrefs() { + EXPECT_FALSE(post_filter_on_load_callback_.is_null()); + post_filter_on_load_callback_.Run(std::move(intercepted_prefs_), false); + post_filter_on_load_callback_.Reset(); +} + +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: + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + + void TearDown() override { + // Make sure all pending tasks have been processed (e.g., deleting the + // JsonPrefStore may post write tasks). + RunLoop().RunUntilIdle(); + } + + // The path to temporary directory used to contain the test operations. + base::ScopedTempDir temp_dir_; + // A message loop that we can use as the file thread message loop. + MessageLoop message_loop_; +}; + +// Test fallback behavior for a nonexistent file. +TEST_F(JsonPrefStoreTest, NonExistentFile) { + base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); + ASSERT_FALSE(PathExists(bogus_input_file)); + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); +} + +// Test fallback behavior for a nonexistent file and alternate file. +TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) { + base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); + base::FilePath bogus_alternate_input_file = + temp_dir_.path().AppendASCII("read_alternate.txt"); + ASSERT_FALSE(PathExists(bogus_input_file)); + ASSERT_FALSE(PathExists(bogus_alternate_input_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(bogus_input_file, bogus_alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + 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) { + base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json"); + ASSERT_LT(0, base::WriteFile(invalid_file, + kInvalidJson, arraysize(kInvalidJson) - 1)); + + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + invalid_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + 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(PathExists(invalid_file)); + base::FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad"); + EXPECT_TRUE(PathExists(moved_aside)); + + std::string moved_aside_contents; + ASSERT_TRUE(base::ReadFileToString(moved_aside, &moved_aside_contents)); + EXPECT_EQ(kInvalidJson, moved_aside_contents); +} + +// This function is used to avoid code duplication while testing synchronous +// and asynchronous version of the JsonPrefStore loading. It validates that the +// given output file's contents matches kWriteGolden. +void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store, + const base::FilePath& 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_TRUE(pref_store->GetValue(kHomePage, &actual)); + std::string string_value; + EXPECT_TRUE(actual->GetAsString(&string_value)); + EXPECT_EQ(cnn, string_value); + + const char kSomeDirectory[] = "some_directory"; + + EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual)); + base::FilePath::StringType path; + EXPECT_TRUE(actual->GetAsString(&path)); + EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path); + base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/")); + + pref_store->SetValue(kSomeDirectory, + make_scoped_ptr(new StringValue(some_path.value())), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(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_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual)); + bool boolean = false; + EXPECT_TRUE(actual->GetAsBoolean(&boolean)); + EXPECT_TRUE(boolean); + + pref_store->SetValue(kNewWindowsInTabs, + make_scoped_ptr(new FundamentalValue(false)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual)); + EXPECT_TRUE(actual->GetAsBoolean(&boolean)); + EXPECT_FALSE(boolean); + + EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual)); + int integer = 0; + EXPECT_TRUE(actual->GetAsInteger(&integer)); + EXPECT_EQ(20, integer); + pref_store->SetValue(kMaxTabs, make_scoped_ptr(new FundamentalValue(10)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual)); + EXPECT_TRUE(actual->GetAsInteger(&integer)); + EXPECT_EQ(10, integer); + + pref_store->SetValue( + kLongIntPref, + make_scoped_ptr(new StringValue(base::Int64ToString(214748364842LL))), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual)); + EXPECT_TRUE(actual->GetAsString(&string_value)); + int64_t value; + base::StringToInt64(string_value, &value); + EXPECT_EQ(214748364842LL, value); + + // Serialize and compare to expected output. + pref_store->CommitPendingWrite(); + RunLoop().RunUntilIdle(); + + std::string output_contents; + ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents)); + EXPECT_EQ(kWriteGolden, output_contents); + ASSERT_TRUE(base::DeleteFile(output_file, false)); +} + +TEST_F(JsonPrefStoreTest, Basic) { + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_LT(0, base::WriteFile(input_file, + kReadJson, arraysize(kReadJson) - 1)); + + // Test that the persistent value can be loaded. + ASSERT_TRUE(PathExists(input_file)); + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, BasicAsync) { + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_LT(0, base::WriteFile(input_file, + kReadJson, arraysize(kReadJson) - 1)); + + // Test that the persistent value can be loaded. + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + + { + 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); + RunLoop().RunUntilIdle(); + pref_store->RemoveObserver(&mock_observer); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + } + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, PreserveEmptyValues) { + FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json"); + + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + + // Set some keys with empty values. + pref_store->SetValue("list", make_scoped_ptr(new base::ListValue), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + pref_store->SetValue("dict", make_scoped_ptr(new base::DictionaryValue), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + // Write to file. + pref_store->CommitPendingWrite(); + RunLoop().RunUntilIdle(); + + // Reload. + pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(), + scoped_ptr<PrefFilter>()); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + ASSERT_FALSE(pref_store->ReadOnly()); + + // Check values. + const Value* result = NULL; + EXPECT_TRUE(pref_store->GetValue("list", &result)); + EXPECT_TRUE(ListValue().Equals(result)); + EXPECT_TRUE(pref_store->GetValue("dict", &result)); + EXPECT_TRUE(DictionaryValue().Equals(result)); +} + +// This test is just documenting some potentially non-obvious behavior. It +// shouldn't be taken as normative. +TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) { + FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json"); + + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); + dict->SetString("key", "value"); + pref_store->SetValue("dict", std::move(dict), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + pref_store->RemoveValue("dict.key", + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + const base::Value* retrieved_dict = NULL; + bool has_dict = pref_store->GetValue("dict", &retrieved_dict); + EXPECT_FALSE(has_dict); +} + +// Tests asynchronous reading of the file when there is no file. +TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) { + base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); + ASSERT_FALSE(PathExists(bogus_input_file)); + scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( + bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + 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); + RunLoop().RunUntilIdle(); + pref_store->RemoveObserver(&mock_observer); + + EXPECT_FALSE(pref_store->ReadOnly()); +} + +TEST_F(JsonPrefStoreTest, ReadWithInterceptor) { + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_LT(0, base::WriteFile(input_file, + kReadJson, arraysize(kReadJson) - 1)); + + scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter( + new InterceptingPrefFilter()); + InterceptingPrefFilter* raw_intercepting_pref_filter_ = + intercepting_pref_filter.get(); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, message_loop_.task_runner(), + std::move(intercepting_pref_filter)); + + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE, + pref_store->ReadPrefs()); + EXPECT_FALSE(pref_store->ReadOnly()); + + // The store shouldn't be considered initialized until the interceptor + // returns. + EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_FALSE(pref_store->IsInitializationComplete()); + EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL)); + + raw_intercepting_pref_filter_->ReleasePrefs(); + + EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL)); + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) { + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_LT(0, base::WriteFile(input_file, + kReadJson, arraysize(kReadJson) - 1)); + + scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter( + new InterceptingPrefFilter()); + InterceptingPrefFilter* raw_intercepting_pref_filter_ = + intercepting_pref_filter.get(); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, message_loop_.task_runner(), + std::move(intercepting_pref_filter)); + + MockPrefStoreObserver mock_observer; + pref_store->AddObserver(&mock_observer); + + // Ownership of the |mock_error_delegate| is handed to the |pref_store| below. + MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; + + { + pref_store->ReadPrefsAsync(mock_error_delegate); + + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0); + // EXPECT_CALL(*mock_error_delegate, + // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + RunLoop().RunUntilIdle(); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_FALSE(pref_store->IsInitializationComplete()); + EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL)); + } + + { + EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); + // EXPECT_CALL(*mock_error_delegate, + // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); + + raw_intercepting_pref_filter_->ReleasePrefs(); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL)); + } + + pref_store->RemoveObserver(&mock_observer); + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, AlternateFile) { + base::FilePath alternate_input_file = + temp_dir_.path().AppendASCII("alternate.json"); + ASSERT_LT(0, base::WriteFile(alternate_input_file, + kReadJson, arraysize(kReadJson) - 1)); + + // Test that the alternate file is moved to the main file and read as-is from + // there. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_FALSE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + + ASSERT_FALSE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) { + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_LT(0, base::WriteFile(input_file, + kReadJson, arraysize(kReadJson) - 1)); + + base::FilePath alternate_input_file = + temp_dir_.path().AppendASCII("alternate.json"); + ASSERT_LT(0, base::WriteFile(alternate_input_file, + kInvalidJson, arraysize(kInvalidJson) - 1)); + + // Test that the alternate file is ignored and that the read occurs from the + // existing main file. There is no attempt at even deleting the alternate + // file as this scenario should never happen in normal user-data-dirs. + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, AlternateFileDNE) { + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + ASSERT_LT(0, base::WriteFile(input_file, + kReadJson, arraysize(kReadJson) - 1)); + + // Test that the basic read works fine when an alternate file is specified but + // does not exist. + base::FilePath alternate_input_file = + temp_dir_.path().AppendASCII("alternate.json"); + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) { + base::FilePath alternate_input_file = + temp_dir_.path().AppendASCII("alternate.json"); + ASSERT_LT(0, base::WriteFile(alternate_input_file, + kReadJson, arraysize(kReadJson) - 1)); + + // Test that the alternate file is moved to the main file and read as-is from + // there even when the read is made asynchronously. + base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); + scoped_refptr<JsonPrefStore> pref_store = + new JsonPrefStore(input_file, alternate_input_file, + message_loop_.task_runner(), scoped_ptr<PrefFilter>()); + + ASSERT_FALSE(PathExists(input_file)); + ASSERT_TRUE(PathExists(alternate_input_file)); + + { + 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); + RunLoop().RunUntilIdle(); + pref_store->RemoveObserver(&mock_observer); + + EXPECT_FALSE(pref_store->ReadOnly()); + EXPECT_TRUE(pref_store->IsInitializationComplete()); + } + + ASSERT_TRUE(PathExists(input_file)); + ASSERT_FALSE(PathExists(alternate_input_file)); + + // 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.get(), input_file); +} + +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) { + base::HistogramTester histogram_tester; + + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32_t report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + histogram.RecordWriteOccured(); + + SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + scoped_ptr<HistogramSamples> samples = + histogram.GetHistogram()->SnapshotSamples(); + + std::string histogram_name = histogram.GetHistogram()->histogram_name(); + histogram_tester.ExpectBucketCount(histogram_name, 1, 1); + histogram_tester.ExpectTotalCount(histogram_name, 1); + + ASSERT_EQ("Settings.JsonDataWriteCount.Local_State", + histogram.GetHistogram()->histogram_name()); + ASSERT_TRUE(histogram.GetHistogram()->HasConstructionArguments(1, 30, 31)); +} + +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) { + base::HistogramTester histogram_tester; + + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32_t report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.7 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // Nothing should be recorded until the report period has elapsed. + std::string histogram_name = histogram.GetHistogram()->histogram_name(); + histogram_tester.ExpectTotalCount(histogram_name, 0); + + SetCurrentTimeInMinutes(1.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // Now the report period has elapsed. + histogram_tester.ExpectBucketCount(histogram_name, 3, 1); + histogram_tester.ExpectTotalCount(histogram_name, 1); + + // The last write won't be recorded because the second count period hasn't + // fully elapsed. + SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + + histogram_tester.ExpectBucketCount(histogram_name, 3, 1); + histogram_tester.ExpectTotalCount(histogram_name, 1); +} + +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) { + base::HistogramTester histogram_tester; + + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32_t report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(0.7 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(1.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(2.1 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(2.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(2.7 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(3.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // The last write won't be recorded because the second count period hasn't + // fully elapsed + SetCurrentTimeInMinutes(3.5 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + std::string histogram_name = histogram.GetHistogram()->histogram_name(); + histogram_tester.ExpectBucketCount(histogram_name, 3, 2); + histogram_tester.ExpectBucketCount(histogram_name, 2, 1); + histogram_tester.ExpectTotalCount(histogram_name, 3); +} + +TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) { + base::HistogramTester histogram_tester; + + SimpleTestClock* test_clock = new SimpleTestClock; + SetCurrentTimeInMinutes(0, test_clock); + JsonPrefStore::WriteCountHistogram histogram( + base::TimeDelta::FromSeconds(10), + base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), + scoped_ptr<base::Clock>(test_clock)); + int32_t report_interval = + JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; + + // 1 write in the first period. + histogram.RecordWriteOccured(); + + // No writes in the second and third periods. + + // 2 writes in the fourth period. + SetCurrentTimeInMinutes(3.1 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(3.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + // No writes in the fifth period. + + // 3 writes in the sixth period. + SetCurrentTimeInMinutes(5.1 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(5.3 * report_interval, test_clock); + histogram.RecordWriteOccured(); + SetCurrentTimeInMinutes(5.5 * report_interval, test_clock); + histogram.RecordWriteOccured(); + + SetCurrentTimeInMinutes(6.1 * report_interval, test_clock); + histogram.ReportOutstandingWrites(); + std::string histogram_name = histogram.GetHistogram()->histogram_name(); + histogram_tester.ExpectBucketCount(histogram_name, 0, 3); + histogram_tester.ExpectBucketCount(histogram_name, 1, 1); + histogram_tester.ExpectBucketCount(histogram_name, 2, 1); + histogram_tester.ExpectBucketCount(histogram_name, 3, 1); + histogram_tester.ExpectTotalCount(histogram_name, 6); +} + +class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest { + protected: + void SetUp() override { + JsonPrefStoreTest::SetUp(); + test_file_ = temp_dir_.path().AppendASCII("test.json"); + } + + // Creates a JsonPrefStore with the given |file_writer|. + scoped_refptr<JsonPrefStore> CreatePrefStore() { + return new JsonPrefStore(test_file_, message_loop_.task_runner(), + scoped_ptr<PrefFilter>()); + } + + // Return the ImportantFileWriter for a given JsonPrefStore. + ImportantFileWriter* GetImportantFileWriter( + scoped_refptr<JsonPrefStore> pref_store) { + return &(pref_store->writer_); + } + + // Get the contents of kTestFile. Pumps the message loop before returning the + // result. + std::string GetTestFileContents() { + RunLoop().RunUntilIdle(); + std::string file_contents; + ReadFileToString(test_file_, &file_contents); + return file_contents; + } + + private: + base::FilePath test_file_; +}; + +TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); + + // Set a normal pref and check that it gets scheduled to be written. + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->SetValue("normal", + make_scoped_ptr(new base::StringValue("normal")), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Set a lossy pref and check that it is not scheduled to be written. + // SetValue/RemoveValue. + pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // SetValueSilently/RemoveValueSilently. + pref_store->SetValueSilently("lossy", + make_scoped_ptr(new base::StringValue("lossy")), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->RemoveValueSilently("lossy", + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // ReportValueChanged. + pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->ReportValueChanged("lossy", + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Call CommitPendingWrite and check that the lossy pref and the normal pref + // are there with the last values set above. + pref_store->CommitPendingWrite(); + ASSERT_FALSE(file_writer->HasPendingWrite()); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); +} + +TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); + + // Set a lossy pref and check that it is not scheduled to be written. + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Set a normal pref and check that it is scheduled to be written. + pref_store->SetValue("normal", + make_scoped_ptr(new base::StringValue("normal")), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call DoScheduledWrite and check both prefs get written. + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); +} + +TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); + + // Set a normal pref and check that it is scheduled to be written. + ASSERT_FALSE(file_writer->HasPendingWrite()); + pref_store->SetValue("normal", + make_scoped_ptr(new base::StringValue("normal")), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Set a lossy pref and check that the write is still scheduled. + pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call DoScheduledWrite and check both prefs get written. + file_writer->DoScheduledWrite(); + ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", + GetTestFileContents()); + ASSERT_FALSE(file_writer->HasPendingWrite()); +} + +TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) { + scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); + ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); + + // Set a lossy pref and check that it is not scheduled to be written. + pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); + ASSERT_FALSE(file_writer->HasPendingWrite()); + + // Schedule pending lossy writes and check that it is scheduled. + pref_store->SchedulePendingLossyWrites(); + ASSERT_TRUE(file_writer->HasPendingWrite()); + + // Call CommitPendingWrite and check that the lossy pref is there with the + // last value set above. + pref_store->CommitPendingWrite(); + ASSERT_FALSE(file_writer->HasPendingWrite()); + ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents()); +} + +} // namespace base diff --git a/chromium/components/prefs/mock_pref_change_callback.cc b/chromium/components/prefs/mock_pref_change_callback.cc new file mode 100644 index 00000000000..7ac2cd78451 --- /dev/null +++ b/chromium/components/prefs/mock_pref_change_callback.cc @@ -0,0 +1,24 @@ +// 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 "components/prefs/mock_pref_change_callback.h" + +#include "base/bind.h" + +MockPrefChangeCallback::MockPrefChangeCallback(PrefService* prefs) + : prefs_(prefs) { +} + +MockPrefChangeCallback::~MockPrefChangeCallback() {} + +PrefChangeRegistrar::NamedChangeCallback MockPrefChangeCallback::GetCallback() { + return base::Bind(&MockPrefChangeCallback::OnPreferenceChanged, + base::Unretained(this)); +} + +void MockPrefChangeCallback::Expect(const std::string& pref_name, + const base::Value* value) { + EXPECT_CALL(*this, OnPreferenceChanged(pref_name)) + .With(PrefValueMatches(prefs_, pref_name, value)); +} diff --git a/chromium/components/prefs/mock_pref_change_callback.h b/chromium/components/prefs/mock_pref_change_callback.h new file mode 100644 index 00000000000..9c0aeecd9c3 --- /dev/null +++ b/chromium/components/prefs/mock_pref_change_callback.h @@ -0,0 +1,51 @@ +// 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 COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_ +#define COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_ + +#include <string> + +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::Pointee; +using testing::Property; +using testing::Truly; + +// Matcher that checks whether the current value of the preference named +// |pref_name| in |prefs| matches |value|. If |value| is NULL, the matcher +// checks that the value is not set. +MATCHER_P3(PrefValueMatches, prefs, pref_name, value, "") { + const PrefService::Preference* pref = prefs->FindPreference(pref_name); + if (!pref) + return false; + + const base::Value* actual_value = pref->GetValue(); + if (!actual_value) + return value == NULL; + if (!value) + return actual_value == NULL; + return value->Equals(actual_value); +} + +// A mock for testing preference notifications and easy setup of expectations. +class MockPrefChangeCallback { + public: + explicit MockPrefChangeCallback(PrefService* prefs); + virtual ~MockPrefChangeCallback(); + + PrefChangeRegistrar::NamedChangeCallback GetCallback(); + + MOCK_METHOD1(OnPreferenceChanged, void(const std::string&)); + + void Expect(const std::string& pref_name, + const base::Value* value); + + private: + PrefService* prefs_; +}; + +#endif // COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_ diff --git a/chromium/components/prefs/overlay_user_pref_store.cc b/chromium/components/prefs/overlay_user_pref_store.cc new file mode 100644 index 00000000000..4d0905326d6 --- /dev/null +++ b/chromium/components/prefs/overlay_user_pref_store.cc @@ -0,0 +1,190 @@ +// 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 "components/prefs/overlay_user_pref_store.h" + +#include <utility> + +#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); +} + +bool OverlayUserPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +bool OverlayUserPrefStore::IsInitializationComplete() const { + return underlay_->IsInitializationComplete(); +} + +bool OverlayUserPrefStore::GetValue(const std::string& key, + const base::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 true; + return underlay_->GetValue(GetUnderlayKey(key), result); +} + +bool OverlayUserPrefStore::GetMutableValue(const std::string& key, + base::Value** result) { + if (!ShallBeStoredInOverlay(key)) + return underlay_->GetMutableValue(GetUnderlayKey(key), result); + + if (overlay_.GetValue(key, result)) + return true; + + // Try to create copy of underlay if the overlay does not contain a value. + base::Value* underlay_value = NULL; + if (!underlay_->GetMutableValue(GetUnderlayKey(key), &underlay_value)) + return false; + + *result = underlay_value->DeepCopy(); + overlay_.SetValue(key, make_scoped_ptr(*result)); + return true; +} + +void OverlayUserPrefStore::SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + if (!ShallBeStoredInOverlay(key)) { + underlay_->SetValue(GetUnderlayKey(key), std::move(value), flags); + return; + } + + if (overlay_.SetValue(key, std::move(value))) + ReportValueChanged(key, flags); +} + +void OverlayUserPrefStore::SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + if (!ShallBeStoredInOverlay(key)) { + underlay_->SetValueSilently(GetUnderlayKey(key), std::move(value), flags); + return; + } + + overlay_.SetValue(key, std::move(value)); +} + +void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + if (!ShallBeStoredInOverlay(key)) { + underlay_->RemoveValue(GetUnderlayKey(key), flags); + return; + } + + if (overlay_.RemoveValue(key)) + ReportValueChanged(key, flags); +} + +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::SchedulePendingLossyWrites() { + underlay_->SchedulePendingLossyWrites(); +} + +void OverlayUserPrefStore::ReportValueChanged(const std::string& key, + uint32_t flags) { + FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key)); +} + +void OverlayUserPrefStore::OnPrefValueChanged(const std::string& key) { + if (!overlay_.GetValue(GetOverlayKey(key), NULL)) + ReportValueChanged(GetOverlayKey(key), DEFAULT_PREF_WRITE_FLAGS); +} + +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; +} + +void OverlayUserPrefStore::ClearMutableValues() { + overlay_.Clear(); +} + +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/chromium/components/prefs/overlay_user_pref_store.h b/chromium/components/prefs/overlay_user_pref_store.h new file mode 100644 index 00000000000..c3382c25d4e --- /dev/null +++ b/chromium/components/prefs/overlay_user_pref_store.h @@ -0,0 +1,91 @@ +// 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 COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_ +#define COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_ + +#include <stdint.h> + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/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 COMPONENTS_PREFS_EXPORT 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. + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + bool IsInitializationComplete() const override; + bool GetValue(const std::string& key, + const base::Value** result) const override; + + // Methods of PersistentPrefStore. + bool GetMutableValue(const std::string& key, base::Value** result) override; + void SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + void SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool ReadOnly() const override; + PrefReadError GetReadError() const override; + PrefReadError ReadPrefs() override; + void ReadPrefsAsync(ReadErrorDelegate* delegate) override; + void CommitPendingWrite() override; + void SchedulePendingLossyWrites() override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + + // Methods of PrefStore::Observer. + void OnPrefValueChanged(const std::string& key) override; + void OnInitializationCompleted(bool succeeded) override; + + void RegisterOverlayPref(const std::string& key); + void RegisterOverlayPref(const std::string& overlay_key, + const std::string& underlay_key); + + void ClearMutableValues() override; + + protected: + ~OverlayUserPrefStore() override; + + 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; + + base::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 // COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_ diff --git a/chromium/components/prefs/overlay_user_pref_store_unittest.cc b/chromium/components/prefs/overlay_user_pref_store_unittest.cc new file mode 100644 index 00000000000..1ac185e9301 --- /dev/null +++ b/chromium/components/prefs/overlay_user_pref_store_unittest.cc @@ -0,0 +1,311 @@ +// 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 "components/prefs/overlay_user_pref_store.h" + +#include "base/values.h" +#include "components/prefs/pref_store_observer_mock.h" +#include "components/prefs/testing_pref_store.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Mock; +using ::testing::StrEq; + +namespace base { +namespace { + +const char kBrowserWindowPlacement[] = "browser.window_placement"; +const char kShowBookmarkBar[] = "bookmark_bar.show_on_all_tabs"; + +const char* const overlay_key = kBrowserWindowPlacement; +const char* const regular_key = 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); + } + + ~OverlayUserPrefStoreTest() override {} + + scoped_refptr<TestingPrefStore> underlay_; + scoped_refptr<OverlayUserPrefStore> overlay_; +}; + +TEST_F(OverlayUserPrefStoreTest, Observer) { + PrefStoreObserverMock obs; + overlay_->AddObserver(&obs); + + // Check that underlay first value is reported. + underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(overlay_key); + + // Check that underlay overwriting is reported. + underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(overlay_key); + + // Check that overwriting change in overlay is reported. + overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(44)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(overlay_key); + + // Check that hidden underlay change is not reported. + underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(45)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + // Check that overlay remove is reported. + overlay_->RemoveValue(overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(overlay_key); + + // Check that underlay remove is reported. + underlay_->RemoveValue(overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(overlay_key); + + // Check respecting of silence. + overlay_->SetValueSilently(overlay_key, + make_scoped_ptr(new FundamentalValue(46)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(47)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(48)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); +} + +TEST_F(OverlayUserPrefStoreTest, GetAndSet) { + const Value* value = NULL; + EXPECT_FALSE(overlay_->GetValue(overlay_key, &value)); + EXPECT_FALSE(underlay_->GetValue(overlay_key, &value)); + + underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + // Value shines through: + EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + EXPECT_TRUE(underlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + EXPECT_TRUE(underlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + overlay_->RemoveValue(overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + // Value shines through: + EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); + + EXPECT_TRUE(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, make_scoped_ptr(new DictionaryValue), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + Value* modify = NULL; + EXPECT_TRUE(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_TRUE(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_TRUE(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. + underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(42)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that underlay overwriting is reported. + underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(43)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that we get this value from the overlay + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + // Check that overwriting change in overlay is reported. + overlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(44)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that we get this value from the overlay and the underlay. + EXPECT_TRUE(overlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); + EXPECT_TRUE(underlay_->GetValue(regular_key, &value)); + EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); + + // Check that overlay remove is reported. + overlay_->RemoveValue(regular_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(regular_key); + + // Check that value was removed from overlay and underlay + EXPECT_FALSE(overlay_->GetValue(regular_key, &value)); + EXPECT_FALSE(underlay_->GetValue(regular_key, &value)); + + // Check respecting of silence. + overlay_->SetValueSilently(regular_key, + make_scoped_ptr(new FundamentalValue(46)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(47)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(48)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); +} + +// 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. + underlay_->SetValue(mapped_underlay_key, + make_scoped_ptr(new FundamentalValue(42)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(mapped_overlay_key); + + // Check that underlay overwriting is reported. + underlay_->SetValue(mapped_underlay_key, + make_scoped_ptr(new FundamentalValue(43)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(mapped_overlay_key); + + // Check that we get this value from the overlay with both keys + EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + // In this case, overlay reads directly from the underlay. + EXPECT_TRUE(overlay_->GetValue(mapped_underlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + // Check that overwriting change in overlay is reported. + overlay_->SetValue(mapped_overlay_key, + make_scoped_ptr(new FundamentalValue(44)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(mapped_overlay_key); + + // Check that we get an overriden value from overlay, while reading the + // value from underlay still holds an old value. + EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(44).Equals(value)); + EXPECT_TRUE(overlay_->GetValue(mapped_underlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + EXPECT_TRUE(underlay_->GetValue(mapped_underlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + + // Check that hidden underlay change is not reported. + underlay_->SetValue(mapped_underlay_key, + make_scoped_ptr(new FundamentalValue(45)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + // Check that overlay remove is reported. + overlay_->RemoveValue(mapped_overlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(mapped_overlay_key); + + // Check that underlay remove is reported. + underlay_->RemoveValue(mapped_underlay_key, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + obs.VerifyAndResetChangedKey(mapped_overlay_key); + + // Check that value was removed. + EXPECT_FALSE(overlay_->GetValue(mapped_overlay_key, &value)); + EXPECT_FALSE(overlay_->GetValue(mapped_underlay_key, &value)); + + // Check respecting of silence. + overlay_->SetValueSilently(mapped_overlay_key, + make_scoped_ptr(new FundamentalValue(46)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); + + overlay_->RemoveObserver(&obs); + + // Check successful unsubscription. + underlay_->SetValue(mapped_underlay_key, + make_scoped_ptr(new FundamentalValue(47)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(mapped_overlay_key, + make_scoped_ptr(new FundamentalValue(48)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + EXPECT_TRUE(obs.changed_keys.empty()); +} + +// Check that mutable values are removed correctly. +TEST_F(OverlayUserPrefStoreTest, ClearMutableValues) { + // Set in overlay and underlay the same preference. + underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + + const Value* value = nullptr; + // Check that an overlay preference is returned. + EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(43).Equals(value)); + overlay_->ClearMutableValues(); + + // Check that an underlay preference is returned. + EXPECT_TRUE(overlay_->GetValue(overlay_key, &value)); + EXPECT_TRUE(base::FundamentalValue(42).Equals(value)); +} + +} // namespace base diff --git a/chromium/components/prefs/persistent_pref_store.h b/chromium/components/prefs/persistent_pref_store.h new file mode 100644 index 00000000000..7bc28278be6 --- /dev/null +++ b/chromium/components/prefs/persistent_pref_store.h @@ -0,0 +1,80 @@ +// 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 COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_ +#define COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_ + +#include <string> + +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/writeable_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 COMPONENTS_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore { + public: + // Unique integer code for each type of error so we can report them + // distinctly in a histogram. + // NOTE: Don't change the explicit values of the enums as it will change the + // server's meaning of the histogram. + enum PrefReadError { + PREF_READ_ERROR_NONE = 0, + PREF_READ_ERROR_JSON_PARSE = 1, + PREF_READ_ERROR_JSON_TYPE = 2, + PREF_READ_ERROR_ACCESS_DENIED = 3, + PREF_READ_ERROR_FILE_OTHER = 4, + PREF_READ_ERROR_FILE_LOCKED = 5, + PREF_READ_ERROR_NO_FILE = 6, + PREF_READ_ERROR_JSON_REPEAT = 7, + // PREF_READ_ERROR_OTHER = 8, // Deprecated. + PREF_READ_ERROR_FILE_NOT_SPECIFIED = 9, + // Indicates that ReadPrefs() couldn't complete synchronously and is waiting + // for an asynchronous task to complete first. + PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE = 10, + PREF_READ_ERROR_MAX_ENUM + }; + + class ReadErrorDelegate { + public: + virtual ~ReadErrorDelegate() {} + + virtual void OnError(PrefReadError error) = 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; + + // Schedule a write if there is any lossy data pending. Unlike + // CommitPendingWrite() this does not immediately sync to disk, instead it + // triggers an eventual write if there is lossy data pending and if there + // isn't one scheduled already. + virtual void SchedulePendingLossyWrites() = 0; + + // It should be called only for Incognito pref store. + virtual void ClearMutableValues() = 0; + + protected: + ~PersistentPrefStore() override {} +}; + +#endif // COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_ diff --git a/chromium/components/prefs/pref_change_registrar.cc b/chromium/components/prefs/pref_change_registrar.cc new file mode 100644 index 00000000000..72c1a4927cd --- /dev/null +++ b/chromium/components/prefs/pref_change_registrar.cc @@ -0,0 +1,95 @@ +// 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 "components/prefs/pref_change_registrar.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "components/prefs/pref_service.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(PrefService* service) { + DCHECK(IsEmpty() || service_ == service); + service_ = service; +} + +void PrefChangeRegistrar::Add(const std::string& path, + const base::Closure& obs) { + Add(path, base::Bind(&PrefChangeRegistrar::InvokeUnnamedCallback, obs)); +} + +void PrefChangeRegistrar::Add(const std::string& path, + const NamedChangeCallback& obs) { + if (!service_) { + NOTREACHED(); + return; + } + DCHECK(!IsObserved(path)) << "Already had pref, \"" << path + << "\", registered."; + + service_->AddPrefObserver(path, this); + observers_[path] = obs; +} + +void PrefChangeRegistrar::Remove(const std::string& path) { + DCHECK(IsObserved(path)); + + observers_.erase(path); + service_->RemovePrefObserver(path, this); +} + +void PrefChangeRegistrar::RemoveAll() { + for (ObserverMap::const_iterator it = observers_.begin(); + it != observers_.end(); ++it) { + service_->RemovePrefObserver(it->first, this); + } + + observers_.clear(); +} + +bool PrefChangeRegistrar::IsEmpty() const { + return observers_.empty(); +} + +bool PrefChangeRegistrar::IsObserved(const std::string& pref) { + return observers_.find(pref) != observers_.end(); +} + +bool PrefChangeRegistrar::IsManaged() { + for (ObserverMap::const_iterator it = observers_.begin(); + it != observers_.end(); ++it) { + const PrefService::Preference* pref = service_->FindPreference(it->first); + if (pref && pref->IsManaged()) + return true; + } + return false; +} + +void PrefChangeRegistrar::OnPreferenceChanged(PrefService* service, + const std::string& pref) { + if (IsObserved(pref)) + observers_[pref].Run(pref); +} + +void PrefChangeRegistrar::InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name) { + callback.Run(); +} + +PrefService* PrefChangeRegistrar::prefs() { + return service_; +} + +const PrefService* PrefChangeRegistrar::prefs() const { + return service_; +} diff --git a/chromium/components/prefs/pref_change_registrar.h b/chromium/components/prefs/pref_change_registrar.h new file mode 100644 index 00000000000..2ae3b195e30 --- /dev/null +++ b/chromium/components/prefs/pref_change_registrar.h @@ -0,0 +1,81 @@ +// 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 COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_ +#define COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_ + +#include <map> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_observer.h" + +class PrefService; + +// 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 COMPONENTS_PREFS_EXPORT PrefChangeRegistrar : public PrefObserver { + public: + // You can register this type of callback if you need to know the + // path of the preference that is changing. + typedef base::Callback<void(const std::string&)> NamedChangeCallback; + + 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(PrefService* service); + + // Adds a 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. + // + // The second version binds a callback that will receive the path of + // the preference that is changing as its parameter. + // + // Only one observer may be registered per path. + void Add(const std::string& path, const base::Closure& obs); + void Add(const std::string& path, const NamedChangeCallback& obs); + + // Removes the pref observer registered for |path|. + void Remove(const std::string& path); + + // 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(); + + // Return the PrefService for this registrar. + PrefService* prefs(); + const PrefService* prefs() const; + + private: + // PrefObserver: + void OnPreferenceChanged(PrefService* service, + const std::string& pref_name) override; + + static void InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name); + + typedef std::map<std::string, NamedChangeCallback> ObserverMap; + + ObserverMap observers_; + PrefService* service_; + + DISALLOW_COPY_AND_ASSIGN(PrefChangeRegistrar); +}; + +#endif // COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_ diff --git a/chromium/components/prefs/pref_change_registrar_unittest.cc b/chromium/components/prefs/pref_change_registrar_unittest.cc new file mode 100644 index 00000000000..ab350922a4c --- /dev/null +++ b/chromium/components/prefs/pref_change_registrar_unittest.cc @@ -0,0 +1,198 @@ +// 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/bind.h" +#include "base/bind_helpers.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_observer.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; +using testing::Eq; + +namespace base { +namespace { + +const char kHomePage[] = "homepage"; +const char kHomePageIsNewTabPage[] = "homepage_is_newtabpage"; +const char kApplicationLocale[] = "intl.app_locale"; + +// A mock provider that allows us to capture pref observer changes. +class MockPrefService : public TestingPrefServiceSimple { + public: + MockPrefService() {} + virtual ~MockPrefService() {} + + MOCK_METHOD2(AddPrefObserver, void(const std::string&, PrefObserver*)); + MOCK_METHOD2(RemovePrefObserver, void(const std::string&, PrefObserver*)); +}; + +} // namespace + +class PrefChangeRegistrarTest : public testing::Test { + public: + PrefChangeRegistrarTest() {} + ~PrefChangeRegistrarTest() override {} + + protected: + void SetUp() override; + + base::Closure observer() const { + return base::Bind(&base::DoNothing); + } + + MockPrefService* service() const { return service_.get(); } + + private: + scoped_ptr<MockPrefService> service_; +}; + +void PrefChangeRegistrarTest::SetUp() { + service_.reset(new MockPrefService()); +} + +TEST_F(PrefChangeRegistrarTest, AddAndRemove) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + // Test adding. + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar)); + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + 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")), ®istrar)); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + registrar.Remove("test.pref.1"); + registrar.Remove("test.pref.2"); + 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")), ®istrar)); + 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")), ®istrar)); +} + +TEST_F(PrefChangeRegistrarTest, RemoveAll) { + PrefChangeRegistrar registrar; + registrar.Init(service()); + + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.1")), ®istrar)); + EXPECT_CALL(*service(), + AddPrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + 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")), ®istrar)); + EXPECT_CALL(*service(), + RemovePrefObserver(Eq(std::string("test.pref.2")), ®istrar)); + 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 TestingPrefServiceSimple); + PrefRegistrySimple* registry = pref_service_->registry(); + registry->RegisterStringPref(kHomePage, "http://google.com"); + registry->RegisterBooleanPref(kHomePageIsNewTabPage, false); + registry->RegisterStringPref(kApplicationLocale, std::string()); + } + + PrefChangeRegistrar* CreatePrefChangeRegistrar() { + PrefChangeRegistrar* pref_set = new PrefChangeRegistrar(); + base::Closure callback = base::Bind(&base::DoNothing); + pref_set->Init(pref_service_.get()); + pref_set->Add(kHomePage, callback); + pref_set->Add(kHomePageIsNewTabPage, callback); + return pref_set; + } + + MOCK_METHOD1(OnPreferenceChanged, void(const std::string&)); + + scoped_ptr<TestingPrefServiceSimple> pref_service_; +}; + +TEST_F(ObserveSetOfPreferencesTest, IsObserved) { + scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar()); + EXPECT_TRUE(pref_set->IsObserved(kHomePage)); + EXPECT_TRUE(pref_set->IsObserved(kHomePageIsNewTabPage)); + EXPECT_FALSE(pref_set->IsObserved(kApplicationLocale)); +} + +TEST_F(ObserveSetOfPreferencesTest, IsManaged) { + scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar()); + EXPECT_FALSE(pref_set->IsManaged()); + pref_service_->SetManagedPref(kHomePage, + new StringValue("http://crbug.com")); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->SetManagedPref(kHomePageIsNewTabPage, + new FundamentalValue(true)); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->RemoveManagedPref(kHomePage); + EXPECT_TRUE(pref_set->IsManaged()); + pref_service_->RemoveManagedPref(kHomePageIsNewTabPage); + EXPECT_FALSE(pref_set->IsManaged()); +} + +TEST_F(ObserveSetOfPreferencesTest, Observe) { + using testing::_; + using testing::Mock; + + PrefChangeRegistrar pref_set; + PrefChangeRegistrar::NamedChangeCallback callback = base::Bind( + &ObserveSetOfPreferencesTest::OnPreferenceChanged, + base::Unretained(this)); + pref_set.Init(pref_service_.get()); + pref_set.Add(kHomePage, callback); + pref_set.Add(kHomePageIsNewTabPage, callback); + + EXPECT_CALL(*this, OnPreferenceChanged(kHomePage)); + pref_service_->SetUserPref(kHomePage, new StringValue("http://crbug.com")); + Mock::VerifyAndClearExpectations(this); + + EXPECT_CALL(*this, OnPreferenceChanged(kHomePageIsNewTabPage)); + pref_service_->SetUserPref(kHomePageIsNewTabPage, + new FundamentalValue(true)); + Mock::VerifyAndClearExpectations(this); + + EXPECT_CALL(*this, OnPreferenceChanged(_)).Times(0); + pref_service_->SetUserPref(kApplicationLocale, new StringValue("en_US.utf8")); + Mock::VerifyAndClearExpectations(this); +} + +} // namespace base diff --git a/chromium/components/prefs/pref_filter.h b/chromium/components/prefs/pref_filter.h new file mode 100644 index 00000000000..8fa66273e67 --- /dev/null +++ b/chromium/components/prefs/pref_filter.h @@ -0,0 +1,55 @@ +// Copyright 2013 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 COMPONENTS_PREFS_PREF_FILTER_H_ +#define COMPONENTS_PREFS_PREF_FILTER_H_ + +#include <string> + +#include "base/callback_forward.h" +#include "base/memory/scoped_ptr.h" +#include "components/prefs/base_prefs_export.h" + +namespace base { +class DictionaryValue; +class Value; +} // namespace base + +// Filters preferences as they are loaded from disk or updated at runtime. +// Currently supported only by JsonPrefStore. +class COMPONENTS_PREFS_EXPORT PrefFilter { + public: + // A callback to be invoked when |prefs| have been read (and possibly + // pre-modified) and are now ready to be handed back to this callback's + // builder. |schedule_write| indicates whether a write should be immediately + // scheduled (typically because the |prefs| were pre-modified). + typedef base::Callback<void(scoped_ptr<base::DictionaryValue> prefs, + bool schedule_write)> PostFilterOnLoadCallback; + + virtual ~PrefFilter() {} + + // This method is given ownership of the |pref_store_contents| read from disk + // before the underlying PersistentPrefStore gets to use them. It must hand + // them back via |post_filter_on_load_callback|, but may modify them first. + // Note: This method is asynchronous, which may make calls like + // PersistentPrefStore::ReadPrefs() asynchronous. The owner of filtered + // PersistentPrefStores should handle this to make the reads look synchronous + // to external users (see SegregatedPrefStore::ReadPrefs() for an example). + virtual void FilterOnLoad( + const PostFilterOnLoadCallback& post_filter_on_load_callback, + scoped_ptr<base::DictionaryValue> pref_store_contents) = 0; + + // Receives notification when a pref store value is changed, before Observers + // are notified. + virtual void FilterUpdate(const std::string& path) = 0; + + // Receives notification when the pref store is about to serialize data + // contained in |pref_store_contents| to a string. Modifications to + // |pref_store_contents| will be persisted to disk and also affect the + // in-memory state. + virtual void FilterSerializeData( + base::DictionaryValue* pref_store_contents) = 0; +}; + +#endif // COMPONENTS_PREFS_PREF_FILTER_H_ diff --git a/chromium/components/prefs/pref_member.cc b/chromium/components/prefs/pref_member.cc new file mode 100644 index 00000000000..b848d722d1c --- /dev/null +++ b/chromium/components/prefs/pref_member.cc @@ -0,0 +1,223 @@ +// 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 "components/prefs/pref_member.h" + +#include <utility> + +#include "base/callback.h" +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/thread_task_runner_handle.h" +#include "base/value_conversions.h" +#include "components/prefs/pref_service.h" + +using base::SingleThreadTaskRunner; + +namespace subtle { + +PrefMemberBase::PrefMemberBase() + : prefs_(NULL), + setting_value_(false) { +} + +PrefMemberBase::~PrefMemberBase() { + Destroy(); +} + +void PrefMemberBase::Init(const std::string& pref_name, + PrefService* prefs, + const NamedChangeCallback& observer) { + observer_ = observer; + Init(pref_name, prefs); +} + +void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs) { + DCHECK(prefs); + DCHECK(pref_name_.empty()); // Check that Init is only called once. + prefs_ = prefs; + pref_name_ = pref_name; + // Check that the preference is registered. + DCHECK(prefs_->FindPreference(pref_name_)) << pref_name << " not registered."; + + // Add ourselves as a pref observer so we can keep our local value in sync. + prefs_->AddPrefObserver(pref_name, this); +} + +void PrefMemberBase::Destroy() { + if (prefs_ && !pref_name_.empty()) { + prefs_->RemovePrefObserver(pref_name_, this); + prefs_ = NULL; + } +} + +void PrefMemberBase::MoveToThread( + scoped_refptr<SingleThreadTaskRunner> task_runner) { + VerifyValuePrefName(); + // Load the value from preferences if it hasn't been loaded so far. + if (!internal()) + UpdateValueFromPref(base::Closure()); + internal()->MoveToThread(std::move(task_runner)); +} + +void PrefMemberBase::OnPreferenceChanged(PrefService* service, + const std::string& pref_name) { + VerifyValuePrefName(); + UpdateValueFromPref((!setting_value_ && !observer_.is_null()) ? + base::Bind(observer_, pref_name) : base::Closure()); +} + +void PrefMemberBase::UpdateValueFromPref(const base::Closure& callback) const { + VerifyValuePrefName(); + const PrefService::Preference* pref = prefs_->FindPreference(pref_name_); + DCHECK(pref); + if (!internal()) + CreateInternal(); + internal()->UpdateValue(pref->GetValue()->DeepCopy(), + pref->IsManaged(), + pref->IsUserModifiable(), + callback); +} + +void PrefMemberBase::VerifyPref() const { + VerifyValuePrefName(); + if (!internal()) + UpdateValueFromPref(base::Closure()); +} + +void PrefMemberBase::InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name) { + callback.Run(); +} + +PrefMemberBase::Internal::Internal() + : thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), + is_managed_(false), + is_user_modifiable_(false) { +} +PrefMemberBase::Internal::~Internal() { } + +bool PrefMemberBase::Internal::IsOnCorrectThread() const { + return thread_task_runner_->BelongsToCurrentThread(); +} + +void PrefMemberBase::Internal::UpdateValue( + base::Value* v, + bool is_managed, + bool is_user_modifiable, + const base::Closure& callback) const { + scoped_ptr<base::Value> value(v); + base::ScopedClosureRunner closure_runner(callback); + if (IsOnCorrectThread()) { + bool rv = UpdateValueInternal(*value); + DCHECK(rv); + is_managed_ = is_managed; + is_user_modifiable_ = is_user_modifiable; + } else { + bool may_run = thread_task_runner_->PostTask( + FROM_HERE, base::Bind(&PrefMemberBase::Internal::UpdateValue, this, + value.release(), is_managed, is_user_modifiable, + closure_runner.Release())); + DCHECK(may_run); + } +} + +void PrefMemberBase::Internal::MoveToThread( + scoped_refptr<SingleThreadTaskRunner> task_runner) { + CheckOnCorrectThread(); + thread_task_runner_ = std::move(task_runner); +} + +bool PrefMemberVectorStringUpdate(const base::Value& value, + std::vector<std::string>* string_vector) { + if (!value.IsType(base::Value::TYPE_LIST)) + return false; + const base::ListValue* list = static_cast<const base::ListValue*>(&value); + + std::vector<std::string> local_vector; + for (base::ListValue::const_iterator it = list->begin(); + it != list->end(); ++it) { + std::string string_value; + if (!(*it)->GetAsString(&string_value)) + return false; + + local_vector.push_back(string_value); + } + + string_vector->swap(local_vector); + return true; +} + +} // namespace subtle + +template <> +void PrefMember<bool>::UpdatePref(const bool& value) { + prefs()->SetBoolean(pref_name(), value); +} + +template <> +bool PrefMember<bool>::Internal::UpdateValueInternal( + const base::Value& value) const { + return value.GetAsBoolean(&value_); +} + +template <> +void PrefMember<int>::UpdatePref(const int& value) { + prefs()->SetInteger(pref_name(), value); +} + +template <> +bool PrefMember<int>::Internal::UpdateValueInternal( + const base::Value& value) const { + return value.GetAsInteger(&value_); +} + +template <> +void PrefMember<double>::UpdatePref(const double& value) { + prefs()->SetDouble(pref_name(), value); +} + +template <> +bool PrefMember<double>::Internal::UpdateValueInternal(const base::Value& value) + const { + return value.GetAsDouble(&value_); +} + +template <> +void PrefMember<std::string>::UpdatePref(const std::string& value) { + prefs()->SetString(pref_name(), value); +} + +template <> +bool PrefMember<std::string>::Internal::UpdateValueInternal( + const base::Value& value) + const { + return value.GetAsString(&value_); +} + +template <> +void PrefMember<base::FilePath>::UpdatePref(const base::FilePath& value) { + prefs()->SetFilePath(pref_name(), value); +} + +template <> +bool PrefMember<base::FilePath>::Internal::UpdateValueInternal( + const base::Value& value) + const { + return base::GetValueAsFilePath(value, &value_); +} + +template <> +void PrefMember<std::vector<std::string> >::UpdatePref( + const std::vector<std::string>& value) { + base::ListValue list_value; + list_value.AppendStrings(value); + prefs()->Set(pref_name(), list_value); +} + +template <> +bool PrefMember<std::vector<std::string> >::Internal::UpdateValueInternal( + const base::Value& value) const { + return subtle::PrefMemberVectorStringUpdate(value, &value_); +} diff --git a/chromium/components/prefs/pref_member.h b/chromium/components/prefs/pref_member.h new file mode 100644 index 00000000000..f46e368603c --- /dev/null +++ b/chromium/components/prefs/pref_member.h @@ -0,0 +1,357 @@ +// 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. +// +// A helper class that stays in sync with a preference (bool, int, real, +// string or filepath). For example: +// +// class MyClass { +// public: +// MyClass(PrefService* prefs) { +// my_string_.Init(prefs::kHomePage, prefs); +// } +// private: +// StringPrefMember my_string_; +// }; +// +// my_string_ should stay in sync with the prefs::kHomePage pref and will +// update if either the pref changes or if my_string_.SetValue is called. +// +// An optional observer can be passed into the Init method which can be used to +// notify MyClass of changes. Note that if you use SetValue(), the observer +// will not be notified. + +#ifndef COMPONENTS_PREFS_PREF_MEMBER_H_ +#define COMPONENTS_PREFS_PREF_MEMBER_H_ + +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/values.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_observer.h" + +class PrefService; + +namespace subtle { + +class COMPONENTS_PREFS_EXPORT PrefMemberBase : public PrefObserver { + public: + // Type of callback you can register if you need to know the name of + // the pref that is changing. + typedef base::Callback<void(const std::string&)> NamedChangeCallback; + + PrefService* prefs() { return prefs_; } + const PrefService* prefs() const { return prefs_; } + + protected: + class COMPONENTS_PREFS_EXPORT Internal + : public base::RefCountedThreadSafe<Internal> { + public: + Internal(); + + // Update the value, either by calling |UpdateValueInternal| directly + // or by dispatching to the right thread. + // Takes ownership of |value|. + void UpdateValue(base::Value* value, + bool is_managed, + bool is_user_modifiable, + const base::Closure& callback) const; + + void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner); + + // See PrefMember<> for description. + bool IsManaged() const { + return is_managed_; + } + + bool IsUserModifiable() const { + return is_user_modifiable_; + } + + protected: + friend class base::RefCountedThreadSafe<Internal>; + virtual ~Internal(); + + void CheckOnCorrectThread() const { + DCHECK(IsOnCorrectThread()); + } + + private: + // This method actually updates the value. It should only be called from + // the thread the PrefMember is on. + virtual bool UpdateValueInternal(const base::Value& value) const = 0; + + bool IsOnCorrectThread() const; + + scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner_; + mutable bool is_managed_; + mutable bool is_user_modifiable_; + + DISALLOW_COPY_AND_ASSIGN(Internal); + }; + + PrefMemberBase(); + virtual ~PrefMemberBase(); + + // See PrefMember<> for description. + void Init(const std::string& pref_name, + PrefService* prefs, + const NamedChangeCallback& observer); + void Init(const std::string& pref_name, PrefService* prefs); + + virtual void CreateInternal() const = 0; + + // See PrefMember<> for description. + void Destroy(); + + void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner); + + // PrefObserver + void OnPreferenceChanged(PrefService* service, + const std::string& pref_name) override; + + void VerifyValuePrefName() const { + DCHECK(!pref_name_.empty()); + } + + // This method is used to do the actual sync with the preference. + // Note: it is logically const, because it doesn't modify the state + // seen by the outside world. It is just doing a lazy load behind the scenes. + void UpdateValueFromPref(const base::Closure& callback) const; + + // Verifies the preference name, and lazily loads the preference value if + // it hasn't been loaded yet. + void VerifyPref() const; + + const std::string& pref_name() const { return pref_name_; } + + virtual Internal* internal() const = 0; + + // Used to allow registering plain base::Closure callbacks. + static void InvokeUnnamedCallback(const base::Closure& callback, + const std::string& pref_name); + + private: + // Ordered the members to compact the class instance. + std::string pref_name_; + NamedChangeCallback observer_; + PrefService* prefs_; + + protected: + bool setting_value_; +}; + +// This function implements StringListPrefMember::UpdateValue(). +// It is exposed here for testing purposes. +bool COMPONENTS_PREFS_EXPORT PrefMemberVectorStringUpdate( + const base::Value& value, + std::vector<std::string>* string_vector); + +} // namespace subtle + +template <typename ValueType> +class PrefMember : public subtle::PrefMemberBase { + public: + // Defer initialization to an Init method so it's easy to make this class be + // a member variable. + PrefMember() {} + virtual ~PrefMember() {} + + // Do the actual initialization of the class. Use the two-parameter + // version if you don't want any notifications of changes. This + // method should only be called on the UI thread. + void Init(const std::string& pref_name, + PrefService* prefs, + const NamedChangeCallback& observer) { + subtle::PrefMemberBase::Init(pref_name, prefs, observer); + } + void Init(const std::string& pref_name, + PrefService* prefs, + const base::Closure& observer) { + subtle::PrefMemberBase::Init( + pref_name, prefs, + base::Bind(&PrefMemberBase::InvokeUnnamedCallback, observer)); + } + void Init(const std::string& pref_name, PrefService* prefs) { + subtle::PrefMemberBase::Init(pref_name, prefs); + } + + // Unsubscribes the PrefMember from the PrefService. After calling this + // function, the PrefMember may not be used any more on the UI thread. + // Assuming |MoveToThread| was previously called, |GetValue|, |IsManaged|, + // and |IsUserModifiable| can still be called from the other thread but + // the results will no longer update from the PrefService. + // This method should only be called on the UI thread. + void Destroy() { + subtle::PrefMemberBase::Destroy(); + } + + // Moves the PrefMember to another thread, allowing read accesses from there. + // Changes from the PrefService will be propagated asynchronously + // via PostTask. + // This method should only be used from the thread the PrefMember is currently + // on, which is the UI thread by default. + void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + subtle::PrefMemberBase::MoveToThread(task_runner); + } + + // Check whether the pref is managed, i.e. controlled externally through + // enterprise configuration management (e.g. windows group policy). Returns + // false for unknown prefs. + // This method should only be used from the thread the PrefMember is currently + // on, which is the UI thread unless changed by |MoveToThread|. + bool IsManaged() const { + VerifyPref(); + return internal_->IsManaged(); + } + + // Checks whether the pref can be modified by the user. This returns false + // when the pref is managed by a policy or an extension, and when a command + // line flag overrides the pref. + // This method should only be used from the thread the PrefMember is currently + // on, which is the UI thread unless changed by |MoveToThread|. + bool IsUserModifiable() const { + VerifyPref(); + return internal_->IsUserModifiable(); + } + + // Retrieve the value of the member variable. + // This method should only be used from the thread the PrefMember is currently + // on, which is the UI thread unless changed by |MoveToThread|. + ValueType GetValue() const { + VerifyPref(); + return internal_->value(); + } + + // Provided as a convenience. + ValueType operator*() const { + return GetValue(); + } + + // Set the value of the member variable. + // This method should only be called on the UI thread. + void SetValue(const ValueType& value) { + VerifyValuePrefName(); + setting_value_ = true; + UpdatePref(value); + setting_value_ = false; + } + + // Returns the pref name. + const std::string& GetPrefName() const { + return pref_name(); + } + + private: + class Internal : public subtle::PrefMemberBase::Internal { + public: + Internal() : value_(ValueType()) {} + + ValueType value() { + CheckOnCorrectThread(); + return value_; + } + + protected: + ~Internal() override {} + + COMPONENTS_PREFS_EXPORT bool UpdateValueInternal( + const base::Value& value) const override; + + // We cache the value of the pref so we don't have to keep walking the pref + // tree. + mutable ValueType value_; + + private: + DISALLOW_COPY_AND_ASSIGN(Internal); + }; + + Internal* internal() const override { return internal_.get(); } + void CreateInternal() const override { internal_ = new Internal(); } + + // This method is used to do the actual sync with pref of the specified type. + void COMPONENTS_PREFS_EXPORT UpdatePref(const ValueType& value); + + mutable scoped_refptr<Internal> internal_; + + DISALLOW_COPY_AND_ASSIGN(PrefMember); +}; + +// Declaration of template specialization need to be repeated here +// specifically for each specialization (rather than just once above) +// or at least one of our compilers won't be happy in all cases. +// Specifically, it was failing on ChromeOS with a complaint about +// PrefMember<FilePath>::UpdateValueInternal not being defined when +// built in a chroot with the following parameters: +// +// FEATURES="noclean nostrip" USE="-chrome_debug -chrome_remoting +// -chrome_internal -chrome_pdf component_build" +// ~/trunk/goma/goma-wrapper cros_chrome_make --board=${BOARD} +// --install --runhooks + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<bool>::UpdatePref(const bool& value); + +template <> +COMPONENTS_PREFS_EXPORT bool PrefMember<bool>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<int>::UpdatePref(const int& value); + +template <> +COMPONENTS_PREFS_EXPORT bool PrefMember<int>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void +PrefMember<double>::UpdatePref(const double& value); + +template <> +COMPONENTS_PREFS_EXPORT bool PrefMember<double>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<std::string>::UpdatePref( + const std::string& value); + +template <> +COMPONENTS_PREFS_EXPORT bool +PrefMember<std::string>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<base::FilePath>::UpdatePref( + const base::FilePath& value); + +template <> +COMPONENTS_PREFS_EXPORT bool +PrefMember<base::FilePath>::Internal::UpdateValueInternal( + const base::Value& value) const; + +template <> +COMPONENTS_PREFS_EXPORT void PrefMember<std::vector<std::string>>::UpdatePref( + const std::vector<std::string>& value); + +template <> +COMPONENTS_PREFS_EXPORT bool +PrefMember<std::vector<std::string>>::Internal::UpdateValueInternal( + const base::Value& value) const; + +typedef PrefMember<bool> BooleanPrefMember; +typedef PrefMember<int> IntegerPrefMember; +typedef PrefMember<double> DoublePrefMember; +typedef PrefMember<std::string> StringPrefMember; +typedef PrefMember<base::FilePath> FilePathPrefMember; +// This preference member is expensive for large string arrays. +typedef PrefMember<std::vector<std::string>> StringListPrefMember; + +#endif // COMPONENTS_PREFS_PREF_MEMBER_H_ diff --git a/chromium/components/prefs/pref_member_unittest.cc b/chromium/components/prefs/pref_member_unittest.cc new file mode 100644 index 00000000000..73b6ed00c86 --- /dev/null +++ b/chromium/components/prefs/pref_member_unittest.cc @@ -0,0 +1,325 @@ +// 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 "components/prefs/pref_member.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const char kBoolPref[] = "bool"; +const char kIntPref[] = "int"; +const char kDoublePref[] = "double"; +const char kStringPref[] = "string"; +const char kStringListPref[] = "string_list"; + +void RegisterTestPrefs(PrefRegistrySimple* registry) { + registry->RegisterBooleanPref(kBoolPref, false); + registry->RegisterIntegerPref(kIntPref, 0); + registry->RegisterDoublePref(kDoublePref, 0.0); + registry->RegisterStringPref(kStringPref, "default"); + registry->RegisterListPref(kStringListPref, new base::ListValue()); +} + +class GetPrefValueHelper + : public base::RefCountedThreadSafe<GetPrefValueHelper> { + public: + GetPrefValueHelper() : value_(false), pref_thread_("pref thread") { + pref_thread_.Start(); + } + + void Init(const std::string& pref_name, PrefService* prefs) { + pref_.Init(pref_name, prefs); + pref_.MoveToThread(pref_thread_.task_runner()); + } + + void Destroy() { + pref_.Destroy(); + } + + void FetchValue() { + base::WaitableEvent event(true, false); + ASSERT_TRUE(pref_thread_.task_runner()->PostTask( + FROM_HERE, + base::Bind(&GetPrefValueHelper::GetPrefValue, this, &event))); + event.Wait(); + } + + // The thread must be stopped on the main thread. GetPrefValueHelper being + // ref-counted, the destructor can be called from any thread. + void StopThread() { + pref_thread_.Stop(); + } + + bool value() { return value_; } + + private: + friend class base::RefCountedThreadSafe<GetPrefValueHelper>; + ~GetPrefValueHelper() {} + + void GetPrefValue(base::WaitableEvent* event) { + value_ = pref_.GetValue(); + event->Signal(); + } + + BooleanPrefMember pref_; + bool value_; + + base::Thread pref_thread_; // The thread |pref_| runs on. +}; + +class PrefMemberTestClass { + public: + explicit PrefMemberTestClass(PrefService* prefs) + : observe_cnt_(0), prefs_(prefs) { + str_.Init(kStringPref, prefs, + base::Bind(&PrefMemberTestClass::OnPreferenceChanged, + base::Unretained(this))); + } + + void OnPreferenceChanged(const std::string& pref_name) { + EXPECT_EQ(pref_name, kStringPref); + EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref)); + ++observe_cnt_; + } + + StringPrefMember str_; + int observe_cnt_; + + private: + PrefService* prefs_; +}; + +} // anonymous namespace + +class PrefMemberTest : public testing::Test { + base::MessageLoop message_loop_; +}; + +TEST_F(PrefMemberTest, BasicGetAndSet) { + TestingPrefServiceSimple prefs; + RegisterTestPrefs(prefs.registry()); + + // Test bool + BooleanPrefMember boolean; + boolean.Init(kBoolPref, &prefs); + + // Check the defaults + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Try changing through the member variable. + boolean.SetValue(true); + EXPECT_TRUE(boolean.GetValue()); + EXPECT_TRUE(prefs.GetBoolean(kBoolPref)); + EXPECT_TRUE(*boolean); + + // Try changing back through the pref. + prefs.SetBoolean(kBoolPref, false); + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Test int + IntegerPrefMember integer; + integer.Init(kIntPref, &prefs); + + // Check the defaults + EXPECT_EQ(0, prefs.GetInteger(kIntPref)); + EXPECT_EQ(0, integer.GetValue()); + EXPECT_EQ(0, *integer); + + // Try changing through the member variable. + integer.SetValue(5); + EXPECT_EQ(5, integer.GetValue()); + EXPECT_EQ(5, prefs.GetInteger(kIntPref)); + EXPECT_EQ(5, *integer); + + // Try changing back through the pref. + prefs.SetInteger(kIntPref, 2); + EXPECT_EQ(2, prefs.GetInteger(kIntPref)); + EXPECT_EQ(2, integer.GetValue()); + EXPECT_EQ(2, *integer); + + // Test double + DoublePrefMember double_member; + double_member.Init(kDoublePref, &prefs); + + // Check the defaults + EXPECT_EQ(0.0, prefs.GetDouble(kDoublePref)); + EXPECT_EQ(0.0, double_member.GetValue()); + EXPECT_EQ(0.0, *double_member); + + // Try changing through the member variable. + double_member.SetValue(1.0); + EXPECT_EQ(1.0, double_member.GetValue()); + EXPECT_EQ(1.0, prefs.GetDouble(kDoublePref)); + EXPECT_EQ(1.0, *double_member); + + // Try changing back through the pref. + prefs.SetDouble(kDoublePref, 3.0); + EXPECT_EQ(3.0, prefs.GetDouble(kDoublePref)); + EXPECT_EQ(3.0, double_member.GetValue()); + EXPECT_EQ(3.0, *double_member); + + // Test string + StringPrefMember string; + string.Init(kStringPref, &prefs); + + // Check the defaults + EXPECT_EQ("default", prefs.GetString(kStringPref)); + EXPECT_EQ("default", string.GetValue()); + EXPECT_EQ("default", *string); + + // Try changing through the member variable. + string.SetValue("foo"); + EXPECT_EQ("foo", string.GetValue()); + EXPECT_EQ("foo", prefs.GetString(kStringPref)); + EXPECT_EQ("foo", *string); + + // Try changing back through the pref. + prefs.SetString(kStringPref, "bar"); + EXPECT_EQ("bar", prefs.GetString(kStringPref)); + EXPECT_EQ("bar", string.GetValue()); + EXPECT_EQ("bar", *string); + + // Test string list + base::ListValue expected_list; + std::vector<std::string> expected_vector; + StringListPrefMember string_list; + string_list.Init(kStringListPref, &prefs); + + // Check the defaults + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); + + // Try changing through the pref member. + expected_list.AppendString("foo"); + expected_vector.push_back("foo"); + string_list.SetValue(expected_vector); + + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); + + // Try adding through the pref. + expected_list.AppendString("bar"); + expected_vector.push_back("bar"); + prefs.Set(kStringListPref, expected_list); + + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); + + // Try removing through the pref. + expected_list.Remove(0, NULL); + expected_vector.erase(expected_vector.begin()); + prefs.Set(kStringListPref, expected_list); + + EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); + EXPECT_EQ(expected_vector, string_list.GetValue()); + EXPECT_EQ(expected_vector, *string_list); +} + +TEST_F(PrefMemberTest, InvalidList) { + // Set the vector to an initial good value. + std::vector<std::string> expected_vector; + expected_vector.push_back("foo"); + + // Try to add a valid list first. + base::ListValue list; + list.AppendString("foo"); + std::vector<std::string> vector; + EXPECT_TRUE(subtle::PrefMemberVectorStringUpdate(list, &vector)); + EXPECT_EQ(expected_vector, vector); + + // Now try to add an invalid list. |vector| should not be changed. + list.AppendInteger(0); + EXPECT_FALSE(subtle::PrefMemberVectorStringUpdate(list, &vector)); + EXPECT_EQ(expected_vector, vector); +} + +TEST_F(PrefMemberTest, TwoPrefs) { + // Make sure two DoublePrefMembers stay in sync. + TestingPrefServiceSimple prefs; + RegisterTestPrefs(prefs.registry()); + + DoublePrefMember pref1; + pref1.Init(kDoublePref, &prefs); + DoublePrefMember pref2; + pref2.Init(kDoublePref, &prefs); + + pref1.SetValue(2.3); + EXPECT_EQ(2.3, *pref2); + + pref2.SetValue(3.5); + EXPECT_EQ(3.5, *pref1); + + prefs.SetDouble(kDoublePref, 4.2); + EXPECT_EQ(4.2, *pref1); + EXPECT_EQ(4.2, *pref2); +} + +TEST_F(PrefMemberTest, Observer) { + TestingPrefServiceSimple prefs; + RegisterTestPrefs(prefs.registry()); + + PrefMemberTestClass test_obj(&prefs); + EXPECT_EQ("default", *test_obj.str_); + + // Calling SetValue should not fire the observer. + test_obj.str_.SetValue("hello"); + EXPECT_EQ(0, test_obj.observe_cnt_); + EXPECT_EQ("hello", prefs.GetString(kStringPref)); + + // Changing the pref does fire the observer. + prefs.SetString(kStringPref, "world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ("world", *(test_obj.str_)); + + // Not changing the value should not fire the observer. + prefs.SetString(kStringPref, "world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ("world", *(test_obj.str_)); + + prefs.SetString(kStringPref, "hello"); + EXPECT_EQ(2, test_obj.observe_cnt_); + EXPECT_EQ("hello", prefs.GetString(kStringPref)); +} + +TEST_F(PrefMemberTest, NoInit) { + // Make sure not calling Init on a PrefMember doesn't cause problems. + IntegerPrefMember pref; +} + +TEST_F(PrefMemberTest, MoveToThread) { + TestingPrefServiceSimple prefs; + scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper()); + RegisterTestPrefs(prefs.registry()); + helper->Init(kBoolPref, &prefs); + + helper->FetchValue(); + EXPECT_FALSE(helper->value()); + + prefs.SetBoolean(kBoolPref, true); + + helper->FetchValue(); + EXPECT_TRUE(helper->value()); + + helper->Destroy(); + + helper->FetchValue(); + EXPECT_TRUE(helper->value()); + + helper->StopThread(); +} diff --git a/chromium/components/prefs/pref_notifier.h b/chromium/components/prefs/pref_notifier.h new file mode 100644 index 00000000000..2abc213cffa --- /dev/null +++ b/chromium/components/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 COMPONENTS_PREFS_PREF_NOTIFIER_H_ +#define COMPONENTS_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 // COMPONENTS_PREFS_PREF_NOTIFIER_H_ diff --git a/chromium/components/prefs/pref_notifier_impl.cc b/chromium/components/prefs/pref_notifier_impl.cc new file mode 100644 index 00000000000..b3d566003c6 --- /dev/null +++ b/chromium/components/prefs/pref_notifier_impl.cc @@ -0,0 +1,117 @@ +// 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 "components/prefs/pref_notifier_impl.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "components/prefs/pref_service.h" + +PrefNotifierImpl::PrefNotifierImpl() + : pref_service_(NULL) { +} + +PrefNotifierImpl::PrefNotifierImpl(PrefService* service) + : pref_service_(service) { +} + +PrefNotifierImpl::~PrefNotifierImpl() { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Verify that there are no pref observers when we shut down. + for (PrefObserverMap::iterator it = pref_observers_.begin(); + it != pref_observers_.end(); ++it) { + PrefObserverList::Iterator obs_iterator(it->second); + if (obs_iterator.GetNext()) { + LOG(WARNING) << "pref observer found at shutdown " << it->first; + } + } + + // Same for initialization observers. + if (!init_observers_.empty()) + LOG(WARNING) << "Init observer found at shutdown."; + + STLDeleteContainerPairSecondPointers(pref_observers_.begin(), + pref_observers_.end()); + pref_observers_.clear(); + init_observers_.clear(); +} + +void PrefNotifierImpl::AddPrefObserver(const std::string& path, + PrefObserver* obs) { + // Get the pref observer list associated with the path. + PrefObserverList* observer_list = NULL; + const PrefObserverMap::iterator observer_iterator = + pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + observer_list = new PrefObserverList; + pref_observers_[path] = observer_list; + } else { + observer_list = observer_iterator->second; + } + + // Add the pref observer. ObserverList will DCHECK if it already is + // in the list. + observer_list->AddObserver(obs); +} + +void PrefNotifierImpl::RemovePrefObserver(const std::string& path, + PrefObserver* obs) { + DCHECK(thread_checker_.CalledOnValidThread()); + + const PrefObserverMap::iterator observer_iterator = + pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + return; + } + + PrefObserverList* observer_list = observer_iterator->second; + observer_list->RemoveObserver(obs); +} + +void PrefNotifierImpl::AddInitObserver(base::Callback<void(bool)> obs) { + init_observers_.push_back(obs); +} + +void PrefNotifierImpl::OnPreferenceChanged(const std::string& path) { + FireObservers(path); +} + +void PrefNotifierImpl::OnInitializationCompleted(bool succeeded) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // We must make a copy of init_observers_ and clear it before we run + // observers, or we can end up in this method re-entrantly before + // clearing the observers list. + PrefInitObserverList observers(init_observers_); + init_observers_.clear(); + + for (PrefInitObserverList::iterator it = observers.begin(); + it != observers.end(); + ++it) { + it->Run(succeeded); + } +} + +void PrefNotifierImpl::FireObservers(const std::string& path) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Only send notifications for registered preferences. + if (!pref_service_->FindPreference(path)) + return; + + const PrefObserverMap::iterator observer_iterator = + pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) + return; + + FOR_EACH_OBSERVER(PrefObserver, + *(observer_iterator->second), + OnPreferenceChanged(pref_service_, path)); +} + +void PrefNotifierImpl::SetPrefService(PrefService* pref_service) { + DCHECK(pref_service_ == NULL); + pref_service_ = pref_service; +} diff --git a/chromium/components/prefs/pref_notifier_impl.h b/chromium/components/prefs/pref_notifier_impl.h new file mode 100644 index 00000000000..e0da264ac3a --- /dev/null +++ b/chromium/components/prefs/pref_notifier_impl.h @@ -0,0 +1,74 @@ +// 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 COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_ +#define COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_ + +#include <list> +#include <string> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/threading/thread_checker.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/pref_observer.h" + +class PrefService; + +// The PrefNotifier implementation used by the PrefService. +class COMPONENTS_PREFS_EXPORT PrefNotifierImpl + : public NON_EXPORTED_BASE(PrefNotifier) { + public: + PrefNotifierImpl(); + explicit PrefNotifierImpl(PrefService* pref_service); + ~PrefNotifierImpl() override; + + // If the pref at the given path changes, we call the observer's + // OnPreferenceChanged method. + void AddPrefObserver(const std::string& path, PrefObserver* observer); + void RemovePrefObserver(const std::string& path, PrefObserver* observer); + + // We run the callback once, when initialization completes. The bool + // parameter will be set to true for successful initialization, + // false for unsuccessful. + void AddInitObserver(base::Callback<void(bool)> observer); + + void SetPrefService(PrefService* pref_service); + + protected: + // PrefNotifier overrides. + void OnPreferenceChanged(const std::string& pref_name) override; + void OnInitializationCompleted(bool succeeded) override; + + // A map from pref names to a list of observers. Observers get fired in the + // order they are added. These should only be accessed externally for unit + // testing. + typedef base::ObserverList<PrefObserver> PrefObserverList; + typedef base::hash_map<std::string, PrefObserverList*> PrefObserverMap; + + typedef std::list<base::Callback<void(bool)>> PrefInitObserverList; + + const PrefObserverMap* pref_observers() const { return &pref_observers_; } + + private: + // For the given pref_name, fire any observer of the pref. Virtual so it can + // be mocked for unit testing. + virtual void FireObservers(const std::string& path); + + // Weak reference; the notifier is owned by the PrefService. + PrefService* pref_service_; + + PrefObserverMap pref_observers_; + PrefInitObserverList init_observers_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(PrefNotifierImpl); +}; + +#endif // COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_ diff --git a/chromium/components/prefs/pref_notifier_impl_unittest.cc b/chromium/components/prefs/pref_notifier_impl_unittest.cc new file mode 100644 index 00000000000..a3f12349b9f --- /dev/null +++ b/chromium/components/prefs/pref_notifier_impl_unittest.cc @@ -0,0 +1,222 @@ +// 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 <stddef.h> + +#include "base/bind.h" +#include "base/callback.h" +#include "components/prefs/mock_pref_change_callback.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_observer.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_value_store.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Field; +using testing::Invoke; +using testing::Mock; +using testing::Truly; + +namespace { + +const char kChangedPref[] = "changed_pref"; +const char kUnchangedPref[] = "unchanged_pref"; + +class MockPrefInitObserver { + public: + MOCK_METHOD1(OnInitializationCompleted, void(bool)); +}; + +// This is an unmodified PrefNotifierImpl, except we make +// OnPreferenceChanged public for tests. +class TestingPrefNotifierImpl : public PrefNotifierImpl { + public: + explicit TestingPrefNotifierImpl(PrefService* service) + : PrefNotifierImpl(service) { + } + + // Make public for tests. + using PrefNotifierImpl::OnPreferenceChanged; +}; + +// Mock PrefNotifier that allows tracking of observers and notifications. +class MockPrefNotifier : public PrefNotifierImpl { + public: + explicit MockPrefNotifier(PrefService* pref_service) + : PrefNotifierImpl(pref_service) {} + virtual ~MockPrefNotifier() {} + + MOCK_METHOD1(FireObservers, void(const std::string& path)); + + size_t CountObserver(const std::string& path, PrefObserver* obs) { + PrefObserverMap::const_iterator observer_iterator = + pref_observers()->find(path); + if (observer_iterator == pref_observers()->end()) + return false; + + PrefObserverList* observer_list = observer_iterator->second; + PrefObserverList::Iterator it(observer_list); + PrefObserver* existing_obs; + size_t count = 0; + while ((existing_obs = it.GetNext()) != NULL) { + if (existing_obs == obs) + count++; + } + + return count; + } + + // Make public for tests below. + using PrefNotifierImpl::OnPreferenceChanged; + using PrefNotifierImpl::OnInitializationCompleted; +}; + +class PrefObserverMock : public PrefObserver { + public: + PrefObserverMock() {} + virtual ~PrefObserverMock() {} + + MOCK_METHOD2(OnPreferenceChanged, void(PrefService*, const std::string&)); +}; + +// Test fixture class. +class PrefNotifierTest : public testing::Test { + protected: + void SetUp() override { + pref_service_.registry()->RegisterBooleanPref(kChangedPref, true); + pref_service_.registry()->RegisterBooleanPref(kUnchangedPref, true); + } + + TestingPrefServiceSimple pref_service_; + + PrefObserverMock obs1_; + PrefObserverMock obs2_; +}; + +TEST_F(PrefNotifierTest, OnPreferenceChanged) { + MockPrefNotifier notifier(&pref_service_); + EXPECT_CALL(notifier, FireObservers(kChangedPref)).Times(1); + notifier.OnPreferenceChanged(kChangedPref); +} + +TEST_F(PrefNotifierTest, OnInitializationCompleted) { + MockPrefNotifier notifier(&pref_service_); + MockPrefInitObserver observer; + notifier.AddInitObserver( + base::Bind(&MockPrefInitObserver::OnInitializationCompleted, + base::Unretained(&observer))); + EXPECT_CALL(observer, OnInitializationCompleted(true)); + notifier.OnInitializationCompleted(true); +} + +TEST_F(PrefNotifierTest, AddAndRemovePrefObservers) { + const char pref_name[] = "homepage"; + const char pref_name2[] = "proxy"; + + MockPrefNotifier notifier(&pref_service_); + notifier.AddPrefObserver(pref_name, &obs1_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + // Re-adding the same observer for the same pref doesn't change anything. + // Skip this in debug mode, since it hits a DCHECK and death tests aren't + // thread-safe. +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) + notifier.AddPrefObserver(pref_name, &obs1_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); +#endif + + // Ensure that we can add the same observer to a different pref. + notifier.AddPrefObserver(pref_name2, &obs1_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + // Ensure that we can add another observer to the same pref. + notifier.AddPrefObserver(pref_name, &obs2_); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + // Ensure that we can remove all observers, and that removing a non-existent + // observer is harmless. + notifier.RemovePrefObserver(pref_name, &obs1_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + notifier.RemovePrefObserver(pref_name, &obs2_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + notifier.RemovePrefObserver(pref_name, &obs1_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); + + notifier.RemovePrefObserver(pref_name2, &obs1_); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_)); + ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_)); +} + +TEST_F(PrefNotifierTest, FireObservers) { + TestingPrefNotifierImpl notifier(&pref_service_); + notifier.AddPrefObserver(kChangedPref, &obs1_); + notifier.AddPrefObserver(kUnchangedPref, &obs1_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref)); + EXPECT_CALL(obs2_, OnPreferenceChanged(_, _)).Times(0); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + notifier.AddPrefObserver(kChangedPref, &obs2_); + notifier.AddPrefObserver(kUnchangedPref, &obs2_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref)); + EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref)); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + // Make sure removing an observer from one pref doesn't affect anything else. + notifier.RemovePrefObserver(kChangedPref, &obs1_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0); + EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref)); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + // Make sure removing an observer entirely doesn't affect anything else. + notifier.RemovePrefObserver(kUnchangedPref, &obs1_); + + EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0); + EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref)); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + notifier.RemovePrefObserver(kChangedPref, &obs2_); + notifier.RemovePrefObserver(kUnchangedPref, &obs2_); +} + +} // namespace diff --git a/chromium/components/prefs/pref_observer.h b/chromium/components/prefs/pref_observer.h new file mode 100644 index 00000000000..3f489318e9e --- /dev/null +++ b/chromium/components/prefs/pref_observer.h @@ -0,0 +1,21 @@ +// 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 COMPONENTS_PREFS_PREF_OBSERVER_H_ +#define COMPONENTS_PREFS_PREF_OBSERVER_H_ + +#include <string> + +class PrefService; + +// Used internally to the Prefs subsystem to pass preference change +// notifications between PrefService, PrefNotifierImpl and +// PrefChangeRegistrar. +class PrefObserver { + public: + virtual void OnPreferenceChanged(PrefService* service, + const std::string& pref_name) = 0; +}; + +#endif // COMPONENTS_PREFS_PREF_OBSERVER_H_ diff --git a/chromium/components/prefs/pref_registry.cc b/chromium/components/prefs/pref_registry.cc new file mode 100644 index 00000000000..b7c91de53c1 --- /dev/null +++ b/chromium/components/prefs/pref_registry.cc @@ -0,0 +1,67 @@ +// 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 "components/prefs/pref_registry.h" + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/pref_store.h" + +PrefRegistry::PrefRegistry() + : defaults_(new DefaultPrefStore()) { +} + +PrefRegistry::~PrefRegistry() { +} + +uint32_t PrefRegistry::GetRegistrationFlags( + const std::string& pref_name) const { + const auto& it = registration_flags_.find(pref_name); + if (it == registration_flags_.end()) + return NO_REGISTRATION_FLAGS; + return it->second; +} + +scoped_refptr<PrefStore> PrefRegistry::defaults() { + return defaults_.get(); +} + +PrefRegistry::const_iterator PrefRegistry::begin() const { + return defaults_->begin(); +} + +PrefRegistry::const_iterator PrefRegistry::end() const { + return defaults_->end(); +} + +void PrefRegistry::SetDefaultPrefValue(const std::string& pref_name, + base::Value* value) { + DCHECK(value); + const base::Value* current_value = NULL; + DCHECK(defaults_->GetValue(pref_name, ¤t_value)) + << "Setting default for unregistered pref: " << pref_name; + DCHECK(value->IsType(current_value->GetType())) + << "Wrong type for new default: " << pref_name; + + defaults_->ReplaceDefaultValue(pref_name, make_scoped_ptr(value)); +} + +void PrefRegistry::RegisterPreference(const std::string& path, + base::Value* default_value, + uint32_t flags) { + base::Value::Type orig_type = default_value->GetType(); + DCHECK(orig_type != base::Value::TYPE_NULL && + orig_type != base::Value::TYPE_BINARY) << + "invalid preference type: " << orig_type; + DCHECK(!defaults_->GetValue(path, NULL)) << + "Trying to register a previously registered pref: " << path; + DCHECK(!ContainsKey(registration_flags_, path)) << + "Trying to register a previously registered pref: " << path; + + defaults_->SetDefaultValue(path, make_scoped_ptr(default_value)); + if (flags != NO_REGISTRATION_FLAGS) + registration_flags_[path] = flags; +} diff --git a/chromium/components/prefs/pref_registry.h b/chromium/components/prefs/pref_registry.h new file mode 100644 index 00000000000..31a4956f793 --- /dev/null +++ b/chromium/components/prefs/pref_registry.h @@ -0,0 +1,90 @@ +// 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 COMPONENTS_PREFS_PREF_REGISTRY_H_ +#define COMPONENTS_PREFS_PREF_REGISTRY_H_ + +#include <stdint.h> + +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_value_map.h" + +namespace base { +class Value; +} + +class DefaultPrefStore; +class PrefStore; + +// Preferences need to be registered with a type and default value +// before they are used. +// +// The way you use a PrefRegistry is that you register all required +// preferences on it (via one of its subclasses), then pass it as a +// construction parameter to PrefService. +// +// Currently, registrations after constructing the PrefService will +// also work, but this is being deprecated. +class COMPONENTS_PREFS_EXPORT PrefRegistry + : public base::RefCounted<PrefRegistry> { + public: + // Registration flags that can be specified which impact how the pref will + // behave or be stored. This will be passed in a bitmask when the pref is + // registered. Subclasses of PrefRegistry can specify their own flags. Care + // must be taken to ensure none of these overlap with the flags below. + enum PrefRegistrationFlags : uint32_t { + // No flags are specified. + NO_REGISTRATION_FLAGS = 0, + + // The first 8 bits are reserved for subclasses of PrefRegistry to use. + + // This marks the pref as "lossy". There is no strict time guarantee on when + // a lossy pref will be persisted to permanent storage when it is modified. + LOSSY_PREF = 1 << 8, + }; + + typedef PrefValueMap::const_iterator const_iterator; + typedef base::hash_map<std::string, uint32_t> PrefRegistrationFlagsMap; + + PrefRegistry(); + + // Retrieve the set of registration flags for the given preference. The return + // value is a bitmask of PrefRegistrationFlags. + uint32_t GetRegistrationFlags(const std::string& pref_name) const; + + // Gets the registered defaults. + scoped_refptr<PrefStore> defaults(); + + // Allows iteration over defaults. + const_iterator begin() const; + const_iterator end() const; + + // Changes the default value for a preference. Takes ownership of |value|. + // + // |pref_name| must be a previously registered preference. + void SetDefaultPrefValue(const std::string& pref_name, base::Value* value); + + protected: + friend class base::RefCounted<PrefRegistry>; + virtual ~PrefRegistry(); + + // Used by subclasses to register a default value and registration flags for + // a preference. |flags| is a bitmask of |PrefRegistrationFlags|. + void RegisterPreference(const std::string& path, + base::Value* default_value, + uint32_t flags); + + scoped_refptr<DefaultPrefStore> defaults_; + + // A map of pref name to a bitmask of PrefRegistrationFlags. + PrefRegistrationFlagsMap registration_flags_; + + private: + DISALLOW_COPY_AND_ASSIGN(PrefRegistry); +}; + +#endif // COMPONENTS_PREFS_PREF_REGISTRY_H_ diff --git a/chromium/components/prefs/pref_registry_simple.cc b/chromium/components/prefs/pref_registry_simple.cc new file mode 100644 index 00000000000..4874261babc --- /dev/null +++ b/chromium/components/prefs/pref_registry_simple.cc @@ -0,0 +1,160 @@ +// 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 "components/prefs/pref_registry_simple.h" + +#include "base/files/file_path.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" + +PrefRegistrySimple::PrefRegistrySimple() { +} + +PrefRegistrySimple::~PrefRegistrySimple() { +} + +void PrefRegistrySimple::RegisterBooleanPref(const std::string& path, + bool default_value) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterIntegerPref(const std::string& path, + int default_value) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterDoublePref(const std::string& path, + double default_value) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterStringPref(const std::string& path, + const std::string& default_value) { + RegisterPrefAndNotify(path, new base::StringValue(default_value), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterFilePathPref( + const std::string& path, + const base::FilePath& default_value) { + RegisterPrefAndNotify(path, new base::StringValue(default_value.value()), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path) { + RegisterPrefAndNotify(path, new base::ListValue(), NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path, + base::ListValue* default_value) { + RegisterPrefAndNotify(path, default_value, NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path) { + RegisterPrefAndNotify(path, new base::DictionaryValue(), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterDictionaryPref( + const std::string& path, + base::DictionaryValue* default_value) { + RegisterPrefAndNotify(path, default_value, NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterInt64Pref(const std::string& path, + int64_t default_value) { + RegisterPrefAndNotify( + path, new base::StringValue(base::Int64ToString(default_value)), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterUint64Pref(const std::string& path, + uint64_t default_value) { + RegisterPrefAndNotify( + path, new base::StringValue(base::Uint64ToString(default_value)), + NO_REGISTRATION_FLAGS); +} + +void PrefRegistrySimple::RegisterBooleanPref(const std::string& path, + bool default_value, + uint32_t flags) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterIntegerPref(const std::string& path, + int default_value, + uint32_t flags) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterDoublePref(const std::string& path, + double default_value, + uint32_t flags) { + RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterStringPref(const std::string& path, + const std::string& default_value, + uint32_t flags) { + RegisterPrefAndNotify(path, new base::StringValue(default_value), flags); +} + +void PrefRegistrySimple::RegisterFilePathPref( + const std::string& path, + const base::FilePath& default_value, + uint32_t flags) { + RegisterPrefAndNotify(path, new base::StringValue(default_value.value()), + flags); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path, + uint32_t flags) { + RegisterPrefAndNotify(path, new base::ListValue(), flags); +} + +void PrefRegistrySimple::RegisterListPref(const std::string& path, + base::ListValue* default_value, + uint32_t flags) { + RegisterPrefAndNotify(path, default_value, flags); +} + +void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path, + uint32_t flags) { + RegisterPrefAndNotify(path, new base::DictionaryValue(), flags); +} + +void PrefRegistrySimple::RegisterDictionaryPref( + const std::string& path, + base::DictionaryValue* default_value, + uint32_t flags) { + RegisterPrefAndNotify(path, default_value, flags); +} + +void PrefRegistrySimple::RegisterInt64Pref(const std::string& path, + int64_t default_value, + uint32_t flags) { + RegisterPrefAndNotify( + path, new base::StringValue(base::Int64ToString(default_value)), flags); +} + +void PrefRegistrySimple::RegisterUint64Pref(const std::string& path, + uint64_t default_value, + uint32_t flags) { + RegisterPrefAndNotify( + path, new base::StringValue(base::Uint64ToString(default_value)), flags); +} + +void PrefRegistrySimple::OnPrefRegistered(const std::string& path, + base::Value* default_value, + uint32_t flags) {} + +void PrefRegistrySimple::RegisterPrefAndNotify(const std::string& path, + base::Value* default_value, + uint32_t flags) { + RegisterPreference(path, default_value, flags); + OnPrefRegistered(path, default_value, flags); +} diff --git a/chromium/components/prefs/pref_registry_simple.h b/chromium/components/prefs/pref_registry_simple.h new file mode 100644 index 00000000000..e3267871590 --- /dev/null +++ b/chromium/components/prefs/pref_registry_simple.h @@ -0,0 +1,91 @@ +// 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 COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_ +#define COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_ + +#include <stdint.h> + +#include <string> + +#include "base/macros.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_registry.h" + +namespace base { +class DictionaryValue; +class FilePath; +class ListValue; +} + +// A simple implementation of PrefRegistry. +class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry { + public: + PrefRegistrySimple(); + + void RegisterBooleanPref(const std::string& path, bool default_value); + void RegisterIntegerPref(const std::string& path, int default_value); + void RegisterDoublePref(const std::string& path, double default_value); + void RegisterStringPref(const std::string& path, + const std::string& default_value); + void RegisterFilePathPref(const std::string& path, + const base::FilePath& default_value); + void RegisterListPref(const std::string& path); + void RegisterDictionaryPref(const std::string& path); + void RegisterListPref(const std::string& path, + base::ListValue* default_value); + void RegisterDictionaryPref(const std::string& path, + base::DictionaryValue* default_value); + void RegisterInt64Pref(const std::string& path, int64_t default_value); + void RegisterUint64Pref(const std::string&, uint64_t default_value); + + // Versions of registration functions that accept PrefRegistrationFlags. + // |flags| is a bitmask of PrefRegistrationFlags. + void RegisterBooleanPref(const std::string&, + bool default_value, + uint32_t flags); + void RegisterIntegerPref(const std::string&, + int default_value, + uint32_t flags); + void RegisterDoublePref(const std::string&, + double default_value, + uint32_t flags); + void RegisterStringPref(const std::string&, + const std::string& default_value, + uint32_t flags); + void RegisterFilePathPref(const std::string&, + const base::FilePath& default_value, + uint32_t flags); + void RegisterListPref(const std::string&, uint32_t flags); + void RegisterDictionaryPref(const std::string&, uint32_t flags); + void RegisterListPref(const std::string&, + base::ListValue* default_value, + uint32_t flags); + void RegisterDictionaryPref(const std::string&, + base::DictionaryValue* default_value, + uint32_t flags); + void RegisterInt64Pref(const std::string&, + int64_t default_value, + uint32_t flags); + void RegisterUint64Pref(const std::string&, + uint64_t default_value, + uint32_t flags); + + protected: + ~PrefRegistrySimple() override; + + // Allows subclasses to hook into pref registration. + virtual void OnPrefRegistered(const std::string&, + base::Value* default_value, + uint32_t flags); + + private: + void RegisterPrefAndNotify(const std::string&, + base::Value* default_value, + uint32_t flags); + + DISALLOW_COPY_AND_ASSIGN(PrefRegistrySimple); +}; + +#endif // COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_ diff --git a/chromium/components/prefs/pref_service.cc b/chromium/components/prefs/pref_service.cc new file mode 100644 index 00000000000..1ef2313b3fa --- /dev/null +++ b/chromium/components/prefs/pref_service.cc @@ -0,0 +1,620 @@ +// 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 "components/prefs/pref_service.h" + +#include <algorithm> +#include <utility> + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/single_thread_task_runner.h" +#include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/thread_task_runner_handle.h" +#include "base/value_conversions.h" +#include "build/build_config.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_registry.h" +#include "components/prefs/pref_value_store.h" + +namespace { + +class ReadErrorHandler : public PersistentPrefStore::ReadErrorDelegate { + public: + ReadErrorHandler(base::Callback<void(PersistentPrefStore::PrefReadError)> cb) + : callback_(cb) {} + + void OnError(PersistentPrefStore::PrefReadError error) override { + callback_.Run(error); + } + + private: + base::Callback<void(PersistentPrefStore::PrefReadError)> callback_; +}; + +// Returns the WriteablePrefStore::PrefWriteFlags for the pref with the given +// |path|. +uint32_t GetWriteFlags(const PrefService::Preference* pref) { + uint32_t write_flags = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; + + if (!pref) + return write_flags; + + if (pref->registration_flags() & PrefRegistry::LOSSY_PREF) + write_flags |= WriteablePrefStore::LOSSY_PREF_WRITE_FLAG; + return write_flags; +} + +} // namespace + +PrefService::PrefService( + PrefNotifierImpl* pref_notifier, + PrefValueStore* pref_value_store, + PersistentPrefStore* user_prefs, + PrefRegistry* pref_registry, + base::Callback<void(PersistentPrefStore::PrefReadError)> + read_error_callback, + bool async) + : pref_notifier_(pref_notifier), + pref_value_store_(pref_value_store), + pref_registry_(pref_registry), + user_pref_store_(user_prefs), + read_error_callback_(read_error_callback) { + pref_notifier_->SetPrefService(this); + + // TODO(battre): This is a check for crbug.com/435208 to make sure that + // access violations are caused by a use-after-free bug and not by an + // initialization bug. + CHECK(pref_registry_); + CHECK(pref_value_store_); + + InitFromStorage(async); +} + +PrefService::~PrefService() { + DCHECK(CalledOnValidThread()); + + // Reset pointers so accesses after destruction reliably crash. + pref_value_store_.reset(); + pref_registry_ = NULL; + user_pref_store_ = NULL; + pref_notifier_.reset(); +} + +void PrefService::InitFromStorage(bool async) { + if (user_pref_store_->IsInitializationComplete()) { + read_error_callback_.Run(user_pref_store_->GetReadError()); + } else if (!async) { + read_error_callback_.Run(user_pref_store_->ReadPrefs()); + } else { + // Guarantee that initialization happens after this function returned. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&PersistentPrefStore::ReadPrefsAsync, user_pref_store_.get(), + new ReadErrorHandler(read_error_callback_))); + } +} + +void PrefService::CommitPendingWrite() { + DCHECK(CalledOnValidThread()); + user_pref_store_->CommitPendingWrite(); +} + +void PrefService::SchedulePendingLossyWrites() { + DCHECK(CalledOnValidThread()); + user_pref_store_->SchedulePendingLossyWrites(); +} + +bool PrefService::GetBoolean(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + bool result = false; + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = value->GetAsBoolean(&result); + DCHECK(rv); + return result; +} + +int PrefService::GetInteger(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + int result = 0; + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = value->GetAsInteger(&result); + DCHECK(rv); + return result; +} + +double PrefService::GetDouble(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + double result = 0.0; + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = value->GetAsDouble(&result); + DCHECK(rv); + return result; +} + +std::string PrefService::GetString(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + std::string result; + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = value->GetAsString(&result); + DCHECK(rv); + return result; +} + +base::FilePath PrefService::GetFilePath(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + base::FilePath result; + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return base::FilePath(result); + } + bool rv = base::GetValueAsFilePath(*value, &result); + DCHECK(rv); + return result; +} + +bool PrefService::HasPrefPath(const std::string& path) const { + const Preference* pref = FindPreference(path); + return pref && !pref->IsDefaultValue(); +} + +scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValues() const { + DCHECK(CalledOnValidThread()); + scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue); + for (const auto& it : *pref_registry_) { + out->Set(it.first, GetPreferenceValue(it.first)->CreateDeepCopy()); + } + return out; +} + +scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValuesOmitDefaults() + const { + DCHECK(CalledOnValidThread()); + scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue); + for (const auto& it : *pref_registry_) { + const Preference* pref = FindPreference(it.first); + if (pref->IsDefaultValue()) + continue; + out->Set(it.first, pref->GetValue()->CreateDeepCopy()); + } + return out; +} + +scoped_ptr<base::DictionaryValue> +PrefService::GetPreferenceValuesWithoutPathExpansion() const { + DCHECK(CalledOnValidThread()); + scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue); + for (const auto& it : *pref_registry_) { + const base::Value* value = GetPreferenceValue(it.first); + DCHECK(value); + out->SetWithoutPathExpansion(it.first, value->CreateDeepCopy()); + } + return out; +} + +const PrefService::Preference* PrefService::FindPreference( + const std::string& pref_name) const { + DCHECK(CalledOnValidThread()); + PreferenceMap::iterator it = prefs_map_.find(pref_name); + if (it != prefs_map_.end()) + return &(it->second); + const base::Value* default_value = NULL; + if (!pref_registry_->defaults()->GetValue(pref_name, &default_value)) + return NULL; + it = prefs_map_.insert( + std::make_pair(pref_name, Preference( + this, pref_name, default_value->GetType()))).first; + return &(it->second); +} + +bool PrefService::ReadOnly() const { + return user_pref_store_->ReadOnly(); +} + +PrefService::PrefInitializationStatus PrefService::GetInitializationStatus() + const { + if (!user_pref_store_->IsInitializationComplete()) + return INITIALIZATION_STATUS_WAITING; + + switch (user_pref_store_->GetReadError()) { + case PersistentPrefStore::PREF_READ_ERROR_NONE: + return INITIALIZATION_STATUS_SUCCESS; + case PersistentPrefStore::PREF_READ_ERROR_NO_FILE: + return INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE; + default: + return INITIALIZATION_STATUS_ERROR; + } +} + +bool PrefService::IsManagedPreference(const std::string& pref_name) const { + const Preference* pref = FindPreference(pref_name); + return pref && pref->IsManaged(); +} + +bool PrefService::IsPreferenceManagedByCustodian( + const std::string& pref_name) const { + const Preference* pref = FindPreference(pref_name); + return pref && pref->IsManagedByCustodian(); +} + +bool PrefService::IsUserModifiablePreference( + const std::string& pref_name) const { + const Preference* pref = FindPreference(pref_name); + return pref && pref->IsUserModifiable(); +} + +const base::DictionaryValue* PrefService::GetDictionary( + const std::string& path) const { + DCHECK(CalledOnValidThread()); + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return NULL; + } + if (value->GetType() != base::Value::TYPE_DICTIONARY) { + NOTREACHED(); + return NULL; + } + return static_cast<const base::DictionaryValue*>(value); +} + +const base::Value* PrefService::GetUserPrefValue( + const std::string& path) const { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to get an unregistered pref: " << path; + return NULL; + } + + // Look for an existing preference in the user store. If it doesn't + // exist, return NULL. + base::Value* value = NULL; + if (!user_pref_store_->GetMutableValue(path, &value)) + return NULL; + + if (!value->IsType(pref->GetType())) { + NOTREACHED() << "Pref value type doesn't match registered type."; + return NULL; + } + + return value; +} + +void PrefService::SetDefaultPrefValue(const std::string& path, + base::Value* value) { + DCHECK(CalledOnValidThread()); + pref_registry_->SetDefaultPrefValue(path, value); +} + +const base::Value* PrefService::GetDefaultPrefValue( + const std::string& path) const { + DCHECK(CalledOnValidThread()); + // Lookup the preference in the default store. + const base::Value* value = NULL; + if (!pref_registry_->defaults()->GetValue(path, &value)) { + NOTREACHED() << "Default value missing for pref: " << path; + return NULL; + } + return value; +} + +const base::ListValue* PrefService::GetList(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return NULL; + } + if (value->GetType() != base::Value::TYPE_LIST) { + NOTREACHED(); + return NULL; + } + return static_cast<const base::ListValue*>(value); +} + +void PrefService::AddPrefObserver(const std::string& path, PrefObserver* obs) { + pref_notifier_->AddPrefObserver(path, obs); +} + +void PrefService::RemovePrefObserver(const std::string& path, + PrefObserver* obs) { + pref_notifier_->RemovePrefObserver(path, obs); +} + +void PrefService::AddPrefInitObserver(base::Callback<void(bool)> obs) { + pref_notifier_->AddInitObserver(obs); +} + +PrefRegistry* PrefService::DeprecatedGetPrefRegistry() { + return pref_registry_.get(); +} + +void PrefService::ClearPref(const std::string& path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to clear an unregistered pref: " << path; + return; + } + user_pref_store_->RemoveValue(path, GetWriteFlags(pref)); +} + +void PrefService::ClearMutableValues() { + user_pref_store_->ClearMutableValues(); +} + +void PrefService::Set(const std::string& path, const base::Value& value) { + SetUserPrefValue(path, value.DeepCopy()); +} + +void PrefService::SetBoolean(const std::string& path, bool value) { + SetUserPrefValue(path, new base::FundamentalValue(value)); +} + +void PrefService::SetInteger(const std::string& path, int value) { + SetUserPrefValue(path, new base::FundamentalValue(value)); +} + +void PrefService::SetDouble(const std::string& path, double value) { + SetUserPrefValue(path, new base::FundamentalValue(value)); +} + +void PrefService::SetString(const std::string& path, const std::string& value) { + SetUserPrefValue(path, new base::StringValue(value)); +} + +void PrefService::SetFilePath(const std::string& path, + const base::FilePath& value) { + SetUserPrefValue(path, base::CreateFilePathValue(value)); +} + +void PrefService::SetInt64(const std::string& path, int64_t value) { + SetUserPrefValue(path, new base::StringValue(base::Int64ToString(value))); +} + +int64_t PrefService::GetInt64(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return 0; + } + std::string result("0"); + bool rv = value->GetAsString(&result); + DCHECK(rv); + + int64_t val; + base::StringToInt64(result, &val); + return val; +} + +void PrefService::SetUint64(const std::string& path, uint64_t value) { + SetUserPrefValue(path, new base::StringValue(base::Uint64ToString(value))); +} + +uint64_t PrefService::GetUint64(const std::string& path) const { + DCHECK(CalledOnValidThread()); + + const base::Value* value = GetPreferenceValue(path); + if (!value) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return 0; + } + std::string result("0"); + bool rv = value->GetAsString(&result); + DCHECK(rv); + + uint64_t val; + base::StringToUint64(result, &val); + return val; +} + +base::Value* PrefService::GetMutableUserPref(const std::string& path, + base::Value::Type type) { + CHECK(type == base::Value::TYPE_DICTIONARY || type == base::Value::TYPE_LIST); + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to get an unregistered pref: " << path; + return NULL; + } + if (pref->GetType() != type) { + NOTREACHED() << "Wrong type for GetMutableValue: " << path; + return NULL; + } + + // Look for an existing preference in the user store. If it doesn't + // exist or isn't the correct type, create a new user preference. + base::Value* value = NULL; + if (!user_pref_store_->GetMutableValue(path, &value) || + !value->IsType(type)) { + if (type == base::Value::TYPE_DICTIONARY) { + value = new base::DictionaryValue; + } else if (type == base::Value::TYPE_LIST) { + value = new base::ListValue; + } else { + NOTREACHED(); + } + user_pref_store_->SetValueSilently(path, make_scoped_ptr(value), + GetWriteFlags(pref)); + } + return value; +} + +void PrefService::ReportUserPrefChanged(const std::string& key) { + DCHECK(CalledOnValidThread()); + user_pref_store_->ReportValueChanged(key, GetWriteFlags(FindPreference(key))); +} + +void PrefService::SetUserPrefValue(const std::string& path, + base::Value* new_value) { + scoped_ptr<base::Value> owned_value(new_value); + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->GetType() != new_value->GetType()) { + NOTREACHED() << "Trying to set pref " << path + << " of type " << pref->GetType() + << " to value of type " << new_value->GetType(); + return; + } + + user_pref_store_->SetValue(path, std::move(owned_value), GetWriteFlags(pref)); +} + +void PrefService::UpdateCommandLinePrefStore(PrefStore* command_line_store) { + pref_value_store_->UpdateCommandLinePrefStore(command_line_store); +} + +/////////////////////////////////////////////////////////////////////////////// +// PrefService::Preference + +PrefService::Preference::Preference(const PrefService* service, + const std::string& name, + base::Value::Type type) + : name_(name), type_(type), pref_service_(service) { + DCHECK(service); + // Cache the registration flags at creation time to avoid multiple map lookups + // later. + registration_flags_ = service->pref_registry_->GetRegistrationFlags(name_); +} + +const std::string PrefService::Preference::name() const { + return name_; +} + +base::Value::Type PrefService::Preference::GetType() const { + return type_; +} + +const base::Value* PrefService::Preference::GetValue() const { + const base::Value* result= pref_service_->GetPreferenceValue(name_); + DCHECK(result) << "Must register pref before getting its value"; + return result; +} + +const base::Value* PrefService::Preference::GetRecommendedValue() const { + DCHECK(pref_service_->FindPreference(name_)) + << "Must register pref before getting its value"; + + const base::Value* found_value = NULL; + if (pref_value_store()->GetRecommendedValue(name_, type_, &found_value)) { + DCHECK(found_value->IsType(type_)); + return found_value; + } + + // The pref has no recommended value. + return NULL; +} + +bool PrefService::Preference::IsManaged() const { + return pref_value_store()->PrefValueInManagedStore(name_); +} + +bool PrefService::Preference::IsManagedByCustodian() const { + return pref_value_store()->PrefValueInSupervisedStore(name_.c_str()); +} + +bool PrefService::Preference::IsRecommended() const { + return pref_value_store()->PrefValueFromRecommendedStore(name_); +} + +bool PrefService::Preference::HasExtensionSetting() const { + return pref_value_store()->PrefValueInExtensionStore(name_); +} + +bool PrefService::Preference::HasUserSetting() const { + return pref_value_store()->PrefValueInUserStore(name_); +} + +bool PrefService::Preference::IsExtensionControlled() const { + return pref_value_store()->PrefValueFromExtensionStore(name_); +} + +bool PrefService::Preference::IsUserControlled() const { + return pref_value_store()->PrefValueFromUserStore(name_); +} + +bool PrefService::Preference::IsDefaultValue() const { + return pref_value_store()->PrefValueFromDefaultStore(name_); +} + +bool PrefService::Preference::IsUserModifiable() const { + return pref_value_store()->PrefValueUserModifiable(name_); +} + +bool PrefService::Preference::IsExtensionModifiable() const { + return pref_value_store()->PrefValueExtensionModifiable(name_); +} + +const base::Value* PrefService::GetPreferenceValue( + const std::string& path) const { + DCHECK(CalledOnValidThread()); + + // TODO(battre): This is a check for crbug.com/435208. After analyzing some + // crash dumps it looks like the PrefService is accessed even though it has + // been cleared already. + CHECK(pref_registry_); + CHECK(pref_registry_->defaults()); + CHECK(pref_value_store_); + + const base::Value* default_value = NULL; + if (pref_registry_->defaults()->GetValue(path, &default_value)) { + const base::Value* found_value = NULL; + base::Value::Type default_type = default_value->GetType(); + if (pref_value_store_->GetValue(path, default_type, &found_value)) { + DCHECK(found_value->IsType(default_type)); + return found_value; + } else { + // Every registered preference has at least a default value. + NOTREACHED() << "no valid value found for registered pref " << path; + } + } + + return NULL; +} diff --git a/chromium/components/prefs/pref_service.h b/chromium/components/prefs/pref_service.h new file mode 100644 index 00000000000..6c95b64f653 --- /dev/null +++ b/chromium/components/prefs/pref_service.h @@ -0,0 +1,388 @@ +// 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 provides a way to access the application's current preferences. + +// Chromium settings and storage represent user-selected preferences and +// information and MUST not be extracted, overwritten or modified except +// through Chromium defined APIs. + +#ifndef COMPONENTS_PREFS_PREF_SERVICE_H_ +#define COMPONENTS_PREFS_PREF_SERVICE_H_ + +#include <stdint.h> + +#include <set> +#include <string> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/containers/hash_tables.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "base/threading/non_thread_safe.h" +#include "base/values.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/persistent_pref_store.h" + +class PrefNotifier; +class PrefNotifierImpl; +class PrefObserver; +class PrefRegistry; +class PrefValueStore; +class PrefStore; + +namespace base { +class FilePath; +} + +namespace subtle { +class PrefMemberBase; +class ScopedUserPrefUpdateBase; +} + +// Base class for PrefServices. You can use the base class to read and +// interact with preferences, but not to register new preferences; for +// that see e.g. PrefRegistrySimple. +// +// Settings and storage accessed through this class represent +// user-selected preferences and information and MUST not be +// extracted, overwritten or modified except through the defined APIs. +class COMPONENTS_PREFS_EXPORT PrefService : public base::NonThreadSafe { + public: + enum PrefInitializationStatus { + INITIALIZATION_STATUS_WAITING, + INITIALIZATION_STATUS_SUCCESS, + INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE, + INITIALIZATION_STATUS_ERROR + }; + + // A helper class to store all the information associated with a preference. + class COMPONENTS_PREFS_EXPORT Preference { + public: + // The type of the preference is determined by the type with which it is + // registered. This type needs to be a boolean, integer, double, string, + // dictionary (a branch), or list. You shouldn't need to construct this on + // your own; use the PrefService::Register*Pref methods instead. + Preference(const PrefService* service, + const std::string& name, + base::Value::Type type); + ~Preference() {} + + // Returns the name of the Preference (i.e., the key, e.g., + // browser.window_placement). + const std::string name() const; + + // Returns the registered type of the preference. + base::Value::Type GetType() const; + + // Returns the value of the Preference, falling back to the registered + // default value if no other has been set. + const base::Value* GetValue() const; + + // Returns the value recommended by the admin, if any. + const base::Value* GetRecommendedValue() const; + + // 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. + bool IsManaged() const; + + // Returns true if the Preference is controlled by the custodian of the + // supervised user. Since a supervised user is not expected to have an admin + // policy, this is the controlling pref if set. + bool IsManagedByCustodian() const; + + // Returns true if the Preference is recommended, i.e. set by an admin + // policy but the user is allowed to change it. + bool IsRecommended() const; + + // Returns true if the Preference has a value set by an extension, even if + // that value is being overridden by a higher-priority source. + bool HasExtensionSetting() const; + + // Returns true if the Preference has a user setting, even if that value is + // being overridden by a higher-priority source. + bool HasUserSetting() const; + + // Returns true if the Preference value is currently being controlled by an + // extension, and not by any higher-priority source. + bool IsExtensionControlled() const; + + // Returns true if the Preference value is currently being controlled by a + // user setting, and not by any higher-priority source. + bool IsUserControlled() const; + + // 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). + bool IsDefaultValue() const; + + // 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. + bool IsUserModifiable() const; + + // 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. + bool IsExtensionModifiable() const; + + // Return the registration flags for this pref as a bitmask of + // PrefRegistry::PrefRegistrationFlags. + uint32_t registration_flags() const { return registration_flags_; } + + private: + friend class PrefService; + + PrefValueStore* pref_value_store() const { + return pref_service_->pref_value_store_.get(); + } + + const std::string name_; + + const base::Value::Type type_; + + uint32_t registration_flags_; + + // Reference to the PrefService in which this pref was created. + const PrefService* pref_service_; + }; + + // You may wish to use PrefServiceFactory or one of its subclasses + // for simplified construction. + PrefService( + PrefNotifierImpl* pref_notifier, + PrefValueStore* pref_value_store, + PersistentPrefStore* user_prefs, + PrefRegistry* pref_registry, + base::Callback<void(PersistentPrefStore::PrefReadError)> + read_error_callback, + bool async); + virtual ~PrefService(); + + // Lands pending writes to disk. This should only be used if we need to save + // immediately (basically, during shutdown). + void CommitPendingWrite(); + + // Schedule a write if there is any lossy data pending. Unlike + // CommitPendingWrite() this does not immediately sync to disk, instead it + // triggers an eventual write if there is lossy data pending and if there + // isn't one scheduled already. + void SchedulePendingLossyWrites(); + + // Returns true if the preference for the given preference name is available + // and is managed. + bool IsManagedPreference(const std::string& pref_name) const; + + // Returns true if the preference for the given preference name is available + // and is controlled by the parent/guardian of the child Account. + bool IsPreferenceManagedByCustodian(const std::string& pref_name) const; + + // Returns |true| if a preference with the given name is available and its + // value can be changed by the user. + bool IsUserModifiablePreference(const std::string& pref_name) const; + + // Look up a preference. Returns NULL if the preference is not + // registered. + const PrefService::Preference* FindPreference(const std::string& path) const; + + // 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. + bool GetBoolean(const std::string& path) const; + int GetInteger(const std::string& path) const; + double GetDouble(const std::string& path) const; + std::string GetString(const std::string& path) const; + base::FilePath GetFilePath(const std::string& path) const; + + // 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. + const base::DictionaryValue* GetDictionary(const std::string& path) const; + const base::ListValue* GetList(const std::string& path) const; + + // Removes a user pref and restores the pref to its default value. + void ClearPref(const std::string& path); + + // 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. + void Set(const std::string& path, const base::Value& value); + void SetBoolean(const std::string& path, bool value); + void SetInteger(const std::string& path, int value); + void SetDouble(const std::string& path, double value); + void SetString(const std::string& path, const std::string& value); + void SetFilePath(const std::string& path, const base::FilePath& value); + + // 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. + void SetInt64(const std::string& path, int64_t value); + int64_t GetInt64(const std::string& path) const; + + // As above, but for unsigned values. + void SetUint64(const std::string& path, uint64_t value); + uint64_t GetUint64(const std::string& path) const; + + // Returns the value of the given preference, from the user pref store. If + // the preference is not set in the user pref store, returns NULL. + const base::Value* GetUserPrefValue(const std::string& path) const; + + // Changes the default value for a preference. Takes ownership of |value|. + // + // Will cause a pref change notification to be fired if this causes + // the effective value to change. + void SetDefaultPrefValue(const std::string& path, base::Value* value); + + // Returns the default value of the given preference. |path| must point to a + // registered preference. In that case, will never return NULL. + const base::Value* GetDefaultPrefValue(const std::string& path) const; + + // Returns true if a value has been set for the specified path. + // NOTE: this is NOT the same as FindPreference. In particular + // FindPreference returns whether RegisterXXX has been invoked, where as + // this checks if a value exists for the path. + bool HasPrefPath(const std::string& path) const; + + // Returns a dictionary with effective preference values. + scoped_ptr<base::DictionaryValue> GetPreferenceValues() const; + + // Returns a dictionary with effective preference values, omitting prefs that + // are at their default values. + scoped_ptr<base::DictionaryValue> GetPreferenceValuesOmitDefaults() const; + + // Returns a dictionary with effective preference values. Contrary to + // GetPreferenceValues(), the paths of registered preferences are not split on + // '.' characters. If a registered preference stores a dictionary, however, + // the hierarchical structure inside the preference will be preserved. + // For example, if "foo.bar" is a registered preference, the result could look + // like this: + // {"foo.bar": {"a": {"b": true}}}. + scoped_ptr<base::DictionaryValue> GetPreferenceValuesWithoutPathExpansion() + const; + + bool ReadOnly() const; + + PrefInitializationStatus GetInitializationStatus() const; + + // Tell our PrefValueStore to update itself to |command_line_store|. + // Takes ownership of the store. + virtual void UpdateCommandLinePrefStore(PrefStore* command_line_store); + + // We run the callback once, when initialization completes. The bool + // parameter will be set to true for successful initialization, + // false for unsuccessful. + void AddPrefInitObserver(base::Callback<void(bool)> callback); + + // Returns the PrefRegistry object for this service. You should not + // use this; the intent is for no registrations to take place after + // PrefService has been constructed. + // + // Instead of using this method, the recommended approach is to + // register all preferences for a class Xyz up front in a static + // Xyz::RegisterPrefs function, which gets invoked early in the + // application's start-up, before a PrefService is created. + // + // As an example, prefs registration in Chrome is triggered by the + // functions chrome::RegisterPrefs (for global preferences) and + // chrome::RegisterProfilePrefs (for user-specific preferences) + // implemented in chrome/browser/prefs/browser_prefs.cc. + PrefRegistry* DeprecatedGetPrefRegistry(); + + // Clears mutable values. + void ClearMutableValues(); + + protected: + // The PrefNotifier handles registering and notifying preference observers. + // It is created and owned by this PrefService. Subclasses may access it for + // unit testing. + scoped_ptr<PrefNotifierImpl> pref_notifier_; + + // The PrefValueStore provides prioritized preference values. It is owned by + // this PrefService. Subclasses may access it for unit testing. + scoped_ptr<PrefValueStore> pref_value_store_; + + scoped_refptr<PrefRegistry> pref_registry_; + + // Pref Stores and profile that we passed to the PrefValueStore. + scoped_refptr<PersistentPrefStore> user_pref_store_; + + // Callback to call when a read error occurs. + base::Callback<void(PersistentPrefStore::PrefReadError)> read_error_callback_; + + private: + // Hash map expected to be fastest here since it minimises expensive + // string comparisons. Order is unimportant, and deletions are rare. + // Confirmed on Android where this speeded Chrome startup by roughly 50ms + // vs. std::map, and by roughly 180ms vs. std::set of Preference pointers. + typedef base::hash_map<std::string, Preference> PreferenceMap; + + // Give access to ReportUserPrefChanged() and GetMutableUserPref(). + friend class subtle::ScopedUserPrefUpdateBase; + friend class PrefServiceTest_WriteablePrefStoreFlags_Test; + + // 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 + // OnPreferenceChanged method. Note that observers should not call + // these methods directly but rather use a PrefChangeRegistrar to + // make sure the observer gets cleaned up properly. + // + // Virtual for testing. + virtual void AddPrefObserver(const std::string& path, PrefObserver* obs); + virtual void RemovePrefObserver(const std::string& path, PrefObserver* obs); + + // Sends notification of a changed preference. This needs to be called by + // a ScopedUserPrefUpdate if a DictionaryValue or ListValue is changed. + void ReportUserPrefChanged(const std::string& key); + + // Sets the value for this pref path in the user pref store and informs the + // PrefNotifier of the change. + void SetUserPrefValue(const std::string& path, base::Value* new_value); + + // Load preferences from storage, attempting to diagnose and handle errors. + // This should only be called from the constructor. + void InitFromStorage(bool async); + + // Used to set the value of dictionary or list values in the user pref store. + // This will create a dictionary or list if one does not exist in the user + // pref store. This method returns NULL only if you're requesting an + // unregistered pref or a non-dict/non-list pref. + // |type| may only be Values::TYPE_DICTIONARY or Values::TYPE_LIST and + // |path| must point to a registered preference of type |type|. + // Ownership of the returned value remains at the user pref store. + base::Value* GetMutableUserPref(const std::string& path, + base::Value::Type type); + + // GetPreferenceValue is the equivalent of FindPreference(path)->GetValue(), + // it has been added for performance. If is faster because it does + // not need to find or create a Preference object to get the + // value (GetValue() calls back though the preference service to + // actually get the value.). + const base::Value* GetPreferenceValue(const std::string& path) const; + + // Local cache of registered Preference objects. The pref_registry_ + // is authoritative with respect to what the types and default values + // of registered preferences are. + mutable PreferenceMap prefs_map_; + + DISALLOW_COPY_AND_ASSIGN(PrefService); +}; + +#endif // COMPONENTS_PREFS_PREF_SERVICE_H_ diff --git a/chromium/components/prefs/pref_service_factory.cc b/chromium/components/prefs/pref_service_factory.cc new file mode 100644 index 00000000000..663695da988 --- /dev/null +++ b/chromium/components/prefs/pref_service_factory.cc @@ -0,0 +1,61 @@ +// Copyright 2013 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 "components/prefs/pref_service_factory.h" + +#include "base/bind.h" +#include "base/sequenced_task_runner.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/json_pref_store.h" +#include "components/prefs/pref_filter.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/pref_value_store.h" + +namespace { + +// Do-nothing default implementation. +void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) { +} + +} // namespace + +PrefServiceFactory::PrefServiceFactory() + : managed_prefs_(NULL), + supervised_user_prefs_(NULL), + extension_prefs_(NULL), + command_line_prefs_(NULL), + user_prefs_(NULL), + recommended_prefs_(NULL), + read_error_callback_(base::Bind(&DoNothingHandleReadError)), + async_(false) {} + +PrefServiceFactory::~PrefServiceFactory() {} + +void PrefServiceFactory::SetUserPrefsFile( + const base::FilePath& prefs_file, + base::SequencedTaskRunner* task_runner) { + user_prefs_ = new JsonPrefStore( + prefs_file, task_runner, scoped_ptr<PrefFilter>()); +} + +scoped_ptr<PrefService> PrefServiceFactory::Create( + PrefRegistry* pref_registry) { + PrefNotifierImpl* pref_notifier = new PrefNotifierImpl(); + scoped_ptr<PrefService> pref_service( + new PrefService(pref_notifier, + new PrefValueStore(managed_prefs_.get(), + supervised_user_prefs_.get(), + extension_prefs_.get(), + command_line_prefs_.get(), + user_prefs_.get(), + recommended_prefs_.get(), + pref_registry->defaults().get(), + pref_notifier), + user_prefs_.get(), + pref_registry, + read_error_callback_, + async_)); + return pref_service; +} diff --git a/chromium/components/prefs/pref_service_factory.h b/chromium/components/prefs/pref_service_factory.h new file mode 100644 index 00000000000..bf7f56ceec1 --- /dev/null +++ b/chromium/components/prefs/pref_service_factory.h @@ -0,0 +1,89 @@ +// Copyright 2013 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 COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_ +#define COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/prefs/pref_registry.h" +#include "components/prefs/pref_store.h" + +class PrefService; + +namespace base { +class FilePath; +class SequencedTaskRunner; +} + +// A class that allows convenient building of PrefService. +class COMPONENTS_PREFS_EXPORT PrefServiceFactory { + public: + PrefServiceFactory(); + virtual ~PrefServiceFactory(); + + // Functions for setting the various parameters of the PrefService to build. + void set_managed_prefs(const scoped_refptr<PrefStore>& managed_prefs) { + managed_prefs_ = managed_prefs; + } + void set_supervised_user_prefs( + const scoped_refptr<PrefStore>& supervised_user_prefs) { + supervised_user_prefs_ = supervised_user_prefs; + } + void set_extension_prefs(const scoped_refptr<PrefStore>& extension_prefs) { + extension_prefs_ = extension_prefs; + } + void set_command_line_prefs( + const scoped_refptr<PrefStore>& command_line_prefs) { + command_line_prefs_ = command_line_prefs; + } + void set_user_prefs(const scoped_refptr<PersistentPrefStore>& user_prefs) { + user_prefs_ = user_prefs; + } + void set_recommended_prefs( + const scoped_refptr<PrefStore>& recommended_prefs) { + recommended_prefs_ = recommended_prefs; + } + + // Sets up error callback for the PrefService. A do-nothing default + // is provided if this is not called. + void set_read_error_callback( + const base::Callback<void(PersistentPrefStore::PrefReadError)>& + read_error_callback) { + read_error_callback_ = read_error_callback; + } + + // Specifies to use an actual file-backed user pref store. + void SetUserPrefsFile(const base::FilePath& prefs_file, + base::SequencedTaskRunner* task_runner); + + void set_async(bool async) { + async_ = async; + } + + // Creates a PrefService object initialized with the parameters from + // this factory. + scoped_ptr<PrefService> Create(PrefRegistry* registry); + + protected: + scoped_refptr<PrefStore> managed_prefs_; + scoped_refptr<PrefStore> supervised_user_prefs_; + scoped_refptr<PrefStore> extension_prefs_; + scoped_refptr<PrefStore> command_line_prefs_; + scoped_refptr<PersistentPrefStore> user_prefs_; + scoped_refptr<PrefStore> recommended_prefs_; + + base::Callback<void(PersistentPrefStore::PrefReadError)> read_error_callback_; + + // Defaults to false. + bool async_; + + private: + DISALLOW_COPY_AND_ASSIGN(PrefServiceFactory); +}; + +#endif // COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_ diff --git a/chromium/components/prefs/pref_service_unittest.cc b/chromium/components/prefs/pref_service_unittest.cc new file mode 100644 index 00000000000..77ecd199fc9 --- /dev/null +++ b/chromium/components/prefs/pref_service_unittest.cc @@ -0,0 +1,430 @@ +// 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 <stddef.h> +#include <stdint.h> + +#include <string> + +#include "base/macros.h" +#include "base/values.h" +#include "components/prefs/json_pref_store.h" +#include "components/prefs/mock_pref_change_callback.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_service_factory.h" +#include "components/prefs/pref_value_store.h" +#include "components/prefs/testing_pref_service.h" +#include "components/prefs/testing_pref_store.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Mock; + +const char kPrefName[] = "pref.name"; + +TEST(PrefServiceTest, NoObserverFire) { + TestingPrefServiceSimple prefs; + + const char pref_name[] = "homepage"; + prefs.registry()->RegisterStringPref(pref_name, std::string()); + + const char new_pref_value[] = "http://www.google.com/"; + MockPrefChangeCallback obs(&prefs); + PrefChangeRegistrar registrar; + registrar.Init(&prefs); + registrar.Add(pref_name, obs.GetCallback()); + + // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged. + const base::StringValue expected_value(new_pref_value); + obs.Expect(pref_name, &expected_value); + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + + // Setting the pref to the same value should not set the pref value a second + // time. + EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0); + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + + // Clearing the pref should cause the pref to fire. + const base::StringValue expected_default_value((std::string())); + obs.Expect(pref_name, &expected_default_value); + prefs.ClearPref(pref_name); + Mock::VerifyAndClearExpectations(&obs); + + // Clearing the pref again should not cause the pref to fire. + EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0); + prefs.ClearPref(pref_name); + Mock::VerifyAndClearExpectations(&obs); +} + +TEST(PrefServiceTest, HasPrefPath) { + TestingPrefServiceSimple prefs; + + const char path[] = "fake.path"; + + // Shouldn't initially have a path. + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Register the path. This doesn't set a value, so the path still shouldn't + // exist. + prefs.registry()->RegisterStringPref(path, std::string()); + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Set a value and make sure we have a path. + prefs.SetString(path, "blah"); + EXPECT_TRUE(prefs.HasPrefPath(path)); +} + +TEST(PrefServiceTest, Observers) { + const char pref_name[] = "homepage"; + + TestingPrefServiceSimple prefs; + prefs.SetUserPref(pref_name, + new base::StringValue("http://www.cnn.com")); + prefs.registry()->RegisterStringPref(pref_name, std::string()); + + const char new_pref_value[] = "http://www.google.com/"; + const base::StringValue expected_new_pref_value(new_pref_value); + MockPrefChangeCallback obs(&prefs); + PrefChangeRegistrar registrar; + registrar.Init(&prefs); + registrar.Add(pref_name, obs.GetCallback()); + + PrefChangeRegistrar registrar_two; + registrar_two.Init(&prefs); + + // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged. + obs.Expect(pref_name, &expected_new_pref_value); + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + + // Now try adding a second pref observer. + const char new_pref_value2[] = "http://www.youtube.com/"; + const base::StringValue expected_new_pref_value2(new_pref_value2); + MockPrefChangeCallback obs2(&prefs); + obs.Expect(pref_name, &expected_new_pref_value2); + obs2.Expect(pref_name, &expected_new_pref_value2); + registrar_two.Add(pref_name, obs2.GetCallback()); + // This should fire the checks in obs and obs2. + prefs.SetString(pref_name, new_pref_value2); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); + + // Set a recommended value. + const base::StringValue recommended_pref_value("http://www.gmail.com/"); + obs.Expect(pref_name, &expected_new_pref_value2); + obs2.Expect(pref_name, &expected_new_pref_value2); + // This should fire the checks in obs and obs2 but with an unchanged value + // as the recommended value is being overridden by the user-set value. + prefs.SetRecommendedPref(pref_name, recommended_pref_value.DeepCopy()); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); + + // Make sure obs2 still works after removing obs. + registrar.Remove(pref_name); + EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0); + obs2.Expect(pref_name, &expected_new_pref_value); + // This should only fire the observer in obs2. + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); +} + +// Make sure that if a preference changes type, so the wrong type is stored in +// the user pref file, it uses the correct fallback value instead. +TEST(PrefServiceTest, GetValueChangedType) { + const int kTestValue = 10; + TestingPrefServiceSimple prefs; + prefs.registry()->RegisterIntegerPref(kPrefName, kTestValue); + + // Check falling back to a recommended value. + prefs.SetUserPref(kPrefName, + new base::StringValue("not an integer")); + const PrefService::Preference* pref = prefs.FindPreference(kPrefName); + ASSERT_TRUE(pref); + const base::Value* value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType()); + int actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kTestValue, actual_int_value); +} + +TEST(PrefServiceTest, GetValueAndGetRecommendedValue) { + const int kDefaultValue = 5; + const int kUserValue = 10; + const int kRecommendedValue = 15; + TestingPrefServiceSimple prefs; + prefs.registry()->RegisterIntegerPref(kPrefName, kDefaultValue); + + // Create pref with a default value only. + const PrefService::Preference* pref = prefs.FindPreference(kPrefName); + ASSERT_TRUE(pref); + + // Check that GetValue() returns the default value. + const base::Value* value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType()); + int actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kDefaultValue, actual_int_value); + + // Check that GetRecommendedValue() returns no value. + value = pref->GetRecommendedValue(); + ASSERT_FALSE(value); + + // Set a user-set value. + prefs.SetUserPref(kPrefName, new base::FundamentalValue(kUserValue)); + + // Check that GetValue() returns the user-set value. + value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kUserValue, actual_int_value); + + // Check that GetRecommendedValue() returns no value. + value = pref->GetRecommendedValue(); + ASSERT_FALSE(value); + + // Set a recommended value. + prefs.SetRecommendedPref(kPrefName, + new base::FundamentalValue(kRecommendedValue)); + + // Check that GetValue() returns the user-set value. + value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kUserValue, actual_int_value); + + // Check that GetRecommendedValue() returns the recommended value. + value = pref->GetRecommendedValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kRecommendedValue, actual_int_value); + + // Remove the user-set value. + prefs.RemoveUserPref(kPrefName); + + // Check that GetValue() returns the recommended value. + value = pref->GetValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kRecommendedValue, actual_int_value); + + // Check that GetRecommendedValue() returns the recommended value. + value = pref->GetRecommendedValue(); + ASSERT_TRUE(value); + EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType()); + actual_int_value = -1; + EXPECT_TRUE(value->GetAsInteger(&actual_int_value)); + EXPECT_EQ(kRecommendedValue, actual_int_value); +} + +// A PrefStore which just stores the last write flags that were used to write +// values to it. +class WriteFlagChecker : public TestingPrefStore { + public: + WriteFlagChecker() {} + + void ReportValueChanged(const std::string& key, uint32_t flags) override { + SetLastWriteFlags(flags); + } + + void SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override { + SetLastWriteFlags(flags); + } + + void SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override { + SetLastWriteFlags(flags); + } + + void RemoveValue(const std::string& key, uint32_t flags) override { + SetLastWriteFlags(flags); + } + + uint32_t GetLastFlagsAndClear() { + CHECK(last_write_flags_set_); + uint32_t result = last_write_flags_; + last_write_flags_set_ = false; + last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; + return result; + } + + bool last_write_flags_set() { return last_write_flags_set_; } + + private: + ~WriteFlagChecker() override {} + + void SetLastWriteFlags(uint32_t flags) { + CHECK(!last_write_flags_set_); + last_write_flags_set_ = true; + last_write_flags_ = flags; + } + + bool last_write_flags_set_ = false; + uint32_t last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS; +}; + +TEST(PrefServiceTest, WriteablePrefStoreFlags) { + scoped_refptr<WriteFlagChecker> flag_checker(new WriteFlagChecker); + scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple); + PrefServiceFactory factory; + factory.set_user_prefs(flag_checker); + scoped_ptr<PrefService> prefs(factory.Create(registry.get())); + + // The first 8 bits of write flags are reserved for subclasses. Create a + // custom flag in this range + uint32_t kCustomRegistrationFlag = 1 << 2; + + // A map of the registration flags that will be tested and the write flags + // they are expected to convert to. + struct RegistrationToWriteFlags { + const char* pref_name; + uint32_t registration_flags; + uint32_t write_flags; + }; + const RegistrationToWriteFlags kRegistrationToWriteFlags[] = { + {"none", + PrefRegistry::NO_REGISTRATION_FLAGS, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS}, + {"lossy", + PrefRegistry::LOSSY_PREF, + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}, + {"custom", + kCustomRegistrationFlag, + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS}, + {"lossyandcustom", + PrefRegistry::LOSSY_PREF | kCustomRegistrationFlag, + WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}}; + + for (size_t i = 0; i < arraysize(kRegistrationToWriteFlags); ++i) { + RegistrationToWriteFlags entry = kRegistrationToWriteFlags[i]; + registry->RegisterDictionaryPref( + entry.pref_name, new base::DictionaryValue(), entry.registration_flags); + + SCOPED_TRACE("Currently testing pref with name: " + + std::string(entry.pref_name)); + + prefs->GetMutableUserPref(entry.pref_name, base::Value::TYPE_DICTIONARY); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->ReportUserPrefChanged(entry.pref_name); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->ClearPref(entry.pref_name); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + + prefs->SetUserPrefValue(entry.pref_name, new base::DictionaryValue()); + EXPECT_TRUE(flag_checker->last_write_flags_set()); + EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear()); + } +} + +class PrefServiceSetValueTest : public testing::Test { + protected: + static const char kName[]; + static const char kValue[]; + + PrefServiceSetValueTest() : observer_(&prefs_) {} + + TestingPrefServiceSimple prefs_; + MockPrefChangeCallback observer_; +}; + +const char PrefServiceSetValueTest::kName[] = "name"; +const char PrefServiceSetValueTest::kValue[] = "value"; + +TEST_F(PrefServiceSetValueTest, SetStringValue) { + const char default_string[] = "default"; + const base::StringValue default_value(default_string); + prefs_.registry()->RegisterStringPref(kName, default_string); + + PrefChangeRegistrar registrar; + registrar.Init(&prefs_); + registrar.Add(kName, observer_.GetCallback()); + + // Changing the controlling store from default to user triggers notification. + observer_.Expect(kName, &default_value); + prefs_.Set(kName, default_value); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.Set(kName, default_value); + Mock::VerifyAndClearExpectations(&observer_); + + base::StringValue new_value(kValue); + observer_.Expect(kName, &new_value); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(PrefServiceSetValueTest, SetDictionaryValue) { + prefs_.registry()->RegisterDictionaryPref(kName); + PrefChangeRegistrar registrar; + registrar.Init(&prefs_); + registrar.Add(kName, observer_.GetCallback()); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.RemoveUserPref(kName); + Mock::VerifyAndClearExpectations(&observer_); + + base::DictionaryValue new_value; + new_value.SetString(kName, kValue); + observer_.Expect(kName, &new_value); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + base::DictionaryValue empty; + observer_.Expect(kName, &empty); + prefs_.Set(kName, empty); + Mock::VerifyAndClearExpectations(&observer_); +} + +TEST_F(PrefServiceSetValueTest, SetListValue) { + prefs_.registry()->RegisterListPref(kName); + PrefChangeRegistrar registrar; + registrar.Init(&prefs_); + registrar.Add(kName, observer_.GetCallback()); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.RemoveUserPref(kName); + Mock::VerifyAndClearExpectations(&observer_); + + base::ListValue new_value; + new_value.Append(new base::StringValue(kValue)); + observer_.Expect(kName, &new_value); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); + + base::ListValue empty; + observer_.Expect(kName, &empty); + prefs_.Set(kName, empty); + Mock::VerifyAndClearExpectations(&observer_); +} diff --git a/chromium/components/prefs/pref_store.cc b/chromium/components/prefs/pref_store.cc new file mode 100644 index 00000000000..2ae0afcb08d --- /dev/null +++ b/chromium/components/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 "components/prefs/pref_store.h" + +bool PrefStore::HasObservers() const { + return false; +} + +bool PrefStore::IsInitializationComplete() const { + return true; +} diff --git a/chromium/components/prefs/pref_store.h b/chromium/components/prefs/pref_store.h new file mode 100644 index 00000000000..ef2bc494d15 --- /dev/null +++ b/chromium/components/prefs/pref_store.h @@ -0,0 +1,63 @@ +// 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 COMPONENTS_PREFS_PREF_STORE_H_ +#define COMPONENTS_PREFS_PREF_STORE_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "components/prefs/base_prefs_export.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 COMPONENTS_PREFS_EXPORT PrefStore : public base::RefCounted<PrefStore> { + public: + // Observer interface for monitoring PrefStore. + class COMPONENTS_PREFS_EXPORT 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() {} + }; + + PrefStore() {} + + // Add and remove observers. + virtual void AddObserver(Observer* observer) {} + virtual void RemoveObserver(Observer* observer) {} + virtual bool HasObservers() 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 true and if |result| + // is not NULL. Ownership of the |*result| value remains with the PrefStore. + virtual bool 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 // COMPONENTS_PREFS_PREF_STORE_H_ diff --git a/chromium/components/prefs/pref_store_observer_mock.cc b/chromium/components/prefs/pref_store_observer_mock.cc new file mode 100644 index 00000000000..a03c8580056 --- /dev/null +++ b/chromium/components/prefs/pref_store_observer_mock.cc @@ -0,0 +1,29 @@ +// 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 "components/prefs/pref_store_observer_mock.h" + +#include "testing/gtest/include/gtest/gtest.h" + +PrefStoreObserverMock::PrefStoreObserverMock() + : initialized(false), initialization_success(false) {} + +PrefStoreObserverMock::~PrefStoreObserverMock() {} + +void PrefStoreObserverMock::VerifyAndResetChangedKey( + const std::string& expected) { + EXPECT_EQ(1u, changed_keys.size()); + if (changed_keys.size() >= 1) + EXPECT_EQ(expected, changed_keys.front()); + changed_keys.clear(); +} + +void PrefStoreObserverMock::OnPrefValueChanged(const std::string& key) { + changed_keys.push_back(key); +} + +void PrefStoreObserverMock::OnInitializationCompleted(bool success) { + initialized = true; + initialization_success = success; +} diff --git a/chromium/components/prefs/pref_store_observer_mock.h b/chromium/components/prefs/pref_store_observer_mock.h new file mode 100644 index 00000000000..01bd6a6d966 --- /dev/null +++ b/chromium/components/prefs/pref_store_observer_mock.h @@ -0,0 +1,35 @@ +// 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 COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_ +#define COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_ + +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "components/prefs/pref_store.h" + +// A mock implementation of PrefStore::Observer. +class PrefStoreObserverMock : public PrefStore::Observer { + public: + PrefStoreObserverMock(); + ~PrefStoreObserverMock() override; + + void VerifyAndResetChangedKey(const std::string& expected); + + // PrefStore::Observer implementation + void OnPrefValueChanged(const std::string& key) override; + void OnInitializationCompleted(bool success) override; + + std::vector<std::string> changed_keys; + bool initialized; + bool initialization_success; // Only valid if |initialized|. + + private: + DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock); +}; + +#endif // COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_ diff --git a/chromium/components/prefs/pref_value_map.cc b/chromium/components/prefs/pref_value_map.cc new file mode 100644 index 00000000000..7b267cc7d16 --- /dev/null +++ b/chromium/components/prefs/pref_value_map.cc @@ -0,0 +1,144 @@ +// 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 "components/prefs/pref_value_map.h" + +#include <map> +#include <utility> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "base/values.h" + +PrefValueMap::PrefValueMap() {} + +PrefValueMap::~PrefValueMap() {} + +bool PrefValueMap::GetValue(const std::string& key, + const base::Value** value) const { + const base::Value* got_value = prefs_.get(key); + if (value && got_value) + *value = got_value; + + return !!got_value; +} + +bool PrefValueMap::GetValue(const std::string& key, base::Value** value) { + base::Value* got_value = prefs_.get(key); + if (value && got_value) + *value = got_value; + + return !!got_value; +} + +bool PrefValueMap::SetValue(const std::string& key, + scoped_ptr<base::Value> value) { + DCHECK(value); + + base::Value* old_value = prefs_.get(key); + if (old_value && value->Equals(old_value)) + return false; + + prefs_.set(key, std::move(value)); + return true; +} + +bool PrefValueMap::RemoveValue(const std::string& key) { + return prefs_.erase(key) != 0; +} + +void PrefValueMap::Clear() { + 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 base::Value* stored_value = nullptr; + return GetValue(key, &stored_value) && stored_value->GetAsBoolean(value); +} + +void PrefValueMap::SetBoolean(const std::string& key, bool value) { + SetValue(key, make_scoped_ptr(new base::FundamentalValue(value))); +} + +bool PrefValueMap::GetString(const std::string& key, + std::string* value) const { + const base::Value* stored_value = nullptr; + return GetValue(key, &stored_value) && stored_value->GetAsString(value); +} + +void PrefValueMap::SetString(const std::string& key, + const std::string& value) { + SetValue(key, make_scoped_ptr(new base::StringValue(value))); +} + +bool PrefValueMap::GetInteger(const std::string& key, int* value) const { + const base::Value* stored_value = nullptr; + return GetValue(key, &stored_value) && stored_value->GetAsInteger(value); +} + +void PrefValueMap::SetInteger(const std::string& key, const int value) { + SetValue(key, make_scoped_ptr(new base::FundamentalValue(value))); +} + +void PrefValueMap::SetDouble(const std::string& key, const double value) { + SetValue(key, make_scoped_ptr(new base::FundamentalValue(value))); +} + +void PrefValueMap::GetDifferingKeys( + const PrefValueMap* other, + std::vector<std::string>* differing_keys) const { + differing_keys->clear(); + + // Put everything into ordered maps. + std::map<std::string, base::Value*> this_prefs(prefs_.begin(), prefs_.end()); + std::map<std::string, base::Value*> other_prefs(other->prefs_.begin(), + other->prefs_.end()); + + // Walk over the maps in lockstep, adding everything that is different. + auto this_pref(this_prefs.begin()); + auto other_pref(other_prefs.begin()); + while (this_pref != this_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 != this_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/chromium/components/prefs/pref_value_map.h b/chromium/components/prefs/pref_value_map.h new file mode 100644 index 00000000000..988a6389197 --- /dev/null +++ b/chromium/components/prefs/pref_value_map.h @@ -0,0 +1,91 @@ +// 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 COMPONENTS_PREFS_PREF_VALUE_MAP_H_ +#define COMPONENTS_PREFS_PREF_VALUE_MAP_H_ + +#include <string> +#include <vector> + +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "components/prefs/base_prefs_export.h" + +namespace base { +class Value; +} + +// A generic string to value map used by the PrefStore implementations. +class COMPONENTS_PREFS_EXPORT PrefValueMap { + public: + using Map = base::ScopedPtrHashMap<std::string, scoped_ptr<base::Value>>; + using iterator = Map::iterator; + using const_iterator = Map::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|. |value| must be non-null. Returns true if the + // value changed. + bool SetValue(const std::string& key, scoped_ptr<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); + + // Sets the value for |key| to the double |value|. + void SetDouble(const std::string& key, const double 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: + Map prefs_; + + DISALLOW_COPY_AND_ASSIGN(PrefValueMap); +}; + +#endif // COMPONENTS_PREFS_PREF_VALUE_MAP_H_ diff --git a/chromium/components/prefs/pref_value_map_unittest.cc b/chromium/components/prefs/pref_value_map_unittest.cc new file mode 100644 index 00000000000..04001a219ea --- /dev/null +++ b/chromium/components/prefs/pref_value_map_unittest.cc @@ -0,0 +1,135 @@ +// 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 "components/prefs/pref_value_map.h" + +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +TEST(PrefValueMapTest, SetValue) { + PrefValueMap map; + const Value* result = NULL; + EXPECT_FALSE(map.GetValue("key", &result)); + EXPECT_FALSE(result); + + EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test")))); + EXPECT_FALSE(map.SetValue("key", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("hi mom!")))); + + EXPECT_TRUE(map.GetValue("key", &result)); + EXPECT_TRUE(StringValue("hi mom!").Equals(result)); +} + +TEST(PrefValueMapTest, GetAndSetIntegerValue) { + PrefValueMap map; + ASSERT_TRUE(map.SetValue("key", make_scoped_ptr(new FundamentalValue(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(PrefValueMapTest, SetDoubleValue) { + PrefValueMap map; + ASSERT_TRUE(map.SetValue("key", make_scoped_ptr(new FundamentalValue(5.5)))); + + const Value* result = NULL; + ASSERT_TRUE(map.GetValue("key", &result)); + double double_value = 0.; + EXPECT_TRUE(result->GetAsDouble(&double_value)); + EXPECT_DOUBLE_EQ(5.5, double_value); +} + +TEST(PrefValueMapTest, RemoveValue) { + PrefValueMap map; + EXPECT_FALSE(map.RemoveValue("key")); + + EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE(map.GetValue("key", NULL)); + + EXPECT_TRUE(map.RemoveValue("key")); + EXPECT_FALSE(map.GetValue("key", NULL)); + + EXPECT_FALSE(map.RemoveValue("key")); +} + +TEST(PrefValueMapTest, Clear) { + PrefValueMap map; + EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE(map.GetValue("key", NULL)); + + map.Clear(); + + EXPECT_FALSE(map.GetValue("key", NULL)); +} + +TEST(PrefValueMapTest, GetDifferingKeys) { + PrefValueMap reference; + EXPECT_TRUE( + reference.SetValue("b", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE( + reference.SetValue("c", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE( + reference.SetValue("e", make_scoped_ptr(new StringValue("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", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE(check.SetValue("c", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE(check.SetValue("d", make_scoped_ptr(new StringValue("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(PrefValueMapTest, SwapTwoMaps) { + PrefValueMap first_map; + EXPECT_TRUE( + first_map.SetValue("a", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE( + first_map.SetValue("b", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE( + first_map.SetValue("c", make_scoped_ptr(new StringValue("test")))); + + PrefValueMap second_map; + EXPECT_TRUE( + second_map.SetValue("d", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE( + second_map.SetValue("e", make_scoped_ptr(new StringValue("test")))); + EXPECT_TRUE( + second_map.SetValue("f", make_scoped_ptr(new StringValue("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)); +} + +} // namespace +} // namespace base diff --git a/chromium/components/prefs/pref_value_store.cc b/chromium/components/prefs/pref_value_store.cc new file mode 100644 index 00000000000..4c7a4e5d9cf --- /dev/null +++ b/chromium/components/prefs/pref_value_store.cc @@ -0,0 +1,290 @@ +// 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 "components/prefs/pref_value_store.h" + +#include <stddef.h> + +#include "base/logging.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/pref_observer.h" + +PrefValueStore::PrefStoreKeeper::PrefStoreKeeper() + : pref_value_store_(NULL), + type_(PrefValueStore::INVALID_STORE) { +} + +PrefValueStore::PrefStoreKeeper::~PrefStoreKeeper() { + if (pref_store_.get()) { + pref_store_->RemoveObserver(this); + pref_store_ = NULL; + } + pref_value_store_ = NULL; +} + +void PrefValueStore::PrefStoreKeeper::Initialize( + PrefValueStore* store, + PrefStore* pref_store, + PrefValueStore::PrefStoreType type) { + if (pref_store_.get()) { + pref_store_->RemoveObserver(this); + DCHECK(!pref_store_->HasObservers()); + } + type_ = type; + pref_value_store_ = store; + pref_store_ = pref_store; + if (pref_store_.get()) + pref_store_->AddObserver(this); +} + +void PrefValueStore::PrefStoreKeeper::OnPrefValueChanged( + const std::string& key) { + pref_value_store_->OnPrefValueChanged(type_, key); +} + +void PrefValueStore::PrefStoreKeeper::OnInitializationCompleted( + bool succeeded) { + pref_value_store_->OnInitializationCompleted(type_, succeeded); +} + +PrefValueStore::PrefValueStore(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier) + : pref_notifier_(pref_notifier), + initialization_failed_(false) { + InitPrefStore(MANAGED_STORE, managed_prefs); + InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs); + InitPrefStore(EXTENSION_STORE, extension_prefs); + InitPrefStore(COMMAND_LINE_STORE, command_line_prefs); + InitPrefStore(USER_STORE, user_prefs); + InitPrefStore(RECOMMENDED_STORE, recommended_prefs); + InitPrefStore(DEFAULT_STORE, default_prefs); + + CheckInitializationCompleted(); +} + +PrefValueStore::~PrefValueStore() {} + +PrefValueStore* PrefValueStore::CloneAndSpecialize( + PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier) { + DCHECK(pref_notifier); + if (!managed_prefs) + managed_prefs = GetPrefStore(MANAGED_STORE); + if (!supervised_user_prefs) + supervised_user_prefs = GetPrefStore(SUPERVISED_USER_STORE); + if (!extension_prefs) + extension_prefs = GetPrefStore(EXTENSION_STORE); + if (!command_line_prefs) + command_line_prefs = GetPrefStore(COMMAND_LINE_STORE); + if (!user_prefs) + user_prefs = GetPrefStore(USER_STORE); + if (!recommended_prefs) + recommended_prefs = GetPrefStore(RECOMMENDED_STORE); + if (!default_prefs) + default_prefs = GetPrefStore(DEFAULT_STORE); + + return new PrefValueStore( + managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs, + user_prefs, recommended_prefs, default_prefs, pref_notifier); +} + +void PrefValueStore::set_callback(const PrefChangedCallback& callback) { + pref_changed_callback_ = callback; +} + +bool PrefValueStore::GetValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const { + // Check the |PrefStore|s in order of their priority from highest to lowest, + // looking for the first preference value with the given |name| and |type|. + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + if (GetValueFromStoreWithType(name, type, static_cast<PrefStoreType>(i), + out_value)) + return true; + } + return false; +} + +bool PrefValueStore::GetRecommendedValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const { + return GetValueFromStoreWithType(name, type, RECOMMENDED_STORE, out_value); +} + +void PrefValueStore::NotifyPrefChanged( + const std::string& path, + PrefValueStore::PrefStoreType new_store) { + DCHECK(new_store != INVALID_STORE); + // A notification is sent when the pref value in any store changes. If this + // store is currently being overridden by a higher-priority store, the + // effective value of the pref will not have changed. + pref_notifier_->OnPreferenceChanged(path); + if (!pref_changed_callback_.is_null()) + pref_changed_callback_.Run(path); +} + +bool PrefValueStore::PrefValueInManagedStore(const std::string& name) const { + return PrefValueInStore(name, MANAGED_STORE); +} + +bool PrefValueStore::PrefValueInSupervisedStore(const std::string& name) const { + return PrefValueInStore(name, SUPERVISED_USER_STORE); +} + +bool PrefValueStore::PrefValueInExtensionStore(const std::string& name) const { + return PrefValueInStore(name, EXTENSION_STORE); +} + +bool PrefValueStore::PrefValueInUserStore(const std::string& name) const { + return PrefValueInStore(name, USER_STORE); +} + +bool PrefValueStore::PrefValueFromExtensionStore( + const std::string& name) const { + return ControllingPrefStoreForPref(name) == EXTENSION_STORE; +} + +bool PrefValueStore::PrefValueFromUserStore(const std::string& name) const { + return ControllingPrefStoreForPref(name) == USER_STORE; +} + +bool PrefValueStore::PrefValueFromRecommendedStore( + const std::string& name) const { + return ControllingPrefStoreForPref(name) == RECOMMENDED_STORE; +} + +bool PrefValueStore::PrefValueFromDefaultStore(const std::string& name) const { + return ControllingPrefStoreForPref(name) == DEFAULT_STORE; +} + +bool PrefValueStore::PrefValueUserModifiable(const std::string& name) const { + PrefStoreType effective_store = ControllingPrefStoreForPref(name); + return effective_store >= USER_STORE || + effective_store == INVALID_STORE; +} + +bool PrefValueStore::PrefValueExtensionModifiable( + const std::string& name) const { + PrefStoreType effective_store = ControllingPrefStoreForPref(name); + return effective_store >= EXTENSION_STORE || + effective_store == INVALID_STORE; +} + +void PrefValueStore::UpdateCommandLinePrefStore(PrefStore* command_line_prefs) { + InitPrefStore(COMMAND_LINE_STORE, command_line_prefs); +} + +bool PrefValueStore::PrefValueInStore( + const std::string& name, + PrefValueStore::PrefStoreType store) const { + // Declare a temp Value* and call GetValueFromStore, + // ignoring the output value. + const base::Value* tmp_value = NULL; + return GetValueFromStore(name, store, &tmp_value); +} + +bool PrefValueStore::PrefValueInStoreRange( + const std::string& name, + PrefValueStore::PrefStoreType first_checked_store, + PrefValueStore::PrefStoreType last_checked_store) const { + if (first_checked_store > last_checked_store) { + NOTREACHED(); + return false; + } + + for (size_t i = first_checked_store; + i <= static_cast<size_t>(last_checked_store); ++i) { + if (PrefValueInStore(name, static_cast<PrefStoreType>(i))) + return true; + } + return false; +} + +PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref( + const std::string& name) const { + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + if (PrefValueInStore(name, static_cast<PrefStoreType>(i))) + return static_cast<PrefStoreType>(i); + } + return INVALID_STORE; +} + +bool PrefValueStore::GetValueFromStore(const std::string& name, + PrefValueStore::PrefStoreType store_type, + const base::Value** out_value) const { + // Only return true if we find a value and it is the correct type, so stale + // values with the incorrect type will be ignored. + const PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(store_type)); + if (store && store->GetValue(name, out_value)) + return true; + + // No valid value found for the given preference name: set the return value + // to false. + *out_value = NULL; + return false; +} + +bool PrefValueStore::GetValueFromStoreWithType( + const std::string& name, + base::Value::Type type, + PrefStoreType store, + const base::Value** out_value) const { + if (GetValueFromStore(name, store, out_value)) { + if ((*out_value)->IsType(type)) + return true; + + LOG(WARNING) << "Expected type for " << name << " is " << type + << " but got " << (*out_value)->GetType() + << " in store " << store; + } + + *out_value = NULL; + return false; +} + +void PrefValueStore::OnPrefValueChanged(PrefValueStore::PrefStoreType type, + const std::string& key) { + NotifyPrefChanged(key, type); +} + +void PrefValueStore::OnInitializationCompleted( + PrefValueStore::PrefStoreType type, bool succeeded) { + if (initialization_failed_) + return; + if (!succeeded) { + initialization_failed_ = true; + pref_notifier_->OnInitializationCompleted(false); + return; + } + CheckInitializationCompleted(); +} + +void PrefValueStore::InitPrefStore(PrefValueStore::PrefStoreType type, + PrefStore* pref_store) { + pref_stores_[type].Initialize(this, pref_store, type); +} + +void PrefValueStore::CheckInitializationCompleted() { + if (initialization_failed_) + return; + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + scoped_refptr<PrefStore> store = + GetPrefStore(static_cast<PrefStoreType>(i)); + if (store.get() && !store->IsInitializationComplete()) + return; + } + pref_notifier_->OnInitializationCompleted(true); +} diff --git a/chromium/components/prefs/pref_value_store.h b/chromium/components/prefs/pref_value_store.h new file mode 100644 index 00000000000..39b6bf1870e --- /dev/null +++ b/chromium/components/prefs/pref_value_store.h @@ -0,0 +1,260 @@ +// 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 COMPONENTS_PREFS_PREF_VALUE_STORE_H_ +#define COMPONENTS_PREFS_PREF_VALUE_STORE_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/values.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_store.h" + +class PrefNotifier; +class PrefStore; + +// The PrefValueStore manages various sources of values for Preferences +// (e.g., configuration policies, extensions, and user settings). It returns +// the value of a Preference from the source with the highest priority, and +// allows setting user-defined values for preferences that are not managed. +// +// Unless otherwise explicitly noted, all of the methods of this class must +// be called on the UI thread. +class COMPONENTS_PREFS_EXPORT PrefValueStore { + public: + typedef base::Callback<void(const std::string&)> PrefChangedCallback; + + // In decreasing order of precedence: + // |managed_prefs| contains all preferences from mandatory policies. + // |supervised_user_prefs| contains all preferences from supervised user + // settings, i.e. settings configured for a supervised user by their + // custodian. + // |extension_prefs| contains preference values set by extensions. + // |command_line_prefs| contains preference values set by command-line + // switches. + // |user_prefs| contains all user-set preference values. + // |recommended_prefs| contains all preferences from recommended policies. + // |default_prefs| contains application-default preference values. It must + // be non-null if any preferences are to be registered. + // + // |pref_notifier| facilitates broadcasting preference change notifications + // to the world. + PrefValueStore(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier); + virtual ~PrefValueStore(); + + // Creates a clone of this PrefValueStore with PrefStores overwritten + // by the parameters passed, if unequal NULL. + PrefValueStore* CloneAndSpecialize(PrefStore* managed_prefs, + PrefStore* supervised_user_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier); + + // A PrefValueStore can have exactly one callback that is directly + // notified of preferences changing in the store. This does not + // filter through the PrefNotifier mechanism, which may not forward + // certain changes (e.g. unregistered prefs). + void set_callback(const PrefChangedCallback& callback); + + // Gets the value for the given preference name that has the specified value + // type. Values stored in a PrefStore that have the matching |name| but + // a non-matching |type| are silently skipped. Returns true if a valid value + // was found in any of the available PrefStores. Most callers should use + // Preference::GetValue() instead of calling this method directly. + bool GetValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const; + + // Gets the recommended value for the given preference name that has the + // specified value type. A value stored in the recommended PrefStore that has + // the matching |name| but a non-matching |type| is silently ignored. Returns + // true if a valid value was found. Most callers should use + // Preference::GetRecommendedValue() instead of calling this method directly. + bool GetRecommendedValue(const std::string& name, + base::Value::Type type, + const base::Value** out_value) const; + + // These methods return true if a preference with the given name is in the + // indicated pref store, even if that value is currently being overridden by + // a higher-priority source. + bool PrefValueInManagedStore(const std::string& name) const; + bool PrefValueInSupervisedStore(const std::string& name) const; + bool PrefValueInExtensionStore(const std::string& name) const; + bool PrefValueInUserStore(const std::string& name) const; + + // These methods return true if a preference with the given name is actually + // being controlled by the indicated pref store and not being overridden by + // a higher-priority source. + bool PrefValueFromExtensionStore(const std::string& name) const; + bool PrefValueFromUserStore(const std::string& name) const; + bool PrefValueFromRecommendedStore(const std::string& name) const; + bool PrefValueFromDefaultStore(const std::string& name) const; + + // Check whether a Preference value is modifiable by the user, i.e. whether + // there is no higher-priority source controlling it. + bool PrefValueUserModifiable(const std::string& name) const; + + // Check whether a Preference value is modifiable by an extension, i.e. + // whether there is no higher-priority source controlling it. + bool PrefValueExtensionModifiable(const std::string& name) const; + + // Update the command line PrefStore with |command_line_prefs|. + void UpdateCommandLinePrefStore(PrefStore* command_line_prefs); + + private: + // PrefStores must be listed here in order from highest to lowest priority. + // MANAGED contains all managed preference values that are provided by + // mandatory policies (e.g. Windows Group Policy or cloud policy). + // SUPERVISED_USER contains preferences that are valid for supervised users. + // EXTENSION contains preference values set by extensions. + // COMMAND_LINE contains preference values set by command-line switches. + // USER contains all user-set preference values. + // RECOMMENDED contains all preferences that are provided by recommended + // policies. + // DEFAULT contains all application default preference values. + enum PrefStoreType { + // INVALID_STORE is not associated with an actual PrefStore but used as + // an invalid marker, e.g. as a return value. + INVALID_STORE = -1, + MANAGED_STORE = 0, + SUPERVISED_USER_STORE, + EXTENSION_STORE, + COMMAND_LINE_STORE, + USER_STORE, + RECOMMENDED_STORE, + DEFAULT_STORE, + PREF_STORE_TYPE_MAX = DEFAULT_STORE + }; + + // Keeps a PrefStore reference on behalf of the PrefValueStore and monitors + // the PrefStore for changes, forwarding notifications to PrefValueStore. This + // indirection is here for the sake of disambiguating notifications from the + // individual PrefStores. + class PrefStoreKeeper : public PrefStore::Observer { + public: + PrefStoreKeeper(); + ~PrefStoreKeeper() override; + + // Takes ownership of |pref_store|. + void Initialize(PrefValueStore* store, + PrefStore* pref_store, + PrefStoreType type); + + PrefStore* store() { return pref_store_.get(); } + const PrefStore* store() const { return pref_store_.get(); } + + private: + // PrefStore::Observer implementation. + void OnPrefValueChanged(const std::string& key) override; + void OnInitializationCompleted(bool succeeded) override; + + // PrefValueStore this keeper is part of. + PrefValueStore* pref_value_store_; + + // The PrefStore managed by this keeper. + scoped_refptr<PrefStore> pref_store_; + + // Type of the pref store. + PrefStoreType type_; + + DISALLOW_COPY_AND_ASSIGN(PrefStoreKeeper); + }; + + typedef std::map<std::string, base::Value::Type> PrefTypeMap; + + // Returns true if the preference with the given name has a value in the + // given PrefStoreType, of the same value type as the preference was + // registered with. + bool PrefValueInStore(const std::string& name, PrefStoreType store) const; + + // Returns true if a preference has an explicit value in any of the + // stores in the range specified by |first_checked_store| and + // |last_checked_store|, even if that value is currently being + // overridden by a higher-priority store. + bool PrefValueInStoreRange(const std::string& name, + PrefStoreType first_checked_store, + PrefStoreType last_checked_store) const; + + // Returns the pref store type identifying the source that controls the + // Preference identified by |name|. If none of the sources has a value, + // INVALID_STORE is returned. In practice, the default PrefStore + // should always have a value for any registered preferencem, so INVALID_STORE + // indicates an error. + PrefStoreType ControllingPrefStoreForPref(const std::string& name) const; + + // Get a value from the specified |store|. + bool GetValueFromStore(const std::string& name, + PrefStoreType store, + const base::Value** out_value) const; + + // Get a value from the specified |store| if its |type| matches. + bool GetValueFromStoreWithType(const std::string& name, + base::Value::Type type, + PrefStoreType store, + const base::Value** out_value) const; + + // Called upon changes in individual pref stores in order to determine whether + // the user-visible pref value has changed. Triggers the change notification + // if the effective value of the preference has changed, or if the store + // controlling the pref has changed. + void NotifyPrefChanged(const std::string& path, PrefStoreType new_store); + + // Called from the PrefStoreKeeper implementation when a pref value for |key| + // changed in the pref store for |type|. + void OnPrefValueChanged(PrefStoreType type, const std::string& key); + + // Handle the event that the store for |type| has completed initialization. + void OnInitializationCompleted(PrefStoreType type, bool succeeded); + + // Initializes a pref store keeper. Sets up a PrefStoreKeeper that will take + // ownership of the passed |pref_store|. + void InitPrefStore(PrefStoreType type, PrefStore* pref_store); + + // Checks whether initialization is completed and tells the notifier if that + // is the case. + void CheckInitializationCompleted(); + + // Get the PrefStore pointer for the given type. May return NULL if there is + // no PrefStore for that type. + PrefStore* GetPrefStore(PrefStoreType type) { + return pref_stores_[type].store(); + } + const PrefStore* GetPrefStore(PrefStoreType type) const { + return pref_stores_[type].store(); + } + + // Keeps the PrefStore references in order of precedence. + PrefStoreKeeper pref_stores_[PREF_STORE_TYPE_MAX + 1]; + + PrefChangedCallback pref_changed_callback_; + + // Used for generating notifications. This is a weak reference, + // since the notifier is owned by the corresponding PrefService. + PrefNotifier* pref_notifier_; + + // A mapping of preference names to their registered types. + PrefTypeMap pref_types_; + + // True if not all of the PrefStores were initialized successfully. + bool initialization_failed_; + + DISALLOW_COPY_AND_ASSIGN(PrefValueStore); +}; + +#endif // COMPONENTS_PREFS_PREF_VALUE_STORE_H_ diff --git a/chromium/components/prefs/pref_value_store_unittest.cc b/chromium/components/prefs/pref_value_store_unittest.cc new file mode 100644 index 00000000000..90ee7d689a8 --- /dev/null +++ b/chromium/components/prefs/pref_value_store_unittest.cc @@ -0,0 +1,670 @@ +// 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 <string> + +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/pref_value_store.h" +#include "components/prefs/testing_pref_store.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::Mock; +using testing::_; + +namespace { + +// Allows to capture pref notifications through gmock. +class MockPrefNotifier : public PrefNotifier { + public: + MOCK_METHOD1(OnPreferenceChanged, void(const std::string&)); + MOCK_METHOD1(OnInitializationCompleted, void(bool)); +}; + +// Allows to capture sync model associator interaction. +class MockPrefModelAssociator { + public: + MOCK_METHOD1(ProcessPrefChange, void(const std::string&)); +}; + +} // namespace + +// Names of the preferences used in this test. +namespace prefs { +const char kManagedPref[] = "this.pref.managed"; +const char kSupervisedUserPref[] = "this.pref.supervised_user"; +const char kCommandLinePref[] = "this.pref.command_line"; +const char kExtensionPref[] = "this.pref.extension"; +const char kUserPref[] = "this.pref.user"; +const char kRecommendedPref[] = "this.pref.recommended"; +const char kDefaultPref[] = "this.pref.default"; +const char kMissingPref[] = "this.pref.does_not_exist"; +} + +// Potentially expected values of all preferences used in this test program. +namespace managed_pref { +const char kManagedValue[] = "managed:managed"; +} + +namespace supervised_user_pref { +const char kManagedValue[] = "supervised_user:managed"; +const char kSupervisedUserValue[] = "supervised_user:supervised_user"; +} + +namespace extension_pref { +const char kManagedValue[] = "extension:managed"; +const char kSupervisedUserValue[] = "extension:supervised_user"; +const char kExtensionValue[] = "extension:extension"; +} + +namespace command_line_pref { +const char kManagedValue[] = "command_line:managed"; +const char kSupervisedUserValue[] = "command_line:supervised_user"; +const char kExtensionValue[] = "command_line:extension"; +const char kCommandLineValue[] = "command_line:command_line"; +} + +namespace user_pref { +const char kManagedValue[] = "user:managed"; +const char kSupervisedUserValue[] = "supervised_user:supervised_user"; +const char kExtensionValue[] = "user:extension"; +const char kCommandLineValue[] = "user:command_line"; +const char kUserValue[] = "user:user"; +} + +namespace recommended_pref { +const char kManagedValue[] = "recommended:managed"; +const char kSupervisedUserValue[] = "recommended:supervised_user"; +const char kExtensionValue[] = "recommended:extension"; +const char kCommandLineValue[] = "recommended:command_line"; +const char kUserValue[] = "recommended:user"; +const char kRecommendedValue[] = "recommended:recommended"; +} + +namespace default_pref { +const char kManagedValue[] = "default:managed"; +const char kSupervisedUserValue[] = "default:supervised_user"; +const char kExtensionValue[] = "default:extension"; +const char kCommandLineValue[] = "default:command_line"; +const char kUserValue[] = "default:user"; +const char kRecommendedValue[] = "default:recommended"; +const char kDefaultValue[] = "default:default"; +} + +class PrefValueStoreTest : public testing::Test { + protected: + void SetUp() override { + // Create TestingPrefStores. + CreateManagedPrefs(); + CreateSupervisedUserPrefs(); + CreateExtensionPrefs(); + CreateCommandLinePrefs(); + CreateUserPrefs(); + CreateRecommendedPrefs(); + CreateDefaultPrefs(); + sync_associator_.reset(new MockPrefModelAssociator()); + + // Create a fresh PrefValueStore. + pref_value_store_.reset( + new PrefValueStore(managed_pref_store_.get(), + supervised_user_pref_store_.get(), + extension_pref_store_.get(), + command_line_pref_store_.get(), + user_pref_store_.get(), + recommended_pref_store_.get(), + default_pref_store_.get(), + &pref_notifier_)); + + pref_value_store_->set_callback( + base::Bind(&MockPrefModelAssociator::ProcessPrefChange, + base::Unretained(sync_associator_.get()))); + } + + void CreateManagedPrefs() { + managed_pref_store_ = new TestingPrefStore; + managed_pref_store_->SetString( + prefs::kManagedPref, + managed_pref::kManagedValue); + } + + void CreateSupervisedUserPrefs() { + supervised_user_pref_store_ = new TestingPrefStore; + supervised_user_pref_store_->SetString( + prefs::kManagedPref, + supervised_user_pref::kManagedValue); + supervised_user_pref_store_->SetString( + prefs::kSupervisedUserPref, + supervised_user_pref::kSupervisedUserValue); + } + + void CreateExtensionPrefs() { + extension_pref_store_ = new TestingPrefStore; + extension_pref_store_->SetString( + prefs::kManagedPref, + extension_pref::kManagedValue); + extension_pref_store_->SetString( + prefs::kSupervisedUserPref, + extension_pref::kSupervisedUserValue); + extension_pref_store_->SetString( + prefs::kExtensionPref, + extension_pref::kExtensionValue); + } + + void CreateCommandLinePrefs() { + command_line_pref_store_ = new TestingPrefStore; + command_line_pref_store_->SetString( + prefs::kManagedPref, + command_line_pref::kManagedValue); + command_line_pref_store_->SetString( + prefs::kSupervisedUserPref, + command_line_pref::kSupervisedUserValue); + command_line_pref_store_->SetString( + prefs::kExtensionPref, + command_line_pref::kExtensionValue); + command_line_pref_store_->SetString( + prefs::kCommandLinePref, + command_line_pref::kCommandLineValue); + } + + void CreateUserPrefs() { + user_pref_store_ = new TestingPrefStore; + user_pref_store_->SetString( + prefs::kManagedPref, + user_pref::kManagedValue); + user_pref_store_->SetString( + prefs::kSupervisedUserPref, + user_pref::kSupervisedUserValue); + user_pref_store_->SetString( + prefs::kCommandLinePref, + user_pref::kCommandLineValue); + user_pref_store_->SetString( + prefs::kExtensionPref, + user_pref::kExtensionValue); + user_pref_store_->SetString( + prefs::kUserPref, + user_pref::kUserValue); + } + + void CreateRecommendedPrefs() { + recommended_pref_store_ = new TestingPrefStore; + recommended_pref_store_->SetString( + prefs::kManagedPref, + recommended_pref::kManagedValue); + recommended_pref_store_->SetString( + prefs::kSupervisedUserPref, + recommended_pref::kSupervisedUserValue); + recommended_pref_store_->SetString( + prefs::kCommandLinePref, + recommended_pref::kCommandLineValue); + recommended_pref_store_->SetString( + prefs::kExtensionPref, + recommended_pref::kExtensionValue); + recommended_pref_store_->SetString( + prefs::kUserPref, + recommended_pref::kUserValue); + recommended_pref_store_->SetString( + prefs::kRecommendedPref, + recommended_pref::kRecommendedValue); + } + + void CreateDefaultPrefs() { + default_pref_store_ = new TestingPrefStore; + default_pref_store_->SetString( + prefs::kSupervisedUserPref, + default_pref::kSupervisedUserValue); + default_pref_store_->SetString( + prefs::kManagedPref, + default_pref::kManagedValue); + default_pref_store_->SetString( + prefs::kCommandLinePref, + default_pref::kCommandLineValue); + default_pref_store_->SetString( + prefs::kExtensionPref, + default_pref::kExtensionValue); + default_pref_store_->SetString( + prefs::kUserPref, + default_pref::kUserValue); + default_pref_store_->SetString( + prefs::kRecommendedPref, + default_pref::kRecommendedValue); + default_pref_store_->SetString( + prefs::kDefaultPref, + default_pref::kDefaultValue); + } + + void ExpectValueChangeNotifications(const std::string& name) { + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(name)); + EXPECT_CALL(*sync_associator_, ProcessPrefChange(name)); + } + + void CheckAndClearValueChangeNotifications() { + Mock::VerifyAndClearExpectations(&pref_notifier_); + Mock::VerifyAndClearExpectations(sync_associator_.get()); + } + + MockPrefNotifier pref_notifier_; + scoped_ptr<MockPrefModelAssociator> sync_associator_; + scoped_ptr<PrefValueStore> pref_value_store_; + + scoped_refptr<TestingPrefStore> managed_pref_store_; + scoped_refptr<TestingPrefStore> supervised_user_pref_store_; + scoped_refptr<TestingPrefStore> extension_pref_store_; + scoped_refptr<TestingPrefStore> command_line_pref_store_; + scoped_refptr<TestingPrefStore> user_pref_store_; + scoped_refptr<TestingPrefStore> recommended_pref_store_; + scoped_refptr<TestingPrefStore> default_pref_store_; +}; + +TEST_F(PrefValueStoreTest, GetValue) { + const base::Value* value; + + // The following tests read a value from the PrefService. The preferences are + // set in a way such that all lower-priority stores have a value and we can + // test whether overrides work correctly. + + // Test getting a managed value. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kManagedPref, + base::Value::TYPE_STRING, &value)); + std::string actual_str_value; + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(managed_pref::kManagedValue, actual_str_value); + + // Test getting a supervised user value. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kSupervisedUserPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(supervised_user_pref::kSupervisedUserValue, actual_str_value); + + // Test getting an extension value. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kExtensionPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(extension_pref::kExtensionValue, actual_str_value); + + // Test getting a command-line value. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kCommandLinePref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(command_line_pref::kCommandLineValue, actual_str_value); + + // Test getting a user-set value. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kUserPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(user_pref::kUserValue, actual_str_value); + + // Test getting a user set value overwriting a recommended value. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kRecommendedPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kRecommendedValue, + actual_str_value); + + // Test getting a default value. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetValue(prefs::kDefaultPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(default_pref::kDefaultValue, actual_str_value); + + // Test getting a preference value that the |PrefValueStore| + // does not contain. + base::FundamentalValue tmp_dummy_value(true); + value = &tmp_dummy_value; + ASSERT_FALSE(pref_value_store_->GetValue(prefs::kMissingPref, + base::Value::TYPE_STRING, &value)); + ASSERT_FALSE(value); +} + +TEST_F(PrefValueStoreTest, GetRecommendedValue) { + const base::Value* value; + + // The following tests read a value from the PrefService. The preferences are + // set in a way such that all lower-priority stores have a value and we can + // test whether overrides do not clutter the recommended value. + + // Test getting recommended value when a managed value is present. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kManagedPref, + base::Value::TYPE_STRING, &value)); + std::string actual_str_value; + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kManagedValue, actual_str_value); + + // Test getting recommended value when a supervised user value is present. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kSupervisedUserPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kSupervisedUserValue, actual_str_value); + + // Test getting recommended value when an extension value is present. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kExtensionPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kExtensionValue, actual_str_value); + + // Test getting recommended value when a command-line value is present. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kCommandLinePref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kCommandLineValue, actual_str_value); + + // Test getting recommended value when a user-set value is present. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kUserPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kUserValue, actual_str_value); + + // Test getting recommended value when no higher-priority value is present. + value = NULL; + ASSERT_TRUE(pref_value_store_->GetRecommendedValue( + prefs::kRecommendedPref, + base::Value::TYPE_STRING, &value)); + EXPECT_TRUE(value->GetAsString(&actual_str_value)); + EXPECT_EQ(recommended_pref::kRecommendedValue, + actual_str_value); + + // Test getting recommended value when no recommended value is present. + base::FundamentalValue tmp_dummy_value(true); + value = &tmp_dummy_value; + ASSERT_FALSE(pref_value_store_->GetRecommendedValue( + prefs::kDefaultPref, + base::Value::TYPE_STRING, &value)); + ASSERT_FALSE(value); + + // Test getting a preference value that the |PrefValueStore| + // does not contain. + value = &tmp_dummy_value; + ASSERT_FALSE(pref_value_store_->GetRecommendedValue( + prefs::kMissingPref, + base::Value::TYPE_STRING, &value)); + ASSERT_FALSE(value); +} + +TEST_F(PrefValueStoreTest, PrefChanges) { + // Check pref controlled by highest-priority store. + ExpectValueChangeNotifications(prefs::kManagedPref); + managed_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + supervised_user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + extension_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + command_line_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + recommended_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kManagedPref); + default_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref); + CheckAndClearValueChangeNotifications(); + + // Check pref controlled by user store. + ExpectValueChangeNotifications(prefs::kUserPref); + managed_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + extension_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + command_line_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + user_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + recommended_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kUserPref); + default_pref_store_->NotifyPrefValueChanged(prefs::kUserPref); + CheckAndClearValueChangeNotifications(); + + // Check pref controlled by default-pref store. + ExpectValueChangeNotifications(prefs::kDefaultPref); + managed_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + extension_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + command_line_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + user_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + recommended_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); + + ExpectValueChangeNotifications(prefs::kDefaultPref); + default_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref); + CheckAndClearValueChangeNotifications(); +} + +TEST_F(PrefValueStoreTest, OnInitializationCompleted) { + EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(0); + managed_pref_store_->SetInitializationCompleted(); + supervised_user_pref_store_->SetInitializationCompleted(); + extension_pref_store_->SetInitializationCompleted(); + command_line_pref_store_->SetInitializationCompleted(); + recommended_pref_store_->SetInitializationCompleted(); + default_pref_store_->SetInitializationCompleted(); + Mock::VerifyAndClearExpectations(&pref_notifier_); + + // The notification should only be triggered after the last store is done. + EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(1); + user_pref_store_->SetInitializationCompleted(); + Mock::VerifyAndClearExpectations(&pref_notifier_); +} + +TEST_F(PrefValueStoreTest, PrefValueInManagedStore) { + EXPECT_TRUE(pref_value_store_->PrefValueInManagedStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueInExtensionStore) { + EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore( + prefs::kManagedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueInUserStore) { + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kManagedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kExtensionPref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueInUserStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInUserStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInUserStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueInUserStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromExtensionStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromUserStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromUserStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromRecommendedStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kRecommendedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueFromDefaultStore) { + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kCommandLinePref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kRecommendedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kDefaultPref)); + EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueUserModifiable) { + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kSupervisedUserPref)); + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kExtensionPref)); + EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kRecommendedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kDefaultPref)); + EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable( + prefs::kMissingPref)); +} + +TEST_F(PrefValueStoreTest, PrefValueExtensionModifiable) { + EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kManagedPref)); + EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kSupervisedUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kExtensionPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kCommandLinePref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kUserPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kRecommendedPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kDefaultPref)); + EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable( + prefs::kMissingPref)); +} diff --git a/chromium/components/prefs/prefs.gyp b/chromium/components/prefs/prefs.gyp new file mode 100644 index 00000000000..554bef4f7ff --- /dev/null +++ b/chromium/components/prefs/prefs.gyp @@ -0,0 +1,87 @@ +# Copyright (c) 2013 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. + +{ + 'targets': [ + { + 'target_name': 'prefs', + 'type': '<(component)', + 'dependencies': [ + '../../base/base.gyp:base', + ], + 'include_dirs': [ + '../..', + ], + 'defines': [ + 'COMPONENTS_PREFS_IMPLEMENTATION', + ], + 'sources': [ + 'default_pref_store.cc', + 'default_pref_store.h', + 'json_pref_store.cc', + 'json_pref_store.h', + 'overlay_user_pref_store.cc', + 'overlay_user_pref_store.h', + 'pref_change_registrar.cc', + 'pref_change_registrar.h', + 'pref_member.cc', + 'pref_member.h', + 'pref_notifier_impl.cc', + 'pref_notifier_impl.h', + 'pref_registry.cc', + 'pref_registry.h', + 'pref_registry_simple.cc', + 'pref_registry_simple.h', + 'pref_service.cc', + 'pref_service.h', + 'pref_service_factory.cc', + 'pref_service_factory.h', + 'pref_store.cc', + 'pref_store.h', + 'pref_value_map.cc', + 'pref_value_map.h', + 'pref_value_store.cc', + 'pref_value_store.h', + 'scoped_user_pref_update.cc', + 'scoped_user_pref_update.h', + 'value_map_pref_store.cc', + 'value_map_pref_store.h', + ], + 'conditions': [ + ['OS!="ios"', { + 'sources': [ + 'base_prefs_export.h', + 'persistent_pref_store.h', + 'pref_filter.h', + 'pref_notifier.h', + 'pref_observer.h', + 'writeable_pref_store.h', + ] + }] + ], + }, + { + 'target_name': 'prefs_test_support', + 'type': 'static_library', + 'include_dirs': [ + '../..', + ], + 'dependencies': [ + 'prefs', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(DEPTH)/testing/gtest.gyp:gtest', + ], + 'sources': [ + 'mock_pref_change_callback.cc', + 'mock_pref_change_callback.h', + 'pref_store_observer_mock.cc', + 'pref_store_observer_mock.h', + 'testing_pref_service.cc', + 'testing_pref_service.h', + 'testing_pref_store.cc', + 'testing_pref_store.h', + ], + }, + ], +} diff --git a/chromium/components/prefs/scoped_user_pref_update.cc b/chromium/components/prefs/scoped_user_pref_update.cc new file mode 100644 index 00000000000..dfba3349a2d --- /dev/null +++ b/chromium/components/prefs/scoped_user_pref_update.cc @@ -0,0 +1,37 @@ +// Copyright 2013 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 "components/prefs/scoped_user_pref_update.h" + +#include "base/logging.h" +#include "components/prefs/pref_notifier.h" +#include "components/prefs/pref_service.h" + +namespace subtle { + +ScopedUserPrefUpdateBase::ScopedUserPrefUpdateBase(PrefService* service, + const std::string& path) + : service_(service), path_(path), value_(NULL) { + DCHECK(service_->CalledOnValidThread()); +} + +ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() { + Notify(); +} + +base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) { + DCHECK(CalledOnValidThread()); + if (!value_) + value_ = service_->GetMutableUserPref(path_, type); + return value_; +} + +void ScopedUserPrefUpdateBase::Notify() { + if (value_) { + service_->ReportUserPrefChanged(path_); + value_ = NULL; + } +} + +} // namespace subtle diff --git a/chromium/components/prefs/scoped_user_pref_update.h b/chromium/components/prefs/scoped_user_pref_update.h new file mode 100644 index 00000000000..bfcdb3f5574 --- /dev/null +++ b/chromium/components/prefs/scoped_user_pref_update.h @@ -0,0 +1,109 @@ +// Copyright 2013 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. +// +// A helper class that assists preferences in firing notifications when lists +// or dictionaries are changed. + +#ifndef COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_ +#define COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_ + +#include <string> + +#include "base/macros.h" +#include "base/threading/non_thread_safe.h" +#include "base/values.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_service.h" + +class PrefService; + +namespace base { +class DictionaryValue; +class ListValue; +} + +namespace subtle { + +// Base class for ScopedUserPrefUpdateTemplate that contains the parts +// that do not depend on ScopedUserPrefUpdateTemplate's template parameter. +// +// We need this base class mostly for making it a friend of PrefService +// and getting access to PrefService::GetMutableUserPref and +// PrefService::ReportUserPrefChanged. +class COMPONENTS_PREFS_EXPORT ScopedUserPrefUpdateBase + : public base::NonThreadSafe { + protected: + ScopedUserPrefUpdateBase(PrefService* service, const std::string& path); + + // Calls Notify(). + ~ScopedUserPrefUpdateBase(); + + // Sets |value_| to |service_|->GetMutableUserPref and returns it. + base::Value* GetValueOfType(base::Value::Type type); + + private: + // If |value_| is not null, triggers a notification of PrefObservers and + // resets |value_|. + void Notify(); + + // Weak pointer. + PrefService* service_; + // Path of the preference being updated. + std::string path_; + // Cache of value from user pref store (set between Get() and Notify() calls). + base::Value* value_; + + DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdateBase); +}; + +} // namespace subtle + +// Class to support modifications to DictionaryValues and ListValues while +// guaranteeing that PrefObservers are notified of changed values. +// +// This class may only be used on the UI thread as it requires access to the +// PrefService. +template <typename T, base::Value::Type type_enum_value> +class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase { + public: + ScopedUserPrefUpdate(PrefService* service, const std::string& path) + : ScopedUserPrefUpdateBase(service, path) {} + + // Triggers an update notification if Get() was called. + virtual ~ScopedUserPrefUpdate() {} + + // Returns a mutable |T| instance that + // - is already in the user pref store, or + // - is (silently) created and written to the user pref store if none existed + // before. + // + // Calling Get() implies that an update notification is necessary at + // destruction time. + // + // The ownership of the return value remains with the user pref store. + // Virtual so it can be overriden in subclasses that transform the value + // before returning it (for example to return a subelement of a dictionary). + virtual T* Get() { + return static_cast<T*>(GetValueOfType(type_enum_value)); + } + + T& operator*() { + return *Get(); + } + + T* operator->() { + return Get(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdate); +}; + +typedef ScopedUserPrefUpdate<base::DictionaryValue, + base::Value::TYPE_DICTIONARY> + DictionaryPrefUpdate; +typedef ScopedUserPrefUpdate<base::ListValue, base::Value::TYPE_LIST> + ListPrefUpdate; + +#endif // COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_ diff --git a/chromium/components/prefs/scoped_user_pref_update_unittest.cc b/chromium/components/prefs/scoped_user_pref_update_unittest.cc new file mode 100644 index 00000000000..437dff6fe9f --- /dev/null +++ b/chromium/components/prefs/scoped_user_pref_update_unittest.cc @@ -0,0 +1,81 @@ +// Copyright 2013 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 "components/prefs/mock_pref_change_callback.h" +#include "components/prefs/pref_change_registrar.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Mock; + +class ScopedUserPrefUpdateTest : public testing::Test { + public: + ScopedUserPrefUpdateTest() : observer_(&prefs_) {} + ~ScopedUserPrefUpdateTest() override {} + + protected: + void SetUp() override { + prefs_.registry()->RegisterDictionaryPref(kPref); + registrar_.Init(&prefs_); + registrar_.Add(kPref, observer_.GetCallback()); + } + + static const char kPref[]; + static const char kKey[]; + static const char kValue[]; + + TestingPrefServiceSimple prefs_; + MockPrefChangeCallback observer_; + PrefChangeRegistrar registrar_; +}; + +const char ScopedUserPrefUpdateTest::kPref[] = "name"; +const char ScopedUserPrefUpdateTest::kKey[] = "key"; +const char ScopedUserPrefUpdateTest::kValue[] = "value"; + +TEST_F(ScopedUserPrefUpdateTest, RegularUse) { + // Dictionary that will be expected to be set at the end. + base::DictionaryValue expected_dictionary; + expected_dictionary.SetString(kKey, kValue); + + { + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + DictionaryPrefUpdate update(&prefs_, kPref); + base::DictionaryValue* value = update.Get(); + ASSERT_TRUE(value); + value->SetString(kKey, kValue); + + // The dictionary was created for us but the creation should have happened + // silently without notifications. + Mock::VerifyAndClearExpectations(&observer_); + + // Modifications happen online and are instantly visible, though. + const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref); + ASSERT_TRUE(current_value); + EXPECT_TRUE(expected_dictionary.Equals(current_value)); + + // Now we are leaving the scope of the update so we should be notified. + observer_.Expect(kPref, &expected_dictionary); + } + Mock::VerifyAndClearExpectations(&observer_); + + const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref); + ASSERT_TRUE(current_value); + EXPECT_TRUE(expected_dictionary.Equals(current_value)); +} + +TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) { + const base::DictionaryValue* old_value = prefs_.GetDictionary(kPref); + EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0); + { + DictionaryPrefUpdate update(&prefs_, kPref); + } + const base::DictionaryValue* new_value = prefs_.GetDictionary(kPref); + EXPECT_EQ(old_value, new_value); + Mock::VerifyAndClearExpectations(&observer_); +} diff --git a/chromium/components/prefs/testing_pref_service.cc b/chromium/components/prefs/testing_pref_service.cc new file mode 100644 index 00000000000..1dd760e72d6 --- /dev/null +++ b/chromium/components/prefs/testing_pref_service.cc @@ -0,0 +1,54 @@ +// 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 "components/prefs/testing_pref_service.h" + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "components/prefs/default_pref_store.h" +#include "components/prefs/pref_notifier_impl.h" +#include "components/prefs/pref_registry_simple.h" +#include "components/prefs/pref_value_store.h" +#include "testing/gtest/include/gtest/gtest.h" + +template <> +TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase( + TestingPrefStore* managed_prefs, + TestingPrefStore* user_prefs, + TestingPrefStore* recommended_prefs, + PrefRegistry* pref_registry, + PrefNotifierImpl* pref_notifier) + : PrefService( + pref_notifier, + new PrefValueStore(managed_prefs, + NULL, + NULL, + NULL, + user_prefs, + recommended_prefs, + pref_registry->defaults().get(), + pref_notifier), + user_prefs, + pref_registry, + base::Bind(&TestingPrefServiceBase<PrefService, + PrefRegistry>::HandleReadError), + false), + managed_prefs_(managed_prefs), + user_prefs_(user_prefs), + recommended_prefs_(recommended_prefs) {} + +TestingPrefServiceSimple::TestingPrefServiceSimple() + : TestingPrefServiceBase<PrefService, PrefRegistry>( + new TestingPrefStore(), + new TestingPrefStore(), + new TestingPrefStore(), + new PrefRegistrySimple(), + new PrefNotifierImpl()) {} + +TestingPrefServiceSimple::~TestingPrefServiceSimple() { +} + +PrefRegistrySimple* TestingPrefServiceSimple::registry() { + return static_cast<PrefRegistrySimple*>(DeprecatedGetPrefRegistry()); +} diff --git a/chromium/components/prefs/testing_pref_service.h b/chromium/components/prefs/testing_pref_service.h new file mode 100644 index 00000000000..756858907f1 --- /dev/null +++ b/chromium/components/prefs/testing_pref_service.h @@ -0,0 +1,196 @@ +// 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 COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_ +#define COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_ + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "components/prefs/pref_registry.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/testing_pref_store.h" + +class PrefNotifierImpl; +class PrefRegistrySimple; +class TestingPrefStore; + +// A PrefService subclass for testing. It operates totally in memory and +// provides additional API for manipulating preferences at the different levels +// (managed, extension, user) conveniently. +// +// Use this via its specializations, e.g. TestingPrefServiceSimple. +template <class SuperPrefService, class ConstructionPrefRegistry> +class TestingPrefServiceBase : public SuperPrefService { + public: + virtual ~TestingPrefServiceBase(); + + // Read the value of a preference from the managed layer. Returns NULL if the + // preference is not defined at the managed layer. + const base::Value* GetManagedPref(const std::string& path) const; + + // Set a preference on the managed layer and fire observers if the preference + // changed. Assumes ownership of |value|. + void SetManagedPref(const std::string& path, base::Value* value); + + // Clear the preference on the managed layer and fire observers if the + // preference has been defined previously. + void RemoveManagedPref(const std::string& path); + + // Similar to the above, but for user preferences. + const base::Value* GetUserPref(const std::string& path) const; + void SetUserPref(const std::string& path, base::Value* value); + void RemoveUserPref(const std::string& path); + + // Similar to the above, but for recommended policy preferences. + const base::Value* GetRecommendedPref(const std::string& path) const; + void SetRecommendedPref(const std::string& path, base::Value* value); + void RemoveRecommendedPref(const std::string& path); + + // Do-nothing implementation for TestingPrefService. + static void HandleReadError(PersistentPrefStore::PrefReadError error) {} + + protected: + TestingPrefServiceBase( + TestingPrefStore* managed_prefs, + TestingPrefStore* user_prefs, + TestingPrefStore* recommended_prefs, + ConstructionPrefRegistry* pref_registry, + PrefNotifierImpl* pref_notifier); + + private: + // Reads the value of the preference indicated by |path| from |pref_store|. + // Returns NULL if the preference was not found. + const base::Value* GetPref(TestingPrefStore* pref_store, + const std::string& path) const; + + // Sets the value for |path| in |pref_store|. + void SetPref(TestingPrefStore* pref_store, + const std::string& path, + base::Value* value); + + // Removes the preference identified by |path| from |pref_store|. + void RemovePref(TestingPrefStore* pref_store, const std::string& path); + + // Pointers to the pref stores our value store uses. + scoped_refptr<TestingPrefStore> managed_prefs_; + scoped_refptr<TestingPrefStore> user_prefs_; + scoped_refptr<TestingPrefStore> recommended_prefs_; + + DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceBase); +}; + +// Test version of PrefService. +class TestingPrefServiceSimple + : public TestingPrefServiceBase<PrefService, PrefRegistry> { + public: + TestingPrefServiceSimple(); + ~TestingPrefServiceSimple() override; + + // This is provided as a convenience for registering preferences on + // an existing TestingPrefServiceSimple instance. On a production + // PrefService you would do all registrations before constructing + // it, passing it a PrefRegistry via its constructor (or via + // e.g. PrefServiceFactory). + PrefRegistrySimple* registry(); + + private: + DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceSimple); +}; + +template<> +TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase( + TestingPrefStore* managed_prefs, + TestingPrefStore* user_prefs, + TestingPrefStore* recommended_prefs, + PrefRegistry* pref_registry, + PrefNotifierImpl* pref_notifier); + +template<class SuperPrefService, class ConstructionPrefRegistry> +TestingPrefServiceBase< + SuperPrefService, ConstructionPrefRegistry>::~TestingPrefServiceBase() { +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* TestingPrefServiceBase< + SuperPrefService, + ConstructionPrefRegistry>::GetManagedPref(const std::string& path) const { + return GetPref(managed_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetManagedPref(const std::string& path, base::Value* value) { + SetPref(managed_prefs_.get(), path, value); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveManagedPref(const std::string& path) { + RemovePref(managed_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetUserPref( + const std::string& path) const { + return GetPref(user_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetUserPref(const std::string& path, base::Value* value) { + SetPref(user_prefs_.get(), path, value); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveUserPref(const std::string& path) { + RemovePref(user_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + GetRecommendedPref(const std::string& path) const { + return GetPref(recommended_prefs_, path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetRecommendedPref(const std::string& path, base::Value* value) { + SetPref(recommended_prefs_.get(), path, value); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemoveRecommendedPref(const std::string& path) { + RemovePref(recommended_prefs_.get(), path); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +const base::Value* +TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetPref( + TestingPrefStore* pref_store, + const std::string& path) const { + const base::Value* res; + return pref_store->GetValue(path, &res) ? res : NULL; +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + SetPref(TestingPrefStore* pref_store, + const std::string& path, + base::Value* value) { + pref_store->SetValue(path, make_scoped_ptr(value), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); +} + +template <class SuperPrefService, class ConstructionPrefRegistry> +void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>:: + RemovePref(TestingPrefStore* pref_store, const std::string& path) { + pref_store->RemoveValue(path, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); +} + +#endif // COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_ diff --git a/chromium/components/prefs/testing_pref_store.cc b/chromium/components/prefs/testing_pref_store.cc new file mode 100644 index 00000000000..3a8be42c9d8 --- /dev/null +++ b/chromium/components/prefs/testing_pref_store.cc @@ -0,0 +1,185 @@ +// 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 "components/prefs/testing_pref_store.h" + +#include <utility> + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" + +TestingPrefStore::TestingPrefStore() + : read_only_(true), + read_success_(true), + read_error_(PersistentPrefStore::PREF_READ_ERROR_NONE), + block_async_read_(false), + pending_async_read_(false), + init_complete_(false), + committed_(true) {} + +bool TestingPrefStore::GetValue(const std::string& key, + const base::Value** value) const { + return prefs_.GetValue(key, value); +} + +bool TestingPrefStore::GetMutableValue(const std::string& key, + base::Value** value) { + return prefs_.GetValue(key, value); +} + +void TestingPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void TestingPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool TestingPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +bool TestingPrefStore::IsInitializationComplete() const { + return init_complete_; +} + +void TestingPrefStore::SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + if (prefs_.SetValue(key, std::move(value))) { + committed_ = false; + NotifyPrefValueChanged(key); + } +} + +void TestingPrefStore::SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + if (prefs_.SetValue(key, std::move(value))) + committed_ = false; +} + +void TestingPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + if (prefs_.RemoveValue(key)) { + committed_ = false; + NotifyPrefValueChanged(key); + } +} + +bool TestingPrefStore::ReadOnly() const { + return read_only_; +} + +PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const { + return read_error_; +} + +PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() { + NotifyInitializationCompleted(); + return read_error_; +} + +void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) { + DCHECK(!pending_async_read_); + error_delegate_.reset(error_delegate); + if (block_async_read_) + pending_async_read_ = true; + else + NotifyInitializationCompleted(); +} + +void TestingPrefStore::CommitPendingWrite() { committed_ = true; } + +void TestingPrefStore::SchedulePendingLossyWrites() {} + +void TestingPrefStore::SetInitializationCompleted() { + NotifyInitializationCompleted(); +} + +void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) { + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void TestingPrefStore::NotifyInitializationCompleted() { + DCHECK(!init_complete_); + init_complete_ = true; + if (read_success_ && read_error_ != PREF_READ_ERROR_NONE && error_delegate_) + error_delegate_->OnError(read_error_); + FOR_EACH_OBSERVER( + Observer, observers_, OnInitializationCompleted(read_success_)); +} + +void TestingPrefStore::ReportValueChanged(const std::string& key, + uint32_t flags) { + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void TestingPrefStore::SetString(const std::string& key, + const std::string& value) { + SetValue(key, make_scoped_ptr(new base::StringValue(value)), + DEFAULT_PREF_WRITE_FLAGS); +} + +void TestingPrefStore::SetInteger(const std::string& key, int value) { + SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)), + DEFAULT_PREF_WRITE_FLAGS); +} + +void TestingPrefStore::SetBoolean(const std::string& key, bool value) { + SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)), + DEFAULT_PREF_WRITE_FLAGS); +} + +bool TestingPrefStore::GetString(const std::string& key, + std::string* value) const { + const base::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 base::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 base::Value* stored_value; + if (!prefs_.GetValue(key, &stored_value) || !stored_value) + return false; + + return stored_value->GetAsBoolean(value); +} + +void TestingPrefStore::SetBlockAsyncRead(bool block_async_read) { + DCHECK(!init_complete_); + block_async_read_ = block_async_read; + if (pending_async_read_ && !block_async_read_) + NotifyInitializationCompleted(); +} + +void TestingPrefStore::ClearMutableValues() { + NOTIMPLEMENTED(); +} + +void TestingPrefStore::set_read_only(bool read_only) { + read_only_ = read_only; +} + +void TestingPrefStore::set_read_success(bool read_success) { + DCHECK(!init_complete_); + read_success_ = read_success; +} + +void TestingPrefStore::set_read_error( + PersistentPrefStore::PrefReadError read_error) { + DCHECK(!init_complete_); + read_error_ = read_error; +} + +TestingPrefStore::~TestingPrefStore() {} diff --git a/chromium/components/prefs/testing_pref_store.h b/chromium/components/prefs/testing_pref_store.h new file mode 100644 index 00000000000..8fb4b8d2c24 --- /dev/null +++ b/chromium/components/prefs/testing_pref_store.h @@ -0,0 +1,116 @@ +// 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 COMPONENTS_PREFS_TESTING_PREF_STORE_H_ +#define COMPONENTS_PREFS_TESTING_PREF_STORE_H_ + +#include <stdint.h> + +#include <string> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "components/prefs/persistent_pref_store.h" +#include "components/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. + bool GetValue(const std::string& key, + const base::Value** result) const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + bool IsInitializationComplete() const override; + + // PersistentPrefStore overrides: + bool GetMutableValue(const std::string& key, base::Value** result) override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + void SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + void SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool ReadOnly() const override; + PrefReadError GetReadError() const override; + PersistentPrefStore::PrefReadError ReadPrefs() override; + void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override; + void CommitPendingWrite() override; + void SchedulePendingLossyWrites() 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; + + // Determines whether ReadPrefsAsync completes immediately. Defaults to false + // (non-blocking). To block, invoke this with true (blocking) before the call + // to ReadPrefsAsync. To unblock, invoke again with false (non-blocking) after + // the call to ReadPrefsAsync. + void SetBlockAsyncRead(bool block_async_read); + + void ClearMutableValues() override; + + // Getter and Setter methods for setting and getting the state of the + // |TestingPrefStore|. + virtual void set_read_only(bool read_only); + void set_read_success(bool read_success); + void set_read_error(PersistentPrefStore::PrefReadError read_error); + bool committed() { return committed_; } + + protected: + ~TestingPrefStore() override; + + private: + // Stores the preference values. + PrefValueMap prefs_; + + // Flag that indicates if the PrefStore is read-only + bool read_only_; + + // The result to pass to PrefStore::Observer::OnInitializationCompleted + bool read_success_; + + // The result to return from ReadPrefs or ReadPrefsAsync. + PersistentPrefStore::PrefReadError read_error_; + + // Whether a call to ReadPrefsAsync should block. + bool block_async_read_; + + // Whether there is a pending call to ReadPrefsAsync. + bool pending_async_read_; + + // Whether initialization has been completed. + bool init_complete_; + + // Whether the store contents have been committed to disk since the last + // mutation. + bool committed_; + + scoped_ptr<ReadErrorDelegate> error_delegate_; + base::ObserverList<PrefStore::Observer, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(TestingPrefStore); +}; + +#endif // COMPONENTS_PREFS_TESTING_PREF_STORE_H_ diff --git a/chromium/components/prefs/value_map_pref_store.cc b/chromium/components/prefs/value_map_pref_store.cc new file mode 100644 index 00000000000..fe2c80893db --- /dev/null +++ b/chromium/components/prefs/value_map_pref_store.cc @@ -0,0 +1,64 @@ +// 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 "components/prefs/value_map_pref_store.h" + +#include <algorithm> +#include <utility> + +#include "base/stl_util.h" +#include "base/values.h" + +ValueMapPrefStore::ValueMapPrefStore() {} + +bool ValueMapPrefStore::GetValue(const std::string& key, + const base::Value** value) const { + return prefs_.GetValue(key, value); +} + +void ValueMapPrefStore::AddObserver(PrefStore::Observer* observer) { + observers_.AddObserver(observer); +} + +void ValueMapPrefStore::RemoveObserver(PrefStore::Observer* observer) { + observers_.RemoveObserver(observer); +} + +bool ValueMapPrefStore::HasObservers() const { + return observers_.might_have_observers(); +} + +void ValueMapPrefStore::SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + if (prefs_.SetValue(key, std::move(value))) + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void ValueMapPrefStore::RemoveValue(const std::string& key, uint32_t flags) { + if (prefs_.RemoveValue(key)) + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +bool ValueMapPrefStore::GetMutableValue(const std::string& key, + base::Value** value) { + return prefs_.GetValue(key, value); +} + +void ValueMapPrefStore::ReportValueChanged(const std::string& key, + uint32_t flags) { + FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key)); +} + +void ValueMapPrefStore::SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) { + prefs_.SetValue(key, std::move(value)); +} + +ValueMapPrefStore::~ValueMapPrefStore() {} + +void ValueMapPrefStore::NotifyInitializationCompleted() { + FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true)); +} diff --git a/chromium/components/prefs/value_map_pref_store.h b/chromium/components/prefs/value_map_pref_store.h new file mode 100644 index 00000000000..bddc64196b7 --- /dev/null +++ b/chromium/components/prefs/value_map_pref_store.h @@ -0,0 +1,57 @@ +// 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 COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_ +#define COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_ + +#include <stdint.h> + +#include <map> +#include <string> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "components/prefs/base_prefs_export.h" +#include "components/prefs/pref_value_map.h" +#include "components/prefs/writeable_pref_store.h" + +// A basic PrefStore implementation that uses a simple name-value map for +// storing the preference values. +class COMPONENTS_PREFS_EXPORT ValueMapPrefStore : public WriteablePrefStore { + public: + ValueMapPrefStore(); + + // PrefStore overrides: + bool GetValue(const std::string& key, + const base::Value** value) const override; + void AddObserver(PrefStore::Observer* observer) override; + void RemoveObserver(PrefStore::Observer* observer) override; + bool HasObservers() const override; + + // WriteablePrefStore overrides: + void SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + void RemoveValue(const std::string& key, uint32_t flags) override; + bool GetMutableValue(const std::string& key, base::Value** value) override; + void ReportValueChanged(const std::string& key, uint32_t flags) override; + void SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) override; + + protected: + ~ValueMapPrefStore() override; + + // Notify observers about the initialization completed event. + void NotifyInitializationCompleted(); + + private: + PrefValueMap prefs_; + + base::ObserverList<PrefStore::Observer, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(ValueMapPrefStore); +}; + +#endif // COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_ diff --git a/chromium/components/prefs/writeable_pref_store.h b/chromium/components/prefs/writeable_pref_store.h new file mode 100644 index 00000000000..0cb9f83d7b3 --- /dev/null +++ b/chromium/components/prefs/writeable_pref_store.h @@ -0,0 +1,72 @@ +// Copyright 2014 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 COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_ +#define COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_ + +#include <stdint.h> + +#include <string> + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "components/prefs/pref_store.h" + +namespace base { +class Value; +} + +// A pref store that can be written to as well as read from. +class COMPONENTS_PREFS_EXPORT WriteablePrefStore : public PrefStore { + public: + // PrefWriteFlags can be used to change the way a pref will be written to + // storage. + enum PrefWriteFlags : uint32_t { + // No flags are specified. + DEFAULT_PREF_WRITE_FLAGS = 0, + + // This marks the pref as "lossy". There is no strict time guarantee on when + // a lossy pref will be persisted to permanent storage when it is modified. + LOSSY_PREF_WRITE_FLAG = 1 << 1 + }; + + WriteablePrefStore() {} + + // Sets a |value| for |key| in the store. |value| must be non-NULL. |flags| is + // a bitmask of PrefWriteFlags. + virtual void SetValue(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) = 0; + + // Removes the value for |key|. + virtual void RemoveValue(const std::string& key, uint32_t flags) = 0; + + // Equivalent to PrefStore::GetValue but returns a mutable value. + virtual bool 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. + // |flags| is a bitmask of PrefWriteFlags. + virtual void ReportValueChanged(const std::string& key, uint32_t flags) = 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. |flags| is a bitmask + // of PrefWriteFlags. + virtual void SetValueSilently(const std::string& key, + scoped_ptr<base::Value> value, + uint32_t flags) = 0; + + protected: + ~WriteablePrefStore() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(WriteablePrefStore); +}; + +#endif // COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_ |