1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 17:23:37 +00:00

[PM-5189] Fixing issues with the inlne menu re-populating the list when switching tabs

This commit is contained in:
Cesar Gonzalez
2024-04-07 18:56:18 -05:00
parent 6a1a684435
commit 991ce3b653
3 changed files with 182 additions and 188 deletions

View File

@@ -1,8 +1,5 @@
import { mock } from "jest-mock-extended"; import { mock } from "jest-mock-extended";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import AutofillPageDetails from "../models/autofill-page-details"; import AutofillPageDetails from "../models/autofill-page-details";
import AutofillScript from "../models/autofill-script"; import AutofillScript from "../models/autofill-script";
import AutofillOverlayContentService from "../services/autofill-overlay-content.service"; import AutofillOverlayContentService from "../services/autofill-overlay-content.service";
@@ -254,9 +251,9 @@ describe("AutofillInit", () => {
it("updates the isCurrentlyFilling property of the overlay to true after filling", async () => { it("updates the isCurrentlyFilling property of the overlay to true after filling", async () => {
jest.useFakeTimers(); jest.useFakeTimers();
jest.spyOn(autofillInit as any, "updateOverlayIsCurrentlyFilling"); jest.spyOn(autofillInit as any, "updateOverlayIsCurrentlyFilling");
jest // jest
.spyOn(autofillInit["autofillOverlayContentService"], "focusMostRecentOverlayField") // .spyOn(autofillInit["autofillOverlayContentService"], "focusMostRecentOverlayField")
.mockImplementation(); // .mockImplementation();
sendMockExtensionMessage({ sendMockExtensionMessage({
command: "fillForm", command: "fillForm",
@@ -304,37 +301,37 @@ describe("AutofillInit", () => {
}); });
}); });
describe("openAutofillOverlay", () => { // describe("openAutofillOverlay", () => {
const message = { // const message = {
command: "openAutofillOverlay", // command: "openAutofillOverlay",
data: { // data: {
isFocusingFieldElement: true, // isFocusingFieldElement: true,
isOpeningFullOverlay: true, // isOpeningFullOverlay: true,
authStatus: AuthenticationStatus.Unlocked, // authStatus: AuthenticationStatus.Unlocked,
}, // },
}; // };
//
it("skips attempting to open the autofill overlay if the autofillOverlayContentService is not present", () => { // it("skips attempting to open the autofill overlay if the autofillOverlayContentService is not present", () => {
const newAutofillInit = new AutofillInit(undefined); // const newAutofillInit = new AutofillInit(undefined);
newAutofillInit.init(); // newAutofillInit.init();
//
sendMockExtensionMessage(message); // sendMockExtensionMessage(message);
//
expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); // expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined);
}); // });
//
it("opens the autofill overlay", () => { // it("opens the autofill overlay", () => {
sendMockExtensionMessage(message); // sendMockExtensionMessage(message);
//
expect( // expect(
autofillInit["autofillOverlayContentService"].openAutofillOverlay, // autofillInit["autofillOverlayContentService"].openAutofillOverlay,
).toHaveBeenCalledWith({ // ).toHaveBeenCalledWith({
isFocusingFieldElement: message.data.isFocusingFieldElement, // isFocusingFieldElement: message.data.isFocusingFieldElement,
isOpeningFullOverlay: message.data.isOpeningFullOverlay, // isOpeningFullOverlay: message.data.isOpeningFullOverlay,
authStatus: message.data.authStatus, // authStatus: message.data.authStatus,
}); // });
}); // });
}); // });
// describe("closeAutofillOverlay", () => { // describe("closeAutofillOverlay", () => {
// beforeEach(() => { // beforeEach(() => {
@@ -404,131 +401,131 @@ describe("AutofillInit", () => {
// }); // });
// }); // });
describe("addNewVaultItemFromOverlay", () => { // describe("addNewVaultItemFromOverlay", () => {
it("will not add a new vault item if the autofillOverlayContentService is not present", () => { // it("will not add a new vault item if the autofillOverlayContentService is not present", () => {
const newAutofillInit = new AutofillInit(undefined); // const newAutofillInit = new AutofillInit(undefined);
newAutofillInit.init(); // newAutofillInit.init();
//
// sendMockExtensionMessage({ command: "addNewVaultItemFromOverlay" });
//
// expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined);
// });
//
// it("will add a new vault item", () => {
// sendMockExtensionMessage({ command: "addNewVaultItemFromOverlay" });
//
// expect(autofillInit["autofillOverlayContentService"].addNewVaultItem).toHaveBeenCalled();
// });
// });
sendMockExtensionMessage({ command: "addNewVaultItemFromOverlay" }); // describe("updateIsOverlayCiphersPopulated", () => {
// const message = {
// command: "updateIsOverlayCiphersPopulated",
// data: {
// isOverlayCiphersPopulated: true,
// },
// };
//
// it("skips updating whether the ciphers are populated if the autofillOverlayContentService does note exist", () => {
// const newAutofillInit = new AutofillInit(undefined);
// newAutofillInit.init();
//
// sendMockExtensionMessage(message);
//
// expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined);
// });
//
// it("updates whether the overlay ciphers are populated", () => {
// sendMockExtensionMessage(message);
//
// expect(autofillInit["autofillOverlayContentService"].isOverlayCiphersPopulated).toEqual(
// message.data.isOverlayCiphersPopulated,
// );
// });
// });
expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); // describe("bgUnlockPopoutOpened", () => {
}); // it("skips attempting to blur and remove the overlay if the autofillOverlayContentService is not present", () => {
// const newAutofillInit = new AutofillInit(undefined);
// newAutofillInit.init();
// jest.spyOn(newAutofillInit as any, "removeAutofillOverlay");
//
// sendMockExtensionMessage({ command: "bgUnlockPopoutOpened" });
//
// expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined);
// // expect(newAutofillInit["removeAutofillOverlay"]).not.toHaveBeenCalled();
// });
//
// it("blurs the most recently focused feel and remove the autofill overlay", () => {
// jest.spyOn(autofillInit["autofillOverlayContentService"], "blurMostRecentOverlayField");
// jest.spyOn(autofillInit as any, "removeAutofillOverlay");
//
// sendMockExtensionMessage({ command: "bgUnlockPopoutOpened" });
//
// expect(
// autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField,
// ).toHaveBeenCalled();
// // expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled();
// });
// });
//
// describe("bgVaultItemRepromptPopoutOpened", () => {
// it("skips attempting to blur and remove the overlay if the autofillOverlayContentService is not present", () => {
// const newAutofillInit = new AutofillInit(undefined);
// newAutofillInit.init();
// jest.spyOn(newAutofillInit as any, "removeAutofillOverlay");
//
// sendMockExtensionMessage({ command: "bgVaultItemRepromptPopoutOpened" });
//
// expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined);
// // expect(newAutofillInit["removeAutofillOverlay"]).not.toHaveBeenCalled();
// });
//
// it("blurs the most recently focused feel and remove the autofill overlay", () => {
// jest.spyOn(autofillInit["autofillOverlayContentService"], "blurMostRecentOverlayField");
// jest.spyOn(autofillInit as any, "removeAutofillOverlay");
//
// sendMockExtensionMessage({ command: "bgVaultItemRepromptPopoutOpened" });
//
// expect(
// autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField,
// ).toHaveBeenCalled();
// // expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled();
// });
// });
it("will add a new vault item", () => { // describe("updateAutofillOverlayVisibility", () => {
sendMockExtensionMessage({ command: "addNewVaultItemFromOverlay" }); // beforeEach(() => {
// autofillInit["autofillOverlayContentService"].autofillOverlayVisibility =
expect(autofillInit["autofillOverlayContentService"].addNewVaultItem).toHaveBeenCalled(); // AutofillOverlayVisibility.OnButtonClick;
}); // });
}); //
// it("skips attempting to update the overlay visibility if the autofillOverlayVisibility data value is not present", () => {
describe("updateIsOverlayCiphersPopulated", () => { // sendMockExtensionMessage({
const message = { // command: "updateAutofillOverlayVisibility",
command: "updateIsOverlayCiphersPopulated", // data: {},
data: { // });
isOverlayCiphersPopulated: true, //
}, // expect(autofillInit["autofillOverlayContentService"].autofillOverlayVisibility).toEqual(
}; // AutofillOverlayVisibility.OnButtonClick,
// );
it("skips updating whether the ciphers are populated if the autofillOverlayContentService does note exist", () => { // });
const newAutofillInit = new AutofillInit(undefined); //
newAutofillInit.init(); // it("updates the overlay visibility value", () => {
// const message = {
sendMockExtensionMessage(message); // command: "updateAutofillOverlayVisibility",
// data: {
expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined); // autofillOverlayVisibility: AutofillOverlayVisibility.Off,
}); // },
// };
it("updates whether the overlay ciphers are populated", () => { //
sendMockExtensionMessage(message); // sendMockExtensionMessage(message);
//
expect(autofillInit["autofillOverlayContentService"].isOverlayCiphersPopulated).toEqual( // expect(autofillInit["autofillOverlayContentService"].autofillOverlayVisibility).toEqual(
message.data.isOverlayCiphersPopulated, // message.data.autofillOverlayVisibility,
); // );
}); // });
}); // });
describe("bgUnlockPopoutOpened", () => {
it("skips attempting to blur and remove the overlay if the autofillOverlayContentService is not present", () => {
const newAutofillInit = new AutofillInit(undefined);
newAutofillInit.init();
jest.spyOn(newAutofillInit as any, "removeAutofillOverlay");
sendMockExtensionMessage({ command: "bgUnlockPopoutOpened" });
expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined);
// expect(newAutofillInit["removeAutofillOverlay"]).not.toHaveBeenCalled();
});
it("blurs the most recently focused feel and remove the autofill overlay", () => {
jest.spyOn(autofillInit["autofillOverlayContentService"], "blurMostRecentOverlayField");
jest.spyOn(autofillInit as any, "removeAutofillOverlay");
sendMockExtensionMessage({ command: "bgUnlockPopoutOpened" });
expect(
autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField,
).toHaveBeenCalled();
// expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled();
});
});
describe("bgVaultItemRepromptPopoutOpened", () => {
it("skips attempting to blur and remove the overlay if the autofillOverlayContentService is not present", () => {
const newAutofillInit = new AutofillInit(undefined);
newAutofillInit.init();
jest.spyOn(newAutofillInit as any, "removeAutofillOverlay");
sendMockExtensionMessage({ command: "bgVaultItemRepromptPopoutOpened" });
expect(newAutofillInit["autofillOverlayContentService"]).toBe(undefined);
// expect(newAutofillInit["removeAutofillOverlay"]).not.toHaveBeenCalled();
});
it("blurs the most recently focused feel and remove the autofill overlay", () => {
jest.spyOn(autofillInit["autofillOverlayContentService"], "blurMostRecentOverlayField");
jest.spyOn(autofillInit as any, "removeAutofillOverlay");
sendMockExtensionMessage({ command: "bgVaultItemRepromptPopoutOpened" });
expect(
autofillInit["autofillOverlayContentService"].blurMostRecentOverlayField,
).toHaveBeenCalled();
// expect(autofillInit["removeAutofillOverlay"]).toHaveBeenCalled();
});
});
describe("updateAutofillOverlayVisibility", () => {
beforeEach(() => {
autofillInit["autofillOverlayContentService"].autofillOverlayVisibility =
AutofillOverlayVisibility.OnButtonClick;
});
it("skips attempting to update the overlay visibility if the autofillOverlayVisibility data value is not present", () => {
sendMockExtensionMessage({
command: "updateAutofillOverlayVisibility",
data: {},
});
expect(autofillInit["autofillOverlayContentService"].autofillOverlayVisibility).toEqual(
AutofillOverlayVisibility.OnButtonClick,
);
});
it("updates the overlay visibility value", () => {
const message = {
command: "updateAutofillOverlayVisibility",
data: {
autofillOverlayVisibility: AutofillOverlayVisibility.Off,
},
};
sendMockExtensionMessage(message);
expect(autofillInit["autofillOverlayContentService"].autofillOverlayVisibility).toEqual(
message.data.autofillOverlayVisibility,
);
});
});
}); });
}); });

