普通文本  |  271行  |  9.26 KB

// 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_service.h"

#include "base/values.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/pref_set_observer.h"
#include "chrome/browser/prefs/proxy_config_dictionary.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_details.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"

PrefProxyConfigTracker::PrefProxyConfigTracker(PrefService* pref_service)
    : pref_service_(pref_service) {
  config_state_ = ReadPrefConfig(&pref_config_);
  proxy_prefs_observer_.reset(
      PrefSetObserver::CreateProxyPrefSetObserver(pref_service_, this));
}

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

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

void PrefProxyConfigTracker::DetachFromPrefService() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  // Stop notifications.
  proxy_prefs_observer_.reset();
  pref_service_ = NULL;
}

void PrefProxyConfigTracker::AddObserver(
    PrefProxyConfigTracker::Observer* observer) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  observers_.AddObserver(observer);
}

void PrefProxyConfigTracker::RemoveObserver(
    PrefProxyConfigTracker::Observer* observer) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  observers_.RemoveObserver(observer);
}

void PrefProxyConfigTracker::Observe(NotificationType type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (type == NotificationType::PREF_CHANGED &&
      Source<PrefService>(source).ptr() == pref_service_) {
    net::ProxyConfig new_config;
    ConfigState config_state = ReadPrefConfig(&new_config);
    BrowserThread::PostTask(
        BrowserThread::IO, FROM_HERE,
        NewRunnableMethod(this,
                          &PrefProxyConfigTracker::InstallProxyConfig,
                          new_config, config_state));
  } else {
    NOTREACHED() << "Unexpected notification of type " << type.value;
  }
}

void PrefProxyConfigTracker::InstallProxyConfig(
    const net::ProxyConfig& config,
    PrefProxyConfigTracker::ConfigState config_state) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  if (config_state_ != config_state ||
      (config_state_ != CONFIG_UNSET && !pref_config_.Equals(config))) {
    config_state_ = config_state;
    if (config_state_ != CONFIG_UNSET)
      pref_config_ = config;
    FOR_EACH_OBSERVER(Observer, observers_, OnPrefProxyConfigChanged());
  }
}

PrefProxyConfigTracker::ConfigState
    PrefProxyConfigTracker::ReadPrefConfig(net::ProxyConfig* config) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Clear the configuration.
  *config = net::ProxyConfig();

  const PrefService::Preference* pref =
      pref_service_->FindPreference(prefs::kProxy);
  const DictionaryValue* dict = pref_service_->GetDictionary(prefs::kProxy);
  DCHECK(dict);
  ProxyConfigDictionary proxy_dict(dict);

  if (PrefConfigToNetConfig(proxy_dict, config)) {
    return (!pref->IsUserModifiable() || pref->HasUserSetting()) ?
        CONFIG_PRESENT : CONFIG_FALLBACK;
  }

  return CONFIG_UNSET;
}

bool PrefProxyConfigTracker::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 true;
    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);
      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;
}

PrefProxyConfigService::PrefProxyConfigService(
    PrefProxyConfigTracker* tracker,
    net::ProxyConfigService* base_service)
    : base_service_(base_service),
      pref_config_tracker_(tracker),
      registered_observers_(false) {
}

PrefProxyConfigService::~PrefProxyConfigService() {
  if (registered_observers_) {
    base_service_->RemoveObserver(this);
    pref_config_tracker_->RemoveObserver(this);
  }
}

void PrefProxyConfigService::AddObserver(
    net::ProxyConfigService::Observer* observer) {
  RegisterObservers();
  observers_.AddObserver(observer);
}

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

net::ProxyConfigService::ConfigAvailability
    PrefProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) {
  RegisterObservers();
  net::ProxyConfig pref_config;
  PrefProxyConfigTracker::ConfigState state =
      pref_config_tracker_->GetProxyConfig(&pref_config);
  if (state == PrefProxyConfigTracker::CONFIG_PRESENT) {
    *config = pref_config;
    return CONFIG_VALID;
  }

  // Ask the base service.
  ConfigAvailability available = base_service_->GetLatestProxyConfig(config);

  // Base service doesn't have a configuration, fall back to prefs or default.
  if (available == CONFIG_UNSET) {
    if (state == PrefProxyConfigTracker::CONFIG_FALLBACK)
      *config = pref_config;
    else
      *config = net::ProxyConfig::CreateDirect();
    return CONFIG_VALID;
  }

  return available;
}

void PrefProxyConfigService::OnLazyPoll() {
  base_service_->OnLazyPoll();
}

void PrefProxyConfigService::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.
  net::ProxyConfig actual_config;
  if (pref_config_tracker_->GetProxyConfig(&actual_config) !=
          PrefProxyConfigTracker::CONFIG_PRESENT) {
    availability = GetLatestProxyConfig(&actual_config);
    FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
                      OnProxyConfigChanged(actual_config, availability));
  }
}

void PrefProxyConfigService::OnPrefProxyConfigChanged() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));

  // 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 PrefProxyConfigService::RegisterObservers() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  if (!registered_observers_) {
    base_service_->AddObserver(this);
    pref_config_tracker_->AddObserver(this);
    registered_observers_ = true;
  }
}

// static
void PrefProxyConfigService::RegisterPrefs(PrefService* pref_service) {
  DictionaryValue* default_settings = ProxyConfigDictionary::CreateSystem();
  pref_service->RegisterDictionaryPref(prefs::kProxy, default_settings);
}