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_service.h"
      6 
      7 #include "base/values.h"
      8 #include "chrome/browser/prefs/pref_service.h"
      9 #include "chrome/browser/prefs/pref_set_observer.h"
     10 #include "chrome/browser/prefs/proxy_config_dictionary.h"
     11 #include "chrome/common/pref_names.h"
     12 #include "content/browser/browser_thread.h"
     13 #include "content/common/notification_details.h"
     14 #include "content/common/notification_source.h"
     15 #include "content/common/notification_type.h"
     16 
     17 PrefProxyConfigTracker::PrefProxyConfigTracker(PrefService* pref_service)
     18     : pref_service_(pref_service) {
     19   config_state_ = ReadPrefConfig(&pref_config_);
     20   proxy_prefs_observer_.reset(
     21       PrefSetObserver::CreateProxyPrefSetObserver(pref_service_, this));
     22 }
     23 
     24 PrefProxyConfigTracker::~PrefProxyConfigTracker() {
     25   DCHECK(pref_service_ == NULL);
     26 }
     27 
     28 PrefProxyConfigTracker::ConfigState
     29     PrefProxyConfigTracker::GetProxyConfig(net::ProxyConfig* config) {
     30   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     31   if (config_state_ != CONFIG_UNSET)
     32     *config = pref_config_;
     33   return config_state_;
     34 }
     35 
     36 void PrefProxyConfigTracker::DetachFromPrefService() {
     37   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     38   // Stop notifications.
     39   proxy_prefs_observer_.reset();
     40   pref_service_ = NULL;
     41 }
     42 
     43 void PrefProxyConfigTracker::AddObserver(
     44     PrefProxyConfigTracker::Observer* observer) {
     45   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     46   observers_.AddObserver(observer);
     47 }
     48 
     49 void PrefProxyConfigTracker::RemoveObserver(
     50     PrefProxyConfigTracker::Observer* observer) {
     51   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     52   observers_.RemoveObserver(observer);
     53 }
     54 
     55 void PrefProxyConfigTracker::Observe(NotificationType type,
     56                                      const NotificationSource& source,
     57                                      const NotificationDetails& details) {
     58   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     59   if (type == NotificationType::PREF_CHANGED &&
     60       Source<PrefService>(source).ptr() == pref_service_) {
     61     net::ProxyConfig new_config;
     62     ConfigState config_state = ReadPrefConfig(&new_config);
     63     BrowserThread::PostTask(
     64         BrowserThread::IO, FROM_HERE,
     65         NewRunnableMethod(this,
     66                           &PrefProxyConfigTracker::InstallProxyConfig,
     67                           new_config, config_state));
     68   } else {
     69     NOTREACHED() << "Unexpected notification of type " << type.value;
     70   }
     71 }
     72 
     73 void PrefProxyConfigTracker::InstallProxyConfig(
     74     const net::ProxyConfig& config,
     75     PrefProxyConfigTracker::ConfigState config_state) {
     76   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     77   if (config_state_ != config_state ||
     78       (config_state_ != CONFIG_UNSET && !pref_config_.Equals(config))) {
     79     config_state_ = config_state;
     80     if (config_state_ != CONFIG_UNSET)
     81       pref_config_ = config;
     82     FOR_EACH_OBSERVER(Observer, observers_, OnPrefProxyConfigChanged());
     83   }
     84 }
     85 
     86 PrefProxyConfigTracker::ConfigState
     87     PrefProxyConfigTracker::ReadPrefConfig(net::ProxyConfig* config) {
     88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     89 
     90   // Clear the configuration.
     91   *config = net::ProxyConfig();
     92 
     93   const PrefService::Preference* pref =
     94       pref_service_->FindPreference(prefs::kProxy);
     95   const DictionaryValue* dict = pref_service_->GetDictionary(prefs::kProxy);
     96   DCHECK(dict);
     97   ProxyConfigDictionary proxy_dict(dict);
     98 
     99   if (PrefConfigToNetConfig(proxy_dict, config)) {
    100     return (!pref->IsUserModifiable() || pref->HasUserSetting()) ?
    101         CONFIG_PRESENT : CONFIG_FALLBACK;
    102   }
    103 
    104   return CONFIG_UNSET;
    105 }
    106 
    107 bool PrefProxyConfigTracker::PrefConfigToNetConfig(
    108     const ProxyConfigDictionary& proxy_dict,
    109     net::ProxyConfig* config) {
    110   ProxyPrefs::ProxyMode mode;
    111   if (!proxy_dict.GetMode(&mode)) {
    112     // Fall back to system settings if the mode preference is invalid.
    113     return false;
    114   }
    115 
    116   switch (mode) {
    117     case ProxyPrefs::MODE_SYSTEM:
    118       // Use system settings.
    119       return true;
    120     case ProxyPrefs::MODE_DIRECT:
    121       // Ignore all the other proxy config preferences if the use of a proxy
    122       // has been explicitly disabled.
    123       return true;
    124     case ProxyPrefs::MODE_AUTO_DETECT:
    125       config->set_auto_detect(true);
    126       return true;
    127     case ProxyPrefs::MODE_PAC_SCRIPT: {
    128       std::string proxy_pac;
    129       if (!proxy_dict.GetPacUrl(&proxy_pac)) {
    130         LOG(ERROR) << "Proxy settings request PAC script but do not specify "
    131                    << "its URL. Falling back to direct connection.";
    132         return true;
    133       }
    134       GURL proxy_pac_url(proxy_pac);
    135       if (!proxy_pac_url.is_valid()) {
    136         LOG(ERROR) << "Invalid proxy PAC url: " << proxy_pac;
    137         return true;
    138       }
    139       config->set_pac_url(proxy_pac_url);
    140       return true;
    141     }
    142     case ProxyPrefs::MODE_FIXED_SERVERS: {
    143       std::string proxy_server;
    144       if (!proxy_dict.GetProxyServer(&proxy_server)) {
    145         LOG(ERROR) << "Proxy settings request fixed proxy servers but do not "
    146                    << "specify their URLs. Falling back to direct connection.";
    147         return true;
    148       }
    149       config->proxy_rules().ParseFromString(proxy_server);
    150 
    151       std::string proxy_bypass;
    152       if (proxy_dict.GetBypassList(&proxy_bypass)) {
    153         config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
    154       }
    155       return true;
    156     }
    157     case ProxyPrefs::kModeCount: {
    158       // Fall through to NOTREACHED().
    159     }
    160   }
    161   NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
    162   return false;
    163 }
    164 
    165 PrefProxyConfigService::PrefProxyConfigService(
    166     PrefProxyConfigTracker* tracker,
    167     net::ProxyConfigService* base_service)
    168     : base_service_(base_service),
    169       pref_config_tracker_(tracker),
    170       registered_observers_(false) {
    171 }
    172 
    173 PrefProxyConfigService::~PrefProxyConfigService() {
    174   if (registered_observers_) {
    175     base_service_->RemoveObserver(this);
    176     pref_config_tracker_->RemoveObserver(this);
    177   }
    178 }
    179 
    180 void PrefProxyConfigService::AddObserver(
    181     net::ProxyConfigService::Observer* observer) {
    182   RegisterObservers();
    183   observers_.AddObserver(observer);
    184 }
    185 
    186 void PrefProxyConfigService::RemoveObserver(
    187     net::ProxyConfigService::Observer* observer) {
    188   observers_.RemoveObserver(observer);
    189 }
    190 
    191 net::ProxyConfigService::ConfigAvailability
    192     PrefProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) {
    193   RegisterObservers();
    194   net::ProxyConfig pref_config;
    195   PrefProxyConfigTracker::ConfigState state =
    196       pref_config_tracker_->GetProxyConfig(&pref_config);
    197   if (state == PrefProxyConfigTracker::CONFIG_PRESENT) {
    198     *config = pref_config;
    199     return CONFIG_VALID;
    200   }
    201 
    202   // Ask the base service.
    203   ConfigAvailability available = base_service_->GetLatestProxyConfig(config);
    204 
    205   // Base service doesn't have a configuration, fall back to prefs or default.
    206   if (available == CONFIG_UNSET) {
    207     if (state == PrefProxyConfigTracker::CONFIG_FALLBACK)
    208       *config = pref_config;
    209     else
    210       *config = net::ProxyConfig::CreateDirect();
    211     return CONFIG_VALID;
    212   }
    213 
    214   return available;
    215 }
    216 
    217 void PrefProxyConfigService::OnLazyPoll() {
    218   base_service_->OnLazyPoll();
    219 }
    220 
    221 void PrefProxyConfigService::OnProxyConfigChanged(
    222     const net::ProxyConfig& config,
    223     ConfigAvailability availability) {
    224   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    225 
    226   // Check whether there is a proxy configuration defined by preferences. In
    227   // this case that proxy configuration takes precedence and the change event
    228   // from the delegate proxy service can be disregarded.
    229   net::ProxyConfig actual_config;
    230   if (pref_config_tracker_->GetProxyConfig(&actual_config) !=
    231           PrefProxyConfigTracker::CONFIG_PRESENT) {
    232     availability = GetLatestProxyConfig(&actual_config);
    233     FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
    234                       OnProxyConfigChanged(actual_config, availability));
    235   }
    236 }
    237 
    238 void PrefProxyConfigService::OnPrefProxyConfigChanged() {
    239   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    240 
    241   // Evaluate the proxy configuration. If GetLatestProxyConfig returns
    242   // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
    243   // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
    244   // called and broadcast the proxy configuration.
    245   // Note: If a switch between a preference proxy configuration and the system
    246   // proxy configuration occurs an unnecessary notification might get send if
    247   // the two configurations agree. This case should be rare however, so we don't
    248   // handle that case specially.
    249   net::ProxyConfig new_config;
    250   ConfigAvailability availability = GetLatestProxyConfig(&new_config);
    251   if (availability != CONFIG_PENDING) {
    252     FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
    253                       OnProxyConfigChanged(new_config, availability));
    254   }
    255 }
    256 
    257 void PrefProxyConfigService::RegisterObservers() {
    258   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    259   if (!registered_observers_) {
    260     base_service_->AddObserver(this);
    261     pref_config_tracker_->AddObserver(this);
    262     registered_observers_ = true;
    263   }
    264 }
    265 
    266 // static
    267 void PrefProxyConfigService::RegisterPrefs(PrefService* pref_service) {
    268   DictionaryValue* default_settings = ProxyConfigDictionary::CreateSystem();
    269   pref_service->RegisterDictionaryPref(prefs::kProxy, default_settings);
    270 }
    271