"use client"; import { Box, Stack, Text, Title } from "@mantine/core"; import { IconBrowserOff, IconProtocol } from "@tabler/icons-react"; import { objectEntries } from "@homarr/common"; import { useI18n } from "@homarr/translation/client"; import type { WidgetComponentProps } from "../definition"; import { getSafeApplicationUrl } from "../common/application-url"; import classes from "./component.module.css"; export default function IFrameWidget({ options, isEditMode }: WidgetComponentProps<"iframe">) { const t = useI18n(); const { embedUrl, allowScrolling, ...permissions } = options; const safeEmbedUrl = getSafeApplicationUrl(embedUrl); const allowedPermissions = getAllowedPermissions(permissions); const sandboxFlags = getSandboxFlags(permissions); if (embedUrl.trim() === "") return ; if (!safeEmbedUrl) { return ; } return ( ); } const supportedProtocols = ["http", "https"]; export const isSupportedProtocol = (url: string) => { return getSafeApplicationUrl(url) !== undefined; }; const NoUrl = () => { const t = useI18n(); return ( {t("widget.iframe.error.noUrl")} ); }; const UnsupportedProtocol = () => { const t = useI18n(); return ( {t("widget.iframe.error.unsupportedProtocol", { supportedProtocols: supportedProtocols.map((protocol) => protocol).join(", "), })} ); }; export const getAllowedPermissions = ( permissions: Omit["options"], "embedUrl" | "allowScrolling">, ) => { return ( objectEntries(permissions) // * means it applies to all origins. Sandbox-only flags such as // allow-modals intentionally have no Permissions Policy mapping. .flatMap(([key, value]) => { const permission = permissionMapping[key]; return value && permission ? [`${permission} *`] : []; }) .join("; ") ); }; export const getSandboxFlags = ( permissions: Omit["options"], "embedUrl" | "allowScrolling">, ) => { const baseSandbox = [ "allow-scripts", "allow-same-origin", "allow-forms", "allow-popups", "allow-top-navigation-by-user-activation", ]; if (permissions.allowFullScreen) { baseSandbox.push("allow-presentation"); } if (permissions.allowPayment) { baseSandbox.push("allow-popups-to-escape-sandbox"); } if (permissions.allowModals) { baseSandbox.push("allow-modals"); } return baseSandbox; }; const permissionMapping: Partial< Record["options"], "embedUrl" | "allowScrolling">, string> > = { allowAutoPlay: "autoplay", allowCamera: "camera", allowFullScreen: "fullscreen", allowGeolocation: "geolocation", allowMicrophone: "microphone", allowPayment: "payment", }; export const getFrameTitle = (url: string) => { try { return new URL(url).host; } catch { return "iframe"; } };