From 444039c142e2f7bcba8cf5eb61cd6318c399c6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 8 Apr 2025 18:43:06 +0200 Subject: [PATCH] Fix opening of links when v1 is embedded in an iframe (#3122) --- .../src/components/primitives/Link.tsx | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/gitbook/src/components/primitives/Link.tsx b/packages/gitbook/src/components/primitives/Link.tsx index 19fa23fbc..5cb026d3a 100644 --- a/packages/gitbook/src/components/primitives/Link.tsx +++ b/packages/gitbook/src/components/primitives/Link.tsx @@ -50,7 +50,7 @@ export const Link = React.forwardRef(function Link( // When the page is embedded in an iframe, for security reasons other urls cannot be opened. // In this case, we open the link in a new tab. - if (isExternal && window.self !== window.top) { + if (window.self !== window.top && isExternalLink(href, window.location.origin)) { event.preventDefault(); window.open(href, '_blank'); } @@ -58,7 +58,9 @@ export const Link = React.forwardRef(function Link( domProps.onClick?.(event); }; - if (isExternal) { + // We test if the link is external, without comparing to the origin + // as this will be rendered on the server and it could result in a mismatch. + if (isExternalLink(href)) { return ( {children} @@ -72,3 +74,27 @@ export const Link = React.forwardRef(function Link( ); }); + +/** + * Check if a link is external, compared to an origin. + */ +function isExternalLink(href: string, origin: string | null = null) { + if (!URL.canParse) { + // If URL.canParse is not available, we quickly check if it looks like a URL + return href.startsWith('http'); + } + + if (!URL.canParse(href)) { + // If we can't parse the href, we consider it a relative path + return false; + } + + if (!origin) { + // If origin is not provided, we consider the link external + return true; + } + + // If the url points to the same origin, we consider it internal + const parsed = new URL(href); + return parsed.origin !== origin; +}