internal/frontend: rewrite img links to GitHub blobs

GitHub will translate a .../blob/... URL in a README to the
corresponding .../raw/... URL. We need to do the same.

Fixes golang/go#45168

Change-Id: Ib5b9a2c33d2b3da324f16f55790ee7b228f39d7f
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/303790
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/frontend/goldmark.go b/internal/frontend/goldmark.go
index c58dd0b..3ed3d2c 100644
--- a/internal/frontend/goldmark.go
+++ b/internal/frontend/goldmark.go
@@ -40,11 +40,11 @@
 		}
 		switch v := n.(type) {
 		case *ast.Image:
-			if d := translateRelativeLink(string(v.Destination), g.info, true, g.readme); d != "" {
+			if d := translateLink(string(v.Destination), g.info, true, g.readme); d != "" {
 				v.Destination = []byte(d)
 			}
 		case *ast.Link:
-			if d := translateRelativeLink(string(v.Destination), g.info, false, g.readme); d != "" {
+			if d := translateLink(string(v.Destination), g.info, false, g.readme); d != "" {
 				v.Destination = []byte(d)
 			}
 		}
diff --git a/internal/frontend/overview.go b/internal/frontend/overview.go
index c3144e6..14b9618 100644
--- a/internal/frontend/overview.go
+++ b/internal/frontend/overview.go
@@ -47,7 +47,7 @@
 			}
 		case blackfriday.Image, blackfriday.Link:
 			useRaw := node.Type == blackfriday.Image
-			if d := translateRelativeLink(string(node.LinkData.Destination), mi.SourceInfo, useRaw, readme); d != "" {
+			if d := translateLink(string(node.LinkData.Destination), mi.SourceInfo, useRaw, readme); d != "" {
 				node.LinkData.Destination = []byte(d)
 			}
 		case blackfriday.HTMLBlock, blackfriday.HTMLSpan:
@@ -115,17 +115,37 @@
 	return ext == ".md" || ext == ".markdown"
 }
 
-// translateRelativeLink converts relative image paths to absolute paths.
+// translateLink converts image links so that they will work on pkgsite.
 //
 // README files sometimes use relative image paths to image files inside the
 // repository. As the discovery site doesn't host the full repository content,
 // in order for the image to render, we need to convert the relative path to an
 // absolute URL to a hosted image.
-func translateRelativeLink(dest string, info *source.Info, useRaw bool, readme *internal.Readme) string {
+//
+// In addition, GitHub will translate absolute non-raw links to image files to raw links.
+// For example, when GitHub renders a README with
+//    <img src="https://github.com/gobuffalo/buffalo/blob/master/logo.svg">
+// it rewrites it to
+//    <img src="https://github.com/gobuffalo/buffalo/raw/master/logo.svg">
+// (replacing "blob" with "raw").
+// We do that too.
+func translateLink(dest string, info *source.Info, useRaw bool, readme *internal.Readme) string {
 	destURL, err := url.Parse(dest)
-	if err != nil || destURL.IsAbs() {
+	if err != nil {
 		return ""
 	}
+	if destURL.IsAbs() {
+		if destURL.Host != "github.com" {
+			return ""
+		}
+		parts := strings.Split(destURL.Path, "/")
+		if len(parts) < 4 || parts[3] != "blob" {
+			return ""
+		}
+		parts[3] = "raw"
+		destURL.Path = strings.Join(parts, "/")
+		return destURL.String()
+	}
 	if destURL.Path == "" {
 		// This is a fragment; leave it.
 		return "#readme-" + destURL.Fragment
@@ -195,7 +215,7 @@
 		var attrs []html.Attribute
 		for _, a := range n.Attr {
 			if a.Key == "src" {
-				if v := translateRelativeLink(a.Val, info, true, readme); v != "" {
+				if v := translateLink(a.Val, info, true, readme); v != "" {
 					a.Val = v
 					changed = true
 				}
diff --git a/internal/frontend/overview_test.go b/internal/frontend/overview_test.go
index ce8e3ae..2cba4f5 100644
--- a/internal/frontend/overview_test.go
+++ b/internal/frontend/overview_test.go
@@ -213,6 +213,24 @@
 			},
 			want: `<p><a href="#readme-heading-id" rel="nofollow">Local Heading</a></p>`,
 		},
+		{
+			name: "absolute link to blob",
+			mi:   aModule,
+			readme: &internal.Readme{
+				Filepath: "README.md",
+				Contents: `<img src="https://github.com/foo/bar/blob/master/logo.svg">`,
+			},
+			want: `<p><img src="https://github.com/foo/bar/raw/master/logo.svg"/></p>`,
+		},
+		{
+			name: "absolute link not to blob",
+			mi:   aModule,
+			readme: &internal.Readme{
+				Filepath: "README.md",
+				Contents: `<img src="https://github.com/foo/bar/bloob/master/logo.svg">`,
+			},
+			want: `<p><img src="https://github.com/foo/bar/bloob/master/logo.svg"></p>`,
+		},
 	}
 	checkReadme := func(ctx context.Context, t *testing.T, mi *internal.ModuleInfo, readme *internal.Readme, want string) {
 		hgot, err := LegacyReadmeHTML(ctx, mi, readme)