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:
@@ -1488,6 +1488,7 @@ describe("OverlayBackground", () => {
|
|||||||
|
|
||||||
describe("triggerDelayedAutofillInlineMenuClosure message handler", () => {
|
describe("triggerDelayedAutofillInlineMenuClosure message handler", () => {
|
||||||
it("skips triggering the delayed closure of the inline menu if a field is currently focused", async () => {
|
it("skips triggering the delayed closure of the inline menu if a field is currently focused", async () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "updateIsFieldCurrentlyFocused",
|
command: "updateIsFieldCurrentlyFocused",
|
||||||
isFieldCurrentlyFocused: true,
|
isFieldCurrentlyFocused: true,
|
||||||
@@ -1499,6 +1500,7 @@ describe("OverlayBackground", () => {
|
|||||||
portKey,
|
portKey,
|
||||||
});
|
});
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
|
||||||
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
|
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
|
||||||
expect(buttonPortSpy.postMessage).not.toHaveBeenCalledWith(message);
|
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 () => {
|
it("sends a message to the button and list ports that triggers a delayed closure of the inline menu", async () => {
|
||||||
|
jest.useFakeTimers();
|
||||||
sendPortMessage(buttonMessageConnectorSpy, {
|
sendPortMessage(buttonMessageConnectorSpy, {
|
||||||
command: "triggerDelayedAutofillInlineMenuClosure",
|
command: "triggerDelayedAutofillInlineMenuClosure",
|
||||||
portKey,
|
portKey,
|
||||||
});
|
});
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
|
||||||
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
|
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
|
||||||
expect(buttonPortSpy.postMessage).toHaveBeenCalledWith(message);
|
expect(buttonPortSpy.postMessage).toHaveBeenCalledWith(message);
|
||||||
expect(listPortSpy.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", () => {
|
describe("autofillInlineMenuBlurred message handler", () => {
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
private inlineMenuPageTranslations: Record<string, string>;
|
private inlineMenuPageTranslations: Record<string, string>;
|
||||||
private inlineMenuFadeInTimeout: number | NodeJS.Timeout;
|
private inlineMenuFadeInTimeout: number | NodeJS.Timeout;
|
||||||
private updateInlineMenuPositionTimeout: number | NodeJS.Timeout;
|
private updateInlineMenuPositionTimeout: number | NodeJS.Timeout;
|
||||||
|
private delayedCloseTimeout: number | NodeJS.Timeout;
|
||||||
private focusedFieldData: FocusedFieldData;
|
private focusedFieldData: FocusedFieldData;
|
||||||
private isFieldCurrentlyFocused: boolean = false;
|
private isFieldCurrentlyFocused: boolean = false;
|
||||||
private isFieldCurrentlyFilling: boolean = false;
|
private isFieldCurrentlyFilling: boolean = false;
|
||||||
@@ -99,8 +100,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
deletedCipher: () => this.updateInlineMenuCiphers(),
|
deletedCipher: () => this.updateInlineMenuCiphers(),
|
||||||
};
|
};
|
||||||
private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = {
|
private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = {
|
||||||
triggerDelayedAutofillInlineMenuClosure: ({ port }) =>
|
triggerDelayedAutofillInlineMenuClosure: ({ port }) => this.triggerDelayedInlineMenuClosure(),
|
||||||
this.triggerDelayedInlineMenuClosure(port.sender),
|
|
||||||
autofillInlineMenuButtonClicked: ({ port }) => this.handleInlineMenuButtonClicked(port),
|
autofillInlineMenuButtonClicked: ({ port }) => this.handleInlineMenuButtonClicked(port),
|
||||||
autofillInlineMenuBlurred: () => this.checkInlineMenuListFocused(),
|
autofillInlineMenuBlurred: () => this.checkInlineMenuListFocused(),
|
||||||
redirectAutofillInlineMenuFocusOut: ({ message, port }) =>
|
redirectAutofillInlineMenuFocusOut: ({ message, port }) =>
|
||||||
@@ -372,7 +372,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateInlineMenuPositionTimeout = setTimeout(
|
this.updateInlineMenuPositionTimeout = globalThis.setTimeout(
|
||||||
() => this.updateInlineMenuPositionAfterSubFrameRebuild(sender),
|
() => this.updateInlineMenuPositionAfterSubFrameRebuild(sender),
|
||||||
650,
|
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.
|
* 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
|
* 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.
|
* 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) {
|
if (this.isFieldCurrentlyFocused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.clearDelayedInlineMenuClosure();
|
||||||
|
|
||||||
|
this.delayedCloseTimeout = globalThis.setTimeout(() => {
|
||||||
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
|
const message = { command: "triggerDelayedAutofillInlineMenuClosure" };
|
||||||
this.inlineMenuButtonPort?.postMessage(message);
|
this.inlineMenuButtonPort?.postMessage(message);
|
||||||
this.inlineMenuListPort?.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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.clearDelayedInlineMenuClosure();
|
||||||
await this.openInlineMenu(false, true);
|
await this.openInlineMenu(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ describe("AutofillInlineMenuIframeService", () => {
|
|||||||
"opacity 65ms ease-out 0s",
|
"opacity 65ms ease-out 0s",
|
||||||
);
|
);
|
||||||
|
|
||||||
jest.advanceTimersByTime(200);
|
jest.advanceTimersByTime(100);
|
||||||
expect(autofillInlineMenuIframeService["iframe"].style.transition).toBe(
|
expect(autofillInlineMenuIframeService["iframe"].style.transition).toBe(
|
||||||
"opacity 125ms ease-out 0s",
|
"opacity 125ms ease-out 0s",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe
|
|||||||
this.delayedCloseTimeout = globalThis.setTimeout(() => {
|
this.delayedCloseTimeout = globalThis.setTimeout(() => {
|
||||||
this.updateElementStyles(this.iframe, { transition: this.fadeInOpacityTransition });
|
this.updateElementStyles(this.iframe, { transition: this.fadeInOpacityTransition });
|
||||||
this.forceCloseInlineMenu();
|
this.forceCloseInlineMenu();
|
||||||
}, 200);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user