summaryrefslogtreecommitdiffstats
path: root/modules/markup
diff options
context:
space:
mode:
authorGiteabot <teabot@gitea.io>2024-06-04 21:26:55 +0800
committerGitHub <noreply@github.com>2024-06-04 13:26:55 +0000
commit5136c879c29fdd713f664c68e6d61d4197b6fec0 (patch)
treeed1a829237a42e6c4ab9e9adc41abe452daf76f2 /modules/markup
parentca414a7ccf5e26272662e360c44ac50221a0f2d4 (diff)
downloadgitea-5136c879c29fdd713f664c68e6d61d4197b6fec0.tar.gz
gitea-5136c879c29fdd713f664c68e6d61d4197b6fec0.zip
Make pasted "img" tag has the same behavior as markdown image (#31235) (#31243)
Backport #31235 by wxiaoguang Fix #31230 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Diffstat (limited to 'modules/markup')
-rw-r--r--modules/markup/html.go60
-rw-r--r--modules/markup/html_internal_test.go19
-rw-r--r--modules/markup/html_test.go53
-rw-r--r--modules/markup/renderer.go2
4 files changed, 75 insertions, 59 deletions
diff --git a/modules/markup/html.go b/modules/markup/html.go
index 2958dc9646..44f88bbd11 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -372,7 +372,42 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
return nil
}
-func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
+func handleNodeImg(ctx *RenderContext, img *html.Node) {
+ for i, attr := range img.Attr {
+ if attr.Key != "src" {
+ continue
+ }
+
+ if attr.Val != "" && !IsFullURLString(attr.Val) && !strings.HasPrefix(attr.Val, "/") {
+ attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
+
+ // By default, the "<img>" tag should also be clickable,
+ // because frontend use `<img>` to paste the re-scaled image into the markdown,
+ // so it must match the default markdown image behavior.
+ hasParentAnchor := false
+ for p := img.Parent; p != nil; p = p.Parent {
+ if hasParentAnchor = p.Type == html.ElementNode && p.Data == "a"; hasParentAnchor {
+ break
+ }
+ }
+ if !hasParentAnchor {
+ imgA := &html.Node{Type: html.ElementNode, Data: "a", Attr: []html.Attribute{
+ {Key: "href", Val: attr.Val},
+ {Key: "target", Val: "_blank"},
+ }}
+ parent := img.Parent
+ imgNext := img.NextSibling
+ parent.RemoveChild(img)
+ parent.InsertBefore(imgA, imgNext)
+ imgA.AppendChild(img)
+ }
+ }
+ attr.Val = camoHandleLink(attr.Val)
+ img.Attr[i] = attr
+ }
+}
+
+func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Node {
// Add user-content- to IDs and "#" links if they don't already have them
for idx, attr := range node.Attr {
val := strings.TrimPrefix(attr.Val, "#")
@@ -397,21 +432,14 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
textNode(ctx, procs, node)
case html.ElementNode:
if node.Data == "img" {
- for i, attr := range node.Attr {
- if attr.Key != "src" {
- continue
- }
- if len(attr.Val) > 0 && !IsFullURLString(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
- attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
- }
- attr.Val = camoHandleLink(attr.Val)
- node.Attr[i] = attr
- }
+ next := node.NextSibling
+ handleNodeImg(ctx, node)
+ return next
} else if node.Data == "a" {
// Restrict text in links to emojis
procs = emojiProcessors
} else if node.Data == "code" || node.Data == "pre" {
- return
+ return node.NextSibling
} else if node.Data == "i" {
for _, attr := range node.Attr {
if attr.Key != "class" {
@@ -434,11 +462,11 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
}
}
}
- for n := node.FirstChild; n != nil; n = n.NextSibling {
- visitNode(ctx, procs, n)
+ for n := node.FirstChild; n != nil; {
+ n = visitNode(ctx, procs, n)
}
}
- // ignore everything else
+ return node.NextSibling
}
// textNode runs the passed node through various processors, in order to handle
@@ -851,7 +879,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
// FIXME: the use of "mode" is quite dirty and hacky, for example: what is a "document"? how should it be rendered?
// The "mode" approach should be refactored to some other more clear&reliable way.
- crossLinkOnly := (ctx.Metas["mode"] == "document" && !ctx.IsWiki)
+ crossLinkOnly := ctx.Metas["mode"] == "document" && !ctx.IsWiki
var (
found bool
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index 3ff0597851..9aa9c22d70 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -18,8 +18,7 @@ import (
const (
TestAppURL = "http://localhost:3000/"
- TestOrgRepo = "gogits/gogs"
- TestRepoURL = TestAppURL + TestOrgRepo + "/"
+ TestRepoURL = TestAppURL + "test-owner/test-repo/"
)
// externalIssueLink an HTML link to an alphanumeric-style issue
@@ -64,8 +63,8 @@ var regexpMetas = map[string]string{
// these values should match the TestOrgRepo const above
var localMetas = map[string]string{
- "user": "gogits",
- "repo": "gogs",
+ "user": "test-owner",
+ "repo": "test-repo",
}
func TestRender_IssueIndexPattern(t *testing.T) {
@@ -362,12 +361,12 @@ func TestRender_FullIssueURLs(t *testing.T) {
`Look here <a href="http://localhost:3000/person/repo/issues/4" class="ref-issue">person/repo#4</a>`)
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4 (comment)</a>`)
- test("http://localhost:3000/gogits/gogs/issues/4",
- `<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a>`)
- test("http://localhost:3000/gogits/gogs/issues/4 test",
- `<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a> test`)
- test("http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123 test",
- `<a href="http://localhost:3000/gogits/gogs/issues/4?a=1&amp;b=2#comment-123" class="ref-issue">#4 (comment)</a> test`)
+ test("http://localhost:3000/test-owner/test-repo/issues/4",
+ `<a href="http://localhost:3000/test-owner/test-repo/issues/4" class="ref-issue">#4</a>`)
+ test("http://localhost:3000/test-owner/test-repo/issues/4 test",
+ `<a href="http://localhost:3000/test-owner/test-repo/issues/4" class="ref-issue">#4</a> test`)
+ test("http://localhost:3000/test-owner/test-repo/issues/4?a=1&b=2#comment-123 test",
+ `<a href="http://localhost:3000/test-owner/test-repo/issues/4?a=1&amp;b=2#comment-123" class="ref-issue">#4 (comment)</a> test`)
test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24",
"http://localhost:3000/testOrg/testOrgRepo/pulls/2/files#issuecomment-24")
test("http://localhost:3000/testOrg/testOrgRepo/pulls/2/files",
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 6b3bcd7257..a0642bcfa4 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -53,7 +53,7 @@ func TestRender_Commits(t *testing.T) {
}
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"
- repo := markup.TestRepoURL
+ repo := "http://localhost:3000/gogits/gogs"
commit := util.URLJoin(repo, "commit", sha)
tree := util.URLJoin(repo, "tree", sha, "src")
@@ -107,8 +107,8 @@ func TestRender_CrossReferences(t *testing.T) {
}
test(
- "gogits/gogs#12345",
- `<p><a href="`+util.URLJoin(markup.TestAppURL, "gogits", "gogs", "issues", "12345")+`" class="ref-issue" rel="nofollow">gogits/gogs#12345</a></p>`)
+ "test-owner/test-repo#12345",
+ `<p><a href="`+util.URLJoin(markup.TestAppURL, "test-owner", "test-repo", "issues", "12345")+`" class="ref-issue" rel="nofollow">test-owner/test-repo#12345</a></p>`)
test(
"go-gitea/gitea#12345",
`<p><a href="`+util.URLJoin(markup.TestAppURL, "go-gitea", "gitea", "issues", "12345")+`" class="ref-issue" rel="nofollow">go-gitea/gitea#12345</a></p>`)
@@ -517,43 +517,31 @@ func TestRender_ShortLinks(t *testing.T) {
}
func TestRender_RelativeImages(t *testing.T) {
- setting.AppURL = markup.TestAppURL
-
- test := func(input, expected, expectedWiki string) {
+ render := func(input string, isWiki bool, links markup.Links) string {
buffer, err := markdown.RenderString(&markup.RenderContext{
- Ctx: git.DefaultContext,
- Links: markup.Links{
- Base: markup.TestRepoURL,
- BranchPath: "master",
- },
- Metas: localMetas,
- }, input)
- assert.NoError(t, err)
- assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
- buffer, err = markdown.RenderString(&markup.RenderContext{
- Ctx: git.DefaultContext,
- Links: markup.Links{
- Base: markup.TestRepoURL,
- },
+ Ctx: git.DefaultContext,
+ Links: links,
Metas: localMetas,
- IsWiki: true,
+ IsWiki: isWiki,
}, input)
assert.NoError(t, err)
- assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
+ return strings.TrimSpace(string(buffer))
}
- rawwiki := util.URLJoin(markup.TestRepoURL, "wiki", "raw")
- mediatree := util.URLJoin(markup.TestRepoURL, "media", "master")
+ out := render(`<img src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo"})
+ assert.Equal(t, `<a href="/test-owner/test-repo/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/LINK"/></a>`, out)
- test(
- `<img src="Link">`,
- `<img src="`+util.URLJoin(mediatree, "Link")+`"/>`,
- `<img src="`+util.URLJoin(rawwiki, "Link")+`"/>`)
+ out = render(`<img src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo"})
+ assert.Equal(t, `<a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a>`, out)
- test(
- `<img src="./icon.png">`,
- `<img src="`+util.URLJoin(mediatree, "icon.png")+`"/>`,
- `<img src="`+util.URLJoin(rawwiki, "icon.png")+`"/>`)
+ out = render(`<img src="LINK">`, false, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
+ assert.Equal(t, `<a href="/test-owner/test-repo/media/test-branch/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/media/test-branch/LINK"/></a>`, out)
+
+ out = render(`<img src="LINK">`, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
+ assert.Equal(t, `<a href="/test-owner/test-repo/wiki/raw/LINK" target="_blank" rel="nofollow noopener"><img src="/test-owner/test-repo/wiki/raw/LINK"/></a>`, out)
+
+ out = render(`<img src="/LINK">`, true, markup.Links{Base: "/test-owner/test-repo", BranchPath: "test-branch"})
+ assert.Equal(t, `<img src="/LINK"/>`, out)
}
func Test_ParseClusterFuzz(t *testing.T) {
@@ -706,5 +694,6 @@ func TestIssue18471(t *testing.T) {
func TestIsFullURL(t *testing.T) {
assert.True(t, markup.IsFullURLString("https://example.com"))
assert.True(t, markup.IsFullURLString("mailto:test@example.com"))
+ assert.True(t, markup.IsFullURLString("data:image/11111"))
assert.False(t, markup.IsFullURLString("/foo:bar"))
}
diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go
index dbd96a1e69..f372dcd5b7 100644
--- a/modules/markup/renderer.go
+++ b/modules/markup/renderer.go
@@ -73,7 +73,7 @@ type RenderContext struct {
Type string
IsWiki bool
Links Links
- Metas map[string]string
+ Metas map[string]string // user, repo, mode(comment/document)
DefaultLink string
GitRepo *git.Repository
ShaExistCache map[string]bool