diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 53fbd936f10..ff98499f445 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -121,6 +121,10 @@ export type OverlayBackgroundExtensionMessageHandlers = { getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number; updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; rebuildSubFrameOffsets: ({ sender }: BackgroundSenderParam) => void; + destroyAutofillInlineMenuListeners: ({ + message, + sender, + }: BackgroundOnMessageHandlerParams) => void; collectPageDetailsResponse: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; unlockCompleted: ({ message }: BackgroundMessageParam) => void; addedCipher: () => void; diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 0ac53f25740..3b95eac3bdc 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -25,7 +25,11 @@ import { openAddEditVaultItemPopout, openViewVaultItemPopout, } from "../../vault/popup/utils/vault-popout-window"; -import { AutofillOverlayElement, AutofillOverlayPort } from "../enums/autofill-overlay.enum"; +import { + AutofillOverlayElement, + AutofillOverlayPort, + MAX_SUB_FRAME_DEPTH, +} from "../enums/autofill-overlay.enum"; import { AutofillService } from "../services/abstractions/autofill.service"; import { generateRandomChars } from "../utils"; @@ -92,6 +96,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender), updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender), rebuildSubFrameOffsets: ({ sender }) => this.rebuildSubFrameOffsets(sender), + destroyAutofillInlineMenuListeners: ({ message, sender }) => + this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId), collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender), unlockCompleted: ({ message }) => this.unlockCompleted(message), addedCipher: () => this.updateInlineMenuCiphers(), @@ -293,6 +299,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param url - The URL of the sub frame */ private async buildSubFrameOffsets(tab: chrome.tabs.Tab, frameId: number, url: string) { + let subFrameDepth = 0; const tabId = tab.id; let subFrameOffsetsForTab = this.subFrameOffsetsForTab[tabId]; if (!subFrameOffsetsForTab) { @@ -307,7 +314,10 @@ export class OverlayBackground implements OverlayBackgroundInterface { const subFrameData: SubFrameOffsetData = { url, top: 0, left: 0, parentFrameIds: [] }; let frameDetails = await BrowserApi.getFrameDetails({ tabId, frameId }); - while (frameDetails && frameDetails.parentFrameId > -1) { + while ( + (frameDetails && frameDetails.parentFrameId > -1) || + subFrameDepth > MAX_SUB_FRAME_DEPTH + ) { const subFrameOffset: SubFrameOffsetData = await BrowserApi.tabSendMessage( tab, { @@ -323,7 +333,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { void BrowserApi.tabSendMessage( tab, { command: "getSubFrameOffsetsFromWindowMessage", subFrameId: frameId }, - { frameId: frameId }, + { frameId }, ); return; } @@ -336,11 +346,38 @@ export class OverlayBackground implements OverlayBackgroundInterface { tabId, frameId: frameDetails.parentFrameId, }); + subFrameDepth++; + } + + if (subFrameDepth > MAX_SUB_FRAME_DEPTH) { + subFrameOffsetsForTab.set(frameId, null); + this.triggerDestroyInlineMenuListeners(tab, frameId); + return; } subFrameOffsetsForTab.set(frameId, subFrameData); } + /** + * Triggers a removal and destruction of all + * + * @param tab - The tab that the sub frame is associated with + * @param frameId - The frame ID of the sub frame + */ + private triggerDestroyInlineMenuListeners(tab: chrome.tabs.Tab, frameId: number) { + this.logService.error( + "Excessive frame depth encountered, destroying inline menu on field within frame", + tab, + frameId, + ); + + void BrowserApi.tabSendMessage( + tab, + { command: "destroyAutofillInlineMenuListeners" }, + { frameId }, + ); + } + /** * Handles rebuilding the sub frame offsets when the tab is repositioned or scrolled. * Will trigger a re-positioning of the inline menu list and button. Note that we diff --git a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts index 761b393d11f..70f3e319ce4 100644 --- a/apps/browser/src/autofill/enums/autofill-overlay.enum.ts +++ b/apps/browser/src/autofill/enums/autofill-overlay.enum.ts @@ -1,19 +1,19 @@ -const AutofillOverlayElement = { +export const AutofillOverlayElement = { Button: "autofill-inline-menu-button", List: "autofill-inline-menu-list", } as const; -const AutofillOverlayPort = { +export const AutofillOverlayPort = { Button: "autofill-inline-menu-button-port", ButtonMessageConnector: "autofill-inline-menu-button-message-connector", List: "autofill-inline-menu-list-port", ListMessageConnector: "autofill-inline-menu-list-message-connector", } as const; -const RedirectFocusDirection = { +export const RedirectFocusDirection = { Current: "current", Previous: "previous", Next: "next", } as const; -export { AutofillOverlayElement, AutofillOverlayPort, RedirectFocusDirection }; +export const MAX_SUB_FRAME_DEPTH = 10; diff --git a/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts index 4546bd16c6e..750e532aaaf 100644 --- a/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/abstractions/autofill-overlay-content.service.ts @@ -11,6 +11,10 @@ export type OpenAutofillInlineMenuOptions = { authStatus?: AuthenticationStatus; }; +export type SubFrameDataFromWindowMessage = SubFrameOffsetData & { + subFrameDepth: number; +}; + export type AutofillOverlayContentExtensionMessageHandlers = { [key: string]: CallableFunction; openAutofillInlineMenu: ({ message }: AutofillExtensionMessageParam) => void; @@ -24,6 +28,7 @@ export type AutofillOverlayContentExtensionMessageHandlers = { getSubFrameOffsets: ({ message }: AutofillExtensionMessageParam) => Promise; getSubFrameOffsetsFromWindowMessage: ({ message }: AutofillExtensionMessageParam) => void; checkMostRecentlyFocusedFieldHasValue: () => boolean; + destroyAutofillInlineMenuListeners: () => void; }; export interface AutofillOverlayContentService { diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index 51541f4bc97..da1ec918c8a 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -10,7 +10,11 @@ import { SubFrameOffsetData, } from "../background/abstractions/overlay.background"; import { AutofillExtensionMessage } from "../content/abstractions/autofill-init"; -import { AutofillOverlayElement, RedirectFocusDirection } from "../enums/autofill-overlay.enum"; +import { + AutofillOverlayElement, + MAX_SUB_FRAME_DEPTH, + RedirectFocusDirection, +} from "../enums/autofill-overlay.enum"; import AutofillField from "../models/autofill-field"; import { ElementWithOpId, FillableFormFieldElement, FormFieldElement } from "../types"; import { elementIsFillableFormField, getAttributeBoolean, sendExtensionMessage } from "../utils"; @@ -19,6 +23,7 @@ import { AutofillOverlayContentExtensionMessageHandlers, AutofillOverlayContentService as AutofillOverlayContentServiceInterface, OpenAutofillInlineMenuOptions, + SubFrameDataFromWindowMessage, } from "./abstractions/autofill-overlay-content.service"; import { AutoFillConstants } from "./autofill-constants"; @@ -54,6 +59,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ getSubFrameOffsetsFromWindowMessage: ({ message }) => this.getSubFrameOffsetsFromWindowMessage(message), checkMostRecentlyFocusedFieldHasValue: () => this.mostRecentlyFocusedFieldHasValue(), + destroyAutofillInlineMenuListeners: () => this.destroy(), }; /** @@ -1025,7 +1031,8 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ left: 0, top: 0, parentFrameIds: [], - }, + subFrameDepth: 0, + } as SubFrameDataFromWindowMessage, }, "*", ); @@ -1050,7 +1057,14 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ * @param event - The message event. */ private calculateSubFramePositioning = async (event: MessageEvent) => { - const subFrameData = event.data.subFrameData; + const subFrameData: SubFrameDataFromWindowMessage = event.data.subFrameData; + + subFrameData.subFrameDepth++; + if (subFrameData.subFrameDepth > MAX_SUB_FRAME_DEPTH) { + void this.sendExtensionMessage("destroyAutofillInlineMenuListeners", { subFrameData }); + return; + } + let subFrameOffsets: SubFrameOffsetData; const iframes = globalThis.document.querySelectorAll("iframe"); for (let i = 0; i < iframes.length; i++) { @@ -1079,9 +1093,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ return; } - void this.sendExtensionMessage("updateSubFrameData", { - subFrameData, - }); + void this.sendExtensionMessage("updateSubFrameData", { subFrameData }); }; /**