OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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. |
| 4 |
| 5 #include "base/string_util.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" |
| 10 |
| 11 #include "net/proxy/proxy_config.h" |
| 12 |
| 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" |
| 17 |
| 18 ASSOCIATE_IID(NS_IOBSERVERSERVICE_IID_STR, nsIObserverService); |
| 19 ASSOCIATE_IID(NS_IPREFBRANCH_IID_STR, nsIPrefBranch); |
| 20 |
| 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"; |
| 27 |
| 28 const int kInvalidIntPref = -1; |
| 29 |
| 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"} }; |
| 37 |
| 38 NpProxyService::NpProxyService(void) |
| 39 : type_(PROXY_CONFIG_LAST), auto_detect_(false), no_proxy_(false), |
| 40 system_config_(false), automation_client_(NULL) { |
| 41 } |
| 42 |
| 43 NpProxyService::~NpProxyService(void) { |
| 44 } |
| 45 |
| 46 bool NpProxyService::Initialize(NPP instance, |
| 47 ChromeFrameAutomationClient* automation_client) { |
| 48 DCHECK(automation_client); |
| 49 automation_client_ = automation_client; |
| 50 |
| 51 // Get the pref service |
| 52 bool result = false; |
| 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."; |
| 59 } else { |
| 60 service_manager_->GetServiceByContractID( |
| 61 NS_PREFSERVICE_CONTRACTID, NS_GET_IID(nsIPrefService), |
| 62 reinterpret_cast<void**>(pref_service_.Receive())); |
| 63 if (!pref_service_) { |
| 64 DLOG(ERROR) << "Failed to create PreferencesService"; |
| 65 } else { |
| 66 result = InitializePrefBranch(pref_service_); |
| 67 } |
| 68 } |
| 69 } |
| 70 return result; |
| 71 } |
| 72 |
| 73 bool NpProxyService::InitializePrefBranch(nsIPrefService* pref_service) { |
| 74 DCHECK(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. |
| 80 bool result = false; |
| 81 ScopedNsPtr<nsIPrefBranch> pref_branch; |
| 82 |
| 83 pref_service->ReadUserPrefs(nsnull); |
| 84 pref_service->GetBranch(kProxyObserverRoot, pref_branch.Receive()); |
| 85 |
| 86 if (!pref_branch) { |
| 87 DLOG(ERROR) << "Failed to get nsIPrefBranch"; |
| 88 } else { |
| 89 if (!ReadProxySettings(pref_branch.get())) { |
| 90 DLOG(ERROR) << "Could not read proxy settings."; |
| 91 } else { |
| 92 observer_pref_branch_.QueryFrom(pref_branch); |
| 93 if (!observer_pref_branch_) { |
| 94 DLOG(ERROR) << "Failed to get observer nsIPrefBranch2"; |
| 95 } else { |
| 96 nsresult res = observer_pref_branch_->AddObserver(kProxyObserverBranch, |
| 97 this, PR_FALSE); |
| 98 result = NS_SUCCEEDED(res); |
| 99 } |
| 100 } |
| 101 } |
| 102 return result; |
| 103 } |
| 104 |
| 105 bool NpProxyService::UnInitialize() { |
| 106 // Fail early if this was never created - we may not be running on FF. |
| 107 if (!pref_service_) |
| 108 return false; |
| 109 |
| 110 // Unhook ourselves as an observer. |
| 111 nsresult res = NS_ERROR_FAILURE; |
| 112 if (observer_pref_branch_) |
| 113 res = observer_pref_branch_->RemoveObserver(kProxyObserverBranch, this); |
| 114 |
| 115 return NS_SUCCEEDED(res); |
| 116 } |
| 117 |
| 118 NS_IMETHODIMP NpProxyService::Observe(nsISupports* subject, const char* topic, |
| 119 const PRUnichar* data) { |
| 120 if (!subject || !topic) { |
| 121 NOTREACHED(); |
| 122 return NS_ERROR_UNEXPECTED; |
| 123 } |
| 124 |
| 125 std::string topic_str(topic); |
| 126 nsresult res = NS_OK; |
| 127 if (topic_str == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) { |
| 128 // Looks like our proxy settings changed. We need to reload! |
| 129 // I have observed some extremely strange behaviour here. Specifically, |
| 130 // we are supposed to be able to QI |subject| and get from it an |
| 131 // nsIPrefBranch from which we can query new values. This has erratic |
| 132 // behaviour, specifically subject starts returning null on all member |
| 133 // queries. So I am using the cached nsIPrefBranch2 (that we used to add |
| 134 // the observer) to do the querying. |
| 135 if (NS_SUCCEEDED(res)) { |
| 136 if (!ReadProxySettings(observer_pref_branch_)) { |
| 137 res = NS_ERROR_UNEXPECTED; |
| 138 } else { |
| 139 std::string proxy_settings; |
| 140 if (GetProxyValueJSONString(&proxy_settings)) |
| 141 automation_client_->SetProxySettings(proxy_settings); |
| 142 } |
| 143 } |
| 144 } else { |
| 145 NOTREACHED(); |
| 146 } |
| 147 |
| 148 return res; |
| 149 } |
| 150 |
| 151 std::string NpProxyService::GetStringPref(nsIPrefBranch* pref_branch, |
| 152 const char* pref_name) { |
| 153 nsCString pref_string; |
| 154 std::string result; |
| 155 nsresult rv = pref_branch->GetCharPref(pref_name, getter_Copies(pref_string)); |
| 156 if (SUCCEEDED(rv) && pref_string.get()) { |
| 157 result = pref_string.get(); |
| 158 } |
| 159 return result; |
| 160 } |
| 161 |
| 162 int NpProxyService::GetIntPref(nsIPrefBranch* pref_branch, |
| 163 const char* pref_name) { |
| 164 PRInt32 pref_int; |
| 165 int result = kInvalidIntPref; |
| 166 nsresult rv = pref_branch->GetIntPref(pref_name, &pref_int); |
| 167 if (SUCCEEDED(rv)) { |
| 168 result = pref_int; |
| 169 } |
| 170 return result; |
| 171 } |
| 172 |
| 173 bool NpProxyService::GetBoolPref(nsIPrefBranch* pref_branch, |
| 174 const char* pref_name) { |
| 175 PRBool pref_bool; |
| 176 bool result = false; |
| 177 nsresult rv = pref_branch->GetBoolPref(pref_name, &pref_bool); |
| 178 if (SUCCEEDED(rv)) { |
| 179 result = pref_bool == PR_TRUE; |
| 180 } |
| 181 return result; |
| 182 } |
| 183 |
| 184 void NpProxyService::Reset() { |
| 185 type_ = PROXY_CONFIG_LAST; |
| 186 auto_detect_ = false; |
| 187 no_proxy_ = false; |
| 188 system_config_ = false; |
| 189 manual_proxies_.clear(); |
| 190 pac_url_.clear(); |
| 191 proxy_bypass_list_.clear(); |
| 192 } |
| 193 |
| 194 bool NpProxyService::ReadProxySettings(nsIPrefBranch* pref_branch) { |
| 195 DCHECK(pref_branch); |
| 196 |
| 197 // Clear our current settings. |
| 198 Reset(); |
| 199 type_ = GetIntPref(pref_branch, kProxyType); |
| 200 if (type_ == kInvalidIntPref) { |
| 201 NOTREACHED(); |
| 202 return false; |
| 203 } |
| 204 |
| 205 switch (type_) { |
| 206 case PROXY_CONFIG_DIRECT: |
| 207 case PROXY_CONFIG_DIRECT4X: |
| 208 no_proxy_ = true; |
| 209 break; |
| 210 case PROXY_CONFIG_SYSTEM: |
| 211 // _SYSTEM is documented as "Use system settings if available, otherwise |
| 212 // DIRECT". It isn't clear under what circumstances system settings would |
| 213 // be unavailable, but I'll special-case this nonetheless and have |
| 214 // GetProxyValueJSONString() return empty if we get this proxy type. |
| 215 DLOG(WARNING) << "Received PROXY_CONFIG_SYSTEM proxy type."; |
| 216 system_config_ = true; |
| 217 break; |
| 218 case PROXY_CONFIG_WPAD: |
| 219 auto_detect_ = true; |
| 220 break; |
| 221 case PROXY_CONFIG_PAC: |
| 222 pac_url_ = GetStringPref(pref_branch, kProxyAutoconfigUrl); |
| 223 break; |
| 224 case PROXY_CONFIG_MANUAL: |
| 225 // Read in the values for each of the known schemes. |
| 226 for (int i = 0; i < arraysize(kProxyInfo); i++) { |
| 227 ManualProxyEntry entry; |
| 228 entry.url = GetStringPref(pref_branch, kProxyInfo[i].pref_name); |
| 229 entry.port = GetIntPref(pref_branch, kProxyInfo[i].port_pref_name); |
| 230 if (!entry.url.empty() && entry.port != kInvalidIntPref) { |
| 231 entry.scheme = kProxyInfo[i].chrome_scheme; |
| 232 manual_proxies_.push_back(entry); |
| 233 } |
| 234 } |
| 235 |
| 236 // Also pick up the list of URLs we bypass proxies for. |
| 237 proxy_bypass_list_ = GetStringPref(pref_branch, kProxyBypassList); |
| 238 break; |
| 239 default: |
| 240 NOTREACHED(); |
| 241 return false; |
| 242 } |
| 243 return true; |
| 244 } |
| 245 |
| 246 DictionaryValue* NpProxyService::BuildProxyValueSet() { |
| 247 scoped_ptr<DictionaryValue> proxy_settings_value(new DictionaryValue); |
| 248 |
| 249 if (auto_detect_) { |
| 250 proxy_settings_value->SetBoolean(automation::kJSONProxyAutoconfig, |
| 251 auto_detect_); |
| 252 } |
| 253 |
| 254 if (no_proxy_) { |
| 255 proxy_settings_value->SetBoolean(automation::kJSONProxyNoProxy, no_proxy_); |
| 256 } |
| 257 |
| 258 if (!pac_url_.empty()) { |
| 259 proxy_settings_value->SetString(automation::kJSONProxyPacUrl, pac_url_); |
| 260 } |
| 261 |
| 262 if (!proxy_bypass_list_.empty()) { |
| 263 proxy_settings_value->SetString(automation::kJSONProxyBypassList, |
| 264 proxy_bypass_list_); |
| 265 } |
| 266 |
| 267 // Fill in the manual proxy settings. Build a string representation that |
| 268 // corresponds to the format of the input parameter to |
| 269 // ProxyConfig::ProxyRules::ParseFromString. |
| 270 std::string manual_proxy_settings; |
| 271 ManualProxyList::const_iterator iter(manual_proxies_.begin()); |
| 272 for (; iter != manual_proxies_.end(); iter++) { |
| 273 DCHECK(!iter->scheme.empty()); |
| 274 DCHECK(!iter->url.empty()); |
| 275 DCHECK(iter->port != kInvalidIntPref); |
| 276 manual_proxy_settings += iter->scheme; |
| 277 manual_proxy_settings += "="; |
| 278 manual_proxy_settings += iter->url; |
| 279 manual_proxy_settings += ":"; |
| 280 manual_proxy_settings += IntToString(iter->port); |
| 281 manual_proxy_settings += ";"; |
| 282 } |
| 283 |
| 284 if (!manual_proxy_settings.empty()) { |
| 285 proxy_settings_value->SetString(automation::kJSONProxyServer, |
| 286 manual_proxy_settings); |
| 287 } |
| 288 |
| 289 return proxy_settings_value.release(); |
| 290 } |
| 291 |
| 292 bool NpProxyService::GetProxyValueJSONString(std::string* output) { |
| 293 DCHECK(output); |
| 294 output->empty(); |
| 295 |
| 296 // If we detected a PROXY_CONFIG_SYSTEM config type or failed to obtain the |
| 297 // pref service then return false here to make Chrome continue using its |
| 298 // default proxy settings. |
| 299 if (system_config_ || !pref_service_) |
| 300 return false; |
| 301 |
| 302 scoped_ptr<DictionaryValue> proxy_settings_value(BuildProxyValueSet()); |
| 303 |
| 304 JSONStringValueSerializer serializer(output); |
| 305 return serializer.Serialize(*static_cast<Value*>(proxy_settings_value.get())); |
| 306 } |
OLD | NEW |