From bd41d7b216da4182092477285a3ce35f979e2291 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Fri, 7 Apr 2023 09:42:12 -0700 Subject: [PATCH] Use synchronous IPC for passing config --- app/main.ts | 146 +++++++++++++++--------------- ts/context/config.ts | 10 +- ts/test-both/util/url_test.ts | 4 - ts/types/RendererConfig.ts | 4 - ts/util/url.ts | 16 ++-- ts/windows/permissions/preload.ts | 4 +- 6 files changed, 87 insertions(+), 97 deletions(-) diff --git a/app/main.ts b/app/main.ts index 55266627d..fc3f057e8 100644 --- a/app/main.ts +++ b/app/main.ts @@ -402,79 +402,7 @@ async function prepareUrl( url: URL, { forCalling, forCamera }: PrepareUrlOptions = {} ): Promise { - const theme = await getResolvedThemeSetting(); - - const directoryConfig = directoryConfigSchema.safeParse({ - directoryUrl: config.get('directoryUrl') || undefined, - directoryMRENCLAVE: - config.get('directoryMRENCLAVE') || undefined, - }); - if (!directoryConfig.success) { - throw new Error( - `prepareUrl: Failed to parse renderer directory config ${JSON.stringify( - directoryConfig.error.flatten() - )}` - ); - } - - const urlParams: RendererConfigType = { - name: packageJson.productName, - resolvedTranslationsLocale: getResolvedMessagesLocale().name, - preferredSystemLocales: getPreferredSystemLocales(), - version: app.getVersion(), - buildCreation: config.get('buildCreation'), - buildExpiration: config.get('buildExpiration'), - challengeUrl: config.get('challengeUrl'), - serverUrl: config.get('serverUrl'), - storageUrl: config.get('storageUrl'), - updatesUrl: config.get('updatesUrl'), - resourcesUrl: config.get('resourcesUrl'), - artCreatorUrl: config.get('artCreatorUrl'), - cdnUrl0: config.get('cdn').get('0'), - cdnUrl2: config.get('cdn').get('2'), - certificateAuthority: config.get('certificateAuthority'), - environment: enableCI ? Environment.Production : getEnvironment(), - enableCI, - nodeVersion: process.versions.node, - hostname: os.hostname(), - appInstance: process.env.NODE_APP_INSTANCE || undefined, - proxyUrl: process.env.HTTPS_PROXY || process.env.https_proxy || undefined, - contentProxyUrl: config.get('contentProxyUrl'), - sfuUrl: config.get('sfuUrl'), - reducedMotionSetting: animationSettings.prefersReducedMotion, - registrationChallengeUrl: config.get('registrationChallengeUrl'), - serverPublicParams: config.get('serverPublicParams'), - serverTrustRoot: config.get('serverTrustRoot'), - theme, - appStartInitialSpellcheckSetting, - userDataPath: app.getPath('userData'), - homePath: app.getPath('home'), - crashDumpsPath: app.getPath('crashDumps'), - - directoryConfig: directoryConfig.data, - - // Only used by the main window - isMainWindowFullScreen: Boolean(mainWindow?.isFullScreen()), - isMainWindowMaximized: Boolean(mainWindow?.isMaximized()), - - // Only for tests - argv: JSON.stringify(process.argv), - - // Only for permission popup window - forCalling: Boolean(forCalling), - forCamera: Boolean(forCamera), - }; - - const parsed = rendererConfigSchema.safeParse(urlParams); - if (!parsed.success) { - throw new Error( - `prepareUrl: Failed to parse renderer config ${JSON.stringify( - parsed.error.flatten() - )}` - ); - } - - return setUrlSearchParams(url, { config: JSON.stringify(parsed.data) }).href; + return setUrlSearchParams(url, { forCalling, forCamera }).href; } async function handleUrl(rawTarget: string) { @@ -2287,6 +2215,78 @@ ipc.on('get-built-in-images', async () => { } }); +ipc.on('get-config', async event => { + const theme = await getResolvedThemeSetting(); + + const directoryConfig = directoryConfigSchema.safeParse({ + directoryUrl: config.get('directoryUrl') || undefined, + directoryMRENCLAVE: + config.get('directoryMRENCLAVE') || undefined, + }); + if (!directoryConfig.success) { + throw new Error( + `prepareUrl: Failed to parse renderer directory config ${JSON.stringify( + directoryConfig.error.flatten() + )}` + ); + } + + const parsed = rendererConfigSchema.safeParse({ + name: packageJson.productName, + resolvedTranslationsLocale: getResolvedMessagesLocale().name, + preferredSystemLocales: getPreferredSystemLocales(), + version: app.getVersion(), + buildCreation: config.get('buildCreation'), + buildExpiration: config.get('buildExpiration'), + challengeUrl: config.get('challengeUrl'), + serverUrl: config.get('serverUrl'), + storageUrl: config.get('storageUrl'), + updatesUrl: config.get('updatesUrl'), + resourcesUrl: config.get('resourcesUrl'), + artCreatorUrl: config.get('artCreatorUrl'), + cdnUrl0: config.get('cdn').get('0'), + cdnUrl2: config.get('cdn').get('2'), + certificateAuthority: config.get('certificateAuthority'), + environment: enableCI ? Environment.Production : getEnvironment(), + enableCI, + nodeVersion: process.versions.node, + hostname: os.hostname(), + appInstance: process.env.NODE_APP_INSTANCE || undefined, + proxyUrl: process.env.HTTPS_PROXY || process.env.https_proxy || undefined, + contentProxyUrl: config.get('contentProxyUrl'), + sfuUrl: config.get('sfuUrl'), + reducedMotionSetting: animationSettings.prefersReducedMotion, + registrationChallengeUrl: config.get('registrationChallengeUrl'), + serverPublicParams: config.get('serverPublicParams'), + serverTrustRoot: config.get('serverTrustRoot'), + theme, + appStartInitialSpellcheckSetting, + userDataPath: app.getPath('userData'), + homePath: app.getPath('home'), + crashDumpsPath: app.getPath('crashDumps'), + + directoryConfig: directoryConfig.data, + + // Only used by the main window + isMainWindowFullScreen: Boolean(mainWindow?.isFullScreen()), + isMainWindowMaximized: Boolean(mainWindow?.isMaximized()), + + // Only for tests + argv: JSON.stringify(process.argv), + } satisfies RendererConfigType); + + if (!parsed.success) { + throw new Error( + `prepareUrl: Failed to parse renderer config ${JSON.stringify( + parsed.error.flatten() + )}` + ); + } + + // eslint-disable-next-line no-param-reassign + event.returnValue = parsed.data; +}); + // Ingested in preload.js via a sendSync call ipc.on('locale-data', event => { // eslint-disable-next-line no-param-reassign diff --git a/ts/context/config.ts b/ts/context/config.ts index c67f20adf..bed045132 100644 --- a/ts/context/config.ts +++ b/ts/context/config.ts @@ -1,12 +1,10 @@ // Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import type { RendererConfigType } from '../types/RendererConfig'; -import { strictAssert } from '../util/assert'; +import { ipcRenderer } from 'electron'; -const params = new URLSearchParams(document.location.search); -const configParam = params.get('config'); -strictAssert(typeof configParam === 'string', 'config is not a string'); -const config: RendererConfigType = JSON.parse(configParam); +import type { RendererConfigType } from '../types/RendererConfig'; + +const config: RendererConfigType = ipcRenderer.sendSync('get-config'); export { config }; diff --git a/ts/test-both/util/url_test.ts b/ts/test-both/util/url_test.ts index e0727651c..0d0bc4335 100644 --- a/ts/test-both/util/url_test.ts +++ b/ts/test-both/util/url_test.ts @@ -44,8 +44,6 @@ describe('URL utilities', () => { number: 123, true_bool: true, false_bool: false, - null_value: null, - undefined_value: undefined, array: ['ok', 'wow'], stringified: { toString: () => 'bar' }, }; @@ -67,8 +65,6 @@ describe('URL utilities', () => { assert.strictEqual(newUrl.searchParams.get('number'), '123'); assert.strictEqual(newUrl.searchParams.get('true_bool'), 'true'); assert.strictEqual(newUrl.searchParams.get('false_bool'), 'false'); - assert.strictEqual(newUrl.searchParams.get('null_value'), ''); - assert.strictEqual(newUrl.searchParams.get('undefined_value'), ''); assert.strictEqual(newUrl.searchParams.get('array'), 'ok,wow'); assert.strictEqual(newUrl.searchParams.get('stringified'), 'bar'); }); diff --git a/ts/types/RendererConfig.ts b/ts/types/RendererConfig.ts index d2cb8801c..52f2f73ab 100644 --- a/ts/types/RendererConfig.ts +++ b/ts/types/RendererConfig.ts @@ -64,10 +64,6 @@ export const rendererConfigSchema = z.object({ // Only for tests argv: configOptionalStringSchema, - - // Only for permission popup window - forCalling: z.boolean(), - forCamera: z.boolean(), }); export type RendererConfigType = z.infer; diff --git a/ts/util/url.ts b/ts/util/url.ts index c934128d9..1f12dbcf2 100644 --- a/ts/util/url.ts +++ b/ts/util/url.ts @@ -1,8 +1,6 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { mapValues } from 'lodash'; - export function maybeParseUrl(value: string): undefined | URL { if (typeof value === 'string') { try { @@ -20,9 +18,13 @@ export function setUrlSearchParams( searchParams: Readonly> ): URL { const result = cloneUrl(url); - result.search = new URLSearchParams( - mapValues(searchParams, stringifySearchParamValue) - ).toString(); + result.search = ''; + for (const [key, value] of Object.entries(searchParams)) { + if (value == null) { + continue; + } + result.searchParams.append(key, String(value)); + } return result; } @@ -30,10 +32,6 @@ function cloneUrl(url: Readonly): URL { return new URL(url.href); } -function stringifySearchParamValue(value: unknown): string { - return value == null ? '' : String(value); -} - export function urlPathFromComponents( components: ReadonlyArray ): string { diff --git a/ts/windows/permissions/preload.ts b/ts/windows/permissions/preload.ts index e3a3fa458..6912d8361 100644 --- a/ts/windows/permissions/preload.ts +++ b/ts/windows/permissions/preload.ts @@ -25,7 +25,9 @@ contextBridge.exposeInMainWorld( contextBridge.exposeInMainWorld('SignalContext', { ...SignalContext, renderWindow: () => { - const { forCalling, forCamera } = SignalContext.config; + const params = new URLSearchParams(document.location.search); + const forCalling = params.get('forCalling') === 'true'; + const forCamera = params.get('forCamera') === 'true'; let message; if (forCalling) {