Skip to content

Commit 8d82ebd

Browse files
committed
fix test, fix open link (force push to make backport easier)
1 parent dee02c7 commit 8d82ebd

File tree

4 files changed

+42
-21
lines changed

4 files changed

+42
-21
lines changed

modules/markup/internal/finalprocessor.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ func (p *finalProcessor) Close() error {
4545
} else {
4646
part1, part2 = nil, buf
4747
}
48-
if _, err := p.output.Write(part1); err != nil {
49-
return err
48+
if len(part1) > 0 {
49+
if _, err := p.output.Write(part1); err != nil {
50+
return err
51+
}
5052
}
5153
if _, err := io.WriteString(p.output, string(p.extraHeadHTML)); err != nil {
5254
return err

tests/integration/markup_external_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,15 @@ func TestExternalMarkupRenderer(t *testing.T) {
6868
assert.Equal(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
6969
doc := NewHTMLParser(t, resp.Body)
7070
iframe := doc.Find("iframe")
71-
assert.Equal(t, "/user30/renderer/render/branch/master/README.html", iframe.AttrOr("src", ""))
71+
assert.Empty(t, iframe.AttrOr("src", "")) // src should be empty, "data-src" is used instead
72+
assert.Equal(t, "/user30/renderer/render/branch/master/README.html", iframe.AttrOr("data-src", ""))
7273

7374
req = NewRequest(t, "GET", "/user30/renderer/render/branch/master/README.html")
7475
resp = MakeRequest(t, req, http.StatusOK)
7576
assert.Equal(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
7677
bs, err := io.ReadAll(resp.Body)
7778
assert.NoError(t, err)
7879
assert.Equal(t, "frame-src 'self'; sandbox allow-scripts allow-popups", resp.Header().Get("Content-Security-Policy"))
79-
assert.Equal(t, "<div>\n\ttest external renderer\n</div>\n", string(bs))
80+
assert.Equal(t, "<script src=\"/assets/js/external-render-iframe.js\"></script><link rel=\"stylesheet\" href=\"/assets/css/external-render-iframe.css\"><div>\n\ttest external renderer\n</div>\n", string(bs))
8081
})
8182
}

web_src/js/markup/render-iframe.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ export async function loadRenderIframeContent(iframe: HTMLIFrameElement) {
66
if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');
77

88
window.addEventListener('message', (e) => {
9-
if (e.data && e.data.giteaIframeCmd === 'resize' && e.data.giteaIframeId === iframe.id) {
10-
iframe.style.height = `${e.data.giteaIframeHeight}px`;
9+
if (!e.data?.giteaIframeCmd || e.data?.giteaIframeId !== iframe.id) return;
10+
const cmd = e.data.giteaIframeCmd;
11+
if (cmd === 'resize') {
12+
iframe.style.height = `${e.data.iframeHeight}px`;
13+
} else if (cmd === 'open-link') {
14+
if (e.data.anchorTarget === '_blank') {
15+
window.open(e.data.openLink, '_blank');
16+
} else {
17+
window.location.href = e.data.openLink;
18+
}
19+
} else {
20+
throw new Error(`Unknown gitea iframe cmd: ${cmd}`);
1121
}
1222
});
1323

web_src/js/standalone/external-render-iframe.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,37 @@
44
ENABLED = true
55
FILE_EXTENSIONS = .in-iframe
66
RENDER_CONTENT_MODE = iframe
7-
RENDER_COMMAND = `echo '<div style="width: 100%; height: 2000px; border: 10px solid red; box-sizing: border-box;">content</div>'`
7+
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>'`
88
99
*/
1010

1111
function mainExternalRenderIframe() {
1212
const u = new URL(window.location.href);
13-
const fn = () => window.parent.postMessage({
14-
giteaIframeCmd: 'resize',
15-
giteaIframeId: u.searchParams.get('gitea-iframe-id'),
16-
giteaIframeHeight: document.documentElement.scrollHeight,
17-
}, '*');
18-
fn();
19-
window.addEventListener('DOMContentLoaded', fn);
20-
setInterval(fn, 1000);
13+
const iframeId = u.searchParams.get('gitea-iframe-id');
2114

22-
// make all absolute links open in new window (otherwise they would be blocked by all parents' frame-src)
23-
document.body.addEventListener('click', (e) => {
15+
// iframe is in different origin, so we need to use postMessage to communicate
16+
const postIframeMsg = (cmd: string, data: Record<string, any> = {}) => {
17+
window.parent.postMessage({giteaIframeCmd: cmd, giteaIframeId: iframeId, ...data}, '*');
18+
};
19+
20+
const updateIframeHeight = () => postIframeMsg('resize', {iframeHeight: document.documentElement.scrollHeight});
21+
updateIframeHeight();
22+
window.addEventListener('DOMContentLoaded', updateIframeHeight);
23+
// the easiest way to handle dynamic content changes and easy to debug, can be fine-tuned in the future
24+
setInterval(updateIframeHeight, 1000);
25+
26+
// no way to open an absolute link with CSP frame-src, it also needs some tricks like "postMessage" or "copy the link to clipboard"
27+
const openIframeLink = (link: string, target: string) => postIframeMsg('open-link', {openLink: link, anchorTarget: target});
28+
document.addEventListener('click', (e) => {
2429
const el = e.target as HTMLAnchorElement;
2530
if (el.nodeName !== 'A') return;
26-
const href = el.getAttribute('href');
27-
if (!href.startsWith('//') && !href.includes('://')) return;
28-
el.target = '_blank';
29-
}, true);
31+
const href = el.getAttribute('href') || '';
32+
// safe links: "./any", "../any", "/any", "//host/any", "http://host/any", "https://host/any"
33+
if (href.startsWith('.') || href.startsWith('/') || href.startsWith('http://') || href.startsWith('https://')) {
34+
e.preventDefault();
35+
openIframeLink(href, el.getAttribute('target'));
36+
}
37+
});
3038
}
3139

3240
mainExternalRenderIframe();

0 commit comments

Comments
 (0)