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 34 namespace chromeos { 35 namespace options { 36 37 namespace { 38 39 // List of settings that should be changeable by all users. 40 const char* kNonPrivilegedSettings[] = { 41 kSystemTimezone 42 }; 43 44 // Returns true if |pref| can be controlled (e.g. by policy or owner). 45 bool 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. 52 base::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. 67 base::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 86 const char kSelectNetworkMessage[] = "selectNetwork"; 87 88 } // namespace 89 90 CoreChromeOSOptionsHandler::CoreChromeOSOptionsHandler() { 91 notification_registrar_.Add(this, 92 chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED, 93 content::NotificationService::AllSources()); 94 } 95 96 CoreChromeOSOptionsHandler::~CoreChromeOSOptionsHandler() { 97 } 98 99 void CoreChromeOSOptionsHandler::RegisterMessages() { 100 CoreOptionsHandler::RegisterMessages(); 101 web_ui()->RegisterMessageCallback( 102 kSelectNetworkMessage, 103 base::Bind(&CoreChromeOSOptionsHandler::SelectNetworkCallback, 104 base::Unretained(this))); 105 } 106 107 void 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 128 void 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 142 void 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 149 base::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 194 void 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 211 void 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 231 void 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 241 base::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 282 void 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 337 void 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 349 void 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 369 void 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 378 void 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