pref_proxy_config_service.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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
17PrefProxyConfigTracker::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
24PrefProxyConfigTracker::~PrefProxyConfigTracker() {
25  DCHECK(pref_service_ == NULL);
26}
27
28PrefProxyConfigTracker::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
36void PrefProxyConfigTracker::DetachFromPrefService() {
37  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
38  // Stop notifications.
39  proxy_prefs_observer_.reset();
40  pref_service_ = NULL;
41}
42
43void PrefProxyConfigTracker::AddObserver(
44    PrefProxyConfigTracker::Observer* observer) {
45  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
46  observers_.AddObserver(observer);
47}
48
49void PrefProxyConfigTracker::RemoveObserver(
50    PrefProxyConfigTracker::Observer* observer) {
51  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
52  observers_.RemoveObserver(observer);
53}
54
55void 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
73void 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
86PrefProxyConfigTracker::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
107bool 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
165PrefProxyConfigService::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
173PrefProxyConfigService::~PrefProxyConfigService() {
174  if (registered_observers_) {
175    base_service_->RemoveObserver(this);
176    pref_config_tracker_->RemoveObserver(this);
177  }
178}
179
180void PrefProxyConfigService::AddObserver(
181    net::ProxyConfigService::Observer* observer) {
182  RegisterObservers();
183  observers_.AddObserver(observer);
184}
185
186void PrefProxyConfigService::RemoveObserver(
187    net::ProxyConfigService::Observer* observer) {
188  observers_.RemoveObserver(observer);
189}
190
191net::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
217void PrefProxyConfigService::OnLazyPoll() {
218  base_service_->OnLazyPoll();
219}
220
221void 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
238void 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
257void 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
267void PrefProxyConfigService::RegisterPrefs(PrefService* pref_service) {
268  DictionaryValue* default_settings = ProxyConfigDictionary::CreateSystem();
269  pref_service->RegisterDictionaryPref(prefs::kProxy, default_settings);
270}
271