summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordanno@chromium.org <danno@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-09 13:53:18 +0000
committerdanno@chromium.org <danno@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-09 13:53:18 +0000
commitba99ca24c0ba8f0e154dbd74d8a43a55736630e1 (patch)
tree69d9ab71f38afeb5a78bc49267b9f5d20fe15cf1
parentea4a1c6aa08d16edba3128fb7810d63bcb3e1ef0 (diff)
downloadchromium_src-ba99ca24c0ba8f0e154dbd74d8a43a55736630e1.zip
chromium_src-ba99ca24c0ba8f0e154dbd74d8a43a55736630e1.tar.gz
chromium_src-ba99ca24c0ba8f0e154dbd74d8a43a55736630e1.tar.bz2
Refactor FileBasedPolicyProvider, introduce AsynchronousPolicyProvider.
Create a superclass of FileBasedPolicyProvider that abstracts how a provider can provide policy on the UI thread that is loaded from the FILE thread. BUG=65094 TEST=AsynchronousPolicyProvider.* Review URL: http://codereview.chromium.org/5562002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68735 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/policy/asynchronous_policy_loader.cc75
-rw-r--r--chrome/browser/policy/asynchronous_policy_loader.h90
-rw-r--r--chrome/browser/policy/asynchronous_policy_loader_unittest.cc132
-rw-r--r--chrome/browser/policy/asynchronous_policy_provider.cc42
-rw-r--r--chrome/browser/policy/asynchronous_policy_provider.h58
-rw-r--r--chrome/browser/policy/asynchronous_policy_provider_unittest.cc54
-rw-r--r--chrome/browser/policy/asynchronous_policy_test_base.h64
-rw-r--r--chrome/browser/policy/config_dir_policy_provider.cc14
-rw-r--r--chrome/browser/policy/config_dir_policy_provider.h35
-rw-r--r--chrome/browser/policy/config_dir_policy_provider_unittest.cc9
-rw-r--r--chrome/browser/policy/configuration_policy_provider.cc2
-rw-r--r--chrome/browser/policy/configuration_policy_provider.h2
-rw-r--r--chrome/browser/policy/configuration_policy_provider_mac.cc14
-rw-r--r--chrome/browser/policy/configuration_policy_provider_mac.h9
-rw-r--r--chrome/browser/policy/file_based_policy_loader.cc205
-rw-r--r--chrome/browser/policy/file_based_policy_loader.h104
-rw-r--r--chrome/browser/policy/file_based_policy_provider.cc238
-rw-r--r--chrome/browser/policy/file_based_policy_provider.h165
-rw-r--r--chrome/browser/policy/file_based_policy_provider_unittest.cc170
-rw-r--r--chrome/browser/policy/mock_configuration_policy_provider.cc1
-rw-r--r--chrome/browser/policy/mock_configuration_policy_provider.h4
-rw-r--r--chrome/chrome_browser.gypi6
-rw-r--r--chrome/chrome_tests.gypi3
23 files changed, 962 insertions, 534 deletions
diff --git a/chrome/browser/policy/asynchronous_policy_loader.cc b/chrome/browser/policy/asynchronous_policy_loader.cc
new file mode 100644
index 0000000..ea27bb7
--- /dev/null
+++ b/chrome/browser/policy/asynchronous_policy_loader.cc
@@ -0,0 +1,75 @@
+// 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/policy/asynchronous_policy_loader.h"
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "chrome/browser/browser_thread.h"
+
+namespace policy {
+
+AsynchronousPolicyLoader::AsynchronousPolicyLoader(
+ AsynchronousPolicyProvider::Delegate* delegate)
+ : delegate_(delegate),
+ provider_(NULL),
+ origin_loop_(MessageLoop::current()) {}
+
+void AsynchronousPolicyLoader::Init() {
+ policy_.reset(delegate_->Load());
+}
+
+void AsynchronousPolicyLoader::Stop() {
+ delegate_.reset();
+}
+
+void AsynchronousPolicyLoader::SetProvider(
+ AsynchronousPolicyProvider* provider) {
+ provider_ = provider;
+}
+
+// Manages the life cycle of a new policy map during until it's life cycle is
+// taken over by the policy loader.
+class UpdatePolicyTask : public Task {
+ public:
+ UpdatePolicyTask(scoped_refptr<AsynchronousPolicyLoader> loader,
+ DictionaryValue* new_policy)
+ : loader_(loader),
+ new_policy_(new_policy) {}
+
+ virtual void Run() {
+ loader_->UpdatePolicy(new_policy_.release());
+ }
+
+ private:
+ scoped_refptr<AsynchronousPolicyLoader> loader_;
+ scoped_ptr<DictionaryValue> new_policy_;
+ DISALLOW_COPY_AND_ASSIGN(UpdatePolicyTask);
+};
+
+void AsynchronousPolicyLoader::Reload() {
+ if (delegate_.get()) {
+ DictionaryValue* new_policy = delegate_->Load();
+ PostUpdatePolicyTask(new_policy);
+ }
+}
+
+void AsynchronousPolicyLoader::PostUpdatePolicyTask(
+ DictionaryValue* new_policy) {
+ origin_loop_->PostTask(FROM_HERE, new UpdatePolicyTask(this, new_policy));
+}
+
+void AsynchronousPolicyLoader::UpdatePolicy(DictionaryValue* new_policy_raw) {
+ DCHECK(policy_.get());
+ if (!policy_->Equals(new_policy_raw)) {
+ policy_.reset(new_policy_raw);
+ // TODO(danno): Change the notification between the provider and the
+ // PrefStore into a notification mechanism, removing the need for the
+ // WeakPtr for the provider.
+ if (provider_)
+ provider_->NotifyStoreOfPolicyChange();
+ }
+}
+
+} // namespace policy
diff --git a/chrome/browser/policy/asynchronous_policy_loader.h b/chrome/browser/policy/asynchronous_policy_loader.h
new file mode 100644
index 0000000..2600847
--- /dev/null
+++ b/chrome/browser/policy/asynchronous_policy_loader.h
@@ -0,0 +1,90 @@
+// 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_POLICY_ASYNCHRONOUS_POLICY_LOADER_H_
+#define CHROME_BROWSER_POLICY_ASYNCHRONOUS_POLICY_LOADER_H_
+#pragma once
+
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/values.h"
+#include "chrome/browser/policy/asynchronous_policy_provider.h"
+
+namespace policy {
+
+class ConfigurationPolicyProvider;
+
+// Used by the implementation of asynchronous policy provider to manage the
+// tasks on the file thread that do the heavy lifting of loading policies.
+class AsynchronousPolicyLoader
+ : public base::RefCountedThreadSafe<AsynchronousPolicyLoader> {
+ public:
+ explicit AsynchronousPolicyLoader(
+ AsynchronousPolicyProvider::Delegate* delegate);
+
+ // Triggers initial policy load.
+ virtual void Init();
+
+ // Reloads policy, sending notification of changes if necessary. Must be
+ // called on the file thread.
+ virtual void Reload();
+
+ // Stops any pending reload tasks.
+ virtual void Stop();
+
+ // Associates a provider with the loader to allow the loader to notify the
+ // provider when policy changes.
+ void SetProvider(AsynchronousPolicyProvider* provider);
+
+ const DictionaryValue* policy() const { return policy_.get(); }
+
+ protected:
+ friend class UpdatePolicyTask;
+
+ // AsynchronousPolicyLoader objects should only be deleted by
+ // RefCountedThreadSafe.
+ friend class base::RefCountedThreadSafe<AsynchronousPolicyLoader>;
+ virtual ~AsynchronousPolicyLoader() {}
+
+ // Schedules a call to UpdatePolicy on |origin_loop_|.
+ void PostUpdatePolicyTask(DictionaryValue* new_policy);
+
+ // Replaces the existing policy to value map with a new one, sending
+ // notification to the provider if there is a policy change. Must be called on
+ // |origin_loop_| so that it's safe to call back into the provider, which is
+ // not thread-safe.
+ void UpdatePolicy(DictionaryValue* new_policy);
+
+ AsynchronousPolicyProvider::Delegate* delegate() {
+ return delegate_.get();
+ }
+
+ AsynchronousPolicyProvider* provider() {
+ return provider_;
+ }
+
+ private:
+ friend class AsynchronousPolicyLoaderTest;
+
+ // Provides the low-level mechanics for loading policy.
+ scoped_ptr<AsynchronousPolicyProvider::Delegate> delegate_;
+
+ // Current policy.
+ scoped_ptr<DictionaryValue> policy_;
+
+ // The provider this loader is associated with. Access only on the thread that
+ // called the constructor. See |origin_loop_| below.
+ AsynchronousPolicyProvider* provider_;
+
+ // The message loop on which this object was constructed. Recorded so that
+ // it's possible to call back into the non thread safe provider to fire the
+ // notification.
+ MessageLoop* origin_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(AsynchronousPolicyLoader);
+};
+
+} // namespace policy
+
+#endif // CHROME_BROWSER_POLICY_ASYNCHRONOUS_POLICY_LOADER_H_
diff --git a/chrome/browser/policy/asynchronous_policy_loader_unittest.cc b/chrome/browser/policy/asynchronous_policy_loader_unittest.cc
new file mode 100644
index 0000000..4995431
--- /dev/null
+++ b/chrome/browser/policy/asynchronous_policy_loader_unittest.cc
@@ -0,0 +1,132 @@
+// 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/policy/asynchronous_policy_loader.h"
+#include "chrome/browser/policy/asynchronous_policy_provider.h"
+#include "chrome/browser/policy/asynchronous_policy_test_base.h"
+#include "chrome/browser/policy/mock_configuration_policy_provider.h"
+#include "chrome/common/notification_observer_mock.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+
+namespace policy {
+
+class AsynchronousPolicyLoaderTest : public AsynchronousPolicyTestBase {
+ public:
+ AsynchronousPolicyLoaderTest() {}
+ virtual ~AsynchronousPolicyLoaderTest() {}
+
+ virtual void SetUp() {
+ AsynchronousPolicyTestBase::SetUp();
+ mock_provider_.reset(new MockConfigurationPolicyProvider());
+ }
+
+ protected:
+ scoped_ptr<MockConfigurationPolicyProvider> mock_provider_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AsynchronousPolicyLoaderTest);
+};
+
+ACTION(CreateTestDictionary) {
+ return new DictionaryValue();
+}
+
+ACTION_P(CreateSequencedTestDictionary, number) {
+ DictionaryValue* test_dictionary = new DictionaryValue();
+ test_dictionary->SetInteger("id", ++(*number));
+ return test_dictionary;
+}
+
+ACTION(RescheduleImmediatePolicyReload) {
+ *arg1 = base::TimeDelta();
+ return false;
+}
+
+TEST_F(AsynchronousPolicyLoaderTest, InitialLoad) {
+ DictionaryValue* template_dict(new DictionaryValue());
+ EXPECT_CALL(*delegate_, Load()).WillOnce(Return(template_dict));
+ scoped_refptr<AsynchronousPolicyLoader> loader =
+ new AsynchronousPolicyLoader(delegate_.release());
+ loader->Init();
+ const DictionaryValue* loaded_dict(loader->policy());
+ EXPECT_TRUE(loaded_dict->Equals(template_dict));
+}
+
+// Verify that the fallback policy requests are made.
+TEST_F(AsynchronousPolicyLoaderTest, InitialLoadWithFallback) {
+ int dictionary_number = 0;
+ InSequence s;
+ EXPECT_CALL(*delegate_, Load()).WillOnce(
+ CreateSequencedTestDictionary(&dictionary_number));
+ EXPECT_CALL(*delegate_, Load()).WillOnce(
+ CreateSequencedTestDictionary(&dictionary_number));
+ scoped_refptr<AsynchronousPolicyLoader> loader =
+ new AsynchronousPolicyLoader(delegate_.release());
+ loader->Init();
+ loop_.RunAllPending();
+ loader->Reload();
+ loop_.RunAllPending();
+
+ const DictionaryValue* loaded_dict(loader->policy());
+ int loaded_number;
+ EXPECT_TRUE(loaded_dict->GetInteger("id", &loaded_number));
+ EXPECT_EQ(dictionary_number, loaded_number);
+}
+
+// Ensure that calling stop on the loader stops subsequent reloads from
+// happening.
+TEST_F(AsynchronousPolicyLoaderTest, Stop) {
+ ON_CALL(*delegate_, Load()).WillByDefault(CreateTestDictionary());
+ EXPECT_CALL(*delegate_, Load()).Times(1);
+ scoped_refptr<AsynchronousPolicyLoader> loader =
+ new AsynchronousPolicyLoader(delegate_.release());
+ loader->Init();
+ loop_.RunAllPending();
+ loader->Stop();
+ loop_.RunAllPending();
+ loader->Reload();
+ loop_.RunAllPending();
+}
+
+// Verifies that the provider is notified upon policy reload, but only
+// if the policy changed.
+TEST_F(AsynchronousPolicyLoaderTest, ProviderNotificationOnPolicyChange) {
+ InSequence s;
+ NotificationObserverMock observer;
+ // NotificationService service;
+ NotificationRegistrar registrar;
+ registrar.Add(&observer,
+ NotificationType::POLICY_CHANGED,
+ NotificationService::AllSources());
+ int dictionary_number_1 = 0;
+ int dictionary_number_2 = 0;
+ EXPECT_CALL(*delegate_, Load()).WillOnce(
+ CreateSequencedTestDictionary(&dictionary_number_1));
+ EXPECT_CALL(*delegate_, Load()).WillOnce(
+ CreateSequencedTestDictionary(&dictionary_number_2));
+ EXPECT_CALL(observer, Observe(_, _, _)).Times(0);
+ EXPECT_CALL(*delegate_, Load()).WillOnce(
+ CreateSequencedTestDictionary(&dictionary_number_2));
+ EXPECT_CALL(observer, Observe(_, _, _)).Times(1);
+ EXPECT_CALL(*delegate_, Load()).WillOnce(
+ CreateSequencedTestDictionary(&dictionary_number_1));
+ scoped_refptr<AsynchronousPolicyLoader> loader =
+ new AsynchronousPolicyLoader(delegate_.release());
+ AsynchronousPolicyProvider provider(NULL, loader);
+ loop_.RunAllPending();
+ loader->Reload();
+ loop_.RunAllPending();
+ loader->Reload();
+ loop_.RunAllPending();
+ loader->Reload();
+ loop_.RunAllPending();
+}
+
+} // namespace policy
diff --git a/chrome/browser/policy/asynchronous_policy_provider.cc b/chrome/browser/policy/asynchronous_policy_provider.cc
new file mode 100644
index 0000000..709568f
--- /dev/null
+++ b/chrome/browser/policy/asynchronous_policy_provider.cc
@@ -0,0 +1,42 @@
+// 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/policy/asynchronous_policy_provider.h"
+
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/policy/asynchronous_policy_loader.h"
+
+namespace policy {
+
+AsynchronousPolicyProvider::AsynchronousPolicyProvider(
+ const PolicyDefinitionList* policy_list,
+ scoped_refptr<AsynchronousPolicyLoader> loader)
+ : ConfigurationPolicyProvider(policy_list),
+ loader_(loader) {
+ // TODO(danno): This explicit registration of the provider shouldn't be
+ // necessary. Instead, the PrefStore should explicitly observe preference
+ // changes that are reported during the policy change.
+ loader_->SetProvider(this);
+ loader_->Init();
+}
+
+AsynchronousPolicyProvider::~AsynchronousPolicyProvider() {
+ DCHECK(CalledOnValidThread());
+ loader_->Stop();
+ loader_->SetProvider(NULL);
+}
+
+bool AsynchronousPolicyProvider::Provide(
+ ConfigurationPolicyStoreInterface* store) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(loader_->policy());
+ DecodePolicyValueTree(loader_->policy(), store);
+ return true;
+}
+
+scoped_refptr<AsynchronousPolicyLoader> AsynchronousPolicyProvider::loader() {
+ return loader_;
+}
+
+} // namespace policy
diff --git a/chrome/browser/policy/asynchronous_policy_provider.h b/chrome/browser/policy/asynchronous_policy_provider.h
new file mode 100644
index 0000000..0f6b649
--- /dev/null
+++ b/chrome/browser/policy/asynchronous_policy_provider.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_POLICY_ASYNCHRONOUS_POLICY_PROVIDER_H_
+#define CHROME_BROWSER_POLICY_ASYNCHRONOUS_POLICY_PROVIDER_H_
+#pragma once
+
+#include "base/non_thread_safe.h"
+#include "base/ref_counted.h"
+#include "base/weak_ptr.h"
+#include "chrome/browser/policy/configuration_policy_provider.h"
+
+namespace policy {
+
+class AsynchronousPolicyLoader;
+
+// Policy provider that loads policy asynchronously. Providers should subclass
+// from this class if loading the policy requires disk access or must for some
+// other reason be performed on the file thread. The actual logic for loading
+// policy is handled by a delegate passed at construction time.
+class AsynchronousPolicyProvider
+ : public ConfigurationPolicyProvider,
+ public base::SupportsWeakPtr<AsynchronousPolicyProvider>,
+ public NonThreadSafe {
+ public:
+ // Must be implemented by subclasses of the asynchronous policy provider to
+ // provide the implementation details of how policy is loaded.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ virtual DictionaryValue* Load() = 0;
+ };
+
+ // Assumes ownership of |loader|.
+ AsynchronousPolicyProvider(
+ const PolicyDefinitionList* policy_list,
+ scoped_refptr<AsynchronousPolicyLoader> loader);
+ virtual ~AsynchronousPolicyProvider();
+
+ // ConfigurationPolicyProvider implementation.
+ virtual bool Provide(ConfigurationPolicyStoreInterface* store);
+
+ // For tests to trigger reloads.
+ scoped_refptr<AsynchronousPolicyLoader> loader();
+
+ protected:
+ // The loader object used internally.
+ scoped_refptr<AsynchronousPolicyLoader> loader_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AsynchronousPolicyProvider);
+};
+
+} // namespace policy
+
+#endif // CHROME_BROWSER_POLICY_ASYNCHRONOUS_POLICY_PROVIDER_H_
diff --git a/chrome/browser/policy/asynchronous_policy_provider_unittest.cc b/chrome/browser/policy/asynchronous_policy_provider_unittest.cc
new file mode 100644
index 0000000..64306eb
--- /dev/null
+++ b/chrome/browser/policy/asynchronous_policy_provider_unittest.cc
@@ -0,0 +1,54 @@
+// 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/policy/asynchronous_policy_loader.h"
+#include "chrome/browser/policy/asynchronous_policy_provider.h"
+#include "chrome/browser/policy/asynchronous_policy_test_base.h"
+#include "chrome/browser/policy/configuration_policy_pref_store.h"
+#include "chrome/browser/policy/mock_configuration_policy_store.h"
+#include "chrome/common/policy_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+
+namespace policy {
+
+TEST_F(AsynchronousPolicyTestBase, Provide) {
+ InSequence s;
+ DictionaryValue* policies = new DictionaryValue();
+ policies->SetBoolean(policy::key::kSyncDisabled, true);
+ EXPECT_CALL(*delegate_, Load()).WillOnce(Return(policies));
+ EXPECT_CALL(*store_, Apply(policy::kPolicySyncDisabled, _)).Times(1);
+ AsynchronousPolicyProvider provider(
+ ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
+ new AsynchronousPolicyLoader(delegate_.release()));
+ provider.Provide(store_.get());
+}
+
+TEST_F(AsynchronousPolicyTestBase, ProvideAfterRefresh) {
+ InSequence s;
+ DictionaryValue* original_policies = new DictionaryValue();
+ original_policies->SetBoolean(policy::key::kSyncDisabled, true);
+ EXPECT_CALL(*delegate_, Load()).WillOnce(Return(original_policies));
+ DictionaryValue* refresh_policies = new DictionaryValue();
+ refresh_policies->SetBoolean(
+ policy::key::kJavascriptEnabled,
+ true);
+ EXPECT_CALL(*delegate_, Load()).WillOnce(Return(refresh_policies));
+ AsynchronousPolicyLoader* loader = new AsynchronousPolicyLoader(
+ delegate_.release());
+ AsynchronousPolicyProvider provider(
+ ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
+ loader);
+ loop_.RunAllPending();
+ loader->Reload();
+ loop_.RunAllPending();
+ EXPECT_CALL(*store_, Apply(policy::kPolicyJavascriptEnabled, _)).Times(1);
+ provider.Provide(store_.get());
+}
+
+} // namespace policy
diff --git a/chrome/browser/policy/asynchronous_policy_test_base.h b/chrome/browser/policy/asynchronous_policy_test_base.h
new file mode 100644
index 0000000..0281afd
--- /dev/null
+++ b/chrome/browser/policy/asynchronous_policy_test_base.h
@@ -0,0 +1,64 @@
+// 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_POLICY_ASYNCHRONOUS_POLICY_TEST_BASE_H_
+#define CHROME_BROWSER_POLICY_ASYNCHRONOUS_POLICY_TEST_BASE_H_
+#pragma once
+
+#include "base/message_loop.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/policy/asynchronous_policy_provider.h"
+#include "chrome/browser/policy/mock_configuration_policy_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+// A delegate for testing that can feed arbitrary information to the loader.
+class ProviderDelegateMock : public AsynchronousPolicyProvider::Delegate {
+ public:
+ ProviderDelegateMock() : AsynchronousPolicyProvider::Delegate() {}
+ virtual ~ProviderDelegateMock() {}
+
+ MOCK_METHOD0(Load, DictionaryValue*());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProviderDelegateMock);
+};
+
+class AsynchronousPolicyTestBase : public testing::Test {
+ public:
+ AsynchronousPolicyTestBase()
+ : ui_thread_(BrowserThread::UI, &loop_),
+ file_thread_(BrowserThread::FILE, &loop_) {}
+
+ virtual ~AsynchronousPolicyTestBase() {}
+
+ virtual void SetUp() {
+ delegate_.reset(new ProviderDelegateMock());
+ store_.reset(new MockConfigurationPolicyStore);
+ }
+
+ virtual void TearDown() {
+ loop_.RunAllPending();
+ }
+
+ protected:
+ MessageLoop loop_;
+
+ // The mocks that are used in the test must outlive the scope of the test
+ // because they still get accessed in the RunAllPending of the TearDown.
+ scoped_ptr<MockConfigurationPolicyStore> store_;
+ scoped_ptr<ProviderDelegateMock> delegate_;
+
+ private:
+ BrowserThread ui_thread_;
+ BrowserThread file_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(AsynchronousPolicyTestBase);
+};
+
+} // namespace policy
+
+#endif // CHROME_BROWSER_POLICY_ASYNCHRONOUS_POLICY_TEST_BASE_H_
diff --git a/chrome/browser/policy/config_dir_policy_provider.cc b/chrome/browser/policy/config_dir_policy_provider.cc
index f134aeb..be6f586 100644
--- a/chrome/browser/policy/config_dir_policy_provider.cc
+++ b/chrome/browser/policy/config_dir_policy_provider.cc
@@ -12,11 +12,12 @@
namespace policy {
-ConfigDirPolicyLoader::ConfigDirPolicyLoader(const FilePath& config_dir)
- : FileBasedPolicyProvider::Delegate(config_dir) {
+ConfigDirPolicyProviderDelegate::ConfigDirPolicyProviderDelegate(
+ const FilePath& config_dir)
+ : FileBasedPolicyProvider::ProviderDelegate(config_dir) {
}
-DictionaryValue* ConfigDirPolicyLoader::Load() {
+DictionaryValue* ConfigDirPolicyProviderDelegate::Load() {
// Enumerate the files and sort them lexicographically.
std::set<FilePath> files;
file_util::FileEnumerator file_enumerator(config_file_path(), false,
@@ -49,7 +50,7 @@ DictionaryValue* ConfigDirPolicyLoader::Load() {
return policy;
}
-base::Time ConfigDirPolicyLoader::GetLastModification() {
+base::Time ConfigDirPolicyProviderDelegate::GetLastModification() {
base::Time last_modification = base::Time();
base::PlatformFileInfo file_info;
@@ -78,8 +79,9 @@ base::Time ConfigDirPolicyLoader::GetLastModification() {
ConfigDirPolicyProvider::ConfigDirPolicyProvider(
const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list,
const FilePath& config_dir)
- : FileBasedPolicyProvider(policy_list,
- new ConfigDirPolicyLoader(config_dir)) {
+ : FileBasedPolicyProvider(
+ policy_list,
+ new ConfigDirPolicyProviderDelegate(config_dir)) {
}
} // namespace policy
diff --git a/chrome/browser/policy/config_dir_policy_provider.h b/chrome/browser/policy/config_dir_policy_provider.h
index 6096513..3647489 100644
--- a/chrome/browser/policy/config_dir_policy_provider.h
+++ b/chrome/browser/policy/config_dir_policy_provider.h
@@ -10,23 +10,6 @@
namespace policy {
-// A policy loader implementation backed by a set of files in a given directory.
-// The files should contain JSON-formatted policy settings. They are merged
-// together and the result is returned via the PolicyLoader interface. The files
-// are consulted in lexicographic file name order, so the last value read takes
-// precedence in case of preference key collisions.
-class ConfigDirPolicyLoader : public FileBasedPolicyProvider::Delegate {
- public:
- explicit ConfigDirPolicyLoader(const FilePath& config_dir);
-
- // FileBasedPolicyLoader::Delegate implementation.
- virtual DictionaryValue* Load();
- virtual base::Time GetLastModification();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyLoader);
-};
-
// Policy provider backed by JSON files in a configuration directory.
class ConfigDirPolicyProvider : public FileBasedPolicyProvider {
public:
@@ -38,6 +21,24 @@ class ConfigDirPolicyProvider : public FileBasedPolicyProvider {
DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyProvider);
};
+// A provider delegate implementation backed by a set of files in a given
+// directory. The files should contain JSON-formatted policy settings. They are
+// merged together and the result is returned via the ProviderDelegate
+// interface. The files are consulted in lexicographic file name order, so the
+// last value read takes precedence in case of preference key collisions.
+class ConfigDirPolicyProviderDelegate
+ : public FileBasedPolicyProvider::ProviderDelegate {
+ public:
+ explicit ConfigDirPolicyProviderDelegate(const FilePath& config_dir);
+
+ // FileBasedPolicyProvider::ProviderDelegate implementation.
+ virtual DictionaryValue* Load();
+ virtual base::Time GetLastModification();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ConfigDirPolicyProviderDelegate);
+};
+
} // namespace policy
#endif // CHROME_BROWSER_POLICY_CONFIG_DIR_POLICY_PROVIDER_H_
diff --git a/chrome/browser/policy/config_dir_policy_provider_unittest.cc b/chrome/browser/policy/config_dir_policy_provider_unittest.cc
index 25893da..7197f490 100644
--- a/chrome/browser/policy/config_dir_policy_provider_unittest.cc
+++ b/chrome/browser/policy/config_dir_policy_provider_unittest.cc
@@ -8,6 +8,7 @@
#include "base/path_service.h"
#include "base/scoped_temp_dir.h"
#include "base/string_number_conversions.h"
+#include "chrome/browser/browser_thread.h"
#include "chrome/browser/policy/config_dir_policy_provider.h"
#include "chrome/browser/policy/configuration_policy_pref_store.h"
#include "chrome/browser/policy/mock_configuration_policy_store.h"
@@ -49,7 +50,7 @@ class ConfigDirPolicyLoaderTest
// The preferences dictionary is expected to be empty when there are no files to
// load.
TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsEmpty) {
- ConfigDirPolicyLoader loader(test_dir());
+ ConfigDirPolicyProviderDelegate loader(test_dir());
scoped_ptr<DictionaryValue> policy(loader.Load());
EXPECT_TRUE(policy.get());
EXPECT_TRUE(policy->empty());
@@ -59,7 +60,7 @@ TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsEmpty) {
// dictionary.
TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsNonExistentDirectory) {
FilePath non_existent_dir(test_dir().Append(FILE_PATH_LITERAL("not_there")));
- ConfigDirPolicyLoader loader(non_existent_dir);
+ ConfigDirPolicyProviderDelegate loader(non_existent_dir);
scoped_ptr<DictionaryValue> policy(loader.Load());
EXPECT_TRUE(policy.get());
EXPECT_TRUE(policy->empty());
@@ -71,7 +72,7 @@ TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsSinglePref) {
test_dict.SetString("HomepageLocation", "http://www.google.com");
WriteConfigFile(test_dict, "config_file");
- ConfigDirPolicyLoader loader(test_dir());
+ ConfigDirPolicyProviderDelegate loader(test_dir());
scoped_ptr<DictionaryValue> policy(loader.Load());
EXPECT_TRUE(policy.get());
EXPECT_TRUE(policy->Equals(&test_dict));
@@ -93,7 +94,7 @@ TEST_F(ConfigDirPolicyLoaderTest, ReadPrefsMergePrefs) {
for (unsigned int i = 5; i <= 8; ++i)
WriteConfigFile(test_dict_bar, base::IntToString(i));
- ConfigDirPolicyLoader loader(test_dir());
+ ConfigDirPolicyProviderDelegate loader(test_dir());
scoped_ptr<DictionaryValue> policy(loader.Load());
EXPECT_TRUE(policy.get());
EXPECT_TRUE(policy->Equals(&test_dict_foo));
diff --git a/chrome/browser/policy/configuration_policy_provider.cc b/chrome/browser/policy/configuration_policy_provider.cc
index ce7106f..a8ca10f 100644
--- a/chrome/browser/policy/configuration_policy_provider.cc
+++ b/chrome/browser/policy/configuration_policy_provider.cc
@@ -24,7 +24,7 @@ void ConfigurationPolicyProvider::NotifyStoreOfPolicyChange() {
}
void ConfigurationPolicyProvider::DecodePolicyValueTree(
- DictionaryValue* policies,
+ const DictionaryValue* policies,
ConfigurationPolicyStoreInterface* store) {
const PolicyDefinitionList* policy_list(policy_definition_list());
for (const PolicyDefinitionList::Entry* i = policy_list->begin;
diff --git a/chrome/browser/policy/configuration_policy_provider.h b/chrome/browser/policy/configuration_policy_provider.h
index e3989b9..6d5cc7c 100644
--- a/chrome/browser/policy/configuration_policy_provider.h
+++ b/chrome/browser/policy/configuration_policy_provider.h
@@ -53,7 +53,7 @@ class ConfigurationPolicyProvider {
virtual void NotifyStoreOfPolicyChange();
// Decodes the value tree and writes the configuration to the given |store|.
- void DecodePolicyValueTree(DictionaryValue* policies,
+ void DecodePolicyValueTree(const DictionaryValue* policies,
ConfigurationPolicyStoreInterface* store);
protected:
const PolicyDefinitionList* policy_definition_list() const {
diff --git a/chrome/browser/policy/configuration_policy_provider_mac.cc b/chrome/browser/policy/configuration_policy_provider_mac.cc
index 62b7436..b8b6011 100644
--- a/chrome/browser/policy/configuration_policy_provider_mac.cc
+++ b/chrome/browser/policy/configuration_policy_provider_mac.cc
@@ -34,15 +34,15 @@ static FilePath GetManagedPolicyPath() {
return path.Append(base::SysCFStringRefToUTF8(bundle_id) + ".plist");
}
-MacPreferencesPolicyLoader::MacPreferencesPolicyLoader(
+MacPreferencesPolicyProviderDelegate::MacPreferencesPolicyProviderDelegate(
MacPreferences* preferences,
const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list)
- : FileBasedPolicyProvider::Delegate(GetManagedPolicyPath()),
+ : FileBasedPolicyProvider::ProviderDelegate(GetManagedPolicyPath()),
policy_list_(policy_list),
preferences_(preferences) {
}
-DictionaryValue* MacPreferencesPolicyLoader::Load() {
+DictionaryValue* MacPreferencesPolicyProviderDelegate::Load() {
preferences_->AppSynchronize(kCFPreferencesCurrentApplication);
DictionaryValue* policy = new DictionaryValue;
@@ -110,7 +110,7 @@ DictionaryValue* MacPreferencesPolicyLoader::Load() {
return policy;
}
-base::Time MacPreferencesPolicyLoader::GetLastModification() {
+base::Time MacPreferencesPolicyProviderDelegate::GetLastModification() {
base::PlatformFileInfo file_info;
if (!file_util::GetFileInfo(config_file_path(), &file_info) ||
file_info.is_directory) {
@@ -123,14 +123,16 @@ base::Time MacPreferencesPolicyLoader::GetLastModification() {
ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac(
const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list)
: FileBasedPolicyProvider(policy_list,
- new MacPreferencesPolicyLoader(new MacPreferences, policy_list)) {
+ new MacPreferencesPolicyProviderDelegate(new MacPreferences,
+ policy_list)) {
}
ConfigurationPolicyProviderMac::ConfigurationPolicyProviderMac(
const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list,
MacPreferences* preferences)
: FileBasedPolicyProvider(policy_list,
- new MacPreferencesPolicyLoader(preferences, policy_list)) {
+ new MacPreferencesPolicyProviderDelegate(preferences,
+ policy_list)) {
}
} // namespace policy
diff --git a/chrome/browser/policy/configuration_policy_provider_mac.h b/chrome/browser/policy/configuration_policy_provider_mac.h
index d804500..9332e2b 100644
--- a/chrome/browser/policy/configuration_policy_provider_mac.h
+++ b/chrome/browser/policy/configuration_policy_provider_mac.h
@@ -13,11 +13,12 @@
namespace policy {
-// A policy loader implementation that read Mac OS X's managed preferences.
-class MacPreferencesPolicyLoader : public FileBasedPolicyProvider::Delegate {
+// A provider delegate implementation that reads Mac OS X's managed preferences.
+class MacPreferencesPolicyProviderDelegate
+ : public FileBasedPolicyProvider::ProviderDelegate {
public:
// Takes ownership of |preferences|.
- MacPreferencesPolicyLoader(
+ MacPreferencesPolicyProviderDelegate(
MacPreferences* preferences,
const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list);
@@ -35,7 +36,7 @@ class MacPreferencesPolicyLoader : public FileBasedPolicyProvider::Delegate {
scoped_ptr<MacPreferences> preferences_;
- DISALLOW_COPY_AND_ASSIGN(MacPreferencesPolicyLoader);
+ DISALLOW_COPY_AND_ASSIGN(MacPreferencesPolicyProviderDelegate);
};
// An implementation of |ConfigurationPolicyProvider| using the mechanism
diff --git a/chrome/browser/policy/file_based_policy_loader.cc b/chrome/browser/policy/file_based_policy_loader.cc
new file mode 100644
index 0000000..4c33b91
--- /dev/null
+++ b/chrome/browser/policy/file_based_policy_loader.cc
@@ -0,0 +1,205 @@
+// 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/policy/file_based_policy_loader.h"
+
+namespace {
+
+// Amount of time we wait for the files on disk to settle before trying to load
+// them. This alleviates the problem of reading partially written files and
+// makes it possible to batch quasi-simultaneous changes.
+const int kSettleIntervalSeconds = 5;
+
+// The time interval for rechecking policy. This is our fallback in case the
+// delegate never reports a change to the ReloadObserver.
+const int kReloadIntervalMinutes = 15;
+
+}
+
+namespace policy {
+
+FileBasedPolicyLoader::FileBasedPolicyLoader(
+ FileBasedPolicyProvider::ProviderDelegate* provider_delegate)
+ : AsynchronousPolicyLoader(provider_delegate),
+ config_file_path_(provider_delegate->config_file_path()),
+ reload_task_(NULL),
+ reload_interval_(base::TimeDelta::FromMinutes(kReloadIntervalMinutes)),
+ settle_interval_(base::TimeDelta::FromSeconds(kSettleIntervalSeconds)) {
+}
+
+class FileBasedPolicyWatcherDelegate : public FilePathWatcher::Delegate {
+ public:
+ explicit FileBasedPolicyWatcherDelegate(
+ scoped_refptr<FileBasedPolicyLoader> loader)
+ : loader_(loader) {}
+ virtual ~FileBasedPolicyWatcherDelegate() {}
+
+ // FilePathWatcher::Delegate implementation:
+ void OnFilePathChanged(const FilePath& path) {
+ loader_->OnFilePathChanged(path);
+ }
+
+ void OnError() {
+ loader_->OnError();
+ }
+
+ private:
+ scoped_refptr<FileBasedPolicyLoader> loader_;
+ DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcherDelegate);
+};
+
+void FileBasedPolicyLoader::Init() {
+ AsynchronousPolicyLoader::Init();
+
+ // Initialization can happen early when the file thread is not yet available,
+ // but the watcher's initialization must be done on the file thread. Posting
+ // to a to the file directly before the file thread is initialized will cause
+ // the task to be forgotten. Instead, post a task to the ui thread to delay
+ // the remainder of initialization until threading is fully initialized.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &FileBasedPolicyLoader::InitAfterFileThreadAvailable));
+}
+
+void FileBasedPolicyLoader::Stop() {
+ AsynchronousPolicyLoader::Stop();
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &FileBasedPolicyLoader::StopOnFileThread));
+}
+
+void FileBasedPolicyLoader::OnFilePathChanged(
+ const FilePath& path) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Reload();
+}
+
+void FileBasedPolicyLoader::OnError() {
+ LOG(ERROR) << "FilePathWatcher on " << config_file_path().value()
+ << " failed.";
+}
+
+void FileBasedPolicyLoader::Reload() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ if (!delegate())
+ return;
+
+ // Check the directory time in order to see whether a reload is required.
+ base::TimeDelta delay;
+ base::Time now = base::Time::Now();
+ if (!IsSafeToReloadPolicy(now, &delay)) {
+ ScheduleReloadTask(delay);
+ return;
+ }
+
+ // Load the policy definitions.
+ scoped_ptr<DictionaryValue> new_policy(delegate()->Load());
+
+ // Check again in case the directory has changed while reading it.
+ if (!IsSafeToReloadPolicy(now, &delay)) {
+ ScheduleReloadTask(delay);
+ return;
+ }
+
+ PostUpdatePolicyTask(new_policy.release());
+
+ ScheduleFallbackReloadTask();
+}
+
+void FileBasedPolicyLoader::InitAfterFileThreadAvailable() {
+ if (provider()) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &FileBasedPolicyLoader::InitWatcher));
+
+ ScheduleFallbackReloadTask();
+ }
+}
+
+void FileBasedPolicyLoader::InitWatcher() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ watcher_.reset(new FilePathWatcher);
+ if (!config_file_path().empty() &&
+ !watcher_->Watch(config_file_path(),
+ new FileBasedPolicyWatcherDelegate(this))) {
+ OnError();
+ }
+
+ // There might have been changes to the directory in the time between
+ // construction of the loader and initialization of the watcher. Call reload
+ // to detect if that is the case.
+ Reload();
+}
+
+void FileBasedPolicyLoader::StopOnFileThread() {
+ watcher_.reset();
+ if (reload_task_) {
+ reload_task_->Cancel();
+ reload_task_ = NULL;
+ }
+}
+
+void FileBasedPolicyLoader::ScheduleReloadTask(
+ const base::TimeDelta& delay) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ if (reload_task_)
+ reload_task_->Cancel();
+
+ reload_task_ =
+ NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask);
+ BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_,
+ delay.InMilliseconds());
+}
+
+void FileBasedPolicyLoader::ScheduleFallbackReloadTask() {
+ // As a safeguard in case that the load delegate failed to timely notice a
+ // change in policy, schedule a reload task that'll make us recheck after a
+ // reasonable interval.
+ ScheduleReloadTask(reload_interval_);
+}
+
+void FileBasedPolicyLoader::ReloadFromTask() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // Drop the reference to the reload task, since the task might be the only
+ // referer that keeps us alive, so we should not Cancel() it.
+ reload_task_ = NULL;
+
+ Reload();
+}
+
+bool FileBasedPolicyLoader::IsSafeToReloadPolicy(
+ const base::Time& now,
+ base::TimeDelta* delay) {
+ DCHECK(delay);
+
+ // A null modification time indicates there's no data.
+ FileBasedPolicyProvider::ProviderDelegate* provider_delegate =
+ static_cast<FileBasedPolicyProvider::ProviderDelegate*>(delegate());
+ base::Time last_modification(provider_delegate->GetLastModification());
+ if (last_modification.is_null())
+ return true;
+
+ // If there was a change since the last recorded modification, wait some more.
+ if (last_modification != last_modification_file_) {
+ last_modification_file_ = last_modification;
+ last_modification_clock_ = now;
+ *delay = settle_interval_;
+ return false;
+ }
+
+ // Check whether the settle interval has elapsed.
+ base::TimeDelta age = now - last_modification_clock_;
+ if (age < settle_interval_) {
+ *delay = settle_interval_ - age;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace policy
diff --git a/chrome/browser/policy/file_based_policy_loader.h b/chrome/browser/policy/file_based_policy_loader.h
new file mode 100644
index 0000000..8acf9b2d
--- /dev/null
+++ b/chrome/browser/policy/file_based_policy_loader.h
@@ -0,0 +1,104 @@
+// 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_POLICY_FILE_BASED_POLICY_LOADER_H_
+#define CHROME_BROWSER_POLICY_FILE_BASED_POLICY_LOADER_H_
+#pragma once
+
+#include "chrome/browser/file_path_watcher/file_path_watcher.h"
+#include "chrome/browser/policy/asynchronous_policy_loader.h"
+#include "chrome/browser/policy/file_based_policy_provider.h"
+
+namespace policy {
+
+// A customized asynchronous policy loader that handles loading policy from a
+// file using a FilePathWatcher. The loader creates a fallback task to load
+// policy periodically in case the watcher fails and retries policy loads when
+// the watched file is in flux.
+class FileBasedPolicyLoader : public AsynchronousPolicyLoader {
+ public:
+ FileBasedPolicyLoader(
+ FileBasedPolicyProvider::ProviderDelegate* provider_delegate);
+
+ // AsynchronousPolicyLoader implementation:
+ virtual void Init();
+ virtual void Stop();
+ virtual void Reload();
+
+ void OnFilePathChanged(const FilePath& path);
+ void OnError();
+
+ protected:
+ // FileBasedPolicyLoader objects should only be deleted by
+ // RefCountedThreadSafe.
+ friend class base::RefCountedThreadSafe<AsynchronousPolicyLoader>;
+ virtual ~FileBasedPolicyLoader() {}
+
+ const FilePath& config_file_path() { return config_file_path_; }
+
+ private:
+ // Finishes initialization after the threading system has been fully
+ // intiialized.
+ void InitAfterFileThreadAvailable();
+
+ // Creates the file path watcher and configures it watch |config_file_path_|.
+ // Must be called on the file thread.
+ void InitWatcher();
+
+ // Cancels file path watch notification and destroys the watcher.
+ // Must be called on file thread.
+ void StopOnFileThread();
+
+ // Schedules a reload task to run when |delay| expires. Must be called on the
+ // file thread.
+ void ScheduleReloadTask(const base::TimeDelta& delay);
+
+ // Schedules a reload task to run after the number of minutes specified
+ // in |reload_interval_minutes_|. Must be called on the file thread.
+ void ScheduleFallbackReloadTask();
+
+ // Invoked from the reload task on the file thread.
+ void ReloadFromTask();
+
+ // Checks whether policy information is safe to read. If not, returns false
+ // and then delays until it is considered safe to reload in |delay|.
+ // Must be called on the file thread.
+ bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay);
+
+ // The path at which we look for configuration files.
+ const FilePath config_file_path_;
+
+ // Managed with a scoped_ptr rather than being declared as an inline member to
+ // decouple the watcher's life cycle from the loader's. This decoupling makes
+ // it possible to destroy the watcher before the loader's destructor is called
+ // (e.g. during Stop), since |watcher_| internally holds a reference to the
+ // loader and keeps it alive.
+ scoped_ptr<FilePathWatcher> watcher_;
+
+ // The reload task. Access only on the file thread. Holds a reference to the
+ // currently posted task, so we can cancel and repost it if necessary.
+ CancelableTask* reload_task_;
+
+ // The interval that a policy reload will be triggered as a fallback even if
+ // the delegate doesn't indicate that one is needed.
+ const base::TimeDelta reload_interval_;
+
+ // Settle interval.
+ const base::TimeDelta settle_interval_;
+
+ // Records last known modification timestamp of |config_file_path_|.
+ base::Time last_modification_file_;
+
+ // The wall clock time at which the last modification timestamp was
+ // recorded. It's better to not assume the file notification time and the
+ // wall clock times come from the same source, just in case there is some
+ // non-local filesystem involved.
+ base::Time last_modification_clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyLoader);
+};
+
+} // namespace policy
+
+#endif // CHROME_BROWSER_POLICY_FILE_BASED_POLICY_LOADER_H_
diff --git a/chrome/browser/policy/file_based_policy_provider.cc b/chrome/browser/policy/file_based_policy_provider.cc
index 578277d..488f19f 100644
--- a/chrome/browser/policy/file_based_policy_provider.cc
+++ b/chrome/browser/policy/file_based_policy_provider.cc
@@ -4,241 +4,21 @@
#include "chrome/browser/policy/file_based_policy_provider.h"
-#include <set>
-
-#include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/task.h"
-#include "base/utf_string_conversions.h"
-#include "base/values.h"
-#include "chrome/browser/browser_thread.h"
-#include "chrome/common/json_value_serializer.h"
+#include "chrome/browser/policy/file_based_policy_loader.h"
namespace policy {
-// Amount of time we wait for the files on disk to settle before trying to load
-// it. This alleviates the problem of reading partially written files and allows
-// to batch quasi-simultaneous changes.
-const int kSettleIntervalSeconds = 5;
-
-// The time interval for rechecking policy. This is our fallback in case the
-// file path watch fails or doesn't report a change.
-const int kReloadIntervalMinutes = 15;
-
-// FileBasedPolicyProvider implementation:
+FileBasedPolicyProvider::ProviderDelegate::ProviderDelegate(
+ const FilePath& config_file_path)
+ : config_file_path_(config_file_path) {}
-FileBasedPolicyProvider::Delegate::~Delegate() {
-}
-
-FileBasedPolicyProvider::Delegate::Delegate(const FilePath& config_file_path)
- : config_file_path_(config_file_path) {
-}
+FileBasedPolicyProvider::ProviderDelegate::~ProviderDelegate() {}
FileBasedPolicyProvider::FileBasedPolicyProvider(
const ConfigurationPolicyProvider::PolicyDefinitionList* policy_list,
- FileBasedPolicyProvider::Delegate* delegate)
- : ConfigurationPolicyProvider(policy_list) {
- loader_ = new FileBasedPolicyLoader(AsWeakPtr(),
- delegate,
- kSettleIntervalSeconds,
- kReloadIntervalMinutes);
- watcher_ = new FileBasedPolicyWatcher;
- watcher_->Init(loader_.get());
-}
-
-FileBasedPolicyProvider::~FileBasedPolicyProvider() {
- loader_->Stop();
-}
-
-bool FileBasedPolicyProvider::Provide(
- ConfigurationPolicyStoreInterface* store) {
- scoped_ptr<DictionaryValue> policy(loader_->GetPolicy());
- DCHECK(policy.get());
- DecodePolicyValueTree(policy.get(), store);
- return true;
-}
-
-// FileBasedPolicyLoader implementation:
-
-FileBasedPolicyLoader::FileBasedPolicyLoader(
- base::WeakPtr<ConfigurationPolicyProvider> provider,
- FileBasedPolicyProvider::Delegate* delegate,
- int settle_interval_seconds,
- int reload_interval_minutes)
- : delegate_(delegate),
- provider_(provider),
- origin_loop_(MessageLoop::current()),
- reload_task_(NULL),
- settle_interval_seconds_(settle_interval_seconds),
- reload_interval_minutes_(reload_interval_minutes) {
- // Force an initial load, so GetPolicy() works.
- policy_.reset(delegate_->Load());
- DCHECK(policy_.get());
-}
-
-void FileBasedPolicyLoader::Stop() {
- if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(this, &FileBasedPolicyLoader::Stop));
- return;
- }
-
- if (reload_task_) {
- reload_task_->Cancel();
- reload_task_ = NULL;
- }
-}
-
-void FileBasedPolicyLoader::Reload() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- // Check the directory time in order to see whether a reload is required.
- base::TimeDelta delay;
- base::Time now = base::Time::Now();
- if (!IsSafeToReloadPolicy(now, &delay)) {
- ScheduleReloadTask(delay);
- return;
- }
-
- // Load the policy definitions.
- scoped_ptr<DictionaryValue> new_policy(delegate_->Load());
-
- // Check again in case the directory has changed while reading it.
- if (!IsSafeToReloadPolicy(now, &delay)) {
- ScheduleReloadTask(delay);
- return;
- }
-
- // Replace policy definition.
- bool changed = false;
- {
- AutoLock lock(lock_);
- changed = !policy_->Equals(new_policy.get());
- policy_.reset(new_policy.release());
- }
-
- // There's a change, report it!
- if (changed) {
- VLOG(0) << "Policy reload from " << config_file_path().value()
- << " succeeded.";
- origin_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this, &FileBasedPolicyLoader::NotifyPolicyChanged));
- }
-
- // As a safeguard in case the file watcher fails, schedule a reload task
- // that'll make us recheck after a reasonable interval.
- ScheduleReloadTask(base::TimeDelta::FromMinutes(reload_interval_minutes_));
-}
-
-DictionaryValue* FileBasedPolicyLoader::GetPolicy() {
- AutoLock lock(lock_);
- return static_cast<DictionaryValue*>(policy_->DeepCopy());
-}
-
-void FileBasedPolicyLoader::OnFilePathChanged(const FilePath& path) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- Reload();
-}
-
-void FileBasedPolicyLoader::OnError() {
- LOG(ERROR) << "FilePathWatcher on " << config_file_path().value()
- << " failed.";
-}
-
-FileBasedPolicyLoader::~FileBasedPolicyLoader() {
-}
-
-bool FileBasedPolicyLoader::IsSafeToReloadPolicy(const base::Time& now,
- base::TimeDelta* delay) {
- DCHECK(delay);
-
- // A null modification time indicates there's no data.
- base::Time last_modification(delegate_->GetLastModification());
- if (last_modification.is_null())
- return true;
-
- // If there was a change since the last recorded modification, wait some more.
- base::TimeDelta settleInterval(
- base::TimeDelta::FromSeconds(settle_interval_seconds_));
- if (last_modification != last_modification_file_) {
- last_modification_file_ = last_modification;
- last_modification_clock_ = now;
- *delay = settleInterval;
- return false;
- }
-
- // Check whether the settle interval has elapsed.
- base::TimeDelta age = now - last_modification_clock_;
- if (age < settleInterval) {
- *delay = settleInterval - age;
- return false;
- }
-
- return true;
-}
-
-void FileBasedPolicyLoader::ScheduleReloadTask(const base::TimeDelta& delay) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- if (reload_task_)
- reload_task_->Cancel();
-
- reload_task_ =
- NewRunnableMethod(this, &FileBasedPolicyLoader::ReloadFromTask);
- BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_,
- delay.InMilliseconds());
-}
-
-void FileBasedPolicyLoader::NotifyPolicyChanged() {
- DCHECK_EQ(origin_loop_, MessageLoop::current());
- if (provider_)
- provider_->NotifyStoreOfPolicyChange();
-}
-
-void FileBasedPolicyLoader::ReloadFromTask() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
-
- // Drop the reference to the reload task, since the task might be the only
- // referer that keeps us alive, so we should not Cancel() it.
- reload_task_ = NULL;
-
- Reload();
-}
-
-// FileBasedPolicyWatcher implementation:
-
-FileBasedPolicyWatcher::FileBasedPolicyWatcher() {
-}
-
-void FileBasedPolicyWatcher::Init(FileBasedPolicyLoader* loader) {
- // Initialization can happen early when the file thread is not yet available.
- // So post a task to ourselves on the UI thread which will run after threading
- // is up and schedule watch initialization on the file thread.
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(this,
- &FileBasedPolicyWatcher::InitWatcher,
- scoped_refptr<FileBasedPolicyLoader>(loader)));
-}
-
-FileBasedPolicyWatcher::~FileBasedPolicyWatcher() {
-}
-
-void FileBasedPolicyWatcher::InitWatcher(
- const scoped_refptr<FileBasedPolicyLoader>& loader) {
- if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(this, &FileBasedPolicyWatcher::InitWatcher, loader));
- return;
- }
-
- if (!loader->config_file_path().empty() &&
- !watcher_.Watch(loader->config_file_path(), loader.get()))
- loader->OnError();
-
- // There might have been changes to the directory in the time between
- // construction of the loader and initialization of the watcher. Call reload
- // to detect if that is the case.
- loader->Reload();
-}
+ FileBasedPolicyProvider::ProviderDelegate* delegate)
+ : AsynchronousPolicyProvider(
+ policy_list,
+ new FileBasedPolicyLoader(delegate)) {}
} // namespace policy
diff --git a/chrome/browser/policy/file_based_policy_provider.h b/chrome/browser/policy/file_based_policy_provider.h
index 7b1807a..c94e1d4 100644
--- a/chrome/browser/policy/file_based_policy_provider.h
+++ b/chrome/browser/policy/file_based_policy_provider.h
@@ -6,39 +6,25 @@
#define CHROME_BROWSER_POLICY_FILE_BASED_POLICY_PROVIDER_H_
#pragma once
-#include "base/basictypes.h"
#include "base/file_path.h"
-#include "base/lock.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
#include "base/time.h"
-#include "base/weak_ptr.h"
-#include "chrome/browser/file_path_watcher/file_path_watcher.h"
-#include "chrome/browser/policy/configuration_policy_provider.h"
-
-class CancelableTask;
-class DictionaryValue;
-class MessageLoop;
+#include "chrome/browser/policy/asynchronous_policy_provider.h"
namespace policy {
-class FileBasedPolicyLoader;
-class FileBasedPolicyWatcher;
-
// File based policy provider that coordinates watching and reloading policy
// information from the configuration path. Actual logic for loading policy
// information is handled by a delegate passed at construction time.
-class FileBasedPolicyProvider
- : public ConfigurationPolicyProvider,
- public base::SupportsWeakPtr<FileBasedPolicyProvider> {
+class FileBasedPolicyProvider : public AsynchronousPolicyProvider {
public:
+
// Delegate interface for actual policy loading from the system.
- class Delegate {
+ class ProviderDelegate : public AsynchronousPolicyProvider::Delegate {
public:
- virtual ~Delegate();
+ explicit ProviderDelegate(const FilePath& config_file_path);
+ virtual ~ProviderDelegate();
- // Loads the policy information. Ownership of the return value is
- // transferred to the caller.
+ // AsynchronousPolicyProvider::Delegate implementation:
virtual DictionaryValue* Load() = 0;
// Gets the last modification timestamp for the policy information from the
@@ -48,152 +34,21 @@ class FileBasedPolicyProvider
const FilePath& config_file_path() { return config_file_path_; }
- protected:
- explicit Delegate(const FilePath& config_file_path);
-
private:
- // The path at which we look for configuration files.
const FilePath config_file_path_;
- DISALLOW_COPY_AND_ASSIGN(Delegate);
+ DISALLOW_COPY_AND_ASSIGN(ProviderDelegate);
};
// Assumes ownership of |delegate|.
FileBasedPolicyProvider(const PolicyDefinitionList* policy_list,
- Delegate* delegate);
- virtual ~FileBasedPolicyProvider();
-
- // ConfigurationPolicyProvider implementation.
- virtual bool Provide(ConfigurationPolicyStoreInterface* store);
+ ProviderDelegate* delegate);
+ virtual ~FileBasedPolicyProvider() {}
private:
- // Watches for changes to the configuration directory.
- scoped_refptr<FileBasedPolicyWatcher> watcher_;
-
- // The loader object we use internally.
- scoped_refptr<FileBasedPolicyLoader> loader_;
-
DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyProvider);
};
-// FilePathWatcher delegate implementation that handles change notifications for
-// the configuration file or directory. It keeps the authorative version of the
-// currently effective policy dictionary and updates it as appropriate. The
-// actual loading logic is handled by a delegate.
-class FileBasedPolicyLoader : public FilePathWatcher::Delegate {
- public:
- // Creates a new loader that'll load its data from |config_file_path|.
- // Assumes ownership of |delegate|, which provides the actual loading logic.
- // The parameters |settle_interval_seconds| and |reload_interval_minutes|
- // specify the time to wait before reading the file contents after a change
- // and the period for checking |config_file_path| for changes, respectively.
- FileBasedPolicyLoader(base::WeakPtr<ConfigurationPolicyProvider> provider,
- FileBasedPolicyProvider::Delegate* delegate,
- int settle_interval_seconds,
- int reload_interval_minutes);
-
- // Stops any pending reload tasks.
- void Stop();
-
- // Reloads the policies and sends out a notification, if appropriate. Must be
- // called on the file thread.
- void Reload();
-
- // Gets the current dictionary value object. Ownership of the returned value
- // is transferred to the caller.
- DictionaryValue* GetPolicy();
-
- const FilePath& config_file_path() { return delegate_->config_file_path(); }
-
- // FilePathWatcher::Delegate implementation:
- virtual void OnFilePathChanged(const FilePath& path);
- virtual void OnError();
-
- private:
- // FileBasedPolicyLoader objects should only be deleted by
- // RefCountedThreadSafe.
- friend class base::RefCountedThreadSafe<FileBasedPolicyLoader>;
- virtual ~FileBasedPolicyLoader();
-
- // Checks whether reading policy information is safe to do. If not, returns
- // false and the delay until it is considered safe to reload in |delay|.
- bool IsSafeToReloadPolicy(const base::Time& now, base::TimeDelta* delay);
-
- // Schedules a reload task to run when |delay| expires. Must be called on the
- // file thread.
- void ScheduleReloadTask(const base::TimeDelta& delay);
-
- // Notifies the policy provider to send out a policy changed notification.
- // Must be called on |origin_loop_|.
- void NotifyPolicyChanged();
-
- // Invoked from the reload task on the file thread.
- void ReloadFromTask();
-
- // The delegate.
- scoped_ptr<FileBasedPolicyProvider::Delegate> delegate_;
-
- // The provider this loader is associated with. Access only on the thread that
- // called the constructor. See |origin_loop_| below.
- base::WeakPtr<ConfigurationPolicyProvider> provider_;
-
- // The message loop on which this object was constructed and |provider_|
- // received on. Recorded so we can call back into the non thread safe provider
- // to fire the notification.
- MessageLoop* origin_loop_;
-
- // Records last known modification timestamp of |config_file_path_|.
- base::Time last_modification_file_;
-
- // The wall clock time at which the last modification timestamp was recorded.
- // It's better to not assume the file notification time and the wall clock
- // times come from the same source, just in case there is some non-local
- // filesystem involved.
- base::Time last_modification_clock_;
-
- // Protects |policy_|.
- Lock lock_;
-
- // The current policy definition.
- scoped_ptr<DictionaryValue> policy_;
-
- // The reload task. Access only on the file thread. Holds a reference to the
- // currently posted task, so we can cancel and repost it if necessary.
- CancelableTask* reload_task_;
-
- // Settle and reload intervals.
- const int settle_interval_seconds_;
- const int reload_interval_minutes_;
-
- DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyLoader);
-};
-
-// Wraps a FilePathWatcher for the configuration path and takes care of
-// initializing the watcher object on the file thread.
-class FileBasedPolicyWatcher
- : public base::RefCountedThreadSafe<FileBasedPolicyWatcher> {
- public:
- FileBasedPolicyWatcher();
-
- // Runs initialization. This is in a separate method since we need to post a
- // task (which cannot be done from the constructor).
- void Init(FileBasedPolicyLoader* loader);
-
- private:
- // FileBasedPolicyWatcher objects should only be deleted by
- // RefCountedThreadSafe.
- friend class base::RefCountedThreadSafe<FileBasedPolicyWatcher>;
- virtual ~FileBasedPolicyWatcher();
-
- // Actually sets up the watch with the FilePathWatcher code.
- void InitWatcher(const scoped_refptr<FileBasedPolicyLoader>& loader);
-
- // Wrapped watcher that takes care of the actual watching.
- FilePathWatcher watcher_;
-
- DISALLOW_COPY_AND_ASSIGN(FileBasedPolicyWatcher);
-};
-
} // namespace policy
#endif // CHROME_BROWSER_POLICY_FILE_BASED_POLICY_PROVIDER_H_
diff --git a/chrome/browser/policy/file_based_policy_provider_unittest.cc b/chrome/browser/policy/file_based_policy_provider_unittest.cc
index c78c586..1bb2185 100644
--- a/chrome/browser/policy/file_based_policy_provider_unittest.cc
+++ b/chrome/browser/policy/file_based_policy_provider_unittest.cc
@@ -2,130 +2,80 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "chrome/browser/policy/asynchronous_policy_loader.h"
+#include "chrome/browser/policy/asynchronous_policy_test_base.h"
#include "chrome/browser/policy/configuration_policy_pref_store.h"
+#include "chrome/browser/policy/configuration_policy_store_interface.h"
#include "chrome/browser/policy/file_based_policy_provider.h"
+#include "chrome/common/policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using testing::Mock;
+using testing::_;
+using testing::InSequence;
+using testing::Return;
namespace policy {
-// Shorter reload intervals for testing FileBasedPolicyLoader.
-const int kSettleIntervalSecondsForTesting = 0;
-const int kReloadIntervalMinutesForTesting = 1;
-
-// A delegate for testing that can feed arbitrary information to the loader.
-class TestDelegate : public FileBasedPolicyProvider::Delegate {
- public:
- TestDelegate()
- : FileBasedPolicyProvider::Delegate(FilePath(FILE_PATH_LITERAL("fake"))) {
- }
-
- // FileBasedPolicyProvider::Delegate implementation:
- virtual DictionaryValue* Load() {
- return static_cast<DictionaryValue*>(dict_.DeepCopy());
- }
-
- virtual base::Time GetLastModification() {
- return last_modification_;
- }
-
- DictionaryValue* dict() { return &dict_; }
- void set_last_modification(const base::Time& last_modification) {
- last_modification_ = last_modification;
- }
-
- private:
- DictionaryValue dict_;
- base::Time last_modification_;
-};
-
-// A mock provider that allows us to capture reload notifications.
-class MockPolicyProvider : public ConfigurationPolicyProvider,
- public base::SupportsWeakPtr<MockPolicyProvider> {
+class FileBasedPolicyProviderDelegateMock
+ : public FileBasedPolicyProvider::ProviderDelegate {
public:
- explicit MockPolicyProvider()
- : ConfigurationPolicyProvider(
- ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList()) {
- }
-
- virtual bool Provide(ConfigurationPolicyStoreInterface* store) {
- return true;
- }
-
- MOCK_METHOD0(NotifyStoreOfPolicyChange, void());
+ FileBasedPolicyProviderDelegateMock()
+ : FileBasedPolicyProvider::ProviderDelegate(FilePath()) {}
+ MOCK_METHOD0(Load, DictionaryValue*());
+ MOCK_METHOD0(GetLastModification, base::Time());
};
-class FileBasedPolicyLoaderTest : public testing::Test {
- protected:
- FileBasedPolicyLoaderTest()
- : ui_thread_(BrowserThread::UI, &loop_),
- file_thread_(BrowserThread::FILE, &loop_) {}
-
- virtual void TearDown() {
- loop_.RunAllPending();
- }
-
- MessageLoop loop_;
-
- private:
- BrowserThread ui_thread_;
- BrowserThread file_thread_;
-};
-
-TEST_F(FileBasedPolicyLoaderTest, BasicLoad) {
- TestDelegate* test_delegate = new TestDelegate;
- test_delegate->dict()->SetString("HomepageLocation", "http://www.google.com");
-
- scoped_refptr<FileBasedPolicyLoader> loader(
- new FileBasedPolicyLoader(base::WeakPtr<FileBasedPolicyProvider>(),
- test_delegate,
- kSettleIntervalSecondsForTesting,
- kReloadIntervalMinutesForTesting));
- scoped_ptr<DictionaryValue> policy(loader->GetPolicy());
- EXPECT_TRUE(policy.get());
- EXPECT_EQ(1U, policy->size());
-
- std::string str_value;
- EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value));
- EXPECT_EQ("http://www.google.com", str_value);
-
- loader->Stop();
+TEST_F(AsynchronousPolicyTestBase, ProviderInit) {
+ base::Time last_modified;
+ FileBasedPolicyProviderDelegateMock* provider_delegate =
+ new FileBasedPolicyProviderDelegateMock();
+ EXPECT_CALL(*provider_delegate, GetLastModification()).WillRepeatedly(
+ Return(last_modified));
+ InSequence s;
+ EXPECT_CALL(*provider_delegate, Load()).WillOnce(Return(
+ new DictionaryValue));
+ DictionaryValue* policies = new DictionaryValue();
+ policies->SetBoolean(policy::key::kSyncDisabled, true);
+ // A second call to Load gets triggered during the provider's construction
+ // when the file watcher is initialized, since this file may have changed
+ // between the initial load and creating watcher.
+ EXPECT_CALL(*provider_delegate, Load()).WillOnce(Return(policies));
+ FileBasedPolicyProvider provider(
+ ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
+ provider_delegate);
+ loop_.RunAllPending();
+ EXPECT_CALL(*store_, Apply(policy::kPolicySyncDisabled, _)).Times(1);
+ provider.Provide(store_.get());
}
-TEST_F(FileBasedPolicyLoaderTest, TestRefresh) {
- MockPolicyProvider provider;
- TestDelegate* test_delegate = new TestDelegate;
-
- scoped_refptr<FileBasedPolicyLoader> loader(
- new FileBasedPolicyLoader(provider.AsWeakPtr(),
- test_delegate,
- kSettleIntervalSecondsForTesting,
- kReloadIntervalMinutesForTesting));
- scoped_ptr<DictionaryValue> policy(loader->GetPolicy());
- EXPECT_TRUE(policy.get());
- EXPECT_EQ(0U, policy->size());
-
- test_delegate->dict()->SetString("HomepageLocation", "http://www.google.com");
-
- EXPECT_CALL(provider, NotifyStoreOfPolicyChange()).Times(1);
- loader->OnFilePathChanged(FilePath(FILE_PATH_LITERAL("fake")));
-
- // Run the loop. The refresh should be handled immediately since the settle
- // interval has been disabled.
+TEST_F(AsynchronousPolicyTestBase, ProviderRefresh) {
+ base::Time last_modified;
+ FileBasedPolicyProviderDelegateMock* provider_delegate =
+ new FileBasedPolicyProviderDelegateMock();
+ EXPECT_CALL(*provider_delegate, GetLastModification()).WillRepeatedly(
+ Return(last_modified));
+ InSequence s;
+ EXPECT_CALL(*provider_delegate, Load()).WillOnce(Return(
+ new DictionaryValue));
+ FileBasedPolicyProvider file_based_provider(
+ ConfigurationPolicyPrefStore::GetChromePolicyDefinitionList(),
+ provider_delegate);
+ // A second call to Load gets triggered during the provider's construction
+ // when the file watcher is initialized, since this file may have changed
+ // between the initial load and creating watcher.
+ EXPECT_CALL(*provider_delegate, Load()).WillOnce(Return(
+ new DictionaryValue));
loop_.RunAllPending();
- Mock::VerifyAndClearExpectations(&provider);
-
- policy.reset(loader->GetPolicy());
- EXPECT_TRUE(policy.get());
- EXPECT_EQ(1U, policy->size());
-
- std::string str_value;
- EXPECT_TRUE(policy->GetString("HomepageLocation", &str_value));
- EXPECT_EQ("http://www.google.com", str_value);
-
- loader->Stop();
+ // A third and final call to Load is made by the explicit Reload. This
+ // should be the one that provides the current policy.
+ DictionaryValue* policies = new DictionaryValue();
+ policies->SetBoolean(policy::key::kSyncDisabled, true);
+ EXPECT_CALL(*provider_delegate, Load()).WillOnce(Return(policies));
+ file_based_provider.loader()->Reload();
+ loop_.RunAllPending();
+ EXPECT_CALL(*store_, Apply(policy::kPolicySyncDisabled, _)).Times(1);
+ file_based_provider.Provide(store_.get());
}
} // namespace policy
diff --git a/chrome/browser/policy/mock_configuration_policy_provider.cc b/chrome/browser/policy/mock_configuration_policy_provider.cc
index 4f11497..9c2db8d 100644
--- a/chrome/browser/policy/mock_configuration_policy_provider.cc
+++ b/chrome/browser/policy/mock_configuration_policy_provider.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/policy/mock_configuration_policy_provider.h"
+#include "base/stl_util-inl.h"
#include "chrome/browser/policy/configuration_policy_pref_store.h"
namespace policy {
diff --git a/chrome/browser/policy/mock_configuration_policy_provider.h b/chrome/browser/policy/mock_configuration_policy_provider.h
index 8ba8a88..731256e 100644
--- a/chrome/browser/policy/mock_configuration_policy_provider.h
+++ b/chrome/browser/policy/mock_configuration_policy_provider.h
@@ -9,8 +9,8 @@
#include <map>
#include <utility>
-#include "base/stl_util-inl.h"
#include "chrome/browser/policy/configuration_policy_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
namespace policy {
@@ -26,6 +26,8 @@ class MockConfigurationPolicyProvider : public ConfigurationPolicyProvider {
// ConfigurationPolicyProvider method overrides.
virtual bool Provide(ConfigurationPolicyStoreInterface* store);
+ MOCK_METHOD0(NotifyStoreOfPolicyChange, void());
+
private:
typedef std::map<ConfigurationPolicyType, Value*> PolicyMap;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index f62f4c9..a5bb572 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1937,6 +1937,10 @@
'browser/plugin_service.h',
'browser/plugin_updater.cc',
'browser/plugin_updater.h',
+ 'browser/policy/asynchronous_policy_loader.cc',
+ 'browser/policy/asynchronous_policy_loader.h',
+ 'browser/policy/asynchronous_policy_provider.cc',
+ 'browser/policy/asynchronous_policy_provider.h',
'browser/policy/config_dir_policy_provider.cc',
'browser/policy/config_dir_policy_provider.h',
'browser/policy/configuration_policy_pref_store.cc',
@@ -1962,6 +1966,8 @@
'browser/policy/device_token_fetcher.cc',
'browser/policy/device_token_fetcher.h',
'browser/policy/dummy_configuration_policy_provider.h',
+ 'browser/policy/file_based_policy_loader.cc',
+ 'browser/policy/file_based_policy_loader.h',
'browser/policy/file_based_policy_provider.cc',
'browser/policy/file_based_policy_provider.h',
'browser/policy/managed_prefs_banner_base.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index a626e06..b1da7ad 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1256,6 +1256,9 @@
'browser/password_manager/password_store_win_unittest.cc',
'browser/plugin_exceptions_table_model_unittest.cc',
'browser/plugin_service_unittest.cc',
+ 'browser/policy/asynchronous_policy_loader_unittest.cc',
+ 'browser/policy/asynchronous_policy_provider_unittest.cc',
+ 'browser/policy/asynchronous_policy_test_base.h',
'browser/policy/config_dir_policy_provider_unittest.cc',
'browser/policy/configuration_policy_pref_store_unittest.cc',
'browser/policy/configuration_policy_provider_mac_unittest.cc',