diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index c52bceec72..f08176469c 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -131,8 +131,7 @@ export type PortOnMessageHandlerParams = PortMessageParam & PortConnectionParam; export type InlineMenuButtonPortMessageHandlers = { [key: string]: CallableFunction; autofillInlineMenuButtonClicked: ({ port }: PortConnectionParam) => void; - closeAutofillInlineMenu: ({ port }: PortConnectionParam) => void; - forceCloseAutofillInlineMenu: ({ port }: PortConnectionParam) => void; + triggerDelayedInlineMenuClosure: ({ port }: PortConnectionParam) => void; autofillInlineMenuBlurred: () => void; redirectAutofillInlineMenuFocusOut: ({ message, port }: PortOnMessageHandlerParams) => void; updateAutofillInlineMenuColorScheme: () => void; @@ -141,7 +140,6 @@ export type InlineMenuButtonPortMessageHandlers = { export type InlineMenuListPortMessageHandlers = { [key: string]: CallableFunction; checkAutofillInlineMenuButtonFocused: () => void; - forceCloseAutofillInlineMenu: ({ port }: PortConnectionParam) => void; autofillInlineMenuBlurred: () => void; unlockVault: ({ port }: PortConnectionParam) => void; fillSelectedAutofillInlineMenuListItem: ({ message, port }: PortOnMessageHandlerParams) => void; diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 6190861851..70ba44aecc 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -96,9 +96,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { }; private readonly inlineMenuButtonPortMessageHandlers: InlineMenuButtonPortMessageHandlers = { autofillInlineMenuButtonClicked: ({ port }) => this.handleInlineMenuButtonClicked(port), - closeAutofillInlineMenu: ({ port }) => this.closeInlineMenu(port.sender), - forceCloseAutofillInlineMenu: ({ port }) => - this.closeInlineMenu(port.sender, { forceCloseAutofillInlineMenu: true }), + triggerDelayedInlineMenuClosure: ({ port }) => + this.triggerDelayedInlineMenuClosure(port.sender), autofillInlineMenuBlurred: () => this.checkInlineMenuListFocused(), redirectAutofillInlineMenuFocusOut: ({ message, port }) => this.redirectInlineMenuFocusOut(message, port), @@ -106,8 +105,6 @@ export class OverlayBackground implements OverlayBackgroundInterface { }; private readonly inlineMenuListPortMessageHandlers: InlineMenuListPortMessageHandlers = { checkAutofillInlineMenuButtonFocused: () => this.checkInlineMenuButtonFocused(), - forceCloseAutofillInlineMenu: ({ port }) => - this.closeInlineMenu(port.sender, { forceCloseAutofillInlineMenu: true }), autofillInlineMenuBlurred: () => this.checkInlineMenuButtonFocused(), unlockVault: ({ port }) => this.unlockVault(port), fillSelectedAutofillInlineMenuListItem: ({ message, port }) => @@ -510,6 +507,16 @@ export class OverlayBackground implements OverlayBackgroundInterface { ); } + private triggerDelayedInlineMenuClosure(sender: chrome.runtime.MessageSender) { + if (this.isFieldCurrentlyFocused) { + return; + } + + const message = { command: "triggerDelayedAutofillInlineMenuClosure" }; + this.inlineMenuButtonPort?.postMessage(message); + this.inlineMenuListPort?.postMessage(message); + } + /** * Handles cleanup when an overlay element is closed. Disconnects * the list and button ports and sets them to null. diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts index efab7cba6f..3204727f0d 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts @@ -16,6 +16,8 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe private iframe: HTMLIFrameElement; private ariaAlertElement: HTMLDivElement; private ariaAlertTimeout: number | NodeJS.Timeout; + private delayedCloseTimeout: number | NodeJS.Timeout; + private readonly defaultOpacityTransition = "opacity 125ms ease-out 0s"; private iframeStyles: Partial = { all: "initial", position: "fixed", @@ -23,7 +25,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe zIndex: "2147483647", lineHeight: "0", overflow: "hidden", - transition: "opacity 125ms ease-out 0s", + transition: this.defaultOpacityTransition, visibility: "visible", clipPath: "none", pointerEvents: "auto", @@ -47,6 +49,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe updateInlineMenuIframePosition: ({ message }) => this.updateIframePosition(message.styles), updateInlineMenuHidden: ({ message }) => this.updateElementStyles(this.iframe, message.styles), updateAutofillInlineMenuColorScheme: () => this.updateAutofillInlineMenuColorScheme(), + triggerDelayedAutofillInlineMenuClosure: () => this.handleDelayedAutofillInlineMenuClosure(), }; constructor( @@ -403,4 +406,24 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe return false; } + + private handleDelayedAutofillInlineMenuClosure() { + if (this.delayedCloseTimeout) { + clearTimeout(this.delayedCloseTimeout); + } + + this.updateElementStyles(this.iframe, { + opacity: "0", + transition: "none", + pointerEvents: "none", + }); + + this.delayedCloseTimeout = globalThis.setTimeout(() => { + this.updateElementStyles(this.iframe, { + transition: this.defaultOpacityTransition, + pointerEvents: "auto", + }); + this.forceCloseAutofillInlineMenu(); + }, 250); + } } diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts index 23424f189b..28f2a3ca19 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.spec.ts @@ -74,7 +74,7 @@ describe("AutofillInlineMenuButton", () => { await flushPromises(); expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith({ - command: "closeAutofillInlineMenu", + command: "triggerDelayedInlineMenuClosure", }); }); @@ -85,7 +85,7 @@ describe("AutofillInlineMenuButton", () => { await flushPromises(); expect(globalThis.parent.postMessage).toHaveBeenCalledWith( - { command: "closeAutofillInlineMenu", portKey }, + { command: "triggerDelayedInlineMenuClosure", portKey }, "*", ); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts index 03debe861c..29bdce7b97 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/button/autofill-inline-menu-button.ts @@ -121,7 +121,7 @@ class AutofillInlineMenuButton extends AutofillInlineMenuPageElement { return; } - this.postMessageToParent({ command: "closeAutofillInlineMenu" }); + this.postMessageToParent({ command: "triggerDelayedInlineMenuClosure" }); } } diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts index a516dedd46..7e5a7ba35c 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts @@ -69,8 +69,6 @@ export class AutofillInlineMenuContainer { private setupPortMessageListener = (message: InitInlineMenuElementMessage) => { this.port = chrome.runtime.connect({ name: this.portName }); - this.port.onMessage.addListener(this.handlePortMessage); - this.postMessageToInlineMenuPage(message); }; @@ -90,14 +88,6 @@ export class AutofillInlineMenuContainer { this.port.postMessage(message); } - private handlePortMessage = (message: any, port: chrome.runtime.Port) => { - if (port.name !== this.portName) { - return; - } - - this.postMessageToInlineMenuPage(message); - }; - private handleWindowMessage = (event: MessageEvent) => { const message = event.data; if (this.isForeignWindowMessage(event)) {