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/ui/webui/options/chromeos/core_chromeos_options_handler.h"
6
7#include <string>
8
9#include "ash/session/session_state_delegate.h"
10#include "ash/shell.h"
11#include "base/bind.h"
12#include "base/prefs/pref_change_registrar.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/strings/string_util.h"
15#include "base/strings/utf_string_conversions.h"
16#include "base/sys_info.h"
17#include "chrome/browser/browser_process.h"
18#include "chrome/browser/chrome_notification_types.h"
19#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
20#include "chrome/browser/chromeos/profiles/profile_helper.h"
21#include "chrome/browser/chromeos/proxy_cros_settings_parser.h"
22#include "chrome/browser/chromeos/settings/cros_settings.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
25#include "chrome/browser/ui/webui/options/chromeos/accounts_options_handler.h"
26#include "chrome/common/pref_names.h"
27#include "chrome/grit/generated_resources.h"
28#include "components/user_manager/user_manager.h"
29#include "content/public/browser/notification_service.h"
30#include "content/public/browser/user_metrics.h"
31#include "content/public/browser/web_ui.h"
32#include "ui/base/l10n/l10n_util.h"
33
34namespace chromeos {
35namespace options {
36
37namespace {
38
39// List of settings that should be changeable by all users.
40const char* kNonPrivilegedSettings[] = {
41    kSystemTimezone
42};
43
44// Returns true if |pref| can be controlled (e.g. by policy or owner).
45bool IsSettingPrivileged(const std::string& pref) {
46  const char** end = kNonPrivilegedSettings + arraysize(kNonPrivilegedSettings);
47  return std::find(kNonPrivilegedSettings, end, pref) == end;
48}
49
50// Creates a user info dictionary to be stored in the |ListValue| that is
51// passed to Javascript for the |kAccountsPrefUsers| preference.
52base::DictionaryValue* CreateUserInfo(const std::string& username,
53                                      const std::string& display_email,
54                                      const std::string& display_name) {
55  base::DictionaryValue* user_dict = new base::DictionaryValue;
56  user_dict->SetString("username", username);
57  user_dict->SetString("name", display_email);
58  user_dict->SetString("email", display_name);
59
60  bool is_owner = user_manager::UserManager::Get()->GetOwnerEmail() == username;
61  user_dict->SetBoolean("owner", is_owner);
62  return user_dict;
63}
64
65// This function decorates the bare list of emails with some more information
66// needed by the UI to properly display the Accounts page.
67base::Value* CreateUsersWhitelist(const base::Value *pref_value) {
68  const base::ListValue* list_value =
69      static_cast<const base::ListValue*>(pref_value);
70  base::ListValue* user_list = new base::ListValue();
71  user_manager::UserManager* user_manager = user_manager::UserManager::Get();
72
73  for (base::ListValue::const_iterator i = list_value->begin();
74       i != list_value->end(); ++i) {
75    std::string email;
76    if ((*i)->GetAsString(&email)) {
77      // Translate email to the display email.
78      std::string display_email = user_manager->GetUserDisplayEmail(email);
79      // TODO(ivankr): fetch display name for existing users.
80      user_list->Append(CreateUserInfo(email, display_email, std::string()));
81    }
82  }
83  return user_list;
84}
85
86const char kSelectNetworkMessage[] = "selectNetwork";
87
88}  // namespace
89
90CoreChromeOSOptionsHandler::CoreChromeOSOptionsHandler() {
91  notification_registrar_.Add(this,
92                              chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
93                              content::NotificationService::AllSources());
94}
95
96CoreChromeOSOptionsHandler::~CoreChromeOSOptionsHandler() {
97}
98
99void CoreChromeOSOptionsHandler::RegisterMessages() {
100  CoreOptionsHandler::RegisterMessages();
101  web_ui()->RegisterMessageCallback(
102      kSelectNetworkMessage,
103      base::Bind(&CoreChromeOSOptionsHandler::SelectNetworkCallback,
104                 base::Unretained(this)));
105}
106
107void CoreChromeOSOptionsHandler::InitializeHandler() {
108  // This function is both called on the initial page load and on each reload.
109  // For the latter case, forget the last selected network.
110  proxy_config_service_.SetCurrentNetwork(std::string());
111  // And clear the cached configuration.
112  proxy_config_service_.UpdateFromPrefs();
113
114  CoreOptionsHandler::InitializeHandler();
115
116  PrefService* profile_prefs = NULL;
117  Profile* profile = Profile::FromWebUI(web_ui());
118  if (!ProfileHelper::IsSigninProfile(profile)) {
119    profile_prefs = profile->GetPrefs();
120    ObservePref(prefs::kOpenNetworkConfiguration);
121  }
122  ObservePref(prefs::kProxy);
123  ObservePref(prefs::kDeviceOpenNetworkConfiguration);
124  proxy_config_service_.SetPrefs(profile_prefs,
125                                 g_browser_process->local_state());
126}
127
128void CoreChromeOSOptionsHandler::Observe(
129    int type,
130    const content::NotificationSource& source,
131    const content::NotificationDetails& details) {
132  // The only expected notification for now is this one.
133  DCHECK(type == chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED);
134
135  // Finish this asynchronously because the notification has to tricle in to all
136  // Chrome components before we can reliably read the status on the other end.
137  base::MessageLoop::current()->PostTask(FROM_HERE,
138      base::Bind(&CoreChromeOSOptionsHandler::NotifyOwnershipChanged,
139                 base::Unretained(this)));
140}
141
142void CoreChromeOSOptionsHandler::NotifyOwnershipChanged() {
143  for (SubscriptionMap::iterator it = pref_subscription_map_.begin();
144       it != pref_subscription_map_.end(); ++it) {
145    NotifySettingsChanged(it->first);
146  }
147}
148
149base::Value* CoreChromeOSOptionsHandler::FetchPref(
150    const std::string& pref_name) {
151  if (proxy_cros_settings_parser::IsProxyPref(pref_name)) {
152    base::Value *value = NULL;
153    proxy_cros_settings_parser::GetProxyPrefValue(
154        proxy_config_service_, pref_name, &value);
155    if (!value)
156      return base::Value::CreateNullValue();
157
158    return value;
159  }
160
161  if (!CrosSettings::IsCrosSettings(pref_name)) {
162    std::string controlling_pref =
163        pref_name == prefs::kUseSharedProxies ? prefs::kProxy : std::string();
164    return CreateValueForPref(pref_name, controlling_pref);
165  }
166
167  const base::Value* pref_value = CrosSettings::Get()->GetPref(pref_name);
168  if (!pref_value)
169    return base::Value::CreateNullValue();
170
171  // Decorate pref value as CoreOptionsHandler::CreateValueForPref() does.
172  // TODO(estade): seems that this should replicate CreateValueForPref less.
173  base::DictionaryValue* dict = new base::DictionaryValue;
174  if (pref_name == kAccountsPrefUsers)
175    dict->Set("value", CreateUsersWhitelist(pref_value));
176  else
177    dict->Set("value", pref_value->DeepCopy());
178
179  std::string controlled_by;
180  if (IsSettingPrivileged(pref_name)) {
181    policy::BrowserPolicyConnectorChromeOS* connector =
182        g_browser_process->platform_part()->browser_policy_connector_chromeos();
183    if (connector->IsEnterpriseManaged())
184      controlled_by = "policy";
185    else if (!ProfileHelper::IsOwnerProfile(Profile::FromWebUI(web_ui())))
186      controlled_by = "owner";
187  }
188  dict->SetBoolean("disabled", !controlled_by.empty());
189  if (!controlled_by.empty())
190    dict->SetString("controlledBy", controlled_by);
191  return dict;
192}
193
194void CoreChromeOSOptionsHandler::ObservePref(const std::string& pref_name) {
195  if (proxy_cros_settings_parser::IsProxyPref(pref_name)) {
196    // We observe those all the time.
197    return;
198  }
199  if (!CrosSettings::IsCrosSettings(pref_name))
200    return ::options::CoreOptionsHandler::ObservePref(pref_name);
201
202  linked_ptr<CrosSettings::ObserverSubscription> subscription(
203      CrosSettings::Get()->AddSettingsObserver(
204          pref_name.c_str(),
205          base::Bind(&CoreChromeOSOptionsHandler::NotifySettingsChanged,
206                     base::Unretained(this),
207                     pref_name)).release());
208  pref_subscription_map_.insert(make_pair(pref_name, subscription));
209}
210
211void CoreChromeOSOptionsHandler::SetPref(const std::string& pref_name,
212                                         const base::Value* value,
213                                         const std::string& metric) {
214  if (proxy_cros_settings_parser::IsProxyPref(pref_name)) {
215    proxy_cros_settings_parser::SetProxyPrefValue(
216        pref_name, value, &proxy_config_service_);
217    base::StringValue proxy_type(pref_name);
218    web_ui()->CallJavascriptFunction(
219        "options.internet.DetailsInternetPage.updateProxySettings",
220        proxy_type);
221    ProcessUserMetric(value, metric);
222    return;
223  }
224  if (!CrosSettings::IsCrosSettings(pref_name))
225    return ::options::CoreOptionsHandler::SetPref(pref_name, value, metric);
226  CrosSettings::Get()->Set(pref_name, *value);
227
228  ProcessUserMetric(value, metric);
229}
230
231void CoreChromeOSOptionsHandler::StopObservingPref(const std::string& path) {
232  if (proxy_cros_settings_parser::IsProxyPref(path))
233    return;  // We unregister those in the destructor.
234  // Unregister this instance from observing prefs of chrome os settings.
235  if (CrosSettings::IsCrosSettings(path))
236    pref_subscription_map_.erase(path);
237  else  // Call base class to handle regular preferences.
238    ::options::CoreOptionsHandler::StopObservingPref(path);
239}
240
241base::Value* CoreChromeOSOptionsHandler::CreateValueForPref(
242    const std::string& pref_name,
243    const std::string& controlling_pref_name) {
244  // Athena doesn't have ash::Shell and its session_state_delegate, so the
245  // following code will cause crash.
246  // TODO(mukai|antrim): re-enable this after having session_state_delegate.
247  // http://crbug.com/370175
248#if !defined(USE_ATHENA)
249  // The screen lock setting is shared if multiple users are logged in and at
250  // least one has chosen to require passwords.
251  if (pref_name == prefs::kEnableAutoScreenLock &&
252      user_manager::UserManager::Get()->GetLoggedInUsers().size() > 1 &&
253      controlling_pref_name.empty()) {
254    PrefService* user_prefs = Profile::FromWebUI(web_ui())->GetPrefs();
255    const PrefService::Preference* pref =
256        user_prefs->FindPreference(prefs::kEnableAutoScreenLock);
257
258    ash::SessionStateDelegate* delegate =
259        ash::Shell::GetInstance()->session_state_delegate();
260    if (pref && pref->IsUserModifiable() &&
261        delegate->ShouldLockScreenBeforeSuspending()) {
262      bool screen_lock = false;
263      bool success = pref->GetValue()->GetAsBoolean(&screen_lock);
264      DCHECK(success);
265      if (!screen_lock) {
266        // Screen lock is enabled for the session, but not in the user's
267        // preferences. Show the user's value in the checkbox, but indicate
268        // that the password requirement is enabled by some other user.
269        base::DictionaryValue* dict = new base::DictionaryValue;
270        dict->Set("value", pref->GetValue()->DeepCopy());
271        dict->SetString("controlledBy", "shared");
272        return dict;
273      }
274    }
275  }
276#endif
277
278  return CoreOptionsHandler::CreateValueForPref(pref_name,
279                                                controlling_pref_name);
280}
281
282void CoreChromeOSOptionsHandler::GetLocalizedValues(
283    base::DictionaryValue* localized_strings) {
284  DCHECK(localized_strings);
285  CoreOptionsHandler::GetLocalizedValues(localized_strings);
286
287  Profile* profile = Profile::FromWebUI(web_ui());
288  AddAccountUITweaksLocalizedValues(localized_strings, profile);
289
290  user_manager::UserManager* user_manager = user_manager::UserManager::Get();
291
292  // Check at load time whether this is a secondary user in a multi-profile
293  // session.
294  user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(profile);
295  if (user && user->email() != user_manager->GetPrimaryUser()->email()) {
296    const std::string& primary_email = user_manager->GetPrimaryUser()->email();
297
298    // Set secondaryUser to show the shared icon by the network section header.
299    localized_strings->SetBoolean("secondaryUser", true);
300    localized_strings->SetString("secondaryUserBannerText",
301        l10n_util::GetStringFUTF16(
302            IDS_OPTIONS_SETTINGS_SECONDARY_USER_BANNER,
303            base::ASCIIToUTF16(primary_email)));
304    localized_strings->SetString("controlledSettingShared",
305        l10n_util::GetStringFUTF16(
306            IDS_OPTIONS_CONTROLLED_SETTING_SHARED,
307            base::ASCIIToUTF16(primary_email)));
308    localized_strings->SetString("controlledSettingsShared",
309        l10n_util::GetStringFUTF16(
310            IDS_OPTIONS_CONTROLLED_SETTINGS_SHARED,
311            base::ASCIIToUTF16(primary_email)));
312  } else {
313    localized_strings->SetBoolean("secondaryUser", false);
314    localized_strings->SetString("secondaryUserBannerText", base::string16());
315    localized_strings->SetString("controlledSettingShared", base::string16());
316    localized_strings->SetString("controlledSettingsShared", base::string16());
317  }
318
319  // Screen lock icon can show up as primary or secondary user.
320  localized_strings->SetString("screenLockShared",
321      l10n_util::GetStringUTF16(
322          IDS_OPTIONS_CONTROLLED_SETTING_SHARED_SCREEN_LOCK));
323
324  policy::BrowserPolicyConnectorChromeOS* connector =
325      g_browser_process->platform_part()->browser_policy_connector_chromeos();
326  if (connector->IsEnterpriseManaged()) {
327    // Managed machines have no "owner".
328    localized_strings->SetString("controlledSettingOwner", base::string16());
329  } else {
330    localized_strings->SetString("controlledSettingOwner",
331        l10n_util::GetStringFUTF16(
332            IDS_OPTIONS_CONTROLLED_SETTING_OWNER,
333            base::ASCIIToUTF16(user_manager->GetOwnerEmail())));
334  }
335}
336
337void CoreChromeOSOptionsHandler::SelectNetworkCallback(
338    const base::ListValue* args) {
339  std::string service_path;
340  if (args->GetSize() != 1 ||
341      !args->GetString(0, &service_path)) {
342    NOTREACHED();
343    return;
344  }
345  proxy_config_service_.SetCurrentNetwork(service_path);
346  NotifyProxyPrefsChanged();
347}
348
349void CoreChromeOSOptionsHandler::OnPreferenceChanged(
350    PrefService* service,
351    const std::string& pref_name) {
352  // Redetermine the current proxy settings and notify the UI if any of these
353  // preferences change.
354  if (pref_name == prefs::kOpenNetworkConfiguration ||
355      pref_name == prefs::kDeviceOpenNetworkConfiguration ||
356      pref_name == prefs::kProxy) {
357    NotifyProxyPrefsChanged();
358    return;
359  }
360  if (pref_name == prefs::kUseSharedProxies) {
361    // kProxy controls kUseSharedProxies and decides if it's managed by
362    // policy/extension.
363    NotifyPrefChanged(prefs::kUseSharedProxies, prefs::kProxy);
364    return;
365  }
366  ::options::CoreOptionsHandler::OnPreferenceChanged(service, pref_name);
367}
368
369void CoreChromeOSOptionsHandler::NotifySettingsChanged(
370    const std::string& setting_name) {
371  DCHECK(CrosSettings::Get()->IsCrosSettings(setting_name));
372  scoped_ptr<base::Value> value(FetchPref(setting_name));
373  if (!value.get())
374    NOTREACHED();
375  DispatchPrefChangeNotification(setting_name, value.Pass());
376}
377
378void CoreChromeOSOptionsHandler::NotifyProxyPrefsChanged() {
379  proxy_config_service_.UpdateFromPrefs();
380  for (size_t i = 0; i < kProxySettingsCount; ++i) {
381    base::Value* value = NULL;
382    proxy_cros_settings_parser::GetProxyPrefValue(
383        proxy_config_service_, kProxySettings[i], &value);
384    DCHECK(value);
385    scoped_ptr<base::Value> ptr(value);
386    DispatchPrefChangeNotification(kProxySettings[i], ptr.Pass());
387  }
388}
389
390}  // namespace options
391}  // namespace chromeos
392