[PM-8833] Setting up password generator to refresh only when a form is blurred entirely

This commit is contained in:
Cesar Gonzalez 2024-09-20 03:58:14 -05:00
parent 43ed792823
commit 55da615caa
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
1 changed files with 44 additions and 31 deletions

View File

@ -1,5 +1,4 @@
import { import {
BehaviorSubject,
debounceTime, debounceTime,
firstValueFrom, firstValueFrom,
map, map,
@ -89,9 +88,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private readonly openUnlockPopout = openUnlockPopout; private readonly openUnlockPopout = openUnlockPopout;
private readonly openViewVaultItemPopout = openViewVaultItemPopout; private readonly openViewVaultItemPopout = openViewVaultItemPopout;
private readonly openAddEditVaultItemPopout = openAddEditVaultItemPopout; private readonly openAddEditVaultItemPopout = openAddEditVaultItemPopout;
private readonly updateOverlayCiphersSubject = new BehaviorSubject<UpdateOverlayCiphersParams>( private readonly updateOverlayCiphersSubject = new Subject<UpdateOverlayCiphersParams>();
null,
);
private readonly storeInlineMenuFido2CredentialsSubject = new ReplaySubject<number>(1); private readonly storeInlineMenuFido2CredentialsSubject = new ReplaySubject<number>(1);
private readonly startInlineMenuDelayedCloseSubject: Subject<void> = new Subject<void>(); private readonly startInlineMenuDelayedCloseSubject: Subject<void> = new Subject<void>();
private readonly cancelInlineMenuDelayedCloseSubject: Subject<boolean> = new Subject<boolean>(); private readonly cancelInlineMenuDelayedCloseSubject: Subject<boolean> = new Subject<boolean>();
@ -123,7 +120,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private isInlineMenuListVisible: boolean = false; private isInlineMenuListVisible: boolean = false;
private showPasskeysLabelsWithinInlineMenu: boolean = false; private showPasskeysLabelsWithinInlineMenu: boolean = false;
private iconsServerUrl: string; private iconsServerUrl: string;
private mostRecentlyGeneratedPassword: string; private generatedPassword: string;
private readonly extensionMessageHandlers: OverlayBackgroundExtensionMessageHandlers = { private readonly extensionMessageHandlers: OverlayBackgroundExtensionMessageHandlers = {
autofillOverlayElementClosed: ({ message, sender }) => autofillOverlayElementClosed: ({ message, sender }) =>
this.overlayElementClosed(message, sender), this.overlayElementClosed(message, sender),
@ -181,7 +178,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
redirectAutofillInlineMenuFocusOut: ({ message, port }) => redirectAutofillInlineMenuFocusOut: ({ message, port }) =>
this.redirectInlineMenuFocusOut(message, port), this.redirectInlineMenuFocusOut(message, port),
updateAutofillInlineMenuListHeight: ({ message }) => this.updateInlineMenuListHeight(message), updateAutofillInlineMenuListHeight: ({ message }) => this.updateInlineMenuListHeight(message),
refreshGeneratedPassword: () => this.refreshGeneratedPassword(), refreshGeneratedPassword: () => this.updateGeneratedPassword(true),
fillGeneratedPassword: ({ port }) => this.fillGeneratedPassword(port), fillGeneratedPassword: ({ port }) => this.fillGeneratedPassword(port),
}; };
@ -204,13 +201,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.initOverlayEventObservables(); this.initOverlayEventObservables();
} }
private async generatePassword(): Promise<string> { private async generatePassword(): Promise<void> {
const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {}; const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {};
const password = await this.passwordGenerationService.generatePassword(options); const password = await this.passwordGenerationService.generatePassword(options);
await this.passwordGenerationService.addHistory(password); await this.passwordGenerationService.addHistory(password);
this.mostRecentlyGeneratedPassword = password; this.generatedPassword = password;
return password;
} }
/** /**
@ -300,7 +295,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
delete this.portKeyForTab[tabId]; delete this.portKeyForTab[tabId];
} }
this.mostRecentlyGeneratedPassword = null; this.generatedPassword = null;
this.focusedFieldData = null; this.focusedFieldData = null;
} }
@ -343,6 +338,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.inlineMenuFido2Credentials.clear(); this.inlineMenuFido2Credentials.clear();
this.storeInlineMenuFido2CredentialsSubject.next(currentTab.id); this.storeInlineMenuFido2CredentialsSubject.next(currentTab.id);
await this.generatePassword();
this.inlineMenuCiphers = new Map(); this.inlineMenuCiphers = new Map();
const ciphersViews = await this.getCipherViews(currentTab, updateAllCipherTypes); const ciphersViews = await this.getCipherViews(currentTab, updateAllCipherTypes);
for (let cipherIndex = 0; cipherIndex < ciphersViews.length; cipherIndex++) { for (let cipherIndex = 0; cipherIndex < ciphersViews.length; cipherIndex++) {
@ -1081,6 +1078,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
sender: chrome.runtime.MessageSender, sender: chrome.runtime.MessageSender,
{ forceCloseInlineMenu, overlayElement }: CloseInlineMenuMessage = {}, { forceCloseInlineMenu, overlayElement }: CloseInlineMenuMessage = {},
) { ) {
this.generatedPassword = null;
const command = "closeAutofillInlineMenu"; const command = "closeAutofillInlineMenu";
const sendOptions = { frameId: 0 }; const sendOptions = { frameId: 0 };
if (forceCloseInlineMenu) { if (forceCloseInlineMenu) {
@ -1375,8 +1374,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
previousFocusedFieldData?.inlineMenuFillType === InlineMenuFillType.AccountCreationUsername && previousFocusedFieldData?.inlineMenuFillType === InlineMenuFillType.AccountCreationUsername &&
this.focusedFieldData.inlineMenuFillType !== InlineMenuFillType.AccountCreationUsername; this.focusedFieldData.inlineMenuFillType !== InlineMenuFillType.AccountCreationUsername;
if (this.focusedFieldData.inlineMenuFillType === InlineMenuFillType.PasswordGeneration) { if (
this.refreshGeneratedPassword().catch((error) => this.logService.error(error)); this.isInlineMenuButtonVisible &&
this.focusedFieldData.inlineMenuFillType === InlineMenuFillType.PasswordGeneration
) {
this.updateGeneratedPassword().catch((error) => this.logService.error(error));
return; return;
} }
@ -1413,7 +1415,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.focusedFieldData.inlineMenuFillType === CipherType.Login && this.focusedFieldData.inlineMenuFillType === CipherType.Login &&
this.focusedFieldData.accountCreationFieldType === "password" this.focusedFieldData.accountCreationFieldType === "password"
) { ) {
await this.refreshGeneratedPassword(); await this.updateGeneratedPassword();
return; return;
} }
@ -1425,16 +1427,20 @@ export class OverlayBackground implements OverlayBackgroundInterface {
}); });
} }
private async refreshGeneratedPassword() { private async updateGeneratedPassword(refreshPassword: boolean = false) {
if (!this.generatedPassword || refreshPassword) {
await this.generatePassword();
}
this.inlineMenuListPort?.postMessage({ this.inlineMenuListPort?.postMessage({
command: "updateAutofillInlineMenuGeneratedPassword", command: "updateAutofillInlineMenuGeneratedPassword",
generatedPassword: await this.generatePassword(), generatedPassword: this.generatedPassword,
}); });
} }
// TODO: Most of the logic here likely should exist within the autofill service instead of here. // TODO: Most of the logic here likely should exist within the autofill service instead of here.
private async fillGeneratedPassword(port: chrome.runtime.Port) { private async fillGeneratedPassword(port: chrome.runtime.Port) {
if (!this.mostRecentlyGeneratedPassword) { if (!this.generatedPassword) {
return; return;
} }
@ -1472,7 +1478,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const cipher = this.buildLoginCipherView({ const cipher = this.buildLoginCipherView({
username: "", username: "",
password: this.mostRecentlyGeneratedPassword, password: this.generatedPassword,
hostname: "", hostname: "",
uri: "", uri: "",
}); });
@ -2401,18 +2407,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
this.portKeyForTab[port.sender.tab.id] = generateRandomChars(12); this.portKeyForTab[port.sender.tab.id] = generateRandomChars(12);
} }
// TODO: Pull this into a separate method
const authStatus = await this.getAuthStatus();
const showInlineMenuAccountCreation = this.showInlineMenuAccountCreation(); const showInlineMenuAccountCreation = this.showInlineMenuAccountCreation();
let generatedPassword = null; const showInlineMenuPasswordGenerator = this.showInlineMenuPasswordGenerator(
if ( isInlineMenuListPort,
authStatus === AuthenticationStatus.Unlocked && showInlineMenuAccountCreation,
isInlineMenuListPort && );
(this.focusedFieldData?.inlineMenuFillType === InlineMenuFillType.PasswordGeneration || if (showInlineMenuPasswordGenerator && !this.generatedPassword) {
(showInlineMenuAccountCreation && await this.generatePassword();
this.focusedFieldData?.accountCreationFieldType === "password"))
) {
generatedPassword = await this.generatePassword();
} }
this.storeOverlayPort(port); this.storeOverlayPort(port);
@ -2426,7 +2427,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
pageTitle: chrome.i18n.getMessage( pageTitle: chrome.i18n.getMessage(
isInlineMenuListPort ? "bitwardenVault" : "bitwardenOverlayButton", isInlineMenuListPort ? "bitwardenVault" : "bitwardenOverlayButton",
), ),
authStatus, authStatus: await this.getAuthStatus(),
styleSheetUrl: chrome.runtime.getURL( styleSheetUrl: chrome.runtime.getURL(
`overlay/menu-${isInlineMenuListPort ? "list" : "button"}.css`, `overlay/menu-${isInlineMenuListPort ? "list" : "button"}.css`,
), ),
@ -2440,7 +2441,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
inlineMenuFillType: this.focusedFieldData?.inlineMenuFillType, inlineMenuFillType: this.focusedFieldData?.inlineMenuFillType,
showInlineMenuAccountCreation, showInlineMenuAccountCreation,
showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu, showPasskeysLabels: this.showPasskeysLabelsWithinInlineMenu,
generatedPassword, generatedPassword: showInlineMenuPasswordGenerator ? this.generatedPassword : null,
}); });
this.updateInlineMenuPosition( this.updateInlineMenuPosition(
{ {
@ -2483,6 +2484,18 @@ export class OverlayBackground implements OverlayBackgroundInterface {
} }
} }
private showInlineMenuPasswordGenerator(
isInlineMenuListPort: boolean,
showInlineMenuAccountCreation: boolean,
) {
return (
isInlineMenuListPort &&
(this.focusedFieldData?.inlineMenuFillType === InlineMenuFillType.PasswordGeneration ||
(showInlineMenuAccountCreation &&
this.focusedFieldData?.accountCreationFieldType === "password"))
);
}
/** /**
* Handles messages sent to the overlay list or button ports. * Handles messages sent to the overlay list or button ports.
* *