diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 017c86a856..25f15e63c5 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -47,6 +47,7 @@ import { createAutofillPageDetailsMock, createPortSpyMock, createFocusedFieldDataMock, + createPageDetailMock, } from "../spec/autofill-mocks"; import { flushPromises, @@ -55,6 +56,7 @@ import { triggerPortOnConnectEvent, triggerPortOnDisconnectEvent, triggerPortOnMessageEvent, + triggerWebNavigationOnCommittedEvent, } from "../spec/testing-utils"; import { @@ -1281,6 +1283,33 @@ describe("OverlayBackground", () => { command: "fadeInAutofillInlineMenuIframe", }); }); + + it("triggers a debounced reposition of the inline menu if the sender frame has a `null` sub frame offsets value", async () => { + jest.useFakeTimers(); + const focusedFieldData = createFocusedFieldDataMock(); + const sender = mock({ + tab: { id: focusedFieldData.tabId }, + frameId: focusedFieldData.frameId, + }); + sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender); + overlayBackground["subFrameOffsetsForTab"][focusedFieldData.tabId] = new Map([ + [focusedFieldData.frameId, null], + ]); + tabsSendMessageSpy.mockImplementation(); + jest.spyOn(overlayBackground as any, "repositionInlineMenu"); + + sendMockExtensionMessage( + { + command: "updateAutofillInlineMenuPosition", + overlayElement: AutofillOverlayElement.List, + }, + sender, + ); + await flushPromises(); + jest.advanceTimersByTime(1000); + + expect(overlayBackground["repositionInlineMenu"]).toHaveBeenCalled(); + }); }); describe("toggleAutofillInlineMenuHidden message handler", () => { @@ -1288,16 +1317,14 @@ describe("OverlayBackground", () => { await initOverlayElementPorts(); }); - it("returns early if the display value is not provided", async () => { - const message = { - command: "toggleAutofillInlineMenuHidden", - }; + it("returns early if the sender tab is not equal to the focused field tab", async () => { + const sender = mock({ tab: { id: 1 } }); + const focusedFieldData = createFocusedFieldDataMock({ tabId: 2 }); + sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }); - sendMockExtensionMessage(message); - await flushPromises(); + sendMockExtensionMessage({ command: "toggleAutofillInlineMenuHidden" }, sender); - expect(buttonPortSpy.postMessage).not.toHaveBeenCalledWith(message); - expect(listPortSpy.postMessage).not.toHaveBeenCalledWith(message); + expect(tabsSendMessageSpy).not.toHaveBeenCalled(); }); it("posts a message to the overlay button and list which hides the menu", async () => { @@ -1924,6 +1951,58 @@ describe("OverlayBackground", () => { }); }); + describe("handle web navigation on committed events", () => { + describe("navigation event occurs in the top frame of the tab", () => { + it("removes the collected page details", async () => { + const sender = mock({ + tabId: 1, + frameId: 0, + }); + overlayBackground["pageDetailsForTab"][sender.tabId] = new Map([ + [sender.frameId, createPageDetailMock()], + ]); + + triggerWebNavigationOnCommittedEvent(sender); + await flushPromises(); + + expect(overlayBackground["pageDetailsForTab"][sender.tabId]).toBe(undefined); + }); + + it("clears the sub frames associated with the tab", () => { + const sender = mock({ + tabId: 1, + frameId: 0, + }); + const subFrameId = 10; + overlayBackground["subFrameOffsetsForTab"][sender.tabId] = new Map([ + [subFrameId, mock()], + ]); + + triggerWebNavigationOnCommittedEvent(sender); + + expect(overlayBackground["subFrameOffsetsForTab"][sender.tabId]).toBe(undefined); + }); + }); + + describe("navigation event occurs within sub frame", () => { + it("clears the sub frame offsets for the current frame", () => { + const sender = mock({ + tabId: 1, + frameId: 1, + }); + overlayBackground["subFrameOffsetsForTab"][sender.tabId] = new Map([ + [sender.frameId, mock()], + ]); + + triggerWebNavigationOnCommittedEvent(sender); + + expect(overlayBackground["subFrameOffsetsForTab"][sender.tabId].get(sender.frameId)).toBe( + undefined, + ); + }); + }); + }); + describe("handle port onConnect", () => { it("skips setting up the overlay port if the port connection is not for an overlay element", async () => { const port = createPortSpyMock("not-an-overlay-element"); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index a3f8c76d0d..4a6a12bd69 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -647,7 +647,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { return; } - this.cancelInlineMenuFadeIn(); + this.cancelInlineMenuFadeInAndPositionUpdate(); await BrowserApi.tabSendMessage( sender.tab, @@ -660,8 +660,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { if (subFrameOffsetsForTab) { subFrameOffsets = subFrameOffsetsForTab.get(this.focusedFieldData.frameId); if (subFrameOffsets === null) { - this.cancelUpdateInlineMenuPositionSubject.next(); - this.startUpdateInlineMenuPositionSubject.next(sender); + this.repositionInlineMenuSubject.next(sender); return; } } diff --git a/apps/browser/src/autofill/spec/testing-utils.ts b/apps/browser/src/autofill/spec/testing-utils.ts index 2b821d2f73..06d1aca145 100644 --- a/apps/browser/src/autofill/spec/testing-utils.ts +++ b/apps/browser/src/autofill/spec/testing-utils.ts @@ -114,6 +114,17 @@ export function triggerTabOnRemovedEvent(tabId: number, removeInfo: chrome.tabs. }); } +export function triggerWebNavigationOnCommittedEvent( + details: chrome.webNavigation.WebNavigationFramedCallbackDetails, +) { + (chrome.webNavigation.onCommitted.addListener as unknown as jest.SpyInstance).mock.calls.forEach( + (call) => { + const callback = call[0]; + callback(details); + }, + ); +} + export function mockQuerySelectorAllDefinedCall() { const originalDocumentQuerySelectorAll = document.querySelectorAll; document.querySelectorAll = function (selector: string) {