summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authormnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-24 14:44:02 +0000
committermnissler@chromium.org <mnissler@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-24 14:44:02 +0000
commitcc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d (patch)
treebc595667061dde2db1c835dde6e7505ae257a54d /chrome/browser
parent8e0e513103bb5dc6ff104c669432c3aba4af18d3 (diff)
downloadchromium_src-cc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d.zip
chromium_src-cc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d.tar.gz
chromium_src-cc5bfd4de6d8b8c6f33ff1f4a9b28eecf562ec9d.tar.bz2
Dynamically refresh pref-configured proxies.
BUG=63175,48930 TEST=unit tests Review URL: http://codereview.chromium.org/5005002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67261 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/net/chrome_url_request_context.cc91
-rw-r--r--chrome/browser/net/chrome_url_request_context.h5
-rw-r--r--chrome/browser/net/chrome_url_request_context_unittest.cc181
-rw-r--r--chrome/browser/net/pref_proxy_config_service.cc222
-rw-r--r--chrome/browser/net/pref_proxy_config_service.h127
-rw-r--r--chrome/browser/net/pref_proxy_config_service_unittest.cc384
-rw-r--r--chrome/browser/profile.cc4
-rw-r--r--chrome/browser/profile.h5
-rw-r--r--chrome/browser/profile_impl.cc11
-rw-r--r--chrome/browser/profile_impl.h4
10 files changed, 772 insertions, 262 deletions
diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc
index 4ec3939..bd3e9c6 100644
--- a/chrome/browser/net/chrome_url_request_context.cc
+++ b/chrome/browser/net/chrome_url_request_context.cc
@@ -18,6 +18,7 @@
#include "chrome/browser/net/chrome_net_log.h"
#include "chrome/browser/net/sqlite_persistent_cookie_store.h"
#include "chrome/browser/net/predictor_api.h"
+#include "chrome/browser/net/pref_proxy_config_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
@@ -68,26 +69,25 @@ net::ProxyConfigService* CreateProxyConfigService(Profile* profile) {
// from the UI thread.
CheckCurrentlyOnMainThread();
- scoped_ptr<net::ProxyConfig> proxy_config(CreateProxyConfig(
- profile->GetPrefs()));
+ // Create a baseline service that provides proxy configuration in case nothing
+ // is configured through prefs (Note: prefs include command line and
+ // configuration policy).
+ net::ProxyConfigService* base_service = NULL;
- if (!proxy_config.get()) {
- // Use system settings.
- // TODO(port): the IO and FILE message loops are only used by Linux. Can
- // that code be moved to chrome/browser instead of being in net, so that it
- // can use BrowserThread instead of raw MessageLoop pointers? See bug 25354.
+ // TODO(port): the IO and FILE message loops are only used by Linux. Can
+ // that code be moved to chrome/browser instead of being in net, so that it
+ // can use BrowserThread instead of raw MessageLoop pointers? See bug 25354.
#if defined(OS_CHROMEOS)
- return new chromeos::ProxyConfigService(
- profile->GetChromeOSProxyConfigServiceImpl());
+ base_service = new chromeos::ProxyConfigService(
+ profile->GetChromeOSProxyConfigServiceImpl());
#else
- return net::ProxyService::CreateSystemProxyConfigService(
- g_browser_process->io_thread()->message_loop(),
- g_browser_process->file_thread()->message_loop());
+ base_service = net::ProxyService::CreateSystemProxyConfigService(
+ g_browser_process->io_thread()->message_loop(),
+ g_browser_process->file_thread()->message_loop());
#endif // defined(OS_CHROMEOS)
- }
- // Otherwise use the fixed settings from the command line.
- return new net::ProxyConfigServiceFixed(*proxy_config.get());
+ return new PrefProxyConfigService(profile->GetProxyConfigTracker(),
+ base_service);
}
// Create a proxy service according to the options on command line.
@@ -982,64 +982,3 @@ void ChromeURLRequestContextFactory::ApplyProfileParametersToContext(
context->set_browser_file_system_context(browser_file_system_context_);
context->set_extension_info_map(extension_info_map_);
}
-
-// ----------------------------------------------------------------------------
-
-net::ProxyConfig* CreateProxyConfig(const PrefService* pref_service) {
- // Scan for all "enable" type proxy switches.
- static const char* proxy_prefs[] = {
- prefs::kProxyPacUrl,
- prefs::kProxyServer,
- prefs::kProxyBypassList,
- prefs::kProxyAutoDetect
- };
-
- // Check whether the preference system holds a valid proxy configuration. Note
- // that preferences coming from a lower-priority source than the user settings
- // are ignored. That's because chrome treats the system settings as the
- // default values, which should apply if there's no explicit value forced by
- // policy or the user.
- bool found_enable_proxy_pref = false;
- for (size_t i = 0; i < arraysize(proxy_prefs); i++) {
- const PrefService::Preference* pref =
- pref_service->FindPreference(proxy_prefs[i]);
- DCHECK(pref);
- if (pref && (!pref->IsUserModifiable() || pref->HasUserSetting())) {
- found_enable_proxy_pref = true;
- break;
- }
- }
-
- if (!found_enable_proxy_pref &&
- !pref_service->GetBoolean(prefs::kNoProxyServer)) {
- return NULL;
- }
-
- net::ProxyConfig* proxy_config = new net::ProxyConfig();
- if (pref_service->GetBoolean(prefs::kNoProxyServer)) {
- // Ignore all the other proxy config preferences if the use of a proxy
- // has been explicitly disabled.
- return proxy_config;
- }
-
- if (pref_service->HasPrefPath(prefs::kProxyServer)) {
- std::string proxy_server = pref_service->GetString(prefs::kProxyServer);
- proxy_config->proxy_rules().ParseFromString(proxy_server);
- }
-
- if (pref_service->HasPrefPath(prefs::kProxyPacUrl)) {
- std::string proxy_pac = pref_service->GetString(prefs::kProxyPacUrl);
- proxy_config->set_pac_url(GURL(proxy_pac));
- }
-
- proxy_config->set_auto_detect(pref_service->GetBoolean(
- prefs::kProxyAutoDetect));
-
- if (pref_service->HasPrefPath(prefs::kProxyBypassList)) {
- std::string proxy_bypass =
- pref_service->GetString(prefs::kProxyBypassList);
- proxy_config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
- }
-
- return proxy_config;
-}
diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h
index d7c08c0..8f2c852 100644
--- a/chrome/browser/net/chrome_url_request_context.h
+++ b/chrome/browser/net/chrome_url_request_context.h
@@ -388,9 +388,4 @@ class ChromeURLRequestContextFactory {
DISALLOW_COPY_AND_ASSIGN(ChromeURLRequestContextFactory);
};
-// Creates a proxy configuration from proxy-related preferences fetched
-// from |pref_service|. The relevant preferences in |pref_service| are
-// initialized from the process' command line or by applicable proxy policies.
-net::ProxyConfig* CreateProxyConfig(const PrefService* pref_service);
-
#endif // CHROME_BROWSER_NET_CHROME_URL_REQUEST_CONTEXT_H_
diff --git a/chrome/browser/net/chrome_url_request_context_unittest.cc b/chrome/browser/net/chrome_url_request_context_unittest.cc
deleted file mode 100644
index 5faaf44..0000000
--- a/chrome/browser/net/chrome_url_request_context_unittest.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/net/chrome_url_request_context.h"
-
-#include "base/command_line.h"
-#include "base/format_macros.h"
-#include "chrome/browser/policy/configuration_policy_pref_store.h"
-#include "chrome/browser/prefs/command_line_pref_store.h"
-#include "chrome/browser/prefs/default_pref_store.h"
-#include "chrome/browser/prefs/pref_value_store.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/testing_pref_service.h"
-#include "net/proxy/proxy_config.h"
-#include "net/proxy/proxy_config_service_common_unittest.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// Builds an identifier for each test in an array.
-#define TEST_DESC(desc) StringPrintf("at line %d <%s>", __LINE__, desc)
-
-TEST(ChromeURLRequestContextTest, CreateProxyConfigTest) {
- FilePath unused_path(FILE_PATH_LITERAL("foo.exe"));
- // Build the input command lines here.
- CommandLine empty(unused_path);
- CommandLine no_proxy(unused_path);
- no_proxy.AppendSwitch(switches::kNoProxyServer);
- CommandLine no_proxy_extra_params(unused_path);
- no_proxy_extra_params.AppendSwitch(switches::kNoProxyServer);
- no_proxy_extra_params.AppendSwitchASCII(switches::kProxyServer,
- "http://proxy:8888");
- CommandLine single_proxy(unused_path);
- single_proxy.AppendSwitchASCII(switches::kProxyServer, "http://proxy:8888");
- CommandLine per_scheme_proxy(unused_path);
- per_scheme_proxy.AppendSwitchASCII(switches::kProxyServer,
- "http=httpproxy:8888;ftp=ftpproxy:8889");
- CommandLine per_scheme_proxy_bypass(unused_path);
- per_scheme_proxy_bypass.AppendSwitchASCII(
- switches::kProxyServer,
- "http=httpproxy:8888;ftp=ftpproxy:8889");
- per_scheme_proxy_bypass.AppendSwitchASCII(
- switches::kProxyBypassList,
- ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8");
- CommandLine with_pac_url(unused_path);
- with_pac_url.AppendSwitchASCII(switches::kProxyPacUrl, "http://wpad/wpad.dat");
- with_pac_url.AppendSwitchASCII(
- switches::kProxyBypassList,
- ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8");
- CommandLine with_auto_detect(unused_path);
- with_auto_detect.AppendSwitch(switches::kProxyAutoDetect);
-
- // Inspired from proxy_config_service_win_unittest.cc.
- const struct {
- // Short description to identify the test
- std::string description;
-
- // The command line to build a ProxyConfig from.
- const CommandLine& command_line;
-
- // Expected outputs (fields of the ProxyConfig).
- bool is_null;
- bool auto_detect;
- GURL pac_url;
- net::ProxyRulesExpectation proxy_rules;
- } tests[] = {
- {
- TEST_DESC("Empty command line"),
- // Input
- empty,
- // Expected result
- true, // is_null
- false, // auto_detect
- GURL(), // pac_url
- net::ProxyRulesExpectation::Empty(),
- },
- {
- TEST_DESC("No proxy"),
- // Input
- no_proxy,
- // Expected result
- false, // is_null
- false, // auto_detect
- GURL(), // pac_url
- net::ProxyRulesExpectation::Empty(),
- },
- {
- TEST_DESC("No proxy with extra parameters."),
- // Input
- no_proxy_extra_params,
- // Expected result
- false, // is_null
- false, // auto_detect
- GURL(), // pac_url
- net::ProxyRulesExpectation::Empty(),
- },
- {
- TEST_DESC("Single proxy."),
- // Input
- single_proxy,
- // Expected result
- false, // is_null
- false, // auto_detect
- GURL(), // pac_url
- net::ProxyRulesExpectation::Single(
- "proxy:8888", // single proxy
- ""), // bypass rules
- },
- {
- TEST_DESC("Per scheme proxy."),
- // Input
- per_scheme_proxy,
- // Expected result
- false, // is_null
- false, // auto_detect
- GURL(), // pac_url
- net::ProxyRulesExpectation::PerScheme(
- "httpproxy:8888", // http
- "", // https
- "ftpproxy:8889", // ftp
- ""), // bypass rules
- },
- {
- TEST_DESC("Per scheme proxy with bypass URLs."),
- // Input
- per_scheme_proxy_bypass,
- // Expected result
- false, // is_null
- false, // auto_detect
- GURL(), // pac_url
- net::ProxyRulesExpectation::PerScheme(
- "httpproxy:8888", // http
- "", // https
- "ftpproxy:8889", // ftp
- "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"),
- },
- {
- TEST_DESC("Pac URL with proxy bypass URLs"),
- // Input
- with_pac_url,
- // Expected result
- false, // is_null
- false, // auto_detect
- GURL("http://wpad/wpad.dat"), // pac_url
- net::ProxyRulesExpectation::EmptyWithBypass(
- "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"),
- },
- {
- TEST_DESC("Autodetect"),
- // Input
- with_auto_detect,
- // Expected result
- false, // is_null
- true, // auto_detect
- GURL(), // pac_url
- net::ProxyRulesExpectation::Empty(),
- }
- };
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); i++) {
- SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i,
- tests[i].description.c_str()));
- CommandLine command_line(tests[i].command_line);
- // Only configuration-policy and default prefs are needed.
- PrefService prefs(new TestingPrefService::TestingPrefValueStore(
- new policy::ConfigurationPolicyPrefStore(NULL),
- new policy::ConfigurationPolicyPrefStore(NULL), NULL,
- new CommandLinePrefStore(&command_line), NULL, NULL,
- new DefaultPrefStore()));
- ChromeURLRequestContextGetter::RegisterUserPrefs(&prefs);
- scoped_ptr<net::ProxyConfig> config(CreateProxyConfig(&prefs));
-
- if (tests[i].is_null) {
- EXPECT_TRUE(config == NULL);
- } else {
- EXPECT_TRUE(config != NULL);
- EXPECT_EQ(tests[i].auto_detect, config->auto_detect());
- EXPECT_EQ(tests[i].pac_url, config->pac_url());
- EXPECT_TRUE(tests[i].proxy_rules.Matches(config->proxy_rules()));
- }
- }
-}
diff --git a/chrome/browser/net/pref_proxy_config_service.cc b/chrome/browser/net/pref_proxy_config_service.cc
new file mode 100644
index 0000000..5cd6213
--- /dev/null
+++ b/chrome/browser/net/pref_proxy_config_service.cc
@@ -0,0 +1,222 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/pref_proxy_config_service.h"
+
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/pref_set_observer.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+
+PrefProxyConfigTracker::PrefProxyConfigTracker(PrefService* pref_service)
+ : pref_service_(pref_service) {
+ valid_ = ReadPrefConfig(&pref_config_);
+ proxy_prefs_observer_.reset(
+ PrefSetObserver::CreateProxyPrefSetObserver(pref_service_, this));
+}
+
+PrefProxyConfigTracker::~PrefProxyConfigTracker() {
+ DCHECK(pref_service_ == NULL);
+}
+
+bool PrefProxyConfigTracker::GetProxyConfig(net::ProxyConfig* config) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (valid_)
+ *config = pref_config_;
+ return valid_;
+}
+
+void PrefProxyConfigTracker::DetachFromPrefService() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Stop notifications.
+ proxy_prefs_observer_.reset();
+ pref_service_ = NULL;
+}
+
+void PrefProxyConfigTracker::AddObserver(
+ PrefProxyConfigTracker::ObserverInterface* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ observers_.AddObserver(observer);
+}
+
+void PrefProxyConfigTracker::RemoveObserver(
+ PrefProxyConfigTracker::ObserverInterface* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ observers_.RemoveObserver(observer);
+}
+
+void PrefProxyConfigTracker::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (type == NotificationType::PREF_CHANGED &&
+ Source<PrefService>(source).ptr() == pref_service_) {
+ net::ProxyConfig new_config;
+ bool valid = ReadPrefConfig(&new_config);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &PrefProxyConfigTracker::InstallProxyConfig,
+ new_config, valid));
+ } else {
+ NOTREACHED() << "Unexpected notification of type " << type.value;
+ }
+}
+
+void PrefProxyConfigTracker::InstallProxyConfig(const net::ProxyConfig& config,
+ bool valid) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (valid_ != valid || !pref_config_.Equals(config)) {
+ valid_ = valid;
+ if (valid_)
+ pref_config_ = config;
+ FOR_EACH_OBSERVER(ObserverInterface, observers_,
+ OnPrefProxyConfigChanged());
+ }
+}
+
+bool PrefProxyConfigTracker::ReadPrefConfig(net::ProxyConfig* config) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Clear the configuration.
+ *config = net::ProxyConfig();
+
+ // Scan for all "enable" type proxy switches.
+ static const char* proxy_prefs[] = {
+ prefs::kProxyPacUrl,
+ prefs::kProxyServer,
+ prefs::kProxyBypassList,
+ prefs::kProxyAutoDetect
+ };
+
+ // Check whether the preference system holds a valid proxy configuration. Note
+ // that preferences coming from a lower-priority source than the user settings
+ // are ignored. That's because chrome treats the system settings as the
+ // default values, which should apply if there's no explicit value forced by
+ // policy or the user.
+ bool found_enable_proxy_pref = false;
+ for (size_t i = 0; i < arraysize(proxy_prefs); i++) {
+ const PrefService::Preference* pref =
+ pref_service_->FindPreference(proxy_prefs[i]);
+ DCHECK(pref);
+ if (pref && (!pref->IsUserModifiable() || pref->HasUserSetting())) {
+ found_enable_proxy_pref = true;
+ break;
+ }
+ }
+
+ if (!found_enable_proxy_pref &&
+ !pref_service_->GetBoolean(prefs::kNoProxyServer)) {
+ return false;
+ }
+
+ if (pref_service_->GetBoolean(prefs::kNoProxyServer)) {
+ // Ignore all the other proxy config preferences if the use of a proxy
+ // has been explicitly disabled.
+ return true;
+ }
+
+ if (pref_service_->HasPrefPath(prefs::kProxyServer)) {
+ std::string proxy_server = pref_service_->GetString(prefs::kProxyServer);
+ config->proxy_rules().ParseFromString(proxy_server);
+ }
+
+ if (pref_service_->HasPrefPath(prefs::kProxyPacUrl)) {
+ std::string proxy_pac = pref_service_->GetString(prefs::kProxyPacUrl);
+ config->set_pac_url(GURL(proxy_pac));
+ }
+
+ config->set_auto_detect(pref_service_->GetBoolean(prefs::kProxyAutoDetect));
+
+ if (pref_service_->HasPrefPath(prefs::kProxyBypassList)) {
+ std::string proxy_bypass =
+ pref_service_->GetString(prefs::kProxyBypassList);
+ config->proxy_rules().bypass_rules.ParseFromString(proxy_bypass);
+ }
+
+ return true;
+}
+
+PrefProxyConfigService::PrefProxyConfigService(
+ PrefProxyConfigTracker* tracker,
+ net::ProxyConfigService* base_service)
+ : base_service_(base_service),
+ pref_config_tracker_(tracker),
+ registered_observers_(false) {
+}
+
+PrefProxyConfigService::~PrefProxyConfigService() {
+ if (registered_observers_) {
+ base_service_->RemoveObserver(this);
+ pref_config_tracker_->RemoveObserver(this);
+ }
+}
+
+void PrefProxyConfigService::AddObserver(
+ net::ProxyConfigService::Observer* observer) {
+ RegisterObservers();
+ observers_.AddObserver(observer);
+}
+
+void PrefProxyConfigService::RemoveObserver(
+ net::ProxyConfigService::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool PrefProxyConfigService::GetLatestProxyConfig(net::ProxyConfig* config) {
+ RegisterObservers();
+ const net::ProxyConfig pref_config;
+ if (pref_config_tracker_->GetProxyConfig(config))
+ return true;
+
+ return base_service_->GetLatestProxyConfig(config);
+}
+
+void PrefProxyConfigService::OnLazyPoll() {
+ base_service_->OnLazyPoll();
+}
+
+void PrefProxyConfigService::OnProxyConfigChanged(
+ const net::ProxyConfig& config) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Check whether there is a proxy configuration defined by preferences. In
+ // this case that proxy configuration takes precedence and the change event
+ // from the delegate proxy service can be disregarded.
+ net::ProxyConfig pref_config;
+ if (!pref_config_tracker_->GetProxyConfig(&pref_config)) {
+ FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
+ OnProxyConfigChanged(config));
+ }
+}
+
+void PrefProxyConfigService::OnPrefProxyConfigChanged() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Evaluate the proxy configuration. If GetLatestProxyConfig returns false,
+ // we are using the system proxy service, but it doesn't have a valid
+ // configuration yet. Once it is ready, OnProxyConfigChanged() will be called
+ // and broadcast the proxy configuration.
+ // Note: If a switch between a preference proxy configuration and the system
+ // proxy configuration occurs an unnecessary notification might get send if
+ // the two configurations agree. This case should be rare however, so we don't
+ // handle that case specially.
+ net::ProxyConfig new_config;
+ if (GetLatestProxyConfig(&new_config)) {
+ FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
+ OnProxyConfigChanged(new_config));
+ }
+}
+
+void PrefProxyConfigService::RegisterObservers() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!registered_observers_) {
+ base_service_->AddObserver(this);
+ pref_config_tracker_->AddObserver(this);
+ registered_observers_ = true;
+ }
+}
diff --git a/chrome/browser/net/pref_proxy_config_service.h b/chrome/browser/net/pref_proxy_config_service.h
new file mode 100644
index 0000000..362b109
--- /dev/null
+++ b/chrome/browser/net/pref_proxy_config_service.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NET_PREF_PROXY_CONFIG_SERVICE_H_
+#define CHROME_BROWSER_NET_PREF_PROXY_CONFIG_SERVICE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/notification_observer.h"
+#include "net/proxy/proxy_config.h"
+#include "net/proxy/proxy_config_service.h"
+
+class PrefService;
+class PrefSetObserver;
+
+// A helper class that tracks proxy preferences. It translates the configuration
+// to net::ProxyConfig and proxies the result over to the IO thread for
+// PrefProxyConfigService to use.
+class PrefProxyConfigTracker
+ : public base::RefCountedThreadSafe<PrefProxyConfigTracker>,
+ public NotificationObserver {
+ public:
+ // Observer interface used to send out notifications on the IO thread about
+ // changes to the proxy configuration.
+ class ObserverInterface {
+ public:
+ virtual ~ObserverInterface() {}
+ virtual void OnPrefProxyConfigChanged() = 0;
+ };
+
+ explicit PrefProxyConfigTracker(PrefService* pref_service);
+ virtual ~PrefProxyConfigTracker();
+
+ // Observer manipulation is only valid on the IO thread.
+ void AddObserver(ObserverInterface* observer);
+ void RemoveObserver(ObserverInterface* observer);
+
+ // Get the proxy configuration currently defined by preferences. Writes the
+ // configuration to |config| and returns true on success. |config| is not
+ // touched and false is returned if there is no configuration defined. This
+ // must be called on the IO thread.
+ bool GetProxyConfig(net::ProxyConfig* config);
+
+ // Notifies the tracker that the pref service passed upon construction is
+ // about to go away. This must be called from the UI thread.
+ void DetachFromPrefService();
+
+ private:
+ // NotificationObserver implementation:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Install a new configuration. This is invoked on the IO thread to update
+ // the internal state after handling a pref change on the UI thread. |valid|
+ // indicates whether there is a preference proxy configuration defined, in
+ // which case this configuration is given by |config|. |config| is ignored if
+ // |valid| is false.
+ void InstallProxyConfig(const net::ProxyConfig& config, bool valid);
+
+ // Creates a proxy configuration from proxy-related preferences. Configuration
+ // is stored in |config| and the return value indicates whether the
+ // configuration is valid.
+ bool ReadPrefConfig(net::ProxyConfig* config);
+
+ // Configuration as defined by prefs. Only to be accessed from the IO thread
+ // (expect for construction).
+ net::ProxyConfig pref_config_;
+
+ // Whether |pref_config_| is valid. Only accessed from the IO thread.
+ bool valid_;
+
+ // List of observers, accessed exclusively from the IO thread.
+ ObserverList<ObserverInterface, true> observers_;
+
+ // Pref-related members that should only be accessed from the UI thread.
+ PrefService* pref_service_;
+ scoped_ptr<PrefSetObserver> proxy_prefs_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefProxyConfigTracker);
+};
+
+// A net::ProxyConfigService implementation that applies preference proxy
+// settings as overrides to the proxy configuration determined by a baseline
+// delegate ProxyConfigService.
+class PrefProxyConfigService
+ : public net::ProxyConfigService,
+ public net::ProxyConfigService::Observer,
+ public PrefProxyConfigTracker::ObserverInterface {
+ public:
+ // Takes ownership of the passed |base_service|.
+ PrefProxyConfigService(PrefProxyConfigTracker* tracker,
+ net::ProxyConfigService* base_service);
+ virtual ~PrefProxyConfigService();
+
+ // ProxyConfigService implementation:
+ virtual void AddObserver(net::ProxyConfigService::Observer* observer);
+ virtual void RemoveObserver(net::ProxyConfigService::Observer* observer);
+ virtual bool GetLatestProxyConfig(net::ProxyConfig* config);
+ virtual void OnLazyPoll();
+
+ private:
+ // ProxyConfigService::Observer implementation:
+ virtual void OnProxyConfigChanged(const net::ProxyConfig& config);
+
+ // PrefProxyConfigTracker::Observer implementation:
+ virtual void OnPrefProxyConfigChanged();
+
+ // Makes sure that the observer registrations with the base service and the
+ // tracker object are set up.
+ void RegisterObservers();
+
+ scoped_ptr<net::ProxyConfigService> base_service_;
+ ObserverList<net::ProxyConfigService::Observer, true> observers_;
+ scoped_refptr<PrefProxyConfigTracker> pref_config_tracker_;
+
+ // Indicates whether the base service and tracker registrations are done.
+ bool registered_observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefProxyConfigService);
+};
+
+#endif // CHROME_BROWSER_NET_PREF_PROXY_CONFIG_SERVICE_H_
diff --git a/chrome/browser/net/pref_proxy_config_service_unittest.cc b/chrome/browser/net/pref_proxy_config_service_unittest.cc
new file mode 100644
index 0000000..e7917b1
--- /dev/null
+++ b/chrome/browser/net/pref_proxy_config_service_unittest.cc
@@ -0,0 +1,384 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/pref_proxy_config_service.h"
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_pref_service.h"
+#include "net/proxy/proxy_config_service_common_unittest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace {
+
+const char kFixedPacUrl[] = "http://chromium.org/fixed_pac_url";
+
+// Testing proxy config service that allows us to fire notifications at will.
+class TestProxyConfigService : public net::ProxyConfigService {
+ public:
+ explicit TestProxyConfigService(const net::ProxyConfig& config)
+ : config_(config) {
+ }
+
+ void SetProxyConfig(const net::ProxyConfig config) {
+ config_ = config;
+ FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_,
+ OnProxyConfigChanged(config_));
+ }
+
+ private:
+ virtual void AddObserver(net::ProxyConfigService::Observer* observer) {
+ observers_.AddObserver(observer);
+ }
+
+ virtual void RemoveObserver(net::ProxyConfigService::Observer* observer) {
+ observers_.RemoveObserver(observer);
+ }
+
+ virtual bool GetLatestProxyConfig(net::ProxyConfig* config) {
+ *config = config_;
+ return true;
+ }
+
+ net::ProxyConfig config_;
+ ObserverList<net::ProxyConfigService::Observer, true> observers_;
+};
+
+// A mock observer for capturing callbacks.
+class MockObserver : public net::ProxyConfigService::Observer {
+ public:
+ MOCK_METHOD1(OnProxyConfigChanged, void(const net::ProxyConfig&));
+};
+
+template<typename TESTBASE>
+class PrefProxyConfigServiceTestBase : public TESTBASE {
+ protected:
+ PrefProxyConfigServiceTestBase()
+ : ui_thread_(BrowserThread::UI, &loop_),
+ io_thread_(BrowserThread::IO, &loop_) {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(pref_service_.get());
+ ChromeURLRequestContextGetter::RegisterUserPrefs(pref_service_.get());
+ fixed_config_.set_pac_url(GURL(kFixedPacUrl));
+ delegate_service_ = new TestProxyConfigService(fixed_config_);
+ proxy_config_tracker_ = new PrefProxyConfigTracker(pref_service_.get());
+ proxy_config_service_.reset(
+ new PrefProxyConfigService(proxy_config_tracker_.get(),
+ delegate_service_));
+ }
+
+ virtual void TearDown() {
+ proxy_config_tracker_->DetachFromPrefService();
+ loop_.RunAllPending();
+ proxy_config_service_.reset();
+ pref_service_.reset();
+ }
+
+ MessageLoop loop_;
+ TestProxyConfigService* delegate_service_; // weak
+ scoped_ptr<TestingPrefService> pref_service_;
+ scoped_ptr<PrefProxyConfigService> proxy_config_service_;
+ net::ProxyConfig fixed_config_;
+
+ private:
+ scoped_refptr<PrefProxyConfigTracker> proxy_config_tracker_;
+ BrowserThread ui_thread_;
+ BrowserThread io_thread_;
+};
+
+class PrefProxyConfigServiceTest
+ : public PrefProxyConfigServiceTestBase<testing::Test> {
+ protected:
+ virtual void SetUp() {
+ pref_service_.reset(new TestingPrefService);
+ PrefProxyConfigServiceTestBase<testing::Test>::SetUp();
+ }
+};
+
+TEST_F(PrefProxyConfigServiceTest, BaseConfiguration) {
+ net::ProxyConfig actual_config;
+ proxy_config_service_->GetLatestProxyConfig(&actual_config);
+ EXPECT_EQ(GURL(kFixedPacUrl), actual_config.pac_url());
+}
+
+TEST_F(PrefProxyConfigServiceTest, DynamicPrefOverrides) {
+ pref_service_->SetManagedPref(
+ prefs::kProxyServer, Value::CreateStringValue("http://example.com:3128"));
+ loop_.RunAllPending();
+
+ net::ProxyConfig actual_config;
+ proxy_config_service_->GetLatestProxyConfig(&actual_config);
+ EXPECT_FALSE(actual_config.auto_detect());
+ EXPECT_EQ(net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY,
+ actual_config.proxy_rules().type);
+ EXPECT_EQ(actual_config.proxy_rules().single_proxy,
+ net::ProxyServer::FromURI("http://example.com:3128",
+ net::ProxyServer::SCHEME_HTTP));
+
+ pref_service_->SetManagedPref(
+ prefs::kProxyAutoDetect, Value::CreateBooleanValue(true));
+ loop_.RunAllPending();
+
+ proxy_config_service_->GetLatestProxyConfig(&actual_config);
+ EXPECT_TRUE(actual_config.auto_detect());
+}
+
+// Compares proxy configurations, but allows different identifiers.
+MATCHER_P(ProxyConfigMatches, config, "") {
+ net::ProxyConfig reference(config);
+ reference.set_id(arg.id());
+ return reference.Equals(arg);
+}
+
+TEST_F(PrefProxyConfigServiceTest, Observers) {
+ MockObserver observer;
+ proxy_config_service_->AddObserver(&observer);
+
+ // Firing the observers in the delegate should trigger a notification.
+ net::ProxyConfig config2;
+ config2.set_auto_detect(true);
+ EXPECT_CALL(observer,
+ OnProxyConfigChanged(ProxyConfigMatches(config2))).Times(1);
+ delegate_service_->SetProxyConfig(config2);
+ loop_.RunAllPending();
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Override configuration, this should trigger a notification.
+ net::ProxyConfig pref_config;
+ pref_config.set_pac_url(GURL(kFixedPacUrl));
+ EXPECT_CALL(observer,
+ OnProxyConfigChanged(ProxyConfigMatches(pref_config))).Times(1);
+ pref_service_->SetManagedPref(prefs::kProxyPacUrl,
+ Value::CreateStringValue(kFixedPacUrl));
+ loop_.RunAllPending();
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Since there are pref overrides, delegate changes should be ignored.
+ net::ProxyConfig config3;
+ config3.proxy_rules().ParseFromString("http=config3:80");
+ EXPECT_CALL(observer, OnProxyConfigChanged(_)).Times(0);
+ fixed_config_.set_auto_detect(true);
+ delegate_service_->SetProxyConfig(config3);
+ loop_.RunAllPending();
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Clear the override should switch back to the fixed configuration.
+ EXPECT_CALL(observer,
+ OnProxyConfigChanged(ProxyConfigMatches(config3))).Times(1);
+ pref_service_->RemoveManagedPref(prefs::kProxyPacUrl);
+ loop_.RunAllPending();
+ Mock::VerifyAndClearExpectations(&observer);
+
+ // Delegate service notifications should show up again.
+ net::ProxyConfig config4;
+ config4.proxy_rules().ParseFromString("socks:config4");
+ EXPECT_CALL(observer,
+ OnProxyConfigChanged(ProxyConfigMatches(config4))).Times(1);
+ delegate_service_->SetProxyConfig(config4);
+ loop_.RunAllPending();
+ Mock::VerifyAndClearExpectations(&observer);
+
+ proxy_config_service_->RemoveObserver(&observer);
+}
+
+// Test parameter object for testing command line proxy configuration.
+struct CommandLineTestParams {
+ // Explicit assignment operator, so testing::TestWithParam works with MSVC.
+ CommandLineTestParams& operator=(const CommandLineTestParams& other) {
+ description = other.description;
+ for (unsigned int i = 0; i < arraysize(switches); i++)
+ switches[i] = other.switches[i];
+ is_null = other.is_null;
+ auto_detect = other.auto_detect;
+ pac_url = other.pac_url;
+ proxy_rules = other.proxy_rules;
+ return *this;
+ }
+
+ // Short description to identify the test.
+ const char* description;
+
+ // The command line to build a ProxyConfig from.
+ struct SwitchValue {
+ const char* name;
+ const char* value;
+ } switches[2];
+
+ // Expected outputs (fields of the ProxyConfig).
+ bool is_null;
+ bool auto_detect;
+ GURL pac_url;
+ net::ProxyRulesExpectation proxy_rules;
+};
+
+void PrintTo(const CommandLineTestParams& params, std::ostream* os) {
+ *os << params.description;
+}
+
+class PrefProxyConfigServiceCommandLineTest
+ : public PrefProxyConfigServiceTestBase<
+ testing::TestWithParam<CommandLineTestParams> > {
+ protected:
+ PrefProxyConfigServiceCommandLineTest()
+ : command_line_(CommandLine::NO_PROGRAM) {}
+
+ virtual void SetUp() {
+ for (size_t i = 0; i < arraysize(GetParam().switches); i++) {
+ const char* name = GetParam().switches[i].name;
+ const char* value = GetParam().switches[i].value;
+ if (name && value)
+ command_line_.AppendSwitchASCII(name, value);
+ else if (name)
+ command_line_.AppendSwitch(name);
+ }
+ pref_service_.reset(new TestingPrefService(NULL, NULL, &command_line_));
+ PrefProxyConfigServiceTestBase<
+ testing::TestWithParam<CommandLineTestParams> >::SetUp();
+ }
+
+ private:
+ CommandLine command_line_;
+};
+
+TEST_P(PrefProxyConfigServiceCommandLineTest, CommandLine) {
+ net::ProxyConfig config;
+ proxy_config_service_->GetLatestProxyConfig(&config);
+
+ if (GetParam().is_null) {
+ EXPECT_EQ(GURL(kFixedPacUrl), config.pac_url());
+ } else {
+ EXPECT_NE(GURL(kFixedPacUrl), config.pac_url());
+ EXPECT_EQ(GetParam().auto_detect, config.auto_detect());
+ EXPECT_EQ(GetParam().pac_url, config.pac_url());
+ EXPECT_TRUE(GetParam().proxy_rules.Matches(config.proxy_rules()));
+ }
+}
+
+static const CommandLineTestParams kCommandLineTestParams[] = {
+ {
+ "Empty command line",
+ // Input
+ { },
+ // Expected result
+ true, // is_null
+ false, // auto_detect
+ GURL(), // pac_url
+ net::ProxyRulesExpectation::Empty(),
+ },
+ {
+ "No proxy",
+ // Input
+ {
+ { switches::kNoProxyServer, NULL },
+ },
+ // Expected result
+ false, // is_null
+ false, // auto_detect
+ GURL(), // pac_url
+ net::ProxyRulesExpectation::Empty(),
+ },
+ {
+ "No proxy with extra parameters.",
+ // Input
+ {
+ { switches::kNoProxyServer, NULL },
+ { switches::kProxyServer, "http://proxy:8888" },
+ },
+ // Expected result
+ false, // is_null
+ false, // auto_detect
+ GURL(), // pac_url
+ net::ProxyRulesExpectation::Empty(),
+ },
+ {
+ "Single proxy.",
+ // Input
+ {
+ { switches::kProxyServer, "http://proxy:8888" },
+ },
+ // Expected result
+ false, // is_null
+ false, // auto_detect
+ GURL(), // pac_url
+ net::ProxyRulesExpectation::Single(
+ "proxy:8888", // single proxy
+ ""), // bypass rules
+ },
+ {
+ "Per scheme proxy.",
+ // Input
+ {
+ { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" },
+ },
+ // Expected result
+ false, // is_null
+ false, // auto_detect
+ GURL(), // pac_url
+ net::ProxyRulesExpectation::PerScheme(
+ "httpproxy:8888", // http
+ "", // https
+ "ftpproxy:8889", // ftp
+ ""), // bypass rules
+ },
+ {
+ "Per scheme proxy with bypass URLs.",
+ // Input
+ {
+ { switches::kProxyServer, "http=httpproxy:8888;ftp=ftpproxy:8889" },
+ { switches::kProxyBypassList,
+ ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8" },
+ },
+ // Expected result
+ false, // is_null
+ false, // auto_detect
+ GURL(), // pac_url
+ net::ProxyRulesExpectation::PerScheme(
+ "httpproxy:8888", // http
+ "", // https
+ "ftpproxy:8889", // ftp
+ "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"),
+ },
+ {
+ "Pac URL with proxy bypass URLs",
+ // Input
+ {
+ { switches::kProxyPacUrl, "http://wpad/wpad.dat" },
+ { switches::kProxyBypassList,
+ ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8" },
+ },
+ // Expected result
+ false, // is_null
+ false, // auto_detect
+ GURL("http://wpad/wpad.dat"), // pac_url
+ net::ProxyRulesExpectation::EmptyWithBypass(
+ "*.google.com,foo.com:99,1.2.3.4:22,127.0.0.1/8"),
+ },
+ {
+ "Autodetect",
+ // Input
+ {
+ { switches::kProxyAutoDetect, NULL },
+ },
+ // Expected result
+ false, // is_null
+ true, // auto_detect
+ GURL(), // pac_url
+ net::ProxyRulesExpectation::Empty(),
+ },
+};
+
+INSTANTIATE_TEST_CASE_P(
+ PrefProxyConfigServiceCommandLineTestInstance,
+ PrefProxyConfigServiceCommandLineTest,
+ testing::ValuesIn(kCommandLineTestParams));
+
+} // namespace
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 2b10639..9ccda97 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -590,6 +590,10 @@ class OffTheRecordProfileImpl : public Profile,
return NULL;
}
+ virtual PrefProxyConfigTracker* GetProxyConfigTracker() {
+ return profile_->GetProxyConfigTracker();
+ }
+
private:
NotificationRegistrar registrar_;
diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h
index 57bb13d..5b5cdaf 100644
--- a/chrome/browser/profile.h
+++ b/chrome/browser/profile.h
@@ -72,6 +72,7 @@ class PersonalDataManager;
class PinnedTabService;
class PrefService;
class ExtensionInfoMap;
+class PrefProxyConfigTracker;
class PromoCounter;
class ProfileSyncService;
class ProfileSyncFactory;
@@ -485,6 +486,10 @@ class Profile {
GetChromeOSProxyConfigServiceImpl() = 0;
#endif // defined(OS_CHROMEOS)
+ // Returns the helper object that provides the proxy configuration service
+ // access to the the proxy configuration possibly defined by preferences.
+ virtual PrefProxyConfigTracker* GetProxyConfigTracker() = 0;
+
#ifdef UNIT_TEST
// Use with caution. GetDefaultRequestContext may be called on any thread!
static void set_default_request_context(URLRequestContextGetter* c) {
diff --git a/chrome/browser/profile_impl.cc b/chrome/browser/profile_impl.cc
index c66428d..8ee0b5d 100644
--- a/chrome/browser/profile_impl.cc
+++ b/chrome/browser/profile_impl.cc
@@ -51,6 +51,7 @@
#include "chrome/browser/net/chrome_url_request_context.h"
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/net/net_pref_observer.h"
+#include "chrome/browser/net/pref_proxy_config_service.h"
#include "chrome/browser/net/ssl_config_service_manager.h"
#include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/password_manager/password_store_default.h"
@@ -524,6 +525,9 @@ ProfileImpl::~ProfileImpl() {
if (extensions_service_)
extensions_service_->DestroyingProfile();
+ if (pref_proxy_config_tracker_)
+ pref_proxy_config_tracker_->DetachFromPrefService();
+
// This causes the Preferences file to be written to disk.
MarkAsCleanShutdown();
}
@@ -1318,3 +1322,10 @@ chromeos::ProxyConfigServiceImpl*
return chromeos_proxy_config_service_impl_;
}
#endif // defined(OS_CHROMEOS)
+
+PrefProxyConfigTracker* ProfileImpl::GetProxyConfigTracker() {
+ if (!pref_proxy_config_tracker_)
+ pref_proxy_config_tracker_ = new PrefProxyConfigTracker(GetPrefs());
+
+ return pref_proxy_config_tracker_;
+}
diff --git a/chrome/browser/profile_impl.h b/chrome/browser/profile_impl.h
index ea64bda..b0ea586 100644
--- a/chrome/browser/profile_impl.h
+++ b/chrome/browser/profile_impl.h
@@ -130,6 +130,8 @@ class ProfileImpl : public Profile,
virtual chromeos::ProxyConfigServiceImpl* GetChromeOSProxyConfigServiceImpl();
#endif // defined(OS_CHROMEOS)
+ virtual PrefProxyConfigTracker* GetProxyConfigTracker();
+
// NotificationObserver implementation.
virtual void Observe(NotificationType type,
const NotificationSource& source,
@@ -280,6 +282,8 @@ class ProfileImpl : public Profile,
chromeos_proxy_config_service_impl_;
#endif
+ scoped_refptr<PrefProxyConfigTracker> pref_proxy_config_tracker_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileImpl);
};