diff options
Diffstat (limited to 'net/proxy/proxy_config_service_linux.cc')
-rw-r--r-- | net/proxy/proxy_config_service_linux.cc | 628 |
1 files changed, 544 insertions, 84 deletions
diff --git a/net/proxy/proxy_config_service_linux.cc b/net/proxy/proxy_config_service_linux.cc index 2998a0c..a3a9993 100644 --- a/net/proxy/proxy_config_service_linux.cc +++ b/net/proxy/proxy_config_service_linux.cc @@ -4,13 +4,23 @@ #include "net/proxy/proxy_config_service_linux.h" +#include <errno.h> +#include <fcntl.h> #include <gconf/gconf-client.h> +#include <limits.h> +#include <stdio.h> #include <stdlib.h> +#include <sys/inotify.h> +#include <unistd.h> +#include "base/file_path.h" +#include "base/file_util.h" #include "base/logging.h" +#include "base/message_loop.h" #include "base/string_tokenizer.h" #include "base/string_util.h" #include "base/task.h" +#include "base/timer.h" #include "googleurl/src/url_canon.h" #include "net/base/net_errors.h" #include "net/http/http_util.h" @@ -55,6 +65,10 @@ std::string FixupProxyHostScheme(ProxyServer::Scheme scheme, host = "socks4://" + host; else if (scheme == ProxyServer::SCHEME_SOCKS5) host = "socks5://" + host; + // If there is a trailing slash, remove it so |host| will parse correctly + // even if it includes a port number (since the slash is not numeric). + if (host.length() && host[host.length() - 1] == '/') + host.resize(host.length() - 1); return host; } @@ -155,30 +169,16 @@ bool ProxyConfigServiceLinux::Delegate::GetConfigFromEnv(ProxyConfig* config) { namespace { -// static -// gconf notification callback, dispatched from the default -// glib main loop. -void OnGConfChangeNotification( - GConfClient* client, guint cnxn_id, - GConfEntry* entry, gpointer user_data) { - // It would be nice to debounce multiple callbacks in quick - // succession, since I guess we'll get one for each changed key. As - // it is we will read settings from gconf once for each callback. - LOG(INFO) << "gconf change notification for key " - << gconf_entry_get_key(entry); - // We don't track which key has changed, just that something did change. - // Forward to a method on the proxy config service delegate object. - ProxyConfigServiceLinux::Delegate* config_service_delegate = - reinterpret_cast<ProxyConfigServiceLinux::Delegate*>(user_data); - config_service_delegate->OnCheckProxyConfigSettings(); -} +const int kDebounceTimeoutMilliseconds = 250; -class GConfSettingGetterImpl +// This is the "real" gconf version that actually uses gconf. +class GConfSettingGetterImplGConf : public ProxyConfigServiceLinux::GConfSettingGetter { public: - GConfSettingGetterImpl() : client_(NULL), loop_(NULL) {} + GConfSettingGetterImplGConf() + : client_(NULL), notify_delegate_(NULL), loop_(NULL) {} - virtual ~GConfSettingGetterImpl() { + virtual ~GConfSettingGetterImplGConf() { // client_ should have been released before now, from // Delegate::OnDestroy(), while running on the UI thread. However // on exiting the process, it may happen that @@ -191,20 +191,22 @@ class GConfSettingGetterImpl // We are on the UI thread so we can clean it safely. This is // the case at least for ui_tests running under Valgrind in // bug 16076. - LOG(INFO) << "~GConfSettingGetterImpl: releasing gconf client"; - Release(); + LOG(INFO) << "~GConfSettingGetterImplGConf: releasing gconf client"; + Shutdown(); } else { - LOG(WARNING) << "~GConfSettingGetterImpl: leaking gconf client"; + LOG(WARNING) << "~GConfSettingGetterImplGConf: leaking gconf client"; client_ = NULL; } } DCHECK(!client_); } - virtual bool Init() { + virtual bool Init(MessageLoop* glib_default_loop, + MessageLoopForIO* file_loop) { + DCHECK(MessageLoop::current() == glib_default_loop); DCHECK(!client_); DCHECK(!loop_); - loop_ = MessageLoopForUI::current(); + loop_ = glib_default_loop; client_ = gconf_client_get_default(); if (!client_) { // It's not clear whether/when this can return NULL. @@ -224,13 +226,13 @@ class GConfSettingGetterImpl if (error != NULL) { LOG(ERROR) << "Error requesting gconf directory: " << error->message; g_error_free(error); - Release(); + Shutdown(); return false; } return true; } - void Release() { + void Shutdown() { if (client_) { DCHECK(MessageLoop::current() == loop_); // This also disables gconf notifications. @@ -240,29 +242,38 @@ class GConfSettingGetterImpl } } - bool SetupNotification(void* callback_user_data) { + bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) { DCHECK(client_); DCHECK(MessageLoop::current() == loop_); GError* error = NULL; + notify_delegate_ = delegate; gconf_client_notify_add( client_, "/system/proxy", - OnGConfChangeNotification, callback_user_data, + OnGConfChangeNotification, this, NULL, &error); if (error == NULL) { gconf_client_notify_add( client_, "/system/http_proxy", - OnGConfChangeNotification, callback_user_data, + OnGConfChangeNotification, this, NULL, &error); } if (error != NULL) { LOG(ERROR) << "Error requesting gconf notifications: " << error->message; g_error_free(error); - Release(); + Shutdown(); return false; } return true; } + MessageLoop* GetNotificationLoop() { + return loop_; + } + + virtual const char* GetDataSource() { + return "gconf"; + } + virtual bool GetString(const char* key, std::string* result) { DCHECK(client_); DCHECK(MessageLoop::current() == loop_); @@ -345,14 +356,451 @@ class GConfSettingGetterImpl return false; } + // This is the callback from the debounce timer. + void OnDebouncedNotification() { + DCHECK(MessageLoop::current() == loop_); + DCHECK(notify_delegate_); + // Forward to a method on the proxy config service delegate object. + notify_delegate_->OnCheckProxyConfigSettings(); + } + + void OnChangeNotification() { + // We don't use Reset() because the timer may not yet be running. + // (In that case Stop() is a no-op.) + debounce_timer_.Stop(); + debounce_timer_.Start(base::TimeDelta::FromMilliseconds( + kDebounceTimeoutMilliseconds), this, + &GConfSettingGetterImplGConf::OnDebouncedNotification); + } + + // gconf notification callback, dispatched from the default glib main loop. + static void OnGConfChangeNotification( + GConfClient* client, guint cnxn_id, + GConfEntry* entry, gpointer user_data) { + LOG(INFO) << "gconf change notification for key " + << gconf_entry_get_key(entry); + // We don't track which key has changed, just that something did change. + GConfSettingGetterImplGConf* setting_getter = + reinterpret_cast<GConfSettingGetterImplGConf*>(user_data); + setting_getter->OnChangeNotification(); + } + GConfClient* client_; + ProxyConfigServiceLinux::Delegate* notify_delegate_; + base::OneShotTimer<GConfSettingGetterImplGConf> debounce_timer_; // Message loop of the thread that we make gconf calls on. It should // be the UI thread and all our methods should be called on this // thread. Only for assertions. MessageLoop* loop_; - DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImpl); + DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplGConf); +}; + +// This is the KDE version that reads kioslaverc and simulates gconf. +// Doing this allows the main Delegate code, as well as the unit tests +// for it, to stay the same - and the settings map fairly well besides. +class GConfSettingGetterImplKDE + : public ProxyConfigServiceLinux::GConfSettingGetter, + public base::MessagePumpLibevent::Watcher { + public: + explicit GConfSettingGetterImplKDE( + base::EnvironmentVariableGetter* env_var_getter) + : inotify_fd_(-1), notify_delegate_(NULL), indirect_manual_(false), + auto_no_pac_(false), reversed_exception_(false), file_loop_(NULL) { + // We don't save the env var getter for later use since we don't own it. + // Instead we use it here and save the result we actually care about. + std::string kde_home; + if (!env_var_getter->Getenv("KDE_HOME", &kde_home)) { + if (!env_var_getter->Getenv("HOME", &kde_home)) + // User has no $HOME? Give up. Later we'll report the failure. + return; + kde_home = FilePath(kde_home).Append(FILE_PATH_LITERAL(".kde")).value(); + } + kde_config_dir_ = FilePath(kde_home).Append( + FILE_PATH_LITERAL("share")).Append(FILE_PATH_LITERAL("config")); + } + + virtual ~GConfSettingGetterImplKDE() { + // inotify_fd_ should have been closed before now, from + // Delegate::OnDestroy(), while running on the file thread. However + // on exiting the process, it may happen that Delegate::OnDestroy() + // task is left pending on the file loop after the loop was quit, + // and pending tasks may then be deleted without being run. + // Here in the KDE version, we can safely close the file descriptor + // anyway. (Not that it really matters; the process is exiting.) + if (inotify_fd_ >= 0) + Shutdown(); + DCHECK(inotify_fd_ < 0); + } + + virtual bool Init(MessageLoop* glib_default_loop, + MessageLoopForIO* file_loop) { + DCHECK(inotify_fd_ < 0); + inotify_fd_ = inotify_init(); + if (inotify_fd_ < 0) { + LOG(ERROR) << "inotify_init failed: " << strerror(errno); + return false; + } + int flags = fcntl(inotify_fd_, F_GETFL); + if (fcntl(inotify_fd_, F_SETFL, flags | O_NONBLOCK) < 0) { + LOG(ERROR) << "fcntl failed: " << strerror(errno); + close(inotify_fd_); + inotify_fd_ = -1; + return false; + } + file_loop_ = file_loop; + // The initial read is done on the current thread, not |file_loop_|, + // since we will need to have it for SetupAndFetchInitialConfig(). + UpdateCachedSettings(); + return true; + } + + void Shutdown() { + if (inotify_fd_ >= 0) { + ResetCachedSettings(); + inotify_watcher_.StopWatchingFileDescriptor(); + close(inotify_fd_); + inotify_fd_ = -1; + } + } + + bool SetupNotification(ProxyConfigServiceLinux::Delegate* delegate) { + DCHECK(inotify_fd_ >= 0); + DCHECK(file_loop_); + // We can't just watch the kioslaverc file directly, since KDE will write + // a new copy of it and then rename it whenever settings are changed and + // inotify watches inodes (so we'll be watching the old deleted file after + // the first change, and it will never change again). So, we watch the + // directory instead. We then act only on changes to the kioslaverc entry. + if (inotify_add_watch(inotify_fd_, kde_config_dir_.value().c_str(), + IN_MODIFY | IN_MOVED_TO) < 0) + return false; + notify_delegate_ = delegate; + return file_loop_->WatchFileDescriptor(inotify_fd_, true, + MessageLoopForIO::WATCH_READ, &inotify_watcher_, this); + } + + MessageLoop* GetNotificationLoop() { + return file_loop_; + } + + // Implement base::MessagePumpLibevent::Delegate. + void OnFileCanReadWithoutBlocking(int fd) { + DCHECK(fd == inotify_fd_); + DCHECK(MessageLoop::current() == file_loop_); + OnChangeNotification(); + } + void OnFileCanWriteWithoutBlocking(int fd) { + NOTREACHED(); + } + + virtual const char* GetDataSource() { + return "KDE"; + } + + virtual bool GetString(const char* key, std::string* result) { + string_map_type::iterator it = string_table_.find(key); + if (it == string_table_.end()) + return false; + *result = it->second; + return true; + } + virtual bool GetBoolean(const char* key, bool* result) { + // We don't ever have any booleans. + return false; + } + virtual bool GetInt(const char* key, int* result) { + // We don't ever have any integers. (See AddProxy() below about ports.) + return false; + } + virtual bool GetStringList(const char* key, + std::vector<std::string>* result) { + strings_map_type::iterator it = strings_table_.find(key); + if (it == strings_table_.end()) + return false; + *result = it->second; + return true; + } + + private: + void ResetCachedSettings() { + string_table_.clear(); + strings_table_.clear(); + indirect_manual_ = false; + auto_no_pac_ = false; + reversed_exception_ = false; + } + + void AddProxy(std::string prefix, std::string value) { + if (value.empty() || value.substr(0, 3) == "//:") + // No proxy. + return; + // We don't need to parse the port number out; GetProxyFromGConf() + // would only append it right back again. So we just leave the port + // number right in the host string. + string_table_[prefix + "host"] = value; + } + + void AddKDESetting(std::string key, const char* value) { + // The astute reader may notice that there is no mention of SOCKS + // here. That's because KDE handles socks is a strange way, and we + // don't support it. Rather than just a setting for the SOCKS server, + // it has a setting for a library to LD_PRELOAD in all your programs + // that will transparently SOCKSify them. Such libraries each have + // their own configuration, and thus, we can't get it from KDE. + if (key == "ProxyType") { + const char* mode = "none"; + indirect_manual_ = false; + auto_no_pac_ = false; + switch (StringToInt(value)) { + case 0: // No proxy, or maybe kioslaverc syntax error. + break; + case 1: // Manual configuration. + mode = "manual"; + break; + case 2: // PAC URL. + mode = "auto"; + break; + case 3: // WPAD. + mode = "auto"; + auto_no_pac_ = true; + break; + case 4: // Indirect manual via environment variables. + mode = "manual"; + indirect_manual_ = true; + break; + } + string_table_["/system/proxy/mode"] = mode; + } else if (key == "Proxy Config Script") { + string_table_["/system/proxy/autoconfig_url"] = value; + } else if (key == "httpProxy") { + AddProxy("/system/http_proxy/", value); + } else if (key == "httpsProxy") { + AddProxy("/system/proxy/secure_", value); + } else if (key == "ftpProxy") { + AddProxy("/system/proxy/ftp_", value); + } else if (key == "ReversedException") { + // We count "true" or any nonzero number as true, otherwise false. + // Note that if the value is not actually numeric StringToInt() + // will return 0, which we count as false. + reversed_exception_ = !strcmp(value, "true") || StringToInt(value); + } else if (key == "NoProxyFor") { + std::vector<std::string> exceptions; + StringTokenizer tk(value, ","); + while (tk.GetNext()) { + std::string token = tk.token(); + if (!token.empty()) + exceptions.push_back(token); + } + strings_table_["/system/http_proxy/ignore_hosts"] = exceptions; + } else if (key == "AuthMode") { + // Check for authentication, just so we can warn. + int mode = StringToInt(value); + if (mode) { + // ProxyConfig does not support authentication parameters, but + // Chrome will prompt for the password later. So we ignore this. + LOG(WARNING) << + "Proxy authentication parameters ignored, see bug 16709"; + } + } + } + + void ResolveIndirect(std::string key) { + // We can't save the environment variable getter that was passed + // when this object was constructed, but this setting is likely + // to be pretty unusual and the actual values it would return can + // be tested without using it. So we just use getenv() here. + string_map_type::iterator it = string_table_.find(key); + if (it != string_table_.end()) { + char* value = getenv(it->second.c_str()); + if (value) + it->second = value; + } + } + + // The settings in kioslaverc could occur in any order, but some affect + // others. Rather than read the whole file in and then query them in an + // order that allows us to handle that, we read the settings in whatever + // order they occur and do any necessary tweaking after we finish. + void ResolveModeEffects() { + if (indirect_manual_) { + ResolveIndirect("/system/http_proxy/host"); + ResolveIndirect("/system/proxy/secure_host"); + ResolveIndirect("/system/proxy/ftp_host"); + } + if (auto_no_pac_) { + // Remove the PAC URL; we're not supposed to use it. + string_table_.erase("/system/proxy/autoconfig_url"); + } + if (reversed_exception_) { + // We don't actually support this setting. (It means to use the proxy + // *only* for the exception list, rather than everything but them.) + // Nevertheless we can do better than *exactly the opposite* of the + // desired behavior by clearing the exception list and warning. + strings_table_.erase("/system/http_proxy/ignore_hosts"); + LOG(WARNING) << "KDE reversed proxy exception list not supported"; + } + } + + // Reads kioslaverc one line at a time and calls AddKDESetting() to add + // each relevant name-value pair to the appropriate value table. + void UpdateCachedSettings() { + FilePath kioslaverc = kde_config_dir_.Append( + FILE_PATH_LITERAL("kioslaverc")); + file_util::ScopedFILE input(file_util::OpenFile(kioslaverc, "r")); + if (!input.get()) + return; + ResetCachedSettings(); + bool in_proxy_settings = false; + bool line_too_long = false; + char line[512]; + // feof() and ferror() only tell you if you have hit EOF or an error + // after you try to read some data that encounters that condition. So, + // the initialize statement of this loop is the same as the update + // statement. We need to start out each iteration with fgets(). + while (fgets(line, sizeof(line), input.get())) { + // fgets() guarantees the line will be properly terminated. + size_t length = strlen(line); + if (!length) + continue; + // This should be true even with CRLF endings. + if (line[length - 1] != '\n') { + line_too_long = true; + continue; + } + if (line_too_long) { + // The previous line had no line ending, but this done does. This is + // the end of the line that was too long, so warn here and skip it. + LOG(WARNING) << "skipped very long line in " << kioslaverc.value(); + line_too_long = false; + continue; + } + // Remove the LF at the end, and the CR if there is one. + line[--length] = '\0'; + if (length && line[length - 1] == '\r') + line[--length] = '\0'; + // Now parse the line. + if (line[0] == '[') { + // Switching sections. All we care about is whether this is + // the (a?) proxy settings section, for both KDE3 and KDE4. + in_proxy_settings = !strncmp(line, "[Proxy Settings]", 16); + } else if (in_proxy_settings) { + // A regular line, in the (a?) proxy settings section. + char* value = strchr(line, '='); + if (!value) + continue; + // The length of the value name. + length = value - line; + if (!length) + continue; + // Is the value name localized? + if (line[length - 1] == ']') { + // Find the matching '['. + for (; length && line[length - 1] != '['; --length); + if (!length) + continue; + // Trim the localization indicator off. + line[length - 1] = '\0'; + } + // Split the string on the = sign, and advance |value| to the value. + *(value++) = '\0'; + // Now fill in the tables. + AddKDESetting(line, value); + } + } + if (ferror(input.get())) + LOG(ERROR) << "error reading " << kioslaverc.value(); + ResolveModeEffects(); + } + + // This is the callback from the debounce timer. + void OnDebouncedNotification() { + DCHECK(MessageLoop::current() == file_loop_); + LOG(INFO) << "inotify change notification for kioslaverc"; + UpdateCachedSettings(); + DCHECK(notify_delegate_); + // Forward to a method on the proxy config service delegate object. + notify_delegate_->OnCheckProxyConfigSettings(); + } + + // Called by OnFileCanReadWithoutBlocking() on the file thread. Reads + // from the inotify file descriptor and starts up a debounce timer if + // an event for kioslaverc is seen. + void OnChangeNotification() { + DCHECK(inotify_fd_ >= 0); + DCHECK(MessageLoop::current() == file_loop_); + char event_buf[(sizeof(inotify_event) + NAME_MAX + 1) * 4]; + bool kioslaverc_touched = false; + ssize_t r; + while ((r = read(inotify_fd_, event_buf, sizeof(event_buf))) > 0) { + // inotify returns variable-length structures, which is why we have + // this strange-looking loop instead of iterating through an array. + char* event_ptr = event_buf; + while (event_ptr < event_buf + r) { + inotify_event* event = reinterpret_cast<inotify_event*>(event_ptr); + // The kernel always feeds us whole events. + CHECK(event_ptr + sizeof(inotify_event) <= event_buf + r); + CHECK(event->name + event->len <= event_buf + r); + if (!strcmp(event->name, "kioslaverc")) + kioslaverc_touched = true; + // Advance the pointer just past the end of the filename. + event_ptr = event->name + event->len; + } + // We keep reading even if |kioslaverc_touched| is true to drain the + // inotify event queue. + } + if (!r) + // Instead of returning -1 and setting errno to EINVAL if there is not + // enough buffer space, older kernels (< 2.6.21) return 0. Simulate the + // new behavior (EINVAL) so we can reuse the code below. + errno = EINVAL; + if (errno != EAGAIN) { + LOG(WARNING) << "error reading inotify file descriptor: " + << strerror(errno); + if (errno == EINVAL) { + // Our buffer is not large enough to read the next event. This should + // not happen (because its size is calculated to always be sufficiently + // large), but if it does we'd warn continuously since |inotify_fd_| + // would be forever ready to read. Close it and stop watching instead. + LOG(ERROR) << "inotify failure; no longer watching kioslaverc!"; + inotify_watcher_.StopWatchingFileDescriptor(); + close(inotify_fd_); + inotify_fd_ = -1; + } + } + if (kioslaverc_touched) { + // We don't use Reset() because the timer may not yet be running. + // (In that case Stop() is a no-op.) + debounce_timer_.Stop(); + debounce_timer_.Start(base::TimeDelta::FromMilliseconds( + kDebounceTimeoutMilliseconds), this, + &GConfSettingGetterImplKDE::OnDebouncedNotification); + } + } + + typedef std::map<std::string, std::string> string_map_type; + typedef std::map<std::string, std::vector<std::string> > strings_map_type; + + int inotify_fd_; + base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_; + ProxyConfigServiceLinux::Delegate* notify_delegate_; + base::OneShotTimer<GConfSettingGetterImplKDE> debounce_timer_; + FilePath kde_config_dir_; + bool indirect_manual_; + bool auto_no_pac_; + bool reversed_exception_; + + // We cache these settings whenever we re-read the kioslaverc file. + string_map_type string_table_; + strings_map_type strings_table_; + + // Message loop of the file thread, for reading kioslaverc. If NULL, + // just read it directly (for testing). We also handle inotify events + // on this thread. + MessageLoopForIO* file_loop_; + + DISALLOW_COPY_AND_ASSIGN(GConfSettingGetterImplKDE); }; } // namespace @@ -367,7 +815,7 @@ bool ProxyConfigServiceLinux::Delegate::GetProxyFromGConf( return false; } // Check for an optional port. - int port; + int port = 0; gconf_getter_->GetInt((key + "port").c_str(), &port); if (port != 0) { // If a port is set and non-zero: @@ -478,7 +926,7 @@ bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf( } // Check for authentication, just so we can warn. - bool use_auth; + bool use_auth = false; gconf_getter_->GetBoolean("/system/http_proxy/use_authentication", &use_auth); if (use_auth) { @@ -498,6 +946,24 @@ bool ProxyConfigServiceLinux::Delegate::GetConfigFromGConf( } ProxyConfigServiceLinux::Delegate::Delegate( + base::EnvironmentVariableGetter* env_var_getter) + : env_var_getter_(env_var_getter), + glib_default_loop_(NULL), io_loop_(NULL) { + // Figure out which GConfSettingGetterImpl to use, if any. + switch (base::GetDesktopEnvironment(env_var_getter)) { + case base::DESKTOP_ENVIRONMENT_GNOME: + gconf_getter_.reset(new GConfSettingGetterImplGConf()); + break; + case base::DESKTOP_ENVIRONMENT_KDE3: + case base::DESKTOP_ENVIRONMENT_KDE4: + gconf_getter_.reset(new GConfSettingGetterImplKDE(env_var_getter)); + break; + case base::DESKTOP_ENVIRONMENT_OTHER: + break; + } +} + +ProxyConfigServiceLinux::Delegate::Delegate( base::EnvironmentVariableGetter* env_var_getter, GConfSettingGetter* gconf_getter) : env_var_getter_(env_var_getter), gconf_getter_(gconf_getter), @@ -505,18 +971,19 @@ ProxyConfigServiceLinux::Delegate::Delegate( } void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig( - MessageLoop* glib_default_loop, MessageLoop* io_loop) { + MessageLoop* glib_default_loop, MessageLoop* io_loop, + MessageLoopForIO* file_loop) { // We should be running on the default glib main loop thread right // now. gconf can only be accessed from this thread. DCHECK(MessageLoop::current() == glib_default_loop); glib_default_loop_ = glib_default_loop; io_loop_ = io_loop; - // If we are passed a NULL io_loop, then we don't set up gconf - // notifications. This should not be the usual case but is intended - // to simplify test setups. - if (!io_loop_) - LOG(INFO) << "Monitoring of gconf setting changes is disabled"; + // If we are passed a NULL io_loop or file_loop, then we don't set up + // proxy setting change notifications. This should not be the usual + // case but is intended to simplify test setups. + if (!io_loop_ || !file_loop) + LOG(INFO) << "Monitoring of proxy setting changes is disabled"; // Fetch and cache the current proxy config. The config is left in // cached_config_, where GetProxyConfig() running on the IO thread @@ -531,36 +998,27 @@ void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig( // mislead us. bool got_config = false; - switch (base::GetDesktopEnvironment(env_var_getter_.get())) { - case base::DESKTOP_ENVIRONMENT_GNOME: - if (gconf_getter_->Init() && - (!io_loop || gconf_getter_->SetupNotification(this))) { - if (GetConfigFromGConf(&cached_config_)) { - cached_config_.set_id(1); // mark it as valid - got_config = true; - LOG(INFO) << "Obtained proxy setting from gconf"; - // If gconf proxy mode is "none", meaning direct, then we take - // that to be a valid config and will not check environment - // variables. The alternative would have been to look for a proxy - // where ever we can find one. - // - // Keep a copy of the config for use from this thread for - // comparison with updated settings when we get notifications. - reference_config_ = cached_config_; - reference_config_.set_id(1); // mark it as valid - } else { - gconf_getter_->Release(); // Stop notifications - } + if (gconf_getter_.get()) { + if (gconf_getter_->Init(glib_default_loop, file_loop) && + (!io_loop || !file_loop || gconf_getter_->SetupNotification(this))) { + if (GetConfigFromGConf(&cached_config_)) { + cached_config_.set_id(1); // mark it as valid + got_config = true; + LOG(INFO) << "Obtained proxy settings from " << + gconf_getter_->GetDataSource(); + // If gconf proxy mode is "none", meaning direct, then we take + // that to be a valid config and will not check environment + // variables. The alternative would have been to look for a proxy + // whereever we can find one. + // + // Keep a copy of the config for use from this thread for + // comparison with updated settings when we get notifications. + reference_config_ = cached_config_; + reference_config_.set_id(1); // mark it as valid + } else { + gconf_getter_->Shutdown(); // Stop notifications } - break; - - case base::DESKTOP_ENVIRONMENT_KDE3: - case base::DESKTOP_ENVIRONMENT_KDE4: - NOTIMPLEMENTED() << "Bug 17363: obey KDE proxy settings."; - break; - - case base::DESKTOP_ENVIRONMENT_OTHER: - break; + } } if (!got_config) { @@ -571,14 +1029,14 @@ void ProxyConfigServiceLinux::Delegate::SetupAndFetchInitialConfig( // work. if (GetConfigFromEnv(&cached_config_)) { cached_config_.set_id(1); // mark it as valid - LOG(INFO) << "Obtained proxy setting from environment variables"; + LOG(INFO) << "Obtained proxy settings from environment variables"; } } } void ProxyConfigServiceLinux::Delegate::Reset() { DCHECK(!glib_default_loop_ || MessageLoop::current() == glib_default_loop_); - gconf_getter_->Release(); + gconf_getter_->Shutdown(); cached_config_ = ProxyConfig(); } @@ -592,11 +1050,11 @@ int ProxyConfigServiceLinux::Delegate::GetProxyConfig(ProxyConfig* config) { return cached_config_.is_valid() ? OK : ERR_FAILED; } +// Depending on the GConfSettingGetter in use, this method will be called +// on either the UI thread (GConf) or the file thread (KDE). void ProxyConfigServiceLinux::Delegate::OnCheckProxyConfigSettings() { - // This should be dispatched from the thread with the default glib - // main loop, which allows us to access gconf. - DCHECK(MessageLoop::current() == glib_default_loop_); - + MessageLoop* required_loop = gconf_getter_->GetNotificationLoop(); + DCHECK(!required_loop || MessageLoop::current() == required_loop); ProxyConfig new_config; bool valid = GetConfigFromGConf(&new_config); if (valid) @@ -626,15 +1084,17 @@ void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig( } void ProxyConfigServiceLinux::Delegate::PostDestroyTask() { - if (MessageLoop::current() == glib_default_loop_) { + if (!gconf_getter_.get()) + return; + MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop(); + if (!shutdown_loop || MessageLoop::current() == shutdown_loop) { // Already on the right thread, call directly. // This is the case for the unittests. OnDestroy(); } else { - // Post to UI thread. Note that on browser shutdown, we may quit - // the UI MessageLoop and exit the program before ever running - // this. - glib_default_loop_->PostTask( + // Post to shutdown thread. Note that on browser shutdown, we may quit + // this MessageLoop and exit the program before ever running this. + shutdown_loop->PostTask( FROM_HERE, NewRunnableMethod( this, @@ -642,13 +1102,13 @@ void ProxyConfigServiceLinux::Delegate::PostDestroyTask() { } } void ProxyConfigServiceLinux::Delegate::OnDestroy() { - DCHECK(!glib_default_loop_ || MessageLoop::current() == glib_default_loop_); - gconf_getter_->Release(); + MessageLoop* shutdown_loop = gconf_getter_->GetNotificationLoop(); + DCHECK(!shutdown_loop || MessageLoop::current() == shutdown_loop); + gconf_getter_->Shutdown(); } ProxyConfigServiceLinux::ProxyConfigServiceLinux() - : delegate_(new Delegate(base::EnvironmentVariableGetter::Create(), - new GConfSettingGetterImpl())) { + : delegate_(new Delegate(base::EnvironmentVariableGetter::Create())) { } ProxyConfigServiceLinux::ProxyConfigServiceLinux( |