Skip to content
19 changes: 13 additions & 6 deletions src/blocks/GenericEmbed/Component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const GenericEmbedBlockComponent = ({
className,
isLexical = true,
}: Props) => {
const [sanitizedHtml, setSanitizedHtml] = useState<string | null>(null)
const [blobUrl, setBlobUrl] = useState<string | null>(null)

const bgColorClass = `bg-${backgroundColor}`
const textColor = getTextColorFromBgColor(backgroundColor)
Expand All @@ -29,7 +29,7 @@ export const GenericEmbedBlockComponent = ({
if (typeof window === 'undefined' || !html) return

// Normalize problematic quotes that are parsed incorrectly by DOMParser and DOMPurify
const normalizedHTML = html.replaceAll('"', '"').replaceAll('"', '"')
const normalizedHTML = html.replaceAll('\u201C', '"').replaceAll('\u201D', '"')

const sanitized = DOMPurify.sanitize(normalizedHTML, {
ADD_TAGS: ['iframe', 'script', 'style', 'dbox-widget'],
Expand Down Expand Up @@ -67,10 +67,17 @@ export const GenericEmbedBlockComponent = ({
</style>
`

setSanitizedHtml(sanitized + styleOverrides)
// Use a blob URL instead of srcDoc because Chromium doesn't re-execute
// scripts in srcDoc iframes after SPA client-side navigation.
const fullHtml = `<!DOCTYPE html><html><head></head><body>${sanitized}${styleOverrides}</body></html>`
const blob = new Blob([fullHtml], { type: 'text/html' })
const url = URL.createObjectURL(blob)
setBlobUrl(url)

return () => URL.revokeObjectURL(url)
}, [html])

if (sanitizedHtml === null) return null
if (blobUrl === null) return null

return (
<div className={cn(bgColorClass, textColor)}>
Expand All @@ -87,10 +94,10 @@ export const GenericEmbedBlockComponent = ({
<IframeResizer
id={String(id)}
title={`Embedded content ${id}`}
srcDoc={sanitizedHtml}
src={blobUrl}
sandbox="allow-scripts allow-presentation allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox"
className="w-full border-none m-0 p-0 transition-[height] duration-200 ease-in-out"
height={0} // This iframe will resize to it's content height - this initial height is to avoid the iframe rendering at the browser default 150px initially
height={0}
/>
</div>
</div>
Expand Down
Loading