| 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()));
 | 
| +}
 | 
| 
 |