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