mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-04 18:37:45 +01:00
[PM-7980] Inline autofill menu is not shown inside dialog html tag (#11474)
* [PM-7980] Fix inline menu not showing inside dialog html tag * [PM-7980] Fix inline menu not showing inside dialog html tag * [PM-7980] Fixing an issue where a dialog element could potentially not represent itself in the #top-layer
This commit is contained in:
parent
55ee33206f
commit
32d12b3d6a
@ -367,7 +367,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.cardAndIdentityCiphers.size) {
|
if (!this.cardAndIdentityCiphers?.size) {
|
||||||
this.cardAndIdentityCiphers = null;
|
this.cardAndIdentityCiphers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
let autofillInlineMenuContentService: AutofillInlineMenuContentService;
|
let autofillInlineMenuContentService: AutofillInlineMenuContentService;
|
||||||
let autofillInit: AutofillInit;
|
let autofillInit: AutofillInit;
|
||||||
let sendExtensionMessageSpy: jest.SpyInstance;
|
let sendExtensionMessageSpy: jest.SpyInstance;
|
||||||
let observeBodyMutationsSpy: jest.SpyInstance;
|
let observeContainerMutationsSpy: jest.SpyInstance;
|
||||||
const waitForIdleCallback = () =>
|
const waitForIdleCallback = () =>
|
||||||
new Promise((resolve) => globalThis.requestIdleCallback(resolve));
|
new Promise((resolve) => globalThis.requestIdleCallback(resolve));
|
||||||
|
|
||||||
@ -25,8 +25,8 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
autofillInlineMenuContentService = new AutofillInlineMenuContentService();
|
autofillInlineMenuContentService = new AutofillInlineMenuContentService();
|
||||||
autofillInit = new AutofillInit(domQueryService, null, autofillInlineMenuContentService);
|
autofillInit = new AutofillInit(domQueryService, null, autofillInlineMenuContentService);
|
||||||
autofillInit.init();
|
autofillInit.init();
|
||||||
observeBodyMutationsSpy = jest.spyOn(
|
observeContainerMutationsSpy = jest.spyOn(
|
||||||
autofillInlineMenuContentService["bodyElementMutationObserver"] as any,
|
autofillInlineMenuContentService["containerElementMutationObserver"] as any,
|
||||||
"observe",
|
"observe",
|
||||||
);
|
);
|
||||||
sendExtensionMessageSpy = jest.spyOn(
|
sendExtensionMessageSpy = jest.spyOn(
|
||||||
@ -51,7 +51,7 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
describe("extension message handlers", () => {
|
describe("extension message handlers", () => {
|
||||||
describe("closeAutofillInlineMenu message handler", () => {
|
describe("closeAutofillInlineMenu message handler", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
observeBodyMutationsSpy.mockImplementation();
|
observeContainerMutationsSpy.mockImplementation();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closes the inline menu button", async () => {
|
it("closes the inline menu button", async () => {
|
||||||
@ -87,9 +87,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("closes both inline menu elements and removes the body element mutation observer", async () => {
|
it("closes both inline menu elements and removes the body element mutation observer", async () => {
|
||||||
const unobserveBodyElementSpy = jest.spyOn(
|
const unobserveContainerElementSpy = jest.spyOn(
|
||||||
autofillInlineMenuContentService as any,
|
autofillInlineMenuContentService as any,
|
||||||
"unobserveBodyElement",
|
"unobserveContainerElement",
|
||||||
);
|
);
|
||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "appendAutofillInlineMenuToDom",
|
command: "appendAutofillInlineMenuToDom",
|
||||||
@ -104,7 +104,7 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
command: "closeAutofillInlineMenu",
|
command: "closeAutofillInlineMenu",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(unobserveBodyElementSpy).toHaveBeenCalled();
|
expect(unobserveContainerElementSpy).toHaveBeenCalled();
|
||||||
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("autofillOverlayElementClosed", {
|
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("autofillOverlayElementClosed", {
|
||||||
overlayElement: AutofillOverlayElement.Button,
|
overlayElement: AutofillOverlayElement.Button,
|
||||||
});
|
});
|
||||||
@ -127,7 +127,7 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
.spyOn(autofillInlineMenuContentService as any, "isInlineMenuListVisible")
|
.spyOn(autofillInlineMenuContentService as any, "isInlineMenuListVisible")
|
||||||
.mockResolvedValue(true);
|
.mockResolvedValue(true);
|
||||||
jest.spyOn(globalThis.document.body, "appendChild");
|
jest.spyOn(globalThis.document.body, "appendChild");
|
||||||
observeBodyMutationsSpy.mockImplementation();
|
observeContainerMutationsSpy.mockImplementation();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("creating the inline menu button", () => {
|
describe("creating the inline menu button", () => {
|
||||||
@ -279,7 +279,8 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("handleBodyElementMutationObserverUpdate", () => {
|
describe("handleContainerElementMutationObserverUpdate", () => {
|
||||||
|
let mockMutationRecord: MockProxy<MutationRecord>;
|
||||||
let buttonElement: HTMLElement;
|
let buttonElement: HTMLElement;
|
||||||
let listElement: HTMLElement;
|
let listElement: HTMLElement;
|
||||||
let isInlineMenuListVisibleSpy: jest.SpyInstance;
|
let isInlineMenuListVisibleSpy: jest.SpyInstance;
|
||||||
@ -289,6 +290,7 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
<div class="overlay-button"></div>
|
<div class="overlay-button"></div>
|
||||||
<div class="overlay-list"></div>
|
<div class="overlay-list"></div>
|
||||||
`;
|
`;
|
||||||
|
mockMutationRecord = mock<MutationRecord>({ target: globalThis.document.body } as any);
|
||||||
buttonElement = document.querySelector(".overlay-button") as HTMLElement;
|
buttonElement = document.querySelector(".overlay-button") as HTMLElement;
|
||||||
listElement = document.querySelector(".overlay-list") as HTMLElement;
|
listElement = document.querySelector(".overlay-list") as HTMLElement;
|
||||||
autofillInlineMenuContentService["buttonElement"] = buttonElement;
|
autofillInlineMenuContentService["buttonElement"] = buttonElement;
|
||||||
@ -309,7 +311,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
autofillInlineMenuContentService["buttonElement"] = undefined;
|
autofillInlineMenuContentService["buttonElement"] = undefined;
|
||||||
autofillInlineMenuContentService["listElement"] = undefined;
|
autofillInlineMenuContentService["listElement"] = undefined;
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||||
@ -323,7 +327,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
)
|
)
|
||||||
.mockReturnValue(true);
|
.mockReturnValue(true);
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||||
@ -332,14 +338,18 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
it("skips re-arranging the DOM elements if the last child of the body is non-existent", async () => {
|
it("skips re-arranging the DOM elements if the last child of the body is non-existent", async () => {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("skips re-arranging the DOM elements if the last child of the body is the overlay list and the second to last child of the body is the overlay button", async () => {
|
it("skips re-arranging the DOM elements if the last child of the body is the overlay list and the second to last child of the body is the overlay button", async () => {
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||||
@ -349,7 +359,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
listElement.remove();
|
listElement.remove();
|
||||||
isInlineMenuListVisibleSpy.mockResolvedValue(false);
|
isInlineMenuListVisibleSpy.mockResolvedValue(false);
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
expect(globalThis.document.body.insertBefore).not.toHaveBeenCalled();
|
||||||
@ -359,7 +371,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
const injectedElement = document.createElement("div");
|
const injectedElement = document.createElement("div");
|
||||||
document.body.insertBefore(injectedElement, listElement);
|
document.body.insertBefore(injectedElement, listElement);
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
||||||
@ -371,7 +385,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
it("positions the overlay button before the overlay list if the elements have inserted in incorrect order", async () => {
|
it("positions the overlay button before the overlay list if the elements have inserted in incorrect order", async () => {
|
||||||
document.body.appendChild(buttonElement);
|
document.body.appendChild(buttonElement);
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
||||||
@ -384,7 +400,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
const injectedElement = document.createElement("div");
|
const injectedElement = document.createElement("div");
|
||||||
document.body.appendChild(injectedElement);
|
document.body.appendChild(injectedElement);
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
expect(globalThis.document.body.insertBefore).toHaveBeenCalledWith(
|
||||||
@ -409,7 +427,9 @@ describe("AutofillInlineMenuContentService", () => {
|
|||||||
1000,
|
1000,
|
||||||
);
|
);
|
||||||
|
|
||||||
await autofillInlineMenuContentService["handleBodyElementMutationObserverUpdate"]();
|
autofillInlineMenuContentService["handleContainerElementMutationObserverUpdate"]([
|
||||||
|
mockMutationRecord,
|
||||||
|
]);
|
||||||
await waitForIdleCallback();
|
await waitForIdleCallback();
|
||||||
|
|
||||||
expect(persistentLastChild.style.getPropertyValue("z-index")).toBe("2147483646");
|
expect(persistentLastChild.style.getPropertyValue("z-index")).toBe("2147483646");
|
||||||
|
@ -30,7 +30,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
private buttonElement: HTMLElement;
|
private buttonElement: HTMLElement;
|
||||||
private listElement: HTMLElement;
|
private listElement: HTMLElement;
|
||||||
private inlineMenuElementsMutationObserver: MutationObserver;
|
private inlineMenuElementsMutationObserver: MutationObserver;
|
||||||
private bodyElementMutationObserver: MutationObserver;
|
private containerElementMutationObserver: MutationObserver;
|
||||||
private mutationObserverIterations = 0;
|
private mutationObserverIterations = 0;
|
||||||
private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout;
|
private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout;
|
||||||
private handlePersistentLastChildOverrideTimeout: number | NodeJS.Timeout;
|
private handlePersistentLastChildOverrideTimeout: number | NodeJS.Timeout;
|
||||||
@ -102,7 +102,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unobserveBodyElement();
|
this.unobserveContainerElement();
|
||||||
this.closeInlineMenuButton();
|
this.closeInlineMenuButton();
|
||||||
this.closeInlineMenuList();
|
this.closeInlineMenuList();
|
||||||
};
|
};
|
||||||
@ -153,7 +153,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(await this.isInlineMenuButtonVisible())) {
|
if (!(await this.isInlineMenuButtonVisible())) {
|
||||||
this.appendInlineMenuElementToBody(this.buttonElement);
|
this.appendInlineMenuElementToDom(this.buttonElement);
|
||||||
this.updateInlineMenuElementIsVisibleStatus(AutofillOverlayElement.Button, true);
|
this.updateInlineMenuElementIsVisibleStatus(AutofillOverlayElement.Button, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(await this.isInlineMenuListVisible())) {
|
if (!(await this.isInlineMenuListVisible())) {
|
||||||
this.appendInlineMenuElementToBody(this.listElement);
|
this.appendInlineMenuElementToDom(this.listElement);
|
||||||
this.updateInlineMenuElementIsVisibleStatus(AutofillOverlayElement.List, true);
|
this.updateInlineMenuElementIsVisibleStatus(AutofillOverlayElement.List, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,8 +196,15 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
*
|
*
|
||||||
* @param element - The inline menu element to append to the body element.
|
* @param element - The inline menu element to append to the body element.
|
||||||
*/
|
*/
|
||||||
private appendInlineMenuElementToBody(element: HTMLElement) {
|
private appendInlineMenuElementToDom(element: HTMLElement) {
|
||||||
this.observeBodyElement();
|
const parentDialogElement = globalThis.document.activeElement?.closest("dialog");
|
||||||
|
if (parentDialogElement && parentDialogElement.open && parentDialogElement.matches(":modal")) {
|
||||||
|
this.observeContainerElement(parentDialogElement);
|
||||||
|
parentDialogElement.appendChild(element);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.observeContainerElement(globalThis.document.body);
|
||||||
globalThis.document.body.appendChild(element);
|
globalThis.document.body.appendChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +283,8 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
this.handleInlineMenuElementMutationObserverUpdate,
|
this.handleInlineMenuElementMutationObserverUpdate,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.bodyElementMutationObserver = new MutationObserver(
|
this.containerElementMutationObserver = new MutationObserver(
|
||||||
this.handleBodyElementMutationObserverUpdate,
|
this.handleContainerElementMutationObserverUpdate,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -306,19 +313,17 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up a mutation observer for the body element. The mutation observer is used
|
* Sets up a mutation observer for the element which contains the inline menu.
|
||||||
* to ensure that the inline menu elements are always present at the bottom of the
|
|
||||||
* body element.
|
|
||||||
*/
|
*/
|
||||||
private observeBodyElement() {
|
private observeContainerElement(element: HTMLElement) {
|
||||||
this.bodyElementMutationObserver?.observe(globalThis.document.body, { childList: true });
|
this.containerElementMutationObserver?.observe(element, { childList: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects the mutation observer for the body element.
|
* Disconnects the mutation observer for the element which contains the inline menu.
|
||||||
*/
|
*/
|
||||||
private unobserveBodyElement() {
|
private unobserveContainerElement() {
|
||||||
this.bodyElementMutationObserver?.disconnect();
|
this.containerElementMutationObserver?.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -370,11 +375,11 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the mutation observer update for the body element. This method will
|
* Handles the mutation observer update for the element that contains the inline menu.
|
||||||
* ensure that the inline menu elements are always present at the bottom of the
|
* This method will ensure that the inline menu elements are always present at the
|
||||||
* body element.
|
* bottom of the container.
|
||||||
*/
|
*/
|
||||||
private handleBodyElementMutationObserverUpdate = () => {
|
private handleContainerElementMutationObserverUpdate = (mutations: MutationRecord[]) => {
|
||||||
if (
|
if (
|
||||||
(!this.buttonElement && !this.listElement) ||
|
(!this.buttonElement && !this.listElement) ||
|
||||||
this.isTriggeringExcessiveMutationObserverIterations()
|
this.isTriggeringExcessiveMutationObserverIterations()
|
||||||
@ -382,15 +387,18 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestIdleCallbackPolyfill(this.processBodyElementMutation, { timeout: 500 });
|
const containerElement = mutations[0].target as HTMLElement;
|
||||||
|
requestIdleCallbackPolyfill(() => this.processContainerElementMutation(containerElement), {
|
||||||
|
timeout: 500,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the mutation of the body element. Will trigger when an
|
* Processes the mutation of the element that contains the inline menu. Will trigger when an
|
||||||
* idle moment in the execution of the main thread is detected.
|
* idle moment in the execution of the main thread is detected.
|
||||||
*/
|
*/
|
||||||
private processBodyElementMutation = async () => {
|
private processContainerElementMutation = async (containerElement: HTMLElement) => {
|
||||||
const lastChild = globalThis.document.body.lastElementChild;
|
const lastChild = containerElement.lastElementChild;
|
||||||
const secondToLastChild = lastChild?.previousElementSibling;
|
const secondToLastChild = lastChild?.previousElementSibling;
|
||||||
const lastChildIsInlineMenuList = lastChild === this.listElement;
|
const lastChildIsInlineMenuList = lastChild === this.listElement;
|
||||||
const lastChildIsInlineMenuButton = lastChild === this.buttonElement;
|
const lastChildIsInlineMenuButton = lastChild === this.buttonElement;
|
||||||
@ -424,11 +432,11 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
|
|||||||
(lastChildIsInlineMenuList && !secondToLastChildIsInlineMenuButton) ||
|
(lastChildIsInlineMenuList && !secondToLastChildIsInlineMenuButton) ||
|
||||||
(lastChildIsInlineMenuButton && isInlineMenuListVisible)
|
(lastChildIsInlineMenuButton && isInlineMenuListVisible)
|
||||||
) {
|
) {
|
||||||
globalThis.document.body.insertBefore(this.buttonElement, this.listElement);
|
containerElement.insertBefore(this.buttonElement, this.listElement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
globalThis.document.body.insertBefore(lastChild, this.buttonElement);
|
containerElement.insertBefore(lastChild, this.buttonElement);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
AutofillOverlayVisibility,
|
AutofillOverlayVisibility,
|
||||||
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
||||||
AUTOFILL_TRIGGER_FORM_FIELD_SUBMIT,
|
AUTOFILL_TRIGGER_FORM_FIELD_SUBMIT,
|
||||||
|
AUTOFILL_OVERLAY_HANDLE_SCROLL,
|
||||||
} from "@bitwarden/common/autofill/constants";
|
} from "@bitwarden/common/autofill/constants";
|
||||||
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
import { InlineMenuVisibilitySetting } from "@bitwarden/common/autofill/types";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
@ -1647,15 +1648,28 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
* the overlay elements on scroll or resize.
|
* the overlay elements on scroll or resize.
|
||||||
*/
|
*/
|
||||||
private setOverlayRepositionEventListeners() {
|
private setOverlayRepositionEventListeners() {
|
||||||
const handler = this.useEventHandlersMemo(
|
const repositionHandler = this.useEventHandlersMemo(
|
||||||
throttle(this.handleOverlayRepositionEvent, 250),
|
throttle(this.handleOverlayRepositionEvent, 250),
|
||||||
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
AUTOFILL_OVERLAY_HANDLE_REPOSITION,
|
||||||
);
|
);
|
||||||
globalThis.addEventListener(EVENTS.SCROLL, handler, {
|
|
||||||
|
const eventTargetDoesNotContainFocusedField = (element: Element) =>
|
||||||
|
typeof element?.contains === "function" && !element.contains(this.mostRecentlyFocusedField);
|
||||||
|
const scrollHandler = this.useEventHandlersMemo(
|
||||||
|
throttle((event) => {
|
||||||
|
if (eventTargetDoesNotContainFocusedField(event.target as Element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
repositionHandler(event);
|
||||||
|
}, 50),
|
||||||
|
AUTOFILL_OVERLAY_HANDLE_SCROLL,
|
||||||
|
);
|
||||||
|
|
||||||
|
globalThis.addEventListener(EVENTS.SCROLL, scrollHandler, {
|
||||||
capture: true,
|
capture: true,
|
||||||
passive: true,
|
passive: true,
|
||||||
});
|
});
|
||||||
globalThis.addEventListener(EVENTS.RESIZE, handler);
|
globalThis.addEventListener(EVENTS.RESIZE, repositionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1663,12 +1677,19 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
* the overlay elements on scroll or resize.
|
* the overlay elements on scroll or resize.
|
||||||
*/
|
*/
|
||||||
private removeOverlayRepositionEventListeners() {
|
private removeOverlayRepositionEventListeners() {
|
||||||
const handler = this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_REPOSITION];
|
globalThis.removeEventListener(
|
||||||
globalThis.removeEventListener(EVENTS.SCROLL, handler, {
|
EVENTS.SCROLL,
|
||||||
|
this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_SCROLL],
|
||||||
|
{
|
||||||
capture: true,
|
capture: true,
|
||||||
});
|
},
|
||||||
globalThis.removeEventListener(EVENTS.RESIZE, handler);
|
);
|
||||||
|
globalThis.removeEventListener(
|
||||||
|
EVENTS.RESIZE,
|
||||||
|
this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_REPOSITION],
|
||||||
|
);
|
||||||
|
|
||||||
|
delete this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_SCROLL];
|
||||||
delete this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_REPOSITION];
|
delete this.eventHandlersMemo[AUTOFILL_OVERLAY_HANDLE_REPOSITION];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@ export const NOTIFICATION_BAR_LIFESPAN_MS = 150000; // 150 seconds
|
|||||||
|
|
||||||
export const AUTOFILL_OVERLAY_HANDLE_REPOSITION = "autofill-overlay-handle-reposition-event";
|
export const AUTOFILL_OVERLAY_HANDLE_REPOSITION = "autofill-overlay-handle-reposition-event";
|
||||||
|
|
||||||
|
export const AUTOFILL_OVERLAY_HANDLE_SCROLL = "autofill-overlay-handle-scroll-event";
|
||||||
|
|
||||||
export const UPDATE_PASSKEYS_HEADINGS_ON_SCROLL = "update-passkeys-headings-on-scroll";
|
export const UPDATE_PASSKEYS_HEADINGS_ON_SCROLL = "update-passkeys-headings-on-scroll";
|
||||||
|
|
||||||
export const AUTOFILL_TRIGGER_FORM_FIELD_SUBMIT = "autofill-trigger-form-field-submit";
|
export const AUTOFILL_TRIGGER_FORM_FIELD_SUBMIT = "autofill-trigger-form-field-submit";
|
||||||
|
Loading…
Reference in New Issue
Block a user