• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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