1 // Copyright (c) 2010 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 "base/string_number_conversions.h"
6 #include "chrome/common/automation_constants.h"
7 #include "chrome/common/json_value_serializer.h"
8 #include "chrome_frame/np_proxy_service.h"
9 #include "chrome_frame/np_browser_functions.h"
11 #include "net/proxy/proxy_config.h"
13 #include "third_party/xulrunner-sdk/win/include/xpcom/nsXPCOM.h"
14 #include "third_party/xulrunner-sdk/win/include/xpcom/nsIObserverService.h"
15 #include "third_party/xulrunner-sdk/win/include/xpcom/nsISupportsUtils.h"
16 #include "third_party/xulrunner-sdk/win/include/xpcom/nsStringAPI.h"
18 ASSOCIATE_IID(NS_IOBSERVERSERVICE_IID_STR
, nsIObserverService
);
19 ASSOCIATE_IID(NS_IPREFBRANCH_IID_STR
, nsIPrefBranch
);
21 // Firefox preference names.
22 const char* kProxyObserverRoot
= "network.";
23 const char* kProxyObserverBranch
= "proxy.";
24 const char* kProxyType
= "proxy.type";
25 const char* kProxyAutoconfigUrl
= "proxy.autoconfig_url";
26 const char* kProxyBypassList
= "proxy.no_proxies_on";
28 const int kInvalidIntPref
= -1;
30 // These are the proxy schemes that Chrome knows about at the moment.
31 // SOCKS is a notable ommission here, this will need to be updated when
32 // Chrome supports SOCKS proxies.
33 const NpProxyService::ProxyNames
NpProxyService::kProxyInfo
[] = {
34 {"http", "proxy.http", "proxy.http_port"},
35 {"https", "proxy.ssl", "proxy.ssl_port"},
36 {"ftp", "proxy.ftp", "proxy.ftp_port"} };
38 NpProxyService::NpProxyService(void)
39 : type_(PROXY_CONFIG_LAST
), auto_detect_(false), no_proxy_(false),
40 system_config_(false), automation_client_(NULL
) {
43 NpProxyService::~NpProxyService(void) {
46 bool NpProxyService::Initialize(NPP instance
,
47 ChromeFrameAutomationClient
* automation_client
) {
48 DCHECK(automation_client
);
49 automation_client_
= automation_client
;
51 // Get the pref service
53 ScopedNsPtr
<nsISupports
> service_manager_base
;
54 npapi::GetValue(instance
, NPNVserviceManager
, service_manager_base
.Receive());
55 if (service_manager_base
!= NULL
) {
56 service_manager_
.QueryFrom(service_manager_base
);
57 if (service_manager_
.get() == NULL
) {
58 DLOG(ERROR
) << "Failed to create ServiceManager. This only works in FF.";
60 service_manager_
->GetServiceByContractID(
61 NS_PREFSERVICE_CONTRACTID
, NS_GET_IID(nsIPrefService
),
62 reinterpret_cast<void**>(pref_service_
.Receive()));
64 DLOG(ERROR
) << "Failed to create PreferencesService";
66 result
= InitializePrefBranch(pref_service_
);
73 bool NpProxyService::InitializePrefBranch(nsIPrefService
* pref_service
) {
75 // Note that we cannot persist a reference to the pref branch because we
76 // also act as an observer of changes to the branch. As per
77 // nsIPrefBranch2.h, this would result in a circular reference between us
78 // and the pref branch, which can impede cleanup. There are workarounds,
79 // but let's try just not caching the branch reference for now.
81 ScopedNsPtr
<nsIPrefBranch
> pref_branch
;
83 pref_service
->GetBranch(kProxyObserverRoot
, pref_branch
.Receive());
86 DLOG(ERROR
) << "Failed to get nsIPrefBranch";
88 if (!ReadProxySettings(pref_branch
.get())) {
89 DLOG(ERROR
) << "Could not read proxy settings.";
91 observer_pref_branch_
.QueryFrom(pref_branch
);
92 if (!observer_pref_branch_
) {
93 DLOG(ERROR
) << "Failed to get observer nsIPrefBranch2";
95 nsresult res
= observer_pref_branch_
->AddObserver(kProxyObserverBranch
,
97 result
= NS_SUCCEEDED(res
);
104 bool NpProxyService::UnInitialize() {
105 // Fail early if this was never created - we may not be running on FF.
109 // Unhook ourselves as an observer.
110 nsresult res
= NS_ERROR_FAILURE
;
111 if (observer_pref_branch_
)
112 res
= observer_pref_branch_
->RemoveObserver(kProxyObserverBranch
, this);
114 return NS_SUCCEEDED(res
);
117 NS_IMETHODIMP
NpProxyService::Observe(nsISupports
* subject
, const char* topic
,
118 const PRUnichar
* data
) {
119 if (!subject
|| !topic
) {
121 return NS_ERROR_UNEXPECTED
;
124 std::string
topic_str(topic
);
125 nsresult res
= NS_OK
;
126 if (topic_str
== NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) {
127 // Looks like our proxy settings changed. We need to reload!
128 // I have observed some extremely strange behaviour here. Specifically,
129 // we are supposed to be able to QI |subject| and get from it an
130 // nsIPrefBranch from which we can query new values. This has erratic
131 // behaviour, specifically subject starts returning null on all member
132 // queries. So I am using the cached nsIPrefBranch2 (that we used to add
133 // the observer) to do the querying.
134 if (NS_SUCCEEDED(res
)) {
135 if (!ReadProxySettings(observer_pref_branch_
)) {
136 res
= NS_ERROR_UNEXPECTED
;
138 std::string proxy_settings
;
139 if (GetProxyValueJSONString(&proxy_settings
))
140 automation_client_
->SetProxySettings(proxy_settings
);
150 std::string
NpProxyService::GetStringPref(nsIPrefBranch
* pref_branch
,
151 const char* pref_name
) {
152 nsCString pref_string
;
154 nsresult rv
= pref_branch
->GetCharPref(pref_name
, getter_Copies(pref_string
));
155 if (SUCCEEDED(rv
) && pref_string
.get()) {
156 result
= pref_string
.get();
161 int NpProxyService::GetIntPref(nsIPrefBranch
* pref_branch
,
162 const char* pref_name
) {
164 int result
= kInvalidIntPref
;
165 nsresult rv
= pref_branch
->GetIntPref(pref_name
, &pref_int
);
172 bool NpProxyService::GetBoolPref(nsIPrefBranch
* pref_branch
,
173 const char* pref_name
) {
176 nsresult rv
= pref_branch
->GetBoolPref(pref_name
, &pref_bool
);
178 result
= pref_bool
== PR_TRUE
;
183 void NpProxyService::Reset() {
184 type_
= PROXY_CONFIG_LAST
;
185 auto_detect_
= false;
187 system_config_
= false;
188 manual_proxies_
.clear();
190 proxy_bypass_list_
.clear();
193 bool NpProxyService::ReadProxySettings(nsIPrefBranch
* pref_branch
) {
196 // Clear our current settings.
198 type_
= GetIntPref(pref_branch
, kProxyType
);
199 if (type_
== kInvalidIntPref
) {
205 case PROXY_CONFIG_DIRECT
:
206 case PROXY_CONFIG_DIRECT4X
:
209 case PROXY_CONFIG_SYSTEM
:
210 // _SYSTEM is documented as "Use system settings if available, otherwise
211 // DIRECT". It isn't clear under what circumstances system settings would
212 // be unavailable, but I'll special-case this nonetheless and have
213 // GetProxyValueJSONString() return empty if we get this proxy type.
214 DLOG(WARNING
) << "Received PROXY_CONFIG_SYSTEM proxy type.";
215 system_config_
= true;
217 case PROXY_CONFIG_WPAD
:
220 case PROXY_CONFIG_PAC
:
221 pac_url_
= GetStringPref(pref_branch
, kProxyAutoconfigUrl
);
223 case PROXY_CONFIG_MANUAL
:
224 // Read in the values for each of the known schemes.
225 for (int i
= 0; i
< arraysize(kProxyInfo
); i
++) {
226 ManualProxyEntry entry
;
227 entry
.url
= GetStringPref(pref_branch
, kProxyInfo
[i
].pref_name
);
228 entry
.port
= GetIntPref(pref_branch
, kProxyInfo
[i
].port_pref_name
);
229 if (!entry
.url
.empty() && entry
.port
!= kInvalidIntPref
) {
230 entry
.scheme
= kProxyInfo
[i
].chrome_scheme
;
231 manual_proxies_
.push_back(entry
);
235 // Also pick up the list of URLs we bypass proxies for.
236 proxy_bypass_list_
= GetStringPref(pref_branch
, kProxyBypassList
);
245 DictionaryValue
* NpProxyService::BuildProxyValueSet() {
246 scoped_ptr
<DictionaryValue
> proxy_settings_value(new DictionaryValue
);
249 proxy_settings_value
->SetBoolean(automation::kJSONProxyAutoconfig
,
254 proxy_settings_value
->SetBoolean(automation::kJSONProxyNoProxy
, no_proxy_
);
257 if (!pac_url_
.empty()) {
258 proxy_settings_value
->SetString(automation::kJSONProxyPacUrl
, pac_url_
);
261 if (!proxy_bypass_list_
.empty()) {
262 proxy_settings_value
->SetString(automation::kJSONProxyBypassList
,
266 // Fill in the manual proxy settings. Build a string representation that
267 // corresponds to the format of the input parameter to
268 // ProxyConfig::ProxyRules::ParseFromString.
269 std::string manual_proxy_settings
;
270 ManualProxyList::const_iterator
iter(manual_proxies_
.begin());
271 for (; iter
!= manual_proxies_
.end(); iter
++) {
272 DCHECK(!iter
->scheme
.empty());
273 DCHECK(!iter
->url
.empty());
274 DCHECK(iter
->port
!= kInvalidIntPref
);
275 manual_proxy_settings
+= iter
->scheme
;
276 manual_proxy_settings
+= "=";
277 manual_proxy_settings
+= iter
->url
;
278 manual_proxy_settings
+= ":";
279 manual_proxy_settings
+= base::IntToString(iter
->port
);
280 manual_proxy_settings
+= ";";
283 if (!manual_proxy_settings
.empty()) {
284 proxy_settings_value
->SetString(automation::kJSONProxyServer
,
285 manual_proxy_settings
);
288 return proxy_settings_value
.release();
291 bool NpProxyService::GetProxyValueJSONString(std::string
* output
) {
295 // If we detected a PROXY_CONFIG_SYSTEM config type or failed to obtain the
296 // pref service then return false here to make Chrome continue using its
297 // default proxy settings.
298 if (system_config_
|| !pref_service_
)
301 scoped_ptr
<DictionaryValue
> proxy_settings_value(BuildProxyValueSet());
303 JSONStringValueSerializer
serializer(output
);
304 return serializer
.Serialize(*static_cast<Value
*>(proxy_settings_value
.get()));