Commit 24241774 authored by akuegel@chromium.org's avatar akuegel@chromium.org

Replace own callback handling with Promises.

Simplify the code by using DOM Promises.

BUG=282464
TEST=browser_tests

Review URL: https://codereview.chromium.org/132013002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245264 0039d316-1c4b-4281-b951-d872f2087c98
parent ae6f005a
...@@ -67,7 +67,7 @@ cr.define('options', function() { ...@@ -67,7 +67,7 @@ cr.define('options', function() {
if (BrowserOptions.getCurrentProfile().isManaged) if (BrowserOptions.getCurrentProfile().isManaged)
return; return;
chrome.send('deleteProfile', [self.profileInfo_.filePath]); chrome.send('deleteProfile', [self.profileInfo_.filePath]);
options.ManagedUserListData.reloadExistingManagedUsers(); options.ManagedUserListData.resetPromise();
}; };
$('add-shortcut-button').onclick = function(event) { $('add-shortcut-button').onclick = function(event) {
chrome.send('addProfileShortcut', [self.profileInfo_.filePath]); chrome.send('addProfileShortcut', [self.profileInfo_.filePath]);
...@@ -276,6 +276,7 @@ cr.define('options', function() { ...@@ -276,6 +276,7 @@ cr.define('options', function() {
* @private * @private
*/ */
hideErrorBubble_: function(mode) { hideErrorBubble_: function(mode) {
$(mode + '-profile-error-bubble').innerHTML = '';
$(mode + '-profile-error-bubble').hidden = true; $(mode + '-profile-error-bubble').hidden = true;
$(mode + '-profile-ok').disabled = false; $(mode + '-profile-ok').disabled = false;
}, },
...@@ -294,33 +295,17 @@ cr.define('options', function() { ...@@ -294,33 +295,17 @@ cr.define('options', function() {
// existing supervised user. // existing supervised user.
if (newName == oldName && mode == 'manage') { if (newName == oldName && mode == 'manage') {
this.hideErrorBubble_(mode); this.hideErrorBubble_(mode);
} else if (this.profileNames_[newName] != undefined) {
var errorHtml =
loadTimeData.getString('manageProfilesDuplicateNameError');
this.showErrorBubble_(errorHtml, mode, true);
} else if (mode == 'create' && } else if (mode == 'create' &&
loadTimeData.getBoolean('allowCreateExistingManagedUsers')) { loadTimeData.getBoolean('allowCreateExistingManagedUsers') &&
this.checkIfSupervisedUserExists_(); $('create-profile-managed').checked) {
options.ManagedUserListData.requestExistingManagedUsers().then(
this.receiveExistingManagedUsers_.bind(this),
this.onSigninError_.bind(this));
} else { } else {
this.updateOkButton_(mode); this.updateOkButton_(mode);
} }
}, },
/**
* Checks if a supervised user with the currently entered name already
* exists.
* @private
*/
checkIfSupervisedUserExists_: function() {
if (!$('create-profile-managed').checked) {
this.updateOkButton_('create');
return;
}
options.ManagedUserListData.requestExistingManagedUsers(
this.receiveExistingManagedUsers_.bind(this),
this.onSigninError_.bind(this));
},
/** /**
* Callback which receives the list of existing managed users. Checks if the * Callback which receives the list of existing managed users. Checks if the
* currently entered name is the name of an already existing managed user. * currently entered name is the name of an already existing managed user.
...@@ -376,20 +361,27 @@ cr.define('options', function() { ...@@ -376,20 +361,27 @@ cr.define('options', function() {
*/ */
onSigninError_: function() { onSigninError_: function() {
this.updateImportExistingManagedUserLink_(false); this.updateImportExistingManagedUserLink_(false);
this.updateManagedUsersAllowed_(false);
}, },
/** /**
* Called if the name seems to be unused so far. * Called to update the state of the ok button depending if the name is
* already used or not.
* @param {string} mode A label that specifies the type of dialog box which * @param {string} mode A label that specifies the type of dialog box which
* is currently being viewed (i.e. 'create' or 'manage'). * is currently being viewed (i.e. 'create' or 'manage').
* @private * @private
*/ */
updateOkButton_: function(mode) { updateOkButton_: function(mode) {
var newName = $(mode + '-profile-name').value;
if (this.profileNames_[newName] != undefined) {
var errorHtml =
loadTimeData.getString('manageProfilesDuplicateNameError');
this.showErrorBubble_(errorHtml, mode, true);
} else {
this.hideErrorBubble_(mode); this.hideErrorBubble_(mode);
var nameIsValid = $(mode + '-profile-name').validity.valid; var nameIsValid = $(mode + '-profile-name').validity.valid;
$(mode + '-profile-ok').disabled = !nameIsValid; $(mode + '-profile-ok').disabled = !nameIsValid;
}
}, },
/** /**
...@@ -403,6 +395,8 @@ cr.define('options', function() { ...@@ -403,6 +395,8 @@ cr.define('options', function() {
chrome.send('setProfileIconAndName', chrome.send('setProfileIconAndName',
[this.profileInfo_.filePath, iconURL, name]); [this.profileInfo_.filePath, iconURL, name]);
if (name != this.profileInfo_.name)
options.ManagedUserListData.resetPromise();
}, },
/** /**
...@@ -670,7 +664,7 @@ cr.define('options', function() { ...@@ -670,7 +664,7 @@ cr.define('options', function() {
this.updateCreateInProgress_(false); this.updateCreateInProgress_(false);
OptionsPage.closeOverlay(); OptionsPage.closeOverlay();
if (profileInfo.isManaged) { if (profileInfo.isManaged) {
options.ManagedUserListData.reloadExistingManagedUsers(); options.ManagedUserListData.resetPromise();
profileInfo.custodianEmail = this.signedInEmail_; profileInfo.custodianEmail = this.signedInEmail_;
ManagedUserCreateConfirmOverlay.setProfileInfo(profileInfo); ManagedUserCreateConfirmOverlay.setProfileInfo(profileInfo);
OptionsPage.showPageByName('managedUserCreateConfirm', false); OptionsPage.showPageByName('managedUserCreateConfirm', false);
......
...@@ -75,7 +75,7 @@ cr.define('options', function() { ...@@ -75,7 +75,7 @@ cr.define('options', function() {
* @override * @override
*/ */
didShowPage: function() { didShowPage: function() {
options.ManagedUserListData.requestExistingManagedUsers( options.ManagedUserListData.requestExistingManagedUsers().then(
this.receiveExistingManagedUsers_, this.onSigninError_.bind(this)); this.receiveExistingManagedUsers_, this.onSigninError_.bind(this));
this.updateImportInProgress_(false); this.updateImportInProgress_(false);
...@@ -173,6 +173,8 @@ cr.define('options', function() { ...@@ -173,6 +173,8 @@ cr.define('options', function() {
*/ */
receiveExistingManagedUsers_: function(managedUsers) { receiveExistingManagedUsers_: function(managedUsers) {
managedUsers.sort(function(a, b) { managedUsers.sort(function(a, b) {
if (a.onCurrentDevice != b.onCurrentDevice)
return a.onCurrentDevice ? 1 : -1;
return a.name.localeCompare(b.name); return a.name.localeCompare(b.name);
}); });
......
...@@ -6,34 +6,18 @@ cr.define('options', function() { ...@@ -6,34 +6,18 @@ cr.define('options', function() {
/** /**
* ManagedUserListData class. * ManagedUserListData class.
* Handles requests for retrieving a list of existing managed users which are * Handles requests for retrieving a list of existing managed users which are
* supervised by the current profile. This list is cached in order to make it * supervised by the current profile. For each request a promise is returned,
* possible to serve future requests immediately. The first request will be * which is cached in order to reuse the retrieved managed users for future
* handled asynchronously. * requests. The first request will be handled asynchronously.
* @constructor * @constructor
* @class * @class
*/ */
function ManagedUserListData() { function ManagedUserListData() {};
this.callbacks_ = [];
this.errbacks_ = [];
this.requestInProgress_ = false;
this.managedUsers_ = null;
};
cr.addSingletonGetter(ManagedUserListData); cr.addSingletonGetter(ManagedUserListData);
/** /**
* Resets to the initial state of no pending requests. * Receives a list of managed users and resolves the promise.
* @private
*/
ManagedUserListData.prototype.reset_ = function() {
this.callbacks_ = [];
this.errbacks_ = [];
this.requestInProgress_ = false;
}
/**
* Receives a list of managed users and passes the list to each of the
* callbacks.
* @param {Array.<Object>} managedUsers An array of managed user objects. * @param {Array.<Object>} managedUsers An array of managed user objects.
* Each object is of the form: * Each object is of the form:
* managedUser = { * managedUser = {
...@@ -43,70 +27,77 @@ cr.define('options', function() { ...@@ -43,70 +27,77 @@ cr.define('options', function() {
* onCurrentDevice: true or false, * onCurrentDevice: true or false,
* needAvatar: true or false * needAvatar: true or false
* } * }
* @private
*/ */
ManagedUserListData.receiveExistingManagedUsers = function(managedUsers) { ManagedUserListData.prototype.receiveExistingManagedUsers_ = function(
var instance = ManagedUserListData.getInstance(); managedUsers) {
var i; assert(this.promise_);
for (i = 0; i < instance.callbacks_.length; i++) { this.resolve_(managedUsers);
instance.callbacks_[i](managedUsers);
}
instance.managedUsers_ = managedUsers;
instance.reset_();
}; };
/** /**
* Called when there is a signin error when retrieving the list of managed * Called when there is a signin error when retrieving the list of managed
* users. Calls the error callbacks which will display an appropriate error * users. Rejects the promise and resets the cached promise to null.
* message to the user. * @private
*/ */
ManagedUserListData.onSigninError = function() { ManagedUserListData.prototype.onSigninError_ = function() {
var instance = ManagedUserListData.getInstance(); assert(this.promise_);
var i; this.reject_();
for (i = 0; i < instance.errbacks_.length; i++) { this.resetPromise_();
instance.errbacks_[i]();
}
// Reset the list of managed users in order to avoid showing stale data.
instance.managedUsers_ = null;
instance.reset_();
}; };
/** /**
* Handles the request for the list of existing managed users. If the data is * Handles the request for the list of existing managed users by returning a
* already available, it will call |callback| immediately. Otherwise, it * promise for the requested data. If there is no cached promise yet, a new
* retrieves the list of existing managed users which is then processed in * one will be created.
* receiveExistingManagedUsers(). * @return {Promise} The promise containing the list of managed users.
* @param {Object} callback The callback function which is called on success. * @private
* @param {Object} errback the callback function which is called on error.
*/ */
ManagedUserListData.requestExistingManagedUsers = function(callback, ManagedUserListData.prototype.requestExistingManagedUsers_ = function() {
errback) { if (this.promise_)
var instance = ManagedUserListData.getInstance(); return this.promise_;
instance.callbacks_.push(callback); this.promise_ = this.createPromise_();
instance.errbacks_.push(errback);
if (!instance.requestInProgress_) {
if (instance.managedUsers_ != null) {
ManagedUserListData.receiveExistingManagedUsers(instance.managedUsers_);
return;
}
instance.requestInProgress_ = true;
chrome.send('requestManagedUserImportUpdate'); chrome.send('requestManagedUserImportUpdate');
} return this.promise_;
}; };
/** /**
* Reload the list of existing managed users. Should be called when a new * Creates the promise containing the list of managed users. The promise is
* supervised user profile was created or a supervised user profile was * resolved in receiveExistingManagedUsers_() or rejected in
* deleted. * onSigninError_(). The promise is cached, so that for future requests it can
* be resolved immediately.
* @return {Promise} The promise containing the list of managed users.
* @private
*/ */
ManagedUserListData.reloadExistingManagedUsers = function() { ManagedUserListData.prototype.createPromise_ = function() {
var instance = ManagedUserListData.getInstance(); var self = this;
if (instance.requestInProgress_) return new Promise(function(resolve, reject) {
return; self.resolve_ = resolve;
self.reject_ = reject;
});
};
instance.managedUsers_ = null; /**
instance.requestInProgress_ = true; * Resets the promise to null in order to avoid stale data. For the next
chrome.send('requestManagedUserImportUpdate'); * request, a new promise will be created.
* @private
*/
ManagedUserListData.prototype.resetPromise_ = function() {
this.promise_ = null;
};
// Forward public APIs to private implementations.
[
'onSigninError',
'receiveExistingManagedUsers',
'requestExistingManagedUsers',
'resetPromise',
].forEach(function(name) {
ManagedUserListData[name] = function() {
var instance = ManagedUserListData.getInstance();
return instance[name + '_'].apply(instance, arguments);
}; };
});
// Export // Export
return { return {
......
...@@ -140,19 +140,19 @@ TEST_F('ManageProfileUITest', 'CreateManagedUserText', function() { ...@@ -140,19 +140,19 @@ TEST_F('ManageProfileUITest', 'CreateManagedUserText', function() {
assertTrue($('create-profile-managed').disabled); assertTrue($('create-profile-managed').disabled);
}); });
function ManageProfileUITestAsync() {}
ManageProfileUITestAsync.prototype = {
__proto__: ManageProfileUITest.prototype,
isAsync: true,
};
// The import link should show up if the user tries to create a profile with the // The import link should show up if the user tries to create a profile with the
// same name as an existing managed user profile. // same name as an existing managed user profile.
TEST_F('ManageProfileUITest', 'CreateExistingManagedUser', function() { TEST_F('ManageProfileUITestAsync', 'CreateExistingManagedUser', function() {
ManageProfileOverlay.getInstance().initializePage();
var custodianEmail = 'chrome.playpen.test@gmail.com';
CreateProfileOverlay.updateSignedInStatus(custodianEmail);
assertEquals(custodianEmail,
CreateProfileOverlay.getInstance().signedInEmail_);
this.setProfileManaged_(false, 'create');
// Initialize the list of existing managed users. // Initialize the list of existing managed users.
var managedUserListData = options.ManagedUserListData.getInstance(); var managedUsers = [
managedUserListData.managedUsers_ = [
{ {
id: 'managedUser1', id: 'managedUser1',
name: 'Rosalie', name: 'Rosalie',
...@@ -173,10 +173,29 @@ TEST_F('ManageProfileUITest', 'CreateExistingManagedUser', function() { ...@@ -173,10 +173,29 @@ TEST_F('ManageProfileUITest', 'CreateExistingManagedUser', function() {
iconURL: 'chrome://path/to/icon/image', iconURL: 'chrome://path/to/icon/image',
onCurrentDevice: true, onCurrentDevice: true,
needAvatar: false needAvatar: false
},
{
id: 'managedUser4',
name: 'SameName',
iconURL: 'chrome://path/to/icon/image',
onCurrentDevice: false,
needAvatar: false
}]; }];
// Also add the name 'Test' to |profileNames_| to simulate that the profile var promise = Promise.resolve(managedUsers);
// exists on the device. options.ManagedUserListData.getInstance().promise_ = promise;
// Initialize the ManageProfileOverlay.
ManageProfileOverlay.getInstance().initializePage();
var custodianEmail = 'chrome.playpen.test@gmail.com';
CreateProfileOverlay.updateSignedInStatus(custodianEmail);
assertEquals(custodianEmail,
CreateProfileOverlay.getInstance().signedInEmail_);
this.setProfileManaged_(false, 'create');
// Also add the names 'Test' and 'Test2' to |profileNames_| to simulate that
// profiles with those names exist on the device.
ManageProfileOverlay.getInstance().profileNames_.Test = true; ManageProfileOverlay.getInstance().profileNames_.Test = true;
ManageProfileOverlay.getInstance().profileNames_.SameName = true;
// Initially, the ok button should be enabled and the import link should not // Initially, the ok button should be enabled and the import link should not
// exist. // exist.
...@@ -189,24 +208,45 @@ TEST_F('ManageProfileUITest', 'CreateExistingManagedUser', function() { ...@@ -189,24 +208,45 @@ TEST_F('ManageProfileUITest', 'CreateExistingManagedUser', function() {
// A profile which already has an avatar. // A profile which already has an avatar.
nameField.value = 'Rosalie'; nameField.value = 'Rosalie';
ManageProfileOverlay.getInstance().onNameChanged_('create'); ManageProfileOverlay.getInstance().onNameChanged_('create');
// Need to wait until the promise resolves.
promise.then(function() {
assertTrue($('create-profile-ok').disabled); assertTrue($('create-profile-ok').disabled);
assertFalse($('supervised-user-import') == null); assertFalse($('supervised-user-import') == null);
// A profile which doesn't have an avatar yet. // A profile which doesn't have an avatar yet.
nameField.value = 'Fritz'; nameField.value = 'Fritz';
ManageProfileOverlay.getInstance().onNameChanged_('create'); ManageProfileOverlay.getInstance().onNameChanged_('create');
return options.ManagedUserListData.getInstance().promise_;
}).then(function() {
assertTrue($('create-profile-ok').disabled); assertTrue($('create-profile-ok').disabled);
assertFalse($('supervised-user-import') == null); assertFalse($('supervised-user-import') == null);
// A profile which already exists on the device. // A profile which already exists on the device.
nameField.value = 'Test'; nameField.value = 'Test';
ManageProfileOverlay.getInstance().onNameChanged_('create'); ManageProfileOverlay.getInstance().onNameChanged_('create');
return options.ManagedUserListData.getInstance().promise_;
}).then(function() {
assertTrue($('create-profile-ok').disabled); assertTrue($('create-profile-ok').disabled);
assertTrue($('supervised-user-import') == null); assertTrue($('supervised-user-import') == null);
// A profile which does not exist on the device, but there is a profile with
// the same name already on the device.
nameField.value = 'SameName';
ManageProfileOverlay.getInstance().onNameChanged_('create');
return options.ManagedUserListData.getInstance().promise_;
}).then(function() {
assertTrue($('create-profile-ok').disabled);
assertFalse($('supervised-user-import') == null);
// A profile which does not exist yet. // A profile which does not exist yet.
nameField.value = 'NewProfileName'; nameField.value = 'NewProfileName';
ManageProfileOverlay.getInstance().onNameChanged_('create'); ManageProfileOverlay.getInstance().onNameChanged_('create');
return options.ManagedUserListData.getInstance().promise_;
}).then(function() {
assertFalse($('create-profile-ok').disabled); assertFalse($('create-profile-ok').disabled);
assertTrue($('supervised-user-import') == null); assertTrue($('supervised-user-import') == null);
testDone();
});
}); });
// Managed users should not be able to edit their profile names, and the initial // Managed users should not be able to edit their profile names, and the initial
...@@ -325,17 +365,23 @@ TEST_F('ManageProfileUITest', 'CreateConfirmationText', function () { ...@@ -325,17 +365,23 @@ TEST_F('ManageProfileUITest', 'CreateConfirmationText', function () {
// independent of whether they were escaped in the setter. // independent of whether they were escaped in the setter.
'It\'s "&lt;HTML&gt; injection" &amp; more!'); 'It\'s "&lt;HTML&gt; injection" &amp; more!');
// Test elision. MAX_LENGTH = 50, minus 3 for the ellipsis. // Test elision. MAX_LENGTH = 50, minus 1 for the ellipsis.
var name47Characters = '01234567890123456789012345678901234567890123456'; var name49Characters = '0123456789012345678901234567890123456789012345678';
var name60Characters = name47Characters + '0123456789012'; var name50Characters = name49Characters + '9';
checkDialog(name60Characters, name47Characters + '...'); var name51Characters = name50Characters + '0';
var name60Characters = name51Characters + '123456789';
checkDialog(name49Characters, name49Characters);
checkDialog(name50Characters, name50Characters);
checkDialog(name51Characters, name49Characters + '\u2026');
checkDialog(name60Characters, name49Characters + '\u2026');
// Test both elision and HTML escaping. The allowed string length is the // Test both elision and HTML escaping. The allowed string length is the
// visible length, not the length including the entity names. // visible length, not the length including the entity names.
name47Characters = name47Characters.replace('0', '&').replace('1', '>'); name49Characters = name49Characters.replace('0', '&').replace('1', '>');
name60Characters = name60Characters.replace('0', '&').replace('1', '>'); name60Characters = name60Characters.replace('0', '&').replace('1', '>');
var escaped = name47Characters.replace('&', '&amp;').replace('>', '&gt;'); var escaped = name49Characters.replace('&', '&amp;').replace('>', '&gt;');
checkDialog(name60Characters, name47Characters + '...', escaped + '...'); checkDialog(
name60Characters, name49Characters + '\u2026', escaped + '\u2026');
}); });
// An additional warning should be shown when deleting a managed user. // An additional warning should be shown when deleting a managed user.
...@@ -469,9 +515,8 @@ TEST_F('ManageProfileUITest', 'ManagedDelete', function() { ...@@ -469,9 +515,8 @@ TEST_F('ManageProfileUITest', 'ManagedDelete', function() {
this.setProfileManaged_(false, 'manage'); this.setProfileManaged_(false, 'manage');
var messages = clickAndListen(); var messages = clickAndListen();
assertEquals(2, messages.length); assertEquals(1, messages.length);
assertEquals('deleteProfile', messages[0]); assertEquals('deleteProfile', messages[0]);
assertEquals('requestManagedUserImportUpdate', messages[1]);
assertEquals('settings', OptionsPage.getTopmostVisiblePage().name); assertEquals('settings', OptionsPage.getTopmostVisiblePage().name);
ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false)); ManageProfileOverlay.showDeleteDialog(this.testProfileInfo_(false));
......
...@@ -122,9 +122,13 @@ void ManagedUserImportHandler::SendExistingManagedUsers( ...@@ -122,9 +122,13 @@ void ManagedUserImportHandler::SendExistingManagedUsers(
DCHECK(dict); DCHECK(dict);
const ProfileInfoCache& cache = const ProfileInfoCache& cache =
g_browser_process->profile_manager()->GetProfileInfoCache(); g_browser_process->profile_manager()->GetProfileInfoCache();
// Collect the ids of local supervised user profiles.
std::set<std::string> managed_user_ids; std::set<std::string> managed_user_ids;
for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) {
if (cache.ProfileIsManagedAtIndex(i))
managed_user_ids.insert(cache.GetManagedUserIdOfProfileAtIndex(i)); managed_user_ids.insert(cache.GetManagedUserIdOfProfileAtIndex(i));
}
base::ListValue managed_users; base::ListValue managed_users;
for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
......
...@@ -401,11 +401,11 @@ function HTMLEscape(original) { ...@@ -401,11 +401,11 @@ function HTMLEscape(original) {
* @param {string} original The original string. * @param {string} original The original string.
* @param {number} maxLength The maximum length allowed for the string. * @param {number} maxLength The maximum length allowed for the string.
* @return {string} The original string if its length does not exceed * @return {string} The original string if its length does not exceed
* |maxLength|. Otherwise the first |maxLength| - 3 characters with '...' * |maxLength|. Otherwise the first |maxLength| - 1 characters with '...'
* appended. * appended.
*/ */
function elide(original, maxLength) { function elide(original, maxLength) {
if (original.length <= maxLength) if (original.length <= maxLength)
return original; return original;
return original.substring(0, maxLength - 3) + '...'; return original.substring(0, maxLength - 1) + '\u2026';
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment