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.
5 #include "chrome/browser/net/pref_proxy_config_tracker_impl.h"
8 #include "base/prefs/pref_registry_simple.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/profiler/scoped_tracker.h"
11 #include "base/values.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/prefs/proxy_config_dictionary.h"
14 #include "chrome/common/pref_names.h"
15 #include "components/pref_registry/pref_registry_syncable.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_details.h"
18 #include "content/public/browser/notification_source.h"
20 using content::BrowserThread
;
22 //============================= ChromeProxyConfigService =======================
24 ChromeProxyConfigService::ChromeProxyConfigService(
25 net::ProxyConfigService
* base_service
)
26 : base_service_(base_service
),
27 pref_config_state_(ProxyPrefs::CONFIG_UNSET
),
28 pref_config_read_pending_(true),
29 registered_observer_(false) {
32 ChromeProxyConfigService::~ChromeProxyConfigService() {
33 if (registered_observer_
&& base_service_
.get())
34 base_service_
->RemoveObserver(this);
37 void ChromeProxyConfigService::AddObserver(
38 net::ProxyConfigService::Observer
* observer
) {
40 observers_
.AddObserver(observer
);
43 void ChromeProxyConfigService::RemoveObserver(
44 net::ProxyConfigService::Observer
* observer
) {
45 observers_
.RemoveObserver(observer
);
48 net::ProxyConfigService::ConfigAvailability
49 ChromeProxyConfigService::GetLatestProxyConfig(net::ProxyConfig
* config
) {
52 if (pref_config_read_pending_
)
53 return net::ProxyConfigService::CONFIG_PENDING
;
55 // Ask the base service if available.
56 net::ProxyConfig system_config
;
57 ConfigAvailability system_availability
=
58 net::ProxyConfigService::CONFIG_UNSET
;
59 if (base_service_
.get())
60 system_availability
= base_service_
->GetLatestProxyConfig(&system_config
);
62 ProxyPrefs::ConfigState config_state
;
63 return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
64 pref_config_state_
, pref_config_
,
65 system_availability
, system_config
, false,
66 &config_state
, config
);
69 void ChromeProxyConfigService::OnLazyPoll() {
70 if (base_service_
.get())
71 base_service_
->OnLazyPoll();
74 void ChromeProxyConfigService::UpdateProxyConfig(
75 ProxyPrefs::ConfigState config_state
,
76 const net::ProxyConfig
& config
) {
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
79 pref_config_read_pending_
= false;
80 pref_config_state_
= config_state
;
81 pref_config_
= config
;
83 if (!observers_
.might_have_observers())
86 // Evaluate the proxy configuration. If GetLatestProxyConfig returns
87 // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
88 // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
89 // called and broadcast the proxy configuration.
90 // Note: If a switch between a preference proxy configuration and the system
91 // proxy configuration occurs an unnecessary notification might get send if
92 // the two configurations agree. This case should be rare however, so we don't
93 // handle that case specially.
94 net::ProxyConfig new_config
;
95 ConfigAvailability availability
= GetLatestProxyConfig(&new_config
);
96 if (availability
!= CONFIG_PENDING
) {
97 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer
, observers_
,
98 OnProxyConfigChanged(new_config
, availability
));
102 void ChromeProxyConfigService::OnProxyConfigChanged(
103 const net::ProxyConfig
& config
,
104 ConfigAvailability availability
) {
105 // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is fixed.
106 tracked_objects::ScopedTracker
tracking_profile(
107 FROM_HERE_WITH_EXPLICIT_FUNCTION(
108 "455942 ChromeProxyConfigService::OnProxyConfigChanged"));
109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
111 // Check whether there is a proxy configuration defined by preferences. In
112 // this case that proxy configuration takes precedence and the change event
113 // from the delegate proxy service can be disregarded.
114 if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_
)) {
115 net::ProxyConfig actual_config
;
116 availability
= GetLatestProxyConfig(&actual_config
);
117 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer
, observers_
,
118 OnProxyConfigChanged(actual_config
, availability
));
122 void ChromeProxyConfigService::RegisterObserver() {
123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
124 if (!registered_observer_
&& base_service_
.get()) {
125 base_service_
->AddObserver(this);
126 registered_observer_
= true;
130 //========================= PrefProxyConfigTrackerImpl =========================
132 PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
133 PrefService
* pref_service
)
134 : pref_service_(pref_service
),
135 chrome_proxy_config_service_(NULL
),
136 update_pending_(true) {
137 config_state_
= ReadPrefConfig(pref_service_
, &pref_config_
);
138 proxy_prefs_
.Init(pref_service
);
139 proxy_prefs_
.Add(prefs::kProxy
,
140 base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged
,
141 base::Unretained(this)));
144 PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
145 DCHECK(pref_service_
== NULL
);
148 scoped_ptr
<net::ProxyConfigService
>
149 PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService(
150 scoped_ptr
<net::ProxyConfigService
> base_service
) {
151 chrome_proxy_config_service_
=
152 new ChromeProxyConfigService(base_service
.release());
153 VLOG(1) << this << ": set chrome proxy config service to "
154 << chrome_proxy_config_service_
;
155 if (chrome_proxy_config_service_
&& update_pending_
)
156 OnProxyConfigChanged(config_state_
, pref_config_
);
158 return scoped_ptr
<net::ProxyConfigService
>(chrome_proxy_config_service_
);
161 void PrefProxyConfigTrackerImpl::DetachFromPrefService() {
162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
163 // Stop notifications.
164 proxy_prefs_
.RemoveAll();
165 pref_service_
= NULL
;
166 chrome_proxy_config_service_
= NULL
;
170 bool PrefProxyConfigTrackerImpl::PrefPrecedes(
171 ProxyPrefs::ConfigState config_state
) {
172 return config_state
== ProxyPrefs::CONFIG_POLICY
||
173 config_state
== ProxyPrefs::CONFIG_EXTENSION
||
174 config_state
== ProxyPrefs::CONFIG_OTHER_PRECEDE
;
178 net::ProxyConfigService::ConfigAvailability
179 PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
180 ProxyPrefs::ConfigState pref_state
,
181 const net::ProxyConfig
& pref_config
,
182 net::ProxyConfigService::ConfigAvailability system_availability
,
183 const net::ProxyConfig
& system_config
,
184 bool ignore_fallback_config
,
185 ProxyPrefs::ConfigState
* effective_config_state
,
186 net::ProxyConfig
* effective_config
) {
187 *effective_config_state
= pref_state
;
189 if (PrefPrecedes(pref_state
)) {
190 *effective_config
= pref_config
;
191 return net::ProxyConfigService::CONFIG_VALID
;
194 // If there's no system proxy config, fall back to prefs or default.
195 if (system_availability
== net::ProxyConfigService::CONFIG_UNSET
) {
196 if (pref_state
== ProxyPrefs::CONFIG_FALLBACK
&& !ignore_fallback_config
)
197 *effective_config
= pref_config
;
199 *effective_config
= net::ProxyConfig::CreateDirect();
200 return net::ProxyConfigService::CONFIG_VALID
;
203 *effective_config_state
= ProxyPrefs::CONFIG_SYSTEM
;
204 *effective_config
= system_config
;
205 return system_availability
;
209 void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple
* registry
) {
210 base::DictionaryValue
* default_settings
=
211 ProxyConfigDictionary::CreateSystem();
212 registry
->RegisterDictionaryPref(prefs::kProxy
, default_settings
);
216 void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
217 user_prefs::PrefRegistrySyncable
* pref_service
) {
218 base::DictionaryValue
* default_settings
=
219 ProxyConfigDictionary::CreateSystem();
220 pref_service
->RegisterDictionaryPref(
223 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
227 ProxyPrefs::ConfigState
PrefProxyConfigTrackerImpl::ReadPrefConfig(
228 const PrefService
* pref_service
,
229 net::ProxyConfig
* config
) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
232 // Clear the configuration and source.
233 *config
= net::ProxyConfig();
234 ProxyPrefs::ConfigState config_state
= ProxyPrefs::CONFIG_UNSET
;
236 const PrefService::Preference
* pref
=
237 pref_service
->FindPreference(prefs::kProxy
);
240 const base::DictionaryValue
* dict
=
241 pref_service
->GetDictionary(prefs::kProxy
);
243 ProxyConfigDictionary
proxy_dict(dict
);
245 if (PrefConfigToNetConfig(proxy_dict
, config
)) {
246 if (!pref
->IsUserModifiable() || pref
->HasUserSetting()) {
247 if (pref
->IsManaged())
248 config_state
= ProxyPrefs::CONFIG_POLICY
;
249 else if (pref
->IsExtensionControlled())
250 config_state
= ProxyPrefs::CONFIG_EXTENSION
;
252 config_state
= ProxyPrefs::CONFIG_OTHER_PRECEDE
;
254 config_state
= ProxyPrefs::CONFIG_FALLBACK
;
261 ProxyPrefs::ConfigState
PrefProxyConfigTrackerImpl::GetProxyConfig(
262 net::ProxyConfig
* config
) {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
264 if (config_state_
!= ProxyPrefs::CONFIG_UNSET
)
265 *config
= pref_config_
;
266 return config_state_
;
269 void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
270 ProxyPrefs::ConfigState config_state
,
271 const net::ProxyConfig
& config
) {
272 if (!chrome_proxy_config_service_
) {
273 VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
274 update_pending_
= true;
277 update_pending_
= !BrowserThread::PostTask(
278 BrowserThread::IO
, FROM_HERE
,
279 base::Bind(&ChromeProxyConfigService::UpdateProxyConfig
,
280 base::Unretained(chrome_proxy_config_service_
),
281 config_state
, config
));
282 VLOG(1) << this << (update_pending_
? ": Error" : ": Done")
283 << " pushing proxy to UpdateProxyConfig";
286 bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
287 const ProxyConfigDictionary
& proxy_dict
,
288 net::ProxyConfig
* config
) {
289 ProxyPrefs::ProxyMode mode
;
290 if (!proxy_dict
.GetMode(&mode
)) {
291 // Fall back to system settings if the mode preference is invalid.
296 case ProxyPrefs::MODE_SYSTEM
:
297 // Use system settings.
299 case ProxyPrefs::MODE_DIRECT
:
300 // Ignore all the other proxy config preferences if the use of a proxy
301 // has been explicitly disabled.
303 case ProxyPrefs::MODE_AUTO_DETECT
:
304 config
->set_auto_detect(true);
306 case ProxyPrefs::MODE_PAC_SCRIPT
: {
307 std::string proxy_pac
;
308 if (!proxy_dict
.GetPacUrl(&proxy_pac
)) {
309 LOG(ERROR
) << "Proxy settings request PAC script but do not specify "
310 << "its URL. Falling back to direct connection.";
313 GURL
proxy_pac_url(proxy_pac
);
314 if (!proxy_pac_url
.is_valid()) {
315 LOG(ERROR
) << "Invalid proxy PAC url: " << proxy_pac
;
318 config
->set_pac_url(proxy_pac_url
);
319 bool pac_mandatory
= false;
320 proxy_dict
.GetPacMandatory(&pac_mandatory
);
321 config
->set_pac_mandatory(pac_mandatory
);
324 case ProxyPrefs::MODE_FIXED_SERVERS
: {
325 std::string proxy_server
;
326 if (!proxy_dict
.GetProxyServer(&proxy_server
)) {
327 LOG(ERROR
) << "Proxy settings request fixed proxy servers but do not "
328 << "specify their URLs. Falling back to direct connection.";
331 config
->proxy_rules().ParseFromString(proxy_server
);
333 std::string proxy_bypass
;
334 if (proxy_dict
.GetBypassList(&proxy_bypass
)) {
335 config
->proxy_rules().bypass_rules
.ParseFromString(proxy_bypass
);
339 case ProxyPrefs::kModeCount
: {
340 // Fall through to NOTREACHED().
343 NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
347 void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
349 net::ProxyConfig new_config
;
350 ProxyPrefs::ConfigState config_state
= ReadPrefConfig(pref_service_
,
352 if (config_state_
!= config_state
||
353 (config_state_
!= ProxyPrefs::CONFIG_UNSET
&&
354 !pref_config_
.Equals(new_config
))) {
355 config_state_
= config_state
;
356 if (config_state_
!= ProxyPrefs::CONFIG_UNSET
)
357 pref_config_
= new_config
;
358 update_pending_
= true;
361 OnProxyConfigChanged(config_state
, new_config
);