1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

[PM-5189] Fixing an issue with how we handle closing the inline menu after a programmtic redirection

This commit is contained in:
Cesar Gonzalez
2024-06-12 06:00:59 -05:00
parent 7673a55784
commit 42080a7377
4 changed files with 52 additions and 11 deletions

View File

@@ -1488,6 +1488,7 @@ describe("OverlayBackground", () => {
describe("triggerDelayedAutofillInlineMenuClosure message handler", () => {
it("skips triggering the delayed closure of the inline menu if a field is currently focused", async () => {
jest.useFakeTimers();
sendMockExtensionMessage({
command: "updateIsFieldCurrentlyFocused",
isFieldCurrentlyFocused: true,
@@ -1499,6 +1500,7 @@ describe("OverlayBackground", () => {
portKey,
});
await flushPromises();
jest.advanceTimersByTime(100);
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
expect(buttonPortSpy.postMessage).not.toHaveBeenCalledWith(message);
@@ -1506,16 +1508,42 @@ describe("OverlayBackground", () => {
});
it("sends a message to the button and list ports that triggers a delayed closure of the inline menu", async () => {
jest.useFakeTimers();
sendPortMessage(buttonMessageConnectorSpy, {
command: "triggerDelayedAutofillInlineMenuClosure",
portKey,
});
await flushPromises();
jest.advanceTimersByTime(100);
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
expect(buttonPortSpy.postMessage).toHaveBeenCalledWith(message);
expect(listPortSpy.postMessage).toHaveBeenCalledWith(message);
});
it("triggers a single delayed closure if called again within a 100ms threshold", async () => {
jest.useFakeTimers();
sendPortMessage(buttonMessageConnectorSpy, {
command: "triggerDelayedAutofillInlineMenuClosure",
portKey,
});
await flushPromises();
jest.advanceTimersByTime(50);
sendPortMessage(buttonMessageConnectorSpy, {
command: "triggerDelayedAutofillInlineMenuClosure",
portKey,
});
await flushPromises();
jest.advanceTimersByTime(100);
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
expect(buttonPortSpy.postMessage).toHaveBeenCalledTimes(2);
expect(buttonPortSpy.postMessage).not.toHaveBeenNthCalledWith(1, message);
expect(buttonPortSpy.postMessage).toHaveBeenNthCalledWith(2, message);
expect(listPortSpy.postMessage).toHaveBeenCalledTimes(2);
expect(listPortSpy.postMessage).not.toHaveBeenNthCalledWith(1, message);
expect(listPortSpy.postMessage).toHaveBeenNthCalledWith(2, message);
});
});
describe("autofillInlineMenuBlurred message handler", () => {

View File

@@ -60,6 +60,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private inlineMenuPageTranslations: Record<string, string>;
private inlineMenuFadeInTimeout: number | NodeJS.Timeout;
private updateInlineMenuPositionTimeout: number | NodeJS.Timeout;
private delayedCloseTimeout: number | NodeJS.Timeout;
private focusedFieldData: FocusedFieldData;
private isFieldCurrentlyFocused: boolean = false;
private isFieldCurrentlyFilling: boolean = false;
@@ -99,8 +100,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
deletedCipher: () => this.updateInlineMenuCiphers(),
};
private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = {
triggerDelayedAutofillInlineMenuClosure: ({ port }) =>
this.triggerDelayedInlineMenuClosure(port.sender),
triggerDelayedAutofillInlineMenuClosure: ({ port }) => this.triggerDelayedInlineMenuClosure(),
autofillInlineMenuButtonClicked: ({ port }) => this.handleInlineMenuButtonClicked(port),
autofillInlineMenuBlurred: () => this.checkInlineMenuListFocused(),
redirectAutofillInlineMenuFocusOut: ({ message, port }) =>
@@ -372,7 +372,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
}
}
this.updateInlineMenuPositionTimeout = setTimeout(
this.updateInlineMenuPositionTimeout = globalThis.setTimeout(
() => this.updateInlineMenuPositionAfterSubFrameRebuild(sender),
650,
);
@@ -510,17 +510,29 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* Sends a message to the sender tab to trigger a delayed closure of the inline menu.
* This is used to ensure that we capture click events on the inline menu in the case
* that some on page programmatic method attempts to force focus redirection.
*
* @param sender - The sender of the port message
*/
private triggerDelayedInlineMenuClosure(sender: chrome.runtime.MessageSender) {
private triggerDelayedInlineMenuClosure() {
if (this.isFieldCurrentlyFocused) {
return;
}
this.clearDelayedInlineMenuClosure();
this.delayedCloseTimeout = globalThis.setTimeout(() => {
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
this.inlineMenuButtonPort?.postMessage(message);
this.inlineMenuListPort?.postMessage(message);
}, 100);
}
/**
* Clears the delayed closure timeout for the inline menu, effectively
* cancelling the event from occurring.
*/
private clearDelayedInlineMenuClosure() {
if (this.delayedCloseTimeout) {
clearTimeout(this.delayedCloseTimeout);
}
}
/**
@@ -772,6 +784,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
return;
}
this.clearDelayedInlineMenuClosure();
await this.openInlineMenu(false, true);
}

View File

@@ -416,7 +416,7 @@ describe("AutofillInlineMenuIframeService", () => {
"opacity 65ms ease-out 0s",
);
jest.advanceTimersByTime(200);
jest.advanceTimersByTime(100);
expect(autofillInlineMenuIframeService["iframe"].style.transition).toBe(
"opacity 125ms ease-out 0s",
);

View File

@@ -322,7 +322,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe
this.delayedCloseTimeout = globalThis.setTimeout(() => {
this.updateElementStyles(this.iframe, { transition: this.fadeInOpacityTransition });
this.forceCloseInlineMenu();
}, 200);
}, 100);
}
/**