From f2fbf9e7cae3b257e280c2feed16dc1bd68c49f4 Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Thu, 10 Nov 2022 17:24:44 +0100 Subject: [PATCH] fix(window post message communication): set target origin on creation Signed-off-by: Tilman Vatteroth --- src/components/common/motd-modal/motd-modal.test.tsx | 9 +++++++-- ...tor-to-renderer-communicator-context-provider.tsx | 8 +++++++- ...derer-to-editor-communicator-context-provider.tsx | 10 +++++----- .../editor-page/renderer-pane/render-iframe.tsx | 8 ++------ .../window-post-message-communicator.ts | 12 ++++++------ 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/components/common/motd-modal/motd-modal.test.tsx b/src/components/common/motd-modal/motd-modal.test.tsx index 1115cf85f..c2bd07325 100644 --- a/src/components/common/motd-modal/motd-modal.test.tsx +++ b/src/components/common/motd-modal/motd-modal.test.tsx @@ -7,20 +7,25 @@ import { MotdModal } from './motd-modal' import { act, render, screen } from '@testing-library/react' import * as fetchMotdModule from './fetch-motd' +import type { CommonModalProps } from '../modals/common-modal' import * as CommonModalModule from '../modals/common-modal' import type { PropsWithChildren } from 'react' import React from 'react' -import type { CommonModalProps } from '../modals/common-modal' import { mockI18n } from '../../markdown-renderer/test-utils/mock-i18n' import * as RenderIframeModule from '../../editor-page/renderer-pane/render-iframe' import { testId } from '../../../utils/test-id' +import * as UseBaseUrlModule from '../../../hooks/common/use-base-url' jest.mock('./fetch-motd') jest.mock('../modals/common-modal') jest.mock('../../editor-page/renderer-pane/render-iframe') +jest.mock('../../../hooks/common/use-base-url') describe('motd modal', () => { - beforeAll(mockI18n) + beforeAll(async () => { + jest.spyOn(UseBaseUrlModule, 'useBaseUrl').mockImplementation(() => 'https://example.org') + await mockI18n() + }) afterAll(() => { jest.resetAllMocks() diff --git a/src/components/editor-page/render-context/editor-to-renderer-communicator-context-provider.tsx b/src/components/editor-page/render-context/editor-to-renderer-communicator-context-provider.tsx index bc741f11f..1e2237abc 100644 --- a/src/components/editor-page/render-context/editor-to-renderer-communicator-context-provider.tsx +++ b/src/components/editor-page/render-context/editor-to-renderer-communicator-context-provider.tsx @@ -8,6 +8,7 @@ import type { PropsWithChildren } from 'react' import React, { createContext, useContext, useEffect, useMemo } from 'react' import { EditorToRendererCommunicator } from '../../render-page/window-post-message-communicator/editor-to-renderer-communicator' import { v4 as uuid } from 'uuid' +import { ORIGIN, useBaseUrl } from '../../../hooks/common/use-base-url' const EditorToRendererCommunicatorContext = createContext(undefined) @@ -29,7 +30,12 @@ export const useEditorToRendererCommunicator: () => EditorToRendererCommunicator * Provides a {@link EditorToRendererCommunicator editor to renderer communicator} for the child components via Context. */ export const EditorToRendererCommunicatorContextProvider: React.FC = ({ children }) => { - const communicator = useMemo(() => new EditorToRendererCommunicator(uuid()), []) + const rendererUrl = useBaseUrl(ORIGIN.RENDERER) + + const communicator = useMemo( + () => new EditorToRendererCommunicator(uuid(), new URL(rendererUrl).origin), + [rendererUrl] + ) useEffect(() => { const currentCommunicator = communicator diff --git a/src/components/editor-page/render-context/renderer-to-editor-communicator-context-provider.tsx b/src/components/editor-page/render-context/renderer-to-editor-communicator-context-provider.tsx index 0c95fdf6e..bcd6ca632 100644 --- a/src/components/editor-page/render-context/renderer-to-editor-communicator-context-provider.tsx +++ b/src/components/editor-page/render-context/renderer-to-editor-communicator-context-provider.tsx @@ -28,27 +28,27 @@ export const useRendererToEditorCommunicator: () => RendererToEditorCommunicator } export const RendererToEditorCommunicatorContextProvider: React.FC> = ({ children }) => { - const editorOrigin = useBaseUrl(ORIGIN.EDITOR) + const editorUrl = useBaseUrl(ORIGIN.EDITOR) const uuid = useSingleStringUrlParameter('uuid', undefined) const communicator = useMemo(() => { if (uuid === undefined) { throw new Error('no uuid found in url!') } else { - return new RendererToEditorCommunicator(uuid) + return new RendererToEditorCommunicator(uuid, new URL(editorUrl).origin) } - }, [uuid]) + }, [editorUrl, uuid]) useEffect(() => { const currentCommunicator = communicator - currentCommunicator.setMessageTarget(window.parent, new URL(editorOrigin).origin) + currentCommunicator.setMessageTarget(window.parent) currentCommunicator.registerEventListener() currentCommunicator.enableCommunication() currentCommunicator.sendMessageToOtherSide({ type: CommunicationMessageType.RENDERER_READY }) return () => currentCommunicator?.unregisterEventListener() - }, [communicator, editorOrigin]) + }, [communicator, editorUrl]) /** * Provides a {@link RendererToEditorCommunicator renderer to editor communicator} for the child components via Context. diff --git a/src/components/editor-page/renderer-pane/render-iframe.tsx b/src/components/editor-page/renderer-pane/render-iframe.tsx index 53cce6448..08691a236 100644 --- a/src/components/editor-page/renderer-pane/render-iframe.tsx +++ b/src/components/editor-page/renderer-pane/render-iframe.tsx @@ -23,7 +23,6 @@ import { useSendScrollState } from './hooks/use-send-scroll-state' import { Logger } from '../../../utils/logger' import { useEffectOnRenderTypeChange } from './hooks/use-effect-on-render-type-change' import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute' -import { ORIGIN, useBaseUrl } from '../../../hooks/common/use-base-url' import { ShowIf } from '../../common/show-if/show-if' import { WaitSpinner } from '../../common/wait-spinner/wait-spinner' import { useExtensionEventEmitter } from '../../markdown-renderer/hooks/use-extension-event-emitter' @@ -69,7 +68,6 @@ export const RenderIframe: React.FC = ({ }) => { const [rendererReady, setRendererReady] = useState(false) const frameReference = useRef(null) - const rendererBaseUrl = useBaseUrl(ORIGIN.RENDERER) const iframeCommunicator = useEditorToRendererCommunicator() const resetRendererReady = useCallback(() => { log.debug('Reset render status') @@ -143,9 +141,7 @@ export const RenderIframe: React.FC = ({ log.error('Load triggered without content window') return } - const origin = new URL(rendererBaseUrl).origin - log.debug(`Set iframecommunicator window with origin ${origin ?? 'undefined'}`) - iframeCommunicator.setMessageTarget(otherWindow, origin) + iframeCommunicator.setMessageTarget(otherWindow) iframeCommunicator.enableCommunication() iframeCommunicator.sendMessageToOtherSide({ type: CommunicationMessageType.SET_BASE_CONFIGURATION, @@ -155,7 +151,7 @@ export const RenderIframe: React.FC = ({ } }) setRendererReady(true) - }, [iframeCommunicator, rendererBaseUrl, rendererType]) + }, [iframeCommunicator, rendererType]) ) useEffectOnRenderTypeChange(rendererType, onIframeLoad) diff --git a/src/components/render-page/window-post-message-communicator/window-post-message-communicator.ts b/src/components/render-page/window-post-message-communicator/window-post-message-communicator.ts index 8f8a451e4..888753bed 100644 --- a/src/components/render-page/window-post-message-communicator/window-post-message-communicator.ts +++ b/src/components/render-page/window-post-message-communicator/window-post-message-communicator.ts @@ -35,13 +35,12 @@ export abstract class WindowPostMessageCommunicator< MESSAGES extends MessagePayload > { private messageTarget?: Window - private targetOrigin?: string private communicationEnabled: boolean private readonly emitter: EventEmitter2 = new EventEmitter2() private readonly log: Logger private readonly boundListener: (event: MessageEvent) => void - public constructor(private uuid: string) { + public constructor(private readonly uuid: string, private readonly targetOrigin: string) { this.boundListener = this.handleEvent.bind(this) this.communicationEnabled = false this.log = this.createLogger() @@ -72,12 +71,10 @@ export abstract class WindowPostMessageCommunicator< * Messages can be sent as soon as the communication is enabled. * * @param otherSide The target {@link Window} that should receive the messages. - * @param otherOrigin The origin from the URL of the target. If this isn't correct then the message sending will produce CORS errors. * @see enableCommunication */ - public setMessageTarget(otherSide: Window, otherOrigin: string): void { + public setMessageTarget(otherSide: Window): void { this.messageTarget = otherSide - this.targetOrigin = otherOrigin this.communicationEnabled = false } @@ -86,7 +83,6 @@ export abstract class WindowPostMessageCommunicator< */ public unsetMessageTarget(): void { this.messageTarget = undefined - this.targetOrigin = undefined this.communicationEnabled = false } @@ -152,6 +148,10 @@ export abstract class WindowPostMessageCommunicator< */ protected handleEvent(event: MessageEvent>): void { if (event.origin !== this.targetOrigin) { + this.log.error( + `message declined. origin was "${event.origin}" but expected "${String(this.targetOrigin)}"`, + event.data + ) return } Optional.ofNullable(event.data)