| // Copyright 2018 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. |
| |
| Polymer({ |
| is: 'discover-pin-setup-card', |
| |
| behaviors: [DiscoverModuleBehavior], |
| }); |
| |
| { |
| /** @const */ |
| let PIN_SETUP_STEPS = { |
| LOADING: 'loading', |
| PASSWORD: 'password', |
| START: 'start', |
| CONFIRM: 'confirm', |
| DONE: 'done', |
| }; |
| |
| // Time out for quickUnlock API. |
| let kApiTimeout = 10000; |
| |
| Polymer({ |
| is: 'discover-pin-setup-module', |
| |
| behaviors: [DiscoverModuleBehavior], |
| |
| properties: { |
| /** |
| * True when "Learn more" link should be hidden. |
| */ |
| learnMoreLinkHidden: { |
| type: Boolean, |
| value: false, |
| }, |
| |
| /** |
| * Flag from <setup-pin-keyboard>. |
| * @private |
| */ |
| enableSubmit_: { |
| type: Boolean, |
| value: false, |
| }, |
| |
| /** |
| * Flag from <setup-pin-keyboard>. |
| * @private |
| */ |
| isConfirmStep_: { |
| type: Boolean, |
| value: false, |
| observer: 'onIsConfirmStepChanged_', |
| }, |
| |
| /** QuickUnlockPrivate API token. */ |
| authToken_: { |
| type: String, |
| observer: 'onAuthTokenChanged_', |
| }, |
| |
| setModes: Object, |
| |
| /** @type{PIN_SETUP_STEPS} */ |
| step_: { |
| type: Number, |
| value: PIN_SETUP_STEPS.LOADING, |
| observer: 'onStepChanged_', |
| }, |
| |
| /** |
| * Interface for chrome.quickUnlockPrivate calls. May be overridden by |
| * tests. |
| * @type {QuickUnlockPrivate} |
| * @private |
| */ |
| quickUnlockPrivate_: {type: Object, value: chrome.quickUnlockPrivate}, |
| |
| /** |
| * writeUma is a function that handles writing uma stats. It may be |
| * overridden for tests. |
| * |
| * @type {Function} |
| * @private |
| */ |
| writeUma_: { |
| type: Object, |
| value: function() { |
| return settings.recordLockScreenProgress; |
| } |
| }, |
| |
| /** |
| * When this flag is true, Discover UI is displayed as part of FirstRun |
| * UI. |
| */ |
| firstRun: { |
| type: Boolean, |
| value: false, |
| }, |
| |
| /** |
| * Value of user password input field. |
| * @private |
| */ |
| password_: { |
| type: String, |
| value: '', |
| observer: 'onTypedPasswordChange_', |
| }, |
| |
| /** |
| * Helper property which marks password as valid/invalid. |
| * @private |
| */ |
| passwordInvalid_: { |
| type: Boolean, |
| value: false, |
| }, |
| }, |
| |
| /** |
| * True when in first run mode and primary user password has been requested. |
| * @private |
| */ |
| firstRunUserPasswordRequested_: false, |
| |
| /** |
| * Timeout ID for automatic skip timer. |
| * @private |
| */ |
| autoSkipTimer_: undefined, |
| |
| /** |
| * Starts automatic skip timer. |
| * @private |
| */ |
| startAutoSkipTimer_: function() { |
| if (this.autoSkipTimer_ !== undefined) |
| return; |
| |
| this.autoSkipTimer_ = setTimeout(() => { |
| console.error('autoSkipTimer triggered!'); |
| this.onSkipButton_(); |
| }, kApiTimeout); |
| }, |
| |
| /** |
| * Kills automatic skip timer. |
| * @private |
| */ |
| stopAutoSkipTimer_: function() { |
| if (this.autoSkipTimer_) |
| clearTimeout(this.autoSkipTimer_); |
| |
| this.autoSkipTimer_ = undefined; |
| }, |
| |
| /** |
| * step_ observer. Makes sure default focus follows step change, and kills |
| * autoskip timer if spinner is hidden. |
| * @private |
| */ |
| onStepChanged_: function() { |
| let dialogs = this.root.querySelectorAll('oobe-dialog'); |
| for (let dialog of dialogs) { |
| if (dialog.hidden) |
| continue; |
| |
| dialog.focus(); |
| break; |
| } |
| if (this.step_ == PIN_SETUP_STEPS.LOADING) |
| return; |
| |
| this.stopAutoSkipTimer_(); |
| }, |
| |
| /** @override */ |
| show: function() { |
| if (this.firstRun) { |
| this.getFirstRunUserPassword_(); |
| } else { |
| this.step_ = PIN_SETUP_STEPS.PASSWORD; |
| } |
| }, |
| |
| /** |
| * Starts fetching QuickUnlock token. |
| * @private |
| */ |
| getToken_: function(password) { |
| this.step_ = PIN_SETUP_STEPS.LOADING; |
| this.startAutoSkipTimer_(); |
| |
| this.quickUnlockPrivate_.getAuthToken(password, (tokenInfo) => { |
| if (chrome.runtime.lastError) { |
| console.error( |
| 'getAuthToken(): Failed: ' + chrome.runtime.lastError.message); |
| if (this.firstRun) { |
| // Trigger 'token expired' |
| this.authToken_ = ''; |
| } else { |
| this.step_ = PIN_SETUP_STEPS.PASSWORD; |
| this.passwordInvalid_ = true; |
| // Select the whole password if user entered an incorrect password. |
| this.$.passwordInput.select(); |
| } |
| return; |
| } |
| this.setToken(tokenInfo); |
| }); |
| }, |
| |
| /** |
| * Starts fetching primary user password. |
| * @private |
| */ |
| getFirstRunUserPassword_: function() { |
| this.startAutoSkipTimer_(); |
| this.discoverCallWithReply( |
| 'discover.pinSetup.getUserPassword', [], (password) => { |
| if (chrome.runtime.lastError) { |
| console.error( |
| 'getUserPassword() failed: ' + |
| chrome.runtime.lastError.message); |
| // Trigger 'token expired' |
| this.authToken_ = ''; |
| return; |
| } |
| this.getToken_(password); |
| }); |
| }, |
| |
| /** |
| * Receives new AuthToken. |
| * @private |
| */ |
| setToken: function(tokenInfo) { |
| this.authToken_ = tokenInfo.token; |
| // Subtract time from the expiration time to account for IPC delays. |
| // Treat values less than the minimum as 0 for testing. |
| const IPC_SECONDS = 2; |
| const lifetimeMs = tokenInfo.lifetimeSeconds > IPC_SECONDS ? |
| (tokenInfo.lifetimeSeconds - IPC_SECONDS) * 1000 : |
| 0; |
| this.clearAuthTokenTimeoutId_ = setTimeout(() => { |
| this.authToken_ = ''; |
| }, lifetimeMs); |
| |
| this.step_ = PIN_SETUP_STEPS.START; |
| }, |
| |
| /** |
| * Called when the authToken_ changes. If the authToken_ is NOT valid, |
| * skips module. |
| * @private |
| */ |
| onAuthTokenChanged_: function() { |
| this.password_ = ''; |
| if (!this.authToken_) { |
| this.setModes = null; |
| this.step_ = PIN_SETUP_STEPS.LOADING; |
| this.onSkipButton_(); |
| return; |
| } |
| this.setModes = (modes, credentials, onComplete) => { |
| this.quickUnlockPrivate_.setModes( |
| this.authToken_, modes, credentials, () => { |
| let result = true; |
| if (chrome.runtime.lastError) { |
| console.error( |
| 'setModes failed: ' + chrome.runtime.lastError.message); |
| result = false; |
| } |
| onComplete(result); |
| }); |
| }; |
| }, |
| |
| /** @private */ |
| onIsConfirmStepChanged_: function() { |
| if (this.isConfirmStep_) |
| this.step_ = PIN_SETUP_STEPS.CONFIRM; |
| }, |
| |
| /** @private */ |
| onPinSubmit_: function() { |
| this.$.pinKeyboard.doSubmit(); |
| }, |
| |
| /** @private */ |
| onSetPinDone_: function() { |
| this.step_ = PIN_SETUP_STEPS.DONE; |
| }, |
| |
| /** @private */ |
| onSkipButton_: function() { |
| this.password_ = ''; |
| this.authToken_ = ''; |
| |
| this.stopAutoSkipTimer_(); |
| this.$.pinKeyboard.resetState(); |
| this.fire('module-continue'); |
| }, |
| |
| /** @private */ |
| onNextButton_: function() { |
| this.onPinSubmit_(); |
| }, |
| |
| /** @private */ |
| onDoneButton_: function() { |
| this.password_ = ''; |
| this.authToken_ = ''; |
| this.$.pinKeyboard.resetState(); |
| this.fire('module-continue'); |
| }, |
| |
| /** |
| * Returns true if current UI step is different from expected. |
| * @param {PIN_SETUP_STEPS} current_step |
| * @param {PIN_SETUP_STEPS} expected_step |
| * @private |
| */ |
| isStepHidden_: function(current_step, expected_step) { |
| return current_step != expected_step; |
| }, |
| |
| /** |
| * Returns true if "Pin setup" dialog is hidden. |
| * @param {PIN_SETUP_STEPS} current_step |
| * @private |
| */ |
| isPinSetupHidden_: function(current_step) { |
| return !['start', 'confirm', 'done'].includes(current_step); |
| }, |
| |
| /** |
| * Returns true if "Next" button is disabled. |
| * @param {PIN_SETUP_STEPS} step this.step_ |
| * @param {Boolean} enableSubmit this.enableSubmit_ |
| * @private |
| */ |
| isNextDisabled_: function(step, enableSubmit) { |
| return step != PIN_SETUP_STEPS.DONE && !enableSubmit; |
| }, |
| |
| /** |
| * Triggers "Next" button if "Enter" key is pressed when entering password. |
| * @param {Event} e Keyboard event. |
| * @private |
| */ |
| onKeypress_: function(e) { |
| if (e.key != 'Enter') |
| return; |
| |
| this.onPasswordSubmitButton_(); |
| }, |
| |
| /** @private */ |
| onPasswordSubmitButton_: function() { |
| if (!this.password_) |
| return; |
| |
| let password = this.password_; |
| this.password_ = ''; |
| this.getToken_(password); |
| }, |
| |
| /** @private */ |
| onTypedPasswordChange_: function() { |
| this.passwordInvalid_ = false; |
| }, |
| }); |
| } |