/
autofill-overlay-button.ts
122 lines (104 loc) · 4.46 KB
/
autofill-overlay-button.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import "@webcomponents/custom-elements";
import "lit/polyfill-support.js";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { EVENTS } from "@bitwarden/common/autofill/constants";
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";
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),
updateOverlayPageColorScheme: ({ message }) => this.updatePageColorScheme(message),
};
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.postMessageToParent({ command: "getPageColorScheme" });
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 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.
*/
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;