blob: da55d195b57e6d852d1163a242e41b97daa17514 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview The 'certificate-list' component shows a list of
* certificates with a header, an expander, and optionally an "export all"
* button.
*
* This component is used in the new Certificate Management UI in
* ./certificate_manager.ts.
*/
import '/strings.m.js';
import './certificate_entry.js';
import './certificate_manager_style.css.js';
import '//resources/cr_elements/cr_expand_button/cr_expand_button.js';
import '//resources/cr_elements/cr_button/cr_button.js';
import '//resources/cr_elements/cr_collapse/cr_collapse.js';
import '//resources/cr_elements/cr_shared_style.css.js';
import '//resources/cr_elements/cr_shared_vars.css.js';
import type {CrCollapseElement} from '//resources/cr_elements/cr_collapse/cr_collapse.js';
import {I18nMixin} from '//resources/cr_elements/i18n_mixin.js';
import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './certificate_list.html.js';
import type {ActionResult, CertificateSource, SummaryCertInfo} from './certificate_manager.mojom-webui.js';
import {CertificatesBrowserProxy} from './certificates_browser_proxy.js';
const CertificateListElementBase = I18nMixin(PolymerElement);
export interface CertificateListElement {
$: {
certs: CrCollapseElement,
exportCerts: HTMLElement,
importCert: HTMLElement,
importAndBindCert: HTMLElement,
noCertsRow: HTMLElement,
listHeader: HTMLElement,
expandButton: HTMLElement,
};
}
export class CertificateListElement extends CertificateListElementBase {
static get is() {
return 'certificate-list';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
certSource: Number,
headerText: String,
showImport: {
type: Boolean,
value: false,
},
showImportAndBind: {
type: Boolean,
value: false,
},
// True if the list should not be collapsible.
// Empty lists will always not be collapsible.
noCollapse: {
type: Boolean,
value: false,
},
// True if the export button should be hidden.
// Export button may also be hidden if there are no certs in the list.
hideExport: {
type: Boolean,
value: false,
},
// True if the entire list (including the header) should be hidden if the
// list is empty.
hideIfEmpty: {
type: Boolean,
value: false,
},
// True if the header should be hidden. This will make the list
// non-collapsible.
hideHeader: {
type: Boolean,
value: false,
},
// True if the cert metadata is editable
certMetadataEditable: {
type: Boolean,
value: false,
},
inSubpage: {
type: Boolean,
value: false,
},
expanded_: {
type: Boolean,
value: true,
},
certificates_: {
type: Array,
value: () => [],
},
hideEverything_: {
type: Boolean,
computed: 'computeHideEverything_(certificates_)',
},
hasCerts_: {
type: Boolean,
computed: 'computeHasCerts_(certificates_)',
},
};
}
declare certSource: CertificateSource;
declare headerText: string;
declare certMetadataEditable: boolean;
declare showImport: boolean;
declare showImportAndBind: boolean;
declare hideExport: boolean;
declare hideHeader: boolean;
declare inSubpage: boolean;
declare noCollapse: boolean;
declare hideIfEmpty: boolean;
declare private expanded_: boolean;
declare private hideEverything_: boolean;
declare private certificates_: SummaryCertInfo[];
declare private hasCerts_: boolean;
override ready() {
super.ready();
this.refreshCertificates();
if (!this.inSubpage) {
this.$.certs.classList.add('card');
}
if (this.inSubpage) {
this.$.listHeader.classList.add('subpage-padding');
}
const proxy = CertificatesBrowserProxy.getInstance();
proxy.callbackRouter.triggerReload.addListener(
this.onRefreshRequested_.bind(this));
}
private onRefreshRequested_(certSources: CertificateSource[]) {
if (certSources.includes(this.certSource)) {
this.refreshCertificates();
}
}
private refreshCertificates() {
CertificatesBrowserProxy.getInstance()
.handler.getCertificates(this.certSource)
.then((results: {certs: SummaryCertInfo[]}) => {
this.certificates_ = results.certs;
});
}
private onExportCertsClick_(e: Event) {
// Export button click shouldn't collapse the list as well.
e.stopPropagation();
CertificatesBrowserProxy.getInstance().handler.exportCertificates(
this.certSource);
}
private onImportCertClick_(e: Event) {
// Import button click shouldn't collapse the list as well.
e.stopPropagation();
CertificatesBrowserProxy.getInstance()
.handler.importCertificate(this.certSource)
.then(this.handleImportResult.bind(this));
}
private onImportAndBindCertClick_(e: Event) {
// Import button click shouldn't collapse the list as well.
e.stopPropagation();
CertificatesBrowserProxy.getInstance()
.handler.importAndBindCertificate(this.certSource)
.then(this.handleImportResult.bind(this));
}
private handleImportResult(value: {result: ActionResult|null}) {
if (value.result !== null && value.result.success !== undefined) {
// On successful import, refresh the certificate list.
this.refreshCertificates();
}
this.dispatchEvent(new CustomEvent(
'import-result',
{composed: true, bubbles: true, detail: value.result}));
}
private onDeleteResult_(e: CustomEvent<ActionResult|null>) {
const result = e.detail;
if (result !== null && result.success !== undefined) {
// On successful deletion, refresh the certificate list.
this.refreshCertificates();
this.$.importCert.focus();
}
}
private computeHasCerts_(): boolean {
return this.certificates_.length > 0;
}
private computeHideEverything_(): boolean {
return this.hideIfEmpty && this.certificates_.length === 0;
}
private hideCollapseButton_(): boolean {
return this.noCollapse || !this.hasCerts_;
}
private hideExportButton_(): boolean {
return this.hideExport || !this.hasCerts_;
}
}
declare global {
interface HTMLElementTagNameMap {
'certificate-list': CertificateListElement;
}
}
customElements.define(CertificateListElement.is, CertificateListElement);