diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts
index 96b05b81c96..3c19589afef 100644
--- a/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts
+++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.spec.ts
@@ -32,6 +32,17 @@ import { InlineMenuFieldQualificationService } from "./inline-menu-field-qualifi
const defaultWindowReadyState = document.readyState;
const defaultDocumentVisibilityState = document.visibilityState;
+
+const mockRect = (rect: { left: number; top: number; width: number; height: number }) =>
+ ({
+ ...rect,
+ x: rect.left,
+ y: rect.top,
+ right: rect.left + rect.width,
+ bottom: rect.top + rect.height,
+ toJSON: () => ({}),
+ }) as DOMRectReadOnly;
+
describe("AutofillOverlayContentService", () => {
let domQueryService: DomQueryService;
let domElementVisibilityService: DomElementVisibilityService;
@@ -2154,6 +2165,10 @@ describe("AutofillOverlayContentService", () => {
});
it("calculates the sub frame's offsets if a single frame with the referenced url exists", async () => {
+ const iframe = document.querySelector("iframe") as HTMLIFrameElement;
+ jest
+ .spyOn(iframe, "getBoundingClientRect")
+ .mockReturnValue(mockRect({ left: 0, top: 0, width: 1, height: 1 }));
sendMockExtensionMessage(
{
command: "getSubFrameOffsets",
@@ -2270,6 +2285,9 @@ describe("AutofillOverlayContentService", () => {
});
document.body.innerHTML = ``;
const iframe = document.querySelector("iframe") as HTMLIFrameElement;
+ jest
+ .spyOn(iframe, "getBoundingClientRect")
+ .mockReturnValue(mockRect({ width: 1, height: 1, left: 2, top: 2 }));
const subFrameData = {
url: "https://example.com/",
frameId: 10,
@@ -2305,6 +2323,9 @@ describe("AutofillOverlayContentService", () => {
it("posts the calculated sub frame data to the background", async () => {
document.body.innerHTML = ``;
const iframe = document.querySelector("iframe") as HTMLIFrameElement;
+ jest
+ .spyOn(iframe, "getBoundingClientRect")
+ .mockReturnValue(mockRect({ width: 1, height: 1, left: 2, top: 2 }));
const subFrameData = {
url: "https://example.com/",
frameId: 10,
@@ -2335,6 +2356,39 @@ describe("AutofillOverlayContentService", () => {
});
});
+ describe("calculateSubFrameOffsets", () => {
+ it("returns null when iframe has zero width and height", () => {
+ const iframe = document.querySelector("iframe") as HTMLIFrameElement;
+
+ jest
+ .spyOn(iframe, "getBoundingClientRect")
+ .mockReturnValue(mockRect({ left: 0, top: 0, width: 0, height: 0 }));
+
+ const result = autofillOverlayContentService["calculateSubFrameOffsets"](
+ iframe,
+ "https://example.com/",
+ 10,
+ );
+
+ expect(result).toBeNull();
+ });
+
+ it("returns null when iframe is not connected to the document", () => {
+ const iframe = document.createElement("iframe") as HTMLIFrameElement;
+
+ jest
+ .spyOn(iframe, "getBoundingClientRect")
+ .mockReturnValue(mockRect({ width: 100, height: 50, left: 10, top: 20 }));
+
+ const result = autofillOverlayContentService["calculateSubFrameOffsets"](
+ iframe,
+ "https://example.com/",
+ 10,
+ );
+ expect(result).toBeNull();
+ });
+ });
+
describe("checkMostRecentlyFocusedFieldHasValue message handler", () => {
it("returns true if the most recently focused field has a truthy value", async () => {
autofillOverlayContentService["mostRecentlyFocusedField"] = mock<
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 656516d1119..7c98859070a 100644
--- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts
+++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts
@@ -1485,12 +1485,17 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
frameId?: number,
): SubFrameOffsetData {
const iframeRect = iframeElement.getBoundingClientRect();
+ const iframeRectHasSize = iframeRect.width > 0 && iframeRect.height > 0;
const iframeStyles = globalThis.getComputedStyle(iframeElement);
const paddingLeft = parseInt(iframeStyles.getPropertyValue("padding-left")) || 0;
const paddingTop = parseInt(iframeStyles.getPropertyValue("padding-top")) || 0;
const borderWidthLeft = parseInt(iframeStyles.getPropertyValue("border-left-width")) || 0;
const borderWidthTop = parseInt(iframeStyles.getPropertyValue("border-top-width")) || 0;
+ if (!iframeRect || !iframeRectHasSize || !iframeElement.isConnected) {
+ return null;
+ }
+
return {
url: subFrameUrl,
frameId,
@@ -1525,6 +1530,10 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
subFrameData.frameId,
);
+ if (!subFrameOffsets) {
+ return;
+ }
+
subFrameData.top += subFrameOffsets.top;
subFrameData.left += subFrameOffsets.left;
@@ -1657,10 +1666,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
globalThis.addEventListener(EVENTS.RESIZE, repositionHandler);
}
- private shouldRepositionSubFrameInlineMenuOnScroll = async () => {
- return await this.sendExtensionMessage("shouldRepositionSubFrameInlineMenuOnScroll");
- };
-
/**
* Removes the listeners that facilitate repositioning
* the overlay elements on scroll or resize.