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