summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authordanno@chromium.org <danno@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-04 15:31:38 +0000
committerdanno@chromium.org <danno@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-04 15:31:38 +0000
commit61f972be4f7409243bfd81abd41facb31f421550 (patch)
tree6bd79ad823720c1c98553a946f4522ec336379be /chrome
parenteb9afc859fc8df63b52de503c0aa3e690e674963 (diff)
downloadchromium_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.cc9
-rw-r--r--chrome/browser/configuration_policy_provider.h5
-rw-r--r--chrome/browser/configuration_policy_provider_win.cc38
-rw-r--r--chrome/browser/configuration_policy_provider_win.h22
-rw-r--r--chrome/browser/configuration_policy_provider_win_unittest.cc6
-rw-r--r--chrome/browser/pref_service.cc35
-rw-r--r--chrome/browser/pref_service.h30
-rw-r--r--chrome/browser/pref_value_store.cc84
-rw-r--r--chrome/browser/pref_value_store.h46
-rw-r--r--chrome/browser/pref_value_store_unittest.cc210
-rw-r--r--chrome/common/notification_type.h5
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(&current_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.