mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 00:03:56 +00:00
[PM-5560] Implement Autofill Settings state provider (#7767)
* Begin migration of autofill settings Co-authored-by: Cesar Gonzalez <cagonzalezcs@users.noreply.github.com> Co-authored-by: Thomas Avery <Thomas-Avery@users.noreply.github.com> Co-authored-by: Jonathan Prusik <jprusik@users.noreply.github.com> Co-authored-by: Colton Hurst <coltonhurst@users.noreply.github.com> * add browser dependency for AutofillSettingsService Co-authored-by: Matt Gibson <mgibson@bitwarden.com> * update autofill settings service * replace usages of stateService get/set autofillOnPageLoad with autofillSettingsService * replace usages of stateService get/set autofillOnPageLoadDefault with autofillSettingsService * replace usages of stateService get/set autoCopyTotp with autofillSettingsService * replace usages of stateService get/set autoFillOnPageLoadCalloutIsDismissed with autofillSettingsService * replace usages of stateService get/set activateAutoFillOnPageLoadFromPolicy with autofillSettingsService * replace usages of get/set autoFillOverlayVisibility with autofillSettingsService * inlineMenuVisibility should use global state * add the AutofillSettingsService to background scripts * fix typing * replace additional usages of get/set autoFillOverlayVisibility and disableAutoTotpCopy with autofillSettingsService equivalents * replace additional usages of get/set autofillOnPageLoadDefault with autofillSettingsService equivalent * replace additional usages of get/set activateAutoFillOnPageLoadFromPolicy with autofillSettingsService equivalent * remove additional deprecated and unused state service calls * improve naming conventions and consistency * fix missing mock for policy service test * replace missing overlay background tests * cleanup * fix double inversion * fix reference to wrong setter * move handleActivateAutofillPolicy out of BrowserPolicyService * create state migration script * resolve linting issues * remove migrated setting properties * add AutofillSettingsSErvice to jslib-services * handle conditional content script loading via autofillOnPageLoad check * add deprecated note to getFromLocalStorage * add jsdoc decorators to new autofill service methods * handle undefined globalState * move autofill settings out of BrowserPolicyService * Move autofill settings code out of policyService * fix tests * fix typo in state definition --------- Co-authored-by: Matt Gibson <mgibson@bitwarden.com> Co-authored-by: Cesar Gonzalez <cagonzalezcs@users.noreply.github.com> Co-authored-by: Thomas Avery <Thomas-Avery@users.noreply.github.com> Co-authored-by: Colton Hurst <coltonhurst@users.noreply.github.com> Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
This commit is contained in:
174
libs/common/src/autofill/services/autofill-settings.service.ts
Normal file
174
libs/common/src/autofill/services/autofill-settings.service.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { filter, switchMap, tap, firstValueFrom, map, Observable } from "rxjs";
|
||||
|
||||
import {
|
||||
AutofillOverlayVisibility,
|
||||
InlineMenuVisibilitySetting,
|
||||
} from "../../../../../apps/browser/src/autofill/utils/autofill-overlay.enum";
|
||||
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "../../admin-console/enums/index";
|
||||
import { Policy } from "../../admin-console/models/domain/policy";
|
||||
import {
|
||||
AUTOFILL_SETTINGS_DISK,
|
||||
AUTOFILL_SETTINGS_DISK_LOCAL,
|
||||
ActiveUserState,
|
||||
GlobalState,
|
||||
KeyDefinition,
|
||||
StateProvider,
|
||||
} from "../../platform/state";
|
||||
|
||||
const AUTOFILL_ON_PAGE_LOAD = new KeyDefinition(AUTOFILL_SETTINGS_DISK, "autofillOnPageLoad", {
|
||||
deserializer: (value: boolean) => value ?? false,
|
||||
});
|
||||
|
||||
const AUTOFILL_ON_PAGE_LOAD_DEFAULT = new KeyDefinition(
|
||||
AUTOFILL_SETTINGS_DISK,
|
||||
"autofillOnPageLoadDefault",
|
||||
{
|
||||
deserializer: (value: boolean) => value ?? false,
|
||||
},
|
||||
);
|
||||
|
||||
const AUTO_COPY_TOTP = new KeyDefinition(AUTOFILL_SETTINGS_DISK, "autoCopyTotp", {
|
||||
deserializer: (value: boolean) => value ?? false,
|
||||
});
|
||||
|
||||
const AUTOFILL_ON_PAGE_LOAD_CALLOUT_DISMISSED = new KeyDefinition(
|
||||
AUTOFILL_SETTINGS_DISK,
|
||||
"autofillOnPageLoadCalloutIsDismissed",
|
||||
{
|
||||
deserializer: (value: boolean) => value ?? false,
|
||||
},
|
||||
);
|
||||
|
||||
const ACTIVATE_AUTOFILL_ON_PAGE_LOAD_FROM_POLICY = new KeyDefinition(
|
||||
AUTOFILL_SETTINGS_DISK_LOCAL,
|
||||
"activateAutofillOnPageLoadFromPolicy",
|
||||
{
|
||||
deserializer: (value: boolean) => value ?? false,
|
||||
},
|
||||
);
|
||||
|
||||
const INLINE_MENU_VISIBILITY = new KeyDefinition(
|
||||
AUTOFILL_SETTINGS_DISK_LOCAL,
|
||||
"inlineMenuVisibility",
|
||||
{
|
||||
deserializer: (value: InlineMenuVisibilitySetting) => value ?? AutofillOverlayVisibility.Off,
|
||||
},
|
||||
);
|
||||
|
||||
export abstract class AutofillSettingsServiceAbstraction {
|
||||
autofillOnPageLoad$: Observable<boolean>;
|
||||
setAutofillOnPageLoad: (newValue: boolean) => Promise<void>;
|
||||
autofillOnPageLoadDefault$: Observable<boolean>;
|
||||
setAutofillOnPageLoadDefault: (newValue: boolean) => Promise<void>;
|
||||
autoCopyTotp$: Observable<boolean>;
|
||||
setAutoCopyTotp: (newValue: boolean) => Promise<void>;
|
||||
autofillOnPageLoadCalloutIsDismissed$: Observable<boolean>;
|
||||
setAutofillOnPageLoadCalloutIsDismissed: (newValue: boolean) => Promise<void>;
|
||||
activateAutofillOnPageLoadFromPolicy$: Observable<boolean>;
|
||||
setActivateAutofillOnPageLoadFromPolicy: (newValue: boolean) => Promise<void>;
|
||||
inlineMenuVisibility$: Observable<InlineMenuVisibilitySetting>;
|
||||
setInlineMenuVisibility: (newValue: InlineMenuVisibilitySetting) => Promise<void>;
|
||||
handleActivateAutofillPolicy: (policies: Observable<Policy[]>) => Observable<boolean[]>;
|
||||
}
|
||||
|
||||
export class AutofillSettingsService implements AutofillSettingsServiceAbstraction {
|
||||
private autofillOnPageLoadState: ActiveUserState<boolean>;
|
||||
readonly autofillOnPageLoad$: Observable<boolean>;
|
||||
|
||||
private autofillOnPageLoadDefaultState: ActiveUserState<boolean>;
|
||||
readonly autofillOnPageLoadDefault$: Observable<boolean>;
|
||||
|
||||
private autoCopyTotpState: ActiveUserState<boolean>;
|
||||
readonly autoCopyTotp$: Observable<boolean>;
|
||||
|
||||
private autofillOnPageLoadCalloutIsDismissedState: ActiveUserState<boolean>;
|
||||
readonly autofillOnPageLoadCalloutIsDismissed$: Observable<boolean>;
|
||||
|
||||
private activateAutofillOnPageLoadFromPolicyState: ActiveUserState<boolean>;
|
||||
readonly activateAutofillOnPageLoadFromPolicy$: Observable<boolean>;
|
||||
|
||||
private inlineMenuVisibilityState: GlobalState<InlineMenuVisibilitySetting>;
|
||||
readonly inlineMenuVisibility$: Observable<InlineMenuVisibilitySetting>;
|
||||
|
||||
constructor(
|
||||
private stateProvider: StateProvider,
|
||||
policyService: PolicyService,
|
||||
) {
|
||||
this.autofillOnPageLoadState = this.stateProvider.getActive(AUTOFILL_ON_PAGE_LOAD);
|
||||
this.autofillOnPageLoad$ = this.autofillOnPageLoadState.state$.pipe(map((x) => x ?? false));
|
||||
|
||||
this.autofillOnPageLoadDefaultState = this.stateProvider.getActive(
|
||||
AUTOFILL_ON_PAGE_LOAD_DEFAULT,
|
||||
);
|
||||
this.autofillOnPageLoadDefault$ = this.autofillOnPageLoadDefaultState.state$.pipe(
|
||||
map((x) => x ?? true),
|
||||
);
|
||||
|
||||
this.autoCopyTotpState = this.stateProvider.getActive(AUTO_COPY_TOTP);
|
||||
this.autoCopyTotp$ = this.autoCopyTotpState.state$.pipe(map((x) => x ?? false));
|
||||
|
||||
this.autofillOnPageLoadCalloutIsDismissedState = this.stateProvider.getActive(
|
||||
AUTOFILL_ON_PAGE_LOAD_CALLOUT_DISMISSED,
|
||||
);
|
||||
this.autofillOnPageLoadCalloutIsDismissed$ =
|
||||
this.autofillOnPageLoadCalloutIsDismissedState.state$.pipe(map((x) => x ?? false));
|
||||
|
||||
this.activateAutofillOnPageLoadFromPolicyState = this.stateProvider.getActive(
|
||||
ACTIVATE_AUTOFILL_ON_PAGE_LOAD_FROM_POLICY,
|
||||
);
|
||||
this.activateAutofillOnPageLoadFromPolicy$ =
|
||||
this.activateAutofillOnPageLoadFromPolicyState.state$.pipe(map((x) => x ?? false));
|
||||
|
||||
this.inlineMenuVisibilityState = this.stateProvider.getGlobal(INLINE_MENU_VISIBILITY);
|
||||
this.inlineMenuVisibility$ = this.inlineMenuVisibilityState.state$.pipe(
|
||||
map((x) => x ?? AutofillOverlayVisibility.Off),
|
||||
);
|
||||
|
||||
policyService.policies$.pipe(this.handleActivateAutofillPolicy.bind(this)).subscribe();
|
||||
}
|
||||
|
||||
async setAutofillOnPageLoad(newValue: boolean): Promise<void> {
|
||||
await this.autofillOnPageLoadState.update(() => newValue);
|
||||
}
|
||||
|
||||
async setAutofillOnPageLoadDefault(newValue: boolean): Promise<void> {
|
||||
await this.autofillOnPageLoadDefaultState.update(() => newValue);
|
||||
}
|
||||
|
||||
async setAutoCopyTotp(newValue: boolean): Promise<void> {
|
||||
await this.autoCopyTotpState.update(() => newValue);
|
||||
}
|
||||
|
||||
async setAutofillOnPageLoadCalloutIsDismissed(newValue: boolean): Promise<void> {
|
||||
await this.autofillOnPageLoadCalloutIsDismissedState.update(() => newValue);
|
||||
}
|
||||
|
||||
async setActivateAutofillOnPageLoadFromPolicy(newValue: boolean): Promise<void> {
|
||||
await this.activateAutofillOnPageLoadFromPolicyState.update(() => newValue);
|
||||
}
|
||||
|
||||
async setInlineMenuVisibility(newValue: InlineMenuVisibilitySetting): Promise<void> {
|
||||
await this.inlineMenuVisibilityState.update(() => newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the ActivateAutofill policy is enabled, save a flag indicating if we need to
|
||||
* enable Autofill on page load.
|
||||
*/
|
||||
handleActivateAutofillPolicy(policies$: Observable<Policy[]>): Observable<boolean[]> {
|
||||
return policies$.pipe(
|
||||
map((policies) => policies.find((p) => p.type == PolicyType.ActivateAutofill && p.enabled)),
|
||||
filter((p) => p != null),
|
||||
switchMap(async (_) => [
|
||||
await firstValueFrom(this.activateAutofillOnPageLoadFromPolicy$),
|
||||
await firstValueFrom(this.autofillOnPageLoad$),
|
||||
]),
|
||||
tap(([activated, autofillEnabled]) => {
|
||||
if (activated === undefined) {
|
||||
void this.setActivateAutofillOnPageLoadFromPolicy(!autofillEnabled);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user