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