mirror of
https://github.com/bitwarden/browser
synced 2025-12-18 17:23:37 +00:00
[PM-5189] Refactoring implementation
This commit is contained in:
@@ -24,7 +24,7 @@ const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers
|
|||||||
globalThis.addEventListener("load", load);
|
globalThis.addEventListener("load", load);
|
||||||
function load() {
|
function load() {
|
||||||
setupWindowMessageListener();
|
setupWindowMessageListener();
|
||||||
postMessageToConnector({ command: "initNotificationBar" });
|
postMessageToParent({ command: "initNotificationBar" });
|
||||||
}
|
}
|
||||||
|
|
||||||
function initNotificationBar(message: NotificationBarWindowMessage) {
|
function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||||
@@ -392,6 +392,6 @@ function setNotificationBarTheme() {
|
|||||||
document.documentElement.classList.add(`theme_${theme}`);
|
document.documentElement.classList.add(`theme_${theme}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function postMessageToConnector(message: NotificationBarWindowMessage) {
|
function postMessageToParent(message: NotificationBarWindowMessage) {
|
||||||
globalThis.parent.postMessage(message, windowMessageOrigin || "*");
|
globalThis.parent.postMessage(message, windowMessageOrigin || "*");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
|
|
||||||
|
import { OverlayCipherData } from "../../background/abstractions/overlay.background";
|
||||||
|
|
||||||
|
type AutofillOverlayMenuContainerMessage = {
|
||||||
|
command: string;
|
||||||
|
portKey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InitOverlayElementMessage = AutofillOverlayMenuContainerMessage & {
|
||||||
|
iframeUrl?: string;
|
||||||
|
pageTitle?: string;
|
||||||
|
authStatus?: AuthenticationStatus;
|
||||||
|
styleSheetUrl?: string;
|
||||||
|
theme?: string;
|
||||||
|
translations?: Record<string, string>;
|
||||||
|
ciphers?: OverlayCipherData[];
|
||||||
|
portName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AutofillOverlayMenuContainerWindowMessageHandlers = {
|
||||||
|
[key: string]: CallableFunction;
|
||||||
|
initAutofillOverlayList: (message: InitOverlayElementMessage) => void;
|
||||||
|
initAutofillOverlayButton: (message: InitOverlayElementMessage) => void;
|
||||||
|
};
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf
|
|||||||
private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout;
|
private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout;
|
||||||
private readonly backgroundPortMessageHandlers: BackgroundPortMessageHandlers = {
|
private readonly backgroundPortMessageHandlers: BackgroundPortMessageHandlers = {
|
||||||
initAutofillOverlayButton: ({ message }) => this.initAutofillOverlay(message),
|
initAutofillOverlayButton: ({ message }) => this.initAutofillOverlay(message),
|
||||||
initAutofillOverlayList: ({ message }) => this.initAutofillOverlayList(message),
|
initAutofillOverlayList: ({ message }) => this.initAutofillOverlay(message),
|
||||||
updateIframePosition: ({ message }) => this.updateIframePosition(message.styles),
|
updateIframePosition: ({ message }) => this.updateIframePosition(message.styles),
|
||||||
updateOverlayHidden: ({ message }) => this.updateElementStyles(this.iframe, message.styles),
|
updateOverlayHidden: ({ message }) => this.updateElementStyles(this.iframe, message.styles),
|
||||||
updateOverlayPageColorScheme: () => this.updateOverlayPageColorScheme(),
|
updateOverlayPageColorScheme: () => this.updateOverlayPageColorScheme(),
|
||||||
@@ -184,6 +184,11 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf
|
|||||||
|
|
||||||
private initAutofillOverlay(message: AutofillOverlayIframeExtensionMessage) {
|
private initAutofillOverlay(message: AutofillOverlayIframeExtensionMessage) {
|
||||||
this.portKey = message.portKey;
|
this.portKey = message.portKey;
|
||||||
|
if (message.command === "initAutofillOverlayList") {
|
||||||
|
this.initAutofillOverlayList(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.postMessageToIFrame(message);
|
this.postMessageToIFrame(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +200,6 @@ class AutofillOverlayIframeService implements AutofillOverlayIframeServiceInterf
|
|||||||
*/
|
*/
|
||||||
private initAutofillOverlayList(message: AutofillOverlayIframeExtensionMessage) {
|
private initAutofillOverlayList(message: AutofillOverlayIframeExtensionMessage) {
|
||||||
const { theme } = message;
|
const { theme } = message;
|
||||||
this.portKey = message.portKey;
|
|
||||||
let borderColor: string;
|
let borderColor: string;
|
||||||
let verifiedTheme = theme;
|
let verifiedTheme = theme;
|
||||||
if (verifiedTheme === ThemeType.System) {
|
if (verifiedTheme === ThemeType.System) {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class AutofillOverlayButton extends AutofillOverlayPageElement {
|
|||||||
this.getTranslation("toggleBitwardenVaultOverlay"),
|
this.getTranslation("toggleBitwardenVaultOverlay"),
|
||||||
);
|
);
|
||||||
this.buttonElement.addEventListener(EVENTS.CLICK, this.handleButtonElementClick);
|
this.buttonElement.addEventListener(EVENTS.CLICK, this.handleButtonElementClick);
|
||||||
this.postMessageToConnector({ command: "updateOverlayPageColorScheme" });
|
this.postMessageToParent({ command: "updateOverlayPageColorScheme" });
|
||||||
|
|
||||||
this.updateAuthStatus(authStatus);
|
this.updateAuthStatus(authStatus);
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ class AutofillOverlayButton extends AutofillOverlayPageElement {
|
|||||||
* parent window indicating that the button was clicked.
|
* parent window indicating that the button was clicked.
|
||||||
*/
|
*/
|
||||||
private handleButtonElementClick = () => {
|
private handleButtonElementClick = () => {
|
||||||
this.postMessageToConnector({ command: "overlayButtonClicked" });
|
this.postMessageToParent({ command: "overlayButtonClicked" });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,7 +116,7 @@ class AutofillOverlayButton extends AutofillOverlayPageElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.postMessageToConnector({ command: "closeAutofillOverlay" });
|
this.postMessageToParent({ command: "closeAutofillOverlay" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
|
|||||||
* Sends a message to the parent window to unlock the vault.
|
* Sends a message to the parent window to unlock the vault.
|
||||||
*/
|
*/
|
||||||
private handleUnlockButtonClick = () => {
|
private handleUnlockButtonClick = () => {
|
||||||
this.postMessageToConnector({ command: "unlockVault" });
|
this.postMessageToParent({ command: "unlockVault" });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,7 +171,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
|
|||||||
* Sends a message to the parent window to add a new vault item.
|
* Sends a message to the parent window to add a new vault item.
|
||||||
*/
|
*/
|
||||||
private handeNewItemButtonClick = () => {
|
private handeNewItemButtonClick = () => {
|
||||||
this.postMessageToConnector({ command: "addNewVaultItem" });
|
this.postMessageToParent({ command: "addNewVaultItem" });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,7 +278,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
|
|||||||
private handleFillCipherClickEvent = (cipher: OverlayCipherData) => {
|
private handleFillCipherClickEvent = (cipher: OverlayCipherData) => {
|
||||||
return this.useEventHandlersMemo(
|
return this.useEventHandlersMemo(
|
||||||
() =>
|
() =>
|
||||||
this.postMessageToConnector({
|
this.postMessageToParent({
|
||||||
command: "fillSelectedListItem",
|
command: "fillSelectedListItem",
|
||||||
overlayCipherId: cipher.id,
|
overlayCipherId: cipher.id,
|
||||||
}),
|
}),
|
||||||
@@ -343,8 +343,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
|
|||||||
*/
|
*/
|
||||||
private handleViewCipherClickEvent = (cipher: OverlayCipherData) => {
|
private handleViewCipherClickEvent = (cipher: OverlayCipherData) => {
|
||||||
return this.useEventHandlersMemo(
|
return this.useEventHandlersMemo(
|
||||||
() =>
|
() => this.postMessageToParent({ command: "viewSelectedCipher", overlayCipherId: cipher.id }),
|
||||||
this.postMessageToConnector({ command: "viewSelectedCipher", overlayCipherId: cipher.id }),
|
|
||||||
`${cipher.id}-view-cipher-button-click-handler`,
|
`${cipher.id}-view-cipher-button-click-handler`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -479,7 +478,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.postMessageToConnector({ command: "checkAutofillOverlayButtonFocused" });
|
this.postMessageToParent({ command: "checkAutofillOverlayButtonFocused" });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -536,7 +535,7 @@ class AutofillOverlayList extends AutofillOverlayPageElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { height } = entry.contentRect;
|
const { height } = entry.contentRect;
|
||||||
this.postMessageToConnector({
|
this.postMessageToParent({
|
||||||
command: "updateAutofillOverlayListHeight",
|
command: "updateAutofillOverlayListHeight",
|
||||||
styles: { height: `${height}px` },
|
styles: { height: `${height}px` },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import { EVENTS } from "@bitwarden/common/autofill/constants";
|
import { EVENTS } from "@bitwarden/common/autofill/constants";
|
||||||
|
|
||||||
import { setElementStyles } from "../../../utils";
|
import { setElementStyles } from "../../../utils";
|
||||||
|
import {
|
||||||
|
InitOverlayElementMessage,
|
||||||
|
AutofillOverlayMenuContainerWindowMessageHandlers,
|
||||||
|
} from "../../abstractions/autofill-overlay-menu-container";
|
||||||
|
|
||||||
export class AutofillOverlayMenuContainer {
|
export class AutofillOverlayMenuContainer {
|
||||||
private initMessage: any;
|
|
||||||
private extensionOriginsSet: Set<string>;
|
private extensionOriginsSet: Set<string>;
|
||||||
private port: chrome.runtime.Port | null = null;
|
private port: chrome.runtime.Port | null = null;
|
||||||
private portName: string;
|
private portName: string;
|
||||||
private iframe: HTMLIFrameElement;
|
private overlayPageIframe: HTMLIFrameElement;
|
||||||
private iframeStyles: Partial<CSSStyleDeclaration> = {
|
private iframeStyles: Partial<CSSStyleDeclaration> = {
|
||||||
all: "initial",
|
all: "initial",
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
@@ -33,11 +36,10 @@ export class AutofillOverlayMenuContainer {
|
|||||||
allowtransparency: "true",
|
allowtransparency: "true",
|
||||||
tabIndex: "-1",
|
tabIndex: "-1",
|
||||||
};
|
};
|
||||||
private windowMessageHandlers: Record<string, (message: any) => void> = {
|
private windowMessageHandlers: AutofillOverlayMenuContainerWindowMessageHandlers = {
|
||||||
initAutofillOverlayList: (message: any) => this.handleInitOverlayIframe(message),
|
initAutofillOverlayList: (message) => this.handleInitOverlayIframe(message),
|
||||||
initAutofillOverlayButton: (message: any) => this.handleInitOverlayIframe(message),
|
initAutofillOverlayButton: (message) => this.handleInitOverlayIframe(message),
|
||||||
};
|
};
|
||||||
private backgroundPortMessageHandlers: Record<string, (message: any) => void> = {};
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.extensionOriginsSet = new Set([
|
this.extensionOriginsSet = new Set([
|
||||||
@@ -48,31 +50,44 @@ export class AutofillOverlayMenuContainer {
|
|||||||
globalThis.addEventListener("message", this.handleWindowMessage);
|
globalThis.addEventListener("message", this.handleWindowMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleInitOverlayIframe(message: any) {
|
private handleInitOverlayIframe(message: InitOverlayElementMessage) {
|
||||||
this.initMessage = message;
|
|
||||||
this.defaultIframeAttributes.src = message.iframeUrl;
|
this.defaultIframeAttributes.src = message.iframeUrl;
|
||||||
this.defaultIframeAttributes.title = message.pageTitle;
|
this.defaultIframeAttributes.title = message.pageTitle;
|
||||||
this.portName = message.portName;
|
this.portName = message.portName;
|
||||||
|
|
||||||
this.iframe = globalThis.document.createElement("iframe");
|
this.overlayPageIframe = globalThis.document.createElement("iframe");
|
||||||
setElementStyles(this.iframe, this.iframeStyles, true);
|
setElementStyles(this.overlayPageIframe, this.iframeStyles, true);
|
||||||
for (const [attribute, value] of Object.entries(this.defaultIframeAttributes)) {
|
for (const [attribute, value] of Object.entries(this.defaultIframeAttributes)) {
|
||||||
this.iframe.setAttribute(attribute, value);
|
this.overlayPageIframe.setAttribute(attribute, value);
|
||||||
}
|
}
|
||||||
this.iframe.addEventListener(EVENTS.LOAD, this.setupPortMessageListener);
|
this.overlayPageIframe.addEventListener(EVENTS.LOAD, () =>
|
||||||
|
this.setupPortMessageListener(message),
|
||||||
|
);
|
||||||
|
|
||||||
globalThis.document.body.appendChild(this.iframe);
|
globalThis.document.body.appendChild(this.overlayPageIframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupPortMessageListener = () => {
|
private setupPortMessageListener = (message: InitOverlayElementMessage) => {
|
||||||
this.port = chrome.runtime.connect({ name: this.portName });
|
this.port = chrome.runtime.connect({ name: this.portName });
|
||||||
this.port.onMessage.addListener(this.handlePortMessage);
|
this.port.onMessage.addListener(this.handlePortMessage);
|
||||||
|
|
||||||
this.postMessageToIframe(this.initMessage);
|
this.postMessageToOverlayPage(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
private postMessageToIframe(message: any) {
|
private postMessageToOverlayPage(message: any) {
|
||||||
this.iframe?.contentWindow?.postMessage(message, "*");
|
if (!this.overlayPageIframe?.contentWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.overlayPageIframe.contentWindow.postMessage(message, "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
private postMessageToBackground(message: any) {
|
||||||
|
if (!this.port) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.port.postMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handlePortMessage = (message: any, port: chrome.runtime.Port) => {
|
private handlePortMessage = (message: any, port: chrome.runtime.Port) => {
|
||||||
@@ -80,12 +95,7 @@ export class AutofillOverlayMenuContainer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.backgroundPortMessageHandlers[message.command]) {
|
this.postMessageToOverlayPage(message);
|
||||||
this.backgroundPortMessageHandlers[message.command]({ message, port });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.iframe.contentWindow?.postMessage(message, "*");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleWindowMessage = (event: MessageEvent) => {
|
private handleWindowMessage = (event: MessageEvent) => {
|
||||||
@@ -100,11 +110,11 @@ export class AutofillOverlayMenuContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.source === globalThis.parent) {
|
if (event.source === globalThis.parent) {
|
||||||
this.iframe?.contentWindow?.postMessage(message, "*");
|
this.postMessageToOverlayPage(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.port?.postMessage(message);
|
this.postMessageToBackground(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
private isForeignWindowMessage(event: MessageEvent) {
|
private isForeignWindowMessage(event: MessageEvent) {
|
||||||
@@ -116,20 +126,17 @@ export class AutofillOverlayMenuContainer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return !this.isMessageFromOverlayPageIframe(event);
|
||||||
this.iframe?.contentWindow !== event.source ||
|
|
||||||
!this.isFromExtensionOrigin(event.origin.toLowerCase())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private isMessageFromOverlayPageIframe(event: MessageEvent): boolean {
|
||||||
* Chrome returns null for any sandboxed iframe sources.
|
if (!this.overlayPageIframe) {
|
||||||
* Firefox references the extension URI as its origin.
|
return false;
|
||||||
* Any other origin value is a security risk.
|
}
|
||||||
*
|
|
||||||
* @param messageOrigin - The origin of the window message
|
return (
|
||||||
*/
|
this.overlayPageIframe.contentWindow === event.source &&
|
||||||
private isFromExtensionOrigin(messageOrigin: string): boolean {
|
this.extensionOriginsSet.has(event.origin.toLowerCase())
|
||||||
return this.extensionOriginsSet.has(messageOrigin);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ describe("AutofillOverlayPageElement", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("postMessageToConnector", () => {
|
describe("postMessageToParent", () => {
|
||||||
it("posts a message to the parent", () => {
|
it("posts a message to the parent", () => {
|
||||||
autofillOverlayPageElement["postMessageToConnector"]({ command: "test" });
|
autofillOverlayPageElement["postMessageToParent"]({ command: "test" });
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
autofillOverlayPageElement["messageConnectorIframe"].contentWindow.postMessage,
|
autofillOverlayPageElement["messageConnectorIframe"].contentWindow.postMessage,
|
||||||
|
|||||||
@@ -49,52 +49,13 @@ class AutofillOverlayPageElement extends HTMLElement {
|
|||||||
return linkElement;
|
return linkElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes an iframe that acts as the communication bridge between
|
|
||||||
* the inline menu UI and the background script. Awaits the load event
|
|
||||||
* of the iframe before initializing the inline menu element to ensure
|
|
||||||
* messages from the inline menu are not missed.
|
|
||||||
*
|
|
||||||
* @param messageConnectorUrl - The URL of the message connector to use
|
|
||||||
* @param elementName - The name of the element, e.g. "button" or "list"
|
|
||||||
*/
|
|
||||||
// private initMessageConnector(messageConnectorUrl: string, elementName: "button" | "list") {
|
|
||||||
// this.messageConnectorIframe = globalThis.document.createElement("iframe");
|
|
||||||
// this.messageConnectorIframe.src = messageConnectorUrl;
|
|
||||||
// this.messageConnectorIframe.style.opacity = "0";
|
|
||||||
// this.messageConnectorIframe.style.position = "absolute";
|
|
||||||
// this.messageConnectorIframe.style.width = "0";
|
|
||||||
// this.messageConnectorIframe.style.height = "0";
|
|
||||||
// this.messageConnectorIframe.style.border = "none";
|
|
||||||
// this.messageConnectorIframe.style.pointerEvents = "none";
|
|
||||||
// globalThis.document.body.appendChild(this.messageConnectorIframe);
|
|
||||||
//
|
|
||||||
// return new Promise<void>((resolve) => {
|
|
||||||
// this.messageConnectorIframe.addEventListener(EVENTS.LOAD, () => {
|
|
||||||
// this.postMessageToConnector({
|
|
||||||
// command: `initAutofillOverlayPort`,
|
|
||||||
// portName:
|
|
||||||
// elementName === "list"
|
|
||||||
// ? AutofillOverlayPort.ListMessageConnector
|
|
||||||
// : AutofillOverlayPort.ButtonMessageConnector,
|
|
||||||
// });
|
|
||||||
// resolve();
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Posts a window message to the parent window.
|
* Posts a window message to the parent window.
|
||||||
*
|
*
|
||||||
* @param message - The message to post
|
* @param message - The message to post
|
||||||
*/
|
*/
|
||||||
protected postMessageToConnector(message: AutofillOverlayPageElementWindowMessage) {
|
protected postMessageToParent(message: AutofillOverlayPageElementWindowMessage) {
|
||||||
globalThis.parent.postMessage({ portKey: this.portKey, ...message }, "*");
|
globalThis.parent.postMessage({ portKey: this.portKey, ...message }, "*");
|
||||||
|
|
||||||
// this.messageConnectorIframe.contentWindow.postMessage(
|
|
||||||
// { portKey: this.portKey, ...message },
|
|
||||||
// "*",
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,7 +113,7 @@ class AutofillOverlayPageElement extends HTMLElement {
|
|||||||
* Handles the window blur event.
|
* Handles the window blur event.
|
||||||
*/
|
*/
|
||||||
private handleWindowBlurEvent = () => {
|
private handleWindowBlurEvent = () => {
|
||||||
this.postMessageToConnector({ command: "overlayPageBlurred" });
|
this.postMessageToParent({ command: "overlayPageBlurred" });
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,7 +150,7 @@ class AutofillOverlayPageElement extends HTMLElement {
|
|||||||
* @param direction - The direction to redirect the focus out
|
* @param direction - The direction to redirect the focus out
|
||||||
*/
|
*/
|
||||||
private redirectOverlayFocusOutMessage(direction: string) {
|
private redirectOverlayFocusOutMessage(direction: string) {
|
||||||
this.postMessageToConnector({ command: "redirectOverlayFocusOut", direction });
|
this.postMessageToParent({ command: "redirectOverlayFocusOut", direction });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user