Ignore title parameter for navigator.registerProtocolHandler
[chromium-blink-merge.git] / components / policy / core / common / policy_service_impl.cc
blobd515d1923da2877cd06551d7f7d16b9ca7bfe9e4
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/message_loop/message_loop.h"
11 #include "base/stl_util.h"
12 #include "base/values.h"
13 #include "components/policy/core/common/policy_bundle.h"
14 #include "components/policy/core/common/policy_map.h"
15 #include "policy/policy_constants.h"
17 namespace policy {
19 typedef PolicyServiceImpl::Providers::const_iterator Iterator;
21 namespace {
23 const char* kProxyPolicies[] = {
24 key::kProxyMode,
25 key::kProxyServerMode,
26 key::kProxyServer,
27 key::kProxyPacUrl,
28 key::kProxyBypassList,
31 void FixDeprecatedPolicies(PolicyMap* policies) {
32 // Proxy settings have been configured by 5 policies that didn't mix well
33 // together, and maps of policies had to take this into account when merging
34 // policy sources. The proxy settings will eventually be configured by a
35 // single Dictionary policy when all providers have support for that. For
36 // now, the individual policies are mapped here to a single Dictionary policy
37 // that the rest of the policy machinery uses.
39 // The highest (level, scope) pair for an existing proxy policy is determined
40 // first, and then only policies with those exact attributes are merged.
41 PolicyMap::Entry current_priority; // Defaults to the lowest priority.
42 scoped_ptr<base::DictionaryValue> proxy_settings(new base::DictionaryValue);
43 for (size_t i = 0; i < arraysize(kProxyPolicies); ++i) {
44 const PolicyMap::Entry* entry = policies->Get(kProxyPolicies[i]);
45 if (entry) {
46 if (entry->has_higher_priority_than(current_priority)) {
47 proxy_settings->Clear();
48 current_priority = *entry;
50 if (!entry->has_higher_priority_than(current_priority) &&
51 !current_priority.has_higher_priority_than(*entry)) {
52 proxy_settings->Set(kProxyPolicies[i], entry->value->DeepCopy());
54 policies->Erase(kProxyPolicies[i]);
57 // Sets the new |proxy_settings| if kProxySettings isn't set yet, or if the
58 // new priority is higher.
59 const PolicyMap::Entry* existing = policies->Get(key::kProxySettings);
60 if (!proxy_settings->empty() &&
61 (!existing || current_priority.has_higher_priority_than(*existing))) {
62 policies->Set(key::kProxySettings,
63 current_priority.level,
64 current_priority.scope,
65 proxy_settings.release(),
66 NULL);
70 } // namespace
72 PolicyServiceImpl::PolicyServiceImpl(const Providers& providers)
73 : update_task_ptr_factory_(this) {
74 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain)
75 initialization_complete_[domain] = true;
76 providers_ = providers;
77 for (Iterator it = providers.begin(); it != providers.end(); ++it) {
78 ConfigurationPolicyProvider* provider = *it;
79 provider->AddObserver(this);
80 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) {
81 initialization_complete_[domain] &=
82 provider->IsInitializationComplete(static_cast<PolicyDomain>(domain));
85 // There are no observers yet, but calls to GetPolicies() should already get
86 // the processed policy values.
87 MergeAndTriggerUpdates();
90 PolicyServiceImpl::~PolicyServiceImpl() {
91 for (Iterator it = providers_.begin(); it != providers_.end(); ++it)
92 (*it)->RemoveObserver(this);
93 STLDeleteValues(&observers_);
96 void PolicyServiceImpl::AddObserver(PolicyDomain domain,
97 PolicyService::Observer* observer) {
98 Observers*& list = observers_[domain];
99 if (!list)
100 list = new Observers();
101 list->AddObserver(observer);
104 void PolicyServiceImpl::RemoveObserver(PolicyDomain domain,
105 PolicyService::Observer* observer) {
106 ObserverMap::iterator it = observers_.find(domain);
107 if (it == observers_.end()) {
108 NOTREACHED();
109 return;
111 it->second->RemoveObserver(observer);
112 if (!it->second->might_have_observers()) {
113 delete it->second;
114 observers_.erase(it);
118 const PolicyMap& PolicyServiceImpl::GetPolicies(
119 const PolicyNamespace& ns) const {
120 return policy_bundle_.Get(ns);
123 bool PolicyServiceImpl::IsInitializationComplete(PolicyDomain domain) const {
124 DCHECK(domain >= 0 && domain < POLICY_DOMAIN_SIZE);
125 return initialization_complete_[domain];
128 void PolicyServiceImpl::RefreshPolicies(const base::Closure& callback) {
129 if (!callback.is_null())
130 refresh_callbacks_.push_back(callback);
132 if (providers_.empty()) {
133 // Refresh is immediately complete if there are no providers. See the note
134 // on OnUpdatePolicy() about why this is a posted task.
135 update_task_ptr_factory_.InvalidateWeakPtrs();
136 base::MessageLoop::current()->PostTask(
137 FROM_HERE,
138 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
139 update_task_ptr_factory_.GetWeakPtr()));
140 } else {
141 // Some providers might invoke OnUpdatePolicy synchronously while handling
142 // RefreshPolicies. Mark all as pending before refreshing.
143 for (Iterator it = providers_.begin(); it != providers_.end(); ++it)
144 refresh_pending_.insert(*it);
145 for (Iterator it = providers_.begin(); it != providers_.end(); ++it)
146 (*it)->RefreshPolicies();
150 void PolicyServiceImpl::OnUpdatePolicy(ConfigurationPolicyProvider* provider) {
151 DCHECK_EQ(1, std::count(providers_.begin(), providers_.end(), provider));
152 refresh_pending_.erase(provider);
154 // Note: a policy change may trigger further policy changes in some providers.
155 // For example, disabling SigninAllowed would cause the CloudPolicyManager to
156 // drop all its policies, which makes this method enter again for that
157 // provider.
159 // Therefore this update is posted asynchronously, to prevent reentrancy in
160 // MergeAndTriggerUpdates. Also, cancel a pending update if there is any,
161 // since both will produce the same PolicyBundle.
162 update_task_ptr_factory_.InvalidateWeakPtrs();
163 base::MessageLoop::current()->PostTask(
164 FROM_HERE,
165 base::Bind(&PolicyServiceImpl::MergeAndTriggerUpdates,
166 update_task_ptr_factory_.GetWeakPtr()));
169 void PolicyServiceImpl::NotifyNamespaceUpdated(
170 const PolicyNamespace& ns,
171 const PolicyMap& previous,
172 const PolicyMap& current) {
173 ObserverMap::iterator iterator = observers_.find(ns.domain);
174 if (iterator != observers_.end()) {
175 FOR_EACH_OBSERVER(PolicyService::Observer,
176 *iterator->second,
177 OnPolicyUpdated(ns, previous, current));
181 void PolicyServiceImpl::MergeAndTriggerUpdates() {
182 // Merge from each provider in their order of priority.
183 const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
184 PolicyBundle bundle;
185 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) {
186 PolicyBundle provided_bundle;
187 provided_bundle.CopyFrom((*it)->policies());
188 FixDeprecatedPolicies(&provided_bundle.Get(chrome_namespace));
189 bundle.MergeFrom(provided_bundle);
192 // Swap first, so that observers that call GetPolicies() see the current
193 // values.
194 policy_bundle_.Swap(&bundle);
196 // Only notify observers of namespaces that have been modified.
197 const PolicyMap kEmpty;
198 PolicyBundle::const_iterator it_new = policy_bundle_.begin();
199 PolicyBundle::const_iterator end_new = policy_bundle_.end();
200 PolicyBundle::const_iterator it_old = bundle.begin();
201 PolicyBundle::const_iterator end_old = bundle.end();
202 while (it_new != end_new && it_old != end_old) {
203 if (it_new->first < it_old->first) {
204 // A new namespace is available.
205 NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second);
206 ++it_new;
207 } else if (it_old->first < it_new->first) {
208 // A previously available namespace is now gone.
209 NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty);
210 ++it_old;
211 } else {
212 if (!it_new->second->Equals(*it_old->second)) {
213 // An existing namespace's policies have changed.
214 NotifyNamespaceUpdated(it_new->first, *it_old->second, *it_new->second);
216 ++it_new;
217 ++it_old;
221 // Send updates for the remaining new namespaces, if any.
222 for (; it_new != end_new; ++it_new)
223 NotifyNamespaceUpdated(it_new->first, kEmpty, *it_new->second);
225 // Sends updates for the remaining removed namespaces, if any.
226 for (; it_old != end_old; ++it_old)
227 NotifyNamespaceUpdated(it_old->first, *it_old->second, kEmpty);
229 CheckInitializationComplete();
230 CheckRefreshComplete();
233 void PolicyServiceImpl::CheckInitializationComplete() {
234 // Check if all the providers just became initialized for each domain; if so,
235 // notify that domain's observers.
236 for (int domain = 0; domain < POLICY_DOMAIN_SIZE; ++domain) {
237 if (initialization_complete_[domain])
238 continue;
240 PolicyDomain policy_domain = static_cast<PolicyDomain>(domain);
242 bool all_complete = true;
243 for (Iterator it = providers_.begin(); it != providers_.end(); ++it) {
244 if (!(*it)->IsInitializationComplete(policy_domain)) {
245 all_complete = false;
246 break;
249 if (all_complete) {
250 initialization_complete_[domain] = true;
251 ObserverMap::iterator iter = observers_.find(policy_domain);
252 if (iter != observers_.end()) {
253 FOR_EACH_OBSERVER(PolicyService::Observer,
254 *iter->second,
255 OnPolicyServiceInitialized(policy_domain));
261 void PolicyServiceImpl::CheckRefreshComplete() {
262 // Invoke all the callbacks if a refresh has just fully completed.
263 if (refresh_pending_.empty() && !refresh_callbacks_.empty()) {
264 std::vector<base::Closure> callbacks;
265 callbacks.swap(refresh_callbacks_);
266 std::vector<base::Closure>::iterator it;
267 for (it = callbacks.begin(); it != callbacks.end(); ++it)
268 it->Run();
272 } // namespace policy