Skip to content

Commit 9d85eee

Browse files
committed
fix: create more reliable infinite loop protection
In some contexts, the navigator may say cookies are enabled, but setting the actual cookie does nothing. So we set a temporary cookie, check that it was set, and only if that works will we proceed. Also refactored the code to make early exits possible.
1 parent f33ed87 commit 9d85eee

File tree

1 file changed

+36
-24
lines changed

1 file changed

+36
-24
lines changed

src/index.ts

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -50,32 +50,44 @@ export function getHintUtils<Hints extends Record<string, ClientHint<any>>>(
5050
*/
5151
function getClientHintCheckScript() {
5252
return `
53-
const cookies = document.cookie.split(';').map(c => c.trim()).reduce((acc, cur) => {
54-
const [key, value] = cur.split('=');
55-
acc[key] = value;
56-
return acc;
57-
}, {});
58-
let cookieChanged = false;
59-
const hints = [
60-
${Object.values(hints)
61-
.map((hint) => {
62-
const cookieName = JSON.stringify(hint.cookieName)
63-
return `{ name: ${cookieName}, actual: String(${hint.getValueCode}), value: cookies[${cookieName}] != null ? cookies[${cookieName}] : encodeURIComponent("${hint.fallback}") }`
64-
})
65-
.join(',\n')}
66-
];
67-
for (const hint of hints) {
68-
document.cookie = encodeURIComponent(hint.name) + '=' + encodeURIComponent(hint.actual) + '; Max-Age=31536000; path=/';
69-
if (decodeURIComponent(hint.value) !== hint.actual) {
70-
cookieChanged = true;
53+
// This block of code allows us to check if the client hints have changed and
54+
// force a reload of the page with updated hints if they have so you don't get
55+
// a flash of incorrect content.
56+
function checkClientHints() {
57+
if (!navigator.cookieEnabled) return;
58+
59+
// set a short-lived cookie to make sure we can set cookies
60+
document.cookie = "canSetCookies=1; Max-Age=60; SameSite=Lax";
61+
const canSetCookies = document.cookie.includes("canSetCookies=1");
62+
document.cookie = "canSetCookies=; Max-Age=-1; path=/";
63+
if (!canSetCookies) return;
64+
65+
const cookies = document.cookie.split(';').map(c => c.trim()).reduce((acc, cur) => {
66+
const [key, value] = cur.split('=');
67+
acc[key] = value;
68+
return acc;
69+
}, {});
70+
71+
let cookieChanged = false;
72+
const hints = [
73+
${Object.values(hints)
74+
.map((hint) => {
75+
const cookieName = JSON.stringify(hint.cookieName)
76+
return `{ name: ${cookieName}, actual: String(${hint.getValueCode}), value: cookies[${cookieName}] != null ? cookies[${cookieName}] : encodeURIComponent("${hint.fallback}") }`
77+
})
78+
.join(',\n')}
79+
];
80+
for (const hint of hints) {
81+
document.cookie = encodeURIComponent(hint.name) + '=' + encodeURIComponent(hint.actual) + '; Max-Age=31536000; path=/';
82+
if (decodeURIComponent(hint.value) !== hint.actual) {
83+
cookieChanged = true;
84+
}
7185
}
86+
if (cookieChanged) window.location.reload();
7287
}
73-
// if the cookie changed, reload the page, unless the browser doesn't support
74-
// cookies (in which case we would enter an infinite loop of reloads)
75-
if (cookieChanged && navigator.cookieEnabled) {
76-
window.location.reload();
77-
}
78-
`
88+
89+
checkClientHints();
90+
`
7991
}
8092

8193
return { getHints, getClientHintCheckScript }

0 commit comments

Comments
 (0)