Skip to content

Commit 4f1253f

Browse files
committed
Merge branch 'main' into steeve/rnd-2147-automatically-scroll-to-the-current-page-in-table-of
2 parents ced8b6d + aa32198 commit 4f1253f

File tree

7 files changed

+59
-28
lines changed

7 files changed

+59
-28
lines changed

.changeset/beige-crabs-attack.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gitbook': patch
3+
---
4+
5+
Avoid multiple <h1> in the page by using a <div> for the title in the header

.changeset/red-bats-chew.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/react-contentkit': patch
3+
---
4+
5+
Fix rendering of webframe with SSR causing wrong communication between frame and renderer

.changeset/shy-buttons-relate.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gitbook': patch
3+
---
4+
5+
Add optional env `GITBOOK_INTEGRATIONS_HOST` to configure the host serving the integrations

packages/gitbook/src/components/DocumentView/Integration/IntegrationBlock.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Icon } from '@gitbook/icons';
33
import { ContentKit, ContentKitOutput, ContentKitServerContext } from '@gitbook/react-contentkit';
44

55
import { ignoreAPIError, renderIntegrationUi } from '@/lib/api';
6+
import { INTEGRATIONS_HOST } from '@/lib/csp';
67
import { parseMarkdown } from '@/lib/markdown';
78
import { tcls } from '@/lib/tailwind';
89

@@ -69,7 +70,7 @@ export async function IntegrationBlock(props: BlockProps<DocumentBlockIntegratio
6970
return (
7071
<div className={tcls(style)}>
7172
<ContentKit
72-
security={{ firstPartyDomains: ['integrations.gitbook.com'] }}
73+
security={{ firstPartyDomains: [INTEGRATIONS_HOST] }}
7374
initialInput={initialInput}
7475
initialOutput={initialOutput}
7576
render={async (request) => {

packages/gitbook/src/components/Header/HeaderLogo.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ function LogoFallback(props: HeaderLogoProps) {
108108
style={['object-contain', 'size-8']}
109109
fetchPriority="high"
110110
/>
111-
<h1
111+
<div
112112
className={tcls(
113113
'text-pretty',
114114
'line-clamp-3',
@@ -126,7 +126,7 @@ function LogoFallback(props: HeaderLogoProps) {
126126
)}
127127
>
128128
{getContentTitle(space, customization, parent)}
129-
</h1>
129+
</div>
130130
</>
131131
);
132132
}

packages/gitbook/src/lib/csp.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export function createContentSecurityPolicyNonce(): string {
2525
return nonce;
2626
}
2727

28+
/**
29+
* Hostname serving the integrations.
30+
*/
31+
export const INTEGRATIONS_HOST =
32+
process.env.GITBOOK_INTEGRATIONS_HOST ?? 'integrations.gitbook.com';
33+
2834
/**
2935
* Generate a Content Security Policy header for a space.
3036
*/
@@ -39,10 +45,10 @@ export function getContentSecurityPolicy(scripts: SpaceIntegrationScript[], nonc
3945
// Since I can't get the nonce to work for inline styles, we need to allow unsafe-inline
4046
const defaultCSP = `
4147
default-src 'self' ${assetsDomain};
42-
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' ${assetsDomain} https://integrations.gitbook.com https://cdn.iframe.ly;
48+
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' ${assetsDomain} https://${INTEGRATIONS_HOST} https://cdn.iframe.ly;
4349
style-src 'self' ${assetsDomain} fonts.googleapis.com 'unsafe-inline';
4450
img-src * 'self' blob: data: files.gitbook.com ${assetsDomain} ${iconsAssetsSrc};
45-
connect-src * 'self' integrations.gitbook.com app.gitbook.com api.gitbook.com srv.buysellads.com ${assetsDomain} ${iconsAssetsSrc};
51+
connect-src * 'self' ${INTEGRATIONS_HOST} app.gitbook.com api.gitbook.com srv.buysellads.com ${assetsDomain} ${iconsAssetsSrc};
4652
font-src 'self' fonts.gstatic.com ${assetsDomain};
4753
frame-src *;
4854
object-src 'none';

packages/react-contentkit/src/ElementWebframe.tsx

+32-23
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { resolveDynamicBinding } from './dynamic';
1010
export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWebFrame>) {
1111
const { element } = props;
1212

13+
const [mounted, setMounted] = React.useState(false);
1314
const renderer = useContentKitClientContext();
1415
const iframeRef = React.useRef<HTMLIFrameElement>(null);
1516
const [size, setSize] = React.useState<{
@@ -23,10 +24,6 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
2324

2425
const sendMessage = React.useCallback(
2526
(message: object) => {
26-
if (!iframeRef.current) {
27-
return;
28-
}
29-
3027
const target = new URL(element.source.url);
3128

3229
// For security reasons, only iframe from our integrations domains are allowed
@@ -36,6 +33,10 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
3633
}
3734

3835
if (readyRef.current) {
36+
if (!iframeRef.current) {
37+
return;
38+
}
39+
3940
iframeRef.current.contentWindow!.postMessage(
4041
message,
4142
`${target.protocol}//${target.host}`,
@@ -61,7 +62,7 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
6162

6263
// For security reasons, only iframe from our integrations domains are allowed
6364
// to send and receive messages
64-
if (!renderer.security.firstPartyDomains.includes(origin.host) && 0) {
65+
if (!renderer.security.firstPartyDomains.includes(origin.host)) {
6566
return;
6667
}
6768

@@ -117,6 +118,11 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
117118
};
118119

119120
window.addEventListener('message', callback);
121+
122+
// We only render the iframe once we have added the event listener
123+
// otherwise during SSR, we'll miss messages
124+
setMounted(true);
125+
120126
return () => {
121127
window.removeEventListener('message', callback);
122128
};
@@ -142,26 +148,29 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
142148
<div
143149
className={`contentkit-webframe`}
144150
style={{
145-
aspectRatio: element.aspectRatio,
146-
...size,
151+
aspectRatio: size.aspectRatio || element.aspectRatio || undefined,
152+
maxWidth: size.maxWidth || undefined,
153+
maxHeight: size.maxHeight || undefined,
147154
}}
148155
>
149-
<iframe
150-
ref={iframeRef}
151-
src={element.source.url}
152-
allowFullScreen
153-
allow="clipboard-write"
154-
style={{
155-
position: 'absolute',
156-
top: 0,
157-
left: 0,
158-
bottom: 0,
159-
right: 0,
160-
width: '100%',
161-
height: '100%',
162-
border: 'none',
163-
}}
164-
/>
156+
{mounted ? (
157+
<iframe
158+
ref={iframeRef}
159+
src={element.source.url}
160+
allowFullScreen
161+
allow="clipboard-write"
162+
style={{
163+
position: 'absolute',
164+
top: 0,
165+
left: 0,
166+
bottom: 0,
167+
right: 0,
168+
width: '100%',
169+
height: '100%',
170+
border: 'none',
171+
}}
172+
/>
173+
) : null}
165174
</div>
166175
);
167176
}

0 commit comments

Comments
 (0)