root/chrome/browser/net/pref_proxy_config_tracker_impl.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. registered_observer_
  2. AddObserver
  3. RemoveObserver
  4. GetLatestProxyConfig
  5. OnLazyPoll
  6. UpdateProxyConfig
  7. OnProxyConfigChanged
  8. RegisterObserver
  9. update_pending_
  10. CreateTrackingProxyConfigService
  11. DetachFromPrefService
  12. PrefPrecedes
  13. GetEffectiveProxyConfig
  14. RegisterPrefs
  15. RegisterProfilePrefs
  16. ReadPrefConfig
  17. GetProxyConfig
  18. OnProxyConfigChanged
  19. PrefConfigToNetConfig
  20. OnProxyPrefChanged

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/net/pref_proxy_config_tracker_impl.h"

#include "base/bind.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/prefs/proxy_config_dictionary.h"
#include "chrome/common/pref_names.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"

using content::BrowserThread;

//============================= ChromeProxyConfigService =======================

ChromeProxyConfigService::ChromeProxyConfigService(
    net::ProxyConfigService* base_service)
    : base_service_(base_service),
      pref_config_state_(ProxyPrefs::CONFIG_UNSET),
      pref_config_read_pending_(true),
      registered_observer_(false) {
}

ChromeProxyConfigService::~ChromeProxyConfigService() {
  if (registered_observer_ && base_service_.get())
    base_service_->RemoveObserver(this);
}

void ChromeProxyConfigService::AddObserver(
    net::ProxyConfigService::Observer* observer) {
  RegisterObserver();
  observers_.AddObserver(observer);
}

void ChromeProxyConfigService::RemoveObserver(
    net::ProxyConfigService::Observer* observer) {
  observers_.RemoveObserver(observer);
}

net::ProxyConfigService::ConfigAvailability
    ChromeProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) {
  RegisterObserver();

  if (pref_config_read_pending_)
    return net::ProxyConfigService::CONFIG_PENDING;

  // Ask the base service if available.
  net::ProxyConfig system_config;
  ConfigAvailability system_availability =
      net::ProxyConfigService::CONFIG_UNSET;
  if (base_service_.get())
    system_availability = base_service_->GetLatestProxyConfig(&system_config);

  ProxyPrefs::ConfigState config_state;
  return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
      pref_config_state_, pref_config_,
      system_availability, system_config, false,
      &config_state, config);
}

void ChromeProxyConfigService::OnLazyPoll() {
  if (base_service_.get())
    base_service_->OnLazyPoll();
}

void ChromeProxyConfigService::UpdateProxyConfig(
    ProxyPrefs::ConfigState config_state,
    const net::ProxyConfig& config) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  pref_config_read_pending_ = false;
  pref_config_state_ = config_state;
  pref_config_ = config;

  if (!observers_.might_have_observers())
    return;

  // Evaluate the proxy configuration. If GetLatestProxyConfig returns
  // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
  // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
  // called and broadcast the proxy configuration.
  // Note: If a switch between a preference proxy configuration and the system
  // proxy configuration occurs an unnecessary notification might get send if
  // the two configurations agree. This case should be rare however, so we don't
  // handle that case specially.
  net::ProxyConfig new_config;
  ConfigAvailability availability = GetLatestProxyConfig(&new_config);
  if (availability != CONFIG_PENDING) {
    FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
                      OnProxyConfigChanged(new_config, availability));
  }
}

void ChromeProxyConfigService::OnProxyConfigChanged(
    const net::ProxyConfig& config,
    ConfigAvailability availability) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  // Check whether there is a proxy configuration defined by preferences. In
  // this case that proxy configuration takes precedence and the change event
  // from the delegate proxy service can be disregarded.
  if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_)) {
    net::ProxyConfig actual_config;
    availability = GetLatestProxyConfig(&actual_config);
    FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
                      OnProxyConfigChanged(actual_config, availability));
  }
}

