import * as React from 'react'; export type IframeProps = { src: string; fit?: boolean; onMessage?: (event: MessageEvent) => void; onLoad?: () => void; onFail?: () => void; skipHeadCheck?: boolean; } & React.DetailedHTMLProps, HTMLIFrameElement>; export interface IframeRefObject { amIIframe: () => boolean; sendMessage: (message: string, targetOrigin?: string) => void; onMessage: (listener: (event: MessageEvent) => void) => (() => void); }; export const ParentProxy: IframeRefObject = { amIIframe: () => window !== window.top, sendMessage: (message, targetOrigin = document.referrer) => { return window.parent.postMessage(message, targetOrigin); }, onMessage: (listener: (event: MessageEvent) => void) => { const secureListener = (event: MessageEvent) => { if (event.origin === window.document.referrer) { return listener(event); } return null; }; window.addEventListener( 'message', secureListener, false, ); return () => { window.removeEventListener('message', secureListener); }; } }; export const Iframe = React.forwardRef(( { src, fit = false, skipHeadCheck = false, onMessage, onLoad, onFail, ...other }: IframeProps, ref: React.Ref, ) => { const [canBeLoaded, setCanBeLoaded] = React.useState(false); const iframe = React.createRef(); // eslint-disable-next-line no-param-reassign delete (other as any).computedMatch; React.useEffect(() => { if (skipHeadCheck) { setCanBeLoaded(true); } else { setCanBeLoaded(false); fetch( src, { method: 'HEAD', mode: 'no-cors' }, ).then( ({ status }) => { if (status !== 0) { throw new TypeError('Failed to fetch'); } setCanBeLoaded(true); } ).catch( () => onFail && onFail() ); } }, [src, skipHeadCheck]); React.useEffect(() => { const targetOrigin = new URL(src).origin; if (canBeLoaded) { const proxy = { amIIframe: () => false, onMessage: (listener: (event: MessageEvent) => void) => { const secureListener = (event: MessageEvent) => { if (event.origin === targetOrigin) { return listener(event); } return null; }; window.addEventListener('message', secureListener, false); return () => window.removeEventListener('message', secureListener); }, sendMessage: (message: string) => iframe.current && iframe.current.contentWindow && iframe.current.contentWindow.postMessage(message, targetOrigin), } as IframeRefObject; if (ref) { // eslint-disable-next-line no-param-reassign (ref as any).current = proxy; } if (onMessage) { return proxy.onMessage(onMessage); } } return () => undefined; }, [onMessage, canBeLoaded]); React.useEffect(() => { if (iframe.current && canBeLoaded) { iframe.current.onload = () => { if (onLoad) { onLoad(); } if (fit && !!iframe.current && iframe.current.contentWindow) { iframe.current.width = `${iframe.current.contentWindow.document.body.scrollWidth}`; iframe.current.height = `${iframe.current.contentWindow.document.body.scrollHeight}`; } }; } return () => { if (iframe.current) { delete (iframe.current as any).onload; } }; }, [iframe.current, canBeLoaded]); if (!canBeLoaded) { return null; } return (