1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/policy/asynchronous_policy_loader.h"
6
7 #include "base/message_loop.h"
8 #include "base/task.h"
9 #include "content/browser/browser_thread.h"
10
11 namespace policy {
12
AsynchronousPolicyLoader(AsynchronousPolicyProvider::Delegate * delegate,int reload_interval_minutes)13 AsynchronousPolicyLoader::AsynchronousPolicyLoader(
14 AsynchronousPolicyProvider::Delegate* delegate,
15 int reload_interval_minutes)
16 : delegate_(delegate),
17 reload_task_(NULL),
18 reload_interval_(base::TimeDelta::FromMinutes(reload_interval_minutes)),
19 origin_loop_(MessageLoop::current()),
20 stopped_(false) {}
21
Init()22 void AsynchronousPolicyLoader::Init() {
23 policy_.reset(delegate_->Load());
24 // Initialization can happen early when the file thread is not yet available,
25 // but the subclass of the loader must do some of their initialization on the
26 // file thread. Posting to the file thread directly before it is initialized
27 // will cause the task to be forgotten. Instead, post a task to the ui thread
28 // to delay the remainder of initialization until threading is fully
29 // initialized.
30 BrowserThread::PostTask(
31 BrowserThread::UI, FROM_HERE,
32 NewRunnableMethod(
33 this,
34 &AsynchronousPolicyLoader::InitAfterFileThreadAvailable));
35 }
36
Stop()37 void AsynchronousPolicyLoader::Stop() {
38 if (!stopped_) {
39 stopped_ = true;
40 delegate_.reset();
41 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
42 observer_list_,
43 OnProviderGoingAway());
44 BrowserThread::PostTask(
45 BrowserThread::FILE, FROM_HERE,
46 NewRunnableMethod(this, &AsynchronousPolicyLoader::StopOnFileThread));
47 }
48 }
49
~AsynchronousPolicyLoader()50 AsynchronousPolicyLoader::~AsynchronousPolicyLoader() {
51 }
52
53 // Manages the life cycle of a new policy map during until its life cycle is
54 // taken over by the policy loader.
55 class UpdatePolicyTask : public Task {
56 public:
UpdatePolicyTask(scoped_refptr<AsynchronousPolicyLoader> loader,DictionaryValue * new_policy)57 UpdatePolicyTask(scoped_refptr<AsynchronousPolicyLoader> loader,
58 DictionaryValue* new_policy)
59 : loader_(loader),
60 new_policy_(new_policy) {}
61
Run()62 virtual void Run() {
63 loader_->UpdatePolicy(new_policy_.release());
64 }
65
66 private:
67 scoped_refptr<AsynchronousPolicyLoader> loader_;
68 scoped_ptr<DictionaryValue> new_policy_;
69 DISALLOW_COPY_AND_ASSIGN(UpdatePolicyTask);
70 };
71
Reload()72 void AsynchronousPolicyLoader::Reload() {
73 if (delegate_.get()) {
74 DictionaryValue* new_policy = delegate_->Load();
75 PostUpdatePolicyTask(new_policy);
76 }
77 }
78
AddObserver(ConfigurationPolicyProvider::Observer * observer)79 void AsynchronousPolicyLoader::AddObserver(
80 ConfigurationPolicyProvider::Observer* observer) {
81 observer_list_.AddObserver(observer);
82 }
83
RemoveObserver(ConfigurationPolicyProvider::Observer * observer)84 void AsynchronousPolicyLoader::RemoveObserver(
85 ConfigurationPolicyProvider::Observer* observer) {
86 observer_list_.RemoveObserver(observer);
87 }
88
CancelReloadTask()89 void AsynchronousPolicyLoader::CancelReloadTask() {
90 if (reload_task_) {
91 // Only check the thread if there's still a reload task. During
92 // destruction of unit tests, the message loop destruction can
93 // call this method when the file thread is no longer around,
94 // but in that case reload_task_ is NULL.
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
96 reload_task_->Cancel();
97 reload_task_ = NULL;
98 }
99 }
100
ScheduleReloadTask(const base::TimeDelta & delay)101 void AsynchronousPolicyLoader::ScheduleReloadTask(
102 const base::TimeDelta& delay) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
104
105 CancelReloadTask();
106
107 reload_task_ =
108 NewRunnableMethod(this, &AsynchronousPolicyLoader::ReloadFromTask);
109 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE, reload_task_,
110 delay.InMilliseconds());
111 }
112
ScheduleFallbackReloadTask()113 void AsynchronousPolicyLoader::ScheduleFallbackReloadTask() {
114 // As a safeguard in case that the load delegate failed to timely notice a
115 // change in policy, schedule a reload task that'll make us recheck after a
116 // reasonable interval.
117 ScheduleReloadTask(reload_interval_);
118 }
119
ReloadFromTask()120 void AsynchronousPolicyLoader::ReloadFromTask() {
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
122
123 // Drop the reference to the reload task, since the task might be the only
124 // referrer that keeps us alive, so we should not Cancel() it.
125 reload_task_ = NULL;
126
127 Reload();
128 }
129
InitOnFileThread()130 void AsynchronousPolicyLoader::InitOnFileThread() {
131 }
132
StopOnFileThread()133 void AsynchronousPolicyLoader::StopOnFileThread() {
134 CancelReloadTask();
135 }
136
PostUpdatePolicyTask(DictionaryValue * new_policy)137 void AsynchronousPolicyLoader::PostUpdatePolicyTask(
138 DictionaryValue* new_policy) {
139 origin_loop_->PostTask(FROM_HERE, new UpdatePolicyTask(this, new_policy));
140 }
141
UpdatePolicy(DictionaryValue * new_policy_raw)142 void AsynchronousPolicyLoader::UpdatePolicy(DictionaryValue* new_policy_raw) {
143 scoped_ptr<DictionaryValue> new_policy(new_policy_raw);
144 DCHECK(policy_.get());
145 if (!policy_->Equals(new_policy.get())) {
146 policy_.reset(new_policy.release());
147 FOR_EACH_OBSERVER(ConfigurationPolicyProvider::Observer,
148 observer_list_,
149 OnUpdatePolicy());
150 }
151 }
152
InitAfterFileThreadAvailable()153 void AsynchronousPolicyLoader::InitAfterFileThreadAvailable() {
154 if (!stopped_) {
155 BrowserThread::PostTask(
156 BrowserThread::FILE, FROM_HERE,
157 NewRunnableMethod(this, &AsynchronousPolicyLoader::InitOnFileThread));
158 }
159 }
160
161 } // namespace policy
162