• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5cr.define('options', function() {
6  var OptionsPage = options.OptionsPage;
7  var ArrayDataModel = cr.ui.ArrayDataModel;
8
9  /**
10   * ManageProfileOverlay class
11   * Encapsulated handling of the 'Manage profile...' overlay page.
12   * @constructor
13   * @class
14   */
15  function ManageProfileOverlay() {
16    OptionsPage.call(this, 'manageProfile',
17                     loadTimeData.getString('manageProfileTabTitle'),
18                     'manage-profile-overlay');
19  };
20
21  cr.addSingletonGetter(ManageProfileOverlay);
22
23  ManageProfileOverlay.prototype = {
24    // Inherit from OptionsPage.
25    __proto__: OptionsPage.prototype,
26
27    // Info about the currently managed/deleted profile.
28    profileInfo_: null,
29
30    // Whether the currently chosen name for a new profile was assigned
31    // automatically by choosing an avatar. Set on receiveNewProfileDefaults;
32    // cleared on first edit (in onNameChanged_).
33    profileNameIsDefault_: false,
34
35    // List of default profile names corresponding to the respective icons.
36    defaultProfileNames_: [],
37
38    // An object containing all names of existing profiles.
39    existingProfileNames_: {},
40
41    // The currently selected icon in the icon grid.
42    iconGridSelectedURL_: null,
43
44    /**
45     * Initialize the page.
46     */
47    initializePage: function() {
48      // Call base class implementation to start preference initialization.
49      OptionsPage.prototype.initializePage.call(this);
50
51      var self = this;
52      options.ProfilesIconGrid.decorate($('manage-profile-icon-grid'));
53      options.ProfilesIconGrid.decorate($('create-profile-icon-grid'));
54      self.registerCommonEventHandlers_('create',
55                                        self.submitCreateProfile_.bind(self));
56      self.registerCommonEventHandlers_('manage',
57                                        self.submitManageChanges_.bind(self));
58
59      // Override the create-profile-ok and create-* keydown handlers, to avoid
60      // closing the overlay until we finish creating the profile.
61      $('create-profile-ok').onclick = function(event) {
62        self.submitCreateProfile_();
63      };
64
65      $('create-profile-cancel').onclick = function(event) {
66        CreateProfileOverlay.cancelCreateProfile();
67      };
68
69      $('manage-profile-cancel').onclick =
70          $('disconnect-managed-profile-cancel').onclick =
71          $('delete-profile-cancel').onclick = function(event) {
72        OptionsPage.closeOverlay();
73      };
74      $('delete-profile-ok').onclick = function(event) {
75        OptionsPage.closeOverlay();
76        if (BrowserOptions.getCurrentProfile().isManaged)
77          return;
78        chrome.send('deleteProfile', [self.profileInfo_.filePath]);
79        options.ManagedUserListData.resetPromise();
80      };
81      $('add-shortcut-button').onclick = function(event) {
82        chrome.send('addProfileShortcut', [self.profileInfo_.filePath]);
83      };
84      $('remove-shortcut-button').onclick = function(event) {
85        chrome.send('removeProfileShortcut', [self.profileInfo_.filePath]);
86      };
87
88      $('disconnect-managed-profile-ok').onclick = function(event) {
89        OptionsPage.closeOverlay();
90        chrome.send('deleteProfile',
91                    [BrowserOptions.getCurrentProfile().filePath]);
92      }
93
94      $('create-profile-managed-signed-in-learn-more-link').onclick =
95          function(event) {
96        OptionsPage.navigateToPage('managedUserLearnMore');
97        return false;
98      };
99
100      $('create-profile-managed-not-signed-in-link').onclick = function(event) {
101        // The signin process will open an overlay to configure sync, which
102        // would replace this overlay. It's smoother to close this one now.
103        // TODO(pamg): Move the sync-setup overlay to a higher layer so this one
104        // can stay open under it, after making sure that doesn't break anything
105        // else.
106        OptionsPage.closeOverlay();
107        SyncSetupOverlay.startSignIn();
108      };
109
110      $('create-profile-managed-sign-in-again-link').onclick = function(event) {
111        OptionsPage.closeOverlay();
112        SyncSetupOverlay.showSetupUI();
113      };
114
115      $('import-existing-managed-user-link').onclick = function(event) {
116        // Hide the import button to trigger a cursor update. The import button
117        // is shown again when the import overlay loads. TODO(akuegel): Remove
118        // this temporary fix when crbug/246304 is resolved.
119        $('import-existing-managed-user-link').hidden = true;
120        OptionsPage.navigateToPage('managedUserImport');
121      };
122    },
123
124    /** @override */
125    didShowPage: function() {
126      chrome.send('requestDefaultProfileIcons', ['manage']);
127
128      // Just ignore the manage profile dialog on Chrome OS, they use /accounts.
129      if (!cr.isChromeOS && window.location.pathname == '/manageProfile')
130        ManageProfileOverlay.getInstance().prepareForManageDialog_();
131
132      // When editing a profile, initially hide the "add shortcut" and
133      // "remove shortcut" buttons and ask the handler which to show. It will
134      // call |receiveHasProfileShortcuts|, which will show the appropriate one.
135      $('remove-shortcut-button').hidden = true;
136      $('add-shortcut-button').hidden = true;
137
138      if (loadTimeData.getBoolean('profileShortcutsEnabled')) {
139        var profileInfo = ManageProfileOverlay.getInstance().profileInfo_;
140        chrome.send('requestHasProfileShortcuts', [profileInfo.filePath]);
141      }
142
143      var manageNameField = $('manage-profile-name');
144      // Supervised users cannot edit their names.
145      if (manageNameField.disabled)
146        $('manage-profile-ok').focus();
147      else
148        manageNameField.focus();
149
150      this.profileNameIsDefault_ = false;
151    },
152
153    /**
154     * Registers event handlers that are common between create and manage modes.
155     * @param {string} mode A label that specifies the type of dialog box which
156     *     is currently being viewed (i.e. 'create' or 'manage').
157     * @param {function()} submitFunction The function that should be called
158     *     when the user chooses to submit (e.g. by clicking the OK button).
159     * @private
160     */
161    registerCommonEventHandlers_: function(mode, submitFunction) {
162      var self = this;
163      $(mode + '-profile-icon-grid').addEventListener('change', function(e) {
164        self.onIconGridSelectionChanged_(mode);
165      });
166      $(mode + '-profile-name').oninput = function(event) {
167        self.onNameChanged_(mode);
168      };
169      $(mode + '-profile-ok').onclick = function(event) {
170        OptionsPage.closeOverlay();
171        submitFunction();
172      };
173    },
174
175    /**
176     * Set the profile info used in the dialog.
177     * @param {Object} profileInfo An object of the form:
178     *     profileInfo = {
179     *       name: "Profile Name",
180     *       iconURL: "chrome://path/to/icon/image",
181     *       filePath: "/path/to/profile/data/on/disk",
182     *       isCurrentProfile: false,
183     *       isManaged: false
184     *     };
185     * @param {string} mode A label that specifies the type of dialog box which
186     *     is currently being viewed (i.e. 'create' or 'manage').
187     * @private
188     */
189    setProfileInfo_: function(profileInfo, mode) {
190      this.iconGridSelectedURL_ = profileInfo.iconURL;
191      this.profileInfo_ = profileInfo;
192      $(mode + '-profile-name').value = profileInfo.name;
193      $(mode + '-profile-icon-grid').selectedItem = profileInfo.iconURL;
194    },
195
196    /**
197     * Sets the name of the profile being edited or created.
198     * @param {string} name New profile name.
199     * @param {string} mode A label that specifies the type of dialog box which
200     *     is currently being viewed (i.e. 'create' or 'manage').
201     * @private
202     */
203    setProfileName_: function(name, mode) {
204      if (this.profileInfo_)
205        this.profileInfo_.name = name;
206      $(mode + '-profile-name').value = name;
207    },
208
209    /**
210     * Set an array of default icon URLs. These will be added to the grid that
211     * the user will use to choose their profile icon.
212     * @param {string} mode A label that specifies the type of dialog box which
213     *     is currently being viewed (i.e. 'create' or 'manage').
214     * @param {Array.<string>} iconURLs An array of icon URLs.
215     * @param {Array.<string>} names An array of default names
216     *     corresponding to the icons.
217     * @private
218     */
219    receiveDefaultProfileIconsAndNames_: function(mode, iconURLs, names) {
220      this.defaultProfileNames_ = names;
221
222      var grid = $(mode + '-profile-icon-grid');
223
224      grid.dataModel = new ArrayDataModel(iconURLs);
225
226      if (this.profileInfo_)
227        grid.selectedItem = this.profileInfo_.iconURL;
228
229      // Recalculate the measured item size.
230      grid.measured_ = null;
231      grid.columns = 0;
232      grid.redraw();
233    },
234
235    /**
236     * Callback to set the initial values when creating a new profile.
237     * @param {Object} profileInfo An object of the form:
238     *     profileInfo = {
239     *       name: "Profile Name",
240     *       iconURL: "chrome://path/to/icon/image",
241     *     };
242     * @private
243     */
244    receiveNewProfileDefaults_: function(profileInfo) {
245      ManageProfileOverlay.setProfileInfo(profileInfo, 'create');
246      this.profileNameIsDefault_ = true;
247      $('create-profile-name-label').hidden = false;
248      $('create-profile-name').hidden = false;
249      // Trying to change the focus if this isn't the topmost overlay can
250      // instead cause the FocusManager to override another overlay's focus,
251      // e.g. if an overlay above this one is in the process of being reloaded.
252      // But the C++ handler calls this method directly on ManageProfileOverlay,
253      // so check the pageDiv to also include its subclasses (in particular
254      // CreateProfileOverlay, which has higher sub-overlays).
255      if (OptionsPage.getTopmostVisiblePage().pageDiv == this.pageDiv) {
256        // This will only have an effect if the 'create-profile-name' element
257        //  is visible, i.e. if the overlay is in create mode.
258        $('create-profile-name').focus();
259      }
260      $('create-profile-ok').disabled = false;
261    },
262
263    /**
264     * Set a dictionary of all profile names. These are used to prevent the
265     * user from naming two profiles the same.
266     * @param {Object} profileNames A dictionary of profile names.
267     * @private
268     */
269    receiveExistingProfileNames_: function(profileNames) {
270      this.existingProfileNames_ = profileNames;
271    },
272
273    /**
274     * Callback to show the add/remove shortcut buttons when in edit mode,
275     * called by the handler as a result of the 'requestHasProfileShortcuts_'
276     * message.
277     * @param {boolean} hasShortcuts Whether profile has any existing shortcuts.
278     * @private
279     */
280    receiveHasProfileShortcuts_: function(hasShortcuts) {
281      $('add-shortcut-button').hidden = hasShortcuts;
282      $('remove-shortcut-button').hidden = !hasShortcuts;
283    },
284
285    /**
286     * Display the error bubble, with |errorHtml| in the bubble.
287     * @param {string} errorHtml The html string to display as an error.
288     * @param {string} mode A label that specifies the type of dialog box which
289     *     is currently being viewed (i.e. 'create' or 'manage').
290     * @param {boolean} disableOKButton True if the dialog's OK button should be
291     *     disabled when the error bubble is shown. It will be (re-)enabled when
292     *     the error bubble is hidden.
293     * @private
294     */
295    showErrorBubble_: function(errorHtml, mode, disableOKButton) {
296      var nameErrorEl = $(mode + '-profile-error-bubble');
297      nameErrorEl.hidden = false;
298      nameErrorEl.innerHTML = errorHtml;
299
300      if (disableOKButton)
301        $(mode + '-profile-ok').disabled = true;
302    },
303
304    /**
305     * Hide the error bubble.
306     * @param {string} mode A label that specifies the type of dialog box which
307     *     is currently being viewed (i.e. 'create' or 'manage').
308     * @private
309     */
310    hideErrorBubble_: function(mode) {
311      $(mode + '-profile-error-bubble').innerHTML = '';
312      $(mode + '-profile-error-bubble').hidden = true;
313      $(mode + '-profile-ok').disabled = false;
314    },
315
316    /**
317     * oninput callback for <input> field.
318     * @param {string} mode A label that specifies the type of dialog box which
319     *     is currently being viewed (i.e. 'create' or 'manage').
320     * @private
321     */
322    onNameChanged_: function(mode) {
323      this.profileNameIsDefault_ = false;
324      this.updateCreateOrImport_(mode);
325    },
326
327    /**
328     * Called when the profile name is changed or the 'create managed' checkbox
329     * is toggled. Updates the 'ok' button and the 'import existing supervised
330     * user' link.
331     * @param {string} mode A label that specifies the type of dialog box which
332     *     is currently being viewed (i.e. 'create' or 'manage').
333     * @private
334     */
335    updateCreateOrImport_: function(mode) {
336      // In 'create' mode, check for existing managed users with the same name.
337      if (mode == 'create' && $('create-profile-managed').checked) {
338        options.ManagedUserListData.requestExistingManagedUsers().then(
339            this.receiveExistingManagedUsers_.bind(this),
340            this.onSigninError_.bind(this));
341      } else {
342        this.updateOkButton_(mode);
343      }
344    },
345
346    /**
347     * Callback which receives the list of existing managed users. Checks if the
348     * currently entered name is the name of an already existing managed user.
349     * If yes, the user is prompted to import the existing managed user, and the
350     * create button is disabled.
351     * @param {Array.<Object>} The list of existing managed users.
352     * @private
353     */
354    receiveExistingManagedUsers_: function(managedUsers) {
355      var newName = $('create-profile-name').value;
356      var i;
357      for (i = 0; i < managedUsers.length; ++i) {
358        if (managedUsers[i].name == newName &&
359            !managedUsers[i].onCurrentDevice) {
360          var errorHtml = loadTimeData.getStringF(
361              'manageProfilesExistingSupervisedUser',
362              HTMLEscape(elide(newName, /* maxLength */ 50)));
363          this.showErrorBubble_(errorHtml, 'create', true);
364
365          // Check if another supervised user also exists with that name.
366          var nameIsUnique = true;
367          var j;
368          for (j = i + 1; j < managedUsers.length; ++j) {
369            if (managedUsers[j].name == newName) {
370              nameIsUnique = false;
371              break;
372            }
373          }
374          var self = this;
375          function getImportHandler(managedUser, nameIsUnique) {
376            return function() {
377              if (managedUser.needAvatar || !nameIsUnique) {
378                OptionsPage.navigateToPage('managedUserImport');
379              } else {
380                self.hideErrorBubble_('create');
381                CreateProfileOverlay.updateCreateInProgress(true);
382                chrome.send('createProfile',
383                    [managedUser.name, managedUser.iconURL, false, true,
384                        managedUser.id]);
385              }
386            }
387          };
388          $('supervised-user-import').onclick =
389              getImportHandler(managedUsers[i], nameIsUnique);
390          $('create-profile-ok').disabled = true;
391          return;
392        }
393      }
394      this.updateOkButton_('create');
395    },
396
397    /**
398     * Called in case the request for the list of managed users fails because of
399     * a signin error.
400     * @private
401     */
402    onSigninError_: function() {
403      this.updateImportExistingManagedUserLink_(false);
404    },
405
406    /**
407     * Called to update the state of the ok button depending if the name is
408     * already used or not.
409     * @param {string} mode A label that specifies the type of dialog box which
410     *     is currently being viewed (i.e. 'create' or 'manage').
411     * @private
412     */
413    updateOkButton_: function(mode) {
414      var oldName = this.profileInfo_.name;
415      var newName = $(mode + '-profile-name').value;
416      var nameIsDuplicate = this.existingProfileNames_[newName] != undefined;
417      if (mode == 'manage' && oldName == newName)
418        nameIsDuplicate = false;
419      if (nameIsDuplicate) {
420        var errorHtml =
421            loadTimeData.getString('manageProfilesDuplicateNameError');
422        this.showErrorBubble_(errorHtml, mode, true);
423      } else {
424        this.hideErrorBubble_(mode);
425
426        var nameIsValid = $(mode + '-profile-name').validity.valid;
427        $(mode + '-profile-ok').disabled = !nameIsValid;
428      }
429    },
430
431    /**
432     * Called when the user clicks "OK" or hits enter. Saves the newly changed
433     * profile info.
434     * @private
435     */
436    submitManageChanges_: function() {
437      var name = $('manage-profile-name').value;
438      var iconURL = $('manage-profile-icon-grid').selectedItem;
439
440      chrome.send('setProfileIconAndName',
441                  [this.profileInfo_.filePath, iconURL, name]);
442      if (name != this.profileInfo_.name)
443        options.ManagedUserListData.resetPromise();
444    },
445
446    /**
447     * Called when the user clicks "OK" or hits enter. Creates the profile
448     * using the information in the dialog.
449     * @private
450     */
451    submitCreateProfile_: function() {
452      // This is visual polish: the UI to access this should be disabled for
453      // managed users, and the back end will prevent user creation anyway.
454      if (this.profileInfo_ && this.profileInfo_.isManaged)
455        return;
456
457      this.hideErrorBubble_('create');
458      CreateProfileOverlay.updateCreateInProgress(true);
459
460      // Get the user's chosen name and icon, or default if they do not
461      // wish to customize their profile.
462      var name = $('create-profile-name').value;
463      var iconUrl = $('create-profile-icon-grid').selectedItem;
464      var createShortcut = $('create-shortcut').checked;
465      var isManaged = $('create-profile-managed').checked;
466      var existingManagedUserId = '';
467
468      // 'createProfile' is handled by the CreateProfileHandler.
469      chrome.send('createProfile',
470                  [name, iconUrl, createShortcut,
471                   isManaged, existingManagedUserId]);
472    },
473
474    /**
475     * Called when the selected icon in the icon grid changes.
476     * @param {string} mode A label that specifies the type of dialog box which
477     *     is currently being viewed (i.e. 'create' or 'manage').
478     * @private
479     */
480    onIconGridSelectionChanged_: function(mode) {
481      var iconURL = $(mode + '-profile-icon-grid').selectedItem;
482      if (!iconURL || iconURL == this.iconGridSelectedURL_)
483        return;
484      this.iconGridSelectedURL_ = iconURL;
485      if (this.profileNameIsDefault_) {
486        var index = $(mode + '-profile-icon-grid').selectionModel.selectedIndex;
487        var name = this.defaultProfileNames_[index];
488        if (name) {
489          this.setProfileName_(name, mode);
490        }
491      }
492      if (this.profileInfo_ && this.profileInfo_.filePath) {
493        chrome.send('profileIconSelectionChanged',
494                    [this.profileInfo_.filePath, iconURL]);
495      }
496    },
497
498    /**
499     * Updates the contents of the "Manage Profile" section of the dialog,
500     * and shows that section.
501     * @private
502     */
503    prepareForManageDialog_: function() {
504      chrome.send('refreshGaiaPicture');
505      var profileInfo = BrowserOptions.getCurrentProfile();
506      ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
507      $('manage-profile-overlay-create').hidden = true;
508      $('manage-profile-overlay-manage').hidden = false;
509      $('manage-profile-overlay-delete').hidden = true;
510      $('manage-profile-overlay-disconnect-managed').hidden = true;
511      $('manage-profile-name').disabled = profileInfo.isManaged;
512      this.hideErrorBubble_('manage');
513    },
514
515    /**
516     * Display the "Manage Profile" dialog.
517     * @private
518     */
519    showManageDialog_: function() {
520      this.prepareForManageDialog_();
521      OptionsPage.navigateToPage('manageProfile');
522    },
523
524    /**
525     * Display the "Delete Profile" dialog.
526     * @param {Object} profileInfo The profile object of the profile to delete.
527     * @private
528     */
529    showDeleteDialog_: function(profileInfo) {
530      if (BrowserOptions.getCurrentProfile().isManaged)
531        return;
532
533      ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
534      $('manage-profile-overlay-create').hidden = true;
535      $('manage-profile-overlay-manage').hidden = true;
536      $('manage-profile-overlay-delete').hidden = false;
537      $('manage-profile-overlay-disconnect-managed').hidden = true;
538      $('delete-profile-icon').style.content =
539          getProfileAvatarIcon(profileInfo.iconURL);
540      $('delete-profile-text').textContent =
541          loadTimeData.getStringF('deleteProfileMessage',
542                                  elide(profileInfo.name, /* maxLength */ 50));
543      $('delete-managed-profile-addendum').hidden = !profileInfo.isManaged;
544
545      // Because this dialog isn't useful when refreshing or as part of the
546      // history, don't create a history entry for it when showing.
547      OptionsPage.showPageByName('manageProfile', false);
548    },
549
550    /**
551     * Display the "Disconnect Managed Profile" dialog.
552     * @private
553     */
554    showDisconnectManagedProfileDialog_: function(replacements) {
555      loadTimeData.overrideValues(replacements);
556      $('manage-profile-overlay-create').hidden = true;
557      $('manage-profile-overlay-manage').hidden = true;
558      $('manage-profile-overlay-delete').hidden = true;
559      $('disconnect-managed-profile-domain-information').innerHTML =
560          loadTimeData.getString('disconnectManagedProfileDomainInformation');
561      $('disconnect-managed-profile-text').innerHTML =
562          loadTimeData.getString('disconnectManagedProfileText');
563      $('manage-profile-overlay-disconnect-managed').hidden = false;
564
565      // Because this dialog isn't useful when refreshing or as part of the
566      // history, don't create a history entry for it when showing.
567      OptionsPage.showPageByName('manageProfile', false);
568    },
569
570    /**
571     * Display the "Create Profile" dialog.
572     * @private
573     */
574    showCreateDialog_: function() {
575      OptionsPage.navigateToPage('createProfile');
576    },
577  };
578
579  // Forward public APIs to private implementations.
580  [
581    'receiveDefaultProfileIconsAndNames',
582    'receiveNewProfileDefaults',
583    'receiveExistingProfileNames',
584    'receiveHasProfileShortcuts',
585    'setProfileInfo',
586    'setProfileName',
587    'showManageDialog',
588    'showDeleteDialog',
589    'showDisconnectManagedProfileDialog',
590    'showCreateDialog',
591  ].forEach(function(name) {
592    ManageProfileOverlay[name] = function() {
593      var instance = ManageProfileOverlay.getInstance();
594      return instance[name + '_'].apply(instance, arguments);
595    };
596  });
597
598  function CreateProfileOverlay() {
599    OptionsPage.call(this, 'createProfile',
600                     loadTimeData.getString('createProfileTabTitle'),
601                     'manage-profile-overlay');
602  };
603
604  cr.addSingletonGetter(CreateProfileOverlay);
605
606  CreateProfileOverlay.prototype = {
607    // Inherit from ManageProfileOverlay.
608    __proto__: ManageProfileOverlay.prototype,
609
610    // The signed-in email address of the current profile, or empty if they're
611    // not signed in.
612    signedInEmail_: '',
613
614    /** @override */
615    canShowPage: function() {
616      return !BrowserOptions.getCurrentProfile().isManaged;
617    },
618
619    /**
620     * Configures the overlay to the "create user" mode.
621     * @override
622     */
623    didShowPage: function() {
624      chrome.send('requestCreateProfileUpdate');
625      chrome.send('requestDefaultProfileIcons', ['create']);
626      chrome.send('requestNewProfileDefaults');
627
628      $('manage-profile-overlay-create').hidden = false;
629      $('manage-profile-overlay-manage').hidden = true;
630      $('manage-profile-overlay-delete').hidden = true;
631      $('manage-profile-overlay-disconnect-managed').hidden = true;
632      $('create-profile-instructions').textContent =
633         loadTimeData.getStringF('createProfileInstructions');
634      this.hideErrorBubble_();
635      this.updateCreateInProgress_(false);
636
637      var shortcutsEnabled = loadTimeData.getBoolean('profileShortcutsEnabled');
638      $('create-shortcut-container').hidden = !shortcutsEnabled;
639      $('create-shortcut').checked = shortcutsEnabled;
640
641      $('create-profile-name-label').hidden = true;
642      $('create-profile-name').hidden = true;
643      $('create-profile-ok').disabled = true;
644
645      $('create-profile-managed').checked = false;
646      $('import-existing-managed-user-link').hidden = true;
647      $('create-profile-managed').onchange = function() {
648        ManageProfileOverlay.getInstance().updateCreateOrImport_('create');
649      };
650      $('create-profile-managed-signed-in').disabled = true;
651      $('create-profile-managed-signed-in').hidden = true;
652      $('create-profile-managed-not-signed-in').hidden = true;
653
654      this.profileNameIsDefault_ = false;
655    },
656
657    /** @override */
658    handleCancel: function() {
659      this.cancelCreateProfile_();
660    },
661
662    /** @override */
663    showErrorBubble_: function(errorHtml) {
664      ManageProfileOverlay.getInstance().showErrorBubble_(errorHtml,
665                                                          'create',
666                                                          false);
667    },
668
669    /** @override */
670    hideErrorBubble_: function() {
671      ManageProfileOverlay.getInstance().hideErrorBubble_('create');
672    },
673
674    /**
675     * Updates the UI when a profile create step begins or ends.
676     * Note that hideErrorBubble_() also enables the "OK" button, so it
677     * must be called before this function if both are used.
678     * @param {boolean} inProgress True if the UI should be updated to show that
679     *     profile creation is now in progress.
680     * @private
681     */
682    updateCreateInProgress_: function(inProgress) {
683      this.createInProgress_ = inProgress;
684      this.updateCreateManagedUserCheckbox_();
685
686      $('create-profile-icon-grid').disabled = inProgress;
687      $('create-profile-name').disabled = inProgress;
688      $('create-shortcut').disabled = inProgress;
689      $('create-profile-ok').disabled = inProgress;
690      $('import-existing-managed-user-link').disabled = inProgress;
691
692      $('create-profile-throbber').hidden = !inProgress;
693    },
694
695    /**
696     * Cancels the creation of the a profile. It is safe to call this even
697     * when no profile is in the process of being created.
698     * @private
699     */
700    cancelCreateProfile_: function() {
701      OptionsPage.closeOverlay();
702      chrome.send('cancelCreateProfile');
703      this.hideErrorBubble_();
704      this.updateCreateInProgress_(false);
705    },
706
707    /**
708     * Shows an error message describing an error that occurred while creating
709     * a new profile.
710     * Called by BrowserOptions via the BrowserOptionsHandler.
711     * @param {string} error The error message to display.
712     * @private
713     */
714    onError_: function(error) {
715      this.updateCreateInProgress_(false);
716      this.showErrorBubble_(error);
717    },
718
719    /**
720     * Shows a warning message giving information while creating a new profile.
721     * Called by BrowserOptions via the BrowserOptionsHandler.
722     * @param {string} warning The warning message to display.
723     * @private
724     */
725    onWarning_: function(warning) {
726      this.showErrorBubble_(warning);
727    },
728
729    /**
730     * For new supervised users, shows a confirmation page after successfully
731     * creating a new profile; otherwise, the handler will open a new window.
732     * @param {Object} profileInfo An object of the form:
733     *     profileInfo = {
734     *       name: "Profile Name",
735     *       filePath: "/path/to/profile/data/on/disk"
736     *       isManaged: (true|false),
737     *     };
738     * @private
739     */
740    onSuccess_: function(profileInfo) {
741      this.updateCreateInProgress_(false);
742      OptionsPage.closeOverlay();
743      if (profileInfo.isManaged) {
744        options.ManagedUserListData.resetPromise();
745        profileInfo.custodianEmail = this.signedInEmail_;
746        ManagedUserCreateConfirmOverlay.setProfileInfo(profileInfo);
747        OptionsPage.showPageByName('managedUserCreateConfirm', false);
748        BrowserOptions.updateManagesSupervisedUsers(true);
749      }
750    },
751
752    /**
753     * Updates the signed-in or not-signed-in UI when in create mode. Called by
754     * the handler in response to the 'requestCreateProfileUpdate' message.
755     * updateManagedUsersAllowed_ is expected to be called after this is, and
756     * will update additional UI elements.
757     * @param {string} email The email address of the currently signed-in user.
758     *     An empty string indicates that the user is not signed in.
759     * @param {boolean} hasError Whether the user's sign-in credentials are
760     *     still valid.
761     * @private
762     */
763    updateSignedInStatus_: function(email, hasError) {
764      this.signedInEmail_ = email;
765      this.hasError_ = hasError;
766      var isSignedIn = email !== '';
767      $('create-profile-managed-signed-in').hidden = !isSignedIn;
768      $('create-profile-managed-not-signed-in').hidden = isSignedIn;
769
770      if (isSignedIn) {
771        var accountDetailsOutOfDate =
772            $('create-profile-managed-account-details-out-of-date-label');
773        accountDetailsOutOfDate.textContent = loadTimeData.getStringF(
774            'manageProfilesManagedAccountDetailsOutOfDate', email);
775        accountDetailsOutOfDate.hidden = !hasError;
776
777        $('create-profile-managed-signed-in-label').textContent =
778            loadTimeData.getStringF(
779                'manageProfilesManagedSignedInLabel', email);
780        $('create-profile-managed-signed-in-label').hidden = hasError;
781
782        $('create-profile-managed-sign-in-again-link').hidden = !hasError;
783        $('create-profile-managed-signed-in-learn-more-link').hidden = hasError;
784      }
785
786      this.updateImportExistingManagedUserLink_(isSignedIn && !hasError);
787    },
788
789    /**
790     * Enables/disables the 'import existing managed users' link button.
791     * It also updates the button text.
792     * @param {boolean} enable True to enable the link button and
793     *     false otherwise.
794     * @private
795     */
796    updateImportExistingManagedUserLink_: function(enable) {
797      var importManagedUserElement = $('import-existing-managed-user-link');
798      importManagedUserElement.hidden = false;
799      importManagedUserElement.disabled = !enable || this.createInProgress_;
800      importManagedUserElement.textContent = enable ?
801          loadTimeData.getString('importExistingManagedUserLink') :
802          loadTimeData.getString('signInToImportManagedUsers');
803    },
804
805    /**
806     * Sets whether creating managed users is allowed or not. Called by the
807     * handler in response to the 'requestCreateProfileUpdate' message or a
808     * change in the (policy-controlled) pref that prohibits creating managed
809     * users, after the signed-in status has been updated.
810     * @param {boolean} allowed True if creating managed users should be
811     *     allowed.
812     * @private
813     */
814    updateManagedUsersAllowed_: function(allowed) {
815      this.managedUsersAllowed_ = allowed;
816      this.updateCreateManagedUserCheckbox_();
817
818      $('create-profile-managed-not-signed-in-link').hidden = !allowed;
819      if (!allowed) {
820        $('create-profile-managed-indicator').setAttribute('controlled-by',
821                                                           'policy');
822      } else {
823        $('create-profile-managed-indicator').removeAttribute('controlled-by');
824      }
825    },
826
827    /**
828     * Updates the status of the "create managed user" checkbox. Called from
829     * updateManagedUsersAllowed_() or updateCreateInProgress_().
830     * updateSignedInStatus_() does not call this method directly, because it
831     * will be followed by a call to updateManagedUsersAllowed_().
832     * @private
833     */
834    updateCreateManagedUserCheckbox_: function() {
835      $('create-profile-managed').disabled =
836          !this.managedUsersAllowed_ || this.createInProgress_ ||
837          this.signedInEmail_ == '' || this.hasError_;
838    },
839  };
840
841  // Forward public APIs to private implementations.
842  [
843    'cancelCreateProfile',
844    'onError',
845    'onSuccess',
846    'onWarning',
847    'updateCreateInProgress',
848    'updateManagedUsersAllowed',
849    'updateSignedInStatus',
850  ].forEach(function(name) {
851    CreateProfileOverlay[name] = function() {
852      var instance = CreateProfileOverlay.getInstance();
853      return instance[name + '_'].apply(instance, arguments);
854    };
855  });
856
857  // Export
858  return {
859    ManageProfileOverlay: ManageProfileOverlay,
860    CreateProfileOverlay: CreateProfileOverlay,
861  };
862});
863