From 9c6dcb4fba459e5c15b127f82db8f7bed3f705e4 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 8 Aug 2023 15:11:23 -0400 Subject: [PATCH 1/4] Handle disbaled preload config in firefox --- packages/remix-react/links.ts | 54 ++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/packages/remix-react/links.ts b/packages/remix-react/links.ts index ec4962fb113..731b3ebd4c2 100644 --- a/packages/remix-react/links.ts +++ b/packages/remix-react/links.ts @@ -221,6 +221,9 @@ export function getLinksForMatches( return dedupe(descriptors, preloads); } +let stylesheetPreloadTimeouts = 0; +let isPreloadDisabled = false; + export async function prefetchStyleLinks( routeModule: RouteModule ): Promise { @@ -228,6 +231,36 @@ export async function prefetchStyleLinks( let descriptors = routeModule.links(); if (!descriptors) return; + if (isPreloadDisabled) { + return; + } + + // If we've hit our timeout 3 times, we may be in firefox with the + // `network.preload` config disabled and we'll _never_ get onload/onerror + // callbacks. Let's try to confirm this with a totally invalid link preload + // which should immediately throw the onerror + if (stylesheetPreloadTimeouts >= 3) { + let linkLoadedOrErrored = await prefetchStyleLink({ + rel: "preload", + as: "style", + href: "__remix-preload-detection-404.css", + }); + if (linkLoadedOrErrored) { + // If this processed correctly, then our previous timeouts were probably + // legit, reset the counter. + stylesheetPreloadTimeouts = 0; + } else { + // If this bogus preload also times out without an onerror then it's safe + // to assume preloading is disabled and let's just stop trying. This + // _will_ cause FOUC on destination pages but there's nothing we can + // really do there if preloading is disabled since client-side injected + // scripts aren't render blocking. Maybe eventually React's client side + // async component stuff will provide an easier solution here + console.warn("Disabling preload due to lack of browser support"); + isPreloadDisabled = true; + } + } + let styleLinks: HtmlLinkDescriptor[] = []; for (let descriptor of descriptors) { if (!isPageLinkDescriptor(descriptor) && descriptor.rel === "stylesheet") { @@ -246,17 +279,15 @@ export async function prefetchStyleLinks( (!link.media || window.matchMedia(link.media).matches) && !document.querySelector(`link[rel="stylesheet"][href="${link.href}"]`) ); - await Promise.all(matchingLinks.map(prefetchStyleLink)); } async function prefetchStyleLink( descriptor: HtmlLinkDescriptor -): Promise { +): Promise { return new Promise((resolve) => { let link = document.createElement("link"); Object.assign(link, descriptor); - function removeLink() { // if a navigation interrupts this prefetch React will update the // and remove the link we put in there manually, so we check if it's still @@ -266,20 +297,23 @@ async function prefetchStyleLink( } } - link.onload = () => { + // Allow 3s for the link preload to timeout + let timeoutId = setTimeout(() => { + stylesheetPreloadTimeouts++; removeLink(); - resolve(); - }; + resolve(false); + }, 3_000); - link.onerror = () => { + let done = () => { + clearTimeout(timeoutId); removeLink(); - resolve(); + resolve(true); }; - + link.onload = done; + link.onerror = done; document.head.appendChild(link); }); } - //////////////////////////////////////////////////////////////////////////////// export function isPageLinkDescriptor( object: any From 8879d8b6fe59f642f7aa5558ea4deafe004e2846 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 8 Aug 2023 15:38:10 -0400 Subject: [PATCH 2/4] add changeset --- .changeset/disabled-link-preload.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/disabled-link-preload.md diff --git a/.changeset/disabled-link-preload.md b/.changeset/disabled-link-preload.md new file mode 100644 index 00000000000..5578bd3d821 --- /dev/null +++ b/.changeset/disabled-link-preload.md @@ -0,0 +1,5 @@ +--- +"@remix-run/react": patch +--- + +Add `` timeout counter and disabling logic in case preloading is disabled by the user in Firefox. This prevents us from hanging on client-side navigations when we try to preload stylesheets and never receive a `load`/`error` event on the `link` tag. From ed8b507da67973e6e571c572f9aef7921aa6fc5c Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 8 Aug 2023 15:39:33 -0400 Subject: [PATCH 3/4] Remove whitespace changes --- packages/remix-react/links.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/remix-react/links.ts b/packages/remix-react/links.ts index 731b3ebd4c2..29310194432 100644 --- a/packages/remix-react/links.ts +++ b/packages/remix-react/links.ts @@ -288,6 +288,7 @@ async function prefetchStyleLink( return new Promise((resolve) => { let link = document.createElement("link"); Object.assign(link, descriptor); + function removeLink() { // if a navigation interrupts this prefetch React will update the // and remove the link we put in there manually, so we check if it's still @@ -314,6 +315,7 @@ async function prefetchStyleLink( document.head.appendChild(link); }); } + //////////////////////////////////////////////////////////////////////////////// export function isPageLinkDescriptor( object: any From 4aa2878c1dd051053b53ab266532d99f2faa3d56 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 8 Aug 2023 15:40:41 -0400 Subject: [PATCH 4/4] syntaxified --- packages/remix-react/links.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/remix-react/links.ts b/packages/remix-react/links.ts index 29310194432..8ff5025c2fc 100644 --- a/packages/remix-react/links.ts +++ b/packages/remix-react/links.ts @@ -230,10 +230,7 @@ export async function prefetchStyleLinks( if (!routeModule.links) return; let descriptors = routeModule.links(); if (!descriptors) return; - - if (isPreloadDisabled) { - return; - } + if (isPreloadDisabled) return; // If we've hit our timeout 3 times, we may be in firefox with the // `network.preload` config disabled and we'll _never_ get onload/onerror