Skip to content

Commit

Permalink
Fix mermaid / contentkit webframe not updating properly (#2427)
Browse files Browse the repository at this point in the history
  • Loading branch information
SamyPesse authored Aug 13, 2024
1 parent 3996110 commit 0f1565c
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 26 deletions.
5 changes: 5 additions & 0 deletions .changeset/red-bats-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@gitbook/react-contentkit': patch
---

Fix rendering of webframe with SSR causing wrong communication between frame and renderer
5 changes: 5 additions & 0 deletions .changeset/shy-buttons-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'gitbook': patch
---

Add optional env `GITBOOK_INTEGRATIONS_HOST` to configure the host serving the integrations
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Icon } from '@gitbook/icons';
import { ContentKit, ContentKitOutput, ContentKitServerContext } from '@gitbook/react-contentkit';

import { ignoreAPIError, renderIntegrationUi } from '@/lib/api';
import { INTEGRATIONS_HOST } from '@/lib/csp';
import { parseMarkdown } from '@/lib/markdown';
import { tcls } from '@/lib/tailwind';

Expand Down Expand Up @@ -69,7 +70,7 @@ export async function IntegrationBlock(props: BlockProps<DocumentBlockIntegratio
return (
<div className={tcls(style)}>
<ContentKit
security={{ firstPartyDomains: ['integrations.gitbook.com'] }}
security={{ firstPartyDomains: [INTEGRATIONS_HOST] }}
initialInput={initialInput}
initialOutput={initialOutput}
render={async (request) => {
Expand Down
10 changes: 8 additions & 2 deletions packages/gitbook/src/lib/csp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ export function createContentSecurityPolicyNonce(): string {
return nonce;
}

/**
* Hostname serving the integrations.
*/
export const INTEGRATIONS_HOST =
process.env.GITBOOK_INTEGRATIONS_HOST ?? 'integrations.gitbook.com';

/**
* Generate a Content Security Policy header for a space.
*/
Expand All @@ -39,10 +45,10 @@ export function getContentSecurityPolicy(scripts: SpaceIntegrationScript[], nonc
// Since I can't get the nonce to work for inline styles, we need to allow unsafe-inline
const defaultCSP = `
default-src 'self' ${assetsDomain};
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' ${assetsDomain} https://integrations.gitbook.com https://cdn.iframe.ly;
script-src 'self' 'nonce-${nonce}' 'strict-dynamic' 'unsafe-inline' 'unsafe-eval' ${assetsDomain} https://${INTEGRATIONS_HOST} https://cdn.iframe.ly;
style-src 'self' ${assetsDomain} fonts.googleapis.com 'unsafe-inline';
img-src * 'self' blob: data: files.gitbook.com ${assetsDomain} ${iconsAssetsSrc};
connect-src * 'self' integrations.gitbook.com app.gitbook.com api.gitbook.com srv.buysellads.com ${assetsDomain} ${iconsAssetsSrc};
connect-src * 'self' ${INTEGRATIONS_HOST} app.gitbook.com api.gitbook.com srv.buysellads.com ${assetsDomain} ${iconsAssetsSrc};
font-src 'self' fonts.gstatic.com ${assetsDomain};
frame-src *;
object-src 'none';
Expand Down
55 changes: 32 additions & 23 deletions packages/react-contentkit/src/ElementWebframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { resolveDynamicBinding } from './dynamic';
export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWebFrame>) {
const { element } = props;

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

const sendMessage = React.useCallback(
(message: object) => {
if (!iframeRef.current) {
return;
}

const target = new URL(element.source.url);

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

if (readyRef.current) {
if (!iframeRef.current) {
return;
}

iframeRef.current.contentWindow!.postMessage(
message,
`${target.protocol}//${target.host}`,
Expand All @@ -61,7 +62,7 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe

// For security reasons, only iframe from our integrations domains are allowed
// to send and receive messages
if (!renderer.security.firstPartyDomains.includes(origin.host) && 0) {
if (!renderer.security.firstPartyDomains.includes(origin.host)) {
return;
}

Expand Down Expand Up @@ -117,6 +118,11 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
};

window.addEventListener('message', callback);

// We only render the iframe once we have added the event listener
// otherwise during SSR, we'll miss messages
setMounted(true);

return () => {
window.removeEventListener('message', callback);
};
Expand All @@ -142,26 +148,29 @@ export function ElementWebframe(props: ContentKitClientElementProps<ContentKitWe
<div
className={`contentkit-webframe`}
style={{
aspectRatio: element.aspectRatio,
...size,
aspectRatio: size.aspectRatio || element.aspectRatio || undefined,
maxWidth: size.maxWidth || undefined,
maxHeight: size.maxHeight || undefined,
}}
>
<iframe
ref={iframeRef}
src={element.source.url}
allowFullScreen
allow="clipboard-write"
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
width: '100%',
height: '100%',
border: 'none',
}}
/>
{mounted ? (
<iframe
ref={iframeRef}
src={element.source.url}
allowFullScreen
allow="clipboard-write"
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
width: '100%',
height: '100%',
border: 'none',
}}
/>
) : null}
</div>
);
}

0 comments on commit 0f1565c

Please sign in to comment.