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