1
1
mirror of https://github.com/go-gitea/gitea synced 2025-10-28 09:58:25 +00:00

Make external iframe render work (#35730)

Fix #35729, #17635, #21098
This commit is contained in:
wxiaoguang
2025-10-23 16:01:38 +08:00
committed by GitHub
parent 8085c75356
commit 522c466e24
18 changed files with 294 additions and 92 deletions

View File

@@ -4,6 +4,7 @@ import {initMarkupCodeCopy} from './codecopy.ts';
import {initMarkupRenderAsciicast} from './asciicast.ts';
import {initMarkupTasklist} from './tasklist.ts';
import {registerGlobalSelectorFunc} from '../modules/observer.ts';
import {initMarkupRenderIframe} from './render-iframe.ts';
// code that runs for all markup content
export function initMarkupContent(): void {
@@ -13,5 +14,6 @@ export function initMarkupContent(): void {
initMarkupCodeMermaid(el);
initMarkupCodeMath(el);
initMarkupRenderAsciicast(el);
initMarkupRenderIframe(el);
});
}

View File

@@ -0,0 +1,32 @@
import {generateElemId, queryElemChildren} from '../utils/dom.ts';
import {isDarkTheme} from '../utils.ts';
export async function loadRenderIframeContent(iframe: HTMLIFrameElement) {
const iframeSrcUrl = iframe.getAttribute('data-src');
if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');
window.addEventListener('message', (e) => {
if (!e.data?.giteaIframeCmd || e.data?.giteaIframeId !== iframe.id) return;
const cmd = e.data.giteaIframeCmd;
if (cmd === 'resize') {
iframe.style.height = `${e.data.iframeHeight}px`;
} else if (cmd === 'open-link') {
if (e.data.anchorTarget === '_blank') {
window.open(e.data.openLink, '_blank');
} else {
window.location.href = e.data.openLink;
}
} else {
throw new Error(`Unknown gitea iframe cmd: ${cmd}`);
}
});
const u = new URL(iframeSrcUrl, window.location.origin);
u.searchParams.set('gitea-is-dark-theme', String(isDarkTheme()));
u.searchParams.set('gitea-iframe-id', iframe.id);
iframe.src = u.href;
}
export function initMarkupRenderIframe(el: HTMLElement) {
queryElemChildren(el, 'iframe.external-render-iframe', loadRenderIframeContent);
}

View File

@@ -0,0 +1,43 @@
/* To manually test:
[markup.in-iframe]
ENABLED = true
FILE_EXTENSIONS = .in-iframe
RENDER_CONTENT_MODE = iframe
RENDER_COMMAND = `echo '<div style="width: 100%; height: 2000px; border: 10px solid red; box-sizing: border-box;"><a href="/">a link</a> <a target="_blank" href="//gitea.com">external link</a></div>'`
;RENDER_COMMAND = cat /path/to/file.pdf
;RENDER_CONTENT_SANDBOX = disabled
*/
function mainExternalRenderIframe() {
const u = new URL(window.location.href);
const iframeId = u.searchParams.get('gitea-iframe-id');
// iframe is in different origin, so we need to use postMessage to communicate
const postIframeMsg = (cmd: string, data: Record<string, any> = {}) => {
window.parent.postMessage({giteaIframeCmd: cmd, giteaIframeId: iframeId, ...data}, '*');
};
const updateIframeHeight = () => postIframeMsg('resize', {iframeHeight: document.documentElement.scrollHeight});
updateIframeHeight();
window.addEventListener('DOMContentLoaded', updateIframeHeight);
// the easiest way to handle dynamic content changes and easy to debug, can be fine-tuned in the future
setInterval(updateIframeHeight, 1000);
// no way to open an absolute link with CSP frame-src, it also needs some tricks like "postMessage" or "copy the link to clipboard"
const openIframeLink = (link: string, target: string) => postIframeMsg('open-link', {openLink: link, anchorTarget: target});
document.addEventListener('click', (e) => {
const el = e.target as HTMLAnchorElement;
if (el.nodeName !== 'A') return;
const href = el.getAttribute('href') || '';
// safe links: "./any", "../any", "/any", "//host/any", "http://host/any", "https://host/any"
if (href.startsWith('.') || href.startsWith('/') || href.startsWith('http://') || href.startsWith('https://')) {
e.preventDefault();
openIframeLink(href, el.getAttribute('target'));
}
});
}
mainExternalRenderIframe();