Chromium Code Reviews| Index: ui/login/account_picker/user_pod_row.js |
| diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js |
| index c52ccfa36948317df4a453d3197ba3dc7e0dd191..718681b71c58391ce1d0421924ab719e931116e8 100644 |
| --- a/ui/login/account_picker/user_pod_row.js |
| +++ b/ui/login/account_picker/user_pod_row.js |
| @@ -125,6 +125,30 @@ cr.define('login', function() { |
| 5: 'forceOfflinePassword' |
| }; |
| + /** |
| + * Supported fingerprint unlock states. |
| + * @enum {number} |
| + * @const |
| + */ |
| + var FINGERPRINT_STATES = { |
| + DEFAULT: 0, |
| + FINGERPRINT_SIGNIN: 1, |
| + FINGERPRINT_FAILED: 2, |
| + }; |
| + |
| + /** |
| + * The fingerprint states to classes mapping. |
| + * {@code state} properties indicate current fingerprint unlock state. |
| + * {@code class} properties are CSS classes used to set the icons' background |
| + * and password placeholder color. |
| + * @const {Array<{type: !number, class: !string}>} |
| + */ |
| + var FINGERPRINT_STATES_MAPPING = [ |
| + {state: 0, class: 'default'}, |
|
jdufault
2017/03/08 23:21:28
fingerprintDefault? Or remove fingerprint prefix f
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Done.
|
| + {state: 1, class: 'fingerprintSignin'}, |
| + {state: 2, class: 'fingerprintFailed'} |
| + ]; |
| + |
| // Focus and tab order are organized as follows: |
| // |
| // (1) all user pods have tab index 1 so they are traversed first; |
| @@ -711,6 +735,13 @@ cr.define('login', function() { |
| */ |
| userClickAuthAllowed_: false, |
| + /** |
| + * Whether the user has recently authencated with fingerprint. |
|
jdufault
2017/03/08 23:21:27
nit: authencated -> authenticated
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Done.
|
| + * @type {boolean} |
| + * @private |
| + */ |
| + fingerprintAuthenticated_: false, |
| + |
| /** @override */ |
| decorate: function() { |
| this.tabIndex = UserPodTabOrder.POD_INPUT; |
| @@ -750,6 +781,16 @@ cr.define('login', function() { |
| this.actionBoxRemoveUserWarningButtonElement.addEventListener('keydown', |
| this.handleRemoveUserConfirmationKeyDown_.bind(this)); |
| + if (this.fingerprintIconElement) { |
| + this.fingerprintIconElement.addEventListener( |
| + 'mouseover', this.handleFingerprintIconMouseOver_.bind(this)); |
| + this.fingerprintIconElement.addEventListener( |
| + 'mouseout', this.handleFingerprintIconMouseOut_.bind(this)); |
| + this.fingerprintIconElement.addEventListener( |
| + 'mousedown', stopEventPropagation); |
| + this.fingerprintIconElement.tabIndex = UserPodTabOrder.POD_CUSTOM_ICON; |
|
jdufault
2017/03/08 23:21:27
Does tabbing work properly when easy unlock and PI
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
easy unlock seems to set the tabIndex only when th
|
| + } |
| + |
| var customIcon = this.customIconElement; |
| customIcon.parentNode.replaceChild(new UserPodCustomIcon(), customIcon); |
| }, |
| @@ -1096,6 +1137,14 @@ cr.define('login', function() { |
| }, |
| /** |
| + * Gets the fingerprint icon area. |
| + * @type {!HTMLDivElement} |
| + */ |
| + get fingerprintIconElement() { |
| + return this.querySelector('.fingerprint-icon-container'); |
| + }, |
| + |
| + /** |
| * Updates the user pod element. |
| */ |
| update: function() { |
| @@ -1216,6 +1265,10 @@ cr.define('login', function() { |
| this.userTypeIconAreaElement.hidden = false; |
| }, |
| + isFingerprintIconShown: function() { |
| + return this.fingerprintIconElement && !this.fingerprintIconElement.hidden; |
| + }, |
| + |
| /** |
| * The user that this pod represents. |
| * @type {!Object} |
| @@ -1412,6 +1465,10 @@ cr.define('login', function() { |
| this.classList.toggle('signing-in', true); |
| chrome.send('attemptUnlock', [this.user.username]); |
| } else if (this.isAuthTypePassword) { |
| + if (this.fingerprintAuthenticated_) { |
| + this.fingerprintAuthenticated_ = false; |
| + return true; |
| + } |
| var pinValue = this.pinKeyboard ? this.pinKeyboard.value : ''; |
| var password = this.passwordElement.value || pinValue; |
| if (!password) |
| @@ -1822,6 +1879,45 @@ cr.define('login', function() { |
| }, |
| /** |
| + * Handles mouseover event on fingerprint icon. |
| + * @param {Event} e MouseOver event. |
| + */ |
| + handleFingerprintIconMouseOver_: function(e) { |
| + var bubbleContent = document.createElement('div'); |
| + bubbleContent.textContent = |
| + loadTimeData.getString('fingerprintIconMessage'); |
| + this.passwordElement.placeholder = |
| + loadTimeData.getString('fingerprintHint'); |
| + |
| + /** @const */ var BUBBLE_OFFSET = 25; |
| + /** @const */ var BUBBLE_PADDING = -8; |
| + var attachment = this.isPinShown() ? cr.ui.Bubble.Attachment.RIGHT : |
| + cr.ui.Bubble.Attachment.BOTTOM; |
| + var bubbleAnchor = this; |
| + if (this.isPinShown()) |
| + bubbleAnchor = (this.getElementsByClassName('auth-container'))[0]; |
| + $('bubble').showContentForElement( |
| + bubbleAnchor, attachment, bubbleContent, BUBBLE_OFFSET, |
| + BUBBLE_PADDING, true); |
| + }, |
| + |
| + /** |
| + * Handles mouseout event on fingerprint icon. |
| + * @param {Event} e MouseOut event. |
| + */ |
| + handleFingerprintIconMouseOut_: function(e) { |
| + if(this.isPinShown()) { |
| + $('bubble').hideForElement( |
| + (this.getElementsByClassName('auth-container'))[0]); |
| + } else { |
| + $('bubble').hideForElement(this); |
| + } |
| + this.passwordElement.placeholder = loadTimeData.getString( |
| + this.isPinShown() ? 'pinKeyboardPlaceholderPinPassword' : |
| + 'passwordHint'); |
| + }, |
| + |
| + /** |
| * Handles a keydown event on remove user confirmation button. |
| * @param {Event} e KeyDown event. |
| */ |
| @@ -1899,8 +1995,14 @@ cr.define('login', function() { |
| * button color and state and hides the error popup bubble. |
| */ |
| updateInput_: function() { |
| - if (this.submitButton) |
| + if (this.submitButton) { |
| this.submitButton.disabled = this.passwordElement.value.length <= 0; |
|
jdufault
2017/03/08 23:21:27
Since you're here, can you change these <= to just
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Done.
|
| + if (this.isFingerprintIconShown()) { |
| + this.submitButton.hidden = this.passwordElement.value.length <= 0; |
| + } else { |
| + this.submitButton.hidden = false; |
| + } |
| + } |
| this.showError = false; |
| $('bubble').hide(); |
| }, |
| @@ -3001,6 +3103,9 @@ cr.define('login', function() { |
| // immediatelly. |
| pod.customIconElement.setTooltip( |
| icon.tooltip || {text: '', autoshow: false}); |
| + |
| + // Hide fingerprint icon when custom icon is shown. |
| + this.setUserPodFingerprintIcon(false, username); |
|
jdufault
2017/03/08 23:21:27
Looks like you're missing the third 'state' parame
jdufault
2017/03/08 23:21:28
Please annotate the false parameter, ie, false /*v
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Done.
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
I've consolidate 'visible' with 'state'.
|
| }, |
| /** |
| @@ -3037,6 +3142,104 @@ cr.define('login', function() { |
| // TODO(tengs): Allow option for a fading transition. |
| pod.customIconElement.hide(); |
| + |
| + // Show fingerprint icon if applicable. |
| + this.setUserPodFingerprintIcon( |
| + true, username, FINGERPRINT_STATES.DEFAULT); |
|
jdufault
2017/03/08 23:21:27
Please annotate true parameter, ie, true /*visible
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Done.
|
| + }, |
| + |
| + /** |
| + * Set a fingerprint icon in the user pod of |username|. |
| + * @param {boolean} visible Set visiblity of the fingerprint icon |
| + * @param {string} username Username of the selected user |
| + * @param {number} state Fingerprint unlock state |
| + */ |
| + setUserPodFingerprintIcon: function(visible, username, state) { |
|
jdufault
2017/03/08 23:21:27
Instead of having |visible| and |state| be separat
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Thanks. I added a hidden class to map with hidden
|
| + var pod = this.getPodWithUsername_(username); |
| + if (pod == null) { |
| + console.error( |
| + 'Unable to set user pod fingerprint icon: user pod not found.'); |
| + return; |
| + } |
| + pod.fingerprintAuthenticated_ = false; |
| + if (!pod.fingerprintIconElement) |
| + return; |
| + if (!pod.user.allowFingerprint || !visible || |
| + !pod.customIconElement.hidden) { |
| + pod.fingerprintIconElement.hidden = true; |
| + pod.submitButton.hidden = false; |
| + return; |
| + } |
| + |
| + FINGERPRINT_STATES_MAPPING.forEach(function(icon) { |
| + pod.fingerprintIconElement.classList.toggle( |
| + icon.class, state == icon.state); |
| + }); |
| + pod.fingerprintIconElement.setAttribute( |
| + 'aria-label', loadTimeData.getString('fingerprintIconMessage')); |
| + pod.fingerprintIconElement.hidden = false; |
| + pod.submitButton.hidden = pod.passwordElement.value.length <= 0; |
| + this.updatePasswordField_(pod, state); |
| + if (state == FINGERPRINT_STATES.DEFAULT) |
| + return; |
| + |
| + pod.fingerprintAuthenticated_ = true; |
| + this.setActivatedPod(pod); |
| + if (state == FINGERPRINT_STATES.FINGERPRINT_FAILED) { |
| + /** @const */ var RESET_ICON_TIMEOUT_MS = 500; |
| + setTimeout( |
| + this.resetIconAndPasswordField_.bind(this, pod), |
| + RESET_ICON_TIMEOUT_MS); |
| + } |
| + }, |
| + |
| + /** |
| + * Reset the fingerprint icon and password field. |
| + * @param {UserPod} pod Pod to reset. |
| + */ |
| + resetIconAndPasswordField_:function(pod) { |
| + if (!pod.fingerprintIconElement) |
| + return; |
| + this.setUserPodFingerprintIcon( |
| + true, pod.user.username, FINGERPRINT_STATES.DEFAULT); |
| + }, |
| + |
| + /** |
| + * Remove the fingerprint icon in the user pod. |
| + * @param {string} username Username of the selected user |
| + */ |
| + removeUserPodFingerprintIcon: function(username) { |
| + var pod = this.getPodWithUsername_(username); |
| + if (pod == null) { |
| + console.error( |
| + 'Unable to remove user pod fingerprint icon: user pod not found.'); |
| + return; |
| + } |
| + this.resetIconAndPasswordField_(pod); |
| + if (pod.fingerprintIconElement) |
|
jdufault
2017/03/08 23:21:27
nit: {}
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:11
Done.
|
| + pod.fingerprintIconElement.parentNode.removeChild( |
| + pod.fingerprintIconElement); |
| + pod.submitButton.hidden = false; |
| + }, |
| + |
| + /** |
| + * Updates the password field in the user pod. |
| + * @param {UserPod} pod Pod to update. |
| + * @param {number} state Fingerprint unlock state |
| + */ |
| + updatePasswordField_: function(pod, state) { |
| + FINGERPRINT_STATES_MAPPING.forEach(function(item) { |
| + pod.passwordElement.classList.toggle(item.class, state == item.state); |
| + }); |
| + var placeholderStr = loadTimeData.getString( |
| + pod.isPinShown() ? 'pinKeyboardPlaceholderPinPassword' : |
| + 'passwordHint'); |
| + if (state == FINGERPRINT_STATES.FINGERPRINT_SIGNIN) { |
|
jdufault
2017/03/08 23:21:27
There are only two ifs here so it is probably not
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Thanks for the suggestion. I will create the mappi
jdufault
2017/03/09 21:11:25
Yep, as-is is good - with only two elements it's n
|
| + placeholderStr = loadTimeData.getString('fingerprintSigningin'); |
| + } else if (state == FINGERPRINT_STATES.FINGERPRINT_FAILED) { |
| + placeholderStr = loadTimeData.getString('fingerprintSigninFailed'); |
| + } |
| + pod.passwordElement.placeholder = placeholderStr; |
| }, |
| /** |
| @@ -3308,6 +3511,7 @@ cr.define('login', function() { |
| pod.isActionBoxMenuHovered = false; |
| pod.classList.remove('focused'); |
| pod.setPinVisibility(false); |
| + this.setUserPodFingerprintIcon(false, pod.user.username); |
|
jdufault
2017/03/08 23:21:27
Annotate false parameter
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Done.
|
| // On Desktop, the faded style is not set correctly, so we should |
| // manually fade out non-focused pods if there is a focused pod. |
| if (pod.user.isDesktopUser && podToFocus) |
| @@ -3347,6 +3551,8 @@ cr.define('login', function() { |
| this.firstShown_ = false; |
| this.lastFocusedPod_ = podToFocus; |
| this.scrollFocusedPodIntoView(); |
| + this.setUserPodFingerprintIcon( |
| + true, podToFocus.user.username, FINGERPRINT_STATES.DEFAULT); |
|
jdufault
2017/03/08 23:21:27
Annotate true parameter
xiaoyinh(OOO Sep 11-29)
2017/03/09 20:59:12
Done.
|
| } else { |
| chrome.send('noPodFocused'); |
| } |