1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-04-04 18:27:12 +02:00
bitwarden-browser/apps/browser/src/autofill/popup/settings/autofill.component.ts
Jonathan Prusik 53e69e6dc6
[PM-9673] Autofocus newly added input in the excluded domains view (#10641)
* autofocus newly added input in the excluded domains view

* update subscription
2024-08-26 10:03:32 -04:00

385 lines
14 KiB
TypeScript

import { CommonModule } from "@angular/common";
import { Component, OnInit } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { RouterModule } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
AutofillOverlayVisibility,
BrowserClientVendors,
BrowserShortcutsUris,
ClearClipboardDelay,
DisablePasswordManagerUris,
} from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import {
BrowserClientVendor,
BrowserShortcutsUri,
ClearClipboardDelaySetting,
DisablePasswordManagerUri,
InlineMenuVisibilitySetting,
} from "@bitwarden/common/autofill/types";
import {
UriMatchStrategy,
UriMatchStrategySetting,
} from "@bitwarden/common/models/domain/domain-service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import {
CardComponent,
CheckboxModule,
DialogService,
FormFieldModule,
IconButtonModule,
ItemModule,
LinkModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
TypographyModule,
} from "@bitwarden/components";
import { BrowserApi } from "../../../platform/browser/browser-api";
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
@Component({
templateUrl: "autofill.component.html",
standalone: true,
imports: [
CardComponent,
CheckboxModule,
CommonModule,
FormFieldModule,
FormsModule,
IconButtonModule,
ItemModule,
JslibModule,
LinkModule,
PopOutComponent,
PopupFooterComponent,
PopupHeaderComponent,
PopupPageComponent,
RouterModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
TypographyModule,
],
})
export class AutofillComponent implements OnInit {
/*
* Default values set here are used in component state operations
* until corresponding stored settings have loaded on init.
*/
protected canOverrideBrowserAutofillSetting: boolean = false;
protected defaultBrowserAutofillDisabled: boolean = false;
protected inlineMenuVisibility: InlineMenuVisibilitySetting =
AutofillOverlayVisibility.OnFieldFocus;
protected browserClientVendor: BrowserClientVendor = BrowserClientVendors.Unknown;
protected disablePasswordManagerURI: DisablePasswordManagerUri =
DisablePasswordManagerUris.Unknown;
protected browserShortcutsURI: BrowserShortcutsUri = BrowserShortcutsUris.Unknown;
protected browserClientIsUnknown: boolean;
protected autofillOnPageLoadFromPolicy$ =
this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$;
enableAutofillOnPageLoad: boolean = false;
enableInlineMenu: boolean = false;
enableInlineMenuOnIconSelect: boolean = false;
autofillOnPageLoadDefault: boolean = false;
autofillOnPageLoadOptions: { name: string; value: boolean }[];
enableContextMenuItem: boolean = false;
enableAutoTotpCopy: boolean = false;
clearClipboard: ClearClipboardDelaySetting;
clearClipboardOptions: { name: string; value: ClearClipboardDelaySetting }[];
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain;
uriMatchOptions: { name: string; value: UriMatchStrategySetting }[];
showCardsCurrentTab: boolean = true;
showIdentitiesCurrentTab: boolean = true;
autofillKeyboardHelperText: string;
accountSwitcherEnabled: boolean = false;
constructor(
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private domainSettingsService: DomainSettingsService,
private dialogService: DialogService,
private autofillSettingsService: AutofillSettingsServiceAbstraction,
private messagingService: MessagingService,
private vaultSettingsService: VaultSettingsService,
) {
this.autofillOnPageLoadOptions = [
{ name: i18nService.t("autoFillOnPageLoadYes"), value: true },
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false },
];
this.clearClipboardOptions = [
{ name: i18nService.t("never"), value: ClearClipboardDelay.Never },
{ name: i18nService.t("tenSeconds"), value: ClearClipboardDelay.TenSeconds },
{ name: i18nService.t("twentySeconds"), value: ClearClipboardDelay.TwentySeconds },
{ name: i18nService.t("thirtySeconds"), value: ClearClipboardDelay.ThirtySeconds },
{ name: i18nService.t("oneMinute"), value: ClearClipboardDelay.OneMinute },
{ name: i18nService.t("twoMinutes"), value: ClearClipboardDelay.TwoMinutes },
{ name: i18nService.t("fiveMinutes"), value: ClearClipboardDelay.FiveMinutes },
];
this.uriMatchOptions = [
{ name: i18nService.t("baseDomainOptionRecommended"), value: UriMatchStrategy.Domain },
{ name: i18nService.t("host"), value: UriMatchStrategy.Host },
{ name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith },
{ name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression },
{ name: i18nService.t("exact"), value: UriMatchStrategy.Exact },
{ name: i18nService.t("never"), value: UriMatchStrategy.Never },
];
this.browserClientVendor = this.getBrowserClientVendor();
this.disablePasswordManagerURI = DisablePasswordManagerUris[this.browserClientVendor];
this.browserShortcutsURI = BrowserShortcutsUris[this.browserClientVendor];
this.browserClientIsUnknown = this.browserClientVendor === BrowserClientVendors.Unknown;
}
async ngOnInit() {
this.canOverrideBrowserAutofillSetting = !this.browserClientIsUnknown;
this.defaultBrowserAutofillDisabled = await this.browserAutofillSettingCurrentlyOverridden();
this.inlineMenuVisibility = await firstValueFrom(
this.autofillSettingsService.inlineMenuVisibility$,
);
this.enableInlineMenuOnIconSelect =
this.inlineMenuVisibility === AutofillOverlayVisibility.OnButtonClick;
this.enableInlineMenu =
this.inlineMenuVisibility === AutofillOverlayVisibility.OnFieldFocus ||
this.enableInlineMenuOnIconSelect;
this.enableAutofillOnPageLoad = await firstValueFrom(
this.autofillSettingsService.autofillOnPageLoad$,
);
this.autofillOnPageLoadDefault = await firstValueFrom(
this.autofillSettingsService.autofillOnPageLoadDefault$,
);
this.enableContextMenuItem = await firstValueFrom(
this.autofillSettingsService.enableContextMenu$,
);
this.enableAutoTotpCopy = await firstValueFrom(this.autofillSettingsService.autoCopyTotp$);
this.clearClipboard = await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$);
const defaultUriMatch = await firstValueFrom(
this.domainSettingsService.defaultUriMatchStrategy$,
);
this.defaultUriMatch = defaultUriMatch == null ? UriMatchStrategy.Domain : defaultUriMatch;
const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
await this.setAutofillKeyboardHelperText(command);
this.showCardsCurrentTab = await firstValueFrom(this.vaultSettingsService.showCardsCurrentTab$);
this.showIdentitiesCurrentTab = await firstValueFrom(
this.vaultSettingsService.showIdentitiesCurrentTab$,
);
}
async updateInlineMenuVisibility() {
if (!this.enableInlineMenu) {
this.enableInlineMenuOnIconSelect = false;
}
const newInlineMenuVisibilityValue = this.enableInlineMenuOnIconSelect
? AutofillOverlayVisibility.OnButtonClick
: this.enableInlineMenu
? AutofillOverlayVisibility.OnFieldFocus
: AutofillOverlayVisibility.Off;
await this.autofillSettingsService.setInlineMenuVisibility(newInlineMenuVisibilityValue);
await this.requestPrivacyPermission();
}
async updateAutofillOnPageLoad() {
await this.autofillSettingsService.setAutofillOnPageLoad(this.enableAutofillOnPageLoad);
}
async updateAutofillOnPageLoadDefault() {
await this.autofillSettingsService.setAutofillOnPageLoadDefault(this.autofillOnPageLoadDefault);
}
async saveDefaultUriMatch() {
await this.domainSettingsService.setDefaultUriMatchStrategy(this.defaultUriMatch);
}
private async setAutofillKeyboardHelperText(command: string) {
if (command) {
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutText", command);
} else {
this.autofillKeyboardHelperText = this.i18nService.t("autofillLoginShortcutNotSet");
}
}
private getBrowserClientVendor(): BrowserClientVendor {
if (this.platformUtilsService.isChrome()) {
return BrowserClientVendors.Chrome;
}
if (this.platformUtilsService.isOpera()) {
return BrowserClientVendors.Opera;
}
if (this.platformUtilsService.isEdge()) {
return BrowserClientVendors.Edge;
}
if (this.platformUtilsService.isVivaldi()) {
return BrowserClientVendors.Vivaldi;
}
return BrowserClientVendors.Unknown;
}
protected async openURI(event: Event, uri: BrowserShortcutsUri | DisablePasswordManagerUri) {
event.preventDefault();
// If the destination is a password management settings page, ask the user to confirm before proceeding
if (uri === DisablePasswordManagerUris[this.browserClientVendor]) {
await this.dialogService.openSimpleDialog({
...(this.browserClientIsUnknown
? {
content: { key: "confirmContinueToHelpCenterPasswordManagementContent" },
title: { key: "confirmContinueToHelpCenter" },
}
: {
content: { key: "confirmContinueToBrowserPasswordManagementSettingsContent" },
title: { key: "confirmContinueToBrowserSettingsTitle" },
}),
acceptButtonText: { key: "continue" },
acceptAction: async () => {
await BrowserApi.createNewTab(uri);
},
cancelButtonText: { key: "cancel" },
type: "info",
});
return;
}
// If the destination is a browser shortcut settings page, ask the user to confirm before proceeding
if (uri === BrowserShortcutsUris[this.browserClientVendor]) {
await this.dialogService.openSimpleDialog({
...(this.browserClientIsUnknown
? {
content: { key: "confirmContinueToHelpCenterKeyboardShortcutsContent" },
title: { key: "confirmContinueToHelpCenter" },
}
: {
content: { key: "confirmContinueToBrowserKeyboardShortcutSettingsContent" },
title: { key: "confirmContinueToBrowserSettingsTitle" },
}),
acceptButtonText: { key: "continue" },
acceptAction: async () => {
await BrowserApi.createNewTab(uri);
},
cancelButtonText: { key: "cancel" },
type: "info",
});
return;
}
await BrowserApi.createNewTab(uri);
}
async requestPrivacyPermission() {
if (
this.inlineMenuVisibility === AutofillOverlayVisibility.Off ||
!this.canOverrideBrowserAutofillSetting ||
(await this.browserAutofillSettingCurrentlyOverridden())
) {
return;
}
await this.dialogService.openSimpleDialog({
title: { key: "overrideDefaultBrowserAutofillTitle" },
content: { key: "overrideDefaultBrowserAutofillDescription" },
acceptButtonText: { key: "continue" },
acceptAction: async () => await this.handleOverrideDialogAccept(),
cancelButtonText: { key: "cancel" },
type: "info",
});
}
async updateDefaultBrowserAutofillDisabled() {
const privacyPermissionGranted = await this.privacyPermissionGranted();
if (!this.defaultBrowserAutofillDisabled && !privacyPermissionGranted) {
return;
}
if (
!privacyPermissionGranted &&
!(await BrowserApi.requestPermission({ permissions: ["privacy"] }))
) {
await this.dialogService.openSimpleDialog({
title: { key: "privacyPermissionAdditionNotGrantedTitle" },
content: { key: "privacyPermissionAdditionNotGrantedDescription" },
acceptButtonText: { key: "ok" },
cancelButtonText: null,
type: "warning",
});
this.defaultBrowserAutofillDisabled = false;
return;
}
BrowserApi.updateDefaultBrowserAutofillSettings(!this.defaultBrowserAutofillDisabled);
}
private handleOverrideDialogAccept = async () => {
this.defaultBrowserAutofillDisabled = true;
await this.updateDefaultBrowserAutofillDisabled();
};
async browserAutofillSettingCurrentlyOverridden() {
if (!this.canOverrideBrowserAutofillSetting) {
return false;
}
if (!(await this.privacyPermissionGranted())) {
return false;
}
return await BrowserApi.browserAutofillSettingsOverridden();
}
async privacyPermissionGranted(): Promise<boolean> {
return await BrowserApi.permissionsGranted(["privacy"]);
}
async updateContextMenuItem() {
await this.autofillSettingsService.setEnableContextMenu(this.enableContextMenuItem);
this.messagingService.send("bgUpdateContextMenu");
}
async updateAutoTotpCopy() {
await this.autofillSettingsService.setAutoCopyTotp(this.enableAutoTotpCopy);
}
async saveClearClipboard() {
await this.autofillSettingsService.setClearClipboardDelay(this.clearClipboard);
}
async updateShowCardsCurrentTab() {
await this.vaultSettingsService.setShowCardsCurrentTab(this.showCardsCurrentTab);
}
async updateShowIdentitiesCurrentTab() {
await this.vaultSettingsService.setShowIdentitiesCurrentTab(this.showIdentitiesCurrentTab);
}
}