Index: chrome_frame/np_proxy_service.cc |
=================================================================== |
--- chrome_frame/np_proxy_service.cc (revision 0) |
+++ chrome_frame/np_proxy_service.cc (revision 0) |
@@ -0,0 +1,306 @@ |
+// Copyright (c) 2009 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_util.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->ReadUserPrefs(nsnull); |
+ 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 += 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())); |
+} |