void ChromeProxyConfigService::RegisterObserver() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  if (!registered_observer_ && base_service_.get()) {
    base_service_->AddObserver(this);
    registered_observer_ = true;
  }
}

//========================= PrefProxyConfigTrackerImpl =========================

PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
    PrefService* pref_service)
    : pref_service_(pref_service),
      chrome_proxy_config_service_(NULL),
      update_pending_(true) {
  config_state_ = ReadPrefConfig(pref_service_, &pref_config_);
  proxy_prefs_.Init(pref_service);
  proxy_prefs_.Add(prefs::kProxy,
                   base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged,
                              base::Unretained(this)));
}

PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
  DCHECK(pref_service_ == NULL);
}

scoped_ptr<net::ProxyConfigService>
PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService(
    scoped_ptr<net::ProxyConfigService> base_service) {
  chrome_proxy_config_service_ =
      new ChromeProxyConfigService(base_service.release());
  VLOG(1) << this << ": set chrome proxy config service to "
          << chrome_proxy_config_service_;
  if (chrome_proxy_config_service_ && update_pending_)
    OnProxyConfigChanged(config_state_, pref_config_);

  return scoped_ptr<net::ProxyConfigService>(chrome_proxy_config_service_);
}

void PrefProxyConfigTrackerImpl::DetachFromPrefService() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Stop notifications.
  proxy_prefs_.RemoveAll();
  pref_service_ = NULL;
  chrome_proxy_config_service_ = NULL;
}

// static
bool PrefProxyConfigTrackerImpl::PrefPrecedes(
    ProxyPrefs::ConfigState config_state) {
  return config_state == ProxyPrefs::CONFIG_POLICY ||
         config_state == ProxyPrefs::CONFIG_EXTENSION ||
         config_state == ProxyPrefs::CONFIG_OTHER_PRECEDE;
}

// static
net::ProxyConfigService::ConfigAvailability
    PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
        ProxyPrefs::ConfigState pref_state,
        const net::ProxyConfig& pref_config,
        net::ProxyConfigService::ConfigAvailability system_availability,
        const net::ProxyConfig& system_config,
        bool ignore_fallback_config,
        ProxyPrefs::ConfigState* effective_config_state,
        net::ProxyConfig* effective_config) {
  *effective_config_state = pref_state;

  if (PrefPrecedes(pref_state)) {
    *effective_config = pref_config;
    return net::ProxyConfigService::CONFIG_VALID;
  }

  // If there's no system proxy config, fall back to prefs or default.
  if (system_availability == net::ProxyConfigService::CONFIG_UNSET) {
    if (pref_state == ProxyPrefs::CONFIG_FALLBACK && !ignore_fallback_config)
      *effective_config = pref_config;
    else
      *effective_config = net::ProxyConfig::CreateDirect();
    return net::ProxyConfigService::CONFIG_VALID;
  }

  *effective_config_state = ProxyPrefs::CONFIG_SYSTEM;
  *effective_config = system_config;
  return system_availability;
}

// static
void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
  base::DictionaryValue* default_settings =
      ProxyConfigDictionary::CreateSystem();
  registry->RegisterDictionaryPref(prefs::kProxy, default_settings);
}

// static
void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* pref_service) {
  base::DictionaryValue* default_settings =
      ProxyConfigDictionary::CreateSystem();
  pref_service->RegisterDictionaryPref(
      prefs::kProxy,
      default_settings,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}

// static
ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::ReadPrefConfig(
    const PrefService* pref_service,
    net::ProxyConfig* config) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Clear the configuration and source.
  *config = net::ProxyConfig();
  ProxyPrefs::ConfigState config_state = ProxyPrefs::CONFIG_UNSET;

  const PrefService::Preference* pref =
      pref_service->FindPreference(prefs::kProxy);
  DCHECK(pref);

  const base::DictionaryValue* dict =
      pref_service->GetDictionary(prefs::kProxy);
  DCHECK(dict);
  ProxyConfigDictionary proxy_dict(dict);

  if (PrefConfigToNetConfig(proxy_dict, config)) {
    if (!pref->IsUserModifiable() || pref->HasUserSetting()) {
      if (pref->IsManaged())
        config_state = ProxyPrefs::CONFIG_POLICY;
      else if (pref->IsExtensionControlled())
        config_state = ProxyPrefs::CONFIG_EXTENSION;
      else
        config_state = ProxyPrefs::CONFIG_OTHER_PRECEDE;
    } else  {
      config_state = ProxyPrefs::CONFIG_FALLBACK;
    }
  }

  return config_state;
}

ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::GetProxyConfig(
    net::ProxyConfig* config) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (config_state_ != ProxyPrefs::CONFIG_UNSET)
    *config = pref_config_;
  return config_state_;
}

