pref_proxy_config_tracker_impl.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant// Use of this source code is governed by a BSD-style license that can be
3f5256e16dfc425c1d466f6308d4026d529ce9e0bHoward Hinnant// found in the LICENSE file.
4bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
5b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant#include "chrome/browser/net/pref_proxy_config_tracker_impl.h"
6b64f8b07c104c6cc986570ac8ee0ed16a9f23976Howard Hinnant
7bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/bind.h"
8bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/prefs/pref_registry_simple.h"
9bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/prefs/pref_service.h"
10bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "base/values.h"
11bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "chrome/browser/prefs/proxy_config_dictionary.h"
12bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "chrome/common/chrome_notification_types.h"
13bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "chrome/common/pref_names.h"
14bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "components/user_prefs/pref_registry_syncable.h"
15bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "content/public/browser/browser_thread.h"
16bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "content/public/browser/notification_details.h"
17bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant#include "content/public/browser/notification_source.h"
18bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
19bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantusing content::BrowserThread;
20bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
21bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant//============================= ChromeProxyConfigService =======================
22bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
23bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard HinnantChromeProxyConfigService::ChromeProxyConfigService(
24bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    net::ProxyConfigService* base_service)
25bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    : base_service_(base_service),
26bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant      pref_config_state_(ProxyPrefs::CONFIG_UNSET),
27bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant      pref_config_read_pending_(true),
28bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant      registered_observer_(false) {
29bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant}
30bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
31bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard HinnantChromeProxyConfigService::~ChromeProxyConfigService() {
32bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  if (registered_observer_ && base_service_.get())
33bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    base_service_->RemoveObserver(this);
34bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant}
35bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
36bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantvoid ChromeProxyConfigService::AddObserver(
37bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    net::ProxyConfigService::Observer* observer) {
38bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  RegisterObserver();
39bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  observers_.AddObserver(observer);
40bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant}
41bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
42bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantvoid ChromeProxyConfigService::RemoveObserver(
43bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    net::ProxyConfigService::Observer* observer) {
44bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  observers_.RemoveObserver(observer);
45bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant}
46bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
47bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantnet::ProxyConfigService::ConfigAvailability
48bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    ChromeProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) {
49bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  RegisterObserver();
50bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
51bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  if (pref_config_read_pending_)
52bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    return net::ProxyConfigService::CONFIG_PENDING;
53bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
54bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // Ask the base service if available.
55bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  net::ProxyConfig system_config;
56bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  ConfigAvailability system_availability =
57bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant      net::ProxyConfigService::CONFIG_UNSET;
58bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  if (base_service_.get())
59bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    system_availability = base_service_->GetLatestProxyConfig(&system_config);
60bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
61bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  ProxyPrefs::ConfigState config_state;
62bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
63bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant      pref_config_state_, pref_config_,
64bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant      system_availability, system_config, false,
65bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant      &config_state, config);
66bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant}
67bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
68bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantvoid ChromeProxyConfigService::OnLazyPoll() {
69bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  if (base_service_.get())
70bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    base_service_->OnLazyPoll();
71bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant}
72bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
73bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnantvoid ChromeProxyConfigService::UpdateProxyConfig(
74bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    ProxyPrefs::ConfigState config_state,
75bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    const net::ProxyConfig& config) {
76bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
77bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
78bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  pref_config_read_pending_ = false;
79bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  pref_config_state_ = config_state;
80bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  pref_config_ = config;
81bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
82bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  if (!observers_.size())
83bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    return;
84bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant
85bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // Evaluate the proxy configuration. If GetLatestProxyConfig returns
86bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
87bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
88bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // called and broadcast the proxy configuration.
89bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // Note: If a switch between a preference proxy configuration and the system
90bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // proxy configuration occurs an unnecessary notification might get send if
91bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // the two configurations agree. This case should be rare however, so we don't
92bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  // handle that case specially.
93bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  net::ProxyConfig new_config;
94bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  ConfigAvailability availability = GetLatestProxyConfig(&new_config);
95bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant  if (availability != CONFIG_PENDING) {
96bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant    FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
97bc8d3f97eb5c958007f2713238472e0c1c8fe02Howard Hinnant                      OnProxyConfigChanged(new_config, availability));
98  }
99}
100
101void 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
117void 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
127PrefProxyConfigTrackerImpl::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_config_);
133  proxy_prefs_.Init(pref_service);
134  proxy_prefs_.Add(prefs::kProxy,
135                   base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged,
136                              base::Unretained(this)));
137}
138
139PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
140  DCHECK(pref_service_ == NULL);
141}
142
143void PrefProxyConfigTrackerImpl::SetChromeProxyConfigService(
144    ChromeProxyConfigService* chrome_proxy_config_service) {
145  VLOG(1) << this << ": set chrome proxy config service to "
146          << chrome_proxy_config_service;
147  chrome_proxy_config_service_ = chrome_proxy_config_service;
148  if (chrome_proxy_config_service_ && update_pending_)
149    OnProxyConfigChanged(config_state_, pref_config_);
150}
151
152void PrefProxyConfigTrackerImpl::DetachFromPrefService() {
153  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
154  // Stop notifications.
155  proxy_prefs_.RemoveAll();
156  pref_service_ = NULL;
157  SetChromeProxyConfigService(NULL);
158}
159
160// static
161bool PrefProxyConfigTrackerImpl::PrefPrecedes(
162    ProxyPrefs::ConfigState config_state) {
163  return config_state == ProxyPrefs::CONFIG_POLICY ||
164         config_state == ProxyPrefs::CONFIG_EXTENSION ||
165         config_state == ProxyPrefs::CONFIG_OTHER_PRECEDE;
166}
167
168// static
169net::ProxyConfigService::ConfigAvailability
170    PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
171        ProxyPrefs::ConfigState pref_state,
172        const net::ProxyConfig& pref_config,
173        net::ProxyConfigService::ConfigAvailability system_availability,
174        const net::ProxyConfig& system_config,
175        bool ignore_fallback_config,
176        ProxyPrefs::ConfigState* effective_config_state,
177        net::ProxyConfig* effective_config) {
178  *effective_config_state = pref_state;
179
180  if (PrefPrecedes(pref_state)) {
181    *effective_config = pref_config;
182    return net::ProxyConfigService::CONFIG_VALID;
183  }
184
185  // If there's no system proxy config, fall back to prefs or default.
186  if (system_availability == net::ProxyConfigService::CONFIG_UNSET) {
187    if (pref_state == ProxyPrefs::CONFIG_FALLBACK && !ignore_fallback_config)
188      *effective_config = pref_config;
189    else
190      *effective_config = net::ProxyConfig::CreateDirect();
191    return net::ProxyConfigService::CONFIG_VALID;
192  }
193
194  *effective_config_state = ProxyPrefs::CONFIG_SYSTEM;
195  *effective_config = system_config;
196  return system_availability;
197}
198
199// static
200void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
201  DictionaryValue* default_settings = ProxyConfigDictionary::CreateSystem();
202  registry->RegisterDictionaryPref(prefs::kProxy, default_settings);
203}
204
205// static
206void PrefProxyConfigTrackerImpl::RegisterUserPrefs(
207    user_prefs::PrefRegistrySyncable* pref_service) {
208  DictionaryValue* default_settings = ProxyConfigDictionary::CreateSystem();
209  pref_service->RegisterDictionaryPref(
210      prefs::kProxy,
211      default_settings,
212      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
213}
214
215ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::GetProxyConfig(
216    net::ProxyConfig* config) {
217  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218  if (config_state_ != ProxyPrefs::CONFIG_UNSET)
219    *config = pref_config_;
220  return config_state_;
221}
222
223void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
224    ProxyPrefs::ConfigState config_state,
225    const net::ProxyConfig& config) {
226  if (!chrome_proxy_config_service_) {
227    VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
228    update_pending_ = true;
229    return;
230  }
231  update_pending_ = !BrowserThread::PostTask(
232      BrowserThread::IO, FROM_HERE,
233      base::Bind(&ChromeProxyConfigService::UpdateProxyConfig,
234                 base::Unretained(chrome_proxy_config_service_),
235                 config_state, config));
236  VLOG(1) << this << (update_pending_ ? ": Error" : ": Done")
237          << " pushing proxy to UpdateProxyConfig";
238}
239
240bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
241    const ProxyConfigDictionary& proxy_dict,
242    net::ProxyConfig* config) {
243  ProxyPrefs::ProxyMode mode;
244  if (!proxy_dict.GetMode(&mode)) {
245    // Fall back to system settings if the mode preference is invalid.
246    return false;
247  }
248
249  switch (mode) {
250    case ProxyPrefs::MODE_SYSTEM:
251      // Use system settings.
252      return false;
253    case ProxyPrefs::MODE_DIRECT:
254      // Ignore all the other proxy config preferences if the use of a proxy
255      // has been explicitly disabled.
256      return true;
257    case ProxyPrefs::MODE_AUTO_DETECT:
258      config->set_auto_detect(true);
259      return true;
260    case ProxyPrefs::MODE_PAC_SCRIPT: {
261      std::string proxy_pac;
262      if (!proxy_dict.GetPacUrl(&proxy_pac)) {
263        LOG(ERROR) << "Proxy settings request PAC script but do not specify "
264                   << "its URL. Falling back to direct connection.";
265        return true;
266      }
267      GURL proxy_pac_url(proxy_pac);
268      if (!proxy_pac_url.is_valid()) {
269        LOG(ERROR) << "Invalid proxy PAC url: " << proxy_pac;
270        return true;
271      }
272      config->set_pac_url(proxy_pac_url);
273      bool pac_mandatory = false;
274      proxy_dict.GetPacMandatory(&pac_mandatory);
275      config->set_pac_mandatory(pac_mandatory);
276      return true;
277    }
278    case ProxyPrefs::MODE_FIXED_SERVERS: {
279      std::string proxy_server;
280      if (!proxy_dict.GetProxyServer(&proxy_server)) {
281        LOG(ERROR) << "Proxy settings request fixed proxy servers but do not "
282                   << "specify their URLs. Falling back to direct connection.";
283        return true;
284      }
285      config->proxy_rules().ParseFromString(proxy_server);
286
287      std::string proxy_bypass;
288      if (proxy_dict.GetBypassList(&proxy_bypass)) {
289        config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
290      }
291      return true;
292    }
293    case ProxyPrefs::kModeCount: {
294      // Fall through to NOTREACHED().
295    }
296  }
297  NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
298  return false;
299}
300
301void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
302  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
303  net::ProxyConfig new_config;
304  ProxyPrefs::ConfigState config_state = ReadPrefConfig(&new_config);
305  if (config_state_ != config_state ||
306      (config_state_ != ProxyPrefs::CONFIG_UNSET &&
307       !pref_config_.Equals(new_config))) {
308    config_state_ = config_state;
309    if (config_state_ != ProxyPrefs::CONFIG_UNSET)
310      pref_config_ = new_config;
311    update_pending_ = true;
312  }
313  if (update_pending_)
314    OnProxyConfigChanged(config_state, new_config);
315}
316
317ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::ReadPrefConfig(
318    net::ProxyConfig* config) {
319  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
320
321  // Clear the configuration and source.
322  *config = net::ProxyConfig();
323  ProxyPrefs::ConfigState config_state = ProxyPrefs::CONFIG_UNSET;
324
325  const PrefService::Preference* pref =
326      pref_service_->FindPreference(prefs::kProxy);
327  DCHECK(pref);
328
329  const DictionaryValue* dict = pref_service_->GetDictionary(prefs::kProxy);
330  DCHECK(dict);
331  ProxyConfigDictionary proxy_dict(dict);
332
333  if (PrefConfigToNetConfig(proxy_dict, config)) {
334    if (!pref->IsUserModifiable() || pref->HasUserSetting()) {
335      if (pref->IsManaged())
336        config_state = ProxyPrefs::CONFIG_POLICY;
337      else if (pref->IsExtensionControlled())
338        config_state = ProxyPrefs::CONFIG_EXTENSION;
339      else
340        config_state = ProxyPrefs::CONFIG_OTHER_PRECEDE;
341    } else  {
342      config_state = ProxyPrefs::CONFIG_FALLBACK;
343    }
344  }
345
346  return config_state;
347}
348