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