diff options
Diffstat (limited to 'chrome/browser/chromeos')
6 files changed, 590 insertions, 191 deletions
diff --git a/chrome/browser/chromeos/cros/cros_library.cc b/chrome/browser/chromeos/cros/cros_library.cc index a408c07..9065304 100644 --- a/chrome/browser/chromeos/cros/cros_library.cc +++ b/chrome/browser/chromeos/cros/cros_library.cc @@ -121,6 +121,10 @@ void CrosLibrary::TestApi::SetUseStubImpl() { library_->use_stub_impl_ = true; } +void CrosLibrary::TestApi::ResetUseStubImpl() { + library_->use_stub_impl_ = false; +} + void CrosLibrary::TestApi::SetLibraryLoader(LibraryLoader* loader, bool own) { if (library_->library_loader_ == loader) return; diff --git a/chrome/browser/chromeos/cros/cros_library.h b/chrome/browser/chromeos/cros/cros_library.h index d73b223..6695acf 100644 --- a/chrome/browser/chromeos/cros/cros_library.h +++ b/chrome/browser/chromeos/cros/cros_library.h @@ -41,6 +41,9 @@ class CrosLibrary { // Use the stub implementations of the library. This is mainly for // running the chromeos build of chrome on the desktop. void SetUseStubImpl(); + // Reset the stub implementations of the library, called after + // SetUseStubImp is called. + void ResetUseStubImpl(); // Passing true for own for these setters will cause them to be deleted // when the CrosLibrary is deleted (or other mocks are set). // Setter for LibraryLoader. diff --git a/chrome/browser/chromeos/proxy_config_service_impl.cc b/chrome/browser/chromeos/proxy_config_service_impl.cc index 6478314..a7fa754 100644 --- a/chrome/browser/chromeos/proxy_config_service_impl.cc +++ b/chrome/browser/chromeos/proxy_config_service_impl.cc @@ -9,8 +9,9 @@ #include "base/logging.h" #include "base/string_util.h" #include "base/task.h" -#include "base/values.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/chromeos/cros/cros_library.h" +#include "chrome/common/json_value_serializer.h" namespace chromeos { @@ -91,9 +92,27 @@ std::string ProxyConfigToString( return stream.str(); } +// Name of signed setting persisted on device, writeable only by owner. +const char* kSettingProxyEverywhere = "cros.proxy.everywhere"; + +// Names used for dictionary values to serialize chromeos::ProxyConfig. +const char* kMode = "mode"; +const char* kSource = "src"; +const char* kAutomaticProxy = "auto"; +const char* kSingleProxy = "single"; +const char* kHttpProxy = "http"; +const char* kHttpsProxy = "https"; +const char* kFtpProxy = "ftp"; +const char* kSocksProxy = "socks"; +const char* kPACUrl = "pac"; +const char* kServer = "server"; +const char* kBypassRules = "bypass_rules"; +const char* kRulesNum = "num"; +const char* kRulesList = "list"; + } // namespace -//-------------- ProxyConfigServiceImpl::ProxyConfig methods ------------------- +//---------- ProxyConfigServiceImpl::ProxyConfig::Setting methods -------------- bool ProxyConfigServiceImpl::ProxyConfig::Setting::CanBeWrittenByUser( bool user_is_owner) { @@ -102,7 +121,67 @@ bool ProxyConfigServiceImpl::ProxyConfig::Setting::CanBeWrittenByUser( return user_is_owner && source != ProxyConfig::SOURCE_POLICY; } -void ProxyConfigServiceImpl::ProxyConfig::ConvertToNetProxyConfig( +DictionaryValue* ProxyConfigServiceImpl::ProxyConfig::Setting::Encode() const { + DictionaryValue* dict = new DictionaryValue; + dict->SetInteger(kSource, source); + return dict; +} + +bool ProxyConfigServiceImpl::ProxyConfig::Setting::Decode( + DictionaryValue* dict) { + int int_source; + if (!dict->GetInteger(kSource, &int_source)) + return false; + source = static_cast<Source>(int_source); + return true; +} + +//------- ProxyConfigServiceImpl::ProxyConfig::AutomaticProxy methods ---------- + +DictionaryValue* + ProxyConfigServiceImpl::ProxyConfig::AutomaticProxy::Encode() const { + DictionaryValue* dict = Setting::Encode(); + if (!pac_url.is_empty()) + dict->SetString(kPACUrl, pac_url.spec()); + return dict; +} + +bool ProxyConfigServiceImpl::ProxyConfig::AutomaticProxy::Decode( + DictionaryValue* dict, Mode mode) { + if (!Setting::Decode(dict)) + return false; + if (mode == MODE_PAC_SCRIPT) { + std::string value; + if (!dict->GetString(kPACUrl, &value)) + return false; + pac_url = GURL(value); + } + return true; +} + +//--------- ProxyConfigServiceImpl::ProxyConfig::ManualProxy methods ----------- + +DictionaryValue* + ProxyConfigServiceImpl::ProxyConfig::ManualProxy::Encode() const { + DictionaryValue* dict = Setting::Encode(); + dict->SetString(kServer, server.ToURI()); + return dict; +} + +bool ProxyConfigServiceImpl::ProxyConfig::ManualProxy::Decode( + DictionaryValue* dict, net::ProxyServer::Scheme scheme) { + if (!Setting::Decode(dict)) + return false; + std::string value; + if (!dict->GetString(kServer, &value)) + return false; + server = net::ProxyServer::FromURI(value, scheme); + return true; +} + +//----------- ProxyConfigServiceImpl::ProxyConfig: public methods -------------- + +void ProxyConfigServiceImpl::ProxyConfig::ToNetProxyConfig( net::ProxyConfig* net_config) { switch (mode) { case MODE_DIRECT: @@ -138,28 +217,165 @@ void ProxyConfigServiceImpl::ProxyConfig::ConvertToNetProxyConfig( } } +bool ProxyConfigServiceImpl::ProxyConfig::Serialize(std::string* output) { + scoped_ptr<DictionaryValue> dict(new DictionaryValue); + dict->SetInteger(kMode, mode); + DictionaryValue* proxy_dict; + switch (mode) { + case MODE_DIRECT: + case MODE_AUTO_DETECT: + case MODE_PAC_SCRIPT: + proxy_dict = automatic_proxy.Encode(); + dict->Set(kAutomaticProxy, proxy_dict); + break; + case MODE_SINGLE_PROXY: + EncodeManualProxy(single_proxy, dict.get(), kSingleProxy); + break; + case MODE_PROXY_PER_SCHEME: + EncodeManualProxy(http_proxy, dict.get(), kHttpProxy); + EncodeManualProxy(https_proxy, dict.get(), kHttpsProxy); + EncodeManualProxy(ftp_proxy, dict.get(), kFtpProxy); + EncodeManualProxy(socks_proxy, dict.get(), kSocksProxy); + break; + default: + NOTREACHED() << "Unrecognized proxy config mode"; + break; + } + net::ProxyBypassRules::RuleList rules = bypass_rules.rules(); + if (!rules.empty()) { + DictionaryValue* bypass_dict = new DictionaryValue; + bypass_dict->SetInteger(kRulesNum, rules.size()); + ListValue* list = new ListValue; + for (size_t i = 0; i < rules.size(); ++i) { + list->Append(Value::CreateStringValue(rules[i]->ToString())); + } + bypass_dict->Set(kRulesList, list); + dict->Set(kBypassRules, bypass_dict); + } + JSONStringValueSerializer serializer(output); + return serializer.Serialize(*dict.get()); +} + +bool ProxyConfigServiceImpl::ProxyConfig::Deserialize( + const std::string& input) { + JSONStringValueSerializer serializer(input); + scoped_ptr<Value> value(serializer.Deserialize(NULL, NULL)); + if (!value.get() || value->GetType() != Value::TYPE_DICTIONARY) + return false; + DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); + int int_mode; + if (!dict->GetInteger(kMode, &int_mode)) + return false; + mode = static_cast<Mode>(int_mode); + DictionaryValue* proxy_dict = NULL; + switch (mode) { + case MODE_DIRECT: + case MODE_AUTO_DETECT: + case MODE_PAC_SCRIPT: + if (!dict->GetDictionary(kAutomaticProxy, &proxy_dict) || + !automatic_proxy.Decode(proxy_dict, mode)) + return false; + break; + case MODE_SINGLE_PROXY: + if (!DecodeManualProxy(dict, kSingleProxy, false, + net::ProxyServer::SCHEME_HTTP, &single_proxy)) + return false; + break; + case MODE_PROXY_PER_SCHEME: + if (!DecodeManualProxy(dict, kHttpProxy, true, + net::ProxyServer::SCHEME_HTTP, &http_proxy)) + return false; + if (!DecodeManualProxy(dict, kHttpsProxy, true, + net::ProxyServer::SCHEME_HTTPS, &https_proxy)) + return false; + if (!DecodeManualProxy(dict, kFtpProxy, true, + net::ProxyServer::SCHEME_HTTP, &ftp_proxy)) + return false; + if (!DecodeManualProxy(dict, kSocksProxy, true, + net::ProxyServer::SCHEME_SOCKS4, &socks_proxy)) + return false; + // Make sure we have valid server for at least one of the protocols. + if (!(http_proxy.server.is_valid() || https_proxy.server.is_valid() || + ftp_proxy.server.is_valid() || socks_proxy.server.is_valid())) + return false; + break; + default: + NOTREACHED() << "Unrecognized proxy config mode"; + break; + } + DictionaryValue* bypass_dict = NULL; + if (dict->GetDictionary(kBypassRules, &bypass_dict)) { + int num_rules = 0; + if (bypass_dict->GetInteger(kRulesNum, &num_rules) && num_rules > 0) { + ListValue* list; + if (!bypass_dict->GetList(kRulesList, &list)) + return false; + for (size_t i = 0; i < list->GetSize(); ++i) { + std::string rule; + if (!list->GetString(i, &rule)) + return false; + bypass_rules.AddRuleFromString(rule); + } + } + } + return true; +} + std::string ProxyConfigServiceImpl::ProxyConfig::ToString() const { return ProxyConfigToString(*this); } +//----------- ProxyConfigServiceImpl::ProxyConfig: private methods ------------- + +void ProxyConfigServiceImpl::ProxyConfig::EncodeManualProxy( + const ManualProxy& manual_proxy, DictionaryValue* dict, + const char* key_name) { + if (!manual_proxy.server.is_valid()) + return; + DictionaryValue* proxy_dict = manual_proxy.Encode(); + dict->Set(key_name, proxy_dict); +} + +bool ProxyConfigServiceImpl::ProxyConfig::DecodeManualProxy( + DictionaryValue* dict, const char* key_name, bool ok_if_absent, + net::ProxyServer::Scheme scheme, ManualProxy* manual_proxy) { + DictionaryValue* proxy_dict; + if (!dict->GetDictionary(key_name, &proxy_dict)) + return ok_if_absent; + return manual_proxy->Decode(proxy_dict, scheme); +} + //------------------- ProxyConfigServiceImpl: public methods ------------------- -ProxyConfigServiceImpl::ProxyConfigServiceImpl() { - // Fetch and cache proxy config from cros settings persisted on device. +ProxyConfigServiceImpl::ProxyConfigServiceImpl() + : can_post_task_(false), + has_config_(false), + persist_to_device_pending_(false) { + // Start async fetch of proxy config from settings persisted on device. // TODO(kuan): retrieve config from policy and owner and merge them - // for now, hardcode it as AUTO_DETECT and it'll pick up the PAC script set in - // chromeos environment. - cached_config_.mode = ProxyConfig::MODE_AUTO_DETECT; - cached_config_.automatic_proxy.source = ProxyConfig::SOURCE_OWNER; - - // Update the thread-private copy in |reference_config_| as well. - reference_config_ = cached_config_; + bool use_default = true; + if (CrosLibrary::Get()->EnsureLoaded()) { + retrieve_property_op_ = SignedSettings::CreateRetrievePropertyOp( + kSettingProxyEverywhere, this); + if (retrieve_property_op_ && retrieve_property_op_->Execute()) { + LOG(INFO) << "Start retrieving proxy setting from device"; + use_default = false; + } else { + LOG(INFO) << "Fail to retrieve proxy setting from device"; + } + } + if (use_default) + InitConfigToDefault(false); + can_post_task_ = true; } -ProxyConfigServiceImpl::ProxyConfigServiceImpl(const ProxyConfig& init_config) { - cached_config_ = init_config; - // Update the thread-private copy in |reference_config_| as well. - reference_config_ = cached_config_; +ProxyConfigServiceImpl::ProxyConfigServiceImpl(const ProxyConfig& init_config) + : can_post_task_(true), + has_config_(true), + persist_to_device_pending_(false) { + reference_config_ = init_config; + // Update the IO-accessible copy in |cached_config_| as well. + cached_config_ = reference_config_; } ProxyConfigServiceImpl::~ProxyConfigServiceImpl() { @@ -172,38 +388,50 @@ void ProxyConfigServiceImpl::UIGetProxyConfig(ProxyConfig* config) { *config = reference_config_; } -void ProxyConfigServiceImpl::UISetProxyConfigToDirect() { +bool ProxyConfigServiceImpl::UISetProxyConfigToDirect() { // Should be called from UI thread. CheckCurrentlyOnUIThread(); reference_config_.mode = ProxyConfig::MODE_DIRECT; - OnUISetProxyConfig(); + OnUISetProxyConfig(true); + return true; } -void ProxyConfigServiceImpl::UISetProxyConfigToAutoDetect() { +bool ProxyConfigServiceImpl::UISetProxyConfigToAutoDetect() { // Should be called from UI thread. CheckCurrentlyOnUIThread(); reference_config_.mode = ProxyConfig::MODE_AUTO_DETECT; - OnUISetProxyConfig(); + OnUISetProxyConfig(true); + return true; } -void ProxyConfigServiceImpl::UISetProxyConfigToPACScript(const GURL& url) { +bool ProxyConfigServiceImpl::UISetProxyConfigToPACScript(const GURL& pac_url) { // Should be called from UI thread. CheckCurrentlyOnUIThread(); reference_config_.mode = ProxyConfig::MODE_PAC_SCRIPT; - reference_config_.automatic_proxy.pac_url = url; - OnUISetProxyConfig(); + reference_config_.automatic_proxy.pac_url = pac_url; + if (pac_url.is_valid()) { + OnUISetProxyConfig(true); + return true; + } + LOG(INFO) << "Cannot set proxy: invalid pac url"; + return false; } -void ProxyConfigServiceImpl::UISetProxyConfigToSingleProxy( +bool ProxyConfigServiceImpl::UISetProxyConfigToSingleProxy( const net::ProxyServer& server) { // Should be called from UI thread. CheckCurrentlyOnUIThread(); reference_config_.mode = ProxyConfig::MODE_SINGLE_PROXY; reference_config_.single_proxy.server = server; - OnUISetProxyConfig(); + if (server.is_valid()) { + OnUISetProxyConfig(true); + return true; + } + LOG(INFO) << "Cannot set proxy: invalid single server"; + return false; } -void ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme( +bool ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme( const std::string& scheme, const net::ProxyServer& server) { // Should be called from UI thread. CheckCurrentlyOnUIThread(); @@ -216,26 +444,35 @@ void ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme( proxy = &reference_config_.ftp_proxy; else if (scheme == "socks") proxy = &reference_config_.socks_proxy; - else - NOTREACHED() << "Unrecognized scheme for setting proxy config"; - if (proxy) { - reference_config_.mode = ProxyConfig::MODE_PROXY_PER_SCHEME; - proxy->server = server; - OnUISetProxyConfig(); + if (!proxy) { + NOTREACHED() << "Cannot set proxy: invalid scheme [" << scheme << "]"; + return false; + } + reference_config_.mode = ProxyConfig::MODE_PROXY_PER_SCHEME; + proxy->server = server; + if (server.is_valid()) { + OnUISetProxyConfig(true); + return true; } + LOG(INFO) << "Cannot set proxy: invalid " << scheme << " server"; + return false; } -void ProxyConfigServiceImpl::UISetProxyConfigBypassRules( +bool ProxyConfigServiceImpl::UISetProxyConfigBypassRules( const net::ProxyBypassRules& bypass_rules) { // Should be called from UI thread. CheckCurrentlyOnUIThread(); DCHECK(reference_config_.mode == ProxyConfig::MODE_SINGLE_PROXY || reference_config_.mode == ProxyConfig::MODE_PROXY_PER_SCHEME); - if (reference_config_.mode == ProxyConfig::MODE_SINGLE_PROXY || - reference_config_.mode == ProxyConfig::MODE_PROXY_PER_SCHEME) { - reference_config_.bypass_rules = bypass_rules; - OnUISetProxyConfig(); + if (reference_config_.mode != ProxyConfig::MODE_SINGLE_PROXY && + reference_config_.mode != ProxyConfig::MODE_PROXY_PER_SCHEME) { + LOG(INFO) << "Cannot set bypass rules for proxy mode [" + << reference_config_.mode << "]"; + return false; } + reference_config_.bypass_rules = bypass_rules; + OnUISetProxyConfig(true); + return true; } void ProxyConfigServiceImpl::AddObserver( @@ -255,21 +492,76 @@ void ProxyConfigServiceImpl::RemoveObserver( bool ProxyConfigServiceImpl::IOGetProxyConfig(net::ProxyConfig* net_config) { // Should be called from IO thread. CheckCurrentlyOnIOThread(); + if (has_config_) { + // Simply return the last cached proxy configuration. + cached_config_.ToNetProxyConfig(net_config); + return true; + } + return false; +} - // Simply return the last cached proxy configuration. - cached_config_.ConvertToNetProxyConfig(net_config); +void ProxyConfigServiceImpl::OnSettingsOpSucceeded(bool value) { + LOG(INFO) << "Stored proxy setting to device"; + store_property_op_ = NULL; + if (persist_to_device_pending_) + PersistConfigToDevice(); +} - // We return true to indicate that *config was filled in. It is always - // going to be available since we initialized eagerly on the UI thread. - // TODO(kuan): do lazy initialization instead, so we no longer need - // to construct ProxyConfigServiceImpl on the UI thread. - // In which case, we may return false here. - return true; +void ProxyConfigServiceImpl::OnSettingsOpSucceeded(std::string value) { + LOG(INFO) << "Retrieved proxy setting from device, value=[" << value << "]"; + if (reference_config_.Deserialize(value)) { + OnUISetProxyConfig(false); + } else { + LOG(WARNING) << "Error deserializing device's proxy setting"; + InitConfigToDefault(true); + } + retrieve_property_op_ = NULL; +} + +void ProxyConfigServiceImpl::OnSettingsOpFailed() { + if (retrieve_property_op_) { + LOG(WARNING) << "Error retrieving proxy setting from device"; + InitConfigToDefault(true); + retrieve_property_op_ = NULL; + } else { + LOG(WARNING) << "Error storing proxy setting to device"; + store_property_op_ = NULL; + if (persist_to_device_pending_) + PersistConfigToDevice(); + } } //------------------ ProxyConfigServiceImpl: private methods ------------------- -void ProxyConfigServiceImpl::OnUISetProxyConfig() { +void ProxyConfigServiceImpl::InitConfigToDefault(bool post_to_io_thread) { + LOG(INFO) << "Using default proxy config: auto-detect"; + reference_config_.mode = ProxyConfig::MODE_AUTO_DETECT; + reference_config_.automatic_proxy.source = ProxyConfig::SOURCE_OWNER; + if (post_to_io_thread && can_post_task_) { + OnUISetProxyConfig(false); + } else { + // Update the IO-accessible copy in |cached_config_| as well. + cached_config_ = reference_config_; + has_config_ = true; + } +} + +void ProxyConfigServiceImpl::PersistConfigToDevice() { + DCHECK(!store_property_op_); + persist_to_device_pending_ = false; + std::string value; + if (!reference_config_.Serialize(&value)) { + LOG(INFO) << "Error serializing proxy config"; + return; + } + store_property_op_ = SignedSettings::CreateStorePropertyOp( + kSettingProxyEverywhere, value, this); + bool rc = store_property_op_->Execute(); + LOG(INFO) << "Start storing proxy setting to device, value=" << value + << ", rc=" << rc; +} + +void ProxyConfigServiceImpl::OnUISetProxyConfig(bool persist_to_device) { // Posts a task to IO thread with the new config, so it can update // |cached_config_|. Task* task = NewRunnableMethod(this, @@ -279,7 +571,14 @@ void ProxyConfigServiceImpl::OnUISetProxyConfig() { delete task; } - // TODO(kuan): write new config out to cros settings for persistence. + if (persist_to_device && CrosLibrary::Get()->EnsureLoaded()) { + if (store_property_op_) { + persist_to_device_pending_ = true; + LOG(INFO) << "Pending persisting proxy setting to device"; + } else { + PersistConfigToDevice(); + } + } } void ProxyConfigServiceImpl::CheckCurrentlyOnIOThread() { @@ -294,10 +593,11 @@ void ProxyConfigServiceImpl::IOSetProxyConfig(const ProxyConfig& new_config) { // This is called on the IO thread (posted from UI thread). CheckCurrentlyOnIOThread(); LOG(INFO) << "Proxy configuration changed"; + has_config_ = true; cached_config_ = new_config; // Notify observers of new proxy config. net::ProxyConfig net_config; - cached_config_.ConvertToNetProxyConfig(&net_config); + cached_config_.ToNetProxyConfig(&net_config); FOR_EACH_OBSERVER(net::ProxyConfigService::Observer, observers_, OnProxyConfigChanged(net_config)); } diff --git a/chrome/browser/chromeos/proxy_config_service_impl.h b/chrome/browser/chromeos/proxy_config_service_impl.h index c22f1a5..e8e184d 100644 --- a/chrome/browser/chromeos/proxy_config_service_impl.h +++ b/chrome/browser/chromeos/proxy_config_service_impl.h @@ -13,6 +13,8 @@ #include "base/observer_list.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/chromeos/login/signed_settings.h" #include "net/proxy/proxy_config.h" #include "net/proxy/proxy_config_service.h" #include "net/proxy/proxy_server.h" @@ -32,7 +34,9 @@ namespace chromeos { // - TODO(kuan): persists proxy configuration settings on chromeos device using // cros settings class ProxyConfigServiceImpl - : public base::RefCountedThreadSafe<ProxyConfigServiceImpl> { + : public base::RefCountedThreadSafe<ProxyConfigServiceImpl>, + public SignedSettings::Delegate<bool>, + public SignedSettings::Delegate<std::string> { public: // ProxyConfigServiceImpl is created on the UI thread in // chrome/browser/net/chrome_url_request_context.cc::CreateProxyConfigService @@ -84,25 +88,40 @@ class ProxyConfigServiceImpl struct Setting { Setting() : source(SOURCE_NONE) {} + bool CanBeWrittenByUser(bool user_is_owner); + virtual DictionaryValue* Encode() const; + bool Decode(DictionaryValue* dict); Source source; - bool CanBeWrittenByUser(bool user_is_owner); }; // Proxy setting for mode = direct or auto-detect or using pac script. struct AutomaticProxy : public Setting { + virtual DictionaryValue* Encode() const; + bool Decode(DictionaryValue* dict, Mode mode); + GURL pac_url; // Set if proxy is using pac script. }; // Proxy setting for mode = single-proxy or proxy-per-scheme. struct ManualProxy : public Setting { + virtual DictionaryValue* Encode() const; + bool Decode(DictionaryValue* dict, net::ProxyServer::Scheme scheme); + net::ProxyServer server; }; ProxyConfig() : mode(MODE_DIRECT) {} // Converts |this| to net::ProxyConfig. - void ConvertToNetProxyConfig(net::ProxyConfig* net_config); + void ToNetProxyConfig(net::ProxyConfig* net_config); + + // Serializes config into a DictionaryValue and then into std::string + // persisted as property on device. + bool Serialize(std::string* output); + // Deserializes from property value on device as std::string into a + // DictionaryValue and then into the config. Opposite of Serialize. + bool Deserialize(const std::string& input); // Creates a textual dump of the configuration. std::string ToString() const; @@ -124,6 +143,17 @@ class ProxyConfigServiceImpl // Exceptions for when not to use a proxy. net::ProxyBypassRules bypass_rules; + + private: + // Encodes |manual_proxy| and adds it as value into |key_name| of |dict|. + void EncodeManualProxy(const ManualProxy& manual_proxy, + DictionaryValue* dict, const char* key_name); + // Decodes value of |key_name| in |dict| into |manual_proxy| with |scheme|; + // if |ok_if_absent| is true, function returns true if |key_name| doesn't + // exist in |dict|. + bool DecodeManualProxy(DictionaryValue* dict, const char* key_name, + bool ok_if_absent, net::ProxyServer::Scheme scheme, + ManualProxy* manual_proxy); }; // Usual constructor. @@ -144,20 +174,40 @@ class ProxyConfigServiceImpl void UIGetProxyConfig(ProxyConfig* config); // Called from UI thread to update proxy configuration for different modes. - void UISetProxyConfigToDirect(); - void UISetProxyConfigToAutoDetect(); - void UISetProxyConfigToPACScript(const GURL& url); - void UISetProxyConfigToSingleProxy(const net::ProxyServer& server); - void UISetProxyConfigToProxyPerScheme(const std::string& scheme, + // Returns true if config is set properly and config service has proceeded to + // start activating it on network stack and persisting it to device. + // Returns false if config is not set properly, probably because information + // is incomplete or invalid; while config service won't proceed to activate or + // persist this config, the information is "cached" in the service, so that + // the next UIGetProxyConfig call will return this latest information. + bool UISetProxyConfigToDirect(); + bool UISetProxyConfigToAutoDetect(); + bool UISetProxyConfigToPACScript(const GURL& pac_url); + bool UISetProxyConfigToSingleProxy(const net::ProxyServer& server); + bool UISetProxyConfigToProxyPerScheme(const std::string& scheme, const net::ProxyServer& server); // Only valid for MODE_SINGLE_PROXY or MODE_PROXY_PER_SCHEME. - void UISetProxyConfigBypassRules(const net::ProxyBypassRules& bypass_rules); + bool UISetProxyConfigBypassRules(const net::ProxyBypassRules& bypass_rules); + + // Implementation for SignedSettings::Delegate + virtual void OnSettingsOpSucceeded(bool value); + virtual void OnSettingsOpSucceeded(std::string value); + virtual void OnSettingsOpFailed(); private: friend class base::RefCountedThreadSafe<ProxyConfigServiceImpl>; + // Init proxy to default config, i.e. AutoDetect. + // If |post_to_io_thread| is true, a task will be posted to IO thread to + // update |cached_config|. + void InitConfigToDefault(bool post_to_io_thread); + + // Persists proxy config to device. + void PersistConfigToDevice(); + // Called from UI thread from the various UISetProxyConfigTo* - void OnUISetProxyConfig(); + // |update_to_device| is true to persist new proxy config to device. + void OnUISetProxyConfig(bool update_to_device); // Posted from UI thread to IO thread to carry the new config information. void IOSetProxyConfig(const ProxyConfig& new_config); @@ -170,6 +220,17 @@ class ProxyConfigServiceImpl // Data members. + // True if tasks can be posted, which can only happen if constructor has + // completed (NewRunnableMethod cannot be created for a RefCountedThreadBase's + // method until the class's ref_count is at least one). + bool can_post_task_; + + // True if config has been fetched from device or initialized properly. + bool has_config_; + + // True if there's a pending operation to store proxy setting to device. + bool persist_to_device_pending_; + // Cached proxy configuration, to be converted to net::ProxyConfig and // returned by IOGetProxyConfig. // Initially populated from the UI thread, but afterwards only accessed from @@ -181,8 +242,14 @@ class ProxyConfigServiceImpl // are called by UI to set new proxy but the config has not actually changed. ProxyConfig reference_config_; + // List of observers for changes in proxy config. ObserverList<net::ProxyConfigService::Observer> observers_; + // Operations to retrieve and store proxy setting from and to device + // respectively. + scoped_refptr<SignedSettings> retrieve_property_op_; + scoped_refptr<SignedSettings> store_property_op_; + DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceImpl); }; diff --git a/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc b/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc index 8c6e5ed..c68e42e 100644 --- a/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc +++ b/chrome/browser/chromeos/proxy_config_service_impl_unittest.cc @@ -13,10 +13,15 @@ #include "base/string_util.h" #include "base/stringprintf.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/chromeos/cros/cros_library.h" #include "net/proxy/proxy_config_service_common_unittest.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" +#if !defined(NDEBUG) +#include "chrome/common/json_value_serializer.h" +#endif // !defined(NDEBUG) + namespace chromeos { namespace { @@ -38,12 +43,15 @@ struct Input { // Fields of chromeos::ProxyConfigServiceImpl::ProxyConfig. // Shortcuts to declare enums within chromeos's ProxyConfig. #define MK_MODE(mode) ProxyConfigServiceImpl::ProxyConfig::MODE_##mode #define MK_SRC(src) ProxyConfigServiceImpl::ProxyConfig::SOURCE_##src +#define MK_SCHM(scheme) net::ProxyServer::SCHEME_##scheme // Inspired from net/proxy/proxy_config_service_linux_unittest.cc. const struct { // Short description to identify the test std::string description; + bool is_valid; + Input input; // Expected outputs from fields of net::ProxyConfig (via IO). @@ -53,6 +61,8 @@ const struct { } tests[] = { { TEST_DESC("No proxying"), + true, // is_valid + { // Input. MK_MODE(DIRECT), // mode }, @@ -65,6 +75,8 @@ const struct { { TEST_DESC("Auto detect"), + true, // is_valid + { // Input. MK_MODE(AUTO_DETECT), // mode }, @@ -77,6 +89,8 @@ const struct { { TEST_DESC("Valid PAC URL"), + true, // is_valid + { // Input. MK_MODE(PAC_SCRIPT), // mode "http://wpad/wpad.dat", // pac_url @@ -90,6 +104,8 @@ const struct { { TEST_DESC("Invalid PAC URL"), + false, // is_valid + { // Input. MK_MODE(PAC_SCRIPT), // mode "wpad.dat", // pac_url @@ -103,6 +119,8 @@ const struct { { TEST_DESC("Single-host in proxy list"), + true, // is_valid + { // Input. MK_MODE(SINGLE_PROXY), // mode NULL, // pac_url @@ -119,6 +137,8 @@ const struct { { TEST_DESC("Single-host, different port"), + true, // is_valid + { // Input. MK_MODE(SINGLE_PROXY), // mode NULL, // pac_url @@ -135,6 +155,8 @@ const struct { { TEST_DESC("Tolerate a scheme"), + true, // is_valid + { // Input. MK_MODE(SINGLE_PROXY), // mode NULL, // pac_url @@ -151,99 +173,48 @@ const struct { { TEST_DESC("Per-scheme proxy rules"), - { // Input. - MK_MODE(PROXY_PER_SCHEME), // mode - NULL, // pac_url - NULL, // single_uri - "www.google.com:80", "www.foo.com:110", "ftp.foo.com:121", // per-proto - }, - - // Expected result. - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::PerScheme( // proxy_rules - "www.google.com:80", // http - "www.foo.com:110", // https - "ftp.foo.com:121", // ftp - ""), // bypass rules - }, + true, // is_valid -// TODO(kuan): enable these. -#if defined(TO_ENABLE_SOON) - { - TEST_DESC("socks"), { // Input. MK_MODE(PROXY_PER_SCHEME), // mode NULL, // pac_url NULL, // single_uri - NULL, NULL, NULL, // per-proto proxies + "www.google.com:80", // http_uri + "www.foo.com:110", // https_uri + "ftp.foo.com:121", // ftp_uri "socks.com:888", // socks_uri }, // Expected result. - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Single( // proxy_rules - "socks4://socks.com:888", // single proxy - ""), // bypass rules - }, - - { - TEST_DESC("socks5"), - { // Input. - NULL, // auto_proxy - "", // all_proxy - NULL, NULL, NULL, // per-proto proxies - "socks.com:888", "5", // SOCKS - NULL, // no_proxy - }, - - // Expected result. - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Single( // proxy_rules - "socks5://socks.com:888", // single proxy - ""), // bypass rules - ProxyConfigServiceImpl::READ_ONLY_MAIN, // readonly for owner - ProxyConfigServiceImpl::READ_ONLY_MAIN, // readonly for non-owner - }, - - { - TEST_DESC("socks default port"), - { // Input. - NULL, // auto_proxy - "", // all_proxy - NULL, NULL, NULL, // per-proto proxies - "socks.com", NULL, // SOCKS - NULL, // no_proxy - }, - - // Expected result. - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Single( // proxy_rules - "socks4://socks.com:1080", // single proxy + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::PerSchemeWithSocks( // proxy_rules + "www.google.com:80", // http + "https://www.foo.com:110", // https + "ftp.foo.com:121", // ftp + "socks4://socks.com:888", // fallback proxy ""), // bypass rules }, { - TEST_DESC("bypass"), + TEST_DESC("Bypass rules"), + true, // is_valid + { // Input. - MK_MODE(PROXY_PER_SCHEME), // mode + MK_MODE(SINGLE_PROXY), // mode NULL, // pac_url "www.google.com", // single_uri - NULL, NULL, NULL, NULL, // per-proto & socks proxies + NULL, NULL, NULL, NULL, // per-proto ".google.com, foo.com:99, 1.2.3.4:22, 127.0.0.1/8", // bypass_rules }, - // Expected result; - false, // auto_detect - GURL(), // pac_url - net::ProxyRulesExpectation::Single( // proxy_rules - "www.google.com:80", - "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8"), + // Expected result. + false, // auto_detect + GURL(), // pac_url + net::ProxyRulesExpectation::Single( // proxy_rules + "www.google.com:80", // single proxy + "*.google.com,*foo.com:99,1.2.3.4:22,127.0.0.1/8"), // bypass_rules }, -#endif // TO_ENABLE_SOON }; // tests } // namespace @@ -253,6 +224,7 @@ class ProxyConfigServiceImplTest : public PlatformTest { ProxyConfigServiceImplTest() : ui_thread_(ChromeThread::UI, &message_loop_), io_thread_(ChromeThread::IO, &message_loop_) { + chromeos::CrosLibrary::Get()->GetTestApi()->SetUseStubImpl(); } virtual ~ProxyConfigServiceImplTest() { @@ -282,14 +254,14 @@ class ProxyConfigServiceImplTest : public PlatformTest { ProxyConfigServiceImpl::ProxyConfig::Mode mode, ProxyConfigServiceImpl::ProxyConfig::Source source, const char* server_uri, + net::ProxyServer::Scheme scheme, ProxyConfigServiceImpl::ProxyConfig* config, ProxyConfigServiceImpl::ProxyConfig::ManualProxy* manual_proxy) { if (!server_uri) return; config->mode = mode; manual_proxy->source = source; - manual_proxy->server = net::ProxyServer::FromURI(server_uri, - net::ProxyServer::SCHEME_HTTP); + manual_proxy->server = net::ProxyServer::FromURI(server_uri, scheme); } void InitConfigWithTestInput( @@ -304,18 +276,18 @@ class ProxyConfigServiceImplTest : public PlatformTest { &init_config->automatic_proxy); return; case MK_MODE(SINGLE_PROXY): - SetManualProxy(input.mode, source, input.single_uri, init_config, - &init_config->single_proxy); + SetManualProxy(input.mode, source, input.single_uri, MK_SCHM(HTTP), + init_config, &init_config->single_proxy); break; case MK_MODE(PROXY_PER_SCHEME): - SetManualProxy(input.mode, source, input.http_uri, init_config, - &init_config->http_proxy); - SetManualProxy(input.mode, source, input.https_uri, init_config, - &init_config->https_proxy); - SetManualProxy(input.mode, source, input.ftp_uri, init_config, - &init_config->ftp_proxy); - SetManualProxy(input.mode, source, input.socks_uri, init_config, - &init_config->socks_proxy); + SetManualProxy(input.mode, source, input.http_uri, MK_SCHM(HTTP), + init_config, &init_config->http_proxy); + SetManualProxy(input.mode, source, input.https_uri, MK_SCHM(HTTPS), + init_config, &init_config->https_proxy); + SetManualProxy(input.mode, source, input.ftp_uri, MK_SCHM(HTTP), + init_config, &init_config->ftp_proxy); + SetManualProxy(input.mode, source, input.socks_uri, MK_SCHM(SOCKS4), + init_config, &init_config->socks_proxy); break; } if (input.bypass_rules) { @@ -400,63 +372,70 @@ TEST_F(ProxyConfigServiceImplTest, ReadWriteAccess) { } TEST_F(ProxyConfigServiceImplTest, ModifyFromUI) { - // Init with direct. - ProxyConfigServiceImpl::ProxyConfig init_config; - SetAutomaticProxy(MK_MODE(DIRECT), MK_SRC(OWNER), NULL, &init_config, - &init_config.automatic_proxy); - CreateConfigService(init_config); - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "] %s", i, tests[i].description.c_str())); + // Init with direct. + ProxyConfigServiceImpl::ProxyConfig init_config; + SetAutomaticProxy(MK_MODE(DIRECT), MK_SRC(OWNER), NULL, &init_config, + &init_config.automatic_proxy); + CreateConfigService(init_config); + // Set config to tests[i].input via UI. net::ProxyBypassRules bypass_rules; - net::ProxyServer::Scheme scheme = net::ProxyServer::SCHEME_HTTP; const Input& input = tests[i].input; switch (input.mode) { case MK_MODE(DIRECT) : - config_service()->UISetProxyConfigToDirect(); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToDirect()); break; case MK_MODE(AUTO_DETECT) : - config_service()->UISetProxyConfigToAutoDetect(); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToAutoDetect()); break; case MK_MODE(PAC_SCRIPT) : - config_service()->UISetProxyConfigToPACScript(GURL(input.pac_url)); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToPACScript( + GURL(input.pac_url))); break; case MK_MODE(SINGLE_PROXY) : - config_service()->UISetProxyConfigToSingleProxy( - net::ProxyServer::FromURI(input.single_uri, - net::ProxyServer::SCHEME_HTTP)); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToSingleProxy( + net::ProxyServer::FromURI(input.single_uri, + MK_SCHM(HTTP)))); if (input.bypass_rules) { bypass_rules.ParseFromStringUsingSuffixMatching(input.bypass_rules); - config_service()->UISetProxyConfigBypassRules(bypass_rules); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigBypassRules( + bypass_rules)); } break; case MK_MODE(PROXY_PER_SCHEME) : if (input.http_uri) { - config_service()->UISetProxyConfigToProxyPerScheme( - "http", - net::ProxyServer::FromURI(input.http_uri, scheme)); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToProxyPerScheme("http", + net::ProxyServer::FromURI(input.http_uri, MK_SCHM(HTTP)))); } if (input.https_uri) { - config_service()->UISetProxyConfigToProxyPerScheme( - "https", - net::ProxyServer::FromURI(input.https_uri, scheme)); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToProxyPerScheme("https", + net::ProxyServer::FromURI(input.https_uri, MK_SCHM(HTTPS)))); } if (input.ftp_uri) { - config_service()->UISetProxyConfigToProxyPerScheme( - "ftp", - net::ProxyServer::FromURI(input.ftp_uri, scheme)); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToProxyPerScheme("ftp", + net::ProxyServer::FromURI(input.ftp_uri, MK_SCHM(HTTP)))); } if (input.socks_uri) { - config_service()->UISetProxyConfigToProxyPerScheme( - "socks", - net::ProxyServer::FromURI(input.socks_uri, scheme)); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigToProxyPerScheme("socks", + net::ProxyServer::FromURI(input.socks_uri, MK_SCHM(SOCKS4)))); } if (input.bypass_rules) { bypass_rules.ParseFromStringUsingSuffixMatching(input.bypass_rules); - config_service()->UISetProxyConfigBypassRules(bypass_rules); + EXPECT_EQ(tests[i].is_valid, + config_service()->UISetProxyConfigBypassRules(bypass_rules)); } break; } @@ -472,26 +451,29 @@ TEST_F(ProxyConfigServiceImplTest, ModifyFromUI) { ProxyConfigServiceImpl::ProxyConfig ui_config; config_service()->UIGetProxyConfig(&ui_config); EXPECT_EQ(input.mode, ui_config.mode); - if (input.pac_url) - EXPECT_EQ(GURL(input.pac_url), ui_config.automatic_proxy.pac_url); - const net::ProxyRulesExpectation& proxy_rules = tests[i].proxy_rules; - if (input.single_uri) - EXPECT_EQ(proxy_rules.single_proxy, - ui_config.single_proxy.server.ToURI()); - if (input.http_uri) - EXPECT_EQ(proxy_rules.proxy_for_http, - ui_config.http_proxy.server.ToURI()); - if (input.https_uri) - EXPECT_EQ(proxy_rules.proxy_for_https, - ui_config.https_proxy.server.ToURI()); - if (input.ftp_uri) - EXPECT_EQ(proxy_rules.proxy_for_ftp, ui_config.ftp_proxy.server.ToURI()); - if (input.socks_uri) { - EXPECT_EQ(proxy_rules.fallback_proxy, - ui_config.socks_proxy.server.ToURI()); + if (tests[i].is_valid) { + if (input.pac_url) + EXPECT_EQ(GURL(input.pac_url), ui_config.automatic_proxy.pac_url); + const net::ProxyRulesExpectation& proxy_rules = tests[i].proxy_rules; + if (input.single_uri) + EXPECT_EQ(proxy_rules.single_proxy, + ui_config.single_proxy.server.ToURI()); + if (input.http_uri) + EXPECT_EQ(proxy_rules.proxy_for_http, + ui_config.http_proxy.server.ToURI()); + if (input.https_uri) + EXPECT_EQ(proxy_rules.proxy_for_https, + ui_config.https_proxy.server.ToURI()); + if (input.ftp_uri) + EXPECT_EQ(proxy_rules.proxy_for_ftp, + ui_config.ftp_proxy.server.ToURI()); + if (input.socks_uri) { + EXPECT_EQ(proxy_rules.fallback_proxy, + ui_config.socks_proxy.server.ToURI()); + } + if (input.bypass_rules) + EXPECT_TRUE(bypass_rules.Equals(ui_config.bypass_rules)); } - if (input.bypass_rules) - EXPECT_TRUE(bypass_rules.Equals(ui_config.bypass_rules)); } } @@ -529,8 +511,8 @@ TEST_F(ProxyConfigServiceImplTest, ProxyChangedObserver) { ProxyChangedObserver observer(config_service()); // Set to pac script from UI. - config_service()->UISetProxyConfigToPACScript(GURL("http://wpad.dat")); - + EXPECT_TRUE(config_service()->UISetProxyConfigToPACScript( + GURL("http://wpad.dat"))); // Retrieve config from IO thread. net::ProxyConfig io_config; SyncGetLatestProxyConfig(&io_config); @@ -539,4 +521,42 @@ TEST_F(ProxyConfigServiceImplTest, ProxyChangedObserver) { EXPECT_TRUE(io_config.Equals(observer.config())); } +TEST_F(ProxyConfigServiceImplTest, SerializeAndDeserialize) { + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + if (!tests[i].is_valid) + continue; + SCOPED_TRACE(StringPrintf("Test[%" PRIuS "] %s", i, + tests[i].description.c_str())); + + ProxyConfigServiceImpl::ProxyConfig source_config; + InitConfigWithTestInput(tests[i].input, &source_config); + + // Serialize source_config into std::string. + std::string serialized_value; + EXPECT_TRUE(source_config.Serialize(&serialized_value)); + + // Deserialize std:string into target_config. + ProxyConfigServiceImpl::ProxyConfig target_config; + EXPECT_TRUE(target_config.Deserialize(serialized_value)); + + // Compare the configs after serialization and deserialization. + net::ProxyConfig net_src_cfg; + net::ProxyConfig net_tgt_cfg; + source_config.ToNetProxyConfig(&net_src_cfg); + target_config.ToNetProxyConfig(&net_tgt_cfg); +#if !defined(NDEBUG) + if (!net_src_cfg.Equals(net_tgt_cfg)) { + std::string src_output, tgt_output; + JSONStringValueSerializer src_serializer(&src_output); + src_serializer.Serialize(*net_src_cfg.ToValue()); + JSONStringValueSerializer tgt_serializer(&tgt_output); + tgt_serializer.Serialize(*net_tgt_cfg.ToValue()); + LOG(INFO) << "source:\n" << src_output; + LOG(INFO) << "target:\n" << tgt_output; + } +#endif // !defined(NDEBUG) + EXPECT_TRUE(net_src_cfg.Equals(net_tgt_cfg)); + } +} + } // namespace chromeos diff --git a/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc b/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc index 158b3e8..f7a6f1c 100644 --- a/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc +++ b/chrome/browser/chromeos/status/clock_menu_button_browsertest.cc @@ -25,6 +25,11 @@ namespace chromeos { class ClockMenuButtonTest : public InProcessBrowserTest { protected: ClockMenuButtonTest() : InProcessBrowserTest() {} + virtual void SetUpInProcessBrowserTestFixture() { + // This test requires actual libcros, but InProcessBrowserTest has set + // to use stub, so reset it here. + CrosLibrary::Get()->GetTestApi()->ResetUseStubImpl(); + } ClockMenuButton* GetClockMenuButton() { BrowserView* view = static_cast<BrowserView*>(browser()->window()); return static_cast<StatusAreaView*>(view-> |