From c37eb259af430a02e2334aba3f312d0a5c9efcf2 Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Thu, 29 Feb 2024 10:14:34 -0600 Subject: [PATCH 1/3] [PM-6546] Fix issue with blurring of elements after autofill occurs --- .../insert-autofill-content.service.spec.ts | 16 ++++++++++++++-- .../services/insert-autofill-content.service.ts | 16 +++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts index b137d2cd7ce..ae41f1c9c24 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts @@ -552,17 +552,30 @@ describe("InsertAutofillContentService", () => { insertAutofillContentService as any, "simulateUserMouseClickAndFocusEventInteractions", ); + jest.spyOn(targetInput, "blur"); insertAutofillContentService["handleFocusOnFieldByOpidAction"]("__0"); expect( insertAutofillContentService["collectAutofillContentService"].getAutofillFieldElementByOpid, ).toBeCalledWith("__0"); + expect(targetInput.blur).not.toHaveBeenCalled(); expect( insertAutofillContentService["simulateUserMouseClickAndFocusEventInteractions"], ).toHaveBeenCalledWith(targetInput, true); expect(elementEventCount).toEqual(expectedElementEventCount); }); + + it("blurs the element if it is currently the active element before simulating click and focus events", () => { + const targetInput = document.querySelector('input[type="text"]') as FormElementWithAttribute; + targetInput.opid = "__0"; + targetInput.focus(); + jest.spyOn(targetInput, "blur"); + + insertAutofillContentService["handleFocusOnFieldByOpidAction"]("__0"); + + expect(targetInput.blur).toHaveBeenCalled(); + }); }); describe("insertValueIntoField", () => { @@ -709,7 +722,7 @@ describe("InsertAutofillContentService", () => { }); describe("triggerPostInsertEventsOnElement", () => { - it("triggers simulated event interactions and blurs the element after", () => { + it("triggers simulated event interactions", () => { const elementValue = "test"; document.body.innerHTML = ``; const element = document.getElementById("username") as FillableFormFieldElement; @@ -725,7 +738,6 @@ describe("InsertAutofillContentService", () => { expect(insertAutofillContentService["simulateInputElementChangedEvent"]).toHaveBeenCalledWith( element, ); - expect(element.blur).toHaveBeenCalled(); expect(element.value).toBe(elementValue); }); }); diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts index 32cf76f1a37..82af53f7ff6 100644 --- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts @@ -184,11 +184,18 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf /** * Handles finding an element by opid and triggering click and focus events on the element. - * @param {string} opid - * @private + * To ensure that we trigger a blur event correctly on a filled field, we first check if the + * element is already focused. If it is, we blur the element before focusing on it again. + * + * @param {string} opid - The opid of the element to focus on. */ private handleFocusOnFieldByOpidAction(opid: string) { const element = this.collectAutofillContentService.getAutofillFieldElementByOpid(opid); + + if (document.activeElement === element) { + element.blur(); + } + this.simulateUserMouseClickAndFocusEventInteractions(element, true); } @@ -281,7 +288,6 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf } this.simulateInputElementChangedEvent(element); - element.blur(); } /** @@ -378,10 +384,6 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf element.dispatchEvent(new Event(simulatedInputEvents[index], { bubbles: true })); } } - - private nodeIsElement(node: Node): node is HTMLElement { - return node.nodeType === Node.ELEMENT_NODE; - } } export default InsertAutofillContentService; From 61b375973622da17f619e42738b96e263aac3722 Mon Sep 17 00:00:00 2001 From: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:19:41 +0000 Subject: [PATCH 2/3] [PM-6334] Passing CollectionView or FolderView from Import component to ImportService (#8291) * Passing CollectionView or FolderView from Import component to ImportService * Corrected import service tests * Added tests to validate if the incorrect object type error is thrown on setImportTarget --- .../src/components/import.component.html | 4 +- .../services/import.service.abstraction.ts | 5 +- .../src/services/import.service.spec.ts | 66 ++++++++++++++----- libs/importer/src/services/import.service.ts | 34 +++++----- 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/libs/importer/src/components/import.component.html b/libs/importer/src/components/import.component.html index 6c24b80f92d..836a1d9a1a1 100644 --- a/libs/importer/src/components/import.component.html +++ b/libs/importer/src/components/import.component.html @@ -37,7 +37,7 @@ @@ -46,7 +46,7 @@ diff --git a/libs/importer/src/services/import.service.abstraction.ts b/libs/importer/src/services/import.service.abstraction.ts index 95208c9b99f..dc77e76390e 100644 --- a/libs/importer/src/services/import.service.abstraction.ts +++ b/libs/importer/src/services/import.service.abstraction.ts @@ -1,3 +1,6 @@ +import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view"; +import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; + import { Importer } from "../importers/importer"; import { ImportOption, ImportType } from "../models/import-options"; import { ImportResult } from "../models/import-result"; @@ -10,7 +13,7 @@ export abstract class ImportServiceAbstraction { importer: Importer, fileContents: string, organizationId?: string, - selectedImportTarget?: string, + selectedImportTarget?: FolderView | CollectionView, canAccessImportExport?: boolean, ) => Promise; getImporter: ( diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts index 6a2e0a339c7..a95b74d792c 100644 --- a/libs/importer/src/services/import.service.spec.ts +++ b/libs/importer/src/services/import.service.spec.ts @@ -86,7 +86,7 @@ describe("ImportService", () => { }); it("empty importTarget does nothing", async () => { - await importService["setImportTarget"](importResult, null, ""); + await importService["setImportTarget"](importResult, null, null); expect(importResult.folders.length).toBe(0); }); @@ -99,9 +99,9 @@ describe("ImportService", () => { Promise.resolve([mockImportTargetFolder]), ); - await importService["setImportTarget"](importResult, null, "myImportTarget"); + await importService["setImportTarget"](importResult, null, mockImportTargetFolder); expect(importResult.folders.length).toBe(1); - expect(importResult.folders[0].name).toBe("myImportTarget"); + expect(importResult.folders[0]).toBe(mockImportTargetFolder); }); const mockFolder1 = new FolderView(); @@ -119,16 +119,18 @@ describe("ImportService", () => { mockFolder2, ]); - const myImportTarget = "myImportTarget"; - importResult.folders.push(mockFolder1); importResult.folders.push(mockFolder2); - await importService["setImportTarget"](importResult, null, myImportTarget); + await importService["setImportTarget"](importResult, null, mockImportTargetFolder); expect(importResult.folders.length).toBe(3); - expect(importResult.folders[0].name).toBe(myImportTarget); - expect(importResult.folders[1].name).toBe(`${myImportTarget}/${mockFolder1.name}`); - expect(importResult.folders[2].name).toBe(`${myImportTarget}/${mockFolder2.name}`); + expect(importResult.folders[0]).toBe(mockImportTargetFolder); + expect(importResult.folders[1].name).toBe( + `${mockImportTargetFolder.name}/${mockFolder1.name}`, + ); + expect(importResult.folders[2].name).toBe( + `${mockImportTargetFolder.name}/${mockFolder2.name}`, + ); }); const mockImportTargetCollection = new CollectionView(); @@ -152,9 +154,13 @@ describe("ImportService", () => { mockCollection1, ]); - await importService["setImportTarget"](importResult, organizationId, "myImportTarget"); + await importService["setImportTarget"]( + importResult, + organizationId, + mockImportTargetCollection, + ); expect(importResult.collections.length).toBe(1); - expect(importResult.collections[0].name).toBe("myImportTarget"); + expect(importResult.collections[0]).toBe(mockImportTargetCollection); }); it("passing importTarget sets it as new root for all existing collections", async () => { @@ -164,16 +170,42 @@ describe("ImportService", () => { mockCollection2, ]); - const myImportTarget = "myImportTarget"; - importResult.collections.push(mockCollection1); importResult.collections.push(mockCollection2); - await importService["setImportTarget"](importResult, organizationId, myImportTarget); + await importService["setImportTarget"]( + importResult, + organizationId, + mockImportTargetCollection, + ); expect(importResult.collections.length).toBe(3); - expect(importResult.collections[0].name).toBe(myImportTarget); - expect(importResult.collections[1].name).toBe(`${myImportTarget}/${mockCollection1.name}`); - expect(importResult.collections[2].name).toBe(`${myImportTarget}/${mockCollection2.name}`); + expect(importResult.collections[0]).toBe(mockImportTargetCollection); + expect(importResult.collections[1].name).toBe( + `${mockImportTargetCollection.name}/${mockCollection1.name}`, + ); + expect(importResult.collections[2].name).toBe( + `${mockImportTargetCollection.name}/${mockCollection2.name}`, + ); + }); + + it("passing importTarget as null on setImportTarget with organizationId throws error", async () => { + const setImportTargetMethod = importService["setImportTarget"]( + null, + organizationId, + new Object() as FolderView, + ); + + await expect(setImportTargetMethod).rejects.toThrow("Error assigning target collection"); + }); + + it("passing importTarget as null on setImportTarget throws error", async () => { + const setImportTargetMethod = importService["setImportTarget"]( + null, + "", + new Object() as CollectionView, + ); + + await expect(setImportTargetMethod).rejects.toThrow("Error assigning target folder"); }); }); }); diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index e26b768ab67..a6fd233dcf6 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -110,7 +110,7 @@ export class ImportService implements ImportServiceAbstraction { importer: Importer, fileContents: string, organizationId: string = null, - selectedImportTarget: string = null, + selectedImportTarget: FolderView | CollectionView = null, canAccessImportExport: boolean, ): Promise { let importResult: ImportResult; @@ -147,11 +147,7 @@ export class ImportService implements ImportServiceAbstraction { } } - if ( - organizationId && - Utils.isNullOrWhitespace(selectedImportTarget) && - !canAccessImportExport - ) { + if (organizationId && !selectedImportTarget && !canAccessImportExport) { const hasUnassignedCollections = importResult.collectionRelationships.length < importResult.ciphers.length; if (hasUnassignedCollections) { @@ -428,29 +424,30 @@ export class ImportService implements ImportServiceAbstraction { private async setImportTarget( importResult: ImportResult, organizationId: string, - importTarget: string, + importTarget: FolderView | CollectionView, ) { - if (Utils.isNullOrWhitespace(importTarget)) { + if (!importTarget) { return; } if (organizationId) { - const collectionViews: CollectionView[] = await this.collectionService.getAllDecrypted(); - const targetCollection = collectionViews.find((c) => c.id === importTarget); + if (!(importTarget instanceof CollectionView)) { + throw new Error("Error assigning target collection"); + } const noCollectionRelationShips: [number, number][] = []; importResult.ciphers.forEach((c, index) => { if (!Array.isArray(c.collectionIds) || c.collectionIds.length == 0) { - c.collectionIds = [targetCollection.id]; + c.collectionIds = [importTarget.id]; noCollectionRelationShips.push([index, 0]); } }); const collections: CollectionView[] = [...importResult.collections]; - importResult.collections = [targetCollection]; + importResult.collections = [importTarget as CollectionView]; collections.map((x) => { const f = new CollectionView(); - f.name = `${targetCollection.name}/${x.name}`; + f.name = `${importTarget.name}/${x.name}`; importResult.collections.push(f); }); @@ -463,21 +460,22 @@ export class ImportService implements ImportServiceAbstraction { return; } - const folderViews = await this.folderService.getAllDecryptedFromState(); - const targetFolder = folderViews.find((f) => f.id === importTarget); + if (!(importTarget instanceof FolderView)) { + throw new Error("Error assigning target folder"); + } const noFolderRelationShips: [number, number][] = []; importResult.ciphers.forEach((c, index) => { if (Utils.isNullOrEmpty(c.folderId)) { - c.folderId = targetFolder.id; + c.folderId = importTarget.id; noFolderRelationShips.push([index, 0]); } }); const folders: FolderView[] = [...importResult.folders]; - importResult.folders = [targetFolder]; + importResult.folders = [importTarget as FolderView]; folders.map((x) => { - const newFolderName = `${targetFolder.name}/${x.name}`; + const newFolderName = `${importTarget.name}/${x.name}`; const f = new FolderView(); f.name = newFolderName; importResult.folders.push(f); From 0cdadf284b68dc52c32f80aac53500cc1654fe7e Mon Sep 17 00:00:00 2001 From: Cesar Gonzalez Date: Tue, 19 Mar 2024 13:46:55 -0500 Subject: [PATCH 3/3] [PM-6546] Implementing a methodology where Firefox browsers render the overlay UI within a div element rather than custom web component --- .../autofill-overlay-button-iframe.spec.ts | 15 ++++-- .../autofill-overlay-button-iframe.ts | 3 +- .../autofill-overlay-iframe-element.spec.ts | 16 ++++++- .../autofill-overlay-iframe-element.ts | 7 ++- .../autofill-overlay-list-iframe.spec.ts | 15 ++++-- .../autofill-overlay-list-iframe.ts | 3 +- .../autofill-overlay-content.service.spec.ts | 38 +++++++++++++++ .../autofill-overlay-content.service.ts | 48 +++++++++++++++---- 8 files changed, 122 insertions(+), 23 deletions(-) diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.spec.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.spec.ts index 8106f2698dc..5cba4e0c0e2 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.spec.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.spec.ts @@ -1,8 +1,15 @@ import AutofillOverlayButtonIframe from "./autofill-overlay-button-iframe"; -import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element"; describe("AutofillOverlayButtonIframe", () => { - window.customElements.define("autofill-overlay-button-iframe", AutofillOverlayButtonIframe); + window.customElements.define( + "autofill-overlay-button-iframe", + class extends HTMLElement { + constructor() { + super(); + new AutofillOverlayButtonIframe(this); + } + }, + ); afterAll(() => { jest.clearAllMocks(); @@ -13,7 +20,7 @@ describe("AutofillOverlayButtonIframe", () => { const iframe = document.querySelector("autofill-overlay-button-iframe"); - expect(iframe).toBeInstanceOf(AutofillOverlayButtonIframe); - expect(iframe).toBeInstanceOf(AutofillOverlayIframeElement); + expect(iframe).toBeInstanceOf(HTMLElement); + expect(iframe.shadowRoot).toBeDefined(); }); }); diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts index 813d8054704..4f5d64b3cb8 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-button-iframe.ts @@ -3,8 +3,9 @@ import { AutofillOverlayPort } from "../../utils/autofill-overlay.enum"; import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element"; class AutofillOverlayButtonIframe extends AutofillOverlayIframeElement { - constructor() { + constructor(element: HTMLElement) { super( + element, "overlay/button.html", AutofillOverlayPort.Button, { diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.spec.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.spec.ts index 7af5d973a92..71f7a290de1 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.spec.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.spec.ts @@ -4,7 +4,21 @@ import AutofillOverlayIframeService from "./autofill-overlay-iframe.service"; jest.mock("./autofill-overlay-iframe.service"); describe("AutofillOverlayIframeElement", () => { - window.customElements.define("autofill-overlay-iframe", AutofillOverlayIframeElement); + window.customElements.define( + "autofill-overlay-iframe", + class extends HTMLElement { + constructor() { + super(); + new AutofillOverlayIframeElement( + this, + "overlay/button.html", + "overlay/button", + { background: "transparent", border: "none" }, + "bitwardenOverlayButton", + ); + } + }, + ); afterAll(() => { jest.clearAllMocks(); diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts index 209834410f9..ed61c1eb8f1 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe-element.ts @@ -1,16 +1,15 @@ import AutofillOverlayIframeService from "./autofill-overlay-iframe.service"; -class AutofillOverlayIframeElement extends HTMLElement { +class AutofillOverlayIframeElement { constructor( + element: HTMLElement, iframePath: string, portName: string, initStyles: Partial, iframeTitle: string, ariaAlert?: string, ) { - super(); - - const shadow: ShadowRoot = this.attachShadow({ mode: "closed" }); + const shadow: ShadowRoot = element.attachShadow({ mode: "closed" }); const autofillOverlayIframeService = new AutofillOverlayIframeService( iframePath, portName, diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.spec.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.spec.ts index 5ba39eb0023..ec89697e75e 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.spec.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.spec.ts @@ -1,8 +1,15 @@ -import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element"; import AutofillOverlayListIframe from "./autofill-overlay-list-iframe"; describe("AutofillOverlayListIframe", () => { - window.customElements.define("autofill-overlay-list-iframe", AutofillOverlayListIframe); + window.customElements.define( + "autofill-overlay-list-iframe", + class extends HTMLElement { + constructor() { + super(); + new AutofillOverlayListIframe(this); + } + }, + ); afterAll(() => { jest.clearAllMocks(); @@ -13,7 +20,7 @@ describe("AutofillOverlayListIframe", () => { const iframe = document.querySelector("autofill-overlay-list-iframe"); - expect(iframe).toBeInstanceOf(AutofillOverlayListIframe); - expect(iframe).toBeInstanceOf(AutofillOverlayIframeElement); + expect(iframe).toBeInstanceOf(HTMLElement); + expect(iframe.shadowRoot).toBeDefined(); }); }); diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts index b60b618e4e7..23df6581546 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-list-iframe.ts @@ -3,8 +3,9 @@ import { AutofillOverlayPort } from "../../utils/autofill-overlay.enum"; import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element"; class AutofillOverlayListIframe extends AutofillOverlayIframeElement { - constructor() { + constructor(element: HTMLElement) { super( + element, "overlay/list.html", AutofillOverlayPort.List, { 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 8926f5b298e..9f3ffea142a 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 @@ -877,6 +877,44 @@ describe("AutofillOverlayContentService", () => { sender: "autofillOverlayContentService", }); }); + + it("builds the overlay elements as custom web components if the user's browser is not Firefox", () => { + let namesIndex = 0; + const customNames = ["op-autofill-overlay-button", "op-autofill-overlay-list"]; + + jest + .spyOn(autofillOverlayContentService as any, "generateRandomCustomElementName") + .mockImplementation(() => { + if (namesIndex > 1) { + return ""; + } + const customName = customNames[namesIndex]; + namesIndex++; + + return customName; + }); + autofillOverlayContentService["isFirefoxBrowser"] = false; + + autofillOverlayContentService.openAutofillOverlay(); + + expect(autofillOverlayContentService["overlayButtonElement"]).toBeInstanceOf(HTMLElement); + expect(autofillOverlayContentService["overlayButtonElement"].tagName).toEqual( + customNames[0].toUpperCase(), + ); + expect(autofillOverlayContentService["overlayListElement"]).toBeInstanceOf(HTMLElement); + expect(autofillOverlayContentService["overlayListElement"].tagName).toEqual( + customNames[1].toUpperCase(), + ); + }); + + it("builds the overlay elements as `div` elements if the user's browser is Firefox", () => { + autofillOverlayContentService["isFirefoxBrowser"] = true; + + autofillOverlayContentService.openAutofillOverlay(); + + expect(autofillOverlayContentService["overlayButtonElement"]).toBeInstanceOf(HTMLDivElement); + expect(autofillOverlayContentService["overlayListElement"]).toBeInstanceOf(HTMLDivElement); + }); }); describe("focusMostRecentOverlayField", () => { 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 2cf063a5ba8..79abdc39381 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -30,6 +30,10 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte isOverlayCiphersPopulated = false; pageDetailsUpdateRequired = false; autofillOverlayVisibility: number; + private isFirefoxBrowser = + globalThis.navigator.userAgent.indexOf(" Firefox/") !== -1 || + globalThis.navigator.userAgent.indexOf(" Gecko/") !== -1; + private readonly generateRandomCustomElementName = generateRandomCustomElementName; private readonly findTabs = tabbable; private readonly sendExtensionMessage = sendExtensionMessage; private formFieldElements: Set> = new Set([]); @@ -593,6 +597,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte private updateOverlayButtonPosition() { if (!this.overlayButtonElement) { this.createAutofillOverlayButton(); + this.updateCustomElementDefaultStyles(this.overlayButtonElement); } if (!this.isOverlayButtonVisible) { @@ -613,6 +618,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte private updateOverlayListPosition() { if (!this.overlayListElement) { this.createAutofillOverlayList(); + this.updateCustomElementDefaultStyles(this.overlayListElement); } if (!this.isOverlayListVisible) { @@ -765,11 +771,24 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte return; } - const customElementName = generateRandomCustomElementName(); - globalThis.customElements?.define(customElementName, AutofillOverlayButtonIframe); - this.overlayButtonElement = globalThis.document.createElement(customElementName); + if (this.isFirefoxBrowser) { + this.overlayButtonElement = globalThis.document.createElement("div"); + new AutofillOverlayButtonIframe(this.overlayButtonElement); - this.updateCustomElementDefaultStyles(this.overlayButtonElement); + return; + } + + const customElementName = this.generateRandomCustomElementName(); + globalThis.customElements?.define( + customElementName, + class extends HTMLElement { + constructor() { + super(); + new AutofillOverlayButtonIframe(this); + } + }, + ); + this.overlayButtonElement = globalThis.document.createElement(customElementName); } /** @@ -781,11 +800,24 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte return; } - const customElementName = generateRandomCustomElementName(); - globalThis.customElements?.define(customElementName, AutofillOverlayListIframe); - this.overlayListElement = globalThis.document.createElement(customElementName); + if (this.isFirefoxBrowser) { + this.overlayListElement = globalThis.document.createElement("div"); + new AutofillOverlayListIframe(this.overlayListElement); - this.updateCustomElementDefaultStyles(this.overlayListElement); + return; + } + + const customElementName = this.generateRandomCustomElementName(); + globalThis.customElements?.define( + customElementName, + class extends HTMLElement { + constructor() { + super(); + new AutofillOverlayListIframe(this); + } + }, + ); + this.overlayListElement = globalThis.document.createElement(customElementName); } /**