mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 09:43:23 +00:00
[PM-6546] Implementing a methodology where Firefox browsers render the overlay UI within a div element rather than custom web component
This commit is contained in:
@@ -1,8 +1,15 @@
|
|||||||
import AutofillOverlayButtonIframe from "./autofill-overlay-button-iframe";
|
import AutofillOverlayButtonIframe from "./autofill-overlay-button-iframe";
|
||||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
|
||||||
|
|
||||||
describe("AutofillOverlayButtonIframe", () => {
|
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(() => {
|
afterAll(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
@@ -13,7 +20,7 @@ describe("AutofillOverlayButtonIframe", () => {
|
|||||||
|
|
||||||
const iframe = document.querySelector("autofill-overlay-button-iframe");
|
const iframe = document.querySelector("autofill-overlay-button-iframe");
|
||||||
|
|
||||||
expect(iframe).toBeInstanceOf(AutofillOverlayButtonIframe);
|
expect(iframe).toBeInstanceOf(HTMLElement);
|
||||||
expect(iframe).toBeInstanceOf(AutofillOverlayIframeElement);
|
expect(iframe.shadowRoot).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { AutofillOverlayPort } from "../../utils/autofill-overlay.enum";
|
|||||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
||||||
|
|
||||||
class AutofillOverlayButtonIframe extends AutofillOverlayIframeElement {
|
class AutofillOverlayButtonIframe extends AutofillOverlayIframeElement {
|
||||||
constructor() {
|
constructor(element: HTMLElement) {
|
||||||
super(
|
super(
|
||||||
|
element,
|
||||||
"overlay/button.html",
|
"overlay/button.html",
|
||||||
AutofillOverlayPort.Button,
|
AutofillOverlayPort.Button,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,21 @@ import AutofillOverlayIframeService from "./autofill-overlay-iframe.service";
|
|||||||
jest.mock("./autofill-overlay-iframe.service");
|
jest.mock("./autofill-overlay-iframe.service");
|
||||||
|
|
||||||
describe("AutofillOverlayIframeElement", () => {
|
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(() => {
|
afterAll(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import AutofillOverlayIframeService from "./autofill-overlay-iframe.service";
|
import AutofillOverlayIframeService from "./autofill-overlay-iframe.service";
|
||||||
|
|
||||||
class AutofillOverlayIframeElement extends HTMLElement {
|
class AutofillOverlayIframeElement {
|
||||||
constructor(
|
constructor(
|
||||||
|
element: HTMLElement,
|
||||||
iframePath: string,
|
iframePath: string,
|
||||||
portName: string,
|
portName: string,
|
||||||
initStyles: Partial<CSSStyleDeclaration>,
|
initStyles: Partial<CSSStyleDeclaration>,
|
||||||
iframeTitle: string,
|
iframeTitle: string,
|
||||||
ariaAlert?: string,
|
ariaAlert?: string,
|
||||||
) {
|
) {
|
||||||
super();
|
const shadow: ShadowRoot = element.attachShadow({ mode: "closed" });
|
||||||
|
|
||||||
const shadow: ShadowRoot = this.attachShadow({ mode: "closed" });
|
|
||||||
const autofillOverlayIframeService = new AutofillOverlayIframeService(
|
const autofillOverlayIframeService = new AutofillOverlayIframeService(
|
||||||
iframePath,
|
iframePath,
|
||||||
portName,
|
portName,
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
|
||||||
import AutofillOverlayListIframe from "./autofill-overlay-list-iframe";
|
import AutofillOverlayListIframe from "./autofill-overlay-list-iframe";
|
||||||
|
|
||||||
describe("AutofillOverlayListIframe", () => {
|
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(() => {
|
afterAll(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
@@ -13,7 +20,7 @@ describe("AutofillOverlayListIframe", () => {
|
|||||||
|
|
||||||
const iframe = document.querySelector("autofill-overlay-list-iframe");
|
const iframe = document.querySelector("autofill-overlay-list-iframe");
|
||||||
|
|
||||||
expect(iframe).toBeInstanceOf(AutofillOverlayListIframe);
|
expect(iframe).toBeInstanceOf(HTMLElement);
|
||||||
expect(iframe).toBeInstanceOf(AutofillOverlayIframeElement);
|
expect(iframe.shadowRoot).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import { AutofillOverlayPort } from "../../utils/autofill-overlay.enum";
|
|||||||
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
import AutofillOverlayIframeElement from "./autofill-overlay-iframe-element";
|
||||||
|
|
||||||
class AutofillOverlayListIframe extends AutofillOverlayIframeElement {
|
class AutofillOverlayListIframe extends AutofillOverlayIframeElement {
|
||||||
constructor() {
|
constructor(element: HTMLElement) {
|
||||||
super(
|
super(
|
||||||
|
element,
|
||||||
"overlay/list.html",
|
"overlay/list.html",
|
||||||
AutofillOverlayPort.List,
|
AutofillOverlayPort.List,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -877,6 +877,44 @@ describe("AutofillOverlayContentService", () => {
|
|||||||
sender: "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", () => {
|
describe("focusMostRecentOverlayField", () => {
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
|||||||
isOverlayCiphersPopulated = false;
|
isOverlayCiphersPopulated = false;
|
||||||
pageDetailsUpdateRequired = false;
|
pageDetailsUpdateRequired = false;
|
||||||
autofillOverlayVisibility: number;
|
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 findTabs = tabbable;
|
||||||
private readonly sendExtensionMessage = sendExtensionMessage;
|
private readonly sendExtensionMessage = sendExtensionMessage;
|
||||||
private formFieldElements: Set<ElementWithOpId<FormFieldElement>> = new Set([]);
|
private formFieldElements: Set<ElementWithOpId<FormFieldElement>> = new Set([]);
|
||||||
@@ -593,6 +597,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
|||||||
private updateOverlayButtonPosition() {
|
private updateOverlayButtonPosition() {
|
||||||
if (!this.overlayButtonElement) {
|
if (!this.overlayButtonElement) {
|
||||||
this.createAutofillOverlayButton();
|
this.createAutofillOverlayButton();
|
||||||
|
this.updateCustomElementDefaultStyles(this.overlayButtonElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isOverlayButtonVisible) {
|
if (!this.isOverlayButtonVisible) {
|
||||||
@@ -613,6 +618,7 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
|||||||
private updateOverlayListPosition() {
|
private updateOverlayListPosition() {
|
||||||
if (!this.overlayListElement) {
|
if (!this.overlayListElement) {
|
||||||
this.createAutofillOverlayList();
|
this.createAutofillOverlayList();
|
||||||
|
this.updateCustomElementDefaultStyles(this.overlayListElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isOverlayListVisible) {
|
if (!this.isOverlayListVisible) {
|
||||||
@@ -765,11 +771,24 @@ class AutofillOverlayContentService implements AutofillOverlayContentServiceInte
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const customElementName = generateRandomCustomElementName();
|
if (this.isFirefoxBrowser) {
|
||||||
globalThis.customElements?.define(customElementName, AutofillOverlayButtonIframe);
|
this.overlayButtonElement = globalThis.document.createElement("div");
|
||||||
this.overlayButtonElement = globalThis.document.createElement(customElementName);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const customElementName = generateRandomCustomElementName();
|
if (this.isFirefoxBrowser) {
|
||||||
globalThis.customElements?.define(customElementName, AutofillOverlayListIframe);
|
this.overlayListElement = globalThis.document.createElement("div");
|
||||||
this.overlayListElement = globalThis.document.createElement(customElementName);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user