blob: 819ac412463666d3fe11d473d93656628e3c3d14 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// 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_button/cr_button.js';
import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import './strings.m.js';
import './password_list_item.js';
import './dialogs/add_password_dialog.js';
import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {assert} from 'chrome://resources/js/assert_ts.js';
import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {PasswordManagerImpl} from './password_manager_proxy.js';
import {getTemplate} from './passwords_section.html.js';
import {Route, RouteObserverMixin, UrlParam} from './router.js';
export interface PasswordsSectionElement {
$: {
addPasswordButton: CrButtonElement,
passwordsList: IronListElement,
};
}
const PasswordsSectionElementBase =
RouteObserverMixin(I18nMixin(PolymerElement));
export class PasswordsSectionElement extends PasswordsSectionElementBase {
static get is() {
return 'passwords-section';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
/**
* Password groups displayed in the UI.
*/
groups_: {
type: Array,
value: () => [],
},
/** Filter on the saved passwords and exceptions. */
searchTerm_: {
type: String,
value: '',
},
showAddPasswordDialog_: Boolean,
};
}
private groups_: chrome.passwordsPrivate.CredentialGroup[] = [];
private searchTerm_: string;
private showAddPasswordDialog_: boolean;
private setSavedPasswordsListener_: (
(entries: chrome.passwordsPrivate.PasswordUiEntry[]) => void)|null = null;
override connectedCallback() {
super.connectedCallback();
const updateGroups = () => {
PasswordManagerImpl.getInstance().getCredentialGroups().then(
groups => this.groups_ = groups);
};
this.setSavedPasswordsListener_ = _passwordList => {
updateGroups();
};
updateGroups();
PasswordManagerImpl.getInstance().addSavedPasswordListChangedListener(
this.setSavedPasswordsListener_);
}
override disconnectedCallback() {
super.disconnectedCallback();
assert(this.setSavedPasswordsListener_);
PasswordManagerImpl.getInstance().removeSavedPasswordListChangedListener(
this.setSavedPasswordsListener_);
this.setSavedPasswordsListener_ = null;
}
override currentRouteChanged(newRoute: Route, _oldRoute: Route): void {
const searchTerm = newRoute.queryParameters.get(UrlParam.SEARCH_TERM) || '';
if (searchTerm !== this.searchTerm_) {
this.searchTerm_ = searchTerm;
}
}
private hideGroupsList_(): boolean {
return this.groups_.filter(this.groupFilter_()).length === 0;
}
private groupFilter_():
((entry: chrome.passwordsPrivate.CredentialGroup) => boolean) {
const term = this.searchTerm_.trim().toLowerCase();
// Group is matching if:
// * group name includes term,
// * any credential's username within a group includes a term,
// * any credential within a group includes a term in a domain.
return group => group.name.toLowerCase().includes(term) ||
group.entries.some(
credential => credential.username.toLowerCase().includes(term) ||
credential.affiliatedDomains?.some(
domain => domain.name.toLowerCase().includes(term)));
}
private async announceSearchResults_() {
if (!this.searchTerm_.trim()) {
return;
}
// TODO(crbug.com/1400289): Announce search result.
}
private onAddPasswordClick_() {
this.showAddPasswordDialog_ = true;
}
private onAddPasswordDialogClosed_() {
this.showAddPasswordDialog_ = false;
}
}
declare global {
interface HTMLElementTagNameMap {
'passwords-section': PasswordsSectionElement;
}
}
customElements.define(PasswordsSectionElement.is, PasswordsSectionElement);