View File

@@ -26,14 +26,12 @@ export type AutofillOverlayContentExtensionMessageHandlers = {
export interface AutofillOverlayContentService { export interface AutofillOverlayContentService {
pageDetailsUpdateRequired: boolean; pageDetailsUpdateRequired: boolean;
autofillOverlayVisibility: number; extensionMessageHandlers: AutofillOverlayContentExtensionMessageHandlers;
extensionMessageHandlers: any;
init(): void; init(): void;
setupAutofillOverlayListenerOnField( setupAutofillOverlayListenerOnField(
autofillFieldElement: ElementWithOpId<FormFieldElement>, autofillFieldElement: ElementWithOpId<FormFieldElement>,
autofillFieldData: AutofillField, autofillFieldData: AutofillField,
): Promise<void>; ): Promise<void>;
focusMostRecentOverlayField(): void;
blurMostRecentOverlayField(isRemovingOverlay?: boolean): void; blurMostRecentOverlayField(isRemovingOverlay?: boolean): void;
destroy(): void; destroy(): void;
} }

View File

@@ -159,7 +159,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* to the background script to add a new cipher. * to the background script to add a new cipher.
*/ */
async addNewVaultItem() { async addNewVaultItem() {
if ((await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true) { if (!(await this.isInlineMenuListVisible())) {
return; return;
} }
@@ -184,13 +184,12 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
if ( if (
!data?.direction || !data?.direction ||
!this.mostRecentlyFocusedField || !this.mostRecentlyFocusedField ||
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true !(await this.isInlineMenuListVisible())
) { ) {
return; return;
} }
const { direction } = data; const { direction } = data;
if (direction === RedirectFocusDirection.Current) { if (direction === RedirectFocusDirection.Current) {
this.focusMostRecentOverlayField(); this.focusMostRecentOverlayField();
setTimeout(() => void this.sendExtensionMessage("closeAutofillOverlay"), 100); setTimeout(() => void this.sendExtensionMessage("closeAutofillOverlay"), 100);
@@ -309,10 +308,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
return; return;
} }
if ( if (eventCode === "Enter" && !(await this.isFieldCurrentlyFilling())) {
eventCode === "Enter" &&
!(await this.sendExtensionMessage("checkIsFieldCurrentlyFilling")) === true
) {
void this.handleOverlayRepositionEvent(); void this.handleOverlayRepositionEvent();
return; return;
} }
@@ -331,10 +327,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* that the overlay list is focused when the user presses the down arrow key. * that the overlay list is focused when the user presses the down arrow key.
*/ */
private async focusOverlayList() { private async focusOverlayList() {
if ( if (this.mostRecentlyFocusedField && !(await this.isInlineMenuListVisible())) {
this.mostRecentlyFocusedField &&
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true
) {
await this.updateMostRecentlyFocusedField(this.mostRecentlyFocusedField); await this.updateMostRecentlyFocusedField(this.mostRecentlyFocusedField);
this.openAutofillOverlay({ isOpeningFullOverlay: true }); this.openAutofillOverlay({ isOpeningFullOverlay: true });
setTimeout(() => this.sendExtensionMessage("focusAutofillOverlayList"), 125); setTimeout(() => this.sendExtensionMessage("focusAutofillOverlayList"), 125);
@@ -372,7 +365,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
if ( if (
formFieldElement.value && formFieldElement.value &&
((await this.isOverlayCiphersPopulated()) || !this.isUserAuthed()) ((await this.isInlineMenuCiphersPopulated()) || !this.isUserAuthed())
) { ) {
void this.sendExtensionMessage("closeAutofillOverlay", { void this.sendExtensionMessage("closeAutofillOverlay", {
overlayElement: AutofillOverlayElement.List, overlayElement: AutofillOverlayElement.List,
@@ -424,10 +417,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* @param formFieldElement - The form field element that triggered the click event. * @param formFieldElement - The form field element that triggered the click event.
*/ */
private async triggerFormFieldClickedAction(formFieldElement: ElementWithOpId<FormFieldElement>) { private async triggerFormFieldClickedAction(formFieldElement: ElementWithOpId<FormFieldElement>) {
if ( if ((await this.isInlineMenuButtonVisible()) || (await this.isInlineMenuListVisible())) {
(await this.sendExtensionMessage("checkIsInlineMenuButtonVisible")) === true ||
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) === true
) {
return; return;
} }
@@ -454,11 +444,11 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
* @param formFieldElement - The form field element that triggered the focus event. * @param formFieldElement - The form field element that triggered the focus event.
*/ */
private async triggerFormFieldFocusedAction(formFieldElement: ElementWithOpId<FormFieldElement>) { private async triggerFormFieldFocusedAction(formFieldElement: ElementWithOpId<FormFieldElement>) {
if ((await this.sendExtensionMessage("checkIsFieldCurrentlyFilling")) === true) { if (await this.isFieldCurrentlyFilling()) {
return; return;
} }
void this.sendExtensionMessage("updateIsFieldCurrentlyFocused", { await this.sendExtensionMessage("updateIsFieldCurrentlyFocused", {
isFieldCurrentlyFocused: true, isFieldCurrentlyFocused: true,
}); });
this.clearUserInteractionEventTimeout(); this.clearUserInteractionEventTimeout();
@@ -470,7 +460,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
this.autofillOverlayVisibility === AutofillOverlayVisibility.OnButtonClick || this.autofillOverlayVisibility === AutofillOverlayVisibility.OnButtonClick ||
(formElementHasValue && initiallyFocusedField !== this.mostRecentlyFocusedField) (formElementHasValue && initiallyFocusedField !== this.mostRecentlyFocusedField)
) { ) {
void this.sendExtensionMessage("closeAutofillOverlay", { await this.sendExtensionMessage("closeAutofillOverlay", {
overlayElement: AutofillOverlayElement.List, overlayElement: AutofillOverlayElement.List,
forceCloseOverlay: true, forceCloseOverlay: true,
}); });
@@ -478,7 +468,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
if ( if (
!formElementHasValue || !formElementHasValue ||
(!(await this.isOverlayCiphersPopulated()) && this.isUserAuthed()) (!(await this.isInlineMenuCiphersPopulated()) && this.isUserAuthed())
) { ) {
void this.sendExtensionMessage("openAutofillOverlay"); void this.sendExtensionMessage("openAutofillOverlay");
return; return;
@@ -733,10 +723,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
private handleOverlayRepositionEvent = async () => { private handleOverlayRepositionEvent = async () => {
this.rebuildSubFrameOffsets(); this.rebuildSubFrameOffsets();
if ( if (!(await this.isInlineMenuButtonVisible()) && !(await this.isInlineMenuListVisible())) {
(await this.sendExtensionMessage("checkIsInlineMenuButtonVisible")) !== true &&
(await this.sendExtensionMessage("checkIsInlineMenuListVisible")) !== true
) {
return; return;
} }
@@ -772,7 +759,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
this.toggleOverlayHidden(false); this.toggleOverlayHidden(false);
if ( if (
(this.mostRecentlyFocusedField as HTMLInputElement).value && (this.mostRecentlyFocusedField as HTMLInputElement).value &&
((await this.isOverlayCiphersPopulated()) || !this.isUserAuthed()) ((await this.isInlineMenuCiphersPopulated()) || !this.isUserAuthed())
) { ) {
void this.sendExtensionMessage("closeAutofillOverlay", { void this.sendExtensionMessage("closeAutofillOverlay", {
overlayElement: AutofillOverlayElement.List, overlayElement: AutofillOverlayElement.List,
@@ -953,7 +940,19 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
this.autofillOverlayVisibility = data.autofillOverlayVisibility; this.autofillOverlayVisibility = data.autofillOverlayVisibility;
} }
private async isOverlayCiphersPopulated() { private async isFieldCurrentlyFilling() {
return (await this.sendExtensionMessage("checkIsFieldCurrentlyFilling")) === true;
}
private async isInlineMenuButtonVisible() {
return (await this.sendExtensionMessage("checkIsInlineMenuButtonVisible")) === true;
}
private async isInlineMenuListVisible() {
return (await this.sendExtensionMessage("checkIsInlineMenuListVisible")) === true;
}
private async isInlineMenuCiphersPopulated() {
return (await this.sendExtensionMessage("checkIsInlineMenuCiphersPopulated")) === true; return (await this.sendExtensionMessage("checkIsInlineMenuCiphersPopulated")) === true;
} }