import "@webcomponents/custom-elements"; import "lit/polyfill-support.js"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { EVENTS } from "../../../constants"; import { logoIcon, logoLockedIcon } from "../../../utils/svg-icons"; import { buildSvgDomElement } from "../../../utils/utils"; import { InitAutofillOverlayButtonMessage, OverlayButtonWindowMessageHandlers, } from "../../abstractions/autofill-overlay-button"; import AutofillOverlayPageElement from "../shared/autofill-overlay-page-element"; class AutofillOverlayButton extends AutofillOverlayPageElement { private authStatus: AuthenticationStatus = AuthenticationStatus.LoggedOut; private readonly buttonElement: HTMLButtonElement; private readonly logoIconElement: HTMLElement; private readonly logoLockedIconElement: HTMLElement; private readonly overlayButtonWindowMessageHandlers: OverlayButtonWindowMessageHandlers = { initAutofillOverlayButton: ({ message }) => this.initAutofillOverlayButton(message), checkAutofillOverlayButtonFocused: () => this.checkButtonFocused(), updateAutofillOverlayButtonAuthStatus: ({ message }) => this.updateAuthStatus(message.authStatus), }; constructor() { super(); this.buttonElement = globalThis.document.createElement("button"); this.setupGlobalListeners(this.overlayButtonWindowMessageHandlers); this.logoIconElement = buildSvgDomElement(logoIcon); this.logoIconElement.classList.add("overlay-button-svg-icon", "logo-icon"); this.logoLockedIconElement = buildSvgDomElement(logoLockedIcon); this.logoLockedIconElement.classList.add("overlay-button-svg-icon", "logo-locked-icon"); } /** * Initializes the overlay button. Facilitates ensuring that the page * is set up with the expected styles and translations. * * @param authStatus - The authentication status of the user * @param styleSheetUrl - The URL of the stylesheet to apply to the page * @param translations - The translations to apply to the page * @private */ private async initAutofillOverlayButton({ authStatus, styleSheetUrl, translations, }: InitAutofillOverlayButtonMessage) { const linkElement = this.initOverlayPage("button", styleSheetUrl, translations); this.buttonElement.tabIndex = -1; this.buttonElement.type = "button"; this.buttonElement.classList.add("overlay-button"); this.buttonElement.setAttribute( "aria-label", this.getTranslation("toggleBitwardenVaultOverlay"), ); this.buttonElement.addEventListener(EVENTS.CLICK, this.handleButtonElementClick); this.updateAuthStatus(authStatus); this.shadowDom.append(linkElement, this.buttonElement); } /** * Updates the authentication status of the user. This will update the icon * displayed on the button. * * @param authStatus - The authentication status of the user */ private updateAuthStatus(authStatus: AuthenticationStatus) { this.authStatus = authStatus; this.buttonElement.innerHTML = ""; const iconElement = this.authStatus === AuthenticationStatus.Unlocked ? this.logoIconElement : this.logoLockedIconElement; this.buttonElement.append(iconElement); } /** * Handles a click event on the button element. Posts a message to the * parent window indicating that the button was clicked. */ private handleButtonElementClick = () => { this.postMessageToParent({ command: "overlayButtonClicked" }); }; /** * Checks if the button is focused. If it is not, then it posts a message * to the parent window indicating that the overlay should be closed. */ private checkButtonFocused() { if (globalThis.document.hasFocus()) { return; } this.postMessageToParent({ command: "closeAutofillOverlay" }); } } export default AutofillOverlayButton;