mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-10 00:21:27 +01:00
[PM-5189] Implementing a set threshold for the maximum depth for which we are willing to calculate sub frame offsets
This commit is contained in:
parent
b857e4943a
commit
d94d85e201
@ -1310,6 +1310,23 @@ describe("OverlayBackground", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("destroyAutofillInlineMenuListeners", () => {
|
||||
it("sends a message to the passed frameId that triggers a destruction of the inline menu listeners on that frame", () => {
|
||||
const sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 }, frameId: 0 });
|
||||
|
||||
sendMockExtensionMessage(
|
||||
{ command: "destroyAutofillInlineMenuListeners", subFrameData: { frameId: 10 } },
|
||||
sender,
|
||||
);
|
||||
|
||||
expect(tabsSendMessageSpy).toHaveBeenCalledWith(
|
||||
sender.tab,
|
||||
{ command: "destroyAutofillInlineMenuListeners" },
|
||||
{ frameId: 10 },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unlockCompleted", () => {
|
||||
let updateInlineMenuCiphersSpy: jest.SpyInstance;
|
||||
|
||||
|
@ -4,7 +4,11 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { AutofillOverlayVisibility, EVENTS } from "@bitwarden/common/autofill/constants";
|
||||
|
||||
import AutofillInit from "../content/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 { createAutofillFieldMock } from "../spec/autofill-mocks";
|
||||
import { flushPromises, sendMockExtensionMessage } from "../spec/testing-utils";
|
||||
@ -1034,89 +1038,6 @@ describe("AutofillOverlayContentService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("destroy", () => {
|
||||
let autofillFieldElement: ElementWithOpId<FormFieldElement>;
|
||||
let autofillFieldData: AutofillField;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<form id="validFormId">
|
||||
<input type="text" id="username-field" placeholder="username" />
|
||||
<input type="password" id="password-field" placeholder="password" />
|
||||
</form>
|
||||
`;
|
||||
|
||||
autofillFieldElement = document.getElementById(
|
||||
"username-field",
|
||||
) as ElementWithOpId<FormFieldElement>;
|
||||
autofillFieldElement.opid = "op-1";
|
||||
autofillFieldData = createAutofillFieldMock({
|
||||
opid: "username-field",
|
||||
form: "validFormId",
|
||||
placeholder: "username",
|
||||
elementNumber: 1,
|
||||
});
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
);
|
||||
autofillOverlayContentService["mostRecentlyFocusedField"] = autofillFieldElement;
|
||||
});
|
||||
|
||||
it("clears the user interaction event timeout", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "clearUserInteractionEventTimeout");
|
||||
|
||||
autofillOverlayContentService.destroy();
|
||||
|
||||
expect(autofillOverlayContentService["clearUserInteractionEventTimeout"]).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("de-registers all global event listeners", () => {
|
||||
jest.spyOn(globalThis.document, "removeEventListener");
|
||||
jest.spyOn(globalThis, "removeEventListener");
|
||||
jest.spyOn(autofillOverlayContentService as any, "removeOverlayRepositionEventListeners");
|
||||
|
||||
autofillOverlayContentService.destroy();
|
||||
|
||||
expect(globalThis.document.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.VISIBILITYCHANGE,
|
||||
autofillOverlayContentService["handleVisibilityChangeEvent"],
|
||||
);
|
||||
expect(globalThis.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.FOCUSOUT,
|
||||
autofillOverlayContentService["handleFormFieldBlurEvent"],
|
||||
);
|
||||
expect(
|
||||
autofillOverlayContentService["removeOverlayRepositionEventListeners"],
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("de-registers any event listeners that are attached to the form field elements", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "removeCachedFormFieldEventListeners");
|
||||
jest.spyOn(autofillFieldElement, "removeEventListener");
|
||||
jest.spyOn(autofillOverlayContentService["formFieldElements"], "delete");
|
||||
|
||||
autofillOverlayContentService.destroy();
|
||||
|
||||
expect(
|
||||
autofillOverlayContentService["removeCachedFormFieldEventListeners"],
|
||||
).toHaveBeenCalledWith(autofillFieldElement);
|
||||
expect(autofillFieldElement.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.BLUR,
|
||||
autofillOverlayContentService["handleFormFieldBlurEvent"],
|
||||
);
|
||||
expect(autofillFieldElement.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.KEYUP,
|
||||
autofillOverlayContentService["handleFormFieldKeyupEvent"],
|
||||
);
|
||||
expect(autofillOverlayContentService["formFieldElements"].delete).toHaveBeenCalledWith(
|
||||
autofillFieldElement,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("extension onMessage handlers", () => {
|
||||
describe("openAutofillInlineMenu message handler", () => {
|
||||
let autofillFieldElement: ElementWithOpId<FormFieldElement>;
|
||||
@ -1563,7 +1484,7 @@ describe("AutofillOverlayContentService", () => {
|
||||
|
||||
describe("getSubFrameOffsetsFromWindowMessage", () => {
|
||||
it("sends a message to the parent to calculate the sub frame positioning", () => {
|
||||
jest.spyOn(globalThis.parent, "postMessage");
|
||||
jest.spyOn(globalThis.parent, "postMessage").mockImplementation();
|
||||
const subFrameId = 10;
|
||||
|
||||
sendMockExtensionMessage({
|
||||
@ -1580,6 +1501,7 @@ describe("AutofillOverlayContentService", () => {
|
||||
left: 0,
|
||||
top: 0,
|
||||
parentFrameIds: [],
|
||||
subFrameDepth: 0,
|
||||
},
|
||||
},
|
||||
"*",
|
||||
@ -1593,6 +1515,29 @@ describe("AutofillOverlayContentService", () => {
|
||||
document.body.innerHTML = ``;
|
||||
});
|
||||
|
||||
it("destroys the inline menu listeners on the origin frame if the depth exceeds the threshold", async () => {
|
||||
document.body.innerHTML = `<iframe id="subframe" src="https://example.com/"></iframe>`;
|
||||
const iframe = document.querySelector("iframe") as HTMLIFrameElement;
|
||||
const subFrameData = {
|
||||
url: "https://example.com/",
|
||||
frameId: 10,
|
||||
left: 0,
|
||||
top: 0,
|
||||
parentFrameIds: [1, 2, 3],
|
||||
subFrameDepth: MAX_SUB_FRAME_DEPTH,
|
||||
};
|
||||
const event = mock<MessageEvent>();
|
||||
// @ts-expect-error - Need to mock the source to be the iframe content window
|
||||
event.source = iframe.contentWindow;
|
||||
event.data.subFrameData = subFrameData;
|
||||
sendExtensionMessageSpy.mockResolvedValue(4);
|
||||
|
||||
await autofillOverlayContentService["calculateSubFramePositioning"](event);
|
||||
await flushPromises();
|
||||
|
||||
expect(globalThis.parent.postMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calculates the sub frame offset for the current frame and sends those values to the parent if not in the top frame", async () => {
|
||||
Object.defineProperty(window, "top", {
|
||||
value: null,
|
||||
@ -1606,6 +1551,7 @@ describe("AutofillOverlayContentService", () => {
|
||||
left: 0,
|
||||
top: 0,
|
||||
parentFrameIds: [1, 2, 3],
|
||||
subFrameDepth: 0,
|
||||
};
|
||||
const event = mock<MessageEvent>();
|
||||
// @ts-expect-error - Need to mock the source to be the iframe content window
|
||||
@ -1625,6 +1571,7 @@ describe("AutofillOverlayContentService", () => {
|
||||
parentFrameIds: [1, 2, 3, 4],
|
||||
top: 2,
|
||||
url: "https://example.com/",
|
||||
subFrameDepth: 1,
|
||||
},
|
||||
},
|
||||
"*",
|
||||
@ -1640,6 +1587,7 @@ describe("AutofillOverlayContentService", () => {
|
||||
left: 0,
|
||||
top: 0,
|
||||
parentFrameIds: [1, 2, 3],
|
||||
subFrameDepth: 1,
|
||||
};
|
||||
const event = mock<MessageEvent>();
|
||||
// @ts-expect-error - Need to mock the source to be the iframe content window
|
||||
@ -1656,13 +1604,14 @@ describe("AutofillOverlayContentService", () => {
|
||||
top: 2,
|
||||
url: "https://example.com/",
|
||||
parentFrameIds: [1, 2, 3],
|
||||
subFrameDepth: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("checkMostRecentlyFocusedFieldHasValue", () => {
|
||||
describe("checkMostRecentlyFocusedFieldHasValue message handler", () => {
|
||||
it("returns true if the most recently focused field has a truthy value", async () => {
|
||||
autofillOverlayContentService["mostRecentlyFocusedField"] = mock<
|
||||
ElementWithOpId<FormFieldElement>
|
||||
@ -1680,5 +1629,98 @@ describe("AutofillOverlayContentService", () => {
|
||||
expect(sendResponseSpy).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("destroyAutofillInlineMenuListeners message handler", () => {
|
||||
it("destroys the inline menu listeners", () => {
|
||||
jest.spyOn(autofillOverlayContentService, "destroy");
|
||||
|
||||
sendMockExtensionMessage({ command: "destroyAutofillInlineMenuListeners" });
|
||||
|
||||
expect(autofillOverlayContentService.destroy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("destroy", () => {
|
||||
let autofillFieldElement: ElementWithOpId<FormFieldElement>;
|
||||
let autofillFieldData: AutofillField;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<form id="validFormId">
|
||||
<input type="text" id="username-field" placeholder="username" />
|
||||
<input type="password" id="password-field" placeholder="password" />
|
||||
</form>
|
||||
`;
|
||||
|
||||
autofillFieldElement = document.getElementById(
|
||||
"username-field",
|
||||
) as ElementWithOpId<FormFieldElement>;
|
||||
autofillFieldElement.opid = "op-1";
|
||||
autofillFieldData = createAutofillFieldMock({
|
||||
opid: "username-field",
|
||||
form: "validFormId",
|
||||
placeholder: "username",
|
||||
elementNumber: 1,
|
||||
});
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
autofillOverlayContentService.setupInlineMenuListenerOnField(
|
||||
autofillFieldElement,
|
||||
autofillFieldData,
|
||||
);
|
||||
autofillOverlayContentService["mostRecentlyFocusedField"] = autofillFieldElement;
|
||||
});
|
||||
|
||||
it("clears the user interaction event timeout", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "clearUserInteractionEventTimeout");
|
||||
|
||||
autofillOverlayContentService.destroy();
|
||||
|
||||
expect(autofillOverlayContentService["clearUserInteractionEventTimeout"]).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("de-registers all global event listeners", () => {
|
||||
jest.spyOn(globalThis.document, "removeEventListener");
|
||||
jest.spyOn(globalThis, "removeEventListener");
|
||||
jest.spyOn(autofillOverlayContentService as any, "removeOverlayRepositionEventListeners");
|
||||
|
||||
autofillOverlayContentService.destroy();
|
||||
|
||||
expect(globalThis.document.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.VISIBILITYCHANGE,
|
||||
autofillOverlayContentService["handleVisibilityChangeEvent"],
|
||||
);
|
||||
expect(globalThis.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.FOCUSOUT,
|
||||
autofillOverlayContentService["handleFormFieldBlurEvent"],
|
||||
);
|
||||
expect(
|
||||
autofillOverlayContentService["removeOverlayRepositionEventListeners"],
|
||||
).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("de-registers any event listeners that are attached to the form field elements", () => {
|
||||
jest.spyOn(autofillOverlayContentService as any, "removeCachedFormFieldEventListeners");
|
||||
jest.spyOn(autofillFieldElement, "removeEventListener");
|
||||
jest.spyOn(autofillOverlayContentService["formFieldElements"], "delete");
|
||||
|
||||
autofillOverlayContentService.destroy();
|
||||
|
||||
expect(
|
||||
autofillOverlayContentService["removeCachedFormFieldEventListeners"],
|
||||
).toHaveBeenCalledWith(autofillFieldElement);
|
||||
expect(autofillFieldElement.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.BLUR,
|
||||
autofillOverlayContentService["handleFormFieldBlurEvent"],
|
||||
);
|
||||
expect(autofillFieldElement.removeEventListener).toHaveBeenCalledWith(
|
||||
EVENTS.KEYUP,
|
||||
autofillOverlayContentService["handleFormFieldKeyupEvent"],
|
||||
);
|
||||
expect(autofillOverlayContentService["formFieldElements"].delete).toHaveBeenCalledWith(
|
||||
autofillFieldElement,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user