diff options
author | danno@chromium.org <danno@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-04 15:31:38 +0000 |
---|---|---|
committer | danno@chromium.org <danno@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-04 15:31:38 +0000 |
commit | 61f972be4f7409243bfd81abd41facb31f421550 (patch) | |
tree | 6bd79ad823720c1c98553a946f4522ec336379be /chrome | |
parent | eb9afc859fc8df63b52de503c0aa3e690e674963 (diff) | |
download | chromium_src-61f972be4f7409243bfd81abd41facb31f421550.zip chromium_src-61f972be4f7409243bfd81abd41facb31f421550.tar.gz chromium_src-61f972be4f7409243bfd81abd41facb31f421550.tar.bz2 |
Changing policy while Chrome is running should refresh preferences without relaunching
Includes windows-only Group Policy watcher that triggers policy refresh.
BUG=45324
TEST=--gtest_filter=PrefValueStoreTest.* and manual
Review URL: http://codereview.chromium.org/2858060
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54902 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/configuration_policy_provider.cc | 9 | ||||
-rw-r--r-- | chrome/browser/configuration_policy_provider.h | 5 | ||||
-rw-r--r-- | chrome/browser/configuration_policy_provider_win.cc | 38 | ||||
-rw-r--r-- | chrome/browser/configuration_policy_provider_win.h | 22 | ||||
-rw-r--r-- | chrome/browser/configuration_policy_provider_win_unittest.cc | 6 | ||||
-rw-r--r-- | chrome/browser/pref_service.cc | 35 | ||||
-rw-r--r-- | chrome/browser/pref_service.h | 30 | ||||
-rw-r--r-- | chrome/browser/pref_value_store.cc | 84 | ||||
-rw-r--r-- | chrome/browser/pref_value_store.h | 46 | ||||
-rw-r--r-- | chrome/browser/pref_value_store_unittest.cc | 210 | ||||
-rw-r--r-- | chrome/common/notification_type.h | 5 |
11 files changed, 461 insertions, 29 deletions
diff --git a/chrome/browser/configuration_policy_provider.cc b/chrome/browser/configuration_policy_provider.cc index 31930a0..3f1f436 100644 --- a/chrome/browser/configuration_policy_provider.cc +++ b/chrome/browser/configuration_policy_provider.cc @@ -6,6 +6,7 @@ #include "base/values.h" #include "chrome/common/policy_constants.h" +#include "chrome/common/notification_service.h" namespace { @@ -70,3 +71,11 @@ const ConfigurationPolicyProvider::PolicyValueMap* } return mapping; } + +void ConfigurationPolicyProvider::NotifyStoreOfPolicyChange() { + NotificationService::current()->Notify( + NotificationType::POLICY_CHANGED, + Source<ConfigurationPolicyProvider>(this), + NotificationService::NoDetails()); +} + diff --git a/chrome/browser/configuration_policy_provider.h b/chrome/browser/configuration_policy_provider.h index 7cb60f6..ae4ad73 100644 --- a/chrome/browser/configuration_policy_provider.h +++ b/chrome/browser/configuration_policy_provider.h @@ -30,6 +30,11 @@ class ConfigurationPolicyProvider { // Returns true if the policy could be provided, otherwise false. virtual bool Provide(ConfigurationPolicyStore* store) = 0; + // Called by the subclass provider at any time to indicate that the currently + // applied policy is not longer current. A policy refresh will be initiated as + // soon as possible. + virtual void NotifyStoreOfPolicyChange(); + protected: // A structure mapping policies to their implementations by providers. struct PolicyValueMapEntry { diff --git a/chrome/browser/configuration_policy_provider_win.cc b/chrome/browser/configuration_policy_provider_win.cc index 61d264c..b70f01d 100644 --- a/chrome/browser/configuration_policy_provider_win.cc +++ b/chrome/browser/configuration_policy_provider_win.cc @@ -4,18 +4,56 @@ #include "chrome/browser/configuration_policy_provider_win.h" +#include <userenv.h> + #include <algorithm> #include "base/logging.h" +#include "base/object_watcher.h" #include "base/registry.h" #include "base/scoped_ptr.h" #include "base/string_piece.h" #include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" #include "base/values.h" +#include "base/waitable_event.h" #include "chrome/common/policy_constants.h" +ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: + GroupPolicyChangeWatcher(ConfigurationPolicyProvider* provider) + : provider_(provider), + user_policy_changed_event_(false, false), + machine_policy_changed_event_(false, false) { + CHECK(RegisterGPNotification(user_policy_changed_event_.handle(), false)); + CHECK(RegisterGPNotification(machine_policy_changed_event_.handle(), true)); + user_policy_watcher_.StartWatching( + user_policy_changed_event_.handle(), + this); + machine_policy_watcher_.StartWatching( + machine_policy_changed_event_.handle(), + this); +} + +void ConfigurationPolicyProviderWin::GroupPolicyChangeWatcher:: + OnObjectSignaled(HANDLE object) { + provider_->NotifyStoreOfPolicyChange(); + if (object == user_policy_changed_event_.handle()) { + user_policy_watcher_.StartWatching( + user_policy_changed_event_.handle(), + this); + } else if (object == machine_policy_changed_event_.handle()) { + machine_policy_watcher_.StartWatching( + machine_policy_changed_event_.handle(), + this); + } else { + // This method should only be called as a result of signals + // to the user- and machine-policy handle watchers. + NOTREACHED(); + } +} + ConfigurationPolicyProviderWin::ConfigurationPolicyProviderWin() { + watcher_.reset(new GroupPolicyChangeWatcher(this)); } bool ConfigurationPolicyProviderWin::GetRegistryPolicyString( diff --git a/chrome/browser/configuration_policy_provider_win.h b/chrome/browser/configuration_policy_provider_win.h index 874a9cc..522b538 100644 --- a/chrome/browser/configuration_policy_provider_win.h +++ b/chrome/browser/configuration_policy_provider_win.h @@ -6,6 +6,9 @@ #define CHROME_BROWSER_CONFIGURATION_POLICY_PROVIDER_WIN_H_ #pragma once +#include "base/object_watcher.h" +#include "base/scoped_ptr.h" +#include "base/waitable_event.h" #include "chrome/browser/configuration_policy_store.h" #include "chrome/browser/configuration_policy_provider.h" @@ -17,6 +20,23 @@ // the latest version of the policy set by administrators. class ConfigurationPolicyProviderWin : public ConfigurationPolicyProvider { public: + // Keeps watch on Windows Group Policy notification event to trigger + // a policy reload when Group Policy changes. + class GroupPolicyChangeWatcher : public base::ObjectWatcher::Delegate { + public: + explicit GroupPolicyChangeWatcher(ConfigurationPolicyProvider* provider); + virtual ~GroupPolicyChangeWatcher() {} + + virtual void OnObjectSignaled(HANDLE object); + + private: + ConfigurationPolicyProvider* provider_; + base::WaitableEvent user_policy_changed_event_; + base::WaitableEvent machine_policy_changed_event_; + base::ObjectWatcher user_policy_watcher_; + base::ObjectWatcher machine_policy_watcher_; + }; + ConfigurationPolicyProviderWin(); virtual ~ConfigurationPolicyProviderWin() { } @@ -29,6 +49,8 @@ class ConfigurationPolicyProviderWin : public ConfigurationPolicyProvider { static const wchar_t kPolicyRegistrySubKey[]; private: + scoped_ptr<GroupPolicyChangeWatcher> watcher_; + // Methods to perfrom type-specific policy lookups in the registry. // HKLM is checked first, then HKCU. bool GetRegistryPolicyString(const wchar_t* value_name, string16* result); diff --git a/chrome/browser/configuration_policy_provider_win_unittest.cc b/chrome/browser/configuration_policy_provider_win_unittest.cc index 4d39ff6..ed66b4f 100644 --- a/chrome/browser/configuration_policy_provider_win_unittest.cc +++ b/chrome/browser/configuration_policy_provider_win_unittest.cc @@ -113,6 +113,12 @@ class ConfigurationPolicyProviderWinTest : public testing::Test { void TestBooleanPolicy(ConfigurationPolicyStore::PolicyType type); private: + // A message loop must be declared and instantiated for these tests, + // because Windows policy provider create WaitableEvents and + // ObjectWatchers that require the tests to have a MessageLoop associated + // with the thread executing the tests. + MessageLoop loop_; + // Keys are created for the lifetime of a test to contain // the sandboxed HKCU and HKLM hives, respectively. RegKey temp_hkcu_hive_key_; diff --git a/chrome/browser/pref_service.cc b/chrome/browser/pref_service.cc index 1b7c909..c02dd34 100644 --- a/chrome/browser/pref_service.cc +++ b/chrome/browser/pref_service.cc @@ -119,6 +119,9 @@ PrefService* PrefService::CreateUserPrefService( PrefService::PrefService(PrefValueStore* pref_value_store) : pref_value_store_(pref_value_store) { InitFromStorage(); + registrar_.Add(this, + NotificationType(NotificationType::POLICY_CHANGED), + NotificationService::AllSources()); } PrefService::~PrefService() { @@ -826,6 +829,38 @@ bool PrefService::Preference::IsUserControlled() const { return pref_value_store_->PrefValueFromUserStore(name_.c_str()); } +void PrefService::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(UTF8ToWide(current->data()).data()); + } +} + bool PrefService::Preference::IsUserModifiable() const { return pref_value_store_->PrefValueUserModifiable(name_.c_str()); } + +void PrefService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::POLICY_CHANGED) { + PrefValueStore::AfterRefreshCallback callback = + NewCallback(this, + &PrefService::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. + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + pref_value_store_.get(), + &PrefValueStore::RefreshPolicyPrefs, + ConfigurationPolicyPrefStore::CreateManagedPolicyPrefStore(), + ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore(), + callback)); + } +} diff --git a/chrome/browser/pref_service.h b/chrome/browser/pref_service.h index 1204297..90e4004 100644 --- a/chrome/browser/pref_service.h +++ b/chrome/browser/pref_service.h @@ -9,22 +9,30 @@ #pragma once #include <set> +#include <string> +#include <vector> #include "base/file_path.h" #include "base/hash_tables.h" #include "base/non_thread_safe.h" #include "base/observer_list.h" +#include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/values.h" #include "chrome/browser/pref_value_store.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "chrome/common/pref_store.h" -class NotificationObserver; +class NotificationDetails; +class NotificationSource; +class NotificationType; class Preference; class Profile; class ScopedPrefUpdate; -class PrefService : public NonThreadSafe { +class PrefService : public NonThreadSafe, + public NotificationObserver { public: // A helper class to store all the information associated with a preference. @@ -254,13 +262,29 @@ class PrefService : public NonThreadSafe { // This should only be called from the constructor. void InitFromStorage(); + // NotificationObserver methods: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // 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); + // The value of a Preference can be: // managed, user defined, recommended or default. // The PrefValueStore manages enforced, user defined and recommended values // for Preferences. It returns the value of a Preference with the // highest priority, and allows to set user defined values for preferences // that are not managed. - scoped_ptr<PrefValueStore> pref_value_store_; + scoped_refptr<PrefValueStore> pref_value_store_; + + NotificationRegistrar registrar_; // A set of all the registered Preference objects. PreferenceSet prefs_; diff --git a/chrome/browser/pref_value_store.cc b/chrome/browser/pref_value_store.cc index 0988b9c..ab976aa 100644 --- a/chrome/browser/pref_value_store.cc +++ b/chrome/browser/pref_value_store.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "chrome/browser/pref_value_store.h" +#include "chrome/common/notification_service.h" PrefValueStore::PrefValueStore(PrefStore* managed_prefs, PrefStore* extension_prefs, @@ -113,7 +114,7 @@ bool PrefValueStore::PrefValueUserModifiable(const wchar_t* name) { } bool PrefValueStore::PrefValueInStore(const wchar_t* name, PrefStoreType type) { - if (pref_stores_[type].get() == NULL) { + if (!pref_stores_[type].get()) { // No store of that type set, so this pref can't be in it. return false; } @@ -129,3 +130,84 @@ PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref( } return INVALID; } + +void PrefValueStore::RefreshPolicyPrefsCompletion( + PrefStore* new_managed_pref_store, + PrefStore* new_recommended_pref_store, + AfterRefreshCallback callback) { + + DictionaryValue* managed_prefs_before(pref_stores_[MANAGED]->prefs()); + DictionaryValue* managed_prefs_after(new_managed_pref_store->prefs()); + DictionaryValue* recommended_prefs_before(pref_stores_[RECOMMENDED]->prefs()); + DictionaryValue* recommended_prefs_after(new_recommended_pref_store->prefs()); + + std::vector<std::string> changed_managed_paths; + managed_prefs_before->GetDifferingPaths(managed_prefs_after, + &changed_managed_paths); + + std::vector<std::string> changed_recommended_paths; + recommended_prefs_before->GetDifferingPaths(recommended_prefs_after, + &changed_recommended_paths); + + std::vector<std::string> changed_paths(changed_managed_paths.size() + + changed_recommended_paths.size()); + std::vector<std::string>::iterator last_insert = + std::merge(changed_managed_paths.begin(), + changed_managed_paths.end(), + changed_recommended_paths.begin(), + changed_recommended_paths.end(), + changed_paths.begin()); + changed_paths.resize(last_insert - changed_paths.begin()); + + pref_stores_[MANAGED].reset(new_managed_pref_store); + pref_stores_[RECOMMENDED].reset(new_recommended_pref_store); + callback->Run(changed_paths); +} + +void PrefValueStore::RefreshPolicyPrefsOnFileThread( + ChromeThread::ID calling_thread_id, + PrefStore* new_managed_pref_store, + PrefStore* new_recommended_pref_store, + AfterRefreshCallback callback) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + scoped_ptr<PrefStore> managed_pref_store(new_managed_pref_store); + scoped_ptr<PrefStore> recommended_pref_store(new_recommended_pref_store); + + PrefStore::PrefReadError read_error = new_managed_pref_store->ReadPrefs(); + if (read_error != PrefStore::PREF_READ_ERROR_NONE) { + LOG(ERROR) << "refresh of managed policy failed: PrefReadError = " + << read_error; + return; + } + + read_error = new_recommended_pref_store->ReadPrefs(); + if (read_error != PrefStore::PREF_READ_ERROR_NONE) { + LOG(ERROR) << "refresh of recommended policy failed: PrefReadError = " + << read_error; + return; + } + + ChromeThread::PostTask( + calling_thread_id, FROM_HERE, + NewRunnableMethod(this, + &PrefValueStore::RefreshPolicyPrefsCompletion, + managed_pref_store.release(), + recommended_pref_store.release(), + callback)); +} + +void PrefValueStore::RefreshPolicyPrefs( + PrefStore* new_managed_pref_store, + PrefStore* new_recommended_pref_store, + AfterRefreshCallback callback) { + ChromeThread::ID current_thread_id; + CHECK(ChromeThread::GetCurrentThreadIdentifier(¤t_thread_id)); + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod(this, + &PrefValueStore::RefreshPolicyPrefsOnFileThread, + current_thread_id, + new_managed_pref_store, + new_recommended_pref_store, + callback)); +} diff --git a/chrome/browser/pref_value_store.h b/chrome/browser/pref_value_store.h index d54e2ab..addb580 100644 --- a/chrome/browser/pref_value_store.h +++ b/chrome/browser/pref_value_store.h @@ -6,11 +6,18 @@ #define CHROME_BROWSER_PREF_VALUE_STORE_H_ #pragma once +#include <string> +#include <vector> + #include "base/basictypes.h" +#include "base/callback.h" #include "base/file_path.h" +#include "base/gtest_prod_util.h" +#include "base/ref_counted.h" #include "base/string16.h" #include "base/scoped_ptr.h" #include "base/values.h" +#include "chrome/browser/chrome_thread.h" #include "chrome/common/pref_store.h" class PrefStore; @@ -27,7 +34,9 @@ class PrefStore; // Otherwise user-defined values have a higher precedence than recommended // values. Recommended preference values are set by a third person // (like an admin). -class PrefValueStore { +// Unless otherwise explicitly noted, all of the methods of this class must +// be called on the UI thread. +class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> { public: // In decreasing order of precedence: // |managed_prefs| contains all managed (policy) preference values. @@ -101,7 +110,28 @@ class PrefValueStore { // there is no higher-priority source controlling it. bool PrefValueUserModifiable(const wchar_t* name); + // 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 preferences from policy. Caller must pass in + // new, uninitialized managed and recommended PrefStores in + // |managed_pref_store| and |recommended_pref_store| respectively, since + // PrefValueStore doesn't know about policy-specific PrefStores. + // |callback| is called with the set of preferences changed by the policy + // refresh. |callback| is called the caller's thread as a Task + // after RefreshPolicyPrefs has returned. + void RefreshPolicyPrefs(PrefStore* managed_pref_store, + PrefStore* recommended_pref_store, + AfterRefreshCallback callback); + private: + friend class PrefValueStoreTest; + FRIEND_TEST_ALL_PREFIXES(PrefValueStoreTest, + TestRefreshPolicyPrefsCompletion); + // PrefStores must be listed here in order from highest to lowest priority. enum PrefStoreType { // Not associated with an actual PrefStore but used as invalid marker, e.g. @@ -124,6 +154,20 @@ class PrefValueStore { // INVALID is returned. PrefStoreType ControllingPrefStoreForPref(const wchar_t* name); + // Called during policy refresh after ReadPrefs completes on the thread + // that initiated the policy refresh. + void RefreshPolicyPrefsCompletion( + PrefStore* new_managed_pref_store, + PrefStore* new_recommended_pref_store, + AfterRefreshCallback callback); + + // Called during policy refresh to do the ReadPrefs on the FILE thread. + void RefreshPolicyPrefsOnFileThread( + ChromeThread::ID calling_thread_id, + PrefStore* new_managed_pref_store, + PrefStore* new_recommended_pref_store, + AfterRefreshCallback callback); + DISALLOW_COPY_AND_ASSIGN(PrefValueStore); }; diff --git a/chrome/browser/pref_value_store_unittest.cc b/chrome/browser/pref_value_store_unittest.cc index 282f305..ae14598 100644 --- a/chrome/browser/pref_value_store_unittest.cc +++ b/chrome/browser/pref_value_store_unittest.cc @@ -3,10 +3,11 @@ // found in the LICENSE file. #include "base/scoped_ptr.h" +#include "base/utf_string_conversions.h" #include "base/values.h" +#include "chrome/browser/configuration_policy_pref_store.h" #include "chrome/browser/dummy_pref_store.h" #include "chrome/browser/pref_value_store.h" - #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -62,22 +63,6 @@ namespace recommended_pref { class PrefValueStoreTest : public testing::Test { protected: - scoped_ptr<PrefValueStore> pref_value_store_; - - // |PrefStore|s are owned by the |PrefValueStore|. - DummyPrefStore* enforced_pref_store_; - DummyPrefStore* extension_pref_store_; - DummyPrefStore* command_line_pref_store_; - DummyPrefStore* recommended_pref_store_; - DummyPrefStore* user_pref_store_; - - // Preferences are owned by the individual |DummyPrefStores|. - DictionaryValue* enforced_prefs_; - DictionaryValue* extension_prefs_; - DictionaryValue* command_line_prefs_; - DictionaryValue* user_prefs_; - DictionaryValue* recommended_prefs_; - virtual void SetUp() { // Create dummy user preferences. enforced_prefs_= CreateEnforcedPrefs(); @@ -100,11 +85,14 @@ class PrefValueStoreTest : public testing::Test { recommended_pref_store_->set_prefs(recommended_prefs_); // Create a new pref-value-store. - pref_value_store_.reset(new PrefValueStore(enforced_pref_store_, - extension_pref_store_, - command_line_pref_store_, - user_pref_store_, - recommended_pref_store_)); + pref_value_store_ = new PrefValueStore(enforced_pref_store_, + extension_pref_store_, + command_line_pref_store_, + user_pref_store_, + recommended_pref_store_); + + ui_thread_.reset(new ChromeThread(ChromeThread::UI, &loop_)); + file_thread_.reset(new ChromeThread(ChromeThread::FILE, &loop_)); } // Creates a new dictionary and stores some sample user preferences @@ -124,6 +112,7 @@ class PrefValueStoreTest : public testing::Test { DictionaryValue* CreateEnforcedPrefs() { DictionaryValue* enforced_prefs = new DictionaryValue(); enforced_prefs->SetString(prefs::kHomepage, enforced_pref::kHomepageValue); + expected_differing_paths_.push_back(WideToUTF8(prefs::kHomepage)); return enforced_prefs; } @@ -154,6 +143,14 @@ class PrefValueStoreTest : public testing::Test { recommended_prefs->SetBoolean( prefs::kRecommendedPref, recommended_pref::kRecommendedPrefValue); + + // Expected differing paths must be added in lexicographic order + // to work properly + expected_differing_paths_.push_back("tabs"); + expected_differing_paths_.push_back(WideToUTF8(prefs::kMaxTabs)); + expected_differing_paths_.push_back("this"); + expected_differing_paths_.push_back("this.pref"); + expected_differing_paths_.push_back(WideToUTF8(prefs::kRecommendedPref)); return recommended_prefs; } DictionaryValue* CreateSampleDictValue() { @@ -173,9 +170,38 @@ class PrefValueStoreTest : public testing::Test { return sample_list; } - virtual void TearDown() {} -}; + virtual void TearDown() { + loop_.RunAllPending(); + } + + MessageLoop loop_; + scoped_refptr<PrefValueStore> pref_value_store_; + + // |PrefStore|s are owned by the |PrefValueStore|. + DummyPrefStore* enforced_pref_store_; + DummyPrefStore* extension_pref_store_; + DummyPrefStore* command_line_pref_store_; + DummyPrefStore* recommended_pref_store_; + DummyPrefStore* user_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* enforced_prefs_; + DictionaryValue* extension_prefs_; + DictionaryValue* command_line_prefs_; + DictionaryValue* user_prefs_; + DictionaryValue* recommended_prefs_; + + private: + scoped_ptr<ChromeThread> ui_thread_; + scoped_ptr<ChromeThread> file_thread_; +}; TEST_F(PrefValueStoreTest, IsReadOnly) { enforced_pref_store_->set_read_only(true); @@ -427,3 +453,139 @@ TEST_F(PrefValueStoreTest, PrefValueInUserStore) { EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(prefs::kMissingPref)); EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(prefs::kMissingPref)); } + +class MockPolicyRefreshCallback { + public: + MockPolicyRefreshCallback() {} + MOCK_METHOD1(DoCallback, void(const std::vector<std::string>)); +}; + +TEST_F(PrefValueStoreTest, TestPolicyRefresh) { + // pref_value_store_ is initialized by PrefValueStoreTest to have values + // in both it's managed and recommended store. By replacing them with + // dummy stores, all of the paths of the prefs originally managed and + // recommended stores should change. + MockPolicyRefreshCallback callback; + EXPECT_CALL(callback, DoCallback(_)).Times(0); + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + pref_value_store_.get(), + &PrefValueStore::RefreshPolicyPrefs, + new DummyPrefStore(), + new DummyPrefStore(), + NewCallback(&callback, + &MockPolicyRefreshCallback::DoCallback))); + Mock::VerifyAndClearExpectations(&callback); + EXPECT_CALL(callback, DoCallback(expected_differing_paths_)).Times(1); + loop_.RunAllPending(); +} + +TEST_F(PrefValueStoreTest, TestRefreshPolicyPrefsCompletion) { + // Test changed preferences in managed 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_store(new DummyPrefStore()); + DictionaryValue* dict = new DictionaryValue(); + dict->SetString(L"homepage", L"some other changed homepage"); + new_managed_store->set_prefs(dict); + MockPolicyRefreshCallback callback; + EXPECT_CALL(callback, DoCallback(expected_differing_paths_)).Times(1); + pref_value_store_->RefreshPolicyPrefsCompletion( + new_managed_store.release(), + new DummyPrefStore(), + NewCallback(&callback, + &MockPolicyRefreshCallback::DoCallback)); + + // Test properties that have been removed from the managed 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); + pref_value_store_->RefreshPolicyPrefsCompletion( + new DummyPrefStore(), + new DummyPrefStore(), + NewCallback(&callback2, + &MockPolicyRefreshCallback::DoCallback)); + + // Test properties that are added to the recommended store. + scoped_ptr<DummyPrefStore> new_recommended_store(new DummyPrefStore()); + dict = new DictionaryValue(); + dict->SetString(L"homepage", L"some other changed homepage 2"); + new_recommended_store->set_prefs(dict); + expected_differing_paths_.clear(); + expected_differing_paths_.push_back(std::string("homepage")); + MockPolicyRefreshCallback callback3; + EXPECT_CALL(callback3, DoCallback(expected_differing_paths_)).Times(1); + pref_value_store_->RefreshPolicyPrefsCompletion( + new DummyPrefStore(), + new_recommended_store.release(), + NewCallback(&callback3, + &MockPolicyRefreshCallback::DoCallback)); + + // Test adding a multi-key path. + new_managed_store.reset(new DummyPrefStore()); + dict = new DictionaryValue(); + dict->SetString("segment1.segment2", "value"); + new_managed_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 callback4; + EXPECT_CALL(callback4, DoCallback(expected_differing_paths_)).Times(1); + pref_value_store_->RefreshPolicyPrefsCompletion( + new_managed_store.release(), + new DummyPrefStore(), + NewCallback(&callback4, + &MockPolicyRefreshCallback::DoCallback)); +} + +TEST_F(PrefValueStoreTest, TestConcurrentPolicyRefresh) { + MockPolicyRefreshCallback callback1; + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + pref_value_store_.get(), + &PrefValueStore::RefreshPolicyPrefs, + new DummyPrefStore(), + new DummyPrefStore(), + NewCallback(&callback1, + &MockPolicyRefreshCallback::DoCallback))); + EXPECT_CALL(callback1, DoCallback(_)).Times(0); + + MockPolicyRefreshCallback callback2; + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + pref_value_store_.get(), + &PrefValueStore::RefreshPolicyPrefs, + new DummyPrefStore(), + new DummyPrefStore(), + NewCallback(&callback2, + &MockPolicyRefreshCallback::DoCallback))); + EXPECT_CALL(callback2, DoCallback(_)).Times(0); + + MockPolicyRefreshCallback callback3; + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + pref_value_store_.get(), + &PrefValueStore::RefreshPolicyPrefs, + new DummyPrefStore(), + new DummyPrefStore(), + 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); + loop_.RunAllPending(); +} diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h index cbb737a..a30fb27 100644 --- a/chrome/common/notification_type.h +++ b/chrome/common/notification_type.h @@ -1118,6 +1118,11 @@ class NotificationType { // |webkit_glue::PasswordForm|s that were affected. LOGINS_CHANGED, + // Configuration Policy ---------------------------------------------------- + // This notification is sent whenever the administrator changes policy. + // The detail of this notification is not used. + POLICY_CHANGED, + // Count (must be last) ---------------------------------------------------- // Used to determine the number of notification types. Not valid as // a type parameter when registering for or posting notifications. |