From c2b0da974dabfa8b28a8ae50500c6fcea9f124dc Mon Sep 17 00:00:00 2001 From: diego Date: Tue, 6 Oct 2026 10:26:37 -0300 Subject: [PATCH] flamegraph: give the page a favicon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every page perf-agent writes showed the browser's blank-document icon. A profiling session ends up with a dozen of these open at once -- on-CPU, off-CPU, GPU, fused, before and after -- and they were indistinguishable in the tab strip. The icon is inlined as a base64 data: URI, not linked. assets.go states the contract the whole package is built around: nothing may reference an external URL, because the deliverable is one file that renders from file:// with no server and no network. A would be a broken icon everywhere but the directory it was written in. Base64 rather than percent-encoding because the markup contains #, < and " — the three characters that make a raw SVG data: URI fragile inside an HTML attribute. The artwork is the 16px mark, which is drawn for that size rather than scaled from the full one: the full mark loses its top rows below about 24px and reads as a smudge in a tab strip. TestRenderHTMLFetchesNothing forbade " and requires the href to be a data: URI. That is stricter than the old substring check, not looser: it also catches a that the old list would have missed by spelling. Verified by mutation -- pointing the favicon at an https URL fails it with "a must resolve to a data: URI". --- internal/flamegraph/assets.go | 13 +++++++++++++ internal/flamegraph/render.go | 3 ++- internal/flamegraph/render_test.go | 16 +++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/internal/flamegraph/assets.go b/internal/flamegraph/assets.go index 97a2149..91da1a9 100644 --- a/internal/flamegraph/assets.go +++ b/internal/flamegraph/assets.go @@ -25,6 +25,19 @@ import ( // one line of warning. The legend, the keyboard map, the notes and the label // table are all one keypress (or one icon) away. +// faviconHRef is the perf-agent mark, inlined as a data: URI so the page +// keeps its one-file contract -- a would +// render a broken tab icon from file:// and leak a request everywhere else. +// +// It is the 16px mark from docs/brand/perf-agent-favicon.svg, which is drawn +// for this size rather than scaled down from the full one: the full mark +// loses its top rows below about 24px and reads as a smudge in a tab strip. +// +// Base64 rather than percent-encoding because the markup contains #, < and +// " -- the three characters that make a raw SVG data: URI fragile inside an +// HTML attribute. +const faviconHRef = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHJlY3QgeD0iMSIgeT0iMTEiIHdpZHRoPSIxNCIgaGVpZ2h0PSI0IiByeD0iMS4zIiBmaWxsPSIjZDE0OTdmIi8+PHJlY3QgeD0iMSIgeT0iNiIgd2lkdGg9IjYiIGhlaWdodD0iNCIgcng9IjEuMyIgZmlsbD0iI2U4ODczYSIvPjxyZWN0IHg9IjgiIHk9IjYiIHdpZHRoPSI3IiBoZWlnaHQ9IjQiIHJ4PSIxLjMiIGZpbGw9IiMzZmEwYWIiLz48cmVjdCB4PSI5IiB5PSIxIiB3aWR0aD0iNiIgaGVpZ2h0PSI0IiByeD0iMS4zIiBmaWxsPSIjNDRhODVlIi8+PC9zdmc+" + // darkChrome is the page chrome's dark theme, written once and emitted // twice: once behind prefers-color-scheme for readers who never touch the // D key, once behind [data-theme="dark"] for readers who do. Two copies of diff --git a/internal/flamegraph/render.go b/internal/flamegraph/render.go index f37a43b..239e178 100644 --- a/internal/flamegraph/render.go +++ b/internal/flamegraph/render.go @@ -141,7 +141,8 @@ func RenderHTML(w io.Writer, res *foldedstacks.Result, opts Options) error { degenerate := res.Degenerate() ew.s("\n\n\n\n") - ew.s("\n") + ew.s("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n") + ew.s("<link rel=\"icon\" href=\"" + faviconHRef + "\">\n<title>") ew.esc(opts.Title) ew.s("\n