proxy_config_service_impl.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1// Copyright (c) 2012 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 "chrome/browser/chromeos/proxy_config_service_impl.h"
6
7#include <ostream>
8
9#include "base/bind.h"
10#include "base/json/json_string_value_serializer.h"
11#include "base/logging.h"
12#include "base/prefs/pref_registry_simple.h"
13#include "base/prefs/pref_service.h"
14#include "base/string_util.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/chromeos/cros/cros_library.h"
17#include "chrome/browser/chromeos/cros/network_property_ui_data.h"
18#include "chrome/browser/chromeos/login/user_manager.h"
19#include "chrome/browser/policy/browser_policy_connector.h"
20#include "chrome/browser/policy/cloud/cloud_policy_constants.h"
21#include "chrome/browser/prefs/proxy_config_dictionary.h"
22#include "chrome/browser/prefs/proxy_prefs.h"
23#include "chrome/browser/profiles/profile_manager.h"
24#include "chrome/common/chrome_notification_types.h"
25#include "chrome/common/pref_names.h"
26#include "chromeos/network/network_ui_data.h"
27#include "chromeos/network/onc/onc_constants.h"
28#include "components/user_prefs/pref_registry_syncable.h"
29#include "content/public/browser/notification_service.h"
30#include "grit/generated_resources.h"
31#include "ui/base/l10n/l10n_util.h"
32
33namespace chromeos {
34
35namespace {
36
37// Shoud we try to push this to base?
38// Helper comparator functor for the find_if call in |findIfEqual|
39template <class T>
40class EqualsComparator{
41 public:
42  explicit EqualsComparator(const T& key) : key_(key) { }
43  bool operator() (const T& element) {
44    return element.Equals(key_);
45  }
46 private:
47  const T& key_;
48};
49
50// Tiny STL helper function to allow using the find_if syntax on objects that
51// doesn't use the operator== but implement the Equals function which is the
52// quasi standard with the coding style we have.
53template<class InputIterator, class T>
54InputIterator findIfEqual(InputIterator first, InputIterator last,
55                          const T& key) {
56  return std::find_if(first, last, EqualsComparator<T>(key));
57}
58
59const char* ModeToString(ProxyConfigServiceImpl::ProxyConfig::Mode mode) {
60  switch (mode) {
61    case ProxyConfigServiceImpl::ProxyConfig::MODE_DIRECT:
62      return "direct";
63    case ProxyConfigServiceImpl::ProxyConfig::MODE_AUTO_DETECT:
64      return "auto-detect";
65    case ProxyConfigServiceImpl::ProxyConfig::MODE_PAC_SCRIPT:
66      return "pacurl";
67    case ProxyConfigServiceImpl::ProxyConfig::MODE_SINGLE_PROXY:
68      return "single-proxy";
69    case ProxyConfigServiceImpl::ProxyConfig::MODE_PROXY_PER_SCHEME:
70      return "proxy-per-scheme";
71  }
72  NOTREACHED() << "Unrecognized mode type";
73  return "";
74}
75
76const char* ConfigStateToString(ProxyPrefs::ConfigState state) {
77  switch (state) {
78    case ProxyPrefs::CONFIG_POLICY:
79      return "config_policy";
80    case ProxyPrefs::CONFIG_EXTENSION:
81      return "config_extension";
82    case ProxyPrefs::CONFIG_OTHER_PRECEDE:
83      return "config_other_precede";
84    case ProxyPrefs::CONFIG_SYSTEM:
85      return "config_network";  // For ChromeOS, system is network.
86    case ProxyPrefs::CONFIG_FALLBACK:
87      return "config_recommended";  // Fallback is recommended.
88    case ProxyPrefs::CONFIG_UNSET:
89      return "config_unset";
90  }
91  NOTREACHED() << "Unrecognized config state type";
92  return "";
93}
94
95// Returns true if proxy settings from |network| is editable.
96bool IsNetworkProxySettingsEditable(const Network* network) {
97  if (!network)
98    return true;  // editable if no network given.
99
100  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
101  const base::DictionaryValue* onc =
102       network_library->FindOncForNetwork(network->unique_id());
103
104  NetworkPropertyUIData proxy_settings_ui_data;
105  proxy_settings_ui_data.ParseOncProperty(
106      network->ui_data().onc_source(),
107      onc,
108      onc::network_config::kProxySettings);
109  return proxy_settings_ui_data.IsEditable();
110}
111
112}  // namespace
113
114//----------- ProxyConfigServiceImpl::ProxyConfig: public methods --------------
115
116ProxyConfigServiceImpl::ProxyConfig::ProxyConfig()
117    : mode(MODE_DIRECT),
118      state(ProxyPrefs::CONFIG_UNSET),
119      user_modifiable(true) {}
120
121ProxyConfigServiceImpl::ProxyConfig::~ProxyConfig() {}
122
123bool ProxyConfigServiceImpl::ProxyConfig::FromNetProxyConfig(
124    const net::ProxyConfig& net_config) {
125  *this = ProxyConfigServiceImpl::ProxyConfig();  // Reset to default.
126  const net::ProxyConfig::ProxyRules& rules = net_config.proxy_rules();
127  switch (rules.type) {
128    case net::ProxyConfig::ProxyRules::TYPE_NO_RULES:
129      if (!net_config.HasAutomaticSettings()) {
130        mode = ProxyConfig::MODE_DIRECT;
131      } else if (net_config.auto_detect()) {
132        mode = ProxyConfig::MODE_AUTO_DETECT;
133      } else if (net_config.has_pac_url()) {
134        mode = ProxyConfig::MODE_PAC_SCRIPT;
135        automatic_proxy.pac_url = net_config.pac_url();
136      } else {
137        return false;
138      }
139      return true;
140    case net::ProxyConfig::ProxyRules::TYPE_SINGLE_PROXY:
141      if (rules.single_proxies.IsEmpty())
142        return false;
143      mode = MODE_SINGLE_PROXY;
144      single_proxy.server = rules.single_proxies.Get();
145      bypass_rules = rules.bypass_rules;
146      return true;
147    case net::ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME:
148      // Make sure we have valid server for at least one of the protocols.
149      if (rules.proxies_for_http.IsEmpty() &&
150          rules.proxies_for_https.IsEmpty() &&
151          rules.proxies_for_ftp.IsEmpty() &&
152          rules.fallback_proxies.IsEmpty()) {
153        return false;
154      }
155      mode = MODE_PROXY_PER_SCHEME;
156      if (!rules.proxies_for_http.IsEmpty())
157        http_proxy.server = rules.proxies_for_http.Get();
158      if (!rules.proxies_for_https.IsEmpty())
159        https_proxy.server = rules.proxies_for_https.Get();
160      if (!rules.proxies_for_ftp.IsEmpty())
161        ftp_proxy.server = rules.proxies_for_ftp.Get();
162      if (!rules.fallback_proxies.IsEmpty())
163        socks_proxy.server = rules.fallback_proxies.Get();
164      bypass_rules = rules.bypass_rules;
165      return true;
166    default:
167      NOTREACHED() << "Unrecognized proxy config mode";
168      break;
169  }
170  return false;
171}
172
173DictionaryValue* ProxyConfigServiceImpl::ProxyConfig::ToPrefProxyConfig() {
174  switch (mode) {
175    case MODE_DIRECT: {
176      return ProxyConfigDictionary::CreateDirect();
177    }
178    case MODE_AUTO_DETECT: {
179      return ProxyConfigDictionary::CreateAutoDetect();
180    }
181    case MODE_PAC_SCRIPT: {
182      return ProxyConfigDictionary::CreatePacScript(
183          automatic_proxy.pac_url.spec(), false);
184    }
185    case MODE_SINGLE_PROXY: {
186      std::string spec;
187      if (single_proxy.server.is_valid())
188        spec = single_proxy.server.ToURI();
189      return ProxyConfigDictionary::CreateFixedServers(
190          spec, bypass_rules.ToString());
191    }
192    case MODE_PROXY_PER_SCHEME: {
193      std::string spec;
194      EncodeAndAppendProxyServer("http", http_proxy.server, &spec);
195      EncodeAndAppendProxyServer("https", https_proxy.server, &spec);
196      EncodeAndAppendProxyServer("ftp", ftp_proxy.server, &spec);
197      EncodeAndAppendProxyServer("socks", socks_proxy.server, &spec);
198      return ProxyConfigDictionary::CreateFixedServers(
199          spec, bypass_rules.ToString());
200    }
201    default:
202      break;
203  }
204  NOTREACHED() << "Unrecognized proxy config mode for preference";
205  return NULL;
206}
207
208ProxyConfigServiceImpl::ProxyConfig::ManualProxy*
209    ProxyConfigServiceImpl::ProxyConfig::MapSchemeToProxy(
210        const std::string& scheme) {
211  if (scheme == "http")
212    return &http_proxy;
213  if (scheme == "https")
214    return &https_proxy;
215  if (scheme == "ftp")
216    return &ftp_proxy;
217  if (scheme == "socks")
218    return &socks_proxy;
219  NOTREACHED() << "Invalid scheme: " << scheme;
220  return NULL;
221}
222
223bool ProxyConfigServiceImpl::ProxyConfig::SerializeForNetwork(
224    std::string* output) {
225  scoped_ptr<DictionaryValue> proxy_dict_ptr(ToPrefProxyConfig());
226  if (!proxy_dict_ptr.get())
227    return false;
228
229  // Return empty string for direct mode for portal check to work correctly.
230  DictionaryValue *dict = proxy_dict_ptr.get();
231  ProxyConfigDictionary proxy_dict(dict);
232  ProxyPrefs::ProxyMode mode;
233  if (proxy_dict.GetMode(&mode)) {
234    if (mode == ProxyPrefs::MODE_DIRECT) {
235      output->clear();
236      return true;
237    }
238  }
239  JSONStringValueSerializer serializer(output);
240  return serializer.Serialize(*dict);
241}
242
243//----------- ProxyConfigServiceImpl::ProxyConfig: private methods -------------
244
245// static
246void ProxyConfigServiceImpl::ProxyConfig::EncodeAndAppendProxyServer(
247    const std::string& url_scheme,
248    const net::ProxyServer& server,
249    std::string* spec) {
250  if (!server.is_valid())
251    return;
252
253  if (!spec->empty())
254    *spec += ';';
255
256  if (!url_scheme.empty()) {
257    *spec += url_scheme;
258    *spec += "=";
259  }
260  *spec += server.ToURI();
261}
262
263//------------------- ProxyConfigServiceImpl: public methods -------------------
264
265ProxyConfigServiceImpl::ProxyConfigServiceImpl(PrefService* pref_service)
266    : PrefProxyConfigTrackerImpl(pref_service),
267      active_config_state_(ProxyPrefs::CONFIG_UNSET),
268      pointer_factory_(this) {
269
270  // Register for notifications of UseSharedProxies user preference.
271  if (pref_service->FindPreference(prefs::kUseSharedProxies)) {
272    use_shared_proxies_.Init(
273        prefs::kUseSharedProxies, pref_service,
274        base::Bind(&ProxyConfigServiceImpl::OnUseSharedProxiesChanged,
275                   base::Unretained(this)));
276  }
277
278  // Register for shill network notifications.
279  NetworkLibrary* network_lib = CrosLibrary::Get()->GetNetworkLibrary();
280  OnActiveNetworkChanged(network_lib, network_lib->active_network());
281  network_lib->AddNetworkManagerObserver(this);
282}
283
284ProxyConfigServiceImpl::~ProxyConfigServiceImpl() {
285  NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
286  if (netlib) {
287    netlib->RemoveNetworkManagerObserver(this);
288    netlib->RemoveObserverForAllNetworks(this);
289  }
290}
291
292void ProxyConfigServiceImpl::UISetCurrentNetwork(
293    const std::string& current_network) {
294  Network* network = CrosLibrary::Get()->GetNetworkLibrary()->FindNetworkByPath(
295      current_network);
296  if (!network) {
297    ResetUICache();
298    LOG(ERROR) << "Can't find requested network " << current_network;
299    return;
300  }
301  current_ui_network_ = current_network;
302  OnUISetCurrentNetwork(network);
303}
304
305void ProxyConfigServiceImpl::UIMakeActiveNetworkCurrent() {
306  Network* network = CrosLibrary::Get()->GetNetworkLibrary()->FindNetworkByPath(
307      active_network_);
308  if (!network) {
309    ResetUICache();
310    LOG(ERROR) << "Can't find requested network " << active_network_;
311    return;
312  }
313  current_ui_network_ = active_network_;
314  OnUISetCurrentNetwork(network);
315}
316
317void ProxyConfigServiceImpl::UIGetCurrentNetworkName(
318    std::string* network_name) {
319  if (!network_name)
320    return;
321  network_name->clear();
322  Network* network = CrosLibrary::Get()->GetNetworkLibrary()->FindNetworkByPath(
323      current_ui_network_);
324  if (!network) {
325    LOG(ERROR) << "Can't find requested network " << current_ui_network_;
326    return;
327  }
328  if (network->name().empty() && network->type() == chromeos::TYPE_ETHERNET) {
329    *network_name =
330        l10n_util::GetStringUTF8(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET);
331  } else {
332    *network_name = network->name();
333  }
334}
335
336void ProxyConfigServiceImpl::UIGetProxyConfig(ProxyConfig* config) {
337  // Simply returns the copy last set from UI via UISetCurrentNetwork or
338  // UIMakeActiveNetworkCurrent.
339  *config = current_ui_config_;
340}
341
342bool ProxyConfigServiceImpl::UISetProxyConfigToDirect() {
343  current_ui_config_.mode = ProxyConfig::MODE_DIRECT;
344  OnUISetProxyConfig();
345  return true;
346}
347
348bool ProxyConfigServiceImpl::UISetProxyConfigToAutoDetect() {
349  current_ui_config_.mode = ProxyConfig::MODE_AUTO_DETECT;
350  OnUISetProxyConfig();
351  return true;
352}
353
354bool ProxyConfigServiceImpl::UISetProxyConfigToPACScript(const GURL& pac_url) {
355  current_ui_config_.mode = ProxyConfig::MODE_PAC_SCRIPT;
356  current_ui_config_.automatic_proxy.pac_url = pac_url;
357  OnUISetProxyConfig();
358  return true;
359}
360
361bool ProxyConfigServiceImpl::UISetProxyConfigToSingleProxy(
362    const net::ProxyServer& server) {
363  current_ui_config_.mode = ProxyConfig::MODE_SINGLE_PROXY;
364  current_ui_config_.single_proxy.server = server;
365  OnUISetProxyConfig();
366  return true;
367}
368
369bool ProxyConfigServiceImpl::UISetProxyConfigToProxyPerScheme(
370    const std::string& scheme, const net::ProxyServer& server) {
371  ProxyConfig::ManualProxy* proxy = current_ui_config_.MapSchemeToProxy(scheme);
372  if (!proxy) {
373    NOTREACHED() << "Cannot set proxy: invalid scheme [" << scheme << "]";
374    return false;
375  }
376  current_ui_config_.mode = ProxyConfig::MODE_PROXY_PER_SCHEME;
377  proxy->server = server;
378  OnUISetProxyConfig();
379  return true;
380}
381
382bool ProxyConfigServiceImpl::UISetProxyConfigBypassRules(
383    const net::ProxyBypassRules& bypass_rules) {
384  if (current_ui_config_.mode != ProxyConfig::MODE_SINGLE_PROXY &&
385      current_ui_config_.mode != ProxyConfig::MODE_PROXY_PER_SCHEME) {
386    NOTREACHED();
387    VLOG(1) << "Cannot set bypass rules for proxy mode ["
388            << current_ui_config_.mode << "]";
389    return false;
390  }
391  current_ui_config_.bypass_rules = bypass_rules;
392  OnUISetProxyConfig();
393  return true;
394}
395
396void ProxyConfigServiceImpl::AddNotificationCallback(base::Closure callback) {
397
398  std::vector<base::Closure>::iterator iter =
399      findIfEqual(callbacks_.begin(), callbacks_.end(), callback);
400  if (iter == callbacks_.end())
401    callbacks_.push_back(callback);
402}
403
404void ProxyConfigServiceImpl::RemoveNotificationCallback(
405    base::Closure callback) {
406  std::vector<base::Closure>::iterator iter =
407      findIfEqual(callbacks_.begin(), callbacks_.end(), callback);
408  if (iter != callbacks_.end())
409    callbacks_.erase(iter);
410}
411
412void ProxyConfigServiceImpl::OnProxyConfigChanged(
413    ProxyPrefs::ConfigState config_state,
414    const net::ProxyConfig& config) {
415  VLOG(1) << "Got prefs change: " << ConfigStateToString(config_state)
416          << ", mode=" << config.proxy_rules().type;
417  Network* network = NULL;
418  if (!active_network_.empty()) {
419    network = CrosLibrary::Get()->GetNetworkLibrary()->FindNetworkByPath(
420        active_network_);
421    if (!network)
422      LOG(ERROR) << "can't find requested network " << active_network_;
423  }
424  DetermineEffectiveConfig(network, true);
425}
426
427void ProxyConfigServiceImpl::OnNetworkManagerChanged(
428    NetworkLibrary* network_lib) {
429  VLOG(1) << "OnNetworkManagerChanged: use-shared-proxies="
430          << GetUseSharedProxies();
431  OnActiveNetworkChanged(network_lib, network_lib->active_network());
432}
433
434void ProxyConfigServiceImpl::OnNetworkChanged(NetworkLibrary* network_lib,
435    const Network* network) {
436  if (!network)
437    return;
438  VLOG(1) << "OnNetworkChanged: "
439          << (network->name().empty() ? network->service_path() :
440                                        network->name())
441          << ", use-shared-proxies=" << GetUseSharedProxies();
442  // We only care about active network.
443  if (network == network_lib->active_network())
444    OnActiveNetworkChanged(network_lib, network);
445}
446
447// static
448bool ProxyConfigServiceImpl::ParseProxyConfig(
449    const std::string& proxy_config_string,
450    net::ProxyConfig* proxy_config) {
451  if (!proxy_config)
452    return false;
453  JSONStringValueSerializer serializer(proxy_config_string);
454  scoped_ptr<Value> value(serializer.Deserialize(NULL, NULL));
455  if (!value.get() || value->GetType() != Value::TYPE_DICTIONARY)
456    return false;
457  ProxyConfigDictionary proxy_dict(static_cast<DictionaryValue*>(value.get()));
458  return PrefProxyConfigTrackerImpl::PrefConfigToNetConfig(proxy_dict,
459                                                           proxy_config);
460}
461
462// static
463void ProxyConfigServiceImpl::RegisterPrefs(PrefRegistrySimple* registry) {
464  // Use shared proxies default to off.  GetUseSharedProxies will return the
465  // correct value based on pre-login and login.
466  registry->RegisterBooleanPref(prefs::kUseSharedProxies, true);
467}
468
469// static
470void ProxyConfigServiceImpl::RegisterUserPrefs(
471    user_prefs::PrefRegistrySyncable* registry) {
472  registry->RegisterBooleanPref(
473      prefs::kUseSharedProxies,
474      true,
475      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
476}
477
478//------------------ ProxyConfigServiceImpl: private methods -------------------
479
480void ProxyConfigServiceImpl::OnUseSharedProxiesChanged() {
481  VLOG(1) << "New use-shared-proxies = " << GetUseSharedProxies();
482
483  // Determine new proxy config which may have changed because of new
484  // use-shared-proxies. If necessary, activate it.
485  Network* network = NULL;
486  if (!active_network_.empty()) {
487    network = CrosLibrary::Get()->GetNetworkLibrary()->FindNetworkByPath(
488        active_network_);
489    if (!network)
490      LOG(WARNING) << "Can't find requested network " << active_network_;
491  }
492  DetermineEffectiveConfig(network, true);
493}
494
495void ProxyConfigServiceImpl::OnUISetProxyConfig() {
496  if (current_ui_network_.empty())
497    return;
498  // Update config to shill.
499  std::string value;
500  if (current_ui_config_.SerializeForNetwork(&value)) {
501    VLOG(1) << "Set proxy (mode=" << current_ui_config_.mode
502            << ") for " << current_ui_network_;
503    current_ui_config_.state = ProxyPrefs::CONFIG_SYSTEM;
504    SetProxyConfigForNetwork(current_ui_network_, value, false);
505  }
506}
507
508void ProxyConfigServiceImpl::OnActiveNetworkChanged(NetworkLibrary* network_lib,
509    const Network* active_network) {
510  std::string new_network;
511  if (active_network)
512    new_network = active_network->service_path();
513
514  if (active_network_ == new_network) {  // Same active network.
515    VLOG(1) << "Same active network: "
516            << (new_network.empty() ? "empty" :
517                    (active_network->name().empty() ?
518                         new_network : active_network->name()));
519    // Even though network is the same, its proxy config (e.g. if private
520    // version of network replaces the shared version after login), or
521    // use-shared-proxies setting (e.g. after login) may have changed,
522    // so re-determine effective proxy config, and activate if different.
523    if (active_network) {
524      VLOG(1) << "Profile=" << active_network->profile_type()
525              << "," << active_network->profile_path()
526              << ", proxy=" << active_network->proxy_config();
527      DetermineEffectiveConfig(active_network, true);
528    }
529    return;
530  }
531
532  // If there was a previous active network, remove it as observer.
533  if (!active_network_.empty())
534    network_lib->RemoveNetworkObserver(active_network_, this);
535
536  active_network_ = new_network;
537
538  if (active_network_.empty()) {
539    VLOG(1) << "New active network: empty";
540    DetermineEffectiveConfig(active_network, true);
541    return;
542  }
543
544  VLOG(1) << "New active network: path=" << active_network->service_path()
545          << ", name=" << active_network->name()
546          << ", profile=" << active_network->profile_type()
547          << "," << active_network->profile_path()
548          << ", proxy=" << active_network->proxy_config();
549
550  // Register observer for new network.
551  network_lib->AddNetworkObserver(active_network_, this);
552
553  // Determine and activate possibly new effective proxy config.
554  DetermineEffectiveConfig(active_network, true);
555}
556
557void ProxyConfigServiceImpl::SetProxyConfigForNetwork(
558    const std::string& network_path, const std::string& value,
559    bool only_set_if_empty) {
560  Network* network = CrosLibrary::Get()->GetNetworkLibrary()->FindNetworkByPath(
561      network_path);
562  if (!network) {
563    NOTREACHED() << "Can't find requested network " << network_path;
564    return;
565  }
566  if (!only_set_if_empty || network->proxy_config().empty()) {
567    network->SetProxyConfig(value);
568    VLOG(1) << "Set proxy for "
569            << (network->name().empty() ? network_path : network->name())
570            << ", value=" << value;
571    if (network_path == active_network_)
572      DetermineEffectiveConfig(network, true);
573  }
574}
575
576bool ProxyConfigServiceImpl::GetUseSharedProxies() {
577  const PrefService::Preference* use_shared_proxies_pref =
578      prefs()->FindPreference(prefs::kUseSharedProxies);
579  if (!use_shared_proxies_pref) {
580    // Make sure that proxies are always enabled at sign in screen.
581    return !UserManager::Get()->IsUserLoggedIn();
582  }
583  return use_shared_proxies_.GetValue();
584}
585
586bool ProxyConfigServiceImpl::IgnoreProxy(const Network* network) {
587  if (network->profile_type() == PROFILE_USER)
588    return false;
589
590  if (network->ui_data().onc_source() == onc::ONC_SOURCE_DEVICE_POLICY &&
591      UserManager::Get()->IsUserLoggedIn()) {
592    policy::BrowserPolicyConnector* connector =
593        g_browser_process->browser_policy_connector();
594    const User* logged_in_user = UserManager::Get()->GetLoggedInUser();
595    if (connector->GetUserAffiliation(logged_in_user->email()) ==
596            policy::USER_AFFILIATION_MANAGED) {
597      VLOG(1) << "Respecting proxy for network " << network->name()
598              << ", as logged-in user belongs to the domain the device "
599              << "is enrolled to.";
600      return false;
601    }
602  }
603
604  return !GetUseSharedProxies();
605}
606
607void ProxyConfigServiceImpl::DetermineEffectiveConfig(const Network* network,
608                                                      bool activate) {
609  // Get prefs proxy config if available.
610  net::ProxyConfig pref_config;
611  ProxyPrefs::ConfigState pref_state = GetProxyConfig(&pref_config);
612
613  // Get network proxy config if available.
614  net::ProxyConfig network_config;
615  net::ProxyConfigService::ConfigAvailability network_availability =
616      net::ProxyConfigService::CONFIG_UNSET;
617  bool ignore_proxy = activate;
618  if (network) {
619    // If we're activating proxy, ignore proxy if necessary;
620    // otherwise, for ui, get actual proxy to show user.
621    ignore_proxy = activate ? IgnoreProxy(network) : false;
622    // If network is shared but use-shared-proxies is off, use direct mode.
623    if (ignore_proxy) {
624      VLOG(1) << "Shared network && !use-shared-proxies, use direct";
625      network_availability = net::ProxyConfigService::CONFIG_VALID;
626    } else if (!network->proxy_config().empty()) {
627      // Network is private or shared with user using shared proxies.
628      if (ParseProxyConfig(network->proxy_config(), &network_config)) {
629        VLOG(1) << this << ": using network proxy: "
630                << network->proxy_config();
631        network_availability = net::ProxyConfigService::CONFIG_VALID;
632      }
633    }
634  }
635
636  // Determine effective proxy config, either from prefs or network.
637  ProxyPrefs::ConfigState effective_config_state;
638  net::ProxyConfig effective_config;
639  GetEffectiveProxyConfig(pref_state, pref_config,
640                          network_availability, network_config, ignore_proxy,
641                          &effective_config_state, &effective_config);
642
643  // Determine if we should activate effective proxy and which proxy config to
644  // store it.
645  if (activate) {  // Activate effective proxy and store into |active_config_|.
646    // If last update didn't complete, we definitely update now.
647    bool update_now = update_pending();
648    if (!update_now) {  // Otherwise, only update now if there're changes.
649      update_now = active_config_state_ != effective_config_state ||
650          (active_config_state_ != ProxyPrefs::CONFIG_UNSET &&
651           !active_config_.Equals(effective_config));
652    }
653    if (update_now) {  // Activate and store new effective config.
654      active_config_state_ = effective_config_state;
655      if (active_config_state_ != ProxyPrefs::CONFIG_UNSET)
656        active_config_ = effective_config;
657      // If effective config is from system (i.e. network), it's considered a
658      // special kind of prefs that ranks below policy/extension but above
659      // others, so bump it up to CONFIG_OTHER_PRECEDE to force its precedence
660      // when PrefProxyConfigTrackerImpl pushes it to ChromeProxyConfigService.
661      if (effective_config_state == ProxyPrefs::CONFIG_SYSTEM)
662        effective_config_state = ProxyPrefs::CONFIG_OTHER_PRECEDE;
663      // If config is manual, add rule to bypass local host.
664      if (effective_config.proxy_rules().type !=
665          net::ProxyConfig::ProxyRules::TYPE_NO_RULES)
666        effective_config.proxy_rules().bypass_rules.AddRuleToBypassLocal();
667      PrefProxyConfigTrackerImpl::OnProxyConfigChanged(effective_config_state,
668                                                       effective_config);
669      if (VLOG_IS_ON(1) && !update_pending()) {  // Update was successful.
670        scoped_ptr<DictionaryValue> config_dict(static_cast<DictionaryValue*>(
671            effective_config.ToValue()));
672        std::string config_value;
673        JSONStringValueSerializer serializer(&config_value);
674        serializer.Serialize(*config_dict.get());
675        VLOG(1) << this << ": Proxy changed: "
676                << ConfigStateToString(active_config_state_)
677                << ", " << config_value;
678      }
679    }
680  } else {  // For UI, store effective proxy into |current_ui_config_|.
681    current_ui_config_.FromNetProxyConfig(effective_config);
682    current_ui_config_.state = effective_config_state;
683    if (PrefPrecedes(effective_config_state)) {
684      current_ui_config_.user_modifiable = false;
685    } else if (!IsNetworkProxySettingsEditable(network)) {
686      // TODO(xiyuan): Figure out the right way to set config state for managed
687      // network.
688      current_ui_config_.state = ProxyPrefs::CONFIG_POLICY;
689      current_ui_config_.user_modifiable = false;
690    } else {
691      current_ui_config_.user_modifiable = !network || !IgnoreProxy(network);
692    }
693  }
694}
695
696void ProxyConfigServiceImpl::OnUISetCurrentNetwork(const Network* network) {
697  DetermineEffectiveConfig(network, false);
698  VLOG(1) << "Current ui network: "
699          << (network->name().empty() ? current_ui_network_ : network->name())
700          << ", " << ModeToString(current_ui_config_.mode)
701          << ", " << ConfigStateToString(current_ui_config_.state)
702          << ", modifiable:" << current_ui_config_.user_modifiable;
703  // Notify whoever is interested in this change.
704  std::vector<base::Closure>::iterator iter = callbacks_.begin();
705  while (iter != callbacks_.end()) {
706    if (iter->is_null()) {
707      iter = callbacks_.erase(iter);
708    } else {
709      iter->Run();
710      ++iter;
711    }
712  }
713}
714
715void ProxyConfigServiceImpl::ResetUICache() {
716  current_ui_network_.clear();
717  current_ui_config_ = ProxyConfig();
718}
719
720}  // namespace chromeos
721