diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 0d3785de321..f5d8257e635 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -12,9 +12,10 @@ type PageDetailsForTab = Record< >; type SubFrameOffsetData = { - url: string; + url?: string; top: number; left: number; + frameId?: number; } | null; type SubFrameOffsetsForTab = Record< @@ -110,6 +111,7 @@ type OverlayBackgroundExtensionMessageHandlers = { updateIsFieldCurrentlyFilling: ({ message }: BackgroundMessageParam) => void; checkIsInlineMenuButtonVisible: ({ sender }: BackgroundSenderParam) => void; checkIsInlineMenuListVisible: ({ sender }: BackgroundSenderParam) => void; + updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; }; type PortMessageParam = { diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index d4673100e8e..653acd526d8 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -84,6 +84,7 @@ class OverlayBackground implements OverlayBackgroundInterface { (this.isCurrentlyFilling = message.isFieldCurrentlyFilling), checkIsInlineMenuButtonVisible: ({ sender }) => this.checkIsInlineMenuButtonVisible(sender), checkIsInlineMenuListVisible: ({ sender }) => this.checkIsInlineMenuListVisible(sender), + updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender), }; private readonly overlayButtonPortMessageHandlers: OverlayButtonPortMessageHandlers = { overlayButtonClicked: ({ port }) => this.handleOverlayButtonClicked(port), @@ -127,6 +128,13 @@ class OverlayBackground implements OverlayBackgroundInterface { return value; } + updateSubFrameData(message: any, sender: chrome.runtime.MessageSender) { + const subFrameOffsetsForTab = this.subFrameOffsetsForTab[sender.tab.id]; + if (subFrameOffsetsForTab) { + subFrameOffsetsForTab.set(message.subFrameData.frameId, message.subFrameData); + } + } + private async checkIsInlineMenuListVisible(sender: chrome.runtime.MessageSender) { return await BrowserApi.tabSendMessage( sender.tab, @@ -277,12 +285,26 @@ class OverlayBackground implements OverlayBackgroundInterface { while (frameDetails.parentFrameId !== -1) { const subFrameOffset: SubFrameOffsetData = await BrowserApi.tabSendMessage( tab, - { command: "getSubFrameOffsets", subFrameUrl: frameDetails.url }, + { + command: "getSubFrameOffsets", + subFrameUrl: frameDetails.url, + subFrameId: frameDetails.documentId, + }, { frameId: frameDetails.parentFrameId }, ); if (!subFrameOffset) { subFrameOffsetsForTab.set(frameId, null); + void BrowserApi.tabSendMessage( + tab, + { + command: "getSubFrameOffsetsThroughWindowMessaging", + subFrameId: frameId, + }, + { + frameId: frameId, + }, + ); return; } diff --git a/apps/browser/src/autofill/content/abstractions/autofill-init.ts b/apps/browser/src/autofill/content/abstractions/autofill-init.ts index da3d58950dd..59e44bfed0c 100644 --- a/apps/browser/src/autofill/content/abstractions/autofill-init.ts +++ b/apps/browser/src/autofill/content/abstractions/autofill-init.ts @@ -10,6 +10,7 @@ export type AutofillExtensionMessage = { fillScript?: AutofillScript; url?: string; subFrameUrl?: string; + subFrameId?: string; pageDetailsUrl?: string; ciphers?: any; isInlineMenuHidden?: boolean; @@ -40,6 +41,7 @@ export type AutofillExtensionMessageHandlers = { bgVaultItemRepromptPopoutOpened: () => void; updateAutofillOverlayVisibility: ({ message }: AutofillExtensionMessageParam) => void; getSubFrameOffsets: ({ message }: AutofillExtensionMessageParam) => Promise; + getSubFrameOffsetsThroughWindowMessaging: ({ message }: AutofillExtensionMessageParam) => void; }; export interface AutofillInit { diff --git a/apps/browser/src/autofill/content/autofill-init.ts b/apps/browser/src/autofill/content/autofill-init.ts index c38850938bc..07e7045c0b5 100644 --- a/apps/browser/src/autofill/content/autofill-init.ts +++ b/apps/browser/src/autofill/content/autofill-init.ts @@ -33,6 +33,8 @@ class AutofillInit implements AutofillInitInterface { bgVaultItemRepromptPopoutOpened: () => this.blurAndRemoveOverlay(), updateAutofillOverlayVisibility: ({ message }) => this.updateAutofillOverlayVisibility(message), getSubFrameOffsets: ({ message }) => this.getSubFrameOffsets(message), + getSubFrameOffsetsThroughWindowMessaging: ({ message }) => + this.getSubFrameOffsetsThroughWindowMessaging(message), }; /** @@ -71,6 +73,45 @@ class AutofillInit implements AutofillInitInterface { this.domElementVisibilityService, this.collectAutofillContentService, ); + + window.addEventListener("message", (event) => { + // if (event.source !== window) { + // return; + // } + + if (event.data.command === "calculateSubFramePositioning") { + const subFrameData = event.data.subFrameData; + let subFrameOffsets: SubFrameOffsetData; + const iframes = document.querySelectorAll("iframe"); + for (let i = 0; i < iframes.length; i++) { + if (iframes[i].contentWindow === event.source) { + const iframeElement = iframes[i]; + subFrameOffsets = this.calculateSubFrameOffsets( + iframeElement, + subFrameData.url, + subFrameData.frameId, + ); + + subFrameData.top += subFrameOffsets.top; + subFrameData.left += subFrameOffsets.left; + + break; + } + } + + if (globalThis.window.self !== globalThis.window.top) { + globalThis.parent.postMessage( + { command: "calculateSubFramePositioning", subFrameData }, + "*", + ); + return; + } + + void sendExtensionMessage("updateSubFrameData", { + subFrameData, + }); + } + }); } /** @@ -245,14 +286,27 @@ class AutofillInit implements AutofillInitInterface { ): Promise { const { subFrameUrl } = message; const subFrameUrlWithoutTrailingSlash = subFrameUrl?.replace(/\/$/, ""); - const iframeElement = document.querySelector( + + let iframeElement: HTMLIFrameElement | null = null; + const iframeElements = document.querySelectorAll( `iframe[src="${subFrameUrl}"], iframe[src="${subFrameUrlWithoutTrailingSlash}"]`, - ); + ) as NodeListOf; + if (iframeElements.length === 1) { + iframeElement = iframeElements[0]; + } if (!iframeElement) { return null; } + return this.calculateSubFrameOffsets(iframeElement, subFrameUrl); + } + + private calculateSubFrameOffsets( + iframeElement: HTMLIFrameElement, + subFrameUrl?: string, + frameId?: number, + ): SubFrameOffsetData { const iframeRect = iframeElement.getBoundingClientRect(); const iframeStyles = globalThis.getComputedStyle(iframeElement); const paddingLeft = parseInt(iframeStyles.getPropertyValue("padding-left")); @@ -262,11 +316,27 @@ class AutofillInit implements AutofillInitInterface { return { url: subFrameUrl, + frameId, top: iframeRect.top + paddingTop + borderWidthTop, left: iframeRect.left + paddingLeft + borderWidthLeft, }; } + private getSubFrameOffsetsThroughWindowMessaging(message: any) { + globalThis.parent.postMessage( + { + command: "calculateSubFramePositioning", + subFrameData: { + url: window.location.href, + frameId: message.subFrameId, + left: 0, + top: 0, + }, + }, + "*", + ); + } + /** * Sets up the extension message listeners for the content script. */