Frontend iframe renderer framework: 3D models, OpenAPI (#37233)

Introduces a frontend external-render framework that runs renderer
plugins inside an `iframe` (loaded via `srcdoc` to keep the CSP
`sandbox` directive working without origin-related console noise), and
migrates the 3D viewer and OpenAPI/Swagger renderers onto it. PDF and
asciicast paths are refactored to share the same `data-render-name`
mechanism.

Adds e2e coverage for 3D, PDF, asciicast and OpenAPI render paths, plus
a regression for the `RefTypeNameSubURL` double-escape on non-ASCII
branch names.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2026-04-18 00:30:17 +02:00
committed by GitHub
parent 0161f3019b
commit d5831b9385
32 changed files with 540 additions and 293 deletions
+2 -1
View File
@@ -43,7 +43,8 @@ func RenderFile(ctx *context.Context) {
CurrentRefPath: ctx.Repo.RefTypeNameSubURL(),
CurrentTreePath: path.Dir(ctx.Repo.TreePath),
}).WithRelativePath(ctx.Repo.TreePath).WithStandalonePage(markup.StandalonePageOptions{
CurrentWebTheme: ctx.TemplateContext.CurrentWebTheme(),
CurrentWebTheme: ctx.TemplateContext.CurrentWebTheme(),
RenderQueryString: ctx.Req.URL.RawQuery,
})
renderer, rendererInput, err := rctx.DetectMarkupRendererByReader(blobReader)
if err != nil {
+6 -4
View File
@@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/git/attribute"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
@@ -76,14 +77,17 @@ func handleFileViewRenderMarkup(ctx *context.Context, prefetchBuf []byte, utf8Re
return false
}
ctx.Data["MarkupType"] = rctx.RenderOptions.MarkupType
var err error
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRenderToHTML(ctx, rctx, renderer, utf8Reader)
if err != nil {
ctx.ServerError("Render", err)
return true
}
opts, ok := markup.GetExternalRendererOptions(renderer)
usingIframe := ok && opts.DisplayInIframe
ctx.Data["MarkupType"] = rctx.RenderOptions.MarkupType
ctx.Data["RenderAsMarkup"] = util.Iif(usingIframe, "markup-iframe", "markup-inplace")
return true
}
@@ -235,8 +239,6 @@ func prepareFileView(ctx *context.Context, entry *git.TreeEntry) {
case fInfo.blobOrLfsSize >= setting.UI.MaxDisplayFileSize:
ctx.Data["IsFileTooLarge"] = true
case handleFileViewRenderMarkup(ctx, buf, contentReader):
// it also sets ctx.Data["FileContent"] and more
ctx.Data["IsMarkup"] = true
case handleFileViewRenderSource(ctx, attrs, fInfo, contentReader):
// it also sets ctx.Data["FileContent"] and more
ctx.Data["IsDisplayingSource"] = true
+3 -3
View File
@@ -195,16 +195,16 @@ func prepareToRenderReadmeFile(ctx *context.Context, subfolder string, readmeFil
}).WithRelativePath(readmeFullPath)
renderer := rctx.DetectMarkupRenderer(buf)
if renderer != nil {
ctx.Data["IsMarkup"] = true
ctx.Data["RenderAsMarkup"] = "markup-inplace"
ctx.Data["MarkupType"] = rctx.RenderOptions.MarkupType
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRenderToHTML(ctx, rctx, renderer, rd)
if err != nil {
log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err)
delete(ctx.Data, "IsMarkup")
delete(ctx.Data, "RenderAsMarkup")
}
}
if ctx.Data["IsMarkup"] != true {
if ctx.Data["RenderAsMarkup"] == nil {
ctx.Data["IsPlainText"] = true
content, err := io.ReadAll(rd)
if err != nil {