diff options
author | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-08 09:49:11 +0000 |
---|---|---|
committer | mnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-08 09:49:11 +0000 |
commit | acd78969cf6569dcbf409dceb0b6511751afd026 (patch) | |
tree | ed2168c663ae3e8a15d857838b78ed2386da3281 /chrome/browser/prefs | |
parent | 5138bbffd1b2d5151ad74b4e7ae8091fc7d44114 (diff) | |
download | chromium_src-acd78969cf6569dcbf409dceb0b6511751afd026.zip chromium_src-acd78969cf6569dcbf409dceb0b6511751afd026.tar.gz chromium_src-acd78969cf6569dcbf409dceb0b6511751afd026.tar.bz2 |
Clean up pref change notification handling.
This is a complete overhaul of PrefValueStore, PrefStore, PrefNotifier, and PrefService. Specifically:
- Add an observer interface to PrefStore that can be used to notify the upper layers of the pref system about value changes. Currently, it's unused mostly, but that'll change when we refactor ExtensionPrefStore and ConfigurationPolicyPrefStore.
- Make PrefNotifier be a dependency of PrefValueStore. That helps in keeping the pref change detection handling local to PrefValueStore.
- Clean up related unit tests, removing redundant mocks and gmockify others.
BUG=64893
TEST=Compiles and passes tests
Review URL: http://codereview.chromium.org/5441002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68574 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/prefs')
20 files changed, 1375 insertions, 1351 deletions
diff --git a/chrome/browser/prefs/dummy_pref_store.cc b/chrome/browser/prefs/dummy_pref_store.cc deleted file mode 100644 index 3a0a81a..0000000 --- a/chrome/browser/prefs/dummy_pref_store.cc +++ /dev/null @@ -1,22 +0,0 @@ -// 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 "chrome/browser/prefs/dummy_pref_store.h" - -#include "base/values.h" - -DummyPrefStore::DummyPrefStore() - : prefs_(new DictionaryValue()), - read_only_(true), - prefs_written_(false) { } - -PrefStore::PrefReadError DummyPrefStore::ReadPrefs() { - prefs_.reset(new DictionaryValue()); - return PrefStore::PREF_READ_ERROR_NONE; -} - -bool DummyPrefStore::WritePrefs() { - prefs_written_ = true; - return prefs_written_; -} diff --git a/chrome/browser/prefs/dummy_pref_store.h b/chrome/browser/prefs/dummy_pref_store.h deleted file mode 100644 index 4a4e6ba..0000000 --- a/chrome/browser/prefs/dummy_pref_store.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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 CHROME_BROWSER_PREFS_DUMMY_PREF_STORE_H_ -#define CHROME_BROWSER_PREFS_DUMMY_PREF_STORE_H_ -#pragma once - -#include "base/basictypes.h" -#include "base/scoped_ptr.h" -#include "chrome/common/pref_store.h" - -class DictionaryValue; - -// |DummyPrefStore| is a stub implementation of the |PrefStore| interface. -// It allows to get and set the state of the |PrefStore|. -class DummyPrefStore : public PrefStore { - public: - DummyPrefStore(); - virtual ~DummyPrefStore() {} - - virtual DictionaryValue* prefs() const { return prefs_.get(); } - - virtual PrefStore::PrefReadError ReadPrefs(); - - virtual bool ReadOnly() { return read_only_; } - - virtual bool WritePrefs(); - - // Getter and Setter methods for setting and getting the state of the - // |DummyPrefStore|. - virtual void set_read_only(bool read_only) { read_only_ = read_only; } - virtual void set_prefs(DictionaryValue* prefs) { prefs_.reset(prefs); } - virtual void set_prefs_written(bool status) { prefs_written_ = status; } - virtual bool get_prefs_written() { return prefs_written_; } - - private: - scoped_ptr<DictionaryValue> prefs_; - - // Flag that indicates if the PrefStore is read-only - bool read_only_; - - // Flag that indicates if the method WritePrefs was called. - bool prefs_written_; - - DISALLOW_COPY_AND_ASSIGN(DummyPrefStore); -}; - -#endif // CHROME_BROWSER_PREFS_DUMMY_PREF_STORE_H_ diff --git a/chrome/browser/prefs/pref_change_registrar_unittest.cc b/chrome/browser/prefs/pref_change_registrar_unittest.cc index 31c513b..3310167 100644 --- a/chrome/browser/prefs/pref_change_registrar_unittest.cc +++ b/chrome/browser/prefs/pref_change_registrar_unittest.cc @@ -4,7 +4,7 @@ #include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/common/notification_details.h" -#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_observer_mock.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" @@ -25,13 +25,6 @@ class MockPrefService : public TestingPrefService { MOCK_METHOD2(RemovePrefObserver, void(const char*, NotificationObserver*)); }; -// A mock observer used as a pref observer -class MockObserver : public NotificationObserver { - public: - MOCK_METHOD3(Observe, void(NotificationType, const NotificationSource& source, - const NotificationDetails& details)); -}; - class PrefChangeRegistrarTest : public testing::Test { public: PrefChangeRegistrarTest() {} @@ -45,12 +38,12 @@ class PrefChangeRegistrarTest : public testing::Test { private: scoped_ptr<MockPrefService> service_; - scoped_ptr<MockObserver> observer_; + scoped_ptr<NotificationObserverMock> observer_; }; void PrefChangeRegistrarTest::SetUp() { service_.reset(new MockPrefService()); - observer_.reset(new MockObserver()); + observer_.reset(new NotificationObserverMock()); } TEST_F(PrefChangeRegistrarTest, AddAndRemove) { diff --git a/chrome/browser/prefs/pref_member_unittest.cc b/chrome/browser/prefs/pref_member_unittest.cc index f59f937..986fc69 100644 --- a/chrome/browser/prefs/pref_member_unittest.cc +++ b/chrome/browser/prefs/pref_member_unittest.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/prefs/dummy_pref_store.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/prefs/pref_value_store.h" #include "chrome/common/notification_service.h" diff --git a/chrome/browser/prefs/pref_notifier.cc b/chrome/browser/prefs/pref_notifier.cc deleted file mode 100644 index 151b128..0000000 --- a/chrome/browser/prefs/pref_notifier.cc +++ /dev/null @@ -1,137 +0,0 @@ -// 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 "chrome/browser/prefs/pref_notifier.h" - -#include "base/stl_util-inl.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/policy/configuration_policy_pref_store.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/browser/prefs/pref_value_store.h" -#include "chrome/common/notification_service.h" - - -PrefNotifier::PrefNotifier(PrefService* service, PrefValueStore* value_store) - : pref_service_(service), - pref_value_store_(value_store) { - registrar_.Add(this, - NotificationType(NotificationType::POLICY_CHANGED), - NotificationService::AllSources()); -} - -PrefNotifier::~PrefNotifier() { - DCHECK(CalledOnValidThread()); - - // Verify that there are no pref observers when we shut down. - for (PrefObserverMap::iterator it = pref_observers_.begin(); - it != pref_observers_.end(); ++it) { - NotificationObserverList::Iterator obs_iterator(*(it->second)); - if (obs_iterator.GetNext()) { - LOG(WARNING) << "pref observer found at shutdown " << it->first; - } - } - - STLDeleteContainerPairSecondPointers(pref_observers_.begin(), - pref_observers_.end()); - pref_observers_.clear(); -} - -void PrefNotifier::OnPreferenceSet(const char* pref_name, - PrefNotifier::PrefStoreType new_store) { - if (pref_value_store_->PrefHasChanged(pref_name, new_store)) - FireObservers(pref_name); -} - -void PrefNotifier::OnUserPreferenceSet(const char* pref_name) { - OnPreferenceSet(pref_name, PrefNotifier::USER_STORE); -} - -void PrefNotifier::FireObservers(const char* path) { - DCHECK(CalledOnValidThread()); - - // Convert path to a std::string because the Details constructor requires a - // class. - std::string path_str(path); - PrefObserverMap::iterator observer_iterator = pref_observers_.find(path_str); - if (observer_iterator == pref_observers_.end()) - return; - - NotificationObserverList::Iterator it(*(observer_iterator->second)); - NotificationObserver* observer; - while ((observer = it.GetNext()) != NULL) { - observer->Observe(NotificationType::PREF_CHANGED, - Source<PrefService>(pref_service_), - Details<std::string>(&path_str)); - } -} - -void PrefNotifier::AddPrefObserver(const char* path, - NotificationObserver* obs) { - // Get the pref observer list associated with the path. - NotificationObserverList* observer_list = NULL; - PrefObserverMap::iterator observer_iterator = pref_observers_.find(path); - if (observer_iterator == pref_observers_.end()) { - observer_list = new NotificationObserverList; - pref_observers_[path] = observer_list; - } else { - observer_list = observer_iterator->second; - } - - // Verify that this observer doesn't already exist. - NotificationObserverList::Iterator it(*observer_list); - NotificationObserver* existing_obs; - while ((existing_obs = it.GetNext()) != NULL) { - DCHECK(existing_obs != obs) << path << " observer already registered"; - if (existing_obs == obs) - return; - } - - // Ok, safe to add the pref observer. - observer_list->AddObserver(obs); -} - -void PrefNotifier::RemovePrefObserver(const char* path, - NotificationObserver* obs) { - DCHECK(CalledOnValidThread()); - - PrefObserverMap::iterator observer_iterator = pref_observers_.find(path); - if (observer_iterator == pref_observers_.end()) { - return; - } - - NotificationObserverList* observer_list = observer_iterator->second; - observer_list->RemoveObserver(obs); -} - -void PrefNotifier::FireObserversForRefreshedManagedPrefs( - std::vector<std::string> changed_prefs_paths) { - DCHECK(CalledOnValidThread()); - std::vector<std::string>::const_iterator current; - for (current = changed_prefs_paths.begin(); - current != changed_prefs_paths.end(); - ++current) { - FireObservers(current->c_str()); - } -} - -void PrefNotifier::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - using policy::ConfigurationPolicyPrefStore; - - if (type == NotificationType::POLICY_CHANGED) { - PrefValueStore::AfterRefreshCallback* callback = - NewCallback(this, - &PrefNotifier::FireObserversForRefreshedManagedPrefs); - // The notification of the policy refresh can come from any thread, - // but the manipulation of the PrefValueStore must happen on the UI - // thread, thus the policy refresh must be explicitly started on it. - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableMethod( - pref_value_store_, - &PrefValueStore::RefreshPolicyPrefs, - callback)); - } -} diff --git a/chrome/browser/prefs/pref_notifier.h b/chrome/browser/prefs/pref_notifier.h index 7334a51..f2d9c52 100644 --- a/chrome/browser/prefs/pref_notifier.h +++ b/chrome/browser/prefs/pref_notifier.h @@ -7,112 +7,21 @@ #pragma once #include <string> -#include <vector> -#include "base/hash_tables.h" -#include "base/non_thread_safe.h" -#include "base/observer_list.h" -#include "chrome/common/notification_observer.h" -#include "chrome/common/notification_registrar.h" - -class NotificationObserver; -class PrefService; -class PrefValueStore; -class Value; - -// Registers observers for particular preferences and sends notifications when -// preference values or sources (i.e., which preference layer controls the -// preference) change. -class PrefNotifier : public NonThreadSafe, - public NotificationObserver { +// 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: - // PrefStores must be listed here in order from highest to lowest priority. - // MANAGED_PLATFORM contains all managed preference values that are - // provided by a platform-specific policy mechanism (e.g. Windows - // Group Policy). - // DEVICE_MANAGEMENT contains all managed preference values supplied - // by the device management server (cloud policy). - // 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 recommended (policy) preference values. - // DEFAULT contains all application default preference values. - // This enum is kept in pref_notifier.h rather than pref_value_store.h in - // order to minimize additional headers needed by the *PrefStore files. - 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_PLATFORM_STORE = 0, - DEVICE_MANAGEMENT_STORE, - EXTENSION_STORE, - COMMAND_LINE_STORE, - USER_STORE, - RECOMMENDED_STORE, - DEFAULT_STORE, - PREF_STORE_TYPE_MAX = DEFAULT_STORE - }; - - // The |service| with which this notifier is associated will be sent as the - // source of any notifications. - PrefNotifier(PrefService* service, PrefValueStore* value_store); - - virtual ~PrefNotifier(); - - // For the given pref_name, fire any observer of the pref if the effective - // value of the pref or the store controlling its value has changed, been - // added, or been removed (but not if it's re-setting the same value it had - // already). |new_store| should be the PrefStoreType of the store reporting - // the change. - void OnPreferenceSet(const char* pref_name, - PrefNotifier::PrefStoreType new_store); - - // Convenience method to be called when a preference is set in the - // USER_STORE. See OnPreferenceSet(). - void OnUserPreferenceSet(const char* pref_name); - - // For the given pref_name, fire any observer of the pref. Virtual so it can - // be mocked for unit testing. - virtual void FireObservers(const char* path); - - // If the pref at the given path changes, we call the observer's Observe - // method with PREF_CHANGED. - void AddPrefObserver(const char* path, NotificationObserver* obs); - void RemovePrefObserver(const char* path, NotificationObserver* obs); - - protected: - // 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 ObserverList<NotificationObserver> NotificationObserverList; - typedef base::hash_map<std::string, NotificationObserverList*> - PrefObserverMap; - const PrefObserverMap* pref_observers() { return &pref_observers_; } - - private: - // Weak references. - PrefService* pref_service_; - PrefValueStore* pref_value_store_; - - NotificationRegistrar registrar_; - - PrefObserverMap pref_observers_; - - // Called after a policy refresh to notify relevant preference observers. - // |changed_prefs_paths| is the vector of preference paths changed by the - // policy update. It is passed by value and not reference because - // this method is called asynchronously as a callback from another thread. - // Copying the vector guarantees that the vector's lifecycle spans the - // method's invocation. - void FireObserversForRefreshedManagedPrefs( - std::vector<std::string> changed_prefs_paths); + virtual ~PrefNotifier() {} - // NotificationObserver methods: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); + // Sends out a change notification for the preference identified by + // |pref_name|. + virtual void OnPreferenceChanged(const std::string& pref_name) = 0; - DISALLOW_COPY_AND_ASSIGN(PrefNotifier); + // Broadcasts the intialization completed notification. + virtual void OnInitializationCompleted() = 0; }; #endif // CHROME_BROWSER_PREFS_PREF_NOTIFIER_H_ diff --git a/chrome/browser/prefs/pref_notifier_impl.cc b/chrome/browser/prefs/pref_notifier_impl.cc new file mode 100644 index 0000000..d15f425 --- /dev/null +++ b/chrome/browser/prefs/pref_notifier_impl.cc @@ -0,0 +1,105 @@ +// 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 "chrome/browser/prefs/pref_notifier_impl.h" + +#include "base/stl_util-inl.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_service.h" + +PrefNotifierImpl::PrefNotifierImpl(PrefService* service) + : pref_service_(service) { +} + +PrefNotifierImpl::~PrefNotifierImpl() { + DCHECK(CalledOnValidThread()); + + // Verify that there are no pref observers when we shut down. + for (PrefObserverMap::iterator it = pref_observers_.begin(); + it != pref_observers_.end(); ++it) { + NotificationObserverList::Iterator obs_iterator(*(it->second)); + if (obs_iterator.GetNext()) { + LOG(WARNING) << "pref observer found at shutdown " << it->first; + } + } + + STLDeleteContainerPairSecondPointers(pref_observers_.begin(), + pref_observers_.end()); + pref_observers_.clear(); +} + +void PrefNotifierImpl::AddPrefObserver(const char* path, + NotificationObserver* obs) { + // Get the pref observer list associated with the path. + NotificationObserverList* observer_list = NULL; + const PrefObserverMap::iterator observer_iterator = + pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + observer_list = new NotificationObserverList; + pref_observers_[path] = observer_list; + } else { + observer_list = observer_iterator->second; + } + + // Verify that this observer doesn't already exist. + NotificationObserverList::Iterator it(*observer_list); + NotificationObserver* existing_obs; + while ((existing_obs = it.GetNext()) != NULL) { + DCHECK(existing_obs != obs) << path << " observer already registered"; + if (existing_obs == obs) + return; + } + + // Ok, safe to add the pref observer. + observer_list->AddObserver(obs); +} + +void PrefNotifierImpl::RemovePrefObserver(const char* path, + NotificationObserver* obs) { + DCHECK(CalledOnValidThread()); + + const PrefObserverMap::iterator observer_iterator = + pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + return; + } + + NotificationObserverList* observer_list = observer_iterator->second; + observer_list->RemoveObserver(obs); +} + +void PrefNotifierImpl::OnPreferenceChanged(const std::string& path) { + FireObservers(path); +} + +void PrefNotifierImpl::OnInitializationCompleted() { + DCHECK(CalledOnValidThread()); + + NotificationService::current()->Notify( + NotificationType::PREF_INITIALIZATION_COMPLETED, + Source<PrefService>(pref_service_), + NotificationService::NoDetails()); +} + +void PrefNotifierImpl::FireObservers(const std::string& path) { + DCHECK(CalledOnValidThread()); + + // Only send notifications for registered preferences. + if (!pref_service_->FindPreference(path.c_str())) + return; + + const PrefObserverMap::iterator observer_iterator = + pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) + return; + + NotificationObserverList::Iterator it(*(observer_iterator->second)); + NotificationObserver* observer; + while ((observer = it.GetNext()) != NULL) { + observer->Observe(NotificationType::PREF_CHANGED, + Source<PrefService>(pref_service_), + Details<const std::string>(&path)); + } +} diff --git a/chrome/browser/prefs/pref_notifier_impl.h b/chrome/browser/prefs/pref_notifier_impl.h new file mode 100644 index 0000000..1d61830 --- /dev/null +++ b/chrome/browser/prefs/pref_notifier_impl.h @@ -0,0 +1,58 @@ +// 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 CHROME_BROWSER_PREFS_PREF_NOTIFIER_IMPL_H_ +#define CHROME_BROWSER_PREFS_PREF_NOTIFIER_IMPL_H_ +#pragma once + +#include <string> + +#include "base/hash_tables.h" +#include "base/non_thread_safe.h" +#include "base/observer_list.h" +#include "chrome/browser/prefs/pref_notifier.h" + +class PrefService; +class NotificationObserver; + +// The PrefNotifier implementation used by the PrefService. +class PrefNotifierImpl : public PrefNotifier, + public NonThreadSafe { + public: + explicit PrefNotifierImpl(PrefService* pref_service); + virtual ~PrefNotifierImpl(); + + // If the pref at the given path changes, we call the observer's Observe + // method with PREF_CHANGED. + void AddPrefObserver(const char* path, NotificationObserver* obs); + void RemovePrefObserver(const char* path, NotificationObserver* obs); + + // PrefNotifier overrides. + virtual void OnPreferenceChanged(const std::string& pref_name); + virtual void OnInitializationCompleted(); + + protected: + // 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 ObserverList<NotificationObserver> NotificationObserverList; + typedef base::hash_map<std::string, NotificationObserverList*> + PrefObserverMap; + + 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_; + + DISALLOW_COPY_AND_ASSIGN(PrefNotifierImpl); +}; + +#endif // CHROME_BROWSER_PREFS_PREF_NOTIFIER_IMPL_H_ diff --git a/chrome/browser/prefs/pref_notifier_impl_unittest.cc b/chrome/browser/prefs/pref_notifier_impl_unittest.cc new file mode 100644 index 0000000..8f3be3c --- /dev/null +++ b/chrome/browser/prefs/pref_notifier_impl_unittest.cc @@ -0,0 +1,199 @@ +// 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 "chrome/browser/prefs/pref_notifier_impl.h" +#include "chrome/browser/prefs/pref_observer_mock.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/pref_value_store.h" +#include "chrome/common/notification_observer_mock.h" +#include "chrome/common/notification_service.h" +#include "chrome/test/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"; + +bool DetailsAreChangedPref(const Details<std::string>& details) { + std::string* string_in = Details<std::string>(details).ptr(); + return strcmp(string_in->c_str(), kChangedPref) == 0; +} + +// Test 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 char* path, NotificationObserver* obs) { + PrefObserverMap::const_iterator observer_iterator = + pref_observers()->find(path); + if (observer_iterator == pref_observers()->end()) + return false; + + NotificationObserverList* observer_list = observer_iterator->second; + NotificationObserverList::Iterator it(*observer_list); + NotificationObserver* existing_obs; + size_t count = 0; + while ((existing_obs = it.GetNext()) != NULL) { + if (existing_obs == obs) + count++; + } + + return count; + } +}; + +// Test fixture class. +class PrefNotifierTest : public testing::Test { + protected: + virtual void SetUp() { + pref_service_.RegisterBooleanPref(kChangedPref, true); + pref_service_.RegisterBooleanPref(kUnchangedPref, true); + } + + TestingPrefService 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_); + NotificationObserverMock observer; + NotificationRegistrar registrar; + registrar.Add(&observer, NotificationType::PREF_INITIALIZATION_COMPLETED, + Source<PrefService>(&pref_service_)); + EXPECT_CALL(observer, Observe( + Field(&NotificationType::value, + NotificationType::PREF_INITIALIZATION_COMPLETED), + Source<PrefService>(&pref_service_), + NotificationService::NoDetails())); + notifier.OnInitializationCompleted(); +} + +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) + 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 // NDEBUG + + // 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) { + FundamentalValue value_true(true); + PrefNotifierImpl notifier(&pref_service_); + notifier.AddPrefObserver(kChangedPref, &obs1_); + notifier.AddPrefObserver(kUnchangedPref, &obs1_); + + obs1_.Expect(&pref_service_, kChangedPref, &value_true); + EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + notifier.AddPrefObserver(kChangedPref, &obs2_); + notifier.AddPrefObserver(kUnchangedPref, &obs2_); + + obs1_.Expect(&pref_service_, kChangedPref, &value_true); + obs2_.Expect(&pref_service_, kChangedPref, &value_true); + 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_, Observe(_, _, _)).Times(0); + obs2_.Expect(&pref_service_, kChangedPref, &value_true); + 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_, Observe(_, _, _)).Times(0); + obs2_.Expect(&pref_service_, kChangedPref, &value_true); + notifier.OnPreferenceChanged(kChangedPref); + Mock::VerifyAndClearExpectations(&obs1_); + Mock::VerifyAndClearExpectations(&obs2_); + + notifier.RemovePrefObserver(kChangedPref, &obs2_); + notifier.RemovePrefObserver(kUnchangedPref, &obs2_); +} + +} // namespace diff --git a/chrome/browser/prefs/pref_notifier_unittest.cc b/chrome/browser/prefs/pref_notifier_unittest.cc deleted file mode 100644 index 2fe4555..0000000 --- a/chrome/browser/prefs/pref_notifier_unittest.cc +++ /dev/null @@ -1,294 +0,0 @@ -// 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 "chrome/browser/prefs/in_memory_pref_store.h" -#include "chrome/browser/prefs/pref_notifier.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/browser/prefs/pref_value_store.h" -#include "chrome/common/notification_observer.h" -#include "chrome/common/notification_type.h" -#include "chrome/common/notification_service.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - - -namespace { - -const char kChangedPref[] = "changed_pref"; -const char kUnchangedPref[] = "unchanged_pref"; - -bool DetailsAreChangedPref(const Details<std::string>& details) { - std::string* string_in = Details<std::string>(details).ptr(); - return strcmp(string_in->c_str(), kChangedPref) == 0; -} - -// Test PrefNotifier that allows tracking of observers and notifications. -class MockPrefNotifier : public PrefNotifier { - public: - MockPrefNotifier(PrefService* prefs, PrefValueStore* value_store) - : PrefNotifier(prefs, value_store) {} - - virtual ~MockPrefNotifier() {} - - MOCK_METHOD1(FireObservers, void(const char* path)); - - void RealFireObservers(const char* path) { - PrefNotifier::FireObservers(path); - } - - size_t CountObserver(const char* path, NotificationObserver* obs) { - PrefObserverMap::const_iterator observer_iterator = - pref_observers()->find(path); - if (observer_iterator == pref_observers()->end()) - return false; - - NotificationObserverList* observer_list = observer_iterator->second; - NotificationObserverList::Iterator it(*observer_list); - NotificationObserver* existing_obs; - size_t count = 0; - while ((existing_obs = it.GetNext()) != NULL) { - if (existing_obs == obs) - count++; - } - - return count; - } -}; - -// Mock PrefValueStore that has no unnecessary PrefStores and injects a simpler -// PrefHasChanged(). -class MockPrefValueStore : public PrefValueStore { - public: - MockPrefValueStore() - : PrefValueStore(NULL, NULL, NULL, NULL, NULL, NULL, - new InMemoryPrefStore(), NULL) {} - - virtual ~MockPrefValueStore() {} - - // This mock version returns true if the pref path starts with "changed". - virtual bool PrefHasChanged(const char* path, - PrefNotifier::PrefStoreType new_store) { - std::string path_str(path); - if (path_str.compare(0, 7, "changed") == 0) - return true; - return false; - } -}; - -// Mock PrefService that allows the PrefNotifier to be injected. -class MockPrefService : public PrefService { - public: - explicit MockPrefService(PrefValueStore* pref_value_store) - : PrefService(pref_value_store) {} - - void SetPrefNotifier(PrefNotifier* notifier) { - pref_notifier_.reset(notifier); - } -}; - -// Mock PrefObserver that verifies notifications. -class MockPrefObserver : public NotificationObserver { - public: - virtual ~MockPrefObserver() {} - - MOCK_METHOD3(Observe, void(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details)); -}; - -// Test fixture class. -class PrefNotifierTest : public testing::Test { - protected: - virtual void SetUp() { - value_store_ = new MockPrefValueStore; - pref_service_.reset(new MockPrefService(value_store_)); - notifier_ = new MockPrefNotifier(pref_service_.get(), value_store_); - pref_service_->SetPrefNotifier(notifier_); - - pref_service_->RegisterBooleanPref(kChangedPref, true); - pref_service_->RegisterBooleanPref(kUnchangedPref, true); - } - - // The PrefService takes ownership of the PrefValueStore and PrefNotifier. - PrefValueStore* value_store_; - MockPrefNotifier* notifier_; - scoped_ptr<MockPrefService> pref_service_; - - MockPrefObserver obs1_; - MockPrefObserver obs2_; -}; - -TEST_F(PrefNotifierTest, OnPreferenceSet) { - EXPECT_CALL(*notifier_, FireObservers(kChangedPref)) - .Times(PrefNotifier::PREF_STORE_TYPE_MAX + 1); - EXPECT_CALL(*notifier_, FireObservers(kUnchangedPref)).Times(0); - - for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) { - notifier_->OnPreferenceSet(kChangedPref, - static_cast<PrefNotifier::PrefStoreType>(i)); - notifier_->OnPreferenceSet(kUnchangedPref, - static_cast<PrefNotifier::PrefStoreType>(i)); - } -} - -TEST_F(PrefNotifierTest, OnUserPreferenceSet) { - EXPECT_CALL(*notifier_, FireObservers(kChangedPref)); - EXPECT_CALL(*notifier_, FireObservers(kUnchangedPref)).Times(0); - notifier_->OnUserPreferenceSet(kChangedPref); - notifier_->OnUserPreferenceSet(kUnchangedPref); -} - -TEST_F(PrefNotifierTest, AddAndRemovePrefObservers) { - const char pref_name[] = "homepage"; - const char pref_name2[] = "proxy"; - - 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) - 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 // NDEBUG - - // 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) { - using testing::_; - using testing::Field; - using testing::Invoke; - using testing::Mock; - using testing::Truly; - - // Tell googlemock to pass calls to the mock's "back door" too. - ON_CALL(*notifier_, FireObservers(_)) - .WillByDefault(Invoke(notifier_, &MockPrefNotifier::RealFireObservers)); - EXPECT_CALL(*notifier_, FireObservers(kChangedPref)).Times(4); - EXPECT_CALL(*notifier_, FireObservers(kUnchangedPref)).Times(0); - - notifier_->AddPrefObserver(kChangedPref, &obs1_); - notifier_->AddPrefObserver(kUnchangedPref, &obs1_); - - EXPECT_CALL(obs1_, Observe( - Field(&NotificationType::value, NotificationType::PREF_CHANGED), - Source<PrefService>(pref_service_.get()), - Truly(DetailsAreChangedPref))); - EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0); - notifier_->OnUserPreferenceSet(kChangedPref); - Mock::VerifyAndClearExpectations(&obs1_); - Mock::VerifyAndClearExpectations(&obs2_); - - EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0); - EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0); - notifier_->OnUserPreferenceSet(kUnchangedPref); - Mock::VerifyAndClearExpectations(&obs1_); - Mock::VerifyAndClearExpectations(&obs2_); - - notifier_->AddPrefObserver(kChangedPref, &obs2_); - notifier_->AddPrefObserver(kUnchangedPref, &obs2_); - - EXPECT_CALL(obs1_, Observe( - Field(&NotificationType::value, NotificationType::PREF_CHANGED), - Source<PrefService>(pref_service_.get()), - Truly(DetailsAreChangedPref))); - EXPECT_CALL(obs2_, Observe( - Field(&NotificationType::value, NotificationType::PREF_CHANGED), - Source<PrefService>(pref_service_.get()), - Truly(DetailsAreChangedPref))); - notifier_->OnUserPreferenceSet(kChangedPref); - Mock::VerifyAndClearExpectations(&obs1_); - Mock::VerifyAndClearExpectations(&obs2_); - - EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0); - EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0); - notifier_->OnUserPreferenceSet(kUnchangedPref); - 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_, Observe(_, _, _)).Times(0); - EXPECT_CALL(obs2_, Observe( - Field(&NotificationType::value, NotificationType::PREF_CHANGED), - Source<PrefService>(pref_service_.get()), - Truly(DetailsAreChangedPref))); - notifier_->OnUserPreferenceSet(kChangedPref); - Mock::VerifyAndClearExpectations(&obs1_); - Mock::VerifyAndClearExpectations(&obs2_); - - EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0); - EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0); - notifier_->OnUserPreferenceSet(kUnchangedPref); - Mock::VerifyAndClearExpectations(&obs1_); - Mock::VerifyAndClearExpectations(&obs2_); - - // Make sure removing an observer entirely doesn't affect anything else. - notifier_->RemovePrefObserver(kUnchangedPref, &obs1_); - - EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0); - EXPECT_CALL(obs2_, Observe( - Field(&NotificationType::value, NotificationType::PREF_CHANGED), - Source<PrefService>(pref_service_.get()), - Truly(DetailsAreChangedPref))); - notifier_->OnUserPreferenceSet(kChangedPref); - Mock::VerifyAndClearExpectations(&obs1_); - Mock::VerifyAndClearExpectations(&obs2_); - - EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0); - EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0); - notifier_->OnUserPreferenceSet(kUnchangedPref); - - notifier_->RemovePrefObserver(kChangedPref, &obs2_); - notifier_->RemovePrefObserver(kUnchangedPref, &obs2_); -} - -} // namespace diff --git a/chrome/browser/prefs/pref_observer_mock.h b/chrome/browser/prefs/pref_observer_mock.h new file mode 100644 index 0000000..f339841 --- /dev/null +++ b/chrome/browser/prefs/pref_observer_mock.h @@ -0,0 +1,60 @@ +// 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 CHROME_BROWSER_PREFS_PREF_OBSERVER_MOCK_H_ +#define CHROME_BROWSER_PREFS_PREF_OBSERVER_MOCK_H_ +#pragma once + +#include <string> + +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.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.c_str()); + if (!pref) + return false; + + const 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 PrefObserverMock : public NotificationObserver { + public: + PrefObserverMock() {} + virtual ~PrefObserverMock() {} + + MOCK_METHOD3(Observe, void(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details)); + + void Expect(const PrefService* prefs, + const std::string& pref_name, + const Value* value) { + EXPECT_CALL(*this, Observe(NotificationType(NotificationType::PREF_CHANGED), + Source<PrefService>(prefs), + Property(&Details<std::string>::ptr, + Pointee(pref_name)))) + .With(PrefValueMatches(prefs, pref_name, value)); + } +}; + +#endif // CHROME_BROWSER_PREFS_PREF_OBSERVER_MOCK_H_ diff --git a/chrome/browser/prefs/pref_service.cc b/chrome/browser/prefs/pref_service.cc index f7a11ffe..524a858 100644 --- a/chrome/browser/prefs/pref_service.cc +++ b/chrome/browser/prefs/pref_service.cc @@ -21,9 +21,13 @@ #include "base/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/browser/browser_thread.h" -#include "chrome/browser/prefs/pref_notifier.h" +#include "chrome/browser/policy/configuration_policy_pref_store.h" +#include "chrome/browser/prefs/command_line_pref_store.h" +#include "chrome/browser/prefs/in_memory_pref_store.h" +#include "chrome/browser/prefs/pref_notifier_impl.h" #include "chrome/browser/prefs/pref_value_store.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/json_pref_store.h" #include "chrome/common/notification_service.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" @@ -83,6 +87,8 @@ void NotifyReadError(PrefService* pref, int message_id) { // static PrefService* PrefService::CreatePrefService(const FilePath& pref_filename, Profile* profile) { + using policy::ConfigurationPolicyPrefStore; + #if defined(OS_LINUX) // We'd like to see what fraction of our users have the preferences // stored on a network file system, as we've had no end of troubles @@ -96,19 +102,54 @@ PrefService* PrefService::CreatePrefService(const FilePath& pref_filename, } #endif - return new PrefService( - PrefValueStore::CreatePrefValueStore(pref_filename, profile, false)); + ConfigurationPolicyPrefStore* managed = + ConfigurationPolicyPrefStore::CreateManagedPlatformPolicyPrefStore(); + ConfigurationPolicyPrefStore* device_management = + ConfigurationPolicyPrefStore::CreateDeviceManagementPolicyPrefStore( + profile); + InMemoryPrefStore* extension = new InMemoryPrefStore(); + CommandLinePrefStore* command_line = + new CommandLinePrefStore(CommandLine::ForCurrentProcess()); + JsonPrefStore* user = new JsonPrefStore( + pref_filename, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); + ConfigurationPolicyPrefStore* recommended = + ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore(); + + return new PrefService(managed, device_management, extension, command_line, + user, recommended, profile); } // static PrefService* PrefService::CreateUserPrefService(const FilePath& pref_filename) { - return new PrefService( - PrefValueStore::CreatePrefValueStore(pref_filename, NULL, true)); -} - -PrefService::PrefService(PrefValueStore* pref_value_store) - : pref_value_store_(pref_value_store) { - pref_notifier_.reset(new PrefNotifier(this, pref_value_store)); + JsonPrefStore* user = new JsonPrefStore( + pref_filename, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); + InMemoryPrefStore* extension = new InMemoryPrefStore(); + + return new PrefService(NULL, NULL, extension, NULL, user, NULL, NULL); +} + +PrefService::PrefService(PrefStore* managed_platform_prefs, + PrefStore* device_management_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + Profile* profile) { + pref_notifier_.reset(new PrefNotifierImpl(this)); + extension_store_ = extension_prefs; + default_store_ = new InMemoryPrefStore(); + pref_value_store_ = + new PrefValueStore(managed_platform_prefs, + device_management_prefs, + extension_store_, + command_line_prefs, + user_prefs, + recommended_prefs, + default_store_, + pref_notifier_.get(), + profile); InitFromStorage(); } @@ -319,6 +360,18 @@ const PrefService::Preference* PrefService::FindPreference( return it == prefs_.end() ? NULL : *it; } +bool PrefService::ReadOnly() const { + return pref_value_store_->ReadOnly(); +} + +PrefNotifier* PrefService::pref_notifier() const { + return pref_notifier_.get(); +} + +PrefStore* PrefService::GetExtensionPrefStore() { + return extension_store_; +} + bool PrefService::IsManagedPreference(const char* pref_name) const { const Preference* pref = FindPreference(pref_name); if (pref && pref->IsManaged()) { @@ -355,10 +408,6 @@ const ListValue* PrefService::GetList(const char* path) const { return static_cast<const ListValue*>(value); } -bool PrefService::ReadOnly() const { - return pref_value_store_->ReadOnly(); -} - void PrefService::AddPrefObserver(const char* path, NotificationObserver* obs) { pref_notifier_->AddPrefObserver(path, obs); @@ -388,11 +437,11 @@ void PrefService::RegisterPreference(const char* path, Value* default_value) { // easier for callers to check for empty dict/list prefs. The PrefValueStore // accepts ownership of the value (null or default_value). if (Value::TYPE_LIST == orig_type || Value::TYPE_DICTIONARY == orig_type) { - pref_value_store_->SetDefaultPrefValue(path, Value::CreateNullValue()); + default_store_->prefs()->Set(path, Value::CreateNullValue()); } else { // Hand off ownership. DCHECK(!PrefStore::IsUseDefaultSentinelValue(default_value)); - pref_value_store_->SetDefaultPrefValue(path, scoped_value.release()); + default_store_->prefs()->Set(path, scoped_value.release()); } pref_value_store_->RegisterPreferenceType(path, orig_type); @@ -407,8 +456,7 @@ void PrefService::ClearPref(const char* path) { NOTREACHED() << "Trying to clear an unregistered pref: " << path; return; } - if (pref_value_store_->RemoveUserPrefValue(path)) - pref_notifier_->OnUserPreferenceSet(path); + pref_value_store_->RemoveUserPrefValue(path); } void PrefService::Set(const char* path, const Value& value) { @@ -422,21 +470,17 @@ void PrefService::Set(const char* path, const Value& value) { // Allow dictionary and list types to be set to null, which removes their // user values. - bool value_changed = false; if (value.GetType() == Value::TYPE_NULL && (pref->GetType() == Value::TYPE_DICTIONARY || pref->GetType() == Value::TYPE_LIST)) { - value_changed = pref_value_store_->RemoveUserPrefValue(path); + pref_value_store_->RemoveUserPrefValue(path); } else if (pref->GetType() != value.GetType()) { NOTREACHED() << "Trying to set pref " << path << " of type " << pref->GetType() << " to value of type " << value.GetType(); } else { - value_changed = pref_value_store_->SetUserPrefValue(path, value.DeepCopy()); + pref_value_store_->SetUserPrefValue(path, value.DeepCopy()); } - - if (value_changed) - pref_notifier_->OnUserPreferenceSet(path); } void PrefService::SetBoolean(const char* path, bool value) { @@ -514,7 +558,7 @@ DictionaryValue* PrefService::GetMutableDictionary(const char* path) { if (!pref_value_store_->GetUserValue(path, &tmp_value) || !tmp_value->IsType(Value::TYPE_DICTIONARY)) { dict = new DictionaryValue; - pref_value_store_->SetUserPrefValue(path, dict); + pref_value_store_->SetUserPrefValueSilently(path, dict); } else { dict = static_cast<DictionaryValue*>(tmp_value); } @@ -541,7 +585,7 @@ ListValue* PrefService::GetMutableList(const char* path) { if (!pref_value_store_->GetUserValue(path, &tmp_value) || !tmp_value->IsType(Value::TYPE_LIST)) { list = new ListValue; - pref_value_store_->SetUserPrefValue(path, list); + pref_value_store_->SetUserPrefValueSilently(path, list); } else { list = static_cast<ListValue*>(tmp_value); } @@ -575,12 +619,7 @@ void PrefService::SetUserPrefValue(const char* path, Value* new_value) { return; } - if (pref_value_store_->SetUserPrefValue(path, new_value)) - pref_notifier_->OnUserPreferenceSet(path); -} - -PrefStore* PrefService::GetExtensionPrefStore() const { - return pref_value_store()->GetExtensionPrefStore(); + pref_value_store_->SetUserPrefValue(path, new_value); } /////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/prefs/pref_service.h b/chrome/browser/prefs/pref_service.h index 18266bc..18c70ed 100644 --- a/chrome/browser/prefs/pref_service.h +++ b/chrome/browser/prefs/pref_service.h @@ -17,15 +17,17 @@ #include "base/values.h" #include "chrome/common/pref_store.h" +class DefaultPrefStore; class FilePath; class NotificationObserver; class PrefChangeObserver; class PrefNotifier; +class PrefNotifierImpl; class PrefValueStore; class Profile; namespace subtle { - class PrefMemberBase; +class PrefMemberBase; }; class PrefService : public NonThreadSafe { @@ -95,11 +97,11 @@ class PrefService : public NonThreadSafe { DISALLOW_COPY_AND_ASSIGN(Preference); }; - // Factory method that creates a new instance of a PrefService with - // a PrefValueStore containing all platform-applicable PrefStores. - // The |pref_filename| points to the user preference file. The |profile| is - // the one to which these preferences apply; it may be NULL if we're dealing - // with the local state. This is the usual way to create a new PrefService. + // Factory method that creates a new instance of a PrefService with the + // applicable PrefStores. The |pref_filename| points to the user preference + // file. The |profile| is the one to which these preferences apply; it may be + // NULL if we're dealing with the local state. This is the usual way to create + // a new PrefService. static PrefService* CreatePrefService(const FilePath& pref_filename, Profile* profile); @@ -109,10 +111,6 @@ class PrefService : public NonThreadSafe { // other types of preferences). static PrefService* CreateUserPrefService(const FilePath& pref_filename); - // This constructor is primarily used by tests. The |pref_value_store| - // provides preference values. - explicit PrefService(PrefValueStore* pref_value_store); - virtual ~PrefService(); // Reloads the data from file. This should only be called when the importer @@ -221,19 +219,33 @@ class PrefService : public NonThreadSafe { bool ReadOnly() const; - PrefNotifier* pref_notifier() const { return pref_notifier_.get(); } - - PrefValueStore* pref_value_store() const { return pref_value_store_.get(); } + // TODO(mnissler): This should not be public. Change client code to call a + // preference setter or use ScopedPrefUpdate. + PrefNotifier* pref_notifier() const; - PrefStore* GetExtensionPrefStore() const; + // Get the extension PrefStore. + PrefStore* GetExtensionPrefStore(); protected: + // Construct a new pref service, specifying the pref sources as explicit + // PrefStore pointers. This constructor is what CreatePrefService() ends up + // calling. It's also used for unit tests. + PrefService(PrefStore* managed_platform_prefs, + PrefStore* device_management_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + Profile* profile); + // 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<PrefNotifier> pref_notifier_; + scoped_ptr<PrefNotifierImpl> pref_notifier_; private: + friend class TestingPrefService; + // 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. @@ -273,6 +285,12 @@ class PrefService : public NonThreadSafe { // and owned by this PrefService. Subclasses may access it for unit testing. scoped_refptr<PrefValueStore> pref_value_store_; + // The extension pref store registered with the PrefValueStore. + PrefStore* extension_store_; + + // Points to the default pref store we passed to the PrefValueStore. + PrefStore* default_store_; + // A set of all the registered Preference objects. PreferenceSet prefs_; diff --git a/chrome/browser/prefs/pref_service_unittest.cc b/chrome/browser/prefs/pref_service_unittest.cc index 7fc4608..6575bdf 100644 --- a/chrome/browser/prefs/pref_service_unittest.cc +++ b/chrome/browser/prefs/pref_service_unittest.cc @@ -12,14 +12,12 @@ #include "chrome/browser/policy/mock_configuration_policy_provider.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/browser/prefs/command_line_pref_store.h" -#include "chrome/browser/prefs/dummy_pref_store.h" +#include "chrome/browser/prefs/testing_pref_store.h" #include "chrome/browser/prefs/pref_change_registrar.h" +#include "chrome/browser/prefs/pref_observer_mock.h" #include "chrome/browser/prefs/pref_value_store.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" -#include "chrome/common/notification_observer_mock.h" -#include "chrome/common/notification_service.h" -#include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/test/testing_pref_service.h" #include "testing/gmock/include/gmock/gmock.h" @@ -27,49 +25,6 @@ using testing::_; using testing::Mock; -using testing::Pointee; -using testing::Property; - -namespace { - -class TestPrefObserver : public NotificationObserver { - public: - TestPrefObserver(const PrefService* prefs, - const std::string& pref_name, - const std::string& new_pref_value) - : observer_fired_(false), - prefs_(prefs), - pref_name_(pref_name), - new_pref_value_(new_pref_value) {} - virtual ~TestPrefObserver() {} - - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - EXPECT_EQ(type.value, NotificationType::PREF_CHANGED); - const PrefService* prefs_in = Source<PrefService>(source).ptr(); - EXPECT_EQ(prefs_in, prefs_); - const std::string* pref_name_in = Details<std::string>(details).ptr(); - EXPECT_EQ(*pref_name_in, pref_name_); - EXPECT_EQ(new_pref_value_, prefs_in->GetString("homepage")); - observer_fired_ = true; - } - - bool observer_fired() { return observer_fired_; } - - void Reset(const std::string& new_pref_value) { - observer_fired_ = false; - new_pref_value_ = new_pref_value; - } - - private: - bool observer_fired_; - const PrefService* prefs_; - const std::string pref_name_; - std::string new_pref_value_; -}; - -} // namespace // TODO(port): port this test to POSIX. #if defined(OS_WIN) @@ -102,33 +57,34 @@ TEST(PrefServiceTest, NoObserverFire) { const char pref_name[] = "homepage"; prefs.RegisterStringPref(pref_name, std::string()); - const std::string new_pref_value("http://www.google.com/"); - TestPrefObserver obs(&prefs, pref_name, new_pref_value); - + const char new_pref_value[] = "http://www.google.com/"; + PrefObserverMock obs; PrefChangeRegistrar registrar; registrar.Init(&prefs); registrar.Add(pref_name, &obs); - // This should fire the checks in TestPrefObserver::Observe. - prefs.SetString(pref_name, new_pref_value); - // Make sure the observer was actually fired. - EXPECT_TRUE(obs.observer_fired()); + // This should fire the checks in PrefObserverMock::Observe. + const StringValue expected_value(new_pref_value); + obs.Expect(&prefs, 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. - obs.Reset(new_pref_value); + EXPECT_CALL(obs, Observe(_, _, _)).Times(0); prefs.SetString(pref_name, new_pref_value); - EXPECT_FALSE(obs.observer_fired()); + Mock::VerifyAndClearExpectations(&obs); // Clearing the pref should cause the pref to fire. - obs.Reset(std::string()); + const StringValue expected_default_value(""); + obs.Expect(&prefs, pref_name, &expected_default_value); prefs.ClearPref(pref_name); - EXPECT_TRUE(obs.observer_fired()); + Mock::VerifyAndClearExpectations(&obs); // Clearing the pref again should not cause the pref to fire. - obs.Reset(std::string()); + EXPECT_CALL(obs, Observe(_, _, _)).Times(0); prefs.ClearPref(pref_name); - EXPECT_FALSE(obs.observer_fired()); + Mock::VerifyAndClearExpectations(&obs); } TEST(PrefServiceTest, HasPrefPath) { @@ -156,35 +112,38 @@ TEST(PrefServiceTest, Observers) { prefs.SetUserPref(pref_name, Value::CreateStringValue("http://www.cnn.com")); prefs.RegisterStringPref(pref_name, std::string()); - const std::string new_pref_value("http://www.google.com/"); - TestPrefObserver obs(&prefs, pref_name, new_pref_value); + const char new_pref_value[] = "http://www.google.com/"; + const StringValue expected_new_pref_value(new_pref_value); + PrefObserverMock obs; PrefChangeRegistrar registrar; registrar.Init(&prefs); registrar.Add(pref_name, &obs); - // This should fire the checks in TestPrefObserver::Observe. - prefs.SetString(pref_name, new_pref_value); - // Make sure the tests were actually run. - EXPECT_TRUE(obs.observer_fired()); + // This should fire the checks in PrefObserverMock::Observe. + obs.Expect(&prefs, pref_name, &expected_new_pref_value); + prefs.SetString(pref_name, new_pref_value); + Mock::VerifyAndClearExpectations(&obs); // Now try adding a second pref observer. - const std::string new_pref_value2("http://www.youtube.com/"); - obs.Reset(new_pref_value2); - TestPrefObserver obs2(&prefs, pref_name, new_pref_value2); + const char new_pref_value2[] = "http://www.youtube.com/"; + const StringValue expected_new_pref_value2(new_pref_value2); + PrefObserverMock obs2; + obs.Expect(&prefs, pref_name, &expected_new_pref_value2); + obs2.Expect(&prefs, pref_name, &expected_new_pref_value2); registrar.Add(pref_name, &obs2); // This should fire the checks in obs and obs2. prefs.SetString(pref_name, new_pref_value2); - EXPECT_TRUE(obs.observer_fired()); - EXPECT_TRUE(obs2.observer_fired()); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); // Make sure obs2 still works after removing obs. registrar.Remove(pref_name, &obs); - obs.Reset(std::string()); - obs2.Reset(new_pref_value); + EXPECT_CALL(obs, Observe(_, _, _)).Times(0); + obs2.Expect(&prefs, pref_name, &expected_new_pref_value); // This should only fire the observer in obs2. prefs.SetString(pref_name, new_pref_value); - EXPECT_FALSE(obs.observer_fired()); - EXPECT_TRUE(obs2.observer_fired()); + Mock::VerifyAndClearExpectations(&obs); + Mock::VerifyAndClearExpectations(&obs2); } TEST(PrefServiceTest, ProxyFromCommandLineNotPolicy) { @@ -340,24 +299,11 @@ class PrefServiceSetValueTest : public testing::Test { static const char kValue[]; PrefServiceSetValueTest() - : name_string_(kName), - null_value_(Value::CreateNullValue()) {} - - void SetExpectNoNotification() { - EXPECT_CALL(observer_, Observe(_, _, _)).Times(0); - } - - void SetExpectPrefChanged() { - EXPECT_CALL(observer_, - Observe(NotificationType(NotificationType::PREF_CHANGED), _, - Property(&Details<std::string>::ptr, - Pointee(name_string_)))); - } + : null_value_(Value::CreateNullValue()) {} TestingPrefService prefs_; - std::string name_string_; scoped_ptr<Value> null_value_; - NotificationObserverMock observer_; + PrefObserverMock observer_; }; const char PrefServiceSetValueTest::kName[] = "name"; @@ -365,8 +311,7 @@ const char PrefServiceSetValueTest::kValue[] = "value"; TEST_F(PrefServiceSetValueTest, SetStringValue) { const char default_string[] = "default"; - const scoped_ptr<Value> default_value( - Value::CreateStringValue(default_string)); + const StringValue default_value(default_string); prefs_.RegisterStringPref(kName, default_string); PrefChangeRegistrar registrar; @@ -374,18 +319,18 @@ TEST_F(PrefServiceSetValueTest, SetStringValue) { registrar.Add(kName, &observer_); // Changing the controlling store from default to user triggers notification. - SetExpectPrefChanged(); - prefs_.Set(kName, *default_value); + observer_.Expect(&prefs_, kName, &default_value); + prefs_.Set(kName, default_value); Mock::VerifyAndClearExpectations(&observer_); - SetExpectNoNotification(); - prefs_.Set(kName, *default_value); + EXPECT_CALL(observer_, Observe(_, _, _)).Times(0); + prefs_.Set(kName, default_value); Mock::VerifyAndClearExpectations(&observer_); - const scoped_ptr<Value> new_value(Value::CreateStringValue(kValue)); - SetExpectPrefChanged(); - prefs_.Set(kName, *new_value); - EXPECT_EQ(kValue, prefs_.GetString(kName)); + StringValue new_value(kValue); + observer_.Expect(&prefs_, kName, &new_value); + prefs_.Set(kName, new_value); + Mock::VerifyAndClearExpectations(&observer_); } TEST_F(PrefServiceSetValueTest, SetDictionaryValue) { @@ -396,30 +341,23 @@ TEST_F(PrefServiceSetValueTest, SetDictionaryValue) { // Dictionary values are special: setting one to NULL is the same as clearing // the user value, allowing the NULL default to take (or keep) control. - SetExpectNoNotification(); + EXPECT_CALL(observer_, Observe(_, _, _)).Times(0); prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); DictionaryValue new_value; new_value.SetString(kName, kValue); - SetExpectPrefChanged(); + observer_.Expect(&prefs_, kName, &new_value); prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); - DictionaryValue* dict = prefs_.GetMutableDictionary(kName); - EXPECT_EQ(1U, dict->size()); - std::string out_value; - dict->GetString(kName, &out_value); - EXPECT_EQ(kValue, out_value); - SetExpectNoNotification(); + EXPECT_CALL(observer_, Observe(_, _, _)).Times(0); prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); - SetExpectPrefChanged(); + observer_.Expect(&prefs_, kName, null_value_.get()); prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); - dict = prefs_.GetMutableDictionary(kName); - EXPECT_EQ(0U, dict->size()); } TEST_F(PrefServiceSetValueTest, SetListValue) { @@ -430,28 +368,21 @@ TEST_F(PrefServiceSetValueTest, SetListValue) { // List values are special: setting one to NULL is the same as clearing the // user value, allowing the NULL default to take (or keep) control. - SetExpectNoNotification(); + EXPECT_CALL(observer_, Observe(_, _, _)).Times(0); prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); ListValue new_value; new_value.Append(Value::CreateStringValue(kValue)); - SetExpectPrefChanged(); + observer_.Expect(&prefs_, kName, &new_value); prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); - const ListValue* list = prefs_.GetMutableList(kName); - ASSERT_EQ(1U, list->GetSize()); - std::string out_value; - list->GetString(0, &out_value); - EXPECT_EQ(kValue, out_value); - SetExpectNoNotification(); + EXPECT_CALL(observer_, Observe(_, _, _)).Times(0); prefs_.Set(kName, new_value); Mock::VerifyAndClearExpectations(&observer_); - SetExpectPrefChanged(); + observer_.Expect(&prefs_, kName, null_value_.get()); prefs_.Set(kName, *null_value_); Mock::VerifyAndClearExpectations(&observer_); - list = prefs_.GetMutableList(kName); - EXPECT_EQ(0U, list->GetSize()); } diff --git a/chrome/browser/prefs/pref_value_store.cc b/chrome/browser/prefs/pref_value_store.cc index b327e33..3dcc813a 100644 --- a/chrome/browser/prefs/pref_value_store.cc +++ b/chrome/browser/prefs/pref_value_store.cc @@ -6,62 +6,69 @@ #include "chrome/browser/browser_thread.h" #include "chrome/browser/policy/configuration_policy_pref_store.h" -#include "chrome/browser/prefs/command_line_pref_store.h" -#include "chrome/browser/prefs/in_memory_pref_store.h" -#include "chrome/common/json_pref_store.h" +#include "chrome/browser/prefs/pref_notifier.h" #include "chrome/common/notification_service.h" -namespace { +PrefValueStore::PrefStoreKeeper::PrefStoreKeeper() + : pref_value_store_(NULL), + type_(PrefValueStore::INVALID_STORE) { +} -// Returns true if the actual value is a valid type for the expected type when -// found in the given store. -bool IsValidType(Value::ValueType expected, Value::ValueType actual, - PrefNotifier::PrefStoreType store) { - if (expected == actual) - return true; +PrefValueStore::PrefStoreKeeper::~PrefStoreKeeper() { + if (pref_store_.get()) + pref_store_->RemoveObserver(this); +} - // Dictionaries and lists are allowed to hold TYPE_NULL values too, but only - // in the default pref store. - if (store == PrefNotifier::DEFAULT_STORE && - actual == Value::TYPE_NULL && - (expected == Value::TYPE_DICTIONARY || expected == Value::TYPE_LIST)) { - return true; - } - return false; +void PrefValueStore::PrefStoreKeeper::Initialize( + PrefValueStore* store, + PrefStore* pref_store, + PrefValueStore::PrefStoreType type) { + if (pref_store_.get()) + pref_store_->RemoveObserver(this); + type_ = type; + pref_value_store_ = store; + pref_store_.reset(pref_store); + if (pref_store_.get()) + pref_store_->AddObserver(this); } -} // namespace +void PrefValueStore::PrefStoreKeeper::OnPrefValueChanged( + const std::string& key) { + pref_value_store_->OnPrefValueChanged(type_, key); +} -// static -PrefValueStore* PrefValueStore::CreatePrefValueStore( - const FilePath& pref_filename, - Profile* profile, - bool user_only) { - using policy::ConfigurationPolicyPrefStore; - ConfigurationPolicyPrefStore* managed = NULL; - ConfigurationPolicyPrefStore* device_management = NULL; - CommandLinePrefStore* command_line = NULL; - ConfigurationPolicyPrefStore* recommended = NULL; - - JsonPrefStore* user = new JsonPrefStore( - pref_filename, - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); - InMemoryPrefStore* extension = new InMemoryPrefStore(); - InMemoryPrefStore* default_store = new InMemoryPrefStore(); - - if (!user_only) { - managed = - ConfigurationPolicyPrefStore::CreateManagedPlatformPolicyPrefStore(); - device_management = - ConfigurationPolicyPrefStore::CreateDeviceManagementPolicyPrefStore( - profile); - command_line = new CommandLinePrefStore(CommandLine::ForCurrentProcess()); - recommended = - ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore(); - } - return new PrefValueStore(managed, device_management, extension, - command_line, user, recommended, default_store, - profile); +void PrefValueStore::PrefStoreKeeper::OnInitializationCompleted() { + pref_value_store_->OnInitializationCompleted(type_); +} + +PrefValueStore::PrefValueStore(PrefStore* managed_platform_prefs, + PrefStore* device_management_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier, + Profile* profile) + : pref_notifier_(pref_notifier), + profile_(profile) { + // NULL default pref store is usually bad, but may be OK for some unit tests. + if (!default_prefs) + LOG(WARNING) << "default pref store is null"; + InitPrefStore(MANAGED_PLATFORM_STORE, managed_platform_prefs); + InitPrefStore(DEVICE_MANAGEMENT_STORE, device_management_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); + + // TODO(mnissler): Remove after policy refresh cleanup. + registrar_.Add(this, + NotificationType(NotificationType::POLICY_CHANGED), + NotificationService::AllSources()); + + CheckInitializationCompleted(); } PrefValueStore::~PrefValueStore() {} @@ -70,9 +77,8 @@ bool PrefValueStore::GetValue(const std::string& name, Value** out_value) const { // Check the |PrefStore|s in order of their priority from highest to lowest // to find the value of the preference described by the given preference name. - for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) { - if (GetValueFromStore(name.c_str(), - static_cast<PrefNotifier::PrefStoreType>(i), + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + if (GetValueFromStore(name.c_str(), static_cast<PrefStoreType>(i), out_value)) return true; } @@ -81,7 +87,7 @@ bool PrefValueStore::GetValue(const std::string& name, bool PrefValueStore::GetUserValue(const std::string& name, Value** out_value) const { - return GetValueFromStore(name.c_str(), PrefNotifier::USER_STORE, out_value); + return GetValueFromStore(name.c_str(), USER_STORE, out_value); } void PrefValueStore::RegisterPreferenceType(const std::string& name, @@ -99,25 +105,28 @@ Value::ValueType PrefValueStore::GetRegisteredType( bool PrefValueStore::WritePrefs() { bool success = true; - for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) { - if (pref_stores_[i].get()) - success = pref_stores_[i]->WritePrefs() && success; + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(i)); + if (store) + success = store->WritePrefs() && success; } return success; } void PrefValueStore::ScheduleWritePrefs() { - for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) { - if (pref_stores_[i].get()) - pref_stores_[i]->ScheduleWritePrefs(); + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(i)); + if (store) + store->ScheduleWritePrefs(); } } PrefStore::PrefReadError PrefValueStore::ReadPrefs() { PrefStore::PrefReadError result = PrefStore::PREF_READ_ERROR_NONE; - for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) { - if (pref_stores_[i].get()) { - PrefStore::PrefReadError this_error = pref_stores_[i]->ReadPrefs(); + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(i)); + if (store) { + PrefStore::PrefReadError this_error = store->ReadPrefs(); if (result == PrefStore::PREF_READ_ERROR_NONE) result = this_error; } @@ -143,158 +152,188 @@ bool PrefValueStore::HasPrefPath(const char* path) const { return rv && !PrefValueFromDefaultStore(path); } -bool PrefValueStore::PrefHasChanged(const char* path, - PrefNotifier::PrefStoreType new_store) { - DCHECK(new_store != PrefNotifier::INVALID_STORE); - // If we get a change notification about an unregistered preference, - // discard it silently because there cannot be any listeners. - if (pref_types_.find(path) == pref_types_.end()) - return false; - - // Replying that the pref has changed may cause problems, but it's the safer - // choice. - if (new_store == PrefNotifier::INVALID_STORE) - return true; +void PrefValueStore::NotifyPrefChanged( + const char* path, + PrefValueStore::PrefStoreType new_store) { + DCHECK(new_store != INVALID_STORE); - PrefNotifier::PrefStoreType controller = ControllingPrefStoreForPref(path); - DCHECK(controller != PrefNotifier::INVALID_STORE) - << "Invalid controller for path " << path; - if (controller == PrefNotifier::INVALID_STORE) - return true; + // If this pref is not registered, just discard the notification. + if (!pref_types_.count(path)) + return; - // If the pref is controlled by a higher-priority store, its effective value - // cannot have changed. - if (controller < new_store) - return false; + bool changed = true; + // Replying that the pref has changed in case the new store is invalid may + // cause problems, but it's the safer choice. + if (new_store != INVALID_STORE) { + PrefStoreType controller = ControllingPrefStoreForPref(path); + DCHECK(controller != INVALID_STORE); + // If the pref is controlled by a higher-priority store, its effective value + // cannot have changed. + if (controller != INVALID_STORE && + controller < new_store) { + changed = false; + } + } - // Otherwise, we take the pref store's word that something changed. - return true; + if (changed) + pref_notifier_->OnPreferenceChanged(path); } -// Note the |DictionaryValue| referenced by the |PrefStore| USER_STORE -// (returned by the method prefs()) takes the ownership of the Value referenced -// by in_value. -bool PrefValueStore::SetUserPrefValue(const char* name, Value* in_value) { +void PrefValueStore::SetUserPrefValue(const char* name, Value* in_value) { + DCHECK(in_value); Value* old_value = NULL; - pref_stores_[PrefNotifier::USER_STORE]->prefs()->Get(name, &old_value); - bool value_changed = !(old_value && old_value->Equals(in_value)); + GetPrefStore(USER_STORE)->prefs()->Get(name, &old_value); + bool value_changed = !old_value || !old_value->Equals(in_value); + GetPrefStore(USER_STORE)->prefs()->Set(name, in_value); - pref_stores_[PrefNotifier::USER_STORE]->prefs()->Set(name, in_value); - return value_changed; + if (value_changed) + NotifyPrefChanged(name, USER_STORE); } -// Note the |DictionaryValue| referenced by the |PrefStore| DEFAULT_STORE -// (returned by the method prefs()) takes the ownership of the Value referenced -// by in_value. -void PrefValueStore::SetDefaultPrefValue(const char* name, Value* in_value) { - pref_stores_[PrefNotifier::DEFAULT_STORE]->prefs()->Set(name, in_value); +void PrefValueStore::SetUserPrefValueSilently(const char* name, + Value* in_value) { + DCHECK(in_value); + GetPrefStore(USER_STORE)->prefs()->Set(name, in_value); } -bool PrefValueStore::ReadOnly() { - return pref_stores_[PrefNotifier::USER_STORE]->ReadOnly(); +bool PrefValueStore::ReadOnly() const { + return GetPrefStore(USER_STORE)->ReadOnly(); } -bool PrefValueStore::RemoveUserPrefValue(const char* name) { - if (pref_stores_[PrefNotifier::USER_STORE].get()) { - return pref_stores_[PrefNotifier::USER_STORE]->prefs()->Remove(name, NULL); +void PrefValueStore::RemoveUserPrefValue(const char* name) { + if (GetPrefStore(USER_STORE)) { + if (GetPrefStore(USER_STORE)->prefs()->Remove(name, NULL)) + NotifyPrefChanged(name, USER_STORE); } - return false; } bool PrefValueStore::PrefValueInManagedPlatformStore(const char* name) const { - return PrefValueInStore(name, PrefNotifier::MANAGED_PLATFORM_STORE); + return PrefValueInStore(name, MANAGED_PLATFORM_STORE); } bool PrefValueStore::PrefValueInDeviceManagementStore(const char* name) const { - return PrefValueInStore(name, PrefNotifier::DEVICE_MANAGEMENT_STORE); + return PrefValueInStore(name, DEVICE_MANAGEMENT_STORE); } bool PrefValueStore::PrefValueInExtensionStore(const char* name) const { - return PrefValueInStore(name, PrefNotifier::EXTENSION_STORE); + return PrefValueInStore(name, EXTENSION_STORE); } bool PrefValueStore::PrefValueInUserStore(const char* name) const { - return PrefValueInStore(name, PrefNotifier::USER_STORE); -} - -bool PrefValueStore::PrefValueInStoreRange( - const char* name, - PrefNotifier::PrefStoreType first_checked_store, - PrefNotifier::PrefStoreType last_checked_store) { - 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<PrefNotifier::PrefStoreType>(i))) - return true; - } - return false; + return PrefValueInStore(name, USER_STORE); } bool PrefValueStore::PrefValueFromExtensionStore(const char* name) const { - return ControllingPrefStoreForPref(name) == PrefNotifier::EXTENSION_STORE; + return ControllingPrefStoreForPref(name) == EXTENSION_STORE; } bool PrefValueStore::PrefValueFromUserStore(const char* name) const { - return ControllingPrefStoreForPref(name) == PrefNotifier::USER_STORE; + return ControllingPrefStoreForPref(name) == USER_STORE; } bool PrefValueStore::PrefValueFromDefaultStore(const char* name) const { - return ControllingPrefStoreForPref(name) == PrefNotifier::DEFAULT_STORE; + return ControllingPrefStoreForPref(name) == DEFAULT_STORE; } bool PrefValueStore::PrefValueUserModifiable(const char* name) const { - PrefNotifier::PrefStoreType effective_store = - ControllingPrefStoreForPref(name); - return effective_store >= PrefNotifier::USER_STORE || - effective_store == PrefNotifier::INVALID_STORE; + PrefStoreType effective_store = ControllingPrefStoreForPref(name); + return effective_store >= USER_STORE || + effective_store == INVALID_STORE; } -PrefNotifier::PrefStoreType PrefValueStore::ControllingPrefStoreForPref( - const char* name) const { - for (int i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) { - if (PrefValueInStore(name, static_cast<PrefNotifier::PrefStoreType>(i))) - return static_cast<PrefNotifier::PrefStoreType>(i); +bool PrefValueStore::HasPolicyConflictingUserProxySettings() const { + using policy::ConfigurationPolicyPrefStore; + ConfigurationPolicyPrefStore::ProxyPreferenceSet proxy_prefs; + ConfigurationPolicyPrefStore::GetProxyPreferenceSet(&proxy_prefs); + ConfigurationPolicyPrefStore::ProxyPreferenceSet::const_iterator i; + for (i = proxy_prefs.begin(); i != proxy_prefs.end(); ++i) { + if ((PrefValueInManagedPlatformStore(*i) || + PrefValueInDeviceManagementStore(*i)) && + PrefValueInStoreRange(*i, + COMMAND_LINE_STORE, + USER_STORE)) + return true; + } + return false; +} + +// Returns true if the actual value is a valid type for the expected type when +// found in the given store. +bool PrefValueStore::IsValidType(Value::ValueType expected, + Value::ValueType actual, + PrefValueStore::PrefStoreType store) { + if (expected == actual) + return true; + + // Dictionaries and lists are allowed to hold TYPE_NULL values too, but only + // in the default pref store. + if (store == DEFAULT_STORE && + actual == Value::TYPE_NULL && + (expected == Value::TYPE_DICTIONARY || expected == Value::TYPE_LIST)) { + return true; } - return PrefNotifier::INVALID_STORE; + return false; } bool PrefValueStore::PrefValueInStore( const char* name, - PrefNotifier::PrefStoreType store) const { + PrefValueStore::PrefStoreType store) const { // Declare a temp Value* and call GetValueFromStore, // ignoring the output value. Value* tmp_value = NULL; return GetValueFromStore(name, store, &tmp_value); } -bool PrefValueStore::GetValueFromStore( +bool PrefValueStore::PrefValueInStoreRange( const char* name, - PrefNotifier::PrefStoreType store, - Value** out_value) const { + 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 char* 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 char* name, + PrefValueStore::PrefStoreType store_type, + 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. - if (pref_stores_[store].get() && - pref_stores_[store]->prefs()->Get(name, out_value)) { - // If the value is the sentinel that redirects to the default - // store, re-fetch the value from the default store explicitly. - // Because the default values are not available when creating - // stores, the default value must be fetched dynamically for every - // redirect. + const PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(store_type)); + if (store && store->prefs()->Get(name, out_value)) { + // If the value is the sentinel that redirects to the default store, + // re-fetch the value from the default store explicitly. Because the default + // values are not available when creating stores, the default value must be + // fetched dynamically for every redirect. if (PrefStore::IsUseDefaultSentinelValue(*out_value)) { - DCHECK(pref_stores_[PrefNotifier::DEFAULT_STORE].get()); - if (!pref_stores_[PrefNotifier::DEFAULT_STORE]->prefs()->Get(name, - out_value)) { + store = GetPrefStore(DEFAULT_STORE); + if (!store || !store->prefs()->Get(name, out_value)) { *out_value = NULL; return false; } - store = PrefNotifier::DEFAULT_STORE; + store_type = DEFAULT_STORE; } - if (IsValidType(GetRegisteredType(name), (*out_value)->GetType(), store)) + if (IsValidType(GetRegisteredType(name), + (*out_value)->GetType(), + store_type)) { return true; + } } // No valid value found for the given preference name: set the return false. *out_value = NULL; @@ -305,9 +344,7 @@ void PrefValueStore::RefreshPolicyPrefsOnFileThread( BrowserThread::ID calling_thread_id, PrefStore* new_managed_platform_pref_store, PrefStore* new_device_management_pref_store, - PrefStore* new_recommended_pref_store, - AfterRefreshCallback* callback_pointer) { - scoped_ptr<AfterRefreshCallback> callback(callback_pointer); + PrefStore* new_recommended_pref_store) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); scoped_ptr<PrefStore> managed_platform_pref_store( new_managed_platform_pref_store); @@ -343,30 +380,54 @@ void PrefValueStore::RefreshPolicyPrefsOnFileThread( &PrefValueStore::RefreshPolicyPrefsCompletion, managed_platform_pref_store.release(), device_management_pref_store.release(), - recommended_pref_store.release(), - callback.release())); + recommended_pref_store.release())); +} + +void PrefValueStore::RefreshPolicyPrefs() { + using policy::ConfigurationPolicyPrefStore; + // Because loading of policy information must happen on the FILE + // thread, it's not possible to just replace the contents of the + // managed and recommended stores in place due to possible + // concurrent access from the UI thread. Instead, new stores are + // created and the refreshed policy read into them. The new stores + // are swapped with the old from a Task on the UI thread after the + // load is complete. + PrefStore* new_managed_platform_pref_store( + ConfigurationPolicyPrefStore::CreateManagedPlatformPolicyPrefStore()); + PrefStore* new_device_management_pref_store( + ConfigurationPolicyPrefStore::CreateDeviceManagementPolicyPrefStore( + profile_)); + PrefStore* new_recommended_pref_store( + ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore()); + BrowserThread::ID current_thread_id; + CHECK(BrowserThread::GetCurrentThreadIdentifier(¤t_thread_id)); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, + &PrefValueStore::RefreshPolicyPrefsOnFileThread, + current_thread_id, + new_managed_platform_pref_store, + new_device_management_pref_store, + new_recommended_pref_store)); } void PrefValueStore::RefreshPolicyPrefsCompletion( PrefStore* new_managed_platform_pref_store, PrefStore* new_device_management_pref_store, - PrefStore* new_recommended_pref_store, - AfterRefreshCallback* callback_pointer) { - scoped_ptr<AfterRefreshCallback> callback(callback_pointer); - + PrefStore* new_recommended_pref_store) { // Determine the paths of all the changed preferences values in the three // policy-related stores (managed platform, device management and // recommended). DictionaryValue* managed_platform_prefs_before( - pref_stores_[PrefNotifier::MANAGED_PLATFORM_STORE]->prefs()); + GetPrefStore(MANAGED_PLATFORM_STORE)->prefs()); DictionaryValue* managed_platform_prefs_after( new_managed_platform_pref_store->prefs()); DictionaryValue* device_management_prefs_before( - pref_stores_[PrefNotifier::DEVICE_MANAGEMENT_STORE]->prefs()); + GetPrefStore(DEVICE_MANAGEMENT_STORE)->prefs()); DictionaryValue* device_management_prefs_after( new_device_management_pref_store->prefs()); DictionaryValue* recommended_prefs_before( - pref_stores_[PrefNotifier::RECOMMENDED_STORE]->prefs()); + GetPrefStore(RECOMMENDED_STORE)->prefs()); DictionaryValue* recommended_prefs_after(new_recommended_pref_store->prefs()); std::vector<std::string> changed_managed_platform_paths; @@ -412,84 +473,45 @@ void PrefValueStore::RefreshPolicyPrefsCompletion( // Replace the old stores with the new and send notification of the changed // preferences. - pref_stores_[PrefNotifier::MANAGED_PLATFORM_STORE].reset( - new_managed_platform_pref_store); - pref_stores_[PrefNotifier::DEVICE_MANAGEMENT_STORE].reset( - new_device_management_pref_store); - pref_stores_[PrefNotifier::RECOMMENDED_STORE].reset( - new_recommended_pref_store); - callback->Run(changed_paths); + InitPrefStore(MANAGED_PLATFORM_STORE, new_managed_platform_pref_store); + InitPrefStore(DEVICE_MANAGEMENT_STORE, new_device_management_pref_store); + InitPrefStore(RECOMMENDED_STORE, new_recommended_pref_store); + + std::vector<std::string>::const_iterator current; + for (current = changed_paths.begin(); + current != changed_paths.end(); + ++current) { + pref_notifier_->OnPreferenceChanged(current->c_str()); + } } -void PrefValueStore::RefreshPolicyPrefs( - AfterRefreshCallback* callback) { - using policy::ConfigurationPolicyPrefStore; - // Because loading of policy information must happen on the FILE - // thread, it's not possible to just replace the contents of the - // managed and recommended stores in place due to possible - // concurrent access from the UI thread. Instead, new stores are - // created and the refreshed policy read into them. The new stores - // are swapped with the old from a Task on the UI thread after the - // load is complete. - PrefStore* new_managed_platform_pref_store( - ConfigurationPolicyPrefStore::CreateManagedPlatformPolicyPrefStore()); - PrefStore* new_device_management_pref_store( - ConfigurationPolicyPrefStore::CreateDeviceManagementPolicyPrefStore( - profile_)); - PrefStore* new_recommended_pref_store( - ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore()); - BrowserThread::ID current_thread_id; - CHECK(BrowserThread::GetCurrentThreadIdentifier(¤t_thread_id)); - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - NewRunnableMethod(this, - &PrefValueStore::RefreshPolicyPrefsOnFileThread, - current_thread_id, - new_managed_platform_pref_store, - new_device_management_pref_store, - new_recommended_pref_store, - callback)); +void PrefValueStore::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::POLICY_CHANGED) + RefreshPolicyPrefs(); } -bool PrefValueStore::HasPolicyConflictingUserProxySettings() { - using policy::ConfigurationPolicyPrefStore; - ConfigurationPolicyPrefStore::ProxyPreferenceSet proxy_prefs; - ConfigurationPolicyPrefStore::GetProxyPreferenceSet(&proxy_prefs); - ConfigurationPolicyPrefStore::ProxyPreferenceSet::const_iterator i; - for (i = proxy_prefs.begin(); i != proxy_prefs.end(); ++i) { - if ((PrefValueInManagedPlatformStore(*i) || - PrefValueInDeviceManagementStore(*i)) && - PrefValueInStoreRange(*i, - PrefNotifier::COMMAND_LINE_STORE, - PrefNotifier::USER_STORE)) - return true; - } - return false; +void PrefValueStore::OnPrefValueChanged(PrefValueStore::PrefStoreType type, + const std::string& key) { + NotifyPrefChanged(key.c_str(), type); } -PrefStore* PrefValueStore::GetExtensionPrefStore() const { - return pref_stores_[PrefNotifier::EXTENSION_STORE].get(); +void PrefValueStore::OnInitializationCompleted( + PrefValueStore::PrefStoreType type) { + CheckInitializationCompleted(); } -PrefValueStore::PrefValueStore(PrefStore* managed_platform_prefs, - PrefStore* device_management_prefs, - PrefStore* extension_prefs, - PrefStore* command_line_prefs, - PrefStore* user_prefs, - PrefStore* recommended_prefs, - PrefStore* default_prefs, - Profile* profile) - : profile_(profile) { - // NULL default pref store is usually bad, but may be OK for some unit tests. - if (!default_prefs) - LOG(WARNING) << "default pref store is null"; - pref_stores_[PrefNotifier::MANAGED_PLATFORM_STORE].reset( - managed_platform_prefs); - pref_stores_[PrefNotifier::DEVICE_MANAGEMENT_STORE].reset( - device_management_prefs); - pref_stores_[PrefNotifier::EXTENSION_STORE].reset(extension_prefs); - pref_stores_[PrefNotifier::COMMAND_LINE_STORE].reset(command_line_prefs); - pref_stores_[PrefNotifier::USER_STORE].reset(user_prefs); - pref_stores_[PrefNotifier::RECOMMENDED_STORE].reset(recommended_prefs); - pref_stores_[PrefNotifier::DEFAULT_STORE].reset(default_prefs); +void PrefValueStore::InitPrefStore(PrefValueStore::PrefStoreType type, + PrefStore* pref_store) { + pref_stores_[type].Initialize(this, pref_store, type); +} + +void PrefValueStore::CheckInitializationCompleted() { + for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) { + PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(i)); + if (store && !store->IsInitializationComplete()) + return; + } + pref_notifier_->OnInitializationCompleted(); } diff --git a/chrome/browser/prefs/pref_value_store.h b/chrome/browser/prefs/pref_value_store.h index f4884e2..1724bbc 100644 --- a/chrome/browser/prefs/pref_value_store.h +++ b/chrome/browser/prefs/pref_value_store.h @@ -17,10 +17,12 @@ #include "base/scoped_ptr.h" #include "base/values.h" #include "chrome/browser/browser_thread.h" -#include "chrome/browser/prefs/pref_notifier.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "chrome/common/pref_store.h" class FilePath; +class PrefNotifier; class PrefStore; class Profile; @@ -28,26 +30,47 @@ class Profile; // (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. -// See PrefNotifier for a list of the available preference sources (PrefStores) -// and their descriptions. // // Unless otherwise explicitly noted, all of the methods of this class must // be called on the UI thread. -class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { +class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore>, + public NotificationObserver { public: - // Returns a new PrefValueStore with all applicable PrefStores. The - // |pref_filename| points to the user preference file. The |profile| is the - // one to which these preferences apply; it may be NULL if we're dealing - // with the local state. If |pref_filename| is empty, the user PrefStore will - // not be created. If |user_only| is true, no PrefStores will be created - // other than the user and default PrefStores. This should not normally be - // called directly: the usual way to create a PrefValueStore is by creating a - // PrefService. - static PrefValueStore* CreatePrefValueStore(const FilePath& pref_filename, - Profile* profile, - bool user_only); - - ~PrefValueStore(); + // In decreasing order of precedence: + // |managed_platform_prefs| contains all managed platform (non-cloud policy) + // preference values. + // |device_management_prefs| contains all device management (cloud policy) + // preference values. + // |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 recommended (policy) preference values. + // |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. + // + // The |profile| parameter is used to construct a replacement device + // management pref store. This is done after policy refresh when we swap out + // the policy pref stores for new ones, so the |profile| pointer needs to be + // kept around for then. It is safe to pass a NULL pointer for local state + // preferences. + // + // TODO(mnissler, danno): Refactor the pref store interface and refresh logic + // so refreshes can be handled by the pref store itself without swapping + // stores. This way we can get rid of the profile pointer here. + PrefValueStore(PrefStore* managed_platform_prefs, + PrefStore* device_management_prefs, + PrefStore* extension_prefs, + PrefStore* command_line_prefs, + PrefStore* user_prefs, + PrefStore* recommended_prefs, + PrefStore* default_prefs, + PrefNotifier* pref_notifier, + Profile* profile); + virtual ~PrefValueStore(); // Gets the value for the given preference name that has a valid value type; // that is, the same type the preference was registered with, or NULL for @@ -56,7 +79,7 @@ class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { // Preference::GetValue() instead of calling this method directly. bool GetValue(const std::string& name, Value** out_value) const; - // Same as GetValue but only searches USER_STORE. + // Same as GetValue but only searches the user store. bool GetUserValue(const std::string& name, Value** out_value) const; // Adds a preference to the mapping of names to types. @@ -74,10 +97,12 @@ class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { // Persists prefs (to disk or elsewhere). Returns true if writing values was // successful. In practice, only the user prefs are expected to be written // out. + // TODO(mnissler, danno): Handle writes through PrefService and remove. bool WritePrefs(); // Calls the method ScheduleWritePrefs on the PrefStores. In practice, only // the user prefs are expected to be written out. + // TODO(mnissler, danno): Handle writes through PrefService and remove. void ScheduleWritePrefs(); // Returns true if the PrefValueStore contains the given preference (i.e., @@ -87,37 +112,35 @@ class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { // store setting a value that happens to be equal to the default does. bool HasPrefPath(const char* name) const; - // Called by the PrefNotifier when the value of the preference at |path| has - // changed, been added, or been removed in one of the PrefStores. The - // |new_store| is the PrefStoreType of the caller. Returns true if the - // effective value of the preference has changed, or if the store controlling - // the pref has changed. Virtual so it can be mocked for a unit test. - virtual bool PrefHasChanged(const char* path, - PrefNotifier::PrefStoreType new_store); - // Returns true if the PrefValueStore is read-only. Because the managed // platform, device management and recommended PrefStores are always // read-only, the PrefValueStore as a whole is read-only if the PrefStore // containing the user preferences is read-only. - bool ReadOnly(); + bool ReadOnly() const; // Alters the user-defined value of a preference. Even if the preference is // managed this method allows the user-defined value of the preference to be - // set. But GetValue calls will not return this value as long as the - // preference is managed. Instead GetValue will return the managed value - // of the preference. Note that the PrefValueStore takes the ownership of - // the value referenced by |in_value|. It is an error to call this when no - // user PrefStore has been set. Returns true if the user-set value of the - // preference was newly added or changed. - bool SetUserPrefValue(const char* name, Value* in_value); - - // Removes a value from the user PrefStore. If a preference is managed - // this function should have no visible effect. Returns true if there was a - // user-set value to be removed. - bool RemoveUserPrefValue(const char* name); - - // Sets a value in the DefaultPrefStore, which takes ownership of the Value. - void SetDefaultPrefValue(const char* name, Value* in_value); + // set. However, GetValue calls will not return this value as long as the + // preference is overriden by a store of higher precedence. Note that the + // PrefValueStore takes the ownership of the value referenced by |in_value|. + // It is an error to call this when no user PrefStore has been set. Triggers + // notifications if the user-visible value changes. + // TODO(mnissler, danno): Handle writes in PrefService and notifications in + // the pref store implementation, so we can remove this call. + void SetUserPrefValue(const char* name, Value* in_value); + + // Like SetUserPrefValue, but silently puts the value without triggering + // notifications. + // TODO(mnissler, danno): Handle writes in PrefService and notifications in + // the pref store implementation, so we can remove this call. + void SetUserPrefValueSilently(const char* name, Value* in_value); + + // Removes a value from the user PrefStore. If a preference is overriden by a + // store of higher precedence, this function will have no immediately visible + // effect. Triggers notifications if the user-visible value changes. + // TODO(mnissler, danno): Handle writes in PrefService and notifications in + // the pref store implementation, so we can remove this call. + void RemoveUserPrefValue(const char* name); // 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 @@ -127,14 +150,6 @@ class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { bool PrefValueInExtensionStore(const char* name) const; bool PrefValueInUserStore(const char* name) 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 char* name, - PrefNotifier::PrefStoreType first_checked_store, - PrefNotifier::PrefStoreType last_checked_store); - // 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. @@ -146,98 +161,129 @@ class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { // there is no higher-priority source controlling it. bool PrefValueUserModifiable(const char* name) const; - // Returns the pref store type identifying the source that controls the - // Preference identified by |name|. If none of the sources has a value, - // PrefNotifier::INVALID_STORE is returned. In practice, the default PrefStore - // should always have a value for any registered preferencem, so INVALID_STORE - // indicates an error. - PrefNotifier::PrefStoreType ControllingPrefStoreForPref( - const char* name) const; - - // Signature of callback triggered after policy refresh. Parameter is not - // passed as reference to prevent passing along a pointer to a set whose - // lifecycle is managed in another thread. - typedef Callback1<std::vector<std::string> >::Type AfterRefreshCallback; - - // Called as a result of a notification of policy change. Triggers a reload of - // managed platform, device management and recommended preferences from policy - // from a Task on the FILE thread. The Task will take ownership of the - // |callback|. |callback| is called with the set of preferences changed by the - // policy refresh. |callback| is called on the caller's thread as a Task - // after RefreshPolicyPrefs has returned. - void RefreshPolicyPrefs(AfterRefreshCallback* callback); - // Returns true if there are proxy preferences in user-modifiable // preference stores (e.g. CommandLinePrefStore, ExtensionPrefStore) // that conflict with proxy settings specified by proxy policy. - bool HasPolicyConflictingUserProxySettings(); - - // TODO(mnissler) delete after applying your patch. - // This is only called only by PrefService. - PrefStore* GetExtensionPrefStore() const; - - protected: - // In decreasing order of precedence: - // |managed_platform_prefs| contains all managed platform (non-cloud policy) - // preference values. - // |device_management_prefs| contains all device management (cloud policy) - // preference values. - // |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 recommended (policy) preference values. - // |default_prefs| contains application-default preference values. It must - // be non-null if any preferences are to be registered. - // - // The |profile| parameter is used to construct a replacement device - // management pref store. This is done after policy refresh when we swap out - // the policy pref stores for new ones, so the |profile| pointer needs to be - // kept around for then. It is safe to pass a NULL pointer for local state - // preferences. - // - // TODO(mnissler, danno): Refactor the pref store interface and refresh logic - // so refreshes can be handled by the pref store itself without swapping - // stores. This way we can get rid of the profile pointer here. - // - // This constructor should only be used internally, or by subclasses in - // testing. The usual way to create a PrefValueStore is by creating a - // PrefService. - PrefValueStore(PrefStore* managed_platform_prefs, - PrefStore* device_management_prefs, - PrefStore* extension_prefs, - PrefStore* command_line_prefs, - PrefStore* user_prefs, - PrefStore* recommended_prefs, - PrefStore* default_prefs, - Profile* profile); + bool HasPolicyConflictingUserProxySettings() const; private: + // PrefStores must be listed here in order from highest to lowest priority. + // MANAGED_PLATFORM contains all managed preference values that are + // provided by a platform-specific policy mechanism (e.g. Windows + // Group Policy). + // DEVICE_MANAGEMENT contains all managed preference values supplied + // by the device management server (cloud policy). + // 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 recommended (policy) preference values. + // 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_PLATFORM_STORE = 0, + DEVICE_MANAGEMENT_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::ObserverInterface { + public: + PrefStoreKeeper(); + virtual ~PrefStoreKeeper(); + + // 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::ObserverInterface implementation. + virtual void OnPrefValueChanged(const std::string& key); + virtual void OnInitializationCompleted(); + + // PrefValueStore this keeper is part of. + PrefValueStore* pref_value_store_; + + // The PrefStore managed by this keeper. + scoped_ptr<PrefStore> pref_store_; + + // Type of the pref store. + PrefStoreType type_; + + DISALLOW_COPY_AND_ASSIGN(PrefStoreKeeper); + }; + typedef std::map<std::string, Value::ValueType> PrefTypeMap; friend class PrefValueStoreTest; + FRIEND_TEST_ALL_PREFIXES(PrefValueStoreTest, TestPolicyRefresh); FRIEND_TEST_ALL_PREFIXES(PrefValueStoreTest, TestRefreshPolicyPrefsCompletion); + FRIEND_TEST_ALL_PREFIXES(PrefValueStoreTest, + TestConcurrentPolicyRefresh); + + // Returns true if the actual type is a valid type for the expected type when + // found in the given store. + static bool IsValidType(Value::ValueType expected, + Value::ValueType actual, + PrefStoreType store); // 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 char* name, - PrefNotifier::PrefStoreType store) const; + bool PrefValueInStore(const char* 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 char* 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 char* name) const; // Get a value from the specified store type. bool GetValueFromStore(const char* name, - PrefNotifier::PrefStoreType store, + PrefStoreType store, 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 char* path, PrefStoreType new_store); + + // Called as a result of a notification of policy change. Triggers a reload of + // managed platform, device management and recommended preferences from policy + // from a Task on the FILE thread. + void RefreshPolicyPrefs(); + // Called during policy refresh after ReadPrefs completes on the thread // that initiated the policy refresh. RefreshPolicyPrefsCompletion takes // ownership of the |callback| object. void RefreshPolicyPrefsCompletion( PrefStore* new_managed_platform_pref_store, PrefStore* new_device_management_pref_store, - PrefStore* new_recommended_pref_store, - AfterRefreshCallback* callback); + PrefStore* new_recommended_pref_store); // Called during policy refresh to do the ReadPrefs on the FILE thread. // RefreshPolicyPrefsOnFileThread takes ownership of the |callback| object. @@ -245,10 +291,44 @@ class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { BrowserThread::ID calling_thread_id, PrefStore* new_managed_platform_pref_store, PrefStore* new_device_management_pref_store, - PrefStore* new_recommended_pref_store, - AfterRefreshCallback* callback); + PrefStore* new_recommended_pref_store); + + // NotificationObserver methods: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // 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); - scoped_ptr<PrefStore> pref_stores_[PrefNotifier::PREF_STORE_TYPE_MAX + 1]; + // Handle the event that the store for |type| has completed initialization. + void OnInitializationCompleted(PrefStoreType type); + + // 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]; + + // Used for generating PREF_CHANGED and PREF_INITIALIZATION_COMPLETED + // 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_; @@ -258,6 +338,9 @@ class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { // upon policy refresh. Profile* profile_; + // TODO(mnissler): Remove this after cleaning up policy refresh handling. + NotificationRegistrar registrar_; + DISALLOW_COPY_AND_ASSIGN(PrefValueStore); }; diff --git a/chrome/browser/prefs/pref_value_store_unittest.cc b/chrome/browser/prefs/pref_value_store_unittest.cc index b67fc05..851a6bb 100644 --- a/chrome/browser/prefs/pref_value_store_unittest.cc +++ b/chrome/browser/prefs/pref_value_store_unittest.cc @@ -2,26 +2,49 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <set> +#include <string> + #include "base/scoped_ptr.h" #include "base/values.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/policy/configuration_policy_pref_store.h" -#include "chrome/browser/prefs/dummy_pref_store.h" +#include "chrome/browser/prefs/pref_notifier.h" #include "chrome/browser/prefs/pref_value_store.h" +#include "chrome/browser/prefs/testing_pref_store.h" #include "chrome/common/pref_names.h" -#include "chrome/test/testing_pref_value_store.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using testing::_; +using testing::AnyNumber; using testing::Mock; +using testing::Invoke; namespace { -class MockPolicyRefreshCallback { +// Records preference changes. +class PrefChangeRecorder { + public: + void Record(const std::string& pref_name) { + changed_prefs_.insert(pref_name); + } + + void Clear() { + changed_prefs_.clear(); + } + + const std::set<std::string>& changed_prefs() { return changed_prefs_; } + + private: + std::set<std::string> changed_prefs_; +}; + +// Allows to capture pref notifications through gmock. +class MockPrefNotifier : public PrefNotifier { public: - MockPolicyRefreshCallback() {} - MOCK_METHOD1(DoCallback, void(const std::vector<std::string>)); + MOCK_METHOD1(OnPreferenceChanged, void(const std::string&)); + MOCK_METHOD0(OnInitializationCompleted, void()); }; } // namespace @@ -83,44 +106,26 @@ const std::string kSearchProviderNameValue = "AreYouFeelingExtraLucky"; class PrefValueStoreTest : public testing::Test { protected: virtual void SetUp() { - // Create dummy user preferences. - managed_platform_prefs_= CreateManagedPlatformPrefs(); - device_management_prefs_ = CreateDeviceManagementPrefs(); - extension_prefs_ = CreateExtensionPrefs(); - command_line_prefs_ = CreateCommandLinePrefs(); - user_prefs_ = CreateUserPrefs(); - recommended_prefs_ = CreateRecommendedPrefs(); - default_prefs_ = CreateDefaultPrefs(); - - std::sort(expected_differing_paths_.begin(), - expected_differing_paths_.end()); - - // Create |DummyPrefStore|s. - managed_platform_pref_store_ = new DummyPrefStore(); - managed_platform_pref_store_->set_prefs(managed_platform_prefs_); - device_management_pref_store_ = new DummyPrefStore(); - device_management_pref_store_->set_prefs(device_management_prefs_); - extension_pref_store_ = new DummyPrefStore(); - extension_pref_store_->set_prefs(extension_prefs_); - command_line_pref_store_ = new DummyPrefStore(); - command_line_pref_store_->set_prefs(command_line_prefs_); - user_pref_store_ = new DummyPrefStore(); - user_pref_store_->set_read_only(false); - user_pref_store_->set_prefs(user_prefs_); - recommended_pref_store_ = new DummyPrefStore(); - recommended_pref_store_->set_prefs(recommended_prefs_); - default_pref_store_ = new DummyPrefStore(); - default_pref_store_->set_prefs(default_prefs_); + // Create TestingPrefStores. + CreateManagedPlatformPrefs(); + CreateDeviceManagementPrefs(); + CreateExtensionPrefs(); + CreateCommandLinePrefs(); + CreateUserPrefs(); + CreateRecommendedPrefs(); + CreateDefaultPrefs(); // Create a new pref-value-store. - pref_value_store_ = new TestingPrefValueStore( + pref_value_store_ = new PrefValueStore( managed_platform_pref_store_, device_management_pref_store_, extension_pref_store_, command_line_pref_store_, user_pref_store_, recommended_pref_store_, - default_pref_store_); + default_pref_store_, + &pref_notifier_, + NULL); // Register prefs with the PrefValueStore. pref_value_store_->RegisterPreferenceType(prefs::kApplicationLocale, @@ -153,90 +158,84 @@ class PrefValueStoreTest : public testing::Test { // Creates a new dictionary and stores some sample user preferences // in it. - DictionaryValue* CreateUserPrefs() { - DictionaryValue* user_prefs = new DictionaryValue(); - user_prefs->SetBoolean(prefs::kDeleteCache, user_pref::kDeleteCacheValue); - user_prefs->SetInteger(prefs::kStabilityLaunchCount, - user_pref::kStabilityLaunchCountValue); - user_prefs->SetString(prefs::kCurrentThemeID, + void CreateUserPrefs() { + user_pref_store_ = new TestingPrefStore; + user_pref_store_->prefs()->SetBoolean(prefs::kDeleteCache, + user_pref::kDeleteCacheValue); + user_pref_store_->prefs()->SetInteger(prefs::kStabilityLaunchCount, + user_pref::kStabilityLaunchCountValue); + user_pref_store_->prefs()->SetString(prefs::kCurrentThemeID, user_pref::kCurrentThemeIDValue); - user_prefs->SetString(prefs::kApplicationLocale, + user_pref_store_->prefs()->SetString(prefs::kApplicationLocale, user_pref::kApplicationLocaleValue); - user_prefs->SetString(prefs::kDefaultSearchProviderName, + user_pref_store_->prefs()->SetString(prefs::kDefaultSearchProviderName, user_pref::kSearchProviderNameValue); - user_prefs->SetString(prefs::kHomePage, user_pref::kHomepageValue); - return user_prefs; + user_pref_store_->prefs()->SetString(prefs::kHomePage, + user_pref::kHomepageValue); } - DictionaryValue* CreateManagedPlatformPrefs() { - DictionaryValue* managed_platform_prefs = new DictionaryValue(); - managed_platform_prefs->SetString( + void CreateManagedPlatformPrefs() { + managed_platform_pref_store_ = new TestingPrefStore; + managed_platform_pref_store_->prefs()->SetString( prefs::kHomePage, managed_platform_pref::kHomepageValue); - expected_differing_paths_.push_back(prefs::kHomePage); - return managed_platform_prefs; + expected_differing_paths_.insert(prefs::kHomePage); } - DictionaryValue* CreateDeviceManagementPrefs() { - DictionaryValue* device_management_prefs = new DictionaryValue(); - device_management_prefs->SetString( + void CreateDeviceManagementPrefs() { + device_management_pref_store_ = new TestingPrefStore; + device_management_pref_store_->prefs()->SetString( prefs::kDefaultSearchProviderName, device_management_pref::kSearchProviderNameValue); - expected_differing_paths_.push_back("default_search_provider"); - expected_differing_paths_.push_back(prefs::kDefaultSearchProviderName); - device_management_prefs->SetString(prefs::kHomePage, - device_management_pref::kHomepageValue); - return device_management_prefs; + expected_differing_paths_.insert("default_search_provider"); + expected_differing_paths_.insert(prefs::kDefaultSearchProviderName); + device_management_pref_store_->prefs()->SetString(prefs::kHomePage, + device_management_pref::kHomepageValue); } - DictionaryValue* CreateExtensionPrefs() { - DictionaryValue* extension_prefs = new DictionaryValue(); - extension_prefs->SetString(prefs::kCurrentThemeID, + void CreateExtensionPrefs() { + extension_pref_store_ = new TestingPrefStore; + extension_pref_store_->prefs()->SetString(prefs::kCurrentThemeID, extension_pref::kCurrentThemeIDValue); - extension_prefs->SetString(prefs::kHomePage, + extension_pref_store_->prefs()->SetString(prefs::kHomePage, extension_pref::kHomepageValue); - extension_prefs->SetString(prefs::kDefaultSearchProviderName, - extension_pref::kSearchProviderNameValue); - return extension_prefs; + extension_pref_store_->prefs()->SetString(prefs::kDefaultSearchProviderName, + extension_pref::kSearchProviderNameValue); } - DictionaryValue* CreateCommandLinePrefs() { - DictionaryValue* command_line_prefs = new DictionaryValue(); - command_line_prefs->SetString(prefs::kCurrentThemeID, + void CreateCommandLinePrefs() { + command_line_pref_store_ = new TestingPrefStore; + command_line_pref_store_->prefs()->SetString(prefs::kCurrentThemeID, command_line_pref::kCurrentThemeIDValue); - command_line_prefs->SetString(prefs::kApplicationLocale, + command_line_pref_store_->prefs()->SetString(prefs::kApplicationLocale, command_line_pref::kApplicationLocaleValue); - command_line_prefs->SetString(prefs::kHomePage, + command_line_pref_store_->prefs()->SetString(prefs::kHomePage, command_line_pref::kHomepageValue); - command_line_prefs->SetString( + command_line_pref_store_->prefs()->SetString( prefs::kDefaultSearchProviderName, command_line_pref::kSearchProviderNameValue); - return command_line_prefs; } - DictionaryValue* CreateRecommendedPrefs() { - DictionaryValue* recommended_prefs = new DictionaryValue(); - recommended_prefs->SetInteger(prefs::kStabilityLaunchCount, + void CreateRecommendedPrefs() { + recommended_pref_store_ = new TestingPrefStore; + recommended_pref_store_->prefs()->SetInteger(prefs::kStabilityLaunchCount, recommended_pref::kStabilityLaunchCountValue); - recommended_prefs->SetBoolean( + recommended_pref_store_->prefs()->SetBoolean( prefs::kRecommendedPref, recommended_pref::kRecommendedPrefValue); - // Expected differing paths must be added in lexicographic order - // to work properly - expected_differing_paths_.push_back("this"); - expected_differing_paths_.push_back("this.pref"); - expected_differing_paths_.push_back(prefs::kRecommendedPref); - expected_differing_paths_.push_back("user_experience_metrics"); - expected_differing_paths_.push_back("user_experience_metrics.stability"); - expected_differing_paths_.push_back(prefs::kStabilityLaunchCount); - return recommended_prefs; + expected_differing_paths_.insert("this"); + expected_differing_paths_.insert("this.pref"); + expected_differing_paths_.insert(prefs::kRecommendedPref); + expected_differing_paths_.insert("user_experience_metrics"); + expected_differing_paths_.insert("user_experience_metrics.stability"); + expected_differing_paths_.insert(prefs::kStabilityLaunchCount); } - DictionaryValue* CreateDefaultPrefs() { - DictionaryValue* default_prefs = new DictionaryValue(); - default_prefs->SetInteger(prefs::kDefaultPref, default_pref::kDefaultValue); - return default_prefs; + void CreateDefaultPrefs() { + default_pref_store_ = new TestingPrefStore; + default_pref_store_->prefs()->SetInteger(prefs::kDefaultPref, + default_pref::kDefaultValue); } DictionaryValue* CreateSampleDictValue() { @@ -261,32 +260,23 @@ class PrefValueStoreTest : public testing::Test { } MessageLoop loop_; - - scoped_refptr<TestingPrefValueStore> pref_value_store_; + MockPrefNotifier pref_notifier_; + scoped_refptr<PrefValueStore> pref_value_store_; // |PrefStore|s are owned by the |PrefValueStore|. - DummyPrefStore* managed_platform_pref_store_; - DummyPrefStore* device_management_pref_store_; - DummyPrefStore* extension_pref_store_; - DummyPrefStore* command_line_pref_store_; - DummyPrefStore* user_pref_store_; - DummyPrefStore* recommended_pref_store_; - DummyPrefStore* default_pref_store_; + TestingPrefStore* managed_platform_pref_store_; + TestingPrefStore* device_management_pref_store_; + TestingPrefStore* extension_pref_store_; + TestingPrefStore* command_line_pref_store_; + TestingPrefStore* user_pref_store_; + TestingPrefStore* recommended_pref_store_; + TestingPrefStore* default_pref_store_; // A vector of the preferences paths in the managed and recommended // PrefStores that are set at the beginning of a test. Can be modified // by the test to track changes that it makes to the preferences // stored in the managed and recommended PrefStores. - std::vector<std::string> expected_differing_paths_; - - // Preferences are owned by the individual |DummyPrefStores|. - DictionaryValue* managed_platform_prefs_; - DictionaryValue* device_management_prefs_; - DictionaryValue* extension_prefs_; - DictionaryValue* command_line_prefs_; - DictionaryValue* user_prefs_; - DictionaryValue* recommended_prefs_; - DictionaryValue* default_prefs_; + std::set<std::string> expected_differing_paths_; private: scoped_ptr<BrowserThread> ui_thread_; @@ -427,7 +417,7 @@ TEST_F(PrefValueStoreTest, HasPrefPath) { EXPECT_FALSE(pref_value_store_->HasPrefPath(prefs::kMissingPref)); } -TEST_F(PrefValueStoreTest, PrefHasChanged) { +TEST_F(PrefValueStoreTest, PrefChanges) { // Setup. const char managed_platform_pref_path[] = "managed_platform_pref"; pref_value_store_->RegisterPreferenceType(managed_platform_pref_path, @@ -443,32 +433,60 @@ TEST_F(PrefValueStoreTest, PrefHasChanged) { default_pref_store_->prefs()->SetString(default_pref_path, "default value"); // Check pref controlled by highest-priority store. - EXPECT_TRUE(pref_value_store_->PrefHasChanged(managed_platform_pref_path, - static_cast<PrefNotifier::PrefStoreType>(0))); - EXPECT_FALSE(pref_value_store_->PrefHasChanged(managed_platform_pref_path, - PrefNotifier::USER_STORE)); + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(managed_platform_pref_path)); + managed_platform_pref_store_->NotifyPrefValueChanged( + managed_platform_pref_path); + Mock::VerifyAndClearExpectations(&pref_notifier_); + + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)).Times(0); + user_pref_store_->NotifyPrefValueChanged(managed_platform_pref_path); + Mock::VerifyAndClearExpectations(&pref_notifier_); // Check pref controlled by user store. - EXPECT_TRUE(pref_value_store_->PrefHasChanged(user_pref_path, - static_cast<PrefNotifier::PrefStoreType>(0))); - EXPECT_TRUE(pref_value_store_->PrefHasChanged(user_pref_path, - PrefNotifier::USER_STORE)); - EXPECT_FALSE(pref_value_store_->PrefHasChanged(user_pref_path, - PrefNotifier::PREF_STORE_TYPE_MAX)); + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(user_pref_path)); + managed_platform_pref_store_->NotifyPrefValueChanged(user_pref_path); + Mock::VerifyAndClearExpectations(&pref_notifier_); + + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(user_pref_path)); + user_pref_store_->NotifyPrefValueChanged(user_pref_path); + Mock::VerifyAndClearExpectations(&pref_notifier_); + + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)).Times(0); + default_pref_store_->NotifyPrefValueChanged(user_pref_path); + Mock::VerifyAndClearExpectations(&pref_notifier_); // Check pref controlled by default-pref store. - EXPECT_TRUE(pref_value_store_->PrefHasChanged(default_pref_path, - PrefNotifier::USER_STORE)); - EXPECT_TRUE(pref_value_store_->PrefHasChanged(default_pref_path, - PrefNotifier::DEFAULT_STORE)); + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(default_pref_path)); + user_pref_store_->NotifyPrefValueChanged(default_pref_path); + Mock::VerifyAndClearExpectations(&pref_notifier_); + + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(default_pref_path)); + default_pref_store_->NotifyPrefValueChanged(default_pref_path); + Mock::VerifyAndClearExpectations(&pref_notifier_); +} + +TEST_F(PrefValueStoreTest, OnInitializationCompleted) { + EXPECT_CALL(pref_notifier_, OnInitializationCompleted()).Times(0); + managed_platform_pref_store_->SetInitializationCompleted(); + device_management_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()).Times(1); + user_pref_store_->SetInitializationCompleted(); + Mock::VerifyAndClearExpectations(&pref_notifier_); } TEST_F(PrefValueStoreTest, ReadPrefs) { pref_value_store_->ReadPrefs(); - // The ReadPrefs method of the |DummyPrefStore| deletes the |pref_store|s + // The ReadPrefs method of the |TestingPrefStore| deletes the |pref_store|s // internal dictionary and creates a new empty dictionary. Hence this // dictionary does not contain any of the preloaded preferences. - // This shows that the ReadPrefs method of the |DummyPrefStore| was called. + // This shows that the ReadPrefs method of the |TestingPrefStore| was called. EXPECT_FALSE(pref_value_store_->HasPrefPath(prefs::kDeleteCache)); } @@ -483,18 +501,20 @@ TEST_F(PrefValueStoreTest, SetUserPrefValue) { Value* actual_value = NULL; // Test that managed platform values can not be set. + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)).Times(0); ASSERT_TRUE(pref_value_store_->PrefValueInManagedPlatformStore( prefs::kHomePage)); - // The Ownership is tranfered to |PrefValueStore|. + // The ownership is transfered to PrefValueStore. new_value = Value::CreateStringValue("http://www.youtube.com"); pref_value_store_->SetUserPrefValue(prefs::kHomePage, new_value); + Mock::VerifyAndClearExpectations(&pref_notifier_); ASSERT_TRUE(pref_value_store_->GetValue(prefs::kHomePage, &actual_value)); std::string value_str; actual_value->GetAsString(&value_str); ASSERT_EQ(managed_platform_pref::kHomepageValue, value_str); - // User preferences values can be set + // User preferences values can be set. ASSERT_FALSE(pref_value_store_->PrefValueInManagedPlatformStore( prefs::kStabilityLaunchCount)); actual_value = NULL; @@ -503,15 +523,19 @@ TEST_F(PrefValueStoreTest, SetUserPrefValue) { EXPECT_TRUE(actual_value->GetAsInteger(&int_value)); EXPECT_EQ(user_pref::kStabilityLaunchCountValue, int_value); + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)).Times(1); new_value = Value::CreateIntegerValue(1); pref_value_store_->SetUserPrefValue(prefs::kStabilityLaunchCount, new_value); actual_value = NULL; pref_value_store_->GetValue(prefs::kStabilityLaunchCount, &actual_value); EXPECT_TRUE(new_value->Equals(actual_value)); + Mock::VerifyAndClearExpectations(&pref_notifier_); - // Set and Get |DictionaryValue| + // Set and Get DictionaryValue. + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)).Times(1); DictionaryValue* expected_dict_value = CreateSampleDictValue(); pref_value_store_->SetUserPrefValue(prefs::kSampleDict, expected_dict_value); + Mock::VerifyAndClearExpectations(&pref_notifier_); actual_value = NULL; std::string key(prefs::kSampleDict); @@ -520,9 +544,11 @@ TEST_F(PrefValueStoreTest, SetUserPrefValue) { ASSERT_EQ(expected_dict_value, actual_value); ASSERT_TRUE(expected_dict_value->Equals(actual_value)); - // Set and Get a |ListValue| + // Set and Get a ListValue. + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)).Times(1); ListValue* expected_list_value = CreateSampleListValue(); pref_value_store_->SetUserPrefValue(prefs::kSampleList, expected_list_value); + Mock::VerifyAndClearExpectations(&pref_notifier_); actual_value = NULL; key = prefs::kSampleList; @@ -640,8 +666,9 @@ TEST_F(PrefValueStoreTest, DetectProxyConfigurationConflict) { // Create conflicting proxy settings in the managed and command-line // preference stores. - command_line_prefs_->SetBoolean(prefs::kProxyAutoDetect, false); - managed_platform_prefs_->SetBoolean(prefs::kProxyAutoDetect, true); + command_line_pref_store_->prefs()->SetBoolean(prefs::kProxyAutoDetect, false); + managed_platform_pref_store_->prefs()->SetBoolean(prefs::kProxyAutoDetect, + true); ASSERT_TRUE(pref_value_store_->HasPolicyConflictingUserProxySettings()); } @@ -746,140 +773,121 @@ TEST_F(PrefValueStoreTest, TestPolicyRefresh) { // replacing them with dummy stores, all of the paths of the prefs originally // in the managed platform, device management and recommended stores should // change. - MockPolicyRefreshCallback callback; - EXPECT_CALL(callback, DoCallback(_)).Times(0); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableMethod( - pref_value_store_.get(), - &PrefValueStore::RefreshPolicyPrefs, - NewCallback(&callback, - &MockPolicyRefreshCallback::DoCallback))); - Mock::VerifyAndClearExpectations(&callback); - EXPECT_CALL(callback, DoCallback(expected_differing_paths_)).Times(1); + pref_value_store_->RefreshPolicyPrefs(); + + PrefChangeRecorder recorder; + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)) + .WillRepeatedly(Invoke(&recorder, &PrefChangeRecorder::Record)); loop_.RunAllPending(); + EXPECT_EQ(expected_differing_paths_, recorder.changed_prefs()); } TEST_F(PrefValueStoreTest, TestRefreshPolicyPrefsCompletion) { + PrefChangeRecorder recorder; + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)) + .WillRepeatedly(Invoke(&recorder, &PrefChangeRecorder::Record)); + // Test changed preferences in the managed platform store and removed // preferences in the recommended store. In addition to "homepage", the other // prefs that are set by default in the test class are removed by the // DummyStore. - scoped_ptr<DummyPrefStore> new_managed_platform_store(new DummyPrefStore()); + scoped_ptr<TestingPrefStore> new_managed_platform_store( + new TestingPrefStore()); DictionaryValue* dict = new DictionaryValue(); dict->SetString("homepage", "some other changed homepage"); new_managed_platform_store->set_prefs(dict); - MockPolicyRefreshCallback callback; - EXPECT_CALL(callback, DoCallback(expected_differing_paths_)).Times(1); + + recorder.Clear(); pref_value_store_->RefreshPolicyPrefsCompletion( new_managed_platform_store.release(), - new DummyPrefStore(), - new DummyPrefStore(), - NewCallback(&callback, - &MockPolicyRefreshCallback::DoCallback)); + new TestingPrefStore(), + new TestingPrefStore()); + EXPECT_EQ(expected_differing_paths_, recorder.changed_prefs()); // Test properties that have been removed from the managed platform store. // Homepage is still set in managed prefs. expected_differing_paths_.clear(); - expected_differing_paths_.push_back(std::string("homepage")); - MockPolicyRefreshCallback callback2; - EXPECT_CALL(callback2, DoCallback(expected_differing_paths_)).Times(1); + expected_differing_paths_.insert(prefs::kHomePage); + + recorder.Clear(); pref_value_store_->RefreshPolicyPrefsCompletion( - new DummyPrefStore(), - new DummyPrefStore(), - new DummyPrefStore(), - NewCallback(&callback2, - &MockPolicyRefreshCallback::DoCallback)); + new TestingPrefStore(), + new TestingPrefStore(), + new TestingPrefStore()); + EXPECT_EQ(expected_differing_paths_, recorder.changed_prefs()); // Test properties that are added to the device management store. expected_differing_paths_.clear(); - expected_differing_paths_.push_back(std::string("homepage")); - scoped_ptr<DummyPrefStore> new_device_management_store( - new DummyPrefStore()); + expected_differing_paths_.insert(prefs::kHomePage); + scoped_ptr<TestingPrefStore> new_device_management_store( + new TestingPrefStore()); dict = new DictionaryValue(); dict->SetString("homepage", "some other changed homepage"); new_device_management_store->set_prefs(dict); - MockPolicyRefreshCallback callback3; - EXPECT_CALL(callback3, DoCallback(expected_differing_paths_)).Times(1); + + recorder.Clear(); pref_value_store_->RefreshPolicyPrefsCompletion( - new DummyPrefStore(), + new TestingPrefStore(), new_device_management_store.release(), - new DummyPrefStore(), - NewCallback(&callback3, - &MockPolicyRefreshCallback::DoCallback)); + new TestingPrefStore()); + EXPECT_EQ(expected_differing_paths_, recorder.changed_prefs()); // Test properties that are added to the recommended store. - scoped_ptr<DummyPrefStore> new_recommended_store(new DummyPrefStore()); + scoped_ptr<TestingPrefStore> new_recommended_store(new TestingPrefStore()); dict = new DictionaryValue(); dict->SetString("homepage", "some other changed homepage 2"); new_recommended_store->set_prefs(dict); expected_differing_paths_.clear(); - expected_differing_paths_.push_back(std::string("homepage")); - MockPolicyRefreshCallback callback4; - EXPECT_CALL(callback4, DoCallback(expected_differing_paths_)).Times(1); + expected_differing_paths_.insert(prefs::kHomePage); + + recorder.Clear(); pref_value_store_->RefreshPolicyPrefsCompletion( - new DummyPrefStore(), - new DummyPrefStore(), - new_recommended_store.release(), - NewCallback(&callback4, - &MockPolicyRefreshCallback::DoCallback)); + new TestingPrefStore(), + new TestingPrefStore(), + new_recommended_store.release()); + EXPECT_EQ(expected_differing_paths_, recorder.changed_prefs()); // Test adding a multi-key path. - new_managed_platform_store.reset(new DummyPrefStore()); + new_managed_platform_store.reset(new TestingPrefStore()); dict = new DictionaryValue(); dict->SetString("segment1.segment2", "value"); new_managed_platform_store->set_prefs(dict); expected_differing_paths_.clear(); - expected_differing_paths_.push_back(std::string("homepage")); - expected_differing_paths_.push_back(std::string("segment1")); - expected_differing_paths_.push_back(std::string("segment1.segment2")); - MockPolicyRefreshCallback callback5; - EXPECT_CALL(callback5, DoCallback(expected_differing_paths_)).Times(1); + expected_differing_paths_.insert(prefs::kHomePage); + expected_differing_paths_.insert("segment1"); + expected_differing_paths_.insert("segment1.segment2"); + + recorder.Clear(); pref_value_store_->RefreshPolicyPrefsCompletion( new_managed_platform_store.release(), - new DummyPrefStore(), - new DummyPrefStore(), - NewCallback(&callback5, - &MockPolicyRefreshCallback::DoCallback)); + new TestingPrefStore(), + new TestingPrefStore()); + EXPECT_EQ(expected_differing_paths_, recorder.changed_prefs()); } TEST_F(PrefValueStoreTest, TestConcurrentPolicyRefresh) { - MockPolicyRefreshCallback callback1; + PrefChangeRecorder recorder; + EXPECT_CALL(pref_notifier_, OnPreferenceChanged(_)) + .WillRepeatedly(Invoke(&recorder, &PrefChangeRecorder::Record)); + BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod( pref_value_store_.get(), - &PrefValueStore::RefreshPolicyPrefs, - NewCallback(&callback1, - &MockPolicyRefreshCallback::DoCallback))); - EXPECT_CALL(callback1, DoCallback(_)).Times(0); + &PrefValueStore::RefreshPolicyPrefs)); - MockPolicyRefreshCallback callback2; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod( pref_value_store_.get(), - &PrefValueStore::RefreshPolicyPrefs, - NewCallback(&callback2, - &MockPolicyRefreshCallback::DoCallback))); - EXPECT_CALL(callback2, DoCallback(_)).Times(0); + &PrefValueStore::RefreshPolicyPrefs)); - MockPolicyRefreshCallback callback3; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod( pref_value_store_.get(), - &PrefValueStore::RefreshPolicyPrefs, - NewCallback(&callback3, - &MockPolicyRefreshCallback::DoCallback))); - EXPECT_CALL(callback3, DoCallback(_)).Times(0); - Mock::VerifyAndClearExpectations(&callback1); - Mock::VerifyAndClearExpectations(&callback2); - Mock::VerifyAndClearExpectations(&callback3); - - EXPECT_CALL(callback1, DoCallback(expected_differing_paths_)).Times(1); - std::vector<std::string> no_differing_paths; - EXPECT_CALL(callback2, DoCallback(no_differing_paths)).Times(1); - EXPECT_CALL(callback3, DoCallback(no_differing_paths)).Times(1); + &PrefValueStore::RefreshPolicyPrefs)); + loop_.RunAllPending(); + EXPECT_EQ(expected_differing_paths_, recorder.changed_prefs()); } diff --git a/chrome/browser/prefs/scoped_pref_update.cc b/chrome/browser/prefs/scoped_pref_update.cc index 05a607c..024d71b 100644 --- a/chrome/browser/prefs/scoped_pref_update.cc +++ b/chrome/browser/prefs/scoped_pref_update.cc @@ -12,5 +12,8 @@ ScopedPrefUpdate::ScopedPrefUpdate(PrefService* service, const char* path) path_(path) {} ScopedPrefUpdate::~ScopedPrefUpdate() { - service_->pref_notifier()->FireObservers(path_.c_str()); + // TODO(mnissler, danno): This sends a notification unconditionally, which is + // wrong. We should rather tell the PrefService that the user pref got + // updated. + service_->pref_notifier()->OnPreferenceChanged(path_.c_str()); } diff --git a/chrome/browser/prefs/testing_pref_store.cc b/chrome/browser/prefs/testing_pref_store.cc new file mode 100644 index 0000000..5194737 --- /dev/null +++ b/chrome/browser/prefs/testing_pref_store.cc @@ -0,0 +1,36 @@ +// 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 "chrome/browser/prefs/testing_pref_store.h" + +#include "base/values.h" + +TestingPrefStore::TestingPrefStore() + : prefs_(new DictionaryValue()), + read_only_(true), + prefs_written_(false), + init_complete_(false) { } + +PrefStore::PrefReadError TestingPrefStore::ReadPrefs() { + prefs_.reset(new DictionaryValue()); + return PrefStore::PREF_READ_ERROR_NONE; +} + +bool TestingPrefStore::WritePrefs() { + prefs_written_ = true; + return prefs_written_; +} + +void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) { + PrefStoreBase::NotifyPrefValueChanged(key); +} + +void TestingPrefStore::NotifyInitializationCompleted() { + PrefStoreBase::NotifyInitializationCompleted(); +} + +void TestingPrefStore::SetInitializationCompleted() { + init_complete_ = true; + NotifyInitializationCompleted(); +} diff --git a/chrome/browser/prefs/testing_pref_store.h b/chrome/browser/prefs/testing_pref_store.h new file mode 100644 index 0000000..fe014f4 --- /dev/null +++ b/chrome/browser/prefs/testing_pref_store.h @@ -0,0 +1,63 @@ +// 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 CHROME_BROWSER_PREFS_TESTING_PREF_STORE_H_ +#define CHROME_BROWSER_PREFS_TESTING_PREF_STORE_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "chrome/common/pref_store_base.h" + +class DictionaryValue; + +// |TestingPrefStore| is a stub implementation of the |PrefStore| interface. +// It allows to get and set the state of the |PrefStore| as well as triggering +// notifications. +class TestingPrefStore : public PrefStoreBase { + public: + TestingPrefStore(); + virtual ~TestingPrefStore() {} + + virtual DictionaryValue* prefs() const { return prefs_.get(); } + + virtual PrefStore::PrefReadError ReadPrefs(); + + virtual bool ReadOnly() const { return read_only_; } + + virtual bool WritePrefs(); + + // Getter and Setter methods for setting and getting the state of the + // |DummyPrefStore|. + virtual void set_read_only(bool read_only) { read_only_ = read_only; } + virtual void set_prefs(DictionaryValue* prefs) { prefs_.reset(prefs); } + virtual void set_prefs_written(bool status) { prefs_written_ = status; } + virtual bool get_prefs_written() { return prefs_written_; } + + // Publish these functions so testing code can call them. + virtual void NotifyPrefValueChanged(const std::string& key); + virtual void NotifyInitializationCompleted(); + + // Whether the store has completed all asynchronous initialization. + virtual bool IsInitializationComplete() { return init_complete_; } + + // Mark the store as having completed initialization. + void SetInitializationCompleted(); + + private: + scoped_ptr<DictionaryValue> prefs_; + + // Flag that indicates if the PrefStore is read-only + bool read_only_; + + // Flag that indicates if the method WritePrefs was called. + bool prefs_written_; + + // Whether initialization has been completed. + bool init_complete_; + + DISALLOW_COPY_AND_ASSIGN(TestingPrefStore); +}; + +#endif // CHROME_BROWSER_PREFS_TESTING_PREF_STORE_H_ |