Home | History | Annotate | Download | only in net
      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/pref_registry/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   base::DictionaryValue* default_settings =
    206       ProxyConfigDictionary::CreateSystem();
    207   registry->RegisterDictionaryPref(prefs::kProxy, default_settings);
    208 }
    209 
    210 // static
    211 void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
    212     user_prefs::PrefRegistrySyncable* pref_service) {
    213   base::DictionaryValue* default_settings =
    214       ProxyConfigDictionary::CreateSystem();
    215   pref_service->RegisterDictionaryPref(
    216       prefs::kProxy,
    217       default_settings,
    218       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    219 }
    220 
    221 // static
    222 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::ReadPrefConfig(
    223     const PrefService* pref_service,
    224     net::ProxyConfig* config) {
    225   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    226 
    227   // Clear the configuration and source.
    228   *config = net::ProxyConfig();
    229   ProxyPrefs::ConfigState config_state = ProxyPrefs::CONFIG_UNSET;
    230 
    231   const PrefService::Preference* pref =
    232       pref_service->FindPreference(prefs::kProxy);
    233   DCHECK(pref);
    234 
    235   const base::DictionaryValue* dict =
    236       pref_service->GetDictionary(prefs::kProxy);
    237   DCHECK(dict);
    238   ProxyConfigDictionary proxy_dict(dict);
    239 
    240   if (PrefConfigToNetConfig(proxy_dict, config)) {
    241     if (!pref->IsUserModifiable() || pref->HasUserSetting()) {
    242       if (pref->IsManaged())
    243         config_state = ProxyPrefs::CONFIG_POLICY;
    244       else if (pref->IsExtensionControlled())
    245         config_state = ProxyPrefs::CONFIG_EXTENSION;
    246       else
    247         config_state = ProxyPrefs::CONFIG_OTHER_PRECEDE;
    248     } else  {
    249       config_state = ProxyPrefs::CONFIG_FALLBACK;
    250     }
    251   }
    252 
    253   return config_state;
    254 }
    255 
    256 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::GetProxyConfig(
    257     net::ProxyConfig* config) {
    258   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    259   if (config_state_ != ProxyPrefs::CONFIG_UNSET)
    260     *config = pref_config_;
    261   return config_state_;
    262 }
    263 
    264 void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
    265     ProxyPrefs::ConfigState config_state,
    266     const net::ProxyConfig& config) {
    267   if (!chrome_proxy_config_service_) {
    268     VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
    269     update_pending_ = true;
    270     return;
    271   }
    272   update_pending_ = !BrowserThread::PostTask(
    273       BrowserThread::IO, FROM_HERE,
    274       base::Bind(&ChromeProxyConfigService::UpdateProxyConfig,
    275                  base::Unretained(chrome_proxy_config_service_),
    276                  config_state, config));
    277   VLOG(1) << this << (update_pending_ ? ": Error" : ": Done")
    278           << " pushing proxy to UpdateProxyConfig";
    279 }
    280 
    281 bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
    282     const ProxyConfigDictionary& proxy_dict,
    283     net::ProxyConfig* config) {
    284   ProxyPrefs::ProxyMode mode;
    285   if (!proxy_dict.GetMode(&mode)) {
    286     // Fall back to system settings if the mode preference is invalid.
    287     return false;
    288   }
    289 
    290   switch (mode) {
    291     case ProxyPrefs::MODE_SYSTEM:
    292       // Use system settings.
    293       return false;
    294     case ProxyPrefs::MODE_DIRECT:
    295       // Ignore all the other proxy config preferences if the use of a proxy
    296       // has been explicitly disabled.
    297       return true;
    298     case ProxyPrefs::MODE_AUTO_DETECT:
    299       config->set_auto_detect(true);
    300       return true;
    301     case ProxyPrefs::MODE_PAC_SCRIPT: {
    302       std::string proxy_pac;
    303       if (!proxy_dict.GetPacUrl(&proxy_pac)) {
    304         LOG(ERROR) << "Proxy settings request PAC script but do not specify "
    305                    << "its URL. Falling back to direct connection.";
    306         return true;
    307       }
    308       GURL proxy_pac_url(proxy_pac);
    309       if (!proxy_pac_url.is_valid()) {
    310         LOG(ERROR) << "Invalid proxy PAC url: " << proxy_pac;
    311         return true;
    312       }
    313       config->set_pac_url(proxy_pac_url);
    314       bool pac_mandatory = false;
    315       proxy_dict.GetPacMandatory(&pac_mandatory);
    316       config->set_pac_mandatory(pac_mandatory);
    317       return true;
    318     }
    319     case ProxyPrefs::MODE_FIXED_SERVERS: {
    320       std::string proxy_server;
    321       if (!proxy_dict.GetProxyServer(&proxy_server)) {
    322         LOG(ERROR) << "Proxy settings request fixed proxy servers but do not "
    323                    << "specify their URLs. Falling back to direct connection.";
    324         return true;
    325       }
    326       config->proxy_rules().ParseFromString(proxy_server);
    327 
    328       std::string proxy_bypass;
    329       if (proxy_dict.GetBypassList(&proxy_bypass)) {
    330         config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
    331       }
    332       return true;
    333     }
    334     case ProxyPrefs::kModeCount: {
    335       // Fall through to NOTREACHED().
    336     }
    337   }
    338   NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
    339   return false;
    340 }
    341 
    342 void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
    343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    344   net::ProxyConfig new_config;
    345   ProxyPrefs::ConfigState config_state = ReadPrefConfig(pref_service_,
    346                                                         &new_config);
    347   if (config_state_ != config_state ||
    348       (config_state_ != ProxyPrefs::CONFIG_UNSET &&
    349        !pref_config_.Equals(new_config))) {
    350     config_state_ = config_state;
    351     if (config_state_ != ProxyPrefs::CONFIG_UNSET)
    352       pref_config_ = new_config;
    353     update_pending_ = true;
    354   }
    355   if (update_pending_)
    356     OnProxyConfigChanged(config_state, new_config);
    357 }
    358