| // 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 "base/string_number_conversions.h" |
| #include "chrome/common/automation_constants.h" |
| #include "chrome/common/json_value_serializer.h" |
| #include "chrome_frame/np_proxy_service.h" |
| #include "chrome_frame/np_browser_functions.h" |
| |
| #include "net/proxy/proxy_config.h" |
| |
| #include "third_party/xulrunner-sdk/win/include/xpcom/nsXPCOM.h" |
| #include "third_party/xulrunner-sdk/win/include/xpcom/nsIObserverService.h" |
| #include "third_party/xulrunner-sdk/win/include/xpcom/nsISupportsUtils.h" |
| #include "third_party/xulrunner-sdk/win/include/xpcom/nsStringAPI.h" |
| |
| ASSOCIATE_IID(NS_IOBSERVERSERVICE_IID_STR, nsIObserverService); |
| ASSOCIATE_IID(NS_IPREFBRANCH_IID_STR, nsIPrefBranch); |
| |
| // Firefox preference names. |
| const char* kProxyObserverRoot = "network."; |
| const char* kProxyObserverBranch = "proxy."; |
| const char* kProxyType = "proxy.type"; |
| const char* kProxyAutoconfigUrl = "proxy.autoconfig_url"; |
| const char* kProxyBypassList = "proxy.no_proxies_on"; |
| |
| const int kInvalidIntPref = -1; |
| |
| // These are the proxy schemes that Chrome knows about at the moment. |
| // SOCKS is a notable ommission here, this will need to be updated when |
| // Chrome supports SOCKS proxies. |
| const NpProxyService::ProxyNames NpProxyService::kProxyInfo[] = { |
| {"http", "proxy.http", "proxy.http_port"}, |
| {"https", "proxy.ssl", "proxy.ssl_port"}, |
| {"ftp", "proxy.ftp", "proxy.ftp_port"} }; |
| |
| NpProxyService::NpProxyService(void) |
| : type_(PROXY_CONFIG_LAST), auto_detect_(false), no_proxy_(false), |
| system_config_(false), automation_client_(NULL) { |
| } |
| |
| NpProxyService::~NpProxyService(void) { |
| } |
| |
| bool NpProxyService::Initialize(NPP instance, |
| ChromeFrameAutomationClient* automation_client) { |
| DCHECK(automation_client); |
| automation_client_ = automation_client; |
| |
| // Get the pref service |
| bool result = false; |
| ScopedNsPtr<nsISupports> service_manager_base; |
| npapi::GetValue(instance, NPNVserviceManager, service_manager_base.Receive()); |
| if (service_manager_base != NULL) { |
| service_manager_.QueryFrom(service_manager_base); |
| if (service_manager_.get() == NULL) { |
| DLOG(ERROR) << "Failed to create ServiceManager. This only works in FF."; |
| } else { |
| service_manager_->GetServiceByContractID( |
| NS_PREFSERVICE_CONTRACTID, NS_GET_IID(nsIPrefService), |
| reinterpret_cast<void**>(pref_service_.Receive())); |
| if (!pref_service_) { |
| DLOG(ERROR) << "Failed to create PreferencesService"; |
| } else { |
| result = InitializePrefBranch(pref_service_); |
| } |
| } |
| } |
| return result; |
| } |
| |
| bool NpProxyService::InitializePrefBranch(nsIPrefService* pref_service) { |
| DCHECK(pref_service); |
| // Note that we cannot persist a reference to the pref branch because we |
| // also act as an observer of changes to the branch. As per |
| // nsIPrefBranch2.h, this would result in a circular reference between us |
| // and the pref branch, which can impede cleanup. There are workarounds, |
| // but let's try just not caching the branch reference for now. |
| bool result = false; |
| ScopedNsPtr<nsIPrefBranch> pref_branch; |
| |
| pref_service->GetBranch(kProxyObserverRoot, pref_branch.Receive()); |
| |
| if (!pref_branch) { |
| DLOG(ERROR) << "Failed to get nsIPrefBranch"; |
| } else { |
| if (!ReadProxySettings(pref_branch.get())) { |
| DLOG(ERROR) << "Could not read proxy settings."; |
| } else { |
| observer_pref_branch_.QueryFrom(pref_branch); |
| if (!observer_pref_branch_) { |
| DLOG(ERROR) << "Failed to get observer nsIPrefBranch2"; |
| } else { |
| nsresult res = observer_pref_branch_->AddObserver(kProxyObserverBranch, |
| this, PR_FALSE); |
| result = NS_SUCCEEDED(res); |
| } |
| } |
| } |
| return result; |
| } |
| |
| bool NpProxyService::UnInitialize() { |
| // Fail early if this was never created - we may not be running on FF. |
| if (!pref_service_) |
| return false; |
| |
| // Unhook ourselves as an observer. |
| nsresult res = NS_ERROR_FAILURE; |
| if (observer_pref_branch_) |
| res = observer_pref_branch_->RemoveObserver(kProxyObserverBranch, this); |
| |
| return NS_SUCCEEDED(res); |
| } |
| |
| NS_IMETHODIMP NpProxyService::Observe(nsISupports* subject, const char* topic, |
| const PRUnichar* data) { |
| if (!subject || !topic) { |
| NOTREACHED(); |
| return NS_ERROR_UNEXPECTED; |
| } |
| |
| std::string topic_str(topic); |
| nsresult res = NS_OK; |
| if (topic_str == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) { |
| // Looks like our proxy settings changed. We need to reload! |
| // I have observed some extremely strange behaviour here. Specifically, |
| // we are supposed to be able to QI |subject| and get from it an |
| // nsIPrefBranch from which we can query new values. This has erratic |
| // behaviour, specifically subject starts returning null on all member |
| // queries. So I am using the cached nsIPrefBranch2 (that we used to add |
| // the observer) to do the querying. |
| if (NS_SUCCEEDED(res)) { |
| if (!ReadProxySettings(observer_pref_branch_)) { |
| res = NS_ERROR_UNEXPECTED; |
| } else { |
| std::string proxy_settings; |
| if (GetProxyValueJSONString(&proxy_settings)) |
| automation_client_->SetProxySettings(proxy_settings); |
| } |
| } |
| } else { |
| NOTREACHED(); |
| } |
| |
| return res; |
| } |
| |
| std::string NpProxyService::GetStringPref(nsIPrefBranch* pref_branch, |
| const char* pref_name) { |
| nsCString pref_string; |
| std::string result; |
| nsresult rv = pref_branch->GetCharPref(pref_name, getter_Copies(pref_string)); |
| if (SUCCEEDED(rv) && pref_string.get()) { |
| result = pref_string.get(); |
| } |
| return result; |
| } |
| |
| int NpProxyService::GetIntPref(nsIPrefBranch* pref_branch, |
| const char* pref_name) { |
| PRInt32 pref_int; |
| int result = kInvalidIntPref; |
| nsresult rv = pref_branch->GetIntPref(pref_name, &pref_int); |
| if (SUCCEEDED(rv)) { |
| result = pref_int; |
| } |
| return result; |
| } |
| |
| bool NpProxyService::GetBoolPref(nsIPrefBranch* pref_branch, |
| const char* pref_name) { |
| PRBool pref_bool; |
| bool result = false; |
| nsresult rv = pref_branch->GetBoolPref(pref_name, &pref_bool); |
| if (SUCCEEDED(rv)) { |
| result = pref_bool == PR_TRUE; |
| } |
| return result; |
| } |
| |
| void NpProxyService::Reset() { |
| type_ = PROXY_CONFIG_LAST; |
| auto_detect_ = false; |
| no_proxy_ = false; |
| system_config_ = false; |
| manual_proxies_.clear(); |
| pac_url_.clear(); |
| proxy_bypass_list_.clear(); |
| } |
| |
| bool NpProxyService::ReadProxySettings(nsIPrefBranch* pref_branch) { |
| DCHECK(pref_branch); |
| |
| // Clear our current settings. |
| Reset(); |
| type_ = GetIntPref(pref_branch, kProxyType); |
| if (type_ == kInvalidIntPref) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| switch (type_) { |
| case PROXY_CONFIG_DIRECT: |
| case PROXY_CONFIG_DIRECT4X: |
| no_proxy_ = true; |
| break; |
| case PROXY_CONFIG_SYSTEM: |
| // _SYSTEM is documented as "Use system settings if available, otherwise |
| // DIRECT". It isn't clear under what circumstances system settings would |
| // be unavailable, but I'll special-case this nonetheless and have |
| // GetProxyValueJSONString() return empty if we get this proxy type. |
| DLOG(WARNING) << "Received PROXY_CONFIG_SYSTEM proxy type."; |
| system_config_ = true; |
| break; |
| case PROXY_CONFIG_WPAD: |
| auto_detect_ = true; |
| break; |
| case PROXY_CONFIG_PAC: |
| pac_url_ = GetStringPref(pref_branch, kProxyAutoconfigUrl); |
| break; |
| case PROXY_CONFIG_MANUAL: |
| // Read in the values for each of the known schemes. |
| for (int i = 0; i < arraysize(kProxyInfo); i++) { |
| ManualProxyEntry entry; |
| entry.url = GetStringPref(pref_branch, kProxyInfo[i].pref_name); |
| entry.port = GetIntPref(pref_branch, kProxyInfo[i].port_pref_name); |
| if (!entry.url.empty() && entry.port != kInvalidIntPref) { |
| entry.scheme = kProxyInfo[i].chrome_scheme; |
| manual_proxies_.push_back(entry); |
| } |
| } |
| |
| // Also pick up the list of URLs we bypass proxies for. |
| proxy_bypass_list_ = GetStringPref(pref_branch, kProxyBypassList); |
| break; |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| return true; |
| } |
| |
| DictionaryValue* NpProxyService::BuildProxyValueSet() { |
| scoped_ptr<DictionaryValue> proxy_settings_value(new DictionaryValue); |
| |
| if (auto_detect_) { |
| proxy_settings_value->SetBoolean(automation::kJSONProxyAutoconfig, |
| auto_detect_); |
| } |
| |
| if (no_proxy_) { |
| proxy_settings_value->SetBoolean(automation::kJSONProxyNoProxy, no_proxy_); |
| } |
| |
| if (!pac_url_.empty()) { |
| proxy_settings_value->SetString(automation::kJSONProxyPacUrl, pac_url_); |
| } |
| |
| if (!proxy_bypass_list_.empty()) { |
| proxy_settings_value->SetString(automation::kJSONProxyBypassList, |
| proxy_bypass_list_); |
| } |
| |
| // Fill in the manual proxy settings. Build a string representation that |
| // corresponds to the format of the input parameter to |
| // ProxyConfig::ProxyRules::ParseFromString. |
| std::string manual_proxy_settings; |
| ManualProxyList::const_iterator iter(manual_proxies_.begin()); |
| for (; iter != manual_proxies_.end(); iter++) { |
| DCHECK(!iter->scheme.empty()); |
| DCHECK(!iter->url.empty()); |
| DCHECK(iter->port != kInvalidIntPref); |
| manual_proxy_settings += iter->scheme; |
| manual_proxy_settings += "="; |
| manual_proxy_settings += iter->url; |
| manual_proxy_settings += ":"; |
| manual_proxy_settings += base::IntToString(iter->port); |
| manual_proxy_settings += ";"; |
| } |
| |
| if (!manual_proxy_settings.empty()) { |
| proxy_settings_value->SetString(automation::kJSONProxyServer, |
| manual_proxy_settings); |
| } |
| |
| return proxy_settings_value.release(); |
| } |
| |
| bool NpProxyService::GetProxyValueJSONString(std::string* output) { |
| DCHECK(output); |
| output->empty(); |
| |
| // If we detected a PROXY_CONFIG_SYSTEM config type or failed to obtain the |
| // pref service then return false here to make Chrome continue using its |
| // default proxy settings. |
| if (system_config_ || !pref_service_) |
| return false; |
| |
| scoped_ptr<DictionaryValue> proxy_settings_value(BuildProxyValueSet()); |
| |
| JSONStringValueSerializer serializer(output); |
| return serializer.Serialize(*static_cast<Value*>(proxy_settings_value.get())); |
| } |