cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / components / policy / core / common / policy_service_impl.cc
blob96922ace797cebb1970bbf359acaa8f729bb74c1
1 // Copyright (c) 2012 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.
5 #include "components/policy/core/common/policy_service_impl.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/values.h"
15 #include "components/policy/core/common/policy_bundle.h"
16 #include "components/policy/core/common/policy_map.h"
17 #include "policy/policy_constants.h"
19 namespace policy {
21 typedef PolicyServiceImpl::Providers::const_iterator Iterator;
23 namespace {
25 const char* kProxyPolicies[] = {
26 key::kProxyMode,
27 key::kProxyServerMode,
28 key::kProxyServer,
29 key::kProxyPacUrl,
30 key::kProxyBypassList,
33 void FixDeprecatedPolicies(PolicyMap* policies) {
34 // Proxy settings have been configured by 5 policies that didn't mix well
35 // together, and maps of policies had to take this into account when merging
36 // policy sources. The proxy settings will eventually be configured by a
37 // single Dictionary policy when all providers have support for that. For
38 // now, the individual policies are mapped here to a single Dictionary policy
39 // that the rest of the policy machinery uses.
41 // The highest (level, scope) pair for an existing proxy policy is determined
42 // first, and then only policies with those exact attributes are merged.
43 PolicyMap::Entry current_priority; // Defaults to the lowest priority.
44 scoped_ptr<base::DictionaryValue> proxy_settings(new base::DictionaryValue);
45 for (size_t i = 0; i < arraysize(kProxyPolicies); ++i) {
46 const PolicyMap::Entry* entry = policies->Get(kProxyPolicies[i]);
47 if (entry) {
48 if (entry->has_higher_priority_than(current_priority)) {
49 proxy_settings->Clear();
50 current_priority = *entry;
52 if (!entry->has_higher_priority_than(current_priority) &&
53 !current_priority.has_higher_priority_than(*entry)) {
54 proxy_settings->Set(kProxyPolicies[i], entry->value->DeepCopy());
56 policies->Erase(kProxyPolicies[i]);
59 // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the
60 // new priority is higher.
61 const PolicyMap::Entry* existing = policies->Get(key::kProxySettings);
62 if (!proxy_settings->empty() &&
63 (!existing || current_priority.has_higher_priority_than(*existing))) {
64 policies->Set(key::kProxySettings,
65 current_priority.level,
66 current_priority.scope,
67 proxy_settings.release(),
68 NULL);
72 } // namespace
74 PolicyServiceImpl::PolicyServiceImpl(const Providers& providers)
75 : update_task_ptr_factory_(this) {
76 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain)
77 initialization_complete_[domain] = true;
78 providers_ = providers;
79 for (Iterator it = providers.begin(); it != providers.end(); ++it) {
80 ConfigurationPolicyProvider* provider = *it;
81 provider->AddObserver(this);
82 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) {
83 initialization_complete_[domain] &=
84 provider->IsInitializationComplete(static_cast<PolicyDomain>(domain));
87 // There are no observers yet, but calls to GetPolicies() should already get
88 // the processed policy values.
89 MergeAndTriggerUpdates();
92 PolicyServiceImpl::~PolicyServiceImpl() {
93 DCHECK(thread_checker_.CalledOnValidThread());
94 for (Iterator it = providers_.begin(); it != providers_.end(); ++it)
95 (*it)->RemoveObserver(this);
96 STLDeleteValues(&observers_);
99 void PolicyServiceImpl::AddObserver(PolicyDomain domain,
100 PolicyService::Observer* observer) {
101 DCHECK(thread_checker_.CalledOnValidThread());
102 Observers*& list = observers_[domain];
103 if (!list)
104 list = new Observers();
105 list->AddObserver(observer);
108 void PolicyServiceImpl::RemoveObserver(PolicyDomain domain,
109 PolicyService::Observer* observer) {
110 DCHECK(thread_checker_.CalledOnValidThread());
111 ObserverMap::iterator it = observers_.find(domain);
112 if (it == observers_.end()) {
113 NOTREACHED();
114 return;
116 it->second->RemoveObserver(observer);
117 if (!it->second->might_have_observers()) {
118 delete it->second;
119 observers_.erase(it);
123 const PolicyMap& PolicyServiceImpl::GetPolicies(
124 const PolicyNamespace& ns) const {
125 DCHECK(thread_checker_.CalledOnValidThread());
126 return policy_bundle_.Get(ns);
129 bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain) const {
130 DCHECK(thread_checker_.CalledOnValidThread());
131 DCHECK(domain >= 0 && domain < POLICY_DOMAIN_SIZE);
132 return initialization_complete_[domain];
135 void PolicyServiceImpl::RefreshPolicies(const base::Closure& callback) {
136 DCHECK(thread_checker_.CalledOnValidThread());
138 if (!callback.is_null())
139 refresh_callbacks_.push_back(callback);
141 if (providers_.empty()) {
142 // Refresh is immediately complete if there are no providers. See the note
143 // on OnUpdatePolicy() about why this is a posted task.
144 update_task_ptr_factory_.InvalidateWeakPtrs();
145 base::ThreadTaskRunnerHandle::Get()->PostTask(
146 FROM_HERE, base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
147 update_task_ptr_factory_.GetWeakPtr()));
148 } else {
149 // Some providers might invoke OnUpdatePolicy synchronously while handling
150 // RefreshPolicies. Mark all as pending before refreshing.
151 for (Iterator it = providers_.begin(); it != providers_.end(); ++it)
152 refresh_pending_.insert(*it);
153 for (Iterator it = providers_.begin(); it != providers_.end(); ++it)
154 (*it)->RefreshPolicies();
158 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider* provider) {
159 DCHECK_EQ(1, std::count(providers_.begin(), providers_.end(), provider));
160 refresh_pending_.erase(provider);
162 // Note: a policy change may trigger further policy changes in some providers.
163 // For example, disabling SigninAllowed would cause the CloudPolicyManager to
164 // drop all its policies, which makes this method enter again for that
165 // provider.
167 // Therefore this update is posted asynchronously, to prevent reentrancy in
168 // MergeAndTriggerUpdates. Also, cancel a pending update if there is any,
169 // since both will produce the same PolicyBundle.
170 update_task_ptr_factory_.InvalidateWeakPtrs();
171 base::ThreadTaskRunnerHandle::Get()->PostTask(
172 FROM_HERE, base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
173 update_task_ptr_factory_.GetWeakPtr()));
176 void PolicyServiceImpl::NotifyNamespaceUpdated(
177 const PolicyNamespace& ns,
178 const PolicyMap& previous,
179 const PolicyMap& current) {
180 DCHECK(thread_checker_.CalledOnValidThread());
181 ObserverMap::iterator iterator = observers_.find(ns.domain);
182 if (iterator != observers_.end()) {
183 FOR_EACH_OBSERVER(PolicyService::Observer,
184 *iterator->second,
185 OnPolicyUpdated(ns, previous, current));
189 void PolicyServiceImpl::MergeAndTriggerUpdates() {
190 // Merge from each provider in their order of priority.
191 const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
192 PolicyBundle bundle;
193 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) {
194 PolicyBundle provided_bundle;
195 provided_bundle.CopyFrom((*it)->policies());
196 FixDeprecatedPolicies(&provided_bundle.Get(chrome_namespace));
197 bundle.MergeFrom(provided_bundle);
200 // Swap first, so that observers that call GetPolicies() see the current
201 // values.
202 policy_bundle_.Swap(&bundle);
204 // Only notify observers of namespaces that have been modified.
205 const PolicyMap kEmpty;
206 PolicyBundle::const_iterator it_new = policy_bundle_.begin();
207 PolicyBundle::const_iterator end_new = policy_bundle_.end();
208 PolicyBundle::const_iterator it_old = bundle.begin();
209 PolicyBundle::const_iterator end_old = bundle.end();
210 while (it_new != end_new && it_old != end_old) {
211 if (it_new->first < it_old->first) {
212 // A new namespace is available.
213 NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second);
214 ++it_new;
215 } else if (it_old->first < it_new->first) {
216 // A previously available namespace is now gone.
217 NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty);
218 ++it_old;
219 } else {
220 if (!it_new->second->Equals(*it_old->second)) {
221 // An existing namespace's policies have changed.
222 NotifyNamespaceUpdated(it_new->first, *it_old->second, *it_new->second);
224 ++it_new;
225 ++it_old;
229 // Send updates for the remaining new namespaces, if any.
230 for (; it_new != end_new; ++it_new)
231 NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second);
233 // Sends updates for the remaining removed namespaces, if any.
234 for (; it_old != end_old; ++it_old)
235 NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty);
237 CheckInitializationComplete();
238 CheckRefreshComplete();
241 void PolicyServiceImpl::CheckInitializationComplete() {
242 DCHECK(thread_checker_.CalledOnValidThread());
244 // Check if all the providers just became initialized for each domain; if so,
245 // notify that domain's observers.
246 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) {
247 if (initialization_complete_[domain])
248 continue;
250 PolicyDomain policy_domain = static_cast<PolicyDomain>(domain);
252 bool all_complete = true;
253 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) {
254 if (!(*it)->IsInitializationComplete(policy_domain)) {
255 all_complete = false;
256 break;
259 if (all_complete) {
260 initialization_complete_[domain] = true;
261 ObserverMap::iterator iter = observers_.find(policy_domain);
262 if (iter != observers_.end()) {
263 FOR_EACH_OBSERVER(PolicyService::Observer,
264 *iter->second,
265 OnPolicyServiceInitialized(policy_domain));
271 void PolicyServiceImpl::CheckRefreshComplete() {
272 // Invoke all the callbacks if a refresh has just fully completed.
273 if (refresh_pending_.empty() && !refresh_callbacks_.empty()) {
274 std::vector<base::Closure> callbacks;
275 callbacks.swap(refresh_callbacks_);
276 std::vector<base::Closure>::iterator it;
277 for (it = callbacks.begin(); it != callbacks.end(); ++it)
278 it->Run();
282 } // namespace policy