Skip to content
Source
Sign inSign up
Open History to see the last commit

favicon.ts

85 lines2.6 KB

1import { isPublicFaviconHost } from "./hostClassification.ts";
2​
3function faviconUrlForPage(rawUrl: string | null | undefined, _size = 32): string | null {
4 if (!rawUrl || rawUrl.length > 4096) return null;
5 try {
6 const pageUrl = new URL(rawUrl);
7 if (pageUrl.protocol !== "http:" && pageUrl.protocol !== "https:") return null;
8 return new URL("/favicon.ico", pageUrl.origin).href;
9 } catch {
10 return null;
11 }
13​
14const THEMED_FAVICON_BY_HOSTNAME: Readonly<
15 Record<string, Readonly<{ light: string; dark: string }>>
16> = {
17 "github.com": {
18 light: "https://github.githubassets.com/favicons/favicon.svg",
19 dark: "https://github.githubassets.com/favicons/favicon-dark.svg",
20 },
21};
22​
23function themedFaviconUrlForPage(
24 rawUrl: string | null | undefined,
25 appearance: "light" | "dark",
26): string | null {
27 if (!rawUrl || rawUrl.length > 4096) return null;
28 try {
29 const pageUrl = new URL(rawUrl);
30 if (pageUrl.protocol !== "http:" && pageUrl.protocol !== "https:") return null;
31 return THEMED_FAVICON_BY_HOSTNAME[pageUrl.hostname.toLowerCase()]?.[appearance] ?? null;
32 } catch {
33 return null;
34 }
36​
37/** Accepts image URLs supplied by a trusted provider event. */
38function explicitFaviconUrl(rawUrl: string | null | undefined): string | null {
39 if (!rawUrl || rawUrl.length > 4096) return null;
40 try {
41 const url = new URL(rawUrl);
42 return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "data:"
43 ? url.href
44 : null;
45 } catch {
46 return null;
47 }
49​
50export function toolActivityFaviconUrl(
51 icon: {
52 readonly pageUrl: string;
53 readonly faviconUrl?: string | undefined;
54 readonly faviconUrlDark?: string | undefined;
55 },
56 appearance: "light" | "dark",
57 size = 32,
58): string | null {
59 if (appearance === "dark") {
60 return (
61 explicitFaviconUrl(icon.faviconUrlDark) ??
62 themedFaviconUrlForPage(icon.pageUrl, "dark") ??
63 explicitFaviconUrl(icon.faviconUrl) ??
64 faviconUrlForPage(icon.pageUrl, size)
65 );
66 }
67 return (
68 explicitFaviconUrl(icon.faviconUrl) ??
69 themedFaviconUrlForPage(icon.pageUrl, "light") ??
70 faviconUrlForPage(icon.pageUrl, size)
71 );
73​
74export function faviconUrlForOrigin(rawUrl: string | null | undefined, size = 32): string | null {
75 if (!rawUrl) return null;
76 try {
77 const url = new URL(rawUrl);
78 if (!url.hostname) return null;
79 if (url.protocol !== "http:" && url.protocol !== "https:") return null;
80 if (!isPublicFaviconHost(url.hostname)) return null;
81 return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(url.hostname)}&sz=${size}`;
82 } catch {
83 return null;
84 }