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