Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / net / pref_proxy_config_tracker_impl.cc
bloba4ec56c3657869bd29ca98fc1cd627102da34e73
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.
5 #include "chrome/browser/net/pref_proxy_config_tracker_impl.h"
7 #include "base/bind.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/prefs/pref_registry_simple.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_util.h"
12 #include "base/values.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/prefs/proxy_config_dictionary.h"
15 #include "chrome/common/pref_names.h"
16 #include "components/pref_registry/pref_registry_syncable.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/notification_details.h"
19 #include "content/public/browser/notification_source.h"
20 #include "net/proxy/proxy_list.h"
21 #include "net/proxy/proxy_server.h"
23 using content::BrowserThread;
25 namespace {
27 // Determine if |proxy| is of the form "*.googlezip.net".
28 bool IsGooglezipDataReductionProxy(const net::ProxyServer& proxy) {
29 return proxy.is_valid() && !proxy.is_direct() &&
30 base::EndsWith(proxy.host_port_pair().host(), ".googlezip.net", true);
33 // Removes any Data Reduction Proxies like *.googlezip.net from |proxy_list|.
34 // Returns the number of proxies that were removed from |proxy_list|.
35 size_t RemoveGooglezipDataReductionProxiesFromList(net::ProxyList* proxy_list) {
36 bool found_googlezip_proxy = false;
37 for (const net::ProxyServer& proxy : proxy_list->GetAll()) {
38 if (IsGooglezipDataReductionProxy(proxy)) {
39 found_googlezip_proxy = true;
40 break;
43 if (!found_googlezip_proxy)
44 return 0;
46 size_t num_removed_proxies = 0;
47 net::ProxyList replacement_list;
48 for (const net::ProxyServer& proxy : proxy_list->GetAll()) {
49 if (!IsGooglezipDataReductionProxy(proxy))
50 replacement_list.AddProxyServer(proxy);
51 else
52 ++num_removed_proxies;
55 if (replacement_list.IsEmpty())
56 replacement_list.AddProxyServer(net::ProxyServer::Direct());
57 *proxy_list = replacement_list;
58 return num_removed_proxies;
61 // Remove any Data Reduction Proxies like *.googlezip.net from |proxy_rules|.
62 // This is to prevent a Data Reduction Proxy from being activated in an
63 // unsupported way, such as from a proxy pref, which could cause Chrome to use
64 // the Data Reduction Proxy without adding any of the necessary authentication
65 // headers or applying the Data Reduction Proxy bypass logic. See
66 // http://crbug.com/476610.
67 // TODO(sclittle): This method should be removed once the UMA indicates that
68 // *.googlezip.net proxies are no longer present in the |proxy_rules|.
69 void RemoveGooglezipDataReductionProxies(
70 net::ProxyConfig::ProxyRules* proxy_rules) {
71 size_t num_removed_proxies =
72 RemoveGooglezipDataReductionProxiesFromList(
73 &proxy_rules->fallback_proxies) +
74 RemoveGooglezipDataReductionProxiesFromList(
75 &proxy_rules->proxies_for_ftp) +
76 RemoveGooglezipDataReductionProxiesFromList(
77 &proxy_rules->proxies_for_http) +
78 RemoveGooglezipDataReductionProxiesFromList(
79 &proxy_rules->proxies_for_https) +
80 RemoveGooglezipDataReductionProxiesFromList(&proxy_rules->single_proxies);
82 UMA_HISTOGRAM_COUNTS_100("Net.PrefProxyConfig.GooglezipProxyRemovalCount",
83 num_removed_proxies);
86 } // namespace
88 //============================= ChromeProxyConfigService =======================
90 ChromeProxyConfigService::ChromeProxyConfigService(
91 net::ProxyConfigService* base_service)
92 : base_service_(base_service),
93 pref_config_state_(ProxyPrefs::CONFIG_UNSET),
94 pref_config_read_pending_(true),
95 registered_observer_(false) {
98 ChromeProxyConfigService::~ChromeProxyConfigService() {
99 if (registered_observer_ && base_service_.get())
100 base_service_->RemoveObserver(this);
103 void ChromeProxyConfigService::AddObserver(
104 net::ProxyConfigService::Observer* observer) {
105 RegisterObserver();
106 observers_.AddObserver(observer);
109 void ChromeProxyConfigService::RemoveObserver(
110 net::ProxyConfigService::Observer* observer) {
111 observers_.RemoveObserver(observer);
114 net::ProxyConfigService::ConfigAvailability
115 ChromeProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) {
116 RegisterObserver();
118 if (pref_config_read_pending_)
119 return net::ProxyConfigService::CONFIG_PENDING;
121 // Ask the base service if available.
122 net::ProxyConfig system_config;
123 ConfigAvailability system_availability =
124 net::ProxyConfigService::CONFIG_UNSET;
125 if (base_service_.get())
126 system_availability = base_service_->GetLatestProxyConfig(&system_config);
128 ProxyPrefs::ConfigState config_state;
129 return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
130 pref_config_state_, pref_config_,
131 system_availability, system_config, false,
132 &config_state, config);
135 void ChromeProxyConfigService::OnLazyPoll() {
136 if (base_service_.get())
137 base_service_->OnLazyPoll();
140 void ChromeProxyConfigService::UpdateProxyConfig(
141 ProxyPrefs::ConfigState config_state,
142 const net::ProxyConfig& config) {
143 DCHECK_CURRENTLY_ON(BrowserThread::IO);
145 pref_config_read_pending_ = false;
146 pref_config_state_ = config_state;
147 pref_config_ = config;
149 if (!observers_.might_have_observers())
150 return;
152 // Evaluate the proxy configuration. If GetLatestProxyConfig returns
153 // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
154 // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
155 // called and broadcast the proxy configuration.
156 // Note: If a switch between a preference proxy configuration and the system
157 // proxy configuration occurs an unnecessary notification might get send if
158 // the two configurations agree. This case should be rare however, so we don't
159 // handle that case specially.
160 net::ProxyConfig new_config;
161 ConfigAvailability availability = GetLatestProxyConfig(&new_config);
162 if (availability != CONFIG_PENDING) {
163 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
164 OnProxyConfigChanged(new_config, availability));
168 void ChromeProxyConfigService::OnProxyConfigChanged(
169 const net::ProxyConfig& config,
170 ConfigAvailability availability) {
171 DCHECK_CURRENTLY_ON(BrowserThread::IO);
173 // Check whether there is a proxy configuration defined by preferences. In
174 // this case that proxy configuration takes precedence and the change event
175 // from the delegate proxy service can be disregarded.
176 if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_)) {
177 net::ProxyConfig actual_config;
178 availability = GetLatestProxyConfig(&actual_config);
179 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
180 OnProxyConfigChanged(actual_config, availability));
184 void ChromeProxyConfigService::RegisterObserver() {
185 DCHECK_CURRENTLY_ON(BrowserThread::IO);
186 if (!registered_observer_ && base_service_.get()) {
187 base_service_->AddObserver(this);
188 registered_observer_ = true;
192 //========================= PrefProxyConfigTrackerImpl =========================
194 PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
195 PrefService* pref_service)
196 : pref_service_(pref_service),
197 chrome_proxy_config_service_(NULL),
198 update_pending_(true) {
199 config_state_ = ReadPrefConfig(pref_service_, &pref_config_);
200 proxy_prefs_.Init(pref_service);
201 proxy_prefs_.Add(prefs::kProxy,
202 base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged,
203 base::Unretained(this)));
206 PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
207 DCHECK(pref_service_ == NULL);
210 scoped_ptr<net::ProxyConfigService>
211 PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService(
212 scoped_ptr<net::ProxyConfigService> base_service) {
213 chrome_proxy_config_service_ =
214 new ChromeProxyConfigService(base_service.release());
215 VLOG(1) << this << ": set chrome proxy config service to "
216 << chrome_proxy_config_service_;
217 if (chrome_proxy_config_service_ && update_pending_)
218 OnProxyConfigChanged(config_state_, pref_config_);
220 return scoped_ptr<net::ProxyConfigService>(chrome_proxy_config_service_);
223 void PrefProxyConfigTrackerImpl::DetachFromPrefService() {
224 DCHECK_CURRENTLY_ON(BrowserThread::UI);
225 // Stop notifications.
226 proxy_prefs_.RemoveAll();
227 pref_service_ = NULL;
228 chrome_proxy_config_service_ = NULL;
231 // static
232 bool PrefProxyConfigTrackerImpl::PrefPrecedes(
233 ProxyPrefs::ConfigState config_state) {
234 return config_state == ProxyPrefs::CONFIG_POLICY ||
235 config_state == ProxyPrefs::CONFIG_EXTENSION ||
236 config_state == ProxyPrefs::CONFIG_OTHER_PRECEDE;
239 // static
240 net::ProxyConfigService::ConfigAvailability
241 PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
242 ProxyPrefs::ConfigState pref_state,
243 const net::ProxyConfig& pref_config,
244 net::ProxyConfigService::ConfigAvailability system_availability,
245 const net::ProxyConfig& system_config,
246 bool ignore_fallback_config,
247 ProxyPrefs::ConfigState* effective_config_state,
248 net::ProxyConfig* effective_config) {
249 net::ProxyConfigService::ConfigAvailability rv;
250 *effective_config_state = pref_state;
252 if (PrefPrecedes(pref_state)) {
253 *effective_config = pref_config;
254 rv = net::ProxyConfigService::CONFIG_VALID;
255 } else if (system_availability == net::ProxyConfigService::CONFIG_UNSET) {
256 // If there's no system proxy config, fall back to prefs or default.
257 if (pref_state == ProxyPrefs::CONFIG_FALLBACK && !ignore_fallback_config)
258 *effective_config = pref_config;
259 else
260 *effective_config = net::ProxyConfig::CreateDirect();
261 rv = net::ProxyConfigService::CONFIG_VALID;
262 } else {
263 *effective_config_state = ProxyPrefs::CONFIG_SYSTEM;
264 *effective_config = system_config;
265 rv = system_availability;
268 // Remove any Data Reduction Proxies like *.googlezip.net from the proxy
269 // config rules, since specifying a DRP in the proxy rules is not a supported
270 // means of activating the DRP, and could cause requests to be sent to the DRP
271 // without the appropriate authentication headers and without using any of the
272 // DRP bypass logic. This prevents the Data Reduction Proxy from being
273 // improperly activated via the proxy pref.
274 // TODO(sclittle): This is a temporary fix for http://crbug.com/476610, and
275 // should be removed once that bug is fixed and verified.
276 if (rv == net::ProxyConfigService::CONFIG_VALID)
277 RemoveGooglezipDataReductionProxies(&effective_config->proxy_rules());
279 return rv;
282 // static
283 void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
284 base::DictionaryValue* default_settings =
285 ProxyConfigDictionary::CreateSystem();
286 registry->RegisterDictionaryPref(prefs::kProxy, default_settings);
289 // static
290 void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
291 user_prefs::PrefRegistrySyncable* pref_service) {
292 base::DictionaryValue* default_settings =
293 ProxyConfigDictionary::CreateSystem();
294 pref_service->RegisterDictionaryPref(prefs::kProxy, default_settings);
297 // static
298 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::ReadPrefConfig(
299 const PrefService* pref_service,
300 net::ProxyConfig* config) {
301 DCHECK_CURRENTLY_ON(BrowserThread::UI);
303 // Clear the configuration and source.
304 *config = net::ProxyConfig();
305 ProxyPrefs::ConfigState config_state = ProxyPrefs::CONFIG_UNSET;
307 const PrefService::Preference* pref =
308 pref_service->FindPreference(prefs::kProxy);
309 DCHECK(pref);
311 const base::DictionaryValue* dict =
312 pref_service->GetDictionary(prefs::kProxy);
313 DCHECK(dict);
314 ProxyConfigDictionary proxy_dict(dict);
316 if (PrefConfigToNetConfig(proxy_dict, config)) {
317 if (!pref->IsUserModifiable() || pref->HasUserSetting()) {
318 if (pref->IsManaged())
319 config_state = ProxyPrefs::CONFIG_POLICY;
320 else if (pref->IsExtensionControlled())
321 config_state = ProxyPrefs::CONFIG_EXTENSION;
322 else
323 config_state = ProxyPrefs::CONFIG_OTHER_PRECEDE;
324 } else {
325 config_state = ProxyPrefs::CONFIG_FALLBACK;
329 return config_state;
332 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::GetProxyConfig(
333 net::ProxyConfig* config) {
334 DCHECK_CURRENTLY_ON(BrowserThread::UI);
335 if (config_state_ != ProxyPrefs::CONFIG_UNSET)
336 *config = pref_config_;
337 return config_state_;
340 void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
341 ProxyPrefs::ConfigState config_state,
342 const net::ProxyConfig& config) {
343 if (!chrome_proxy_config_service_) {
344 VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
345 update_pending_ = true;
346 return;
348 update_pending_ = !BrowserThread::PostTask(
349 BrowserThread::IO, FROM_HERE,
350 base::Bind(&ChromeProxyConfigService::UpdateProxyConfig,
351 base::Unretained(chrome_proxy_config_service_),
352 config_state, config));
353 VLOG(1) << this << (update_pending_ ? ": Error" : ": Done")
354 << " pushing proxy to UpdateProxyConfig";
357 bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
358 const ProxyConfigDictionary& proxy_dict,
359 net::ProxyConfig* config) {
360 ProxyPrefs::ProxyMode mode;
361 if (!proxy_dict.GetMode(&mode)) {
362 // Fall back to system settings if the mode preference is invalid.
363 return false;
366 switch (mode) {
367 case ProxyPrefs::MODE_SYSTEM:
368 // Use system settings.
369 return false;
370 case ProxyPrefs::MODE_DIRECT:
371 // Ignore all the other proxy config preferences if the use of a proxy
372 // has been explicitly disabled.
373 return true;
374 case ProxyPrefs::MODE_AUTO_DETECT:
375 config->set_auto_detect(true);
376 return true;
377 case ProxyPrefs::MODE_PAC_SCRIPT: {
378 std::string proxy_pac;
379 if (!proxy_dict.GetPacUrl(&proxy_pac)) {
380 LOG(ERROR) << "Proxy settings request PAC script but do not specify "
381 << "its URL. Falling back to direct connection.";
382 return true;
384 GURL proxy_pac_url(proxy_pac);
385 if (!proxy_pac_url.is_valid()) {
386 LOG(ERROR) << "Invalid proxy PAC url: " << proxy_pac;
387 return true;
389 config->set_pac_url(proxy_pac_url);
390 bool pac_mandatory = false;
391 proxy_dict.GetPacMandatory(&pac_mandatory);
392 config->set_pac_mandatory(pac_mandatory);
393 return true;
395 case ProxyPrefs::MODE_FIXED_SERVERS: {
396 std::string proxy_server;
397 if (!proxy_dict.GetProxyServer(&proxy_server)) {
398 LOG(ERROR) << "Proxy settings request fixed proxy servers but do not "
399 << "specify their URLs. Falling back to direct connection.";
400 return true;
402 config->proxy_rules().ParseFromString(proxy_server);
404 std::string proxy_bypass;
405 if (proxy_dict.GetBypassList(&proxy_bypass)) {
406 config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
408 return true;
410 case ProxyPrefs::kModeCount: {
411 // Fall through to NOTREACHED().
414 NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
415 return false;
418 void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
419 DCHECK_CURRENTLY_ON(BrowserThread::UI);
420 net::ProxyConfig new_config;
421 ProxyPrefs::ConfigState config_state = ReadPrefConfig(pref_service_,
422 &new_config);
423 if (config_state_ != config_state ||
424 (config_state_ != ProxyPrefs::CONFIG_UNSET &&
425 !pref_config_.Equals(new_config))) {
426 config_state_ = config_state;
427 if (config_state_ != ProxyPrefs::CONFIG_UNSET)
428 pref_config_ = new_config;
429 update_pending_ = true;
431 if (update_pending_)
432 OnProxyConfigChanged(config_state, new_config);