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