Home | History | Annotate | Download | only in options
      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 
      5 cr.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     // An object containing all known profile names.
     31     profileNames_: {},
     32 
     33     // The currently selected icon in the icon grid.
     34     iconGridSelectedURL_: null,
     35 
     36     /**
     37      * Initialize the page.
     38      */
     39     initializePage: function() {
     40       // Call base class implementation to start preference initialization.
     41       OptionsPage.prototype.initializePage.call(this);
     42 
     43       var self = this;
     44       options.ProfilesIconGrid.decorate($('manage-profile-icon-grid'));
     45       options.ProfilesIconGrid.decorate($('create-profile-icon-grid'));
     46       self.registerCommonEventHandlers_('create',
     47                                         self.submitCreateProfile_.bind(self));
     48       self.registerCommonEventHandlers_('manage',
     49                                         self.submitManageChanges_.bind(self));
     50 
     51       // Override the create-profile-ok and create-* keydown handlers, to avoid
     52       // closing the overlay until we finish creating the profile.
     53       $('create-profile-ok').onclick = function(event) {
     54         self.submitCreateProfile_();
     55       };
     56 
     57       $('create-profile-cancel').onclick = function(event) {
     58         CreateProfileOverlay.cancelCreateProfile();
     59       };
     60 
     61       $('create-profile-managed-container').hidden =
     62           !loadTimeData.getBoolean('managedUsersEnabled');
     63       $('select-existing-managed-profile-checkbox').hidden =
     64           !loadTimeData.getBoolean('allowCreateExistingManagedUsers');
     65       $('choose-existing-managed-profile').hidden =
     66           !loadTimeData.getBoolean('allowCreateExistingManagedUsers');
     67 
     68       $('manage-profile-cancel').onclick =
     69           $('delete-profile-cancel').onclick = function(event) {
     70         OptionsPage.closeOverlay();
     71       };
     72       $('delete-profile-ok').onclick = function(event) {
     73         OptionsPage.closeOverlay();
     74         if (BrowserOptions.getCurrentProfile().isManaged)
     75           return;
     76         chrome.send('deleteProfile', [self.profileInfo_.filePath]);
     77       };
     78       $('add-shortcut-button').onclick = function(event) {
     79         chrome.send('addProfileShortcut', [self.profileInfo_.filePath]);
     80       };
     81       $('remove-shortcut-button').onclick = function(event) {
     82         chrome.send('removeProfileShortcut', [self.profileInfo_.filePath]);
     83       };
     84 
     85       $('create-profile-managed-signed-in-learn-more-link').onclick =
     86           function(event) {
     87         OptionsPage.navigateToPage('managedUserLearnMore');
     88         return false;
     89       };
     90 
     91       $('create-profile-managed-not-signed-in-link').onclick = function(event) {
     92         // The signin process will open an overlay to configure sync, which
     93         // would replace this overlay. It's smoother to close this one now.
     94         // TODO(pamg): Move the sync-setup overlay to a higher layer so this one
     95         // can stay open under it, after making sure that doesn't break anything
     96         // else.
     97         OptionsPage.closeOverlay();
     98         SyncSetupOverlay.startSignIn();
     99       };
    100 
    101       $('create-profile-managed-sign-in-again-link').onclick = function(event) {
    102         OptionsPage.closeOverlay();
    103         SyncSetupOverlay.showSetupUI();
    104       };
    105 
    106       $('create-profile-managed').onchange = function(event) {
    107         var createManagedProfile = $('create-profile-managed').checked;
    108         $('select-existing-managed-profile-checkbox').disabled =
    109             !createManagedProfile;
    110 
    111         if (!createManagedProfile) {
    112           $('select-existing-managed-profile-checkbox').checked = false;
    113           $('choose-existing-managed-profile').disabled = true;
    114           $('create-profile-name').disabled = false;
    115         }
    116       };
    117 
    118       $('select-existing-managed-profile-checkbox').onchange = function(event) {
    119         var selectExistingProfile =
    120             $('select-existing-managed-profile-checkbox').checked;
    121         $('choose-existing-managed-profile').disabled = !selectExistingProfile;
    122         $('create-profile-name').disabled = selectExistingProfile;
    123       };
    124     },
    125 
    126     /** @override */
    127     didShowPage: function() {
    128       chrome.send('requestDefaultProfileIcons');
    129 
    130       // Just ignore the manage profile dialog on Chrome OS, they use /accounts.
    131       if (!cr.isChromeOS && window.location.pathname == '/manageProfile')
    132         ManageProfileOverlay.getInstance().prepareForManageDialog_();
    133 
    134       // When editing a profile, initially hide the "add shortcut" and
    135       // "remove shortcut" buttons and ask the handler which to show. It will
    136       // call |receiveHasProfileShortcuts|, which will show the appropriate one.
    137       $('remove-shortcut-button').hidden = true;
    138       $('add-shortcut-button').hidden = true;
    139 
    140       if (loadTimeData.getBoolean('profileShortcutsEnabled')) {
    141         var profileInfo = ManageProfileOverlay.getInstance().profileInfo_;
    142         chrome.send('requestHasProfileShortcuts', [profileInfo.filePath]);
    143       }
    144 
    145       $('manage-profile-name').focus();
    146     },
    147 
    148     /**
    149      * Registers event handlers that are common between create and manage modes.
    150      * @param {string} mode A label that specifies the type of dialog
    151      *     box which is currently being viewed (i.e. 'create' or
    152      *     'manage').
    153      * @param {function()} submitFunction The function that should be called
    154      *     when the user chooses to submit (e.g. by clicking the OK button).
    155      * @private
    156      */
    157     registerCommonEventHandlers_: function(mode, submitFunction) {
    158       var self = this;
    159       $(mode + '-profile-icon-grid').addEventListener('change', function(e) {
    160         self.onIconGridSelectionChanged_(mode);
    161       });
    162       $(mode + '-profile-name').oninput = function(event) {
    163         self.onNameChanged_(event, mode);
    164       };
    165       $(mode + '-profile-ok').onclick = function(event) {
    166         OptionsPage.closeOverlay();
    167         submitFunction();
    168       };
    169     },
    170 
    171     /**
    172      * Set the profile info used in the dialog.
    173      * @param {Object} profileInfo An object of the form:
    174      *     profileInfo = {
    175      *       name: "Profile Name",
    176      *       iconURL: "chrome://path/to/icon/image",
    177      *       filePath: "/path/to/profile/data/on/disk",
    178      *       isCurrentProfile: false,
    179      *       isManaged: false
    180      *     };
    181      * @param {string} mode A label that specifies the type of dialog
    182      *     box which is currently being viewed (i.e. 'create' or
    183      *     'manage').
    184      * @private
    185      */
    186     setProfileInfo_: function(profileInfo, mode) {
    187       this.iconGridSelectedURL_ = profileInfo.iconURL;
    188       this.profileInfo_ = profileInfo;
    189       $(mode + '-profile-name').value = profileInfo.name;
    190       $(mode + '-profile-icon-grid').selectedItem = profileInfo.iconURL;
    191     },
    192 
    193     /**
    194      * Sets the name of the currently edited profile.
    195      * @private
    196      */
    197     setProfileName_: function(name) {
    198       if (this.profileInfo_)
    199         this.profileInfo_.name = name;
    200       $('manage-profile-name').value = name;
    201     },
    202 
    203     /**
    204      * Set an array of default icon URLs. These will be added to the grid that
    205      * the user will use to choose their profile icon.
    206      * @param {Array.<string>} iconURLs An array of icon URLs.
    207      * @private
    208      */
    209     receiveDefaultProfileIcons_: function(iconGrid, iconURLs) {
    210       $(iconGrid).dataModel = new ArrayDataModel(iconURLs);
    211 
    212       if (this.profileInfo_)
    213         $(iconGrid).selectedItem = this.profileInfo_.iconURL;
    214 
    215       var grid = $(iconGrid);
    216       // Recalculate the measured item size.
    217       grid.measured_ = null;
    218       grid.columns = 0;
    219       grid.redraw();
    220     },
    221 
    222     /**
    223      * Callback to set the initial values when creating a new profile.
    224      * @param {Object} profileInfo An object of the form:
    225      *     profileInfo = {
    226      *       name: "Profile Name",
    227      *       iconURL: "chrome://path/to/icon/image",
    228      *     };
    229      * @private
    230      */
    231     receiveNewProfileDefaults_: function(profileInfo) {
    232       ManageProfileOverlay.setProfileInfo(profileInfo, 'create');
    233       $('create-profile-name-label').hidden = false;
    234       $('create-profile-name').hidden = false;
    235       $('create-profile-name').focus();
    236       $('create-profile-ok').disabled = false;
    237     },
    238 
    239     /**
    240      * Set a dictionary of all profile names. These are used to prevent the
    241      * user from naming two profiles the same.
    242      * @param {Object} profileNames A dictionary of profile names.
    243      * @private
    244      */
    245     receiveProfileNames_: function(profileNames) {
    246       this.profileNames_ = profileNames;
    247     },
    248 
    249     /**
    250      * Callback to show the add/remove shortcut buttons when in edit mode,
    251      * called by the handler as a result of the 'requestHasProfileShortcuts_'
    252      * message.
    253      * @param {boolean} hasShortcuts Whether profile has any existing shortcuts.
    254      * @private
    255      */
    256     receiveHasProfileShortcuts_: function(hasShortcuts) {
    257       $('add-shortcut-button').hidden = hasShortcuts;
    258       $('remove-shortcut-button').hidden = !hasShortcuts;
    259     },
    260 
    261     /**
    262      * Display the error bubble, with |errorText| in the bubble.
    263      * @param {string} errorText The localized string id to display as an error.
    264      * @param {string} mode A label that specifies the type of dialog
    265      *     box which is currently being viewed (i.e. 'create' or
    266      *     'manage').
    267      * @param {boolean} disableOKButton True if the dialog's OK button should be
    268      *     disabled when the error bubble is shown. It will be (re-)enabled when
    269      *     the error bubble is hidden.
    270      * @private
    271      */
    272     showErrorBubble_: function(errorText, mode, disableOKButton) {
    273       var nameErrorEl = $(mode + '-profile-error-bubble');
    274       nameErrorEl.hidden = false;
    275       nameErrorEl.textContent = loadTimeData.getString(errorText);
    276 
    277       if (disableOKButton)
    278         $(mode + '-profile-ok').disabled = true;
    279     },
    280 
    281     /**
    282      * Hide the error bubble.
    283      * @param {string} mode A label that specifies the type of dialog
    284      *     box which is currently being viewed (i.e. 'create' or
    285      *     'manage').
    286      * @private
    287      */
    288     hideErrorBubble_: function(mode) {
    289       $(mode + '-profile-error-bubble').hidden = true;
    290       $(mode + '-profile-ok').disabled = false;
    291     },
    292 
    293     /**
    294      * oninput callback for <input> field.
    295      * @param {Event} event The event object.
    296      * @param {string} mode A label that specifies the type of dialog
    297      *     box which is currently being viewed (i.e. 'create' or
    298      *     'manage').
    299      * @private
    300      */
    301     onNameChanged_: function(event, mode) {
    302       var newName = event.target.value;
    303       var oldName = this.profileInfo_.name;
    304 
    305       if (newName == oldName) {
    306         this.hideErrorBubble_(mode);
    307       } else if (this.profileNames_[newName] != undefined) {
    308         this.showErrorBubble_('manageProfilesDuplicateNameError', mode, true);
    309       } else {
    310         this.hideErrorBubble_(mode);
    311 
    312         var nameIsValid = $(mode + '-profile-name').validity.valid;
    313         $(mode + '-profile-ok').disabled = !nameIsValid;
    314       }
    315     },
    316 
    317     /**
    318      * Called when the user clicks "OK" or hits enter. Saves the newly changed
    319      * profile info.
    320      * @private
    321      */
    322     submitManageChanges_: function() {
    323       var name = $('manage-profile-name').value;
    324       var iconURL = $('manage-profile-icon-grid').selectedItem;
    325 
    326       chrome.send('setProfileIconAndName',
    327                   [this.profileInfo_.filePath, iconURL, name]);
    328     },
    329 
    330     /**
    331      * Called when the user clicks "OK" or hits enter. Creates the profile
    332      * using the information in the dialog.
    333      * @private
    334      */
    335     submitCreateProfile_: function() {
    336       // This is visual polish: the UI to access this should be disabled for
    337       // managed users, and the back end will prevent user creation anyway.
    338       if (this.profileInfo_ && this.profileInfo_.isManaged)
    339         return;
    340 
    341       this.hideErrorBubble_('create');
    342       CreateProfileOverlay.updateCreateInProgress(true);
    343 
    344       // Get the user's chosen name and icon, or default if they do not
    345       // wish to customize their profile.
    346       var name = $('create-profile-name').value;
    347       var iconUrl = $('create-profile-icon-grid').selectedItem;
    348       var createShortcut = $('create-shortcut').checked;
    349       var isManaged = $('create-profile-managed').checked;
    350       var existingManagedUserId = '';
    351       if ($('select-existing-managed-profile-checkbox').checked) {
    352         var selectElement = $('choose-existing-managed-profile');
    353         existingManagedUserId =
    354             selectElement.options[selectElement.selectedIndex].value;
    355         name = selectElement.options[selectElement.selectedIndex].text;
    356       }
    357 
    358       // 'createProfile' is handled by the BrowserOptionsHandler.
    359       chrome.send('createProfile',
    360                   [name, iconUrl, createShortcut,
    361                    isManaged, existingManagedUserId]);
    362     },
    363 
    364     /**
    365      * Called when the selected icon in the icon grid changes.
    366      * @param {string} mode A label that specifies the type of dialog
    367      *     box which is currently being viewed (i.e. 'create' or
    368      *     'manage').
    369      * @private
    370      */
    371     onIconGridSelectionChanged_: function(mode) {
    372       var iconURL = $(mode + '-profile-icon-grid').selectedItem;
    373       if (!iconURL || iconURL == this.iconGridSelectedURL_)
    374         return;
    375       this.iconGridSelectedURL_ = iconURL;
    376       if (this.profileInfo_ && this.profileInfo_.filePath) {
    377         chrome.send('profileIconSelectionChanged',
    378                     [this.profileInfo_.filePath, iconURL]);
    379       }
    380     },
    381 
    382     /**
    383      * Updates the contents of the "Manage Profile" section of the dialog,
    384      * and shows that section.
    385      * @private
    386      */
    387     prepareForManageDialog_: function() {
    388       var profileInfo = BrowserOptions.getCurrentProfile();
    389       ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
    390       $('manage-profile-overlay-create').hidden = true;
    391       $('manage-profile-overlay-manage').hidden = false;
    392       $('manage-profile-overlay-delete').hidden = true;
    393       $('manage-profile-name').disabled = profileInfo.isManaged;
    394       this.hideErrorBubble_('manage');
    395     },
    396 
    397     /**
    398      * Display the "Manage Profile" dialog.
    399      * @private
    400      */
    401     showManageDialog_: function() {
    402       this.prepareForManageDialog_();
    403       OptionsPage.navigateToPage('manageProfile');
    404     },
    405 
    406     /**
    407      * Display the "Delete Profile" dialog.
    408      * @param {Object} profileInfo The profile object of the profile to delete.
    409      * @private
    410      */
    411     showDeleteDialog_: function(profileInfo) {
    412       if (BrowserOptions.getCurrentProfile().isManaged)
    413         return;
    414 
    415       ManageProfileOverlay.setProfileInfo(profileInfo, 'manage');
    416       $('manage-profile-overlay-create').hidden = true;
    417       $('manage-profile-overlay-manage').hidden = true;
    418       $('manage-profile-overlay-delete').hidden = false;
    419       $('delete-profile-message').textContent =
    420           loadTimeData.getStringF('deleteProfileMessage', profileInfo.name);
    421       $('delete-profile-message').style.backgroundImage = 'url("' +
    422           profileInfo.iconURL + '")';
    423       $('delete-managed-profile-addendum').hidden = !profileInfo.isManaged;
    424 
    425       // Because this dialog isn't useful when refreshing or as part of the
    426       // history, don't create a history entry for it when showing.
    427       OptionsPage.showPageByName('manageProfile', false);
    428     },
    429 
    430     /**
    431      * Display the "Create Profile" dialog.
    432      * @private
    433      */
    434     showCreateDialog_: function() {
    435       OptionsPage.navigateToPage('createProfile');
    436     },
    437   };
    438 
    439   // Forward public APIs to private implementations.
    440   [
    441     'receiveDefaultProfileIcons',
    442     'receiveNewProfileDefaults',
    443     'receiveProfileNames',
    444     'receiveHasProfileShortcuts',
    445     'setProfileInfo',
    446     'setProfileName',
    447     'showManageDialog',
    448     'showDeleteDialog',
    449     'showCreateDialog',
    450   ].forEach(function(name) {
    451     ManageProfileOverlay[name] = function() {
    452       var instance = ManageProfileOverlay.getInstance();
    453       return instance[name + '_'].apply(instance, arguments);
    454     };
    455   });
    456 
    457   function CreateProfileOverlay() {
    458     OptionsPage.call(this, 'createProfile',
    459                      loadTimeData.getString('createProfileTabTitle'),
    460                      'manage-profile-overlay');
    461   };
    462 
    463   cr.addSingletonGetter(CreateProfileOverlay);
    464 
    465   CreateProfileOverlay.prototype = {
    466     // Inherit from ManageProfileOverlay.
    467     __proto__: ManageProfileOverlay.prototype,
    468 
    469     // The signed-in email address of the current profile, or empty if they're
    470     // not signed in.
    471     signedInEmail_: '',
    472 
    473     /** @override */
    474     canShowPage: function() {
    475       return !BrowserOptions.getCurrentProfile().isManaged;
    476     },
    477 
    478     /**
    479      * Configures the overlay to the "create user" mode.
    480      * @override
    481      */
    482     didShowPage: function() {
    483       chrome.send('requestCreateProfileUpdate');
    484       chrome.send('requestDefaultProfileIcons');
    485       chrome.send('requestNewProfileDefaults');
    486       chrome.send('requestExistingManagedUsers');
    487 
    488       $('manage-profile-overlay-create').hidden = false;
    489       $('manage-profile-overlay-manage').hidden = true;
    490       $('manage-profile-overlay-delete').hidden = true;
    491       $('create-profile-instructions').textContent =
    492          loadTimeData.getStringF('createProfileInstructions');
    493       this.hideErrorBubble_();
    494       this.updateCreateInProgress_(false);
    495 
    496       var shortcutsEnabled = loadTimeData.getBoolean('profileShortcutsEnabled');
    497       $('create-shortcut-container').hidden = !shortcutsEnabled;
    498       $('create-shortcut').checked = shortcutsEnabled;
    499 
    500       $('create-profile-name-label').hidden = true;
    501       $('create-profile-name').hidden = true;
    502       $('create-profile-ok').disabled = true;
    503 
    504       $('create-profile-managed').checked = false;
    505       $('create-profile-managed-signed-in').disabled = true;
    506       $('create-profile-managed-signed-in').hidden = true;
    507       $('create-profile-managed-not-signed-in').hidden = true;
    508       $('select-existing-managed-profile-checkbox').disabled = true;
    509       $('select-existing-managed-profile-checkbox').checked = false;
    510       $('choose-existing-managed-profile').disabled = true;
    511     },
    512 
    513     /** @override */
    514     handleCancel: function() {
    515       this.cancelCreateProfile_();
    516     },
    517 
    518     /** @override */
    519     showErrorBubble_: function(errorText) {
    520       ManageProfileOverlay.getInstance().showErrorBubble_(errorText,
    521                                                           'create',
    522                                                           false);
    523     },
    524 
    525     /** @override */
    526     hideErrorBubble_: function() {
    527       ManageProfileOverlay.getInstance().hideErrorBubble_('create');
    528     },
    529 
    530     /**
    531      * Updates the UI when a profile create step begins or ends.
    532      * Note that hideErrorBubble_() also enables the "OK" button, so it
    533      * must be called before this function if both are used.
    534      * @param {boolean} inProgress True if the UI should be updated to show that
    535      *     profile creation is now in progress.
    536      * @private
    537      */
    538     updateCreateInProgress_: function(inProgress) {
    539       $('create-profile-icon-grid').disabled = inProgress;
    540       $('create-profile-name').disabled = inProgress;
    541       $('create-shortcut').disabled = inProgress;
    542       $('create-profile-managed').disabled = inProgress;
    543       $('create-profile-ok').disabled = inProgress;
    544 
    545       $('create-profile-throbber').hidden = !inProgress;
    546     },
    547 
    548     /**
    549      * Cancels the creation of the a profile. It is safe to call this even
    550      * when no profile is in the process of being created.
    551      * @private
    552      */
    553     cancelCreateProfile_: function() {
    554       OptionsPage.closeOverlay();
    555       chrome.send('cancelCreateProfile');
    556       this.hideErrorBubble_();
    557       this.updateCreateInProgress_(false);
    558     },
    559 
    560     /**
    561      * Shows an error message describing a local error (most likely a disk
    562      * error) when creating a new profile. Called by BrowserOptions via the
    563      * BrowserOptionsHandler.
    564      * @private
    565      */
    566     onLocalError_: function() {
    567       this.updateCreateInProgress_(false);
    568       this.showErrorBubble_('createProfileLocalError');
    569     },
    570 
    571     /**
    572      * Shows an error message describing a remote error (most likely a network
    573      * error) when creating a new profile. Called by BrowserOptions via the
    574      * BrowserOptionsHandler.
    575      * @private
    576      */
    577     onRemoteError_: function() {
    578       this.updateCreateInProgress_(false);
    579       this.showErrorBubble_('createProfileRemoteError');
    580     },
    581 
    582     /**
    583      * For new supervised users, shows a confirmation page after successfully
    584      * creating a new profile; otherwise, the handler will open a new window.
    585      * @param {Object} profileInfo An object of the form:
    586      *     profileInfo = {
    587      *       name: "Profile Name",
    588      *       filePath: "/path/to/profile/data/on/disk"
    589      *       isManaged: (true|false),
    590      *     };
    591      * @private
    592      */
    593     onSuccess_: function(profileInfo) {
    594       this.updateCreateInProgress_(false);
    595       OptionsPage.closeOverlay();
    596       if (profileInfo.isManaged) {
    597         profileInfo.custodianEmail = this.signedInEmail_;
    598         ManagedUserCreateConfirmOverlay.setProfileInfo(profileInfo);
    599         OptionsPage.showPageByName('managedUserCreateConfirm', false);
    600       }
    601     },
    602 
    603     /**
    604      * Updates the signed-in or not-signed-in UI when in create mode. Called by
    605      * the handler in response to the 'requestCreateProfileUpdate' message.
    606      * updateManagedUsersAllowed_ is expected to be called after this is, and
    607      * will update additional UI elements.
    608      * @param {string} email The email address of the currently signed-in user.
    609      *     An empty string indicates that the user is not signed in.
    610      * @param {boolean} hasError Whether the user's sign-in credentials are
    611      *     still valid.
    612      * @private
    613      */
    614     updateSignedInStatus_: function(email, hasError) {
    615       this.signedInEmail_ = email;
    616       this.hasError_ = hasError;
    617       var isSignedIn = email !== '';
    618       $('create-profile-managed-signed-in').hidden = !isSignedIn;
    619       $('create-profile-managed-not-signed-in').hidden = isSignedIn;
    620       var hideSelectExistingManagedUsers =
    621           !isSignedIn ||
    622           !loadTimeData.getBoolean('allowCreateExistingManagedUsers');
    623       $('select-existing-managed-profile-checkbox').hidden =
    624           hideSelectExistingManagedUsers;
    625       $('choose-existing-managed-profile').hidden =
    626           hideSelectExistingManagedUsers;
    627 
    628       if (isSignedIn) {
    629         var accountDetailsOutOfDate =
    630             $('create-profile-managed-account-details-out-of-date-label');
    631         accountDetailsOutOfDate.textContent = loadTimeData.getStringF(
    632             'manageProfilesManagedAccountDetailsOutOfDate', email);
    633         accountDetailsOutOfDate.hidden = !hasError;
    634 
    635         $('create-profile-managed-signed-in-label').textContent =
    636             loadTimeData.getStringF(
    637                 'manageProfilesManagedSignedInLabel', email);
    638         $('create-profile-managed-signed-in-label').hidden = hasError;
    639 
    640         $('create-profile-managed-sign-in-again-link').hidden = !hasError;
    641         $('create-profile-managed-signed-in-learn-more-link').hidden = hasError;
    642       }
    643     },
    644 
    645     /**
    646      * Updates the status of the "create managed user" checkbox. Called by the
    647      * handler in response to the 'requestCreateProfileUpdate' message or a
    648      * change in the (policy-controlled) pref that prohibits creating managed
    649      * users, after the signed-in status has been updated.
    650      * @param {boolean} allowed True if creating managed users should be
    651      *     allowed.
    652      * @private
    653      */
    654     updateManagedUsersAllowed_: function(allowed) {
    655       var isSignedIn = this.signedInEmail_ !== '';
    656       $('create-profile-managed').disabled =
    657           !isSignedIn || !allowed || this.hasError_;
    658 
    659       $('create-profile-managed-not-signed-in-link').hidden = !allowed;
    660       if (!allowed) {
    661         $('create-profile-managed-indicator').setAttribute('controlled-by',
    662                                                            'policy');
    663       } else {
    664         $('create-profile-managed-indicator').removeAttribute('controlled-by');
    665       }
    666     },
    667 
    668     /**
    669      * Populates a dropdown menu with the existing managed users attached
    670      * to the current custodians profile.
    671      * @param {Object} managedUsers A dictionary of managed users IDs and
    672      *     names.
    673      */
    674     receiveExistingManagedUsers_: function(managedUsers) {
    675       var managedUsersArray = Object.keys(managedUsers).map(function(id) {
    676         return {'id': id, 'name': managedUsers[id]};
    677       });
    678 
    679       // No existing managed users, so hide the UI elements.
    680       var hide = managedUsersArray.length == 0 ||
    681           !loadTimeData.getBoolean('allowCreateExistingManagedUsers');
    682       $('select-existing-managed-profile').hidden = hide;
    683       $('choose-existing-managed-profile').hidden = hide;
    684       if (hide) {
    685         $('select-existing-managed-profile-checkbox').checked = false;
    686         return;
    687       }
    688 
    689       // Sort by name.
    690       managedUsersArray.sort(function compare(a, b) {
    691         return a.name.localeCompare(b.name);
    692       });
    693 
    694       // Clear the dropdown list.
    695       while ($('choose-existing-managed-profile').options.length > 0)
    696         $('choose-existing-managed-profile').options.remove(0);
    697 
    698       // Populate the dropdown list.
    699       managedUsersArray.forEach(function(user) {
    700         $('choose-existing-managed-profile').options.add(
    701             new Option(user.name, user.id));
    702       });
    703     },
    704   };
    705 
    706   // Forward public APIs to private implementations.
    707   [
    708     'cancelCreateProfile',
    709     'onLocalError',
    710     'onRemoteError',
    711     'onSuccess',
    712     'receiveExistingManagedUsers',
    713     'updateCreateInProgress',
    714     'updateManagedUsersAllowed',
    715     'updateSignedInStatus',
    716   ].forEach(function(name) {
    717     CreateProfileOverlay[name] = function() {
    718       var instance = CreateProfileOverlay.getInstance();
    719       return instance[name + '_'].apply(instance, arguments);
    720     };
    721   });
    722 
    723   // Export
    724   return {
    725     ManageProfileOverlay: ManageProfileOverlay,
    726     CreateProfileOverlay: CreateProfileOverlay,
    727   };
    728 });
    729