Index: chrome/browser/resources/chromeos/login/user_pod_row.js |
diff --git a/chrome/browser/resources/chromeos/login/user_pod_row.js b/chrome/browser/resources/chromeos/login/user_pod_row.js |
index b5090e0424bd942789f72461b2c5e805a467315f..d3918ac3897f98efb460b24ffc8dfedf0c87ea24 100644 |
--- a/chrome/browser/resources/chromeos/login/user_pod_row.js |
+++ b/chrome/browser/resources/chromeos/login/user_pod_row.js |
@@ -37,8 +37,8 @@ cr.define('login', function() { |
var MAX_NUMBER_OF_ROWS_UNDER_SIGNIN_BANNER = 2; |
/** |
- * Variables used for pod placement processing. |
- * Width and height should be synced with computed CSS sizes of pods. |
+ * Variables used for pod placement processing. Width and height should be |
+ * synced with computed CSS sizes of pods. |
*/ |
var POD_WIDTH = 180; |
var POD_HEIGHT = 217; |
@@ -82,7 +82,7 @@ cr.define('login', function() { |
// |
// (1) all user pods have tab index 1 so they are traversed first; |
// (2) when a user pod is activated, its tab index is set to -1 and its |
- // main input field gets focus and tab index 1; |
+ // main input field gets focus and tab index 1; |
// (3) buttons on the header bar have tab index 2 so they follow user pods; |
// (4) Action box buttons have tab index 3 and follow header bar buttons; |
// (5) lastly, focus jumps to the Status Area and back to user pods. |
@@ -258,14 +258,22 @@ cr.define('login', function() { |
}, |
/** |
- * Gets user signin button. |
- * @type {!HTMLInputElement} |
+ * Gets user sign in button. |
+ * @type {!HTMLButtonElement} |
*/ |
get signinButtonElement() { |
return this.querySelector('.signin-button'); |
}, |
/** |
+ * Gets launch app button. |
+ * @type {!HTMLButtonElement} |
+ */ |
+ get launchAppButtonElement() { |
+ return this.querySelector('.launch-app-button'); |
+ }, |
+ |
+ /** |
* Gets action box area. |
* @type {!HTMLInputElement} |
*/ |
@@ -363,8 +371,8 @@ cr.define('login', function() { |
}, |
/** |
- * Gets the custom button. This button is normally hidden, but can be |
- * shown using the chrome.screenlockPrivate API. |
+ * Gets the custom button. This button is normally hidden, but can be shown |
+ * using the chrome.screenlockPrivate API. |
* @type {!HTMLInputElement} |
*/ |
get customButton() { |
@@ -390,11 +398,16 @@ cr.define('login', function() { |
this.passwordElement.setAttribute('aria-label', loadTimeData.getStringF( |
'passwordFieldAccessibleName', this.user_.emailAddress)); |
- this.updateUserTypeIcon(); |
+ this.customizeUserPodPerUserType(); |
}, |
updateActionBoxArea: function() { |
- this.actionBoxAreaElement.hidden = this.user_.publicAccount; |
+ if (this.user_.publicAccount || this.user_.isApp) { |
+ this.actionBoxAreaElement.hidden = true; |
+ return; |
+ } |
+ |
+ this.actionBoxAreaElement.hidden = false; |
this.actionBoxMenuRemoveElement.hidden = !this.user_.canRemove; |
this.actionBoxAreaElement.setAttribute( |
@@ -414,28 +427,33 @@ cr.define('login', function() { |
loadTimeData.getString('removeUser'); |
}, |
- updateUserTypeIcon: function() { |
+ customizeUserPodPerUserType: function() { |
var isMultiProfilesUI = |
(Oobe.getInstance().displayType == DISPLAY_TYPE.USER_ADDING); |
if (this.user_.locallyManagedUser) { |
- this.userTypeIconAreaElement.classList.add('supervised'); |
- this.userTypeIconAreaElement.hidden = false; |
+ this.setUserPodIconType('supervised'); |
} else if (isMultiProfilesUI && !this.user_.isMultiProfilesAllowed) { |
- // Mark user pod as not focusable. |
+ // Mark user pod as not focusable which in addition to the grayed out |
+ // filter makes it look in disabled state. |
this.classList.add('not-focusable'); |
- |
- this.userTypeIconAreaElement.classList.add('policy'); |
- this.userTypeIconAreaElement.hidden = false; |
+ this.setUserPodIconType('policy'); |
this.querySelector('.mp-policy-title').hidden = false; |
- if (this.user_.multiProfilesPolicy == 'primary-only') |
+ if (this.user.multiProfilesPolicy == 'primary-only') |
this.querySelector('.mp-policy-primary-only-msg').hidden = false; |
else |
this.querySelector('.mp-policy-not-allowed-msg').hidden = false; |
+ } else if (this.user_.isApp) { |
+ this.setUserPodIconType('app'); |
} |
}, |
+ setUserPodIconType: function(userTypeClass) { |
+ this.userTypeIconAreaElement.classList.add(userTypeClass); |
+ this.userTypeIconAreaElement.hidden = false; |
+ }, |
+ |
/** |
* The user that this pod represents. |
* @type {!Object} |
@@ -538,10 +556,11 @@ cr.define('login', function() { |
/** |
* Activates the pod. |
+ * @param {Event} e Event object. |
* @return {boolean} True if activated successfully. |
*/ |
- activate: function() { |
- if (!this.signinButtonElement.hidden) { |
+ activate: function(e) { |
+ if (this.forceOnlineSignin) { |
this.showSigninUI(); |
} else if (!this.passwordElement.value) { |
return false; |
@@ -718,7 +737,7 @@ cr.define('login', function() { |
if (this.parentNode.disabled) |
return; |
- if (!this.signinButtonElement.hidden && !this.isActionBoxMenuActive) { |
+ if (this.forceOnlineSignin && !this.isActionBoxMenuActive) { |
this.showSigninUI(); |
// Prevent default so that we don't trigger 'focus' event. |
e.preventDefault(); |
@@ -814,7 +833,7 @@ cr.define('login', function() { |
this.nameElement.addEventListener('keydown', (function(e) { |
if (e.keyIdentifier == 'Enter') { |
- this.parentNode.activatedPod = this; |
+ this.parentNode.setActivatedPod(this, e); |
// Stop this keydown event from bubbling up to PodRow handler. |
e.stopPropagation(); |
// Prevent default so that we don't trigger a 'click' event on the |
@@ -867,7 +886,7 @@ cr.define('login', function() { |
}, |
/** @override */ |
- activate: function() { |
+ activate: function(e) { |
this.expanded = true; |
this.focusInput(); |
return true; |
@@ -879,15 +898,14 @@ cr.define('login', function() { |
return; |
this.parentNode.focusPod(this); |
- this.parentNode.activatedPod = this; |
+ this.parentNode.setActivatedPod(this, e); |
// Prevent default so that we don't trigger 'focus' event. |
e.preventDefault(); |
}, |
/** |
- * Handle mouse and keyboard events for the learn more button. |
- * Triggering the button causes information about public sessions to be |
- * shown. |
+ * Handle mouse and keyboard events for the learn more button. Triggering |
+ * the button causes information about public sessions to be shown. |
* @param {Event} event Mouse or keyboard event. |
*/ |
handleLearnMoreEvent: function(event) { |
@@ -985,7 +1003,7 @@ cr.define('login', function() { |
}, |
/** @override */ |
- activate: function() { |
+ activate: function(e) { |
if (this.passwordElement.hidden) { |
Oobe.launchUser(this.user.emailAddress, this.user.displayName); |
} else if (!this.passwordElement.value) { |
@@ -1011,7 +1029,7 @@ cr.define('login', function() { |
// If this is an unlocked pod, then open a browser window. Otherwise |
// just activate the pod and show the password field. |
if (!this.user.needsSignin && !this.isActionBoxMenuActive) |
- this.activate(); |
+ this.activate(e); |
}, |
/** @override */ |
@@ -1021,6 +1039,110 @@ cr.define('login', function() { |
}; |
/** |
+ * Creates a user pod that represents kiosk app. |
+ * @constructor |
+ * @extends {UserPod} |
+ */ |
+ var KioskAppPod = cr.ui.define(function() { |
+ var node = UserPod(); |
+ return node; |
+ }); |
+ |
+ KioskAppPod.prototype = { |
+ __proto__: UserPod.prototype, |
+ |
+ /** @override */ |
+ decorate: function() { |
+ UserPod.prototype.decorate.call(this); |
+ this.launchAppButtonElement.addEventListener('click', |
+ this.activate.bind(this)); |
+ }, |
+ |
+ /** @override */ |
+ update: function() { |
+ this.imageElement.src = this.user.iconUrl; |
+ this.imageElement.alt = this.user.label; |
+ this.passwordElement.hidden = true; |
+ this.signinButtonElement.hidden = true; |
+ this.launchAppButtonElement.hidden = false; |
+ this.signedInIndicatorElement.hidden = true; |
+ this.nameElement.textContent = this.user.label; |
+ |
+ UserPod.prototype.updateActionBoxArea.call(this); |
+ UserPod.prototype.customizeUserPodPerUserType.call(this); |
+ }, |
+ |
+ /** @override */ |
+ get mainInput() { |
+ return this.launchAppButtonElement; |
+ }, |
+ |
+ /** @override */ |
+ focusInput: function() { |
+ this.signinButtonElement.hidden = true; |
+ this.launchAppButtonElement.hidden = false; |
+ this.passwordElement.hidden = true; |
+ |
+ // Move tabIndex from the whole pod to the main input. |
+ this.tabIndex = -1; |
+ this.mainInput.tabIndex = UserPodTabOrder.POD_INPUT; |
+ this.mainInput.focus(); |
+ }, |
+ |
+ /** @override */ |
+ get forceOnlineSignin() { |
+ return false; |
+ }, |
+ |
+ /** @override */ |
+ activate: function(e) { |
+ var diagnosticMode = e && e.ctrlKey; |
+ this.launchApp_(this.user, diagnosticMode); |
+ return true; |
+ }, |
+ |
+ /** @override */ |
+ handleClickOnPod_: function(e) { |
+ if (this.parentNode.disabled) |
+ return; |
+ |
+ Oobe.clearErrors(); |
+ this.parentNode.lastFocusedPod_ = this; |
+ this.activate(e); |
+ }, |
+ |
+ /** |
+ * Launch the app. If |diagnosticMode| is true, ask user to confirm. |
+ * @param {Object} app App data. |
+ * @param {boolean} diagnosticMode Whether to run the app in diagnostic |
+ * mode. |
+ */ |
+ launchApp_: function(app, diagnosticMode) { |
+ if (!diagnosticMode) { |
+ chrome.send('launchKioskApp', [app.id, false]); |
+ return; |
+ } |
+ |
+ var oobe = $('oobe'); |
+ if (!oobe.confirmDiagnosticMode_) { |
+ oobe.confirmDiagnosticMode_ = |
+ new cr.ui.dialogs.ConfirmDialog(document.body); |
+ oobe.confirmDiagnosticMode_.setOkLabel( |
+ loadTimeData.getString('confirmKioskAppDiagnosticModeYes')); |
+ oobe.confirmDiagnosticMode_.setCancelLabel( |
+ loadTimeData.getString('confirmKioskAppDiagnosticModeNo')); |
+ } |
+ |
+ oobe.confirmDiagnosticMode_.show( |
+ loadTimeData.getStringF('confirmKioskAppDiagnosticModeFormat', |
+ app.label), |
+ function() { |
+ chrome.send('launchKioskApp', [app.id, true]); |
+ }); |
+ }, |
+ }; |
+ |
+ /** |
* Creates a new pod row element. |
* @constructor |
* @extends {HTMLDivElement} |
@@ -1048,6 +1170,15 @@ cr.define('login', function() { |
// Pods whose initial images haven't been loaded yet. |
podsWithPendingImages_: [], |
+ // Whether pod creation is animated. |
+ user_add_is_animated_: false, |
+ |
+ // Array of apps that are shown in addition to other user pods. |
+ apps_: [], |
+ |
+ // Array of users that are shown (public/supervised/regular). |
+ users_: [], |
+ |
/** @override */ |
decorate: function() { |
// Event listeners that are installed for the time period during which |
@@ -1077,10 +1208,24 @@ cr.define('login', function() { |
}, |
/** |
+ * Returns pod with the given app id. |
+ * @param {!string} app_id Application id to be matched. |
+ * @return {Object} Pod with the given app id. null if pod hasn't been |
+ * found. |
+ */ |
+ getPodWithAppId_: function(app_id) { |
+ for (var i = 0, pod; pod = this.pods[i]; ++i) { |
+ if (pod.user.isApp && pod.user.id == app_id) |
+ return pod; |
+ } |
+ return null; |
+ }, |
+ |
+ /** |
* Returns pod with the given username (null if there is no such pod). |
* @param {string} username Username to be matched. |
* @return {Object} Pod with the given username. null if pod hasn't been |
- * found. |
+ * found. |
*/ |
getPodWithUsername_: function(username) { |
for (var i = 0, pod; pod = this.pods[i]; ++i) { |
@@ -1108,7 +1253,7 @@ cr.define('login', function() { |
/** |
* Creates a user pod from given email. |
- * @param {string} email User's email. |
+ * @param {!Object} user User info dictionary. |
*/ |
createUserPod: function(user) { |
var userPod; |
@@ -1116,6 +1261,8 @@ cr.define('login', function() { |
userPod = new DesktopUserPod({user: user}); |
else if (user.publicAccount) |
userPod = new PublicAccountUserPod({user: user}); |
+ else if (user.isApp) |
+ userPod = new KioskAppPod({user: user}); |
else |
userPod = new UserPod({user: user}); |
@@ -1140,6 +1287,23 @@ cr.define('login', function() { |
}, |
/** |
+ * Runs app with a given id from the list of loaded apps. |
+ * @param {!string} app_id of an app to run. |
+ * @param {boolean=} opt_diagnostic_mode Whether to run the app in |
+ * diagnostic mode. Default is false. |
+ */ |
+ findAndRunAppForTesting: function(app_id, opt_diagnostic_mode) { |
+ var app = this.getPodWithAppId_(app_id); |
+ if (app) { |
+ var activationEvent = cr.doc.createEvent('MouseEvents'); |
+ var ctrlKey = opt_diagnostic_mode; |
+ activationEvent.initMouseEvent('click', true, true, null, |
+ 0, 0, 0, 0, 0, ctrlKey, false, false, false, 0, null); |
+ app.dispatchEvent(activationEvent); |
+ } |
+ }, |
+ |
+ /** |
* Removes user pod from pod row. |
* @param {string} email User's email. |
*/ |
@@ -1202,6 +1366,17 @@ cr.define('login', function() { |
* @param {boolean} animated Whether to use init animation. |
*/ |
loadPods: function(users, animated) { |
+ this.users_ = users; |
+ this.user_add_is_animated_ = animated; |
+ |
+ this.rebuildPods(); |
+ }, |
+ |
+ /** |
+ * Rebuilds pod row using users_ and apps_ that were previously set or |
+ * updated. |
+ */ |
+ rebuildPods: function() { |
// Clear existing pods. |
this.innerHTML = ''; |
this.focusedPod_ = undefined; |
@@ -1212,12 +1387,16 @@ cr.define('login', function() { |
Oobe.getInstance().toggleClass('flying-pods', false); |
// Populate the pod row. |
- for (var i = 0; i < users.length; ++i) { |
- this.addUserPod(users[i], animated); |
- } |
- for (var i = 0, pod; pod = this.pods[i]; ++i) { |
+ for (var i = 0; i < this.users_.length; ++i) |
+ this.addUserPod(this.users_[i], this.user_add_is_animated_); |
+ |
+ for (var i = 0, pod; pod = this.pods[i]; ++i) |
this.podsWithPendingImages_.push(pod); |
- } |
+ |
+ // TODO: Edge case handling when kiosk apps are not fitting. |
+ for (var i = 0; i < this.apps_.length; ++i) |
+ this.addUserPod(this.apps_[i], this.user_add_is_animated_); |
+ |
// Make sure we eventually show the pod row, even if some image is stuck. |
setTimeout(function() { |
$('pod-row').classList.remove('images-loading'); |
@@ -1235,6 +1414,21 @@ cr.define('login', function() { |
}, |
/** |
+ * Adds given apps to the pod row. |
+ * @param {array} apps Array of apps. |
+ */ |
+ setApps: function(apps) { |
+ this.apps_ = apps; |
+ this.rebuildPods(); |
+ chrome.send('kioskAppsLoaded'); |
+ |
+ // Check whether there's a pending kiosk app error. |
+ window.setTimeout(function() { |
+ chrome.send('checkKioskAppLaunchError'); |
+ }, 500); |
+ }, |
+ |
+ /** |
* Shows a button on a user pod with an icon. Clicking on this button |
* triggers an event used by the chrome.screenlockPrivate API. |
* @param {string} username Username of pod to add button |
@@ -1377,7 +1571,7 @@ cr.define('login', function() { |
* Focuses a given user pod or clear focus when given null. |
* @param {UserPod=} podToFocus User pod to focus (undefined clears focus). |
* @param {boolean=} opt_force If true, forces focus update even when |
- * podToFocus is already focused. |
+ * podToFocus is already focused. |
*/ |
focusPod: function(podToFocus, opt_force) { |
if (this.isFocused(podToFocus) && !opt_force) { |
@@ -1423,7 +1617,8 @@ cr.define('login', function() { |
podToFocus.classList.add('focused'); |
podToFocus.reset(true); // Reset and give focus. |
// focusPod() automatically loads wallpaper |
- chrome.send('focusPod', [podToFocus.user.username]); |
+ if (!podToFocus.user.isApp) |
+ chrome.send('focusPod', [podToFocus.user.username]); |
this.firstShown_ = false; |
this.lastFocusedPod_ = podToFocus; |
} |
@@ -1435,7 +1630,7 @@ cr.define('login', function() { |
* Focuses a given user pod by index or clear focus when given null. |
* @param {int=} podToFocus index of User pod to focus. |
* @param {boolean=} opt_force If true, forces focus update even when |
- * podToFocus is already focused. |
+ * podToFocus is already focused. |
*/ |
focusPodByIndex: function(podToFocus, opt_force) { |
if (podToFocus < this.pods.length) |
@@ -1446,7 +1641,7 @@ cr.define('login', function() { |
* Resets wallpaper to the last active user's wallpaper, if any. |
*/ |
loadLastWallpaper: function() { |
- if (this.lastFocusedPod_) |
+ if (this.lastFocusedPod_ && !this.lastFocusedPod_.user.isApp) |
chrome.send('loadWallpaper', [this.lastFocusedPod_.user.username]); |
}, |
@@ -1457,8 +1652,14 @@ cr.define('login', function() { |
get activatedPod() { |
return this.activatedPod_; |
}, |
- set activatedPod(pod) { |
- if (pod && pod.activate()) |
+ |
+ /** |
+ * Sets currently activated pod. |
+ * @param {UserPod} pod Pod to check for focus. |
+ * @param {Event} e Event object. |
+ */ |
+ setActivatedPod: function(pod, e) { |
+ if (pod && pod.activate(e)) |
this.activatedPod_ = pod; |
}, |
@@ -1682,7 +1883,7 @@ cr.define('login', function() { |
break; |
case 'Enter': |
if (this.focusedPod_) { |
- this.activatedPod = this.focusedPod_; |
+ this.setActivatedPod(this.focusedPod_, e); |
e.stopPropagation(); |
} |
break; |
@@ -1713,7 +1914,8 @@ cr.define('login', function() { |
focusedPod.reset(true); |
// Notify screen that it is ready. |
screen.onShow(); |
- chrome.send('loadWallpaper', [focusedPod.user.username]); |
+ if (!focusedPod.user.isApp) |
+ chrome.send('loadWallpaper', [focusedPod.user.username]); |
} |
}); |
// Guard timer for 1 second -- it would conver all possible animations. |