Fix nullptr crash in OnEmbed
[chromium-blink-merge.git] / net / proxy / proxy_config_service_win.cc
blob5ec2b0c261f24cfa4e3ca35dc7de41d1b6e65531
1 // Copyright (c) 2012 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 "net/proxy/proxy_config_service_win.h"
7 #include <windows.h>
8 #include <winhttp.h>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_tokenizer.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/win/registry.h"
20 #include "base/win/scoped_handle.h"
21 #include "net/base/net_errors.h"
22 #include "net/proxy/proxy_config.h"
24 #pragma comment(lib, "winhttp.lib")
26 namespace net {
28 namespace {
30 const int kPollIntervalSec = 10;
32 void FreeIEConfig(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG* ie_config) {
33 if (ie_config->lpszAutoConfigUrl)
34 GlobalFree(ie_config->lpszAutoConfigUrl);
35 if (ie_config->lpszProxy)
36 GlobalFree(ie_config->lpszProxy);
37 if (ie_config->lpszProxyBypass)
38 GlobalFree(ie_config->lpszProxyBypass);
41 } // namespace
43 ProxyConfigServiceWin::ProxyConfigServiceWin()
44 : PollingProxyConfigService(
45 base::TimeDelta::FromSeconds(kPollIntervalSec),
46 &ProxyConfigServiceWin::GetCurrentProxyConfig) {
49 ProxyConfigServiceWin::~ProxyConfigServiceWin() {
50 // The registry functions below will end up going to disk. Do this on another
51 // thread to avoid slowing the IO thread. http://crbug.com/61453
52 base::ThreadRestrictions::ScopedAllowIO allow_io;
53 STLDeleteElements(&keys_to_watch_);
56 void ProxyConfigServiceWin::AddObserver(Observer* observer) {
57 // Lazily-initialize our registry watcher.
58 StartWatchingRegistryForChanges();
60 // Let the super-class do its work now.
61 PollingProxyConfigService::AddObserver(observer);
64 void ProxyConfigServiceWin::StartWatchingRegistryForChanges() {
65 if (!keys_to_watch_.empty())
66 return; // Already initialized.
68 // The registry functions below will end up going to disk. Do this on another
69 // thread to avoid slowing the IO thread. http://crbug.com/61453
70 base::ThreadRestrictions::ScopedAllowIO allow_io;
72 // There are a number of different places where proxy settings can live
73 // in the registry. In some cases it appears in a binary value, in other
74 // cases string values. Furthermore winhttp and wininet appear to have
75 // separate stores, and proxy settings can be configured per-machine
76 // or per-user.
78 // This function is probably not exhaustive in the registry locations it
79 // watches for changes, however it should catch the majority of the
80 // cases. In case we have missed some less common triggers (likely), we
81 // will catch them during the periodic (10 second) polling, so things
82 // will recover.
84 AddKeyToWatchList(
85 HKEY_CURRENT_USER,
86 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
88 AddKeyToWatchList(
89 HKEY_LOCAL_MACHINE,
90 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
92 AddKeyToWatchList(
93 HKEY_LOCAL_MACHINE,
94 L"SOFTWARE\\Policies\\Microsoft\\Windows\\CurrentVersion\\"
95 L"Internet Settings");
98 bool ProxyConfigServiceWin::AddKeyToWatchList(HKEY rootkey,
99 const wchar_t* subkey) {
100 scoped_ptr<base::win::RegKey> key(new base::win::RegKey);
101 if (key->Create(rootkey, subkey, KEY_NOTIFY) != ERROR_SUCCESS)
102 return false;
104 if (!key->StartWatching(base::Bind(&ProxyConfigServiceWin::OnObjectSignaled,
105 base::Unretained(this),
106 base::Unretained(key.get())))) {
107 return false;
110 keys_to_watch_.push_back(key.release());
111 return true;
114 void ProxyConfigServiceWin::OnObjectSignaled(base::win::RegKey* key) {
115 // Figure out which registry key signalled this change.
116 RegKeyList::iterator it =
117 std::find(keys_to_watch_.begin(), keys_to_watch_.end(), key);
118 DCHECK(it != keys_to_watch_.end());
120 // Keep watching the registry key.
121 if (!key->StartWatching(base::Bind(&ProxyConfigServiceWin::OnObjectSignaled,
122 base::Unretained(this),
123 base::Unretained(key)))) {
124 delete *it;
125 keys_to_watch_.erase(it);
128 // Have the PollingProxyConfigService test for changes.
129 CheckForChangesNow();
132 // static
133 void ProxyConfigServiceWin::GetCurrentProxyConfig(ProxyConfig* config) {
134 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_config = {0};
135 if (!WinHttpGetIEProxyConfigForCurrentUser(&ie_config)) {
136 LOG(ERROR) << "WinHttpGetIEProxyConfigForCurrentUser failed: " <<
137 GetLastError();
138 *config = ProxyConfig::CreateDirect();
139 config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED);
140 return;
142 SetFromIEConfig(config, ie_config);
143 FreeIEConfig(&ie_config);
146 // static
147 void ProxyConfigServiceWin::SetFromIEConfig(
148 ProxyConfig* config,
149 const WINHTTP_CURRENT_USER_IE_PROXY_CONFIG& ie_config) {
150 if (ie_config.fAutoDetect)
151 config->set_auto_detect(true);
152 if (ie_config.lpszProxy) {
153 // lpszProxy may be a single proxy, or a proxy per scheme. The format
154 // is compatible with ProxyConfig::ProxyRules's string format.
155 config->proxy_rules().ParseFromString(
156 base::UTF16ToASCII(ie_config.lpszProxy));
158 if (ie_config.lpszProxyBypass) {
159 std::string proxy_bypass = base::UTF16ToASCII(ie_config.lpszProxyBypass);
161 base::StringTokenizer proxy_server_bypass_list(proxy_bypass, ";, \t\n\r");
162 while (proxy_server_bypass_list.GetNext()) {
163 std::string bypass_url_domain = proxy_server_bypass_list.token();
164 config->proxy_rules().bypass_rules.AddRuleFromString(bypass_url_domain);
167 if (ie_config.lpszAutoConfigUrl)
168 config->set_pac_url(GURL(ie_config.lpszAutoConfigUrl));
169 config->set_source(PROXY_CONFIG_SOURCE_SYSTEM);
172 } // namespace net