create_profile_handler.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2013 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/create_profile_handler.h" 6 7#include "base/bind.h" 8#include "base/files/file_path.h" 9#include "base/metrics/histogram.h" 10#include "base/prefs/pref_service.h" 11#include "base/value_conversions.h" 12#include "base/values.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/profiles/profile_manager.h" 15#include "chrome/browser/profiles/profile_metrics.h" 16#include "chrome/browser/profiles/profiles_state.h" 17#include "chrome/browser/supervised_user/supervised_user_registration_utility.h" 18#include "chrome/browser/supervised_user/supervised_user_service.h" 19#include "chrome/browser/supervised_user/supervised_user_service_factory.h" 20#include "chrome/browser/supervised_user/supervised_user_sync_service.h" 21#include "chrome/browser/supervised_user/supervised_user_sync_service_factory.h" 22#include "chrome/browser/sync/profile_sync_service.h" 23#include "chrome/browser/sync/profile_sync_service_factory.h" 24#include "chrome/browser/ui/webui/options/options_handlers_helper.h" 25#include "chrome/common/pref_names.h" 26#include "grit/generated_resources.h" 27#include "ui/base/l10n/l10n_util.h" 28 29namespace options { 30 31CreateProfileHandler::CreateProfileHandler() 32 : profile_creation_type_(NO_CREATION_IN_PROGRESS), 33 weak_ptr_factory_(this) { 34} 35 36CreateProfileHandler::~CreateProfileHandler() { 37 CancelProfileRegistration(false); 38} 39 40void CreateProfileHandler::GetLocalizedValues( 41 base::DictionaryValue* localized_strings) { 42} 43 44void CreateProfileHandler::RegisterMessages() { 45 web_ui()->RegisterMessageCallback( 46 "cancelCreateProfile", 47 base::Bind(&CreateProfileHandler::HandleCancelProfileCreation, 48 base::Unretained(this))); 49 web_ui()->RegisterMessageCallback( 50 "createProfile", 51 base::Bind(&CreateProfileHandler::CreateProfile, 52 base::Unretained(this))); 53} 54 55void CreateProfileHandler::CreateProfile(const base::ListValue* args) { 56 // This handler could have been called for a supervised user, for example 57 // because the user fiddled with the web inspector. Silently return in this 58 // case. 59 Profile* current_profile = Profile::FromWebUI(web_ui()); 60 if (current_profile->IsSupervised()) 61 return; 62 63 if (!profiles::IsMultipleProfilesEnabled()) 64 return; 65 66 // We can have only one in progress profile creation 67 // at any given moment, if new ones are initiated just 68 // ignore them until we are done with the old one. 69 if (profile_creation_type_ != NO_CREATION_IN_PROGRESS) 70 return; 71 72 profile_creation_type_ = NON_SUPERVISED_PROFILE_CREATION; 73 74 DCHECK(profile_path_being_created_.empty()); 75 profile_creation_start_time_ = base::TimeTicks::Now(); 76 77 base::string16 name; 78 base::string16 icon; 79 std::string supervised_user_id; 80 bool create_shortcut = false; 81 bool supervised_user = false; 82 if (args->GetString(0, &name) && args->GetString(1, &icon)) { 83 if (args->GetBoolean(2, &create_shortcut)) { 84 bool success = args->GetBoolean(3, &supervised_user); 85 DCHECK(success); 86 success = args->GetString(4, &supervised_user_id); 87 DCHECK(success); 88 } 89 } 90 91 if (supervised_user) { 92 if (!IsValidExistingSupervisedUserId(supervised_user_id)) 93 return; 94 95 profile_creation_type_ = SUPERVISED_PROFILE_IMPORT; 96 if (supervised_user_id.empty()) { 97 profile_creation_type_ = SUPERVISED_PROFILE_CREATION; 98 supervised_user_id = 99 SupervisedUserRegistrationUtility::GenerateNewSupervisedUserId(); 100 101 // If sync is not yet fully initialized, the creation may take extra time, 102 // so show a message. Import doesn't wait for an acknowledgement, so it 103 // won't have the same potential delay. 104 ProfileSyncService* sync_service = 105 ProfileSyncServiceFactory::GetInstance()->GetForProfile( 106 current_profile); 107 ProfileSyncService::SyncStatusSummary status = 108 sync_service->QuerySyncStatusSummary(); 109 if (status == ProfileSyncService::DATATYPES_NOT_INITIALIZED) { 110 ShowProfileCreationWarning(l10n_util::GetStringUTF16( 111 IDS_PROFILES_CREATE_MANAGED_JUST_SIGNED_IN)); 112 } 113 } 114 } 115 116 ProfileMetrics::LogProfileAddNewUser(ProfileMetrics::ADD_NEW_USER_DIALOG); 117 118 profile_path_being_created_ = ProfileManager::CreateMultiProfileAsync( 119 name, icon, 120 base::Bind(&CreateProfileHandler::OnProfileCreated, 121 weak_ptr_factory_.GetWeakPtr(), 122 create_shortcut, 123 helper::GetDesktopType(web_ui()), 124 supervised_user_id), 125 supervised_user_id); 126} 127 128void CreateProfileHandler::OnProfileCreated( 129 bool create_shortcut, 130 chrome::HostDesktopType desktop_type, 131 const std::string& supervised_user_id, 132 Profile* profile, 133 Profile::CreateStatus status) { 134 if (status != Profile::CREATE_STATUS_CREATED) 135 RecordProfileCreationMetrics(status); 136 137 switch (status) { 138 case Profile::CREATE_STATUS_LOCAL_FAIL: { 139 ShowProfileCreationError(profile, 140 GetProfileCreationErrorMessage(LOCAL_ERROR)); 141 break; 142 } 143 case Profile::CREATE_STATUS_CREATED: { 144 // Do nothing for an intermediate status. 145 break; 146 } 147 case Profile::CREATE_STATUS_INITIALIZED: { 148 HandleProfileCreationSuccess(create_shortcut, desktop_type, 149 supervised_user_id, profile); 150 break; 151 } 152 // User-initiated cancellation is handled in CancelProfileRegistration and 153 // does not call this callback. 154 case Profile::CREATE_STATUS_CANCELED: 155 // Supervised user registration errors are handled in 156 // OnSupervisedUserRegistered(). 157 case Profile::CREATE_STATUS_REMOTE_FAIL: 158 case Profile::MAX_CREATE_STATUS: { 159 NOTREACHED(); 160 break; 161 } 162 } 163} 164 165void CreateProfileHandler::HandleProfileCreationSuccess( 166 bool create_shortcut, 167 chrome::HostDesktopType desktop_type, 168 const std::string& supervised_user_id, 169 Profile* profile) { 170 switch (profile_creation_type_) { 171 case NON_SUPERVISED_PROFILE_CREATION: { 172 DCHECK(supervised_user_id.empty()); 173 CreateShortcutAndShowSuccess(create_shortcut, desktop_type, profile); 174 break; 175 } 176 case SUPERVISED_PROFILE_CREATION: 177 case SUPERVISED_PROFILE_IMPORT: 178 RegisterSupervisedUser(create_shortcut, desktop_type, 179 supervised_user_id, profile); 180 break; 181 case NO_CREATION_IN_PROGRESS: 182 NOTREACHED(); 183 break; 184 } 185} 186 187void CreateProfileHandler::RegisterSupervisedUser( 188 bool create_shortcut, 189 chrome::HostDesktopType desktop_type, 190 const std::string& supervised_user_id, 191 Profile* new_profile) { 192 DCHECK_EQ(profile_path_being_created_.value(), 193 new_profile->GetPath().value()); 194 195 SupervisedUserService* supervised_user_service = 196 SupervisedUserServiceFactory::GetForProfile(new_profile); 197 198 // Register the supervised user using the profile of the custodian. 199 supervised_user_registration_utility_ = 200 SupervisedUserRegistrationUtility::Create(Profile::FromWebUI(web_ui())); 201 supervised_user_service->RegisterAndInitSync( 202 supervised_user_registration_utility_.get(), 203 Profile::FromWebUI(web_ui()), 204 supervised_user_id, 205 base::Bind(&CreateProfileHandler::OnSupervisedUserRegistered, 206 weak_ptr_factory_.GetWeakPtr(), 207 create_shortcut, 208 desktop_type, 209 new_profile)); 210} 211 212void CreateProfileHandler::OnSupervisedUserRegistered( 213 bool create_shortcut, 214 chrome::HostDesktopType desktop_type, 215 Profile* profile, 216 const GoogleServiceAuthError& error) { 217 GoogleServiceAuthError::State state = error.state(); 218 RecordSupervisedProfileCreationMetrics(state); 219 if (state == GoogleServiceAuthError::NONE) { 220 CreateShortcutAndShowSuccess(create_shortcut, desktop_type, profile); 221 return; 222 } 223 224 base::string16 error_msg; 225 if (state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS || 226 state == GoogleServiceAuthError::USER_NOT_SIGNED_UP || 227 state == GoogleServiceAuthError::ACCOUNT_DELETED || 228 state == GoogleServiceAuthError::ACCOUNT_DISABLED) { 229 error_msg = GetProfileCreationErrorMessage(SIGNIN_ERROR); 230 } else { 231 error_msg = GetProfileCreationErrorMessage(REMOTE_ERROR); 232 } 233 ShowProfileCreationError(profile, error_msg); 234} 235 236void CreateProfileHandler::CreateShortcutAndShowSuccess( 237 bool create_shortcut, 238 chrome::HostDesktopType desktop_type, 239 Profile* profile) { 240 if (create_shortcut) { 241 ProfileShortcutManager* shortcut_manager = 242 g_browser_process->profile_manager()->profile_shortcut_manager(); 243 244 if (shortcut_manager) 245 shortcut_manager->CreateProfileShortcut(profile->GetPath()); 246 } 247 248 DCHECK_EQ(profile_path_being_created_.value(), profile->GetPath().value()); 249 profile_path_being_created_.clear(); 250 DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_); 251 base::DictionaryValue dict; 252 dict.SetString("name", 253 profile->GetPrefs()->GetString(prefs::kProfileName)); 254 dict.Set("filePath", base::CreateFilePathValue(profile->GetPath())); 255 bool is_supervised = 256 profile_creation_type_ == SUPERVISED_PROFILE_CREATION || 257 profile_creation_type_ == SUPERVISED_PROFILE_IMPORT; 258 dict.SetBoolean("isManaged", is_supervised); 259 web_ui()->CallJavascriptFunction( 260 GetJavascriptMethodName(PROFILE_CREATION_SUCCESS), dict); 261 262 // If the new profile is a supervised user, instead of opening a new window 263 // right away, a confirmation overlay will be shown by JS from the creation 264 // dialog. If we are importing an existing supervised profile or creating a 265 // new non-supervised user profile we don't show any confirmation, so open 266 // the new window now. 267 if (profile_creation_type_ != SUPERVISED_PROFILE_CREATION) { 268 // Opening the new window must be the last action, after all callbacks 269 // have been run, to give them a chance to initialize the profile. 270 helper::OpenNewWindowForProfile(desktop_type, 271 profile, 272 Profile::CREATE_STATUS_INITIALIZED); 273 } 274 profile_creation_type_ = NO_CREATION_IN_PROGRESS; 275} 276 277void CreateProfileHandler::ShowProfileCreationError( 278 Profile* profile, 279 const base::string16& error) { 280 DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_); 281 profile_creation_type_ = NO_CREATION_IN_PROGRESS; 282 profile_path_being_created_.clear(); 283 web_ui()->CallJavascriptFunction( 284 GetJavascriptMethodName(PROFILE_CREATION_ERROR), 285 base::StringValue(error)); 286 helper::DeleteProfileAtPath(profile->GetPath(), web_ui()); 287} 288 289void CreateProfileHandler::ShowProfileCreationWarning( 290 const base::string16& warning) { 291 DCHECK_EQ(SUPERVISED_PROFILE_CREATION, profile_creation_type_); 292 web_ui()->CallJavascriptFunction("BrowserOptions.showCreateProfileWarning", 293 base::StringValue(warning)); 294} 295 296void CreateProfileHandler::HandleCancelProfileCreation( 297 const base::ListValue* args) { 298 CancelProfileRegistration(true); 299} 300 301void CreateProfileHandler::CancelProfileRegistration(bool user_initiated) { 302 if (profile_path_being_created_.empty()) 303 return; 304 305 ProfileManager* manager = g_browser_process->profile_manager(); 306 Profile* new_profile = manager->GetProfileByPath(profile_path_being_created_); 307 if (!new_profile) 308 return; 309 310 // Non-supervised user creation cannot be canceled. (Creating a non-supervised 311 // profile shouldn't take significant time, and it can easily be deleted 312 // afterward.) 313 if (!new_profile->IsSupervised()) 314 return; 315 316 if (user_initiated) { 317 UMA_HISTOGRAM_MEDIUM_TIMES( 318 "Profile.CreateTimeCanceledNoTimeout", 319 base::TimeTicks::Now() - profile_creation_start_time_); 320 RecordProfileCreationMetrics(Profile::CREATE_STATUS_CANCELED); 321 } 322 323 DCHECK(supervised_user_registration_utility_.get()); 324 supervised_user_registration_utility_.reset(); 325 326 DCHECK_NE(NO_CREATION_IN_PROGRESS, profile_creation_type_); 327 profile_creation_type_ = NO_CREATION_IN_PROGRESS; 328 329 // Cancelling registration means the callback passed into 330 // RegisterAndInitSync() won't be called, so the cleanup must be done here. 331 profile_path_being_created_.clear(); 332 helper::DeleteProfileAtPath(new_profile->GetPath(), web_ui()); 333} 334 335void CreateProfileHandler::RecordProfileCreationMetrics( 336 Profile::CreateStatus status) { 337 UMA_HISTOGRAM_ENUMERATION("Profile.CreateResult", 338 status, 339 Profile::MAX_CREATE_STATUS); 340 UMA_HISTOGRAM_MEDIUM_TIMES( 341 "Profile.CreateTimeNoTimeout", 342 base::TimeTicks::Now() - profile_creation_start_time_); 343} 344 345void CreateProfileHandler::RecordSupervisedProfileCreationMetrics( 346 GoogleServiceAuthError::State error_state) { 347 if (profile_creation_type_ == SUPERVISED_PROFILE_CREATION) { 348 UMA_HISTOGRAM_ENUMERATION("Profile.SupervisedProfileCreateError", 349 error_state, 350 GoogleServiceAuthError::NUM_STATES); 351 UMA_HISTOGRAM_MEDIUM_TIMES( 352 "Profile.SupervisedProfileTotalCreateTime", 353 base::TimeTicks::Now() - profile_creation_start_time_); 354 } else { 355 DCHECK_EQ(SUPERVISED_PROFILE_IMPORT, profile_creation_type_); 356 UMA_HISTOGRAM_ENUMERATION("Profile.SupervisedProfileImportError", 357 error_state, 358 GoogleServiceAuthError::NUM_STATES); 359 UMA_HISTOGRAM_MEDIUM_TIMES( 360 "Profile.SupervisedProfileTotalImportTime", 361 base::TimeTicks::Now() - profile_creation_start_time_); 362 } 363} 364 365base::string16 CreateProfileHandler::GetProfileCreationErrorMessage( 366 ProfileCreationErrorType error) const { 367 int message_id = -1; 368 switch (error) { 369 case SIGNIN_ERROR: 370 message_id = 371 profile_creation_type_ == SUPERVISED_PROFILE_IMPORT ? 372 IDS_MANAGED_USER_IMPORT_SIGN_IN_ERROR : 373 IDS_PROFILES_CREATE_SIGN_IN_ERROR; 374 break; 375 case REMOTE_ERROR: 376 message_id = 377 profile_creation_type_ == SUPERVISED_PROFILE_IMPORT ? 378 IDS_MANAGED_USER_IMPORT_REMOTE_ERROR : 379 IDS_PROFILES_CREATE_REMOTE_ERROR; 380 break; 381 case LOCAL_ERROR: 382 message_id = 383 profile_creation_type_ == SUPERVISED_PROFILE_IMPORT ? 384 IDS_MANAGED_USER_IMPORT_LOCAL_ERROR : 385 IDS_PROFILES_CREATE_LOCAL_ERROR; 386 break; 387 } 388 389 return l10n_util::GetStringUTF16(message_id); 390} 391 392std::string CreateProfileHandler::GetJavascriptMethodName( 393 ProfileCreationStatus status) const { 394 switch (status) { 395 case PROFILE_CREATION_SUCCESS: 396 return profile_creation_type_ == SUPERVISED_PROFILE_IMPORT ? 397 "BrowserOptions.showManagedUserImportSuccess" : 398 "BrowserOptions.showCreateProfileSuccess"; 399 case PROFILE_CREATION_ERROR: 400 return profile_creation_type_ == SUPERVISED_PROFILE_IMPORT ? 401 "BrowserOptions.showManagedUserImportError" : 402 "BrowserOptions.showCreateProfileError"; 403 } 404 405 NOTREACHED(); 406 return std::string(); 407} 408 409bool CreateProfileHandler::IsValidExistingSupervisedUserId( 410 const std::string& existing_supervised_user_id) const { 411 if (existing_supervised_user_id.empty()) 412 return true; 413 414 Profile* profile = Profile::FromWebUI(web_ui()); 415 const base::DictionaryValue* dict = 416 SupervisedUserSyncServiceFactory::GetForProfile(profile)-> 417 GetSupervisedUsers(); 418 if (!dict->HasKey(existing_supervised_user_id)) 419 return false; 420 421 // Check if this supervised user already exists on this machine. 422 const ProfileInfoCache& cache = 423 g_browser_process->profile_manager()->GetProfileInfoCache(); 424 for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) { 425 if (existing_supervised_user_id == 426 cache.GetSupervisedUserIdOfProfileAtIndex(i)) 427 return false; 428 } 429 return true; 430} 431 432} // namespace options 433