mirror of
https://github.com/bitwarden/browser.git
synced 2025-04-04 18:27:12 +02:00
385 lines
14 KiB
TypeScript
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);
|
|
}
|
|
}
|