diff --git a/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-button.ts b/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-button.ts index 380a1f5de2..b6b22be943 100644 --- a/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-button.ts +++ b/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-button.ts @@ -1,6 +1,6 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -type OverlayButtonMessage = { command: string }; +type OverlayButtonMessage = { command: string; colorScheme?: string }; type UpdateAuthStatusMessage = OverlayButtonMessage & { authStatus: AuthenticationStatus }; @@ -18,10 +18,12 @@ type OverlayButtonWindowMessageHandlers = { }: { message: UpdateAuthStatusMessage; }) => void; + updateOverlayPageColorScheme: ({ message }: { message: OverlayButtonMessage }) => void; }; export { UpdateAuthStatusMessage, + OverlayButtonMessage, InitAutofillOverlayButtonMessage, OverlayButtonWindowMessageHandlers, }; diff --git a/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-iframe.service.ts b/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-iframe.service.ts index 1f7009bece..0c4160a070 100644 --- a/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/abstractions/autofill-overlay-iframe.service.ts @@ -7,6 +7,7 @@ type AutofillOverlayIframeExtensionMessage = { type AutofillOverlayIframeWindowMessageHandlers = { [key: string]: CallableFunction; updateAutofillOverlayListHeight: (message: AutofillOverlayIframeExtensionMessage) => void; + getPageColorScheme: () => void; }; type AutofillOverlayIframeExtensionMessageParam = { diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts index 26dc50ecd0..d15404b5b0 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.spec.ts @@ -385,6 +385,46 @@ describe("AutofillOverlayIframeService", () => { expect(autofillOverlayIframeService["iframe"].style.height).toBe("300px"); }); + + describe("getPageColorScheme window message", () => { + afterEach(() => { + globalThis.document.head.innerHTML = ""; + }); + + it("gets and updates the overlay page color scheme", () => { + const colorSchemeMetaTag = globalThis.document.createElement("meta"); + colorSchemeMetaTag.setAttribute("name", "color-scheme"); + colorSchemeMetaTag.setAttribute("content", "dark"); + globalThis.document.head.append(colorSchemeMetaTag); + globalThis.dispatchEvent( + new MessageEvent("message", { + data: { command: "getPageColorScheme" }, + source: autofillOverlayIframeService["iframe"].contentWindow, + origin: "chrome-extension://id", + }), + ); + + expect(autofillOverlayIframeService["iframe"].contentWindow.postMessage).toBeCalledWith( + { command: "updateOverlayPageColorScheme", colorScheme: "dark" }, + "*", + ); + }); + + it("sends a normal color scheme if the color scheme meta tag is not present", () => { + globalThis.dispatchEvent( + new MessageEvent("message", { + data: { command: "getPageColorScheme" }, + source: autofillOverlayIframeService["iframe"].contentWindow, + origin: "chrome-extension://id", + }), + ); + + expect(autofillOverlayIframeService["iframe"].contentWindow.postMessage).toBeCalledWith( + { command: "updateOverlayPageColorScheme", colorScheme: "normal" }, + "*", + ); + }); + }); }); }); diff --git a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts index c878f961f1..29efc3cdc5 100644 --- a/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/iframe-content/autofill-overlay-iframe.service.ts @@ -43,6 +43,7 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf private readonly windowMessageHandlers: AutofillOverlayIframeWindowMessageHandlers = { updateAutofillOverlayListHeight: (message) => this.updateElementStyles(this.iframe, message.styles), + getPageColorScheme: () => this.updateOverlayPageColorScheme(), }; private readonly backgroundPortMessageHandlers: BackgroundPortMessageHandlers = { initAutofillOverlayList: ({ message }) => this.initAutofillOverlayList(message), @@ -238,6 +239,22 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf this.announceAriaAlert(); } + /** + * Gets the page color scheme meta tag and sends a message to the iframe + * to update its color scheme. Will default to "normal" if the meta tag + * does not exist. + */ + private updateOverlayPageColorScheme() { + const colorSchemeValue = globalThis.document + .querySelector("meta[name='color-scheme']") + ?.getAttribute("content"); + + this.iframe.contentWindow?.postMessage( + { command: "updateOverlayPageColorScheme", colorScheme: colorSchemeValue || "normal" }, + "*", + ); + } + /** * Handles messages sent from the iframe. If the message does not have a * specified handler set, it passes the message to the background script. diff --git a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts index c4927f4a97..ca7a714b23 100644 --- a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts +++ b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.spec.ts @@ -64,7 +64,9 @@ describe("AutofillOverlayButton", () => { postWindowMessage({ command: "checkAutofillOverlayButtonFocused" }); - expect(globalThis.parent.postMessage).not.toHaveBeenCalled(); + expect(globalThis.parent.postMessage).not.toHaveBeenCalledWith({ + command: "closeAutofillOverlay", + }); }); it("posts a message to close the autofill overlay if the element is not focused during the focus check", () => { @@ -88,5 +90,19 @@ describe("AutofillOverlayButton", () => { expect(autofillOverlayButton["authStatus"]).toBe(AuthenticationStatus.Unlocked); }); + + it("updates the page color scheme meta tag", () => { + const colorSchemeMetaTag = globalThis.document.createElement("meta"); + colorSchemeMetaTag.setAttribute("name", "color-scheme"); + colorSchemeMetaTag.setAttribute("content", "light"); + globalThis.document.head.append(colorSchemeMetaTag); + + postWindowMessage({ + command: "updateOverlayPageColorScheme", + colorScheme: "dark", + }); + + expect(colorSchemeMetaTag.getAttribute("content")).toBe("dark"); + }); }); }); diff --git a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts index bfb5708745..12c4f2540f 100644 --- a/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts +++ b/apps/browser/src/autofill/overlay/pages/button/autofill-overlay-button.ts @@ -7,6 +7,7 @@ import { buildSvgDomElement } from "../../../utils"; import { logoIcon, logoLockedIcon } from "../../../utils/svg-icons"; import { InitAutofillOverlayButtonMessage, + OverlayButtonMessage, OverlayButtonWindowMessageHandlers, } from "../../abstractions/autofill-overlay-button"; import AutofillOverlayPageElement from "../shared/autofill-overlay-page-element"; @@ -21,6 +22,7 @@ class AutofillOverlayButton extends AutofillOverlayPageElement { checkAutofillOverlayButtonFocused: () => this.checkButtonFocused(), updateAutofillOverlayButtonAuthStatus: ({ message }) => this.updateAuthStatus(message.authStatus), + updateOverlayPageColorScheme: ({ message }) => this.updatePageColorScheme(message), }; constructor() { @@ -61,6 +63,7 @@ class AutofillOverlayButton extends AutofillOverlayPageElement { this.getTranslation("toggleBitwardenVaultOverlay"), ); this.buttonElement.addEventListener(EVENTS.CLICK, this.handleButtonElementClick); + this.postMessageToParent({ command: "getPageColorScheme" }); this.updateAuthStatus(authStatus); @@ -84,6 +87,17 @@ class AutofillOverlayButton extends AutofillOverlayPageElement { this.buttonElement.append(iconElement); } + /** + * Handles updating the page color scheme meta tag. Ensures that the button + * does not present with a non-transparent background on dark mode pages. + * + * @param colorScheme - The color scheme of the iframe's parent page + */ + private updatePageColorScheme({ colorScheme }: OverlayButtonMessage) { + const colorSchemeMetaTag = globalThis.document.querySelector("meta[name='color-scheme']"); + colorSchemeMetaTag?.setAttribute("content", colorScheme); + } + /** * Handles a click event on the button element. Posts a message to the * parent window indicating that the button was clicked.