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 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 31 ChromeProxyConfigService::~ChromeProxyConfigService() { 32 if (registered_observer_ && base_service_.get()) 33 base_service_->RemoveObserver(this); 34 } 35 36 void ChromeProxyConfigService::AddObserver( 37 net::ProxyConfigService::Observer* observer) { 38 RegisterObserver(); 39 observers_.AddObserver(observer); 40 } 41 42 void ChromeProxyConfigService::RemoveObserver( 43 net::ProxyConfigService::Observer* observer) { 44 observers_.RemoveObserver(observer); 45 } 46 47 net::ProxyConfigService::ConfigAvailability 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 68 void ChromeProxyConfigService::OnLazyPoll() { 69 if (base_service_.get()) 70 base_service_->OnLazyPoll(); 71 } 72 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 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 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 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 139 PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() { 140 DCHECK(pref_service_ == NULL); 141 } 142 143 scoped_ptr<net::ProxyConfigService> 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 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 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 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 204 void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) { 205 DictionaryValue* default_settings = ProxyConfigDictionary::CreateSystem(); 206 registry->RegisterDictionaryPref(prefs::kProxy, default_settings); 207 } 208 209 // static 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 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 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 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 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 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