mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Backport #23312 Replace #23310, Close #19733 And fix various UI problems, including regressions from #22959 #22950 and more. ## SVG Detection The old regexp may mismatch non-SVG files. This PR adds new tests for those cases. ## UI Changes ### Before   ### After       Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
		| @@ -4,6 +4,7 @@ | ||||
| package typesniffer | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| @@ -24,8 +25,9 @@ const ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	svgTagRegex      = regexp.MustCompile(`(?si)\A\s*(?:(<!--.*?-->|<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg[\s>\/]`) | ||||
| 	svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(<!--.*?-->|<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg[\s>\/]`) | ||||
| 	svgComment       = regexp.MustCompile(`(?s)<!--.*?-->`) | ||||
| 	svgTagRegex      = regexp.MustCompile(`(?si)\A\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`) | ||||
| 	svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`) | ||||
| ) | ||||
|  | ||||
| // SniffedType contains information about a blobs type. | ||||
| @@ -91,11 +93,18 @@ func DetectContentType(data []byte) SniffedType { | ||||
| 		data = data[:sniffLen] | ||||
| 	} | ||||
|  | ||||
| 	if (strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")) && svgTagRegex.Match(data) || | ||||
| 		strings.Contains(ct, "text/xml") && svgTagInXMLRegex.Match(data) { | ||||
| 		// SVG is unsupported. https://github.com/golang/go/issues/15888 | ||||
| 	// SVG is unsupported by http.DetectContentType, https://github.com/golang/go/issues/15888 | ||||
|  | ||||
| 	detectByHTML := strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html") | ||||
| 	detectByXML := strings.Contains(ct, "text/xml") | ||||
| 	if detectByHTML || detectByXML { | ||||
| 		dataProcessed := svgComment.ReplaceAll(data, nil) | ||||
| 		dataProcessed = bytes.TrimSpace(dataProcessed) | ||||
| 		if detectByHTML && svgTagRegex.Match(dataProcessed) || | ||||
| 			detectByXML && svgTagInXMLRegex.Match(dataProcessed) { | ||||
| 			ct = SvgMimeType | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return SniffedType{ct} | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,6 @@ func TestIsSvgImage(t *testing.T) { | ||||
| 	assert.True(t, DetectContentType([]byte("<svg></svg>")).IsSvgImage()) | ||||
| 	assert.True(t, DetectContentType([]byte("    <svg></svg>")).IsSvgImage()) | ||||
| 	assert.True(t, DetectContentType([]byte(`<svg width="100"></svg>`)).IsSvgImage()) | ||||
| 	assert.True(t, DetectContentType([]byte("<svg/>")).IsSvgImage()) | ||||
| 	assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?><svg></svg>`)).IsSvgImage()) | ||||
| 	assert.True(t, DetectContentType([]byte(`<!-- Comment --> | ||||
| 	<svg></svg>`)).IsSvgImage()) | ||||
| @@ -57,6 +56,10 @@ func TestIsSvgImage(t *testing.T) { | ||||
| 	<!-- Multline | ||||
| 	Comment --> | ||||
| 	<svg></svg>`)).IsSvgImage()) | ||||
|  | ||||
| 	// the DetectContentType should work for incomplete data, because only beginning bytes are used for detection | ||||
| 	assert.True(t, DetectContentType([]byte(`<svg>....`)).IsSvgImage()) | ||||
|  | ||||
| 	assert.False(t, DetectContentType([]byte{}).IsSvgImage()) | ||||
| 	assert.False(t, DetectContentType([]byte("svg")).IsSvgImage()) | ||||
| 	assert.False(t, DetectContentType([]byte("<svgfoo></svgfoo>")).IsSvgImage()) | ||||
| @@ -68,6 +71,26 @@ func TestIsSvgImage(t *testing.T) { | ||||
| 	assert.False(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?> | ||||
| 	<!-- <svg></svg> inside comment --> | ||||
| 	<foo></foo>`)).IsSvgImage()) | ||||
|  | ||||
| 	assert.False(t, DetectContentType([]byte(` | ||||
| <!-- comment1 --> | ||||
| <div> | ||||
| 	<!-- comment2 --> | ||||
| 	<svg></svg> | ||||
| </div> | ||||
| `)).IsSvgImage()) | ||||
|  | ||||
| 	assert.False(t, DetectContentType([]byte(` | ||||
| <!-- comment1 | ||||
| --> | ||||
| <div> | ||||
| 	<!-- comment2 | ||||
| --> | ||||
| 	<svg></svg> | ||||
| </div> | ||||
| `)).IsSvgImage()) | ||||
| 	assert.False(t, DetectContentType([]byte(`<html><body><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg></svg></body></html>`)).IsSvgImage()) | ||||
| 	assert.False(t, DetectContentType([]byte(`<html><body><?xml version="1.0" encoding="UTF-8"?><svg></svg></body></html>`)).IsSvgImage()) | ||||
| } | ||||
|  | ||||
| func TestIsPDF(t *testing.T) { | ||||
|   | ||||
| @@ -129,8 +129,8 @@ export function initImageDiff() { | ||||
|         initOverlay(createContext($imageAfter[2], $imageBefore[2])); | ||||
|       } | ||||
|  | ||||
|       hideElem($container.find('> .loader')); | ||||
|       $container.find('> .gt-hidden').removeClass('gt-hidden'); | ||||
|       hideElem($container.find('.ui.loader')); | ||||
|     } | ||||
|  | ||||
|     function initSideBySide(sizes) { | ||||
| @@ -155,7 +155,7 @@ export function initImageDiff() { | ||||
|         height: sizes.size1.height * factor | ||||
|       }); | ||||
|       sizes.image1.parent().css({ | ||||
|         margin: `${sizes.ratio[1] * factor + 15}px ${sizes.ratio[0] * factor}px ${sizes.ratio[1] * factor}px`, | ||||
|         margin: `10px auto`, | ||||
|         width: sizes.size1.width * factor + 2, | ||||
|         height: sizes.size1.height * factor + 2 | ||||
|       }); | ||||
| @@ -164,7 +164,7 @@ export function initImageDiff() { | ||||
|         height: sizes.size2.height * factor | ||||
|       }); | ||||
|       sizes.image2.parent().css({ | ||||
|         margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`, | ||||
|         margin: `10px auto`, | ||||
|         width: sizes.size2.width * factor + 2, | ||||
|         height: sizes.size2.height * factor + 2 | ||||
|       }); | ||||
| @@ -255,13 +255,12 @@ export function initImageDiff() { | ||||
|         width: sizes.size2.width * factor + 2, | ||||
|         height: sizes.size2.height * factor + 2 | ||||
|       }); | ||||
|  | ||||
|       // some inner elements are `position: absolute`, so the container's height must be large enough | ||||
|       // the "css(width, height)" is somewhat hacky and not easy to understand, it could be improved in the future | ||||
|       sizes.image2.parent().parent().css({ | ||||
|         width: sizes.max.width * factor + 2, | ||||
|         height: sizes.max.height * factor + 2 | ||||
|       }); | ||||
|       $container.find('.onion-skin').css({ | ||||
|         width: sizes.max.width * factor + 2, | ||||
|         height: sizes.max.height * factor + 4 | ||||
|         height: sizes.max.height * factor + 2 + 20 /* extra height for inner "position: absolute" elements */, | ||||
|       }); | ||||
|  | ||||
|       const $range = $container.find("input[type='range']"); | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| .image-diff-container { | ||||
|   text-align: center; | ||||
|   padding: 30px 0; | ||||
|   padding: 1em 0; | ||||
|  | ||||
|   img { | ||||
|     border: 1px solid var(--color-primary-light-7); | ||||
| @@ -22,6 +22,7 @@ | ||||
|       display: inline-block; | ||||
|       line-height: 0; | ||||
|       vertical-align: top; | ||||
|       margin: 0 1em; | ||||
|  | ||||
|       .side-header { | ||||
|         font-weight: bold; | ||||
| @@ -98,7 +99,7 @@ | ||||
|     } | ||||
|  | ||||
|     input { | ||||
|       width: 300px; | ||||
|       max-width: 300px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Giteabot
					Giteabot