1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

PM-25448 return null appropriately for subframe rects in order to reposition inline menu (#17129)

This commit is contained in:
Daniel Riera
2025-11-03 13:39:25 -05:00
committed by GitHub
parent 2fb9277b65
commit 6265fc2d46
2 changed files with 63 additions and 4 deletions

View File

@@ -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 = `<iframe id="subframe" src="https://example.com/"></iframe>`;
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 = `<iframe id="subframe" src="https://example.com/"></iframe>`;
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<

View File

@@ -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.