Adding instrumentation to locate the source of jankiness.
[chromium-blink-merge.git] / chrome / browser / net / pref_proxy_config_tracker_impl.cc
blob6e9b771c466a7806cf2b2b0fad95a1a3dfc92f0e
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/prefs/pref_registry_simple.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/profiler/scoped_tracker.h"
11 #include "base/values.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/prefs/proxy_config_dictionary.h"
14 #include "chrome/common/pref_names.h"
15 #include "components/pref_registry/pref_registry_syncable.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_details.h"
18 #include "content/public/browser/notification_source.h"
20 using content::BrowserThread;
22 //============================= ChromeProxyConfigService =======================
24 ChromeProxyConfigService::ChromeProxyConfigService(
25 net::ProxyConfigService* base_service)
26 : base_service_(base_service),
27 pref_config_state_(ProxyPrefs::CONFIG_UNSET),
28 pref_config_read_pending_(true),
29 registered_observer_(false) {
32 ChromeProxyConfigService::~ChromeProxyConfigService() {
33 if (registered_observer_ && base_service_.get())
34 base_service_->RemoveObserver(this);
37 void ChromeProxyConfigService::AddObserver(
38 net::ProxyConfigService::Observer* observer) {
39 RegisterObserver();
40 observers_.AddObserver(observer);
43 void ChromeProxyConfigService::RemoveObserver(
44 net::ProxyConfigService::Observer* observer) {
45 observers_.RemoveObserver(observer);
48 net::ProxyConfigService::ConfigAvailability
49 ChromeProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) {
50 RegisterObserver();
52 if (pref_config_read_pending_)
53 return net::ProxyConfigService::CONFIG_PENDING;
55 // Ask the base service if available.
56 net::ProxyConfig system_config;
57 ConfigAvailability system_availability =
58 net::ProxyConfigService::CONFIG_UNSET;
59 if (base_service_.get())
60 system_availability = base_service_->GetLatestProxyConfig(&system_config);
62 ProxyPrefs::ConfigState config_state;
63 return PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
64 pref_config_state_, pref_config_,
65 system_availability, system_config, false,
66 &config_state, config);
69 void ChromeProxyConfigService::OnLazyPoll() {
70 if (base_service_.get())
71 base_service_->OnLazyPoll();
74 void ChromeProxyConfigService::UpdateProxyConfig(
75 ProxyPrefs::ConfigState config_state,
76 const net::ProxyConfig& config) {
77 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
79 pref_config_read_pending_ = false;
80 pref_config_state_ = config_state;
81 pref_config_ = config;
83 if (!observers_.might_have_observers())
84 return;
86 // Evaluate the proxy configuration. If GetLatestProxyConfig returns
87 // CONFIG_PENDING, we are using the system proxy service, but it doesn't have
88 // a valid configuration yet. Once it is ready, OnProxyConfigChanged() will be
89 // called and broadcast the proxy configuration.
90 // Note: If a switch between a preference proxy configuration and the system
91 // proxy configuration occurs an unnecessary notification might get send if
92 // the two configurations agree. This case should be rare however, so we don't
93 // handle that case specially.
94 net::ProxyConfig new_config;
95 ConfigAvailability availability = GetLatestProxyConfig(&new_config);
96 if (availability != CONFIG_PENDING) {
97 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
98 OnProxyConfigChanged(new_config, availability));
102 void ChromeProxyConfigService::OnProxyConfigChanged(
103 const net::ProxyConfig& config,
104 ConfigAvailability availability) {
105 // TODO(pkasting): Remove ScopedTracker below once crbug.com/455942 is fixed.
106 tracked_objects::ScopedTracker tracking_profile(
107 FROM_HERE_WITH_EXPLICIT_FUNCTION(
108 "455942 ChromeProxyConfigService::OnProxyConfigChanged"));
109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
111 // Check whether there is a proxy configuration defined by preferences. In
112 // this case that proxy configuration takes precedence and the change event
113 // from the delegate proxy service can be disregarded.
114 if (!PrefProxyConfigTrackerImpl::PrefPrecedes(pref_config_state_)) {
115 net::ProxyConfig actual_config;
116 availability = GetLatestProxyConfig(&actual_config);
117 FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
118 OnProxyConfigChanged(actual_config, availability));
122 void ChromeProxyConfigService::RegisterObserver() {
123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
124 if (!registered_observer_ && base_service_.get()) {
125 base_service_->AddObserver(this);
126 registered_observer_ = true;
130 //========================= PrefProxyConfigTrackerImpl =========================
132 PrefProxyConfigTrackerImpl::PrefProxyConfigTrackerImpl(
133 PrefService* pref_service)
134 : pref_service_(pref_service),
135 chrome_proxy_config_service_(NULL),
136 update_pending_(true) {
137 config_state_ = ReadPrefConfig(pref_service_, &pref_config_);
138 proxy_prefs_.Init(pref_service);
139 proxy_prefs_.Add(prefs::kProxy,
140 base::Bind(&PrefProxyConfigTrackerImpl::OnProxyPrefChanged,
141 base::Unretained(this)));
144 PrefProxyConfigTrackerImpl::~PrefProxyConfigTrackerImpl() {
145 DCHECK(pref_service_ == NULL);
148 scoped_ptr<net::ProxyConfigService>
149 PrefProxyConfigTrackerImpl::CreateTrackingProxyConfigService(
150 scoped_ptr<net::ProxyConfigService> base_service) {
151 chrome_proxy_config_service_ =
152 new ChromeProxyConfigService(base_service.release());
153 VLOG(1) << this << ": set chrome proxy config service to "
154 << chrome_proxy_config_service_;
155 if (chrome_proxy_config_service_ && update_pending_)
156 OnProxyConfigChanged(config_state_, pref_config_);
158 return scoped_ptr<net::ProxyConfigService>(chrome_proxy_config_service_);
161 void PrefProxyConfigTrackerImpl::DetachFromPrefService() {
162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
163 // Stop notifications.
164 proxy_prefs_.RemoveAll();
165 pref_service_ = NULL;
166 chrome_proxy_config_service_ = NULL;
169 // static
170 bool PrefProxyConfigTrackerImpl::PrefPrecedes(
171 ProxyPrefs::ConfigState config_state) {
172 return config_state == ProxyPrefs::CONFIG_POLICY ||
173 config_state == ProxyPrefs::CONFIG_EXTENSION ||
174 config_state == ProxyPrefs::CONFIG_OTHER_PRECEDE;
177 // static
178 net::ProxyConfigService::ConfigAvailability
179 PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig(
180 ProxyPrefs::ConfigState pref_state,
181 const net::ProxyConfig& pref_config,
182 net::ProxyConfigService::ConfigAvailability system_availability,
183 const net::ProxyConfig& system_config,
184 bool ignore_fallback_config,
185 ProxyPrefs::ConfigState* effective_config_state,
186 net::ProxyConfig* effective_config) {
187 *effective_config_state = pref_state;
189 if (PrefPrecedes(pref_state)) {
190 *effective_config = pref_config;
191 return net::ProxyConfigService::CONFIG_VALID;
194 // If there's no system proxy config, fall back to prefs or default.
195 if (system_availability == net::ProxyConfigService::CONFIG_UNSET) {
196 if (pref_state == ProxyPrefs::CONFIG_FALLBACK && !ignore_fallback_config)
197 *effective_config = pref_config;
198 else
199 *effective_config = net::ProxyConfig::CreateDirect();
200 return net::ProxyConfigService::CONFIG_VALID;
203 *effective_config_state = ProxyPrefs::CONFIG_SYSTEM;
204 *effective_config = system_config;
205 return system_availability;
208 // static
209 void PrefProxyConfigTrackerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
210 base::DictionaryValue* default_settings =
211 ProxyConfigDictionary::CreateSystem();
212 registry->RegisterDictionaryPref(prefs::kProxy, default_settings);
215 // static
216 void PrefProxyConfigTrackerImpl::RegisterProfilePrefs(
217 user_prefs::PrefRegistrySyncable* pref_service) {
218 base::DictionaryValue* default_settings =
219 ProxyConfigDictionary::CreateSystem();
220 pref_service->RegisterDictionaryPref(
221 prefs::kProxy,
222 default_settings,
223 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
226 // static
227 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::ReadPrefConfig(
228 const PrefService* pref_service,
229 net::ProxyConfig* config) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
232 // Clear the configuration and source.
233 *config = net::ProxyConfig();
234 ProxyPrefs::ConfigState config_state = ProxyPrefs::CONFIG_UNSET;
236 const PrefService::Preference* pref =
237 pref_service->FindPreference(prefs::kProxy);
238 DCHECK(pref);
240 const base::DictionaryValue* dict =
241 pref_service->GetDictionary(prefs::kProxy);
242 DCHECK(dict);
243 ProxyConfigDictionary proxy_dict(dict);
245 if (PrefConfigToNetConfig(proxy_dict, config)) {
246 if (!pref->IsUserModifiable() || pref->HasUserSetting()) {
247 if (pref->IsManaged())
248 config_state = ProxyPrefs::CONFIG_POLICY;
249 else if (pref->IsExtensionControlled())
250 config_state = ProxyPrefs::CONFIG_EXTENSION;
251 else
252 config_state = ProxyPrefs::CONFIG_OTHER_PRECEDE;
253 } else {
254 config_state = ProxyPrefs::CONFIG_FALLBACK;
258 return config_state;
261 ProxyPrefs::ConfigState PrefProxyConfigTrackerImpl::GetProxyConfig(
262 net::ProxyConfig* config) {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
264 if (config_state_ != ProxyPrefs::CONFIG_UNSET)
265 *config = pref_config_;
266 return config_state_;
269 void PrefProxyConfigTrackerImpl::OnProxyConfigChanged(
270 ProxyPrefs::ConfigState config_state,
271 const net::ProxyConfig& config) {
272 if (!chrome_proxy_config_service_) {
273 VLOG(1) << "No chrome proxy config service to push to UpdateProxyConfig";
274 update_pending_ = true;
275 return;
277 update_pending_ = !BrowserThread::PostTask(
278 BrowserThread::IO, FROM_HERE,
279 base::Bind(&ChromeProxyConfigService::UpdateProxyConfig,
280 base::Unretained(chrome_proxy_config_service_),
281 config_state, config));
282 VLOG(1) << this << (update_pending_ ? ": Error" : ": Done")
283 << " pushing proxy to UpdateProxyConfig";
286 bool PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(
287 const ProxyConfigDictionary& proxy_dict,
288 net::ProxyConfig* config) {
289 ProxyPrefs::ProxyMode mode;
290 if (!proxy_dict.GetMode(&mode)) {
291 // Fall back to system settings if the mode preference is invalid.
292 return false;
295 switch (mode) {
296 case ProxyPrefs::MODE_SYSTEM:
297 // Use system settings.
298 return false;
299 case ProxyPrefs::MODE_DIRECT:
300 // Ignore all the other proxy config preferences if the use of a proxy
301 // has been explicitly disabled.
302 return true;
303 case ProxyPrefs::MODE_AUTO_DETECT:
304 config->set_auto_detect(true);
305 return true;
306 case ProxyPrefs::MODE_PAC_SCRIPT: {
307 std::string proxy_pac;
308 if (!proxy_dict.GetPacUrl(&proxy_pac)) {
309 LOG(ERROR) << "Proxy settings request PAC script but do not specify "
310 << "its URL. Falling back to direct connection.";
311 return true;
313 GURL proxy_pac_url(proxy_pac);
314 if (!proxy_pac_url.is_valid()) {
315 LOG(ERROR) << "Invalid proxy PAC url: " << proxy_pac;
316 return true;
318 config->set_pac_url(proxy_pac_url);
319 bool pac_mandatory = false;
320 proxy_dict.GetPacMandatory(&pac_mandatory);
321 config->set_pac_mandatory(pac_mandatory);
322 return true;
324 case ProxyPrefs::MODE_FIXED_SERVERS: {
325 std::string proxy_server;
326 if (!proxy_dict.GetProxyServer(&proxy_server)) {
327 LOG(ERROR) << "Proxy settings request fixed proxy servers but do not "
328 << "specify their URLs. Falling back to direct connection.";
329 return true;
331 config->proxy_rules().ParseFromString(proxy_server);
333 std::string proxy_bypass;
334 if (proxy_dict.GetBypassList(&proxy_bypass)) {
335 config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
337 return true;
339 case ProxyPrefs::kModeCount: {
340 // Fall through to NOTREACHED().
343 NOTREACHED() << "Unknown proxy mode, falling back to system settings.";
344 return false;
347 void PrefProxyConfigTrackerImpl::OnProxyPrefChanged() {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
349 net::ProxyConfig new_config;
350 ProxyPrefs::ConfigState config_state = ReadPrefConfig(pref_service_,
351 &new_config);
352 if (config_state_ != config_state ||
353 (config_state_ != ProxyPrefs::CONFIG_UNSET &&
354 !pref_config_.Equals(new_config))) {
355 config_state_ = config_state;
356 if (config_state_ != ProxyPrefs::CONFIG_UNSET)
357 pref_config_ = new_config;
358 update_pending_ = true;
360 if (update_pending_)
361 OnProxyConfigChanged(config_state, new_config);