OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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 /** |
| 6 * @fileoverview Account picker screen implementation. |
| 7 */ |
| 8 |
| 9 login.createScreen('AccountPickerScreen', 'account-picker', function() { |
| 10 /** |
| 11 * Maximum number of offline login failures before online login. |
| 12 * @type {number} |
| 13 * @const |
| 14 */ |
| 15 var MAX_LOGIN_ATTEMPTS_IN_POD = 3; |
| 16 |
| 17 /** |
| 18 * Distance between error bubble and user POD. |
| 19 * @type {number} |
| 20 * @const |
| 21 */ |
| 22 var BUBBLE_POD_OFFSET = 4; |
| 23 |
| 24 return { |
| 25 EXTERNAL_API: [ |
| 26 'loadUsers', |
| 27 'runAppForTesting', |
| 28 'setApps', |
| 29 'setShouldShowApps', |
| 30 'showAppError', |
| 31 'updateUserImage', |
| 32 'setCapsLockState', |
| 33 'forceLockedUserPodFocus', |
| 34 'removeUser', |
| 35 'showBannerMessage', |
| 36 'showUserPodCustomIcon', |
| 37 'hideUserPodCustomIcon', |
| 38 'setUserPodFingerprintIcon', |
| 39 'removeUserPodFingerprintIcon', |
| 40 'setPinEnabledForUser', |
| 41 'setAuthType', |
| 42 'setTouchViewState', |
| 43 'setPublicSessionDisplayName', |
| 44 'setPublicSessionLocales', |
| 45 'setPublicSessionKeyboardLayouts', |
| 46 ], |
| 47 |
| 48 preferredWidth_: 0, |
| 49 preferredHeight_: 0, |
| 50 |
| 51 // Whether this screen is shown for the first time. |
| 52 firstShown_: true, |
| 53 |
| 54 // Whether this screen is currently being shown. |
| 55 showing_: false, |
| 56 |
| 57 /** @override */ |
| 58 decorate: function() { |
| 59 login.PodRow.decorate($('pod-row')); |
| 60 }, |
| 61 |
| 62 /** @override */ |
| 63 getPreferredSize: function() { |
| 64 return {width: this.preferredWidth_, height: this.preferredHeight_}; |
| 65 }, |
| 66 |
| 67 /** @override */ |
| 68 onWindowResize: function() { |
| 69 $('pod-row').onWindowResize(); |
| 70 |
| 71 // Reposition the error bubble, if it is showing. Since we are just |
| 72 // moving the bubble, the number of login attempts tried doesn't matter. |
| 73 var errorBubble = $('bubble'); |
| 74 if (errorBubble && !errorBubble.hidden) |
| 75 this.showErrorBubble(0, undefined /* Reuses the existing message. */); |
| 76 }, |
| 77 |
| 78 /** |
| 79 * Sets preferred size for account picker screen. |
| 80 */ |
| 81 setPreferredSize: function(width, height) { |
| 82 this.preferredWidth_ = width; |
| 83 this.preferredHeight_ = height; |
| 84 }, |
| 85 |
| 86 /** |
| 87 * When the account picker is being used to lock the screen, pressing the |
| 88 * exit accelerator key will sign out the active user as it would when |
| 89 * they are signed in. |
| 90 */ |
| 91 exit: function() { |
| 92 // Check and disable the sign out button so that we can never have two |
| 93 // sign out requests generated in a row. |
| 94 if ($('pod-row').lockedPod && !$('sign-out-user-button').disabled) { |
| 95 $('sign-out-user-button').disabled = true; |
| 96 chrome.send('signOutUser'); |
| 97 } |
| 98 }, |
| 99 |
| 100 /* Cancel user adding if ESC was pressed. |
| 101 */ |
| 102 cancel: function() { |
| 103 if (Oobe.getInstance().displayType == DISPLAY_TYPE.USER_ADDING) |
| 104 chrome.send('cancelUserAdding'); |
| 105 }, |
| 106 |
| 107 /** |
| 108 * Event handler that is invoked just after the frame is shown. |
| 109 * @param {string} data Screen init payload. |
| 110 */ |
| 111 onAfterShow: function(data) { |
| 112 $('pod-row').handleAfterShow(); |
| 113 }, |
| 114 |
| 115 /** |
| 116 * Event handler that is invoked just before the frame is shown. |
| 117 * @param {string} data Screen init payload. |
| 118 */ |
| 119 onBeforeShow: function(data) { |
| 120 this.showing_ = true; |
| 121 chrome.send('loginUIStateChanged', ['account-picker', true]); |
| 122 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.ACCOUNT_PICKER; |
| 123 // Header bar should be always visible on Account Picker screen. |
| 124 Oobe.getInstance().headerHidden = false; |
| 125 chrome.send('hideCaptivePortal'); |
| 126 var podRow = $('pod-row'); |
| 127 podRow.handleBeforeShow(); |
| 128 |
| 129 // In case of the preselected pod onShow will be called once pod |
| 130 // receives focus. |
| 131 if (!podRow.preselectedPod) |
| 132 this.onShow(); |
| 133 }, |
| 134 |
| 135 /** |
| 136 * Event handler invoked when the page is shown and ready. |
| 137 */ |
| 138 onShow: function() { |
| 139 if (!this.showing_) { |
| 140 // This method may be called asynchronously when the pod row finishes |
| 141 // initializing. However, at that point, the screen may have been hidden |
| 142 // again already. If that happens, ignore the onShow() call. |
| 143 return; |
| 144 } |
| 145 chrome.send('getTouchViewState'); |
| 146 if (!this.firstShown_) return; |
| 147 this.firstShown_ = false; |
| 148 |
| 149 // Ensure that login is actually visible. |
| 150 window.requestAnimationFrame(function() { |
| 151 chrome.send('accountPickerReady'); |
| 152 chrome.send('loginVisible', ['account-picker']); |
| 153 }); |
| 154 }, |
| 155 |
| 156 /** |
| 157 * Event handler that is invoked just before the frame is hidden. |
| 158 */ |
| 159 onBeforeHide: function() { |
| 160 $('pod-row').clearFocusedPod(); |
| 161 this.showing_ = false; |
| 162 chrome.send('loginUIStateChanged', ['account-picker', false]); |
| 163 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN; |
| 164 $('pod-row').handleHide(); |
| 165 }, |
| 166 |
| 167 /** |
| 168 * Shows sign-in error bubble. |
| 169 * @param {number} loginAttempts Number of login attemps tried. |
| 170 * @param {HTMLElement} content Content to show in bubble. |
| 171 */ |
| 172 showErrorBubble: function(loginAttempts, error) { |
| 173 var activatedPod = $('pod-row').activatedPod; |
| 174 if (!activatedPod) { |
| 175 $('bubble').showContentForElement($('pod-row'), |
| 176 cr.ui.Bubble.Attachment.RIGHT, |
| 177 error); |
| 178 return; |
| 179 } |
| 180 // Show web authentication if this is not a supervised user. |
| 181 if (loginAttempts > MAX_LOGIN_ATTEMPTS_IN_POD && |
| 182 !activatedPod.user.supervisedUser) { |
| 183 chrome.send('maxIncorrectPasswordAttempts', |
| 184 [activatedPod.user.emailAddress]); |
| 185 activatedPod.showSigninUI(); |
| 186 } else { |
| 187 if (loginAttempts == 1) { |
| 188 chrome.send('firstIncorrectPasswordAttempt', |
| 189 [activatedPod.user.emailAddress]); |
| 190 } |
| 191 // Update the pod row display if incorrect password. |
| 192 $('pod-row').setFocusedPodErrorDisplay(true); |
| 193 |
| 194 /** @const */ var BUBBLE_OFFSET = 25; |
| 195 // -8 = 4(BUBBLE_POD_OFFSET) - 2(bubble margin) |
| 196 // - 10(internal bubble adjustment) |
| 197 var bubblePositioningPadding = -8; |
| 198 |
| 199 var bubbleAnchor; |
| 200 var attachment; |
| 201 if (activatedPod.pinContainer) { |
| 202 // Anchor the bubble to the input field. |
| 203 bubbleAnchor = ( |
| 204 activatedPod.getElementsByClassName('auth-container'))[0]; |
| 205 if (!bubbleAnchor) { |
| 206 console.error('auth-container not found!'); |
| 207 bubbleAnchor = activatedPod.mainInput; |
| 208 } |
| 209 attachment = cr.ui.Bubble.Attachment.RIGHT; |
| 210 } else { |
| 211 // Anchor the bubble to the pod instead of the input. |
| 212 bubbleAnchor = activatedPod; |
| 213 attachment = cr.ui.Bubble.Attachment.BOTTOM; |
| 214 } |
| 215 |
| 216 var bubble = $('bubble'); |
| 217 |
| 218 // Cannot use cr.ui.LoginUITools.get* on bubble until it is attached to |
| 219 // the element. getMaxHeight/Width rely on the correct up/left element |
| 220 // side positioning that doesn't happen until bubble is attached. |
| 221 var maxHeight = |
| 222 cr.ui.LoginUITools.getMaxHeightBeforeShelfOverlapping(bubbleAnchor) |
| 223 - bubbleAnchor.offsetHeight - BUBBLE_POD_OFFSET; |
| 224 var maxWidth = cr.ui.LoginUITools.getMaxWidthToFit(bubbleAnchor) |
| 225 - bubbleAnchor.offsetWidth - BUBBLE_POD_OFFSET; |
| 226 |
| 227 // Change bubble visibility temporary to calculate height. |
| 228 var bubbleVisibility = bubble.style.visibility; |
| 229 bubble.style.visibility = 'hidden'; |
| 230 bubble.hidden = false; |
| 231 // Now we need the bubble to have the new content before calculating |
| 232 // size. Undefined |error| == reuse old content. |
| 233 if (error !== undefined) |
| 234 bubble.replaceContent(error); |
| 235 |
| 236 // Get bubble size. |
| 237 var bubbleOffsetHeight = parseInt(bubble.offsetHeight); |
| 238 var bubbleOffsetWidth = parseInt(bubble.offsetWidth); |
| 239 // Restore attributes. |
| 240 bubble.style.visibility = bubbleVisibility; |
| 241 bubble.hidden = true; |
| 242 |
| 243 if (attachment == cr.ui.Bubble.Attachment.BOTTOM) { |
| 244 // Move error bubble if it overlaps the shelf. |
| 245 if (maxHeight < bubbleOffsetHeight) |
| 246 attachment = cr.ui.Bubble.Attachment.TOP; |
| 247 } else { |
| 248 // Move error bubble if it doesn't fit screen. |
| 249 if (maxWidth < bubbleOffsetWidth) { |
| 250 bubblePositioningPadding = 2; |
| 251 attachment = cr.ui.Bubble.Attachment.LEFT; |
| 252 } |
| 253 } |
| 254 var showBubbleCallback = function() { |
| 255 activatedPod.removeEventListener("transitionend", |
| 256 showBubbleCallback); |
| 257 $('bubble').showContentForElement(bubbleAnchor, |
| 258 attachment, |
| 259 error, |
| 260 BUBBLE_OFFSET, |
| 261 bubblePositioningPadding, true); |
| 262 }; |
| 263 activatedPod.addEventListener("transitionend", |
| 264 showBubbleCallback); |
| 265 ensureTransitionEndEvent(activatedPod); |
| 266 } |
| 267 }, |
| 268 |
| 269 /** |
| 270 * Loads given users in pod row. |
| 271 * @param {array} users Array of user. |
| 272 * @param {boolean} showGuest Whether to show guest session button. |
| 273 */ |
| 274 loadUsers: function(users, showGuest) { |
| 275 $('pod-row').loadPods(users); |
| 276 $('login-header-bar').showGuestButton = showGuest; |
| 277 // On Desktop, #login-header-bar has a shadow if there are 8+ profiles. |
| 278 if (Oobe.getInstance().displayType == DISPLAY_TYPE.DESKTOP_USER_MANAGER) |
| 279 $('login-header-bar').classList.toggle('shadow', users.length > 8); |
| 280 }, |
| 281 |
| 282 /** |
| 283 * Runs app with a given id from the list of loaded apps. |
| 284 * @param {!string} app_id of an app to run. |
| 285 * @param {boolean=} opt_diagnostic_mode Whether to run the app in |
| 286 * diagnostic mode. Default is false. |
| 287 */ |
| 288 runAppForTesting: function(app_id, opt_diagnostic_mode) { |
| 289 $('pod-row').findAndRunAppForTesting(app_id, opt_diagnostic_mode); |
| 290 }, |
| 291 |
| 292 /** |
| 293 * Adds given apps to the pod row. |
| 294 * @param {array} apps Array of apps. |
| 295 */ |
| 296 setApps: function(apps) { |
| 297 $('pod-row').setApps(apps); |
| 298 }, |
| 299 |
| 300 /** |
| 301 * Sets the flag of whether app pods should be visible. |
| 302 * @param {boolean} shouldShowApps Whether to show app pods. |
| 303 */ |
| 304 setShouldShowApps: function(shouldShowApps) { |
| 305 $('pod-row').setShouldShowApps(shouldShowApps); |
| 306 }, |
| 307 |
| 308 /** |
| 309 * Shows the given kiosk app error message. |
| 310 * @param {!string} message Error message to show. |
| 311 */ |
| 312 showAppError: function(message) { |
| 313 // TODO(nkostylev): Figure out a way to show kiosk app launch error |
| 314 // pointing to the kiosk app pod. |
| 315 /** @const */ var BUBBLE_PADDING = 12; |
| 316 $('bubble').showTextForElement($('pod-row'), |
| 317 message, |
| 318 cr.ui.Bubble.Attachment.BOTTOM, |
| 319 $('pod-row').offsetWidth / 2, |
| 320 BUBBLE_PADDING); |
| 321 }, |
| 322 |
| 323 /** |
| 324 * Updates current image of a user. |
| 325 * @param {string} username User for which to update the image. |
| 326 */ |
| 327 updateUserImage: function(username) { |
| 328 $('pod-row').updateUserImage(username); |
| 329 }, |
| 330 |
| 331 /** |
| 332 * Updates Caps Lock state (for Caps Lock hint in password input field). |
| 333 * @param {boolean} enabled Whether Caps Lock is on. |
| 334 */ |
| 335 setCapsLockState: function(enabled) { |
| 336 $('pod-row').classList.toggle('capslock-on', enabled); |
| 337 }, |
| 338 |
| 339 /** |
| 340 * Enforces focus on user pod of locked user. |
| 341 */ |
| 342 forceLockedUserPodFocus: function() { |
| 343 var row = $('pod-row'); |
| 344 if (row.lockedPod) |
| 345 row.focusPod(row.lockedPod, true); |
| 346 }, |
| 347 |
| 348 /** |
| 349 * Remove given user from pod row if it is there. |
| 350 * @param {string} user name. |
| 351 */ |
| 352 removeUser: function(username) { |
| 353 $('pod-row').removeUserPod(username); |
| 354 }, |
| 355 |
| 356 /** |
| 357 * Displays a banner containing |message|. If the banner is already present |
| 358 * this function updates the message in the banner. This function is used |
| 359 * by the chrome.screenlockPrivate.showMessage API. |
| 360 * @param {string} message Text to be displayed or empty to hide the banner. |
| 361 */ |
| 362 showBannerMessage: function(message) { |
| 363 var banner = $('signin-banner'); |
| 364 banner.textContent = message; |
| 365 banner.classList.toggle('message-set', !!message); |
| 366 }, |
| 367 |
| 368 /** |
| 369 * Shows a custom icon in the user pod of |username|. This function |
| 370 * is used by the chrome.screenlockPrivate API. |
| 371 * @param {string} username Username of pod to add button |
| 372 * @param {!{id: !string, |
| 373 * hardlockOnClick: boolean, |
| 374 * isTrialRun: boolean, |
| 375 * tooltip: ({text: string, autoshow: boolean} | undefined)}} icon |
| 376 * The icon parameters. |
| 377 */ |
| 378 showUserPodCustomIcon: function(username, icon) { |
| 379 $('pod-row').showUserPodCustomIcon(username, icon); |
| 380 }, |
| 381 |
| 382 /** |
| 383 * Hides the custom icon in the user pod of |username| added by |
| 384 * showUserPodCustomIcon(). This function is used by the |
| 385 * chrome.screenlockPrivate API. |
| 386 * @param {string} username Username of pod to remove button |
| 387 */ |
| 388 hideUserPodCustomIcon: function(username) { |
| 389 $('pod-row').hideUserPodCustomIcon(username); |
| 390 }, |
| 391 |
| 392 /** |
| 393 * Set a fingerprint icon in the user pod of |username|. |
| 394 * @param {string} username Username of the selected user |
| 395 * @param {number} state Fingerprint unlock state |
| 396 */ |
| 397 setUserPodFingerprintIcon: function(username, state) { |
| 398 $('pod-row').setUserPodFingerprintIcon(username, state); |
| 399 }, |
| 400 |
| 401 /** |
| 402 * Removes the fingerprint icon in the user pod of |username|. |
| 403 * @param {string} username Username of the selected user. |
| 404 */ |
| 405 removeUserPodFingerprintIcon: function(username) { |
| 406 $('pod-row').removeUserPodFingerprintIcon(username); |
| 407 }, |
| 408 |
| 409 /** |
| 410 * Sets the authentication type used to authenticate the user. |
| 411 * @param {string} username Username of selected user |
| 412 * @param {number} authType Authentication type, must be a valid value in |
| 413 * the AUTH_TYPE enum in user_pod_row.js. |
| 414 * @param {string} value The initial value to use for authentication. |
| 415 */ |
| 416 setAuthType: function(username, authType, value) { |
| 417 $('pod-row').setAuthType(username, authType, value); |
| 418 }, |
| 419 |
| 420 /** |
| 421 * Sets the state of touch view mode. |
| 422 * @param {boolean} isTouchViewEnabled true if the mode is on. |
| 423 */ |
| 424 setTouchViewState: function(isTouchViewEnabled) { |
| 425 $('pod-row').setTouchViewState(isTouchViewEnabled); |
| 426 }, |
| 427 |
| 428 /** |
| 429 * Enables or disables the pin keyboard for the given user. This may change |
| 430 * pin keyboard visibility. |
| 431 * @param {!string} user |
| 432 * @param {boolean} enabled |
| 433 */ |
| 434 setPinEnabledForUser: function(user, enabled) { |
| 435 $('pod-row').setPinEnabled(user, enabled); |
| 436 }, |
| 437 |
| 438 /** |
| 439 * Updates the display name shown on a public session pod. |
| 440 * @param {string} userID The user ID of the public session |
| 441 * @param {string} displayName The new display name |
| 442 */ |
| 443 setPublicSessionDisplayName: function(userID, displayName) { |
| 444 $('pod-row').setPublicSessionDisplayName(userID, displayName); |
| 445 }, |
| 446 |
| 447 /** |
| 448 * Updates the list of locales available for a public session. |
| 449 * @param {string} userID The user ID of the public session |
| 450 * @param {!Object} locales The list of available locales |
| 451 * @param {string} defaultLocale The locale to select by default |
| 452 * @param {boolean} multipleRecommendedLocales Whether |locales| contains |
| 453 * two or more recommended locales |
| 454 */ |
| 455 setPublicSessionLocales: function(userID, |
| 456 locales, |
| 457 defaultLocale, |
| 458 multipleRecommendedLocales) { |
| 459 $('pod-row').setPublicSessionLocales(userID, |
| 460 locales, |
| 461 defaultLocale, |
| 462 multipleRecommendedLocales); |
| 463 }, |
| 464 |
| 465 /** |
| 466 * Updates the list of available keyboard layouts for a public session pod. |
| 467 * @param {string} userID The user ID of the public session |
| 468 * @param {string} locale The locale to which this list of keyboard layouts |
| 469 * applies |
| 470 * @param {!Object} list List of available keyboard layouts |
| 471 */ |
| 472 setPublicSessionKeyboardLayouts: function(userID, locale, list) { |
| 473 $('pod-row').setPublicSessionKeyboardLayouts(userID, locale, list); |
| 474 } |
| 475 }; |
| 476 }); |
OLD | NEW |