| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview Account picker screen implementation. |
| */ |
| |
| login.createScreen('AccountPickerScreen', 'account-picker', function() { |
| /** |
| * Maximum number of offline login failures before online login. |
| * @type {number} |
| * @const |
| */ |
| var MAX_LOGIN_ATTEMPTS_IN_POD = 3; |
| |
| /** |
| * Distance between error bubble and user POD. |
| * @type {number} |
| * @const |
| */ |
| var BUBBLE_POD_OFFSET = 4; |
| |
| return { |
| EXTERNAL_API: [ |
| 'loadUsers', |
| 'runAppForTesting', |
| 'setApps', |
| 'setShouldShowApps', |
| 'showAppError', |
| 'updateUserImage', |
| 'setCapsLockState', |
| 'forceLockedUserPodFocus', |
| 'removeUser', |
| 'showBannerMessage', |
| 'showUserPodCustomIcon', |
| 'hideUserPodCustomIcon', |
| 'setUserPodFingerprintIcon', |
| 'removeUserPodFingerprintIcon', |
| 'setPinEnabledForUser', |
| 'setAuthType', |
| 'setTouchViewState', |
| 'setPublicSessionDisplayName', |
| 'setPublicSessionLocales', |
| 'setPublicSessionKeyboardLayouts', |
| 'setLockScreenAppsState', |
| ], |
| |
| preferredWidth_: 0, |
| preferredHeight_: 0, |
| |
| // Whether this screen is shown for the first time. |
| firstShown_: true, |
| |
| // Whether this screen is currently being shown. |
| showing_: false, |
| |
| // Last reported lock screen app activity state. |
| lockScreenAppsState_: LOCK_SCREEN_APPS_STATE.NONE, |
| |
| /** @override */ |
| decorate: function() { |
| login.PodRow.decorate($('pod-row')); |
| this.ownerDocument.addEventListener('click', |
| this.handleOwnerDocClick_.bind(this)); |
| }, |
| |
| /** @override */ |
| getPreferredSize: function() { |
| return {width: this.preferredWidth_, height: this.preferredHeight_}; |
| }, |
| |
| /** @override */ |
| onWindowResize: function() { |
| $('pod-row').onWindowResize(); |
| |
| // Reposition the error bubble, if it is showing. Since we are just |
| // moving the bubble, the number of login attempts tried doesn't matter. |
| var errorBubble = $('bubble'); |
| if (errorBubble && !errorBubble.hidden) |
| this.showErrorBubble(0, undefined /* Reuses the existing message. */); |
| }, |
| |
| /** |
| * Sets preferred size for account picker screen. |
| */ |
| setPreferredSize: function(width, height) { |
| this.preferredWidth_ = width; |
| this.preferredHeight_ = height; |
| }, |
| |
| /** |
| * When the account picker is being used to lock the screen, pressing the |
| * exit accelerator key will sign out the active user as it would when |
| * they are signed in. |
| */ |
| exit: function() { |
| // Check and disable the sign out button so that we can never have two |
| // sign out requests generated in a row. |
| if ($('pod-row').lockedPod && !$('sign-out-user-button').disabled) { |
| $('sign-out-user-button').disabled = true; |
| chrome.send('signOutUser'); |
| } |
| }, |
| |
| /* Cancel user adding if ESC was pressed. |
| */ |
| cancel: function() { |
| if (Oobe.getInstance().displayType == DISPLAY_TYPE.USER_ADDING) |
| chrome.send('cancelUserAdding'); |
| }, |
| |
| /** |
| * Event handler that is invoked just after the frame is shown. |
| * @param {string} data Screen init payload. |
| */ |
| onAfterShow: function(data) { |
| $('pod-row').handleAfterShow(); |
| }, |
| |
| /** |
| * Event handler that is invoked just before the frame is shown. |
| * @param {string} data Screen init payload. |
| */ |
| onBeforeShow: function(data) { |
| this.showing_ = true; |
| chrome.send('loginUIStateChanged', ['account-picker', true]); |
| $('login-header-bar').signinUIState = SIGNIN_UI_STATE.ACCOUNT_PICKER; |
| // Header bar should be always visible on Account Picker screen. |
| Oobe.getInstance().headerHidden = false; |
| chrome.send('hideCaptivePortal'); |
| var podRow = $('pod-row'); |
| podRow.handleBeforeShow(); |
| |
| // In case of the preselected pod onShow will be called once pod |
| // receives focus. |
| if (!podRow.preselectedPod) |
| this.onShow(); |
| }, |
| |
| /** |
| * Event handler invoked when the page is shown and ready. |
| */ |
| onShow: function() { |
| if (!this.showing_) { |
| // This method may be called asynchronously when the pod row finishes |
| // initializing. However, at that point, the screen may have been hidden |
| // again already. If that happens, ignore the onShow() call. |
| return; |
| } |
| chrome.send('getTouchViewState'); |
| if (!this.firstShown_) return; |
| this.firstShown_ = false; |
| |
| // Ensure that login is actually visible. |
| window.requestAnimationFrame(function() { |
| chrome.send('accountPickerReady'); |
| chrome.send('loginVisible', ['account-picker']); |
| }); |
| }, |
| |
| /** |
| * Event handler that is invoked just before the frame is hidden. |
| */ |
| onBeforeHide: function() { |
| $('pod-row').clearFocusedPod(); |
| this.showing_ = false; |
| chrome.send('loginUIStateChanged', ['account-picker', false]); |
| $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN; |
| $('pod-row').handleHide(); |
| }, |
| |
| /** |
| * Shows sign-in error bubble. |
| * @param {number} loginAttempts Number of login attemps tried. |
| * @param {HTMLElement} content Content to show in bubble. |
| */ |
| showErrorBubble: function(loginAttempts, error) { |
| var activatedPod = $('pod-row').activatedPod; |
| if (!activatedPod) { |
| $('bubble').showContentForElement($('pod-row'), |
| cr.ui.Bubble.Attachment.RIGHT, |
| error); |
| return; |
| } |
| // Show web authentication if this is not a supervised user. |
| if (loginAttempts > MAX_LOGIN_ATTEMPTS_IN_POD && |
| !activatedPod.user.supervisedUser) { |
| chrome.send('maxIncorrectPasswordAttempts', |
| [activatedPod.user.emailAddress]); |
| activatedPod.showSigninUI(); |
| } else { |
| if (loginAttempts == 1) { |
| chrome.send('firstIncorrectPasswordAttempt', |
| [activatedPod.user.emailAddress]); |
| } |
| // Update the pod row display if incorrect password. |
| $('pod-row').setFocusedPodErrorDisplay(true); |
| |
| /** @const */ var BUBBLE_OFFSET = 25; |
| // -8 = 4(BUBBLE_POD_OFFSET) - 2(bubble margin) |
| // - 10(internal bubble adjustment) |
| var bubblePositioningPadding = -8; |
| |
| var bubbleAnchor; |
| var attachment; |
| if (activatedPod.pinContainer && |
| activatedPod.pinContainer.style.visibility == 'visible') { |
| // Anchor the bubble to the input field. |
| bubbleAnchor = ( |
| activatedPod.getElementsByClassName('auth-container'))[0]; |
| if (!bubbleAnchor) { |
| console.error('auth-container not found!'); |
| bubbleAnchor = activatedPod.mainInput; |
| } |
| attachment = cr.ui.Bubble.Attachment.RIGHT; |
| } else { |
| // Anchor the bubble to the pod instead of the input. |
| bubbleAnchor = activatedPod; |
| attachment = cr.ui.Bubble.Attachment.BOTTOM; |
| } |
| |
| var bubble = $('bubble'); |
| |
| // Cannot use cr.ui.LoginUITools.get* on bubble until it is attached to |
| // the element. getMaxHeight/Width rely on the correct up/left element |
| // side positioning that doesn't happen until bubble is attached. |
| var maxHeight = |
| cr.ui.LoginUITools.getMaxHeightBeforeShelfOverlapping(bubbleAnchor) |
| - bubbleAnchor.offsetHeight - BUBBLE_POD_OFFSET; |
| var maxWidth = cr.ui.LoginUITools.getMaxWidthToFit(bubbleAnchor) |
| - bubbleAnchor.offsetWidth - BUBBLE_POD_OFFSET; |
| |
| // Change bubble visibility temporary to calculate height. |
| var bubbleVisibility = bubble.style.visibility; |
| bubble.style.visibility = 'hidden'; |
| bubble.hidden = false; |
| // Now we need the bubble to have the new content before calculating |
| // size. Undefined |error| == reuse old content. |
| if (error !== undefined) |
| bubble.replaceContent(error); |
| |
| // Get bubble size. |
| var bubbleOffsetHeight = parseInt(bubble.offsetHeight); |
| var bubbleOffsetWidth = parseInt(bubble.offsetWidth); |
| // Restore attributes. |
| bubble.style.visibility = bubbleVisibility; |
| bubble.hidden = true; |
| |
| if (attachment == cr.ui.Bubble.Attachment.BOTTOM) { |
| // Move error bubble if it overlaps the shelf. |
| if (maxHeight < bubbleOffsetHeight) |
| attachment = cr.ui.Bubble.Attachment.TOP; |
| } else { |
| // Move error bubble if it doesn't fit screen. |
| if (maxWidth < bubbleOffsetWidth) { |
| bubblePositioningPadding = 2; |
| attachment = cr.ui.Bubble.Attachment.LEFT; |
| } |
| } |
| var showBubbleCallback = function() { |
| activatedPod.removeEventListener("transitionend", |
| showBubbleCallback); |
| $('bubble').showContentForElement(bubbleAnchor, |
| attachment, |
| error, |
| BUBBLE_OFFSET, |
| bubblePositioningPadding, true); |
| }; |
| activatedPod.addEventListener("transitionend", |
| showBubbleCallback); |
| ensureTransitionEndEvent(activatedPod); |
| } |
| }, |
| |
| /** |
| * Loads given users in pod row. |
| * @param {array} users Array of user. |
| * @param {boolean} showGuest Whether to show guest session button. |
| */ |
| loadUsers: function(users, showGuest) { |
| $('pod-row').loadPods(users); |
| $('login-header-bar').showGuestButton = showGuest; |
| // On Desktop, #login-header-bar has a shadow if there are 8+ profiles. |
| if (Oobe.getInstance().displayType == DISPLAY_TYPE.DESKTOP_USER_MANAGER) |
| $('login-header-bar').classList.toggle('shadow', users.length > 8); |
| }, |
| |
| /** |
| * 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. |
| */ |
| runAppForTesting: function(app_id, opt_diagnostic_mode) { |
| $('pod-row').findAndRunAppForTesting(app_id, opt_diagnostic_mode); |
| }, |
| |
| /** |
| * Adds given apps to the pod row. |
| * @param {array} apps Array of apps. |
| */ |
| setApps: function(apps) { |
| $('pod-row').setApps(apps); |
| }, |
| |
| /** |
| * Sets the flag of whether app pods should be visible. |
| * @param {boolean} shouldShowApps Whether to show app pods. |
| */ |
| setShouldShowApps: function(shouldShowApps) { |
| $('pod-row').setShouldShowApps(shouldShowApps); |
| }, |
| |
| /** |
| * Shows the given kiosk app error message. |
| * @param {!string} message Error message to show. |
| */ |
| showAppError: function(message) { |
| // TODO(nkostylev): Figure out a way to show kiosk app launch error |
| // pointing to the kiosk app pod. |
| /** @const */ var BUBBLE_PADDING = 12; |
| $('bubble').showTextForElement($('pod-row'), |
| message, |
| cr.ui.Bubble.Attachment.BOTTOM, |
| $('pod-row').offsetWidth / 2, |
| BUBBLE_PADDING); |
| }, |
| |
| /** |
| * Updates current image of a user. |
| * @param {string} username User for which to update the image. |
| */ |
| updateUserImage: function(username) { |
| $('pod-row').updateUserImage(username); |
| }, |
| |
| /** |
| * Updates Caps Lock state (for Caps Lock hint in password input field). |
| * @param {boolean} enabled Whether Caps Lock is on. |
| */ |
| setCapsLockState: function(enabled) { |
| $('pod-row').classList.toggle('capslock-on', enabled); |
| }, |
| |
| /** |
| * Enforces focus on user pod of locked user. |
| */ |
| forceLockedUserPodFocus: function() { |
| var row = $('pod-row'); |
| if (row.lockedPod) |
| row.focusPod(row.lockedPod, true); |
| }, |
| |
| /** |
| * Remove given user from pod row if it is there. |
| * @param {string} user name. |
| */ |
| removeUser: function(username) { |
| $('pod-row').removeUserPod(username); |
| }, |
| |
| /** |
| * Displays a banner containing |message|. If the banner is already present |
| * this function updates the message in the banner. This function is used |
| * by the chrome.screenlockPrivate.showMessage API. |
| * @param {string} message Text to be displayed or empty to hide the banner. |
| */ |
| showBannerMessage: function(message) { |
| var banner = $('signin-banner'); |
| banner.textContent = message; |
| banner.classList.toggle('message-set', !!message); |
| }, |
| |
| /** |
| * Shows a custom icon in the user pod of |username|. This function |
| * is used by the chrome.screenlockPrivate API. |
| * @param {string} username Username of pod to add button |
| * @param {!{id: !string, |
| * hardlockOnClick: boolean, |
| * isTrialRun: boolean, |
| * tooltip: ({text: string, autoshow: boolean} | undefined)}} icon |
| * The icon parameters. |
| */ |
| showUserPodCustomIcon: function(username, icon) { |
| $('pod-row').showUserPodCustomIcon(username, icon); |
| }, |
| |
| /** |
| * Hides the custom icon in the user pod of |username| added by |
| * showUserPodCustomIcon(). This function is used by the |
| * chrome.screenlockPrivate API. |
| * @param {string} username Username of pod to remove button |
| */ |
| hideUserPodCustomIcon: function(username) { |
| $('pod-row').hideUserPodCustomIcon(username); |
| }, |
| |
| /** |
| * Set a fingerprint icon in the user pod of |username|. |
| * @param {string} username Username of the selected user |
| * @param {number} state Fingerprint unlock state |
| */ |
| setUserPodFingerprintIcon: function(username, state) { |
| $('pod-row').setUserPodFingerprintIcon(username, state); |
| }, |
| |
| /** |
| * Removes the fingerprint icon in the user pod of |username|. |
| * @param {string} username Username of the selected user. |
| */ |
| removeUserPodFingerprintIcon: function(username) { |
| $('pod-row').removeUserPodFingerprintIcon(username); |
| }, |
| |
| /** |
| * Sets the authentication type used to authenticate the user. |
| * @param {string} username Username of selected user |
| * @param {number} authType Authentication type, must be a valid value in |
| * the AUTH_TYPE enum in user_pod_row.js. |
| * @param {string} value The initial value to use for authentication. |
| */ |
| setAuthType: function(username, authType, value) { |
| $('pod-row').setAuthType(username, authType, value); |
| }, |
| |
| /** |
| * Sets the state of touch view mode. |
| * @param {boolean} isTouchViewEnabled true if the mode is on. |
| */ |
| setTouchViewState: function(isTouchViewEnabled) { |
| $('pod-row').setTouchViewState(isTouchViewEnabled); |
| }, |
| |
| /** |
| * Enables or disables the pin keyboard for the given user. This may change |
| * pin keyboard visibility. |
| * @param {!string} user |
| * @param {boolean} enabled |
| */ |
| setPinEnabledForUser: function(user, enabled) { |
| $('pod-row').setPinEnabled(user, enabled); |
| }, |
| |
| /** |
| * Updates the display name shown on a public session pod. |
| * @param {string} userID The user ID of the public session |
| * @param {string} displayName The new display name |
| */ |
| setPublicSessionDisplayName: function(userID, displayName) { |
| $('pod-row').setPublicSessionDisplayName(userID, displayName); |
| }, |
| |
| /** |
| * Updates the list of locales available for a public session. |
| * @param {string} userID The user ID of the public session |
| * @param {!Object} locales The list of available locales |
| * @param {string} defaultLocale The locale to select by default |
| * @param {boolean} multipleRecommendedLocales Whether |locales| contains |
| * two or more recommended locales |
| */ |
| setPublicSessionLocales: function(userID, |
| locales, |
| defaultLocale, |
| multipleRecommendedLocales) { |
| $('pod-row').setPublicSessionLocales(userID, |
| locales, |
| defaultLocale, |
| multipleRecommendedLocales); |
| }, |
| |
| /** |
| * Updates the list of available keyboard layouts for a public session pod. |
| * @param {string} userID The user ID of the public session |
| * @param {string} locale The locale to which this list of keyboard layouts |
| * applies |
| * @param {!Object} list List of available keyboard layouts |
| */ |
| setPublicSessionKeyboardLayouts: function(userID, locale, list) { |
| $('pod-row').setPublicSessionKeyboardLayouts(userID, locale, list); |
| }, |
| |
| /** |
| * Updates UI based on the provided lock screen apps state. |
| * |
| * @param {LOCK_SCREEN_APPS_STATE} state The current lock screen apps state. |
| */ |
| setLockScreenAppsState: function(state) { |
| if (Oobe.getInstance().displayType != DISPLAY_TYPE.LOCK || |
| state == this.lockScreenAppsState_) { |
| return; |
| } |
| |
| this.lockScreenAppsState_ = state; |
| $('login-header-bar').lockScreenAppsState = state; |
| // When an lock screen app window is in background - i.e. visible behind |
| // the lock screen UI - dim the lock screen background, so it's more |
| // noticeable that the app widow in background is not actionable. |
| $('background').classList.toggle( |
| 'dimmed-background', state == LOCK_SCREEN_APPS_STATE.BACKGROUND); |
| |
| if (state === LOCK_SCREEN_APPS_STATE.FOREGROUND) |
| $('pod-row').clearFocusedPod(); |
| |
| }, |
| |
| /** |
| * Handles clicks on the document which displays the account picker UI. |
| * If the click event target is outer container - i.e. background portion of |
| * UI with no other UI elements, and lock screen apps are in background, a |
| * request is issued to chrome to move lock screen apps to foreground. |
| * @param {Event} event The click event. |
| */ |
| handleOwnerDocClick_: function(event) { |
| if (this.lockScreenAppsState_ != LOCK_SCREEN_APPS_STATE.BACKGROUND || |
| event.target != $('outer-container')) { |
| return; |
| } |
| chrome.send('setLockScreenAppsState', |
| [LOCK_SCREEN_APPS_STATE.FOREGROUND]); |
| |
| event.preventDefault(); |
| event.stopPropagation(); |
| }, |
| }; |
| }); |