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