void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
    ProxyPrefs::ConfigState config_state,
    const net::ProxyConfig& config) {
  if (!chrome_proxy_config_service_) {
    VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
    update_pending_ = true;
    return;
  }
  update_pending_ = !BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&ChromeProxyConfigService::UpdateProxyConfig,
                 base::Unretained(chrome_proxy_config_service_),
                 config_state, config));
  VLOG(1) << this << (update_pending_ ? ": Error" : ": Done")
          << " pushing proxy to UpdateProxyConfig";
}

bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
    const ProxyConfigDictionary& proxy_dict,
    net::ProxyConfig* config) {
  ProxyPrefs::ProxyMode mode;
  if (!proxy_dict.GetMode(&mode)) {
    // Fall back to system settings if the mode preference is invalid.
    return false;
  }

  switch (mode) {
    case ProxyPrefs::MODE_SYSTEM:
      // Use system settings.
      return false;
    case ProxyPrefs::MODE_DIRECT:
      // Ignore all the other proxy config preferences if the use of a proxy
      // has been explicitly disabled.
      return true;
    case ProxyPrefs::MODE_AUTO_DETECT:
      config->set_auto_detect(true);
      return true;
    case ProxyPrefs::MODE_PAC_SCRIPT: {
      std::string proxy_pac;
      if (!proxy_dict.GetPacUrl(&proxy_pac)) {
        LOG(ERROR) << "Proxy settings request PAC script but do not specify "
                   << "its URL. Falling back to direct connection.";
        return true;
      }
      GURL proxy_pac_url(proxy_pac);
      if (!proxy_pac_url.is_valid()) {
        LOG(ERROR) << "Invalid proxy PAC url: " << proxy_pac;
        return true;
      }
      config->set_pac_url(proxy_pac_url);
      bool pac_mandatory = false;
      proxy_dict.GetPacMandatory(&pac_mandatory);
      config->set_pac_mandatory(pac_mandatory);
      return true;
    }
    case ProxyPrefs::MODE_FIXED_SERVERS: {
      std::string proxy_server;
      if (!proxy_dict.GetProxyServer(&proxy_server)) {
        LOG(ERROR) << "Proxy settings request fixed proxy servers but do not "
                   << "specify their URLs. Falling back to direct connection.";
        return true;
      }
      config->proxy_rules().ParseFromString(proxy_server);

      std::string proxy_bypass;
      if (proxy_dict.GetBypassList(&proxy_bypass)) {
        config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
      }
      return true;
    }
    case ProxyPrefs::kModeCount: {
      // Fall through to NOTREACHED().
    }
  }
  NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
  return false;
}

void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  net::ProxyConfig new_config;
  ProxyPrefs::ConfigState config_state = ReadPrefConfig(pref_service_,
                                                        &new_config);
  if (config_state_ != config_state ||
      (config_state_ != ProxyPrefs::CONFIG_UNSET &&
       !pref_config_.Equals(new_config))) {
    config_state_ = config_state;
    if (config_state_ != ProxyPrefs::CONFIG_UNSET)
      pref_config_ = new_config;
    update_pending_ = true;
  }
  if (update_pending_)
    OnProxyConfigChanged(config_state, new_config);
}

/* [<][>][^][v][top][bottom][index][help] */