blob: 5016640abb34db4ddf8e5fe5df658517693d22d9 [file] [log] [blame]
// Copyright 2020 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.
import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import '../settings_shared.css.js';
import 'chrome://resources/cr_elements/icons.m.js';
import 'chrome://resources/cr_elements/shared_style_css.m.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
import 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js';
import '../i18n_setup.js';
import '../route.js';
import '../prefs/prefs.js';
import './password_check_edit_disclaimer_dialog.js';
import './password_check_list_item.js';
import './password_remove_confirmation_dialog.js';
// <if expr="is_chromeos">
import '../controls/password_prompt_dialog.js';
// </if>
import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js';
import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.m.js';
import {I18nMixin, I18nMixinInterface} from 'chrome://resources/js/i18n_mixin.js';
// <if expr="is_chromeos">
import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
// </if>
import {WebUIListenerMixin, WebUIListenerMixinInterface} from 'chrome://resources/js/web_ui_listener_mixin.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
// <if expr="is_chromeos">
import {loadTimeData} from '../i18n_setup.js';
// </if>
import {PrefsMixin, PrefsMixinInterface} from '../prefs/prefs_mixin.js';
import {routes} from '../route.js';
import {Route, RouteObserverMixin, RouteObserverMixinInterface, Router} from '../router.js';
// <if expr="is_chromeos">
import {BlockingRequestManager} from './blocking_request_manager.js';
// </if>
import {MergePasswordsStoreCopiesMixin, MergePasswordsStoreCopiesMixinInterface} from './merge_passwords_store_copies_mixin.js';
import {getTemplate} from './password_check.html.js';
import {PasswordCheckListItemElement} from './password_check_list_item.js';
import {PasswordCheckMixin, PasswordCheckMixinInterface} from './password_check_mixin.js';
import {PasswordCheckInteraction, SavedPasswordListChangedListener} from './password_manager_proxy.js';
import {PasswordRequestorMixin, PasswordRequestorMixinInterface} from './password_requestor_mixin.js';
import {UserUtilMixin, UserUtilMixinInterface} from './user_util_mixin.js';
const CheckState = chrome.passwordsPrivate.PasswordCheckState;
export interface SettingsPasswordCheckElement {
$: {
compromisedCredentialsBody: HTMLElement,
compromisedPasswordsDescription: HTMLElement,
controlPasswordCheckButton: CrButtonElement,
leakedPasswordList: HTMLElement,
menuEditPassword: HTMLButtonElement,
menuShowPassword: HTMLButtonElement,
moreActionsMenu: CrActionMenuElement,
mutedPasswordList: HTMLElement,
noCompromisedCredentials: HTMLElement,
signedOutUserLabel: HTMLElement,
subtitle: HTMLElement,
title: HTMLElement,
titleRow: HTMLElement,
weakCredentialsBody: HTMLElement,
weakPasswordsDescription: HTMLElement,
weakPasswordList: HTMLElement,
};
}
const SettingsPasswordCheckElementBase =
UserUtilMixin(MergePasswordsStoreCopiesMixin(RouteObserverMixin(
WebUIListenerMixin(I18nMixin(PrefsMixin(PasswordRequestorMixin(
PasswordCheckMixin((PolymerElement))))))))) as {
new (): PolymerElement & I18nMixinInterface &
WebUIListenerMixinInterface & PrefsMixinInterface &
PasswordCheckMixinInterface & PasswordRequestorMixinInterface &
RouteObserverMixinInterface &
MergePasswordsStoreCopiesMixinInterface & UserUtilMixinInterface,
};
export class SettingsPasswordCheckElement extends
SettingsPasswordCheckElementBase {
static get is() {
return 'settings-password-check';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
title_: {
type: String,
computed: 'computeTitle_(status, canUsePasswordCheckup_)',
},
mutedLeakedCredentialsTitle_: {
type: String,
computed: 'computeMutedLeakedCredentialsTitle_(mutedPasswords)',
},
canUsePasswordCheckup_: {
type: Boolean,
computed: 'computeCanUsePasswordCheckup_(syncPrefs, ' +
'isSyncingPasswords)',
},
isButtonHidden_: {
type: Boolean,
computed: 'computeIsButtonHidden_(status, signedIn, isInitialStatus)',
},
isMutePasswordButtonEnabled_: {
type: Boolean,
computed: 'computeIsMutePasswordButtonEnabled_(activePassword_)',
},
isUnmutePasswordButtonEnabled_: {
type: Boolean,
computed: 'computeIsUnmutePasswordButtonEnabled_(activePassword_)',
},
showPasswordEditDialog_: Boolean,
showPasswordRemoveDialog_: Boolean,
showPasswordEditDisclaimer_: Boolean,
/**
* The password that the user is interacting with now.
*/
activePassword_: Object,
showCompromisedCredentialsBody_: {
type: Boolean,
computed: 'computeShowCompromisedCredentialsBody_(' +
'signedIn, leakedPasswords, mutedPasswords)',
},
showMutedPasswordsSection_: {
type: Boolean,
computed: 'computeShowMutedLeakedCredentials_(mutedPasswords)',
},
showNoCompromisedPasswordsLabel_: {
type: Boolean,
computed: 'computeShowNoCompromisedPasswordsLabel_(' +
'signedIn, prefs.*, status, leakedPasswords)',
},
showHideMenuTitle_: {
type: String,
computed: 'computeShowHideMenuTitle(activePassword_)',
},
iconHaloClass_: {
type: String,
computed: 'computeIconHaloClass_(' +
'status, signedIn, leakedPasswords, weakPasswords)',
},
/**
* The ids of insecure credentials for which user clicked "Change
* Password" button
*/
clickedChangePasswordIds_: {
type: Object,
value: new Set(),
},
// <if expr="is_chromeos">
showPasswordPromptDialog_: Boolean,
// </if>
};
}
private title_: string;
private mutedPasswordsTitle_: string;
private canUsePasswordCheckup_: boolean;
private isButtonHidden_: boolean;
private isMutePasswordButtonEnabled_: boolean;
private isUnmutePasswordButtonEnabled_: boolean;
private showPasswordEditDialog_: boolean;
private showPasswordRemoveDialog_: boolean;
private showPasswordEditDisclaimer_: boolean;
private activePassword_: chrome.passwordsPrivate.PasswordUiEntry|null;
private showCompromisedCredentialsBody_: boolean;
private showMutedPasswordsSection_: boolean;
private showNoCompromisedPasswordsLabel_: boolean;
private showHideMenuTitle_: string;
private iconHaloClass_: string;
private clickedChangePasswordIds_: Set<number>;
// <if expr="is_chromeos">
private showPasswordPromptDialog_: boolean;
// </if>
private activeDialogAnchorStack_: HTMLElement[]|null;
private activeListItem_: PasswordCheckListItemElement|null;
startCheckAutomaticallySucceeded: boolean = false;
private setSavedPasswordsListener_: SavedPasswordListChangedListener|null;
constructor() {
super();
/**
* A stack of the elements that triggered dialog to open and should
* therefore receive focus when that dialog is closed. The bottom of the
* stack is the element that triggered the earliest open dialog and top of
* the stack is the element that triggered the most recent (i.e. active)
* dialog. If no dialog is open, the stack is empty.
*/
this.activeDialogAnchorStack_ = null;
/**
* The password_check_list_item that the user is interacting with now.
*/
this.activeListItem_ = null;
/**
* Observer for saved passwords to update startCheckAutomaticallySucceeded
* once they are changed. It's needed to run password check on navigation
* again once passwords changed.
*/
this.setSavedPasswordsListener_ = null;
}
override connectedCallback() {
super.connectedCallback();
// <if expr="is_chromeos">
// If the user's account supports the password check, an auth token will be
// required in order for them to view or export passwords. Otherwise there
// is no additional security so |tokenRequestManager_| will immediately
// resolve requests.
this.tokenRequestManager =
loadTimeData.getBoolean('userCannotManuallyEnterPassword') ?
new BlockingRequestManager() :
new BlockingRequestManager(() => this.openPasswordPromptDialog_());
// </if>
this.activeDialogAnchorStack_ = [];
const setSavedPasswordsListener: SavedPasswordListChangedListener =
_list => {
this.startCheckAutomaticallySucceeded = false;
};
this.setSavedPasswordsListener_ = setSavedPasswordsListener;
this.passwordManager!.addSavedPasswordListChangedListener(
setSavedPasswordsListener);
}
override disconnectedCallback() {
super.disconnectedCallback();
assert(this.setSavedPasswordsListener_);
this.passwordManager!.removeSavedPasswordListChangedListener(
this.setSavedPasswordsListener_);
this.setSavedPasswordsListener_ = null;
}
/**
* Tries to start bulk password check on page open if instructed to do so and
* didn't start successfully before
*/
override currentRouteChanged(currentRoute: Route) {
const router = Router.getInstance();
if (currentRoute.path === routes.CHECK_PASSWORDS.path &&
!this.startCheckAutomaticallySucceeded &&
router.getQueryParameters().get('start') === 'true') {
this.passwordManager!.recordPasswordCheckInteraction(
PasswordCheckInteraction.START_CHECK_AUTOMATICALLY);
this.passwordManager!.startBulkPasswordCheck().then(
() => {
this.startCheckAutomaticallySucceeded = true;
},
_error => {
// Catching error
});
}
// Requesting status on navigation to update elapsedTimeSinceLastCheck
this.passwordManager!.getPasswordCheckStatus().then(
status => this.status = status);
}
/**
* Start/Stop bulk password check.
*/
private onPasswordCheckButtonClick_() {
switch (this.status.state) {
case CheckState.RUNNING:
this.passwordManager!.recordPasswordCheckInteraction(
PasswordCheckInteraction.STOP_CHECK);
this.passwordManager!.stopBulkPasswordCheck();
return;
case CheckState.IDLE:
case CheckState.CANCELED:
case CheckState.OFFLINE:
case CheckState.OTHER_ERROR:
this.passwordManager!.recordPasswordCheckInteraction(
PasswordCheckInteraction.START_CHECK_MANUALLY);
this.passwordManager!.startBulkPasswordCheck();
return;
case CheckState.SIGNED_OUT:
// Runs the startBulkPasswordCheck to check passwords for weakness that
// works for both sign in and sign out users.
this.passwordManager!.recordPasswordCheckInteraction(
PasswordCheckInteraction.START_CHECK_MANUALLY);
this.passwordManager!.startBulkPasswordCheck().then(
() => {},
_error => {
// Catching error
});
return;
case CheckState.NO_PASSWORDS:
case CheckState.QUOTA_LIMIT:
}
assertNotReached(
'Can\'t trigger an action for state: ' + this.status.state);
}
/**
* @return true if there are any compromised credentials.
*/
private hasLeakedCredentials_(): boolean {
return !!this.leakedPasswords.length;
}
/**
* @return true if there are any compromised credentials that are dismissed.
*/
private computeShowMutedLeakedCredentials_(): boolean {
return this.isMutedPasswordsEnabled && !!this.mutedPasswords.length;
}
/**
* @return true if there are any weak credentials.
*/
private hasWeakCredentials_(): boolean {
return !!this.weakPasswords.length;
}
/**
* @return true if there are any insecure credentials.
*/
private hasInsecureCredentials_(): boolean {
return !!this.leakedPasswords.length || this.hasWeakCredentials_();
}
/**
* @return A relevant help text for weak passwords. Contains a link that
* depends on whether the user is syncing passwords or not.
*/
private getWeakPasswordsHelpText_(): string {
return this.i18nAdvanced(
this.isSyncingPasswords ? 'weakPasswordsDescriptionGeneration' :
'weakPasswordsDescription');
}
private onMoreActionsClick_(
event: CustomEvent<{moreActionsButton: HTMLElement}>) {
const target = event.detail.moreActionsButton;
this.$.moreActionsMenu.showAt(target);
this.activeDialogAnchorStack_!.push(target);
this.activeListItem_ = event.target as PasswordCheckListItemElement;
this.activePassword_ = this.activeListItem_!.item;
}
private onMenuShowPasswordClick_() {
this.activePassword_!.password ? this.activeListItem_!.hidePassword() :
this.activeListItem_!.showPassword();
this.$.moreActionsMenu.close();
this.activePassword_ = null;
this.activeDialogAnchorStack_!.pop();
}
private onEditPasswordClick_() {
assert(this.activePassword_);
this.requestPlaintextPassword(
this.activePassword_.id,
chrome.passwordsPrivate.PlaintextReason.EDIT)
.then(
password => {
this.activePassword_!.password = password;
this.showPasswordEditDialog_ = true;
this.passwordManager!.recordPasswordCheckInteraction(
PasswordCheckInteraction.EDIT_PASSWORD);
},
_error => {
// <if expr="not (chromeos_ash or chromeos_lacros)">
this.activePassword_ = null;
this.onPasswordEditDialogClosed_();
// </if>
});
this.$.moreActionsMenu.close();
}
private onMenuRemovePasswordClick_() {
this.$.moreActionsMenu.close();
this.showPasswordRemoveDialog_ = true;
}
private onMenuMuteCompromisedPasswordClick_() {
this.passwordManager!.recordPasswordCheckInteraction(
PasswordCheckInteraction.MUTE_PASSWORD);
this.$.moreActionsMenu.close();
this.passwordManager!.muteInsecureCredential(this.activeListItem_!.item);
}
private onMenuUnmuteMutedCompromisedPasswordClick_() {
this.passwordManager!.recordPasswordCheckInteraction(
PasswordCheckInteraction.UNMUTE_PASSWORD);
this.$.moreActionsMenu.close();
this.passwordManager!.unmuteInsecureCredential(this.activeListItem_!.item);
}
private onPasswordRemoveDialogClosed_() {
this.showPasswordRemoveDialog_ = false;
const toFocus = this.activeDialogAnchorStack_!.pop();
assert(toFocus);
focusWithoutInk(toFocus);
}
private onPasswordEditDialogClosed_() {
this.showPasswordEditDialog_ = false;
const toFocus = this.activeDialogAnchorStack_!.pop();
assert(toFocus);
focusWithoutInk(toFocus);
}
private onAlreadyChangedClick_(event: CustomEvent<HTMLElement>) {
const target = event.detail;
// Setting required properties for Password Check Edit dialog
this.activeDialogAnchorStack_!.push(target);
this.activeListItem_ = event.target as PasswordCheckListItemElement;
this.activePassword_ = this.activeListItem_.item;
this.showPasswordEditDisclaimer_ = true;
}
private onEditDisclaimerClosed_() {
this.showPasswordEditDisclaimer_ = false;
const toFocus = this.activeDialogAnchorStack_!.pop();
assert(toFocus);
focusWithoutInk(toFocus);
}
private computeShowHideMenuTitle(): string {
return this.i18n(
this.activeListItem_!.isPasswordVisible ? 'hideCompromisedPassword' :
'showCompromisedPassword');
}
private computeIconHaloClass_(): string {
return !this.isCheckInProgress_() && this.hasLeakedCredentials_() ?
'warning-halo' :
'';
}
/**
* @return the icon (warning, info or error) indicating the check status.
*/
private getStatusIcon_(): string {
if (!this.hasInsecureCredentialsOrErrors_()) {
return 'settings:check-circle';
}
if (this.hasLeakedCredentials_()) {
return 'cr:warning';
}
return 'cr:info';
}
/**
* @return the CSS class used to style the icon (warning, info or error).
*/
private getStatusIconClass_(): string {
if (!this.hasInsecureCredentialsOrErrors_()) {
return this.waitsForFirstCheck_() ? 'hidden' : 'no-security-issues';
}
if (this.hasLeakedCredentials_()) {
return 'has-security-issues';
}
return '';
}
/**
* @return the title message indicating the state of the last/ongoing check.
*/
private computeTitle_(): string {
switch (this.status.state) {
case CheckState.IDLE:
return this.waitsForFirstCheck_() ? '' : this.i18n('checkedPasswords');
case CheckState.CANCELED:
return this.i18n('checkPasswordsCanceled');
case CheckState.RUNNING:
// Returns the progress of a running check. Ensures that both numbers
// are at least 1.
const alreadyProcessed = this.status.alreadyProcessed || 0;
return this.i18n(
'checkPasswordsProgress', alreadyProcessed + 1,
Math.max(this.status.remainingInQueue! + alreadyProcessed, 1));
case CheckState.OFFLINE:
return this.i18n('checkPasswordsErrorOffline');
case CheckState.SIGNED_OUT:
// When user is signed out we run the password weakness check. Since it
// works very fast, we always shows "Checked passwords" in this case.
return this.i18n('checkedPasswords');
case CheckState.NO_PASSWORDS:
return this.i18n('checkPasswordsErrorNoPasswords');
case CheckState.QUOTA_LIMIT:
// Note: For the checkup case we embed the link as HTML, thus we need to
// use i18nAdvanced() here as well as the `inner-h-t-m-l` attribute in
// the DOM.
return this.canUsePasswordCheckup_ ?
this.i18nAdvanced('checkPasswordsErrorQuotaGoogleAccount') :
this.i18n('checkPasswordsErrorQuota');
case CheckState.OTHER_ERROR:
return this.i18n('checkPasswordsErrorGeneric');
default:
assertNotReached('Can\'t find a title for state: ' + this.status.state);
}
}
/**
* @return the muted / dismissed passwords section title which includes the
* number of muted passwords.
*/
private computeMutedLeakedCredentialsTitle_(): string {
return this.i18n('mutedPasswords', this.mutedPasswords.length);
}
/**
* @return true iff a check is running right according to the given |status|.
*/
private isCheckInProgress_(): boolean {
return this.status.state === CheckState.RUNNING;
}
/**
* @return true if a password is compromised. A weak password may not be
* compromised.
*/
private isPasswordCompromised_(): boolean {
return !!this.activePassword_ && !!this.activePassword_!.compromisedInfo;
}
/**
* @return true if the pref value for
* profile.password_dismiss_compromised_alert exists and equals to false.
*/
private isMutingDisabledByPrefs_(): boolean {
return !!this.prefs &&
this.getPref('profile.password_dismiss_compromised_alert').value ===
false;
}
/**
* @return true if muting is enabled
* and the password is compromised and is dismissable/mutable.
*/
private computeIsMutePasswordButtonEnabled_(): boolean {
return this.isMutedPasswordsEnabled && this.isPasswordCompromised_() &&
!this.activePassword_!.compromisedInfo!.isMuted;
}
/**
* @return true if unmuting is enabled
* and the password is compromised and is dismissed/muted.
*/
private computeIsUnmutePasswordButtonEnabled_(): boolean {
return this.isMutedPasswordsEnabled && this.isPasswordCompromised_() &&
!!this.activePassword_!.compromisedInfo!.isMuted;
}
/**
* @return true to show the timestamp when a check was completed successfully.
*/
private showsTimestamp_(): boolean {
return !!this.status.elapsedTimeSinceLastCheck &&
(this.status.state === CheckState.IDLE ||
this.status.state === CheckState.SIGNED_OUT);
}
/**
* @return the button caption indicating it's current functionality.
*/
private getButtonText_(): string {
switch (this.status.state) {
case CheckState.IDLE:
return this.waitsForFirstCheck_() ? this.i18n('checkPasswords') :
this.i18n('checkPasswordsAgain');
case CheckState.CANCELED:
return this.i18n('checkPasswordsAgain');
case CheckState.RUNNING:
return this.i18n('checkPasswordsStop');
case CheckState.OFFLINE:
case CheckState.NO_PASSWORDS:
case CheckState.OTHER_ERROR:
return this.i18n('checkPasswordsAgainAfterError');
case CheckState.SIGNED_OUT:
// We should allow signed out users to click the "Check again" button to
// run the passwords weakness check.
return this.i18n('checkPasswordsAgain');
case CheckState.QUOTA_LIMIT:
return ''; // Undefined behavior. Don't show any misleading text.
default:
assertNotReached(
'Can\'t find a button text for state: ' + this.status.state);
}
}
/**
* @return 'action-button' only for the very first check.
*/
private getButtonTypeClass_(): string {
return this.waitsForFirstCheck_() ? 'action-button' : ' ';
}
/**
* @return true iff the check/stop button should be visible for a given state.
*/
private computeIsButtonHidden_(): boolean {
switch (this.status.state) {
case CheckState.IDLE:
return this.isInitialStatus; // Only a native IDLE state allows checks.
case CheckState.CANCELED:
case CheckState.RUNNING:
case CheckState.OFFLINE:
case CheckState.OTHER_ERROR:
case CheckState.SIGNED_OUT:
return false;
case CheckState.NO_PASSWORDS:
case CheckState.QUOTA_LIMIT:
return true;
default:
assertNotReached(
'Can\'t determine button visibility for state: ' +
this.status.state);
}
}
/**
* @return The chrome:// address where the banner image is located.
*/
private bannerImageSrc_(isDarkMode: boolean): string {
const type =
(this.status.state === CheckState.IDLE && !this.waitsForFirstCheck_()) ?
'positive' :
'neutral';
const suffix = isDarkMode ? '_dark' : '';
return `chrome://settings/images/password_check_${type}${suffix}.svg`;
}
/**
* @return true iff the banner should be shown.
*/
private shouldShowBanner_(): boolean {
if (this.hasInsecureCredentials_()) {
return false;
}
return this.status.state === CheckState.CANCELED ||
!this.hasInsecureCredentialsOrErrors_();
}
/**
* @return true if there are insecure credentials or the status is unexpected
* for a regular password check.
*/
private hasInsecureCredentialsOrErrors_(): boolean {
if (this.hasInsecureCredentials_()) {
return true;
}
switch (this.status.state) {
case CheckState.IDLE:
case CheckState.RUNNING:
case CheckState.SIGNED_OUT:
return false;
case CheckState.CANCELED:
case CheckState.OFFLINE:
case CheckState.NO_PASSWORDS:
case CheckState.QUOTA_LIMIT:
case CheckState.OTHER_ERROR:
return true;
default:
assertNotReached(
'Not specified whether to state is an error: ' + this.status.state);
}
}
/**
* @return true if there are insecure credentials or the status is unexpected
* for a regular password check.
*/
private showsPasswordsCount_(): boolean {
if (this.hasInsecureCredentials_()) {
return true;
}
switch (this.status.state) {
case CheckState.IDLE:
return !this.waitsForFirstCheck_();
case CheckState.CANCELED:
case CheckState.RUNNING:
case CheckState.OFFLINE:
case CheckState.NO_PASSWORDS:
case CheckState.QUOTA_LIMIT:
case CheckState.OTHER_ERROR:
return false;
case CheckState.SIGNED_OUT:
// Shows "No security issues found" if user is signed out and doesn't
// have insecure credentials.
return true;
default:
assertNotReached(
'Not specified whether to show passwords for state: ' +
this.status.state);
}
}
/**
* @return a localized and pluralized string of the passwords count, depending
* on whether the user is signed in and whether other compromised passwords
* exist.
*/
private getPasswordsCount_(): string {
return !this.signedIn && this.leakedPasswords.length === 0 ?
this.weakPasswordsCount :
this.insecurePasswordsCount;
}
/**
* @return the label that should be shown in the compromised password section
* if a user is signed out. This label depends on whether the user already had
* compromised credentials that were found in the past.
*/
private getSignedOutUserLabel_(): string {
// This label contains the link, thus we need to use i18nAdvanced() here as
// well as the `inner-h-t-m-l` attribute in the DOM.
return this.i18nAdvanced(
this.hasLeakedCredentials_() ?
'signedOutUserHasCompromisedCredentialsLabel' :
'signedOutUserLabel');
}
/**
* @return true iff the leak or weak check was performed at least once before.
*/
private waitsForFirstCheck_(): boolean {
return !this.status.elapsedTimeSinceLastCheck;
}
/**
* @return whether the user can use the online Password Checkup.
*/
private computeCanUsePasswordCheckup_(): boolean {
return this.isSyncingPasswords &&
(!this.syncPrefs || !this.syncPrefs.encryptAllData);
}
private computeShowCompromisedCredentialsBody_(): boolean {
// Always shows compromised credentials section if user is signed out.
return !this.signedIn || this.hasLeakedCredentials_() ||
this.computeShowMutedLeakedCredentials_();
}
private computeShowNoCompromisedPasswordsLabel_(): boolean {
// Check if user isn't signed in.
if (!this.signedIn) {
return false;
}
// Check if breach detection is turned off in settings.
if (!this.prefs ||
!this.getPref('profile.password_manager_leak_detection').value) {
return false;
}
// Return true if there was a successful check and no compromised passwords
// were found.
return !this.hasLeakedCredentials_() && this.showsTimestamp_();
}
private onChangePasswordClick_(event: CustomEvent<{id: number}>) {
this.clickedChangePasswordIds_.add(event.detail.id);
this.notifyPath('clickedChangePasswordIds_.size');
}
private clickedChangePassword_(item: chrome.passwordsPrivate.PasswordUiEntry):
boolean {
return this.clickedChangePasswordIds_.has(item.id);
}
// <if expr="is_chromeos">
/**
* Copied from passwords_section.js.
* TODO(crbug.com/1074228): Extract to a separate behavior
*
* @param e Contains newly created auth token
* chrome.quickUnlockPrivate.TokenInfo. Note that its precise value is
* not relevant here, only the facts that it's created.
*/
private onTokenObtained_(e: CustomEvent<any>) {
assert(e.detail);
this.tokenRequestManager.resolve();
}
private onPasswordPromptClosed_() {
this.showPasswordPromptDialog_ = false;
const toFocus = this.activeDialogAnchorStack_!.pop();
assert(toFocus);
focusWithoutInk(toFocus);
}
private openPasswordPromptDialog_() {
this.activeDialogAnchorStack_!.push(getDeepActiveElement() as HTMLElement);
this.showPasswordPromptDialog_ = true;
}
// </if>
}
declare global {
interface HTMLElementTagNameMap {
'settings-password-check': SettingsPasswordCheckElement;
}
}
customElements.define(
SettingsPasswordCheckElement.is, SettingsPasswordCheckElement);