godoc: make line numbers unselectable

Insert line numbers via a ::before pseudo-element so you can't select
them when you copy text. See the code comment for more information.

Fixes golang/go#20077.

Change-Id: I24a5b17786a52c8107b4f830e824526ba03bc38d
Reviewed-on: https://go-review.googlesource.com/41418
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/godoc/server.go b/godoc/server.go
index 162e5a6..ffe5997 100644
--- a/godoc/server.go
+++ b/godoc/server.go
@@ -621,7 +621,16 @@
 	// linkWriter, so we have to add line spans as another pass.
 	n := 1
 	for _, line := range bytes.Split(buf.Bytes(), []byte("\n")) {
-		fmt.Fprintf(saved, "<span id=\"L%d\" class=\"ln\">%6d\t</span>", n, n)
+		// The line numbers are inserted into the document via a CSS ::before
+		// pseudo-element. This prevents them from being copied when users
+		// highlight and copy text.
+		// ::before is supported in 98% of browsers: https://caniuse.com/#feat=css-gencontent
+		// This is also the trick Github uses to hide line numbers.
+		//
+		// The first tab for the code snippet needs to start in column 9, so
+		// it indents a full 8 spaces, hence the two nbsp's. Otherwise the tab
+		// character only indents about two spaces.
+		fmt.Fprintf(saved, `<span id="L%d" class="ln" data-content="%6d">&nbsp;&nbsp;</span>`, n, n)
 		n++
 		saved.Write(line)
 		saved.WriteByte('\n')
diff --git a/godoc/static/static.go b/godoc/static/static.go
index c89ffaa..c8915765 100644
--- a/godoc/static/static.go
+++ b/godoc/static/static.go
@@ -2932,6 +2932,11 @@
 	-ms-user-select: none;
 	user-select: none;
 }
+.ln::before {
+	/* Inserting the line numbers as a ::before pseudo-element avoids making
+	 * them selectable; it's the trick Github uses as well. */
+	content: attr(data-content);
+}
 body {
 	color: #222;
 }
diff --git a/godoc/static/style.css b/godoc/static/style.css
index 025327b..e89ac29 100644
--- a/godoc/static/style.css
+++ b/godoc/static/style.css
@@ -37,6 +37,11 @@
 	-ms-user-select: none;
 	user-select: none;
 }
+.ln::before {
+	/* Inserting the line numbers as a ::before pseudo-element avoids making
+	 * them selectable; it's the trick Github uses as well. */
+	content: attr(data-content);
+}
 body {
 	color: #222;
 }