diff --git a/js/database.js b/js/database.js index d9ecbd4bd..2746c41c8 100644 --- a/js/database.js +++ b/js/database.js @@ -11,7 +11,7 @@ window.Whisper.Database.nolog = true; Whisper.Database.handleDOMException = (prefix, error, reject) => { - window.SignalWindow.log.error( + window.SignalContext.log.error( `${prefix}:`, error && error.name, error && error.message, diff --git a/js/expiring_messages.js b/js/expiring_messages.js index db50b8d05..3cf7099ea 100644 --- a/js/expiring_messages.js +++ b/js/expiring_messages.js @@ -13,13 +13,13 @@ async function destroyExpiredMessages() { try { - window.SignalWindow.log.info( + window.SignalContext.log.info( 'destroyExpiredMessages: Loading messages...' ); const messages = await window.Signal.Data.getExpiredMessages({ MessageCollection: Whisper.MessageCollection, }); - window.SignalWindow.log.info( + window.SignalContext.log.info( `destroyExpiredMessages: found ${messages.length} messages to expire` ); @@ -40,7 +40,7 @@ await Promise.all(messageCleanup); inMemoryMessages.forEach(message => { - window.SignalWindow.log.info('Message expired', { + window.SignalContext.log.info('Message expired', { sentAt: message.get('sent_at'), }); @@ -52,25 +52,25 @@ } }); } catch (error) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'destroyExpiredMessages: Error deleting expired messages', error && error.stack ? error.stack : error ); } - window.SignalWindow.log.info('destroyExpiredMessages: complete'); + window.SignalContext.log.info('destroyExpiredMessages: complete'); checkExpiringMessages(); } let timeout; async function checkExpiringMessages() { - window.SignalWindow.log.info( + window.SignalContext.log.info( 'checkExpiringMessages: checking for expiring messages' ); const soonestExpiry = await window.Signal.Data.getSoonestMessageExpiry(); if (!soonestExpiry) { - window.SignalWindow.log.info( + window.SignalContext.log.info( 'checkExpiringMessages: found no messages to expire' ); return; @@ -88,7 +88,7 @@ wait = 2147483647; } - window.SignalWindow.log.info( + window.SignalContext.log.info( `checkExpiringMessages: next message expires ${new Date( soonestExpiry ).toISOString()}; waiting ${wait} ms before clearing` diff --git a/js/expiring_tap_to_view_messages.js b/js/expiring_tap_to_view_messages.js index 3d9157d92..ad4fe7e9d 100644 --- a/js/expiring_tap_to_view_messages.js +++ b/js/expiring_tap_to_view_messages.js @@ -13,7 +13,7 @@ async function eraseTapToViewMessages() { try { - window.SignalWindow.log.info( + window.SignalContext.log.info( 'eraseTapToViewMessages: Loading messages...' ); const messages = await window.Signal.Data.getTapToViewMessagesNeedingErase( @@ -26,7 +26,7 @@ messages.map(async fromDB => { const message = MessageController.register(fromDB.id, fromDB); - window.SignalWindow.log.info( + window.SignalContext.log.info( 'eraseTapToViewMessages: message data erased', message.idForLogging() ); @@ -35,13 +35,13 @@ }) ); } catch (error) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'eraseTapToViewMessages: Error erasing messages', error && error.stack ? error.stack : error ); } - window.SignalWindow.log.info('eraseTapToViewMessages: complete'); + window.SignalContext.log.info('eraseTapToViewMessages: complete'); } let timeout; @@ -59,7 +59,7 @@ const nextCheck = receivedAt + THIRTY_DAYS; Whisper.TapToViewMessagesListener.nextCheck = nextCheck; - window.SignalWindow.log.info( + window.SignalContext.log.info( 'checkTapToViewMessages: next check at', new Date(nextCheck).toISOString() ); diff --git a/js/modules/debug.js b/js/modules/debug.js index b5749bb3b..f386e8d92 100644 --- a/js/modules/debug.js +++ b/js/modules/debug.js @@ -58,7 +58,7 @@ exports.createConversation = async ({ await Promise.all( range(0, numMessages).map(async index => { await sleep(index * 100); - window.SignalWindow.log.info(`Create message ${index + 1}`); + window.SignalContext.log.info(`Create message ${index + 1}`); const message = await createRandomMessage({ conversationId }); return Signal.Data.saveMessage(message); }) @@ -110,7 +110,7 @@ const createRandomMessage = async ({ conversationId } = {}) => { const message = _createMessage({ commonProperties, conversationId, type }); return Message.initializeSchemaVersion({ message, - logger: window.SignalWindow.log, + logger: window.SignalContext.log, }); }; diff --git a/js/modules/idle_detector.js b/js/modules/idle_detector.js index b71ddb1ea..a012271cb 100644 --- a/js/modules/idle_detector.js +++ b/js/modules/idle_detector.js @@ -16,7 +16,7 @@ class IdleDetector extends EventEmitter { } start() { - window.SignalWindow.log.info('Start idle detector'); + window.SignalContext.log.info('Start idle detector'); this._scheduleNextCallback(); } @@ -25,7 +25,7 @@ class IdleDetector extends EventEmitter { return; } - window.SignalWindow.log.info('Stop idle detector'); + window.SignalContext.log.info('Stop idle detector'); this._clearScheduledCallbacks(); } diff --git a/js/modules/indexeddb.js b/js/modules/indexeddb.js index 699e3f941..b459be287 100644 --- a/js/modules/indexeddb.js +++ b/js/modules/indexeddb.js @@ -12,7 +12,7 @@ module.exports = { }; async function doesDatabaseExist() { - window.SignalWindow.log.info( + window.SignalContext.log.info( 'Checking for the existence of IndexedDB data...' ); return new Promise((resolve, reject) => { @@ -22,7 +22,7 @@ async function doesDatabaseExist() { let existed = true; setTimeout(() => { - window.SignalWindow.log.warn( + window.SignalContext.log.warn( 'doesDatabaseExist: Timed out attempting to check IndexedDB status' ); return resolve(false); @@ -43,7 +43,7 @@ async function doesDatabaseExist() { } function removeDatabase() { - window.SignalWindow.log.info( + window.SignalContext.log.info( `Deleting IndexedDB database '${Whisper.Database.id}'` ); window.indexedDB.deleteDatabase(Whisper.Database.id); diff --git a/js/modules/messages_data_migrator.js b/js/modules/messages_data_migrator.js index 9f89e99e9..f30aae8bb 100644 --- a/js/modules/messages_data_migrator.js +++ b/js/modules/messages_data_migrator.js @@ -45,7 +45,7 @@ exports.processNext = async ({ } ); } catch (error) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'processNext error:', error && error.stack ? error.stack : error ); diff --git a/js/reliable_trigger.js b/js/reliable_trigger.js index 3479c2705..c71e131ac 100644 --- a/js/reliable_trigger.js +++ b/js/reliable_trigger.js @@ -66,7 +66,7 @@ a2 = args[1], a3 = args[2]; const logError = function (error) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'Model caught error triggering', name, 'event:', diff --git a/js/rotate_signed_prekey_listener.js b/js/rotate_signed_prekey_listener.js index d61f57d9c..4304a7946 100644 --- a/js/rotate_signed_prekey_listener.js +++ b/js/rotate_signed_prekey_listener.js @@ -21,13 +21,13 @@ } async function run() { - window.SignalWindow.log.info('Rotating signed prekey...'); + window.SignalContext.log.info('Rotating signed prekey...'); try { await getAccountManager().rotateSignedPreKey(); scheduleNextRotation(); setTimeoutForNextRun(); } catch (error) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'rotateSignedPrekey() failed. Trying again in five minutes' ); setTimeout(setTimeoutForNextRun, 5 * 60 * 1000); @@ -38,7 +38,7 @@ if (navigator.onLine) { run(); } else { - window.SignalWindow.log.info( + window.SignalContext.log.info( 'We are offline; keys will be rotated when we are next online' ); const listener = () => { @@ -53,7 +53,7 @@ const now = Date.now(); const time = storage.get('nextSignedKeyRotationTime', now); - window.SignalWindow.log.info( + window.SignalContext.log.info( 'Next signed key rotation scheduled for', new Date(time).toISOString() ); @@ -71,7 +71,7 @@ Whisper.RotateSignedPreKeyListener = { init(events, newVersion) { if (initComplete) { - window.SignalWindow.log.info( + window.SignalContext.log.info( 'Rotate signed prekey listener: Already initialized' ); return; diff --git a/js/views/react_wrapper_view.js b/js/views/react_wrapper_view.js index 19f860161..57154e84c 100644 --- a/js/views/react_wrapper_view.js +++ b/js/views/react_wrapper_view.js @@ -49,7 +49,7 @@ try { cb(); } catch (error) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'ReactWrapperView.update error:', error && error.stack ? error.stack : error ); diff --git a/libtextsecure/test/_test.js b/libtextsecure/test/_test.js index 6d80df89b..4952266f8 100644 --- a/libtextsecure/test/_test.js +++ b/libtextsecure/test/_test.js @@ -55,12 +55,12 @@ window.Whisper.events = { before(async () => { try { - window.SignalWindow.log.info('Initializing SQL in renderer'); + window.SignalContext.log.info('Initializing SQL in renderer'); const isTesting = true; await window.Signal.Data.startInRenderer(isTesting); - window.SignalWindow.log.info('SQL initialized in renderer'); + window.SignalContext.log.info('SQL initialized in renderer'); } catch (err) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'SQL failed to initialize', err && err.stack ? err.stack : err ); diff --git a/preload.js b/preload.js index e4bd7e89c..bf985456c 100644 --- a/preload.js +++ b/preload.js @@ -16,14 +16,9 @@ try { const { parseIntWithFallback } = require('./ts/util/parseIntWithFallback'); // It is important to call this as early as possible - require('./ts/windows/context'); + const { SignalContext } = require('./ts/windows/context'); - const { - getEnvironment, - setEnvironment, - parseEnvironment, - Environment, - } = require('./ts/environment'); + const { getEnvironment, Environment } = require('./ts/environment'); const ipc = electron.ipcRenderer; const { remote } = electron; @@ -31,8 +26,6 @@ try { const config = require('url').parse(window.location.toString(), true).query; - setEnvironment(parseEnvironment(config.environment)); - const log = require('./ts/logging/log'); let title = config.name; @@ -107,8 +100,6 @@ try { } }; - const localeMessages = ipc.sendSync('locale-data'); - window.setBadgeCount = count => ipc.send('set-badge-count', count); let connectStartTime = 0; @@ -354,8 +345,6 @@ try { // We pull these dependencies in now, from here, because they have Node.js dependencies - require('./ts/logging/set_up_renderer_logging').initialize(); - if (config.proxyUrl) { log.info('Using provided proxy url'); } @@ -413,11 +402,10 @@ try { window.PQueue = require('p-queue').default; const Signal = require('./js/modules/signal'); - const { setupI18n } = require('./ts/util/setupI18n'); const Attachments = require('./app/attachments'); const { locale } = config; - window.i18n = setupI18n(locale, localeMessages); + window.i18n = SignalContext.i18n; window.moment.updateLocale(locale, { relativeTime: { s: window.i18n('timestamp_s'), diff --git a/sticker-creator/app/stages/UploadStage.tsx b/sticker-creator/app/stages/UploadStage.tsx index 542b708f4..8c756007d 100644 --- a/sticker-creator/app/stages/UploadStage.tsx +++ b/sticker-creator/app/stages/UploadStage.tsx @@ -42,7 +42,7 @@ export const UploadStage: React.ComponentType = () => { actions.setPackMeta(packMeta); history.push('/share'); } catch (e) { - window.SignalWindow.log.error('Error uploading image:', e); + window.SignalContext.log.error('Error uploading image:', e); actions.addToast({ key: 'StickerCreator--Toasts--errorUploading', subs: [e.message], diff --git a/sticker-creator/components/StickerGrid.tsx b/sticker-creator/components/StickerGrid.tsx index c779c8f43..829e8568a 100644 --- a/sticker-creator/components/StickerGrid.tsx +++ b/sticker-creator/components/StickerGrid.tsx @@ -58,7 +58,7 @@ const InnerGrid = SortableContainer( const stickerImage = await processStickerImage(path); actions.addImageData(stickerImage); } catch (e) { - window.SignalWindow.log.error('Error processing image:', e); + window.SignalContext.log.error('Error processing image:', e); actions.removeSticker(path); actions.addToast({ key: diff --git a/test/_test.js b/test/_test.js index c7b7ebf07..a6096e0b1 100644 --- a/test/_test.js +++ b/test/_test.js @@ -66,12 +66,12 @@ before(async () => { await deleteIndexedDB(); try { - window.SignalWindow.log.info('Initializing SQL in renderer'); + window.SignalContext.log.info('Initializing SQL in renderer'); const isTesting = true; await window.Signal.Data.startInRenderer(isTesting); - window.SignalWindow.log.info('SQL initialized in renderer'); + window.SignalContext.log.info('SQL initialized in renderer'); } catch (err) { - window.SignalWindow.log.error( + window.SignalContext.log.error( 'SQL failed to initialize', err && err.stack ? err.stack : err ); diff --git a/test/setup-test-node.js b/test/setup-test-node.js index 884fd8066..7cb119a64 100644 --- a/test/setup-test-node.js +++ b/test/setup-test-node.js @@ -6,8 +6,8 @@ const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); +const { Crypto } = require('../ts/context/Crypto'); const { setEnvironment, Environment } = require('../ts/environment'); -const { Context: SignalContext } = require('../ts/context'); const { isValidGuid } = require('../ts/util/isValidGuid'); chai.use(chaiAsPromised); @@ -18,8 +18,8 @@ const storageMap = new Map(); // To replicate logic we have on the client side global.window = { - SignalContext: undefined, - SignalWindow: { + SignalContext: { + crypto: new Crypto(), log: { info: (...args) => console.log(...args), warn: (...args) => console.warn(...args), @@ -34,21 +34,6 @@ global.window = { isValidGuid, }; -const fakeIPC = { - sendSync(channel) { - // See `ts/context/NativeThemeListener.ts` - if (channel === 'native-theme:init') { - return { shouldUseDarkColors: true }; - } - - throw new Error(`Unsupported sendSync channel: ${channel}`); - }, - - on() {}, -}; - -global.window.SignalContext = new SignalContext(fakeIPC); - // For ducks/network.getEmptyState() global.navigator = {}; global.WebSocket = {}; diff --git a/ts/Bytes.ts b/ts/Bytes.ts index ab89b54be..35b10935f 100644 --- a/ts/Bytes.ts +++ b/ts/Bytes.ts @@ -1,7 +1,9 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -const { bytes } = window.SignalContext; +import { Bytes } from './context/Bytes'; + +const bytes = window.SignalContext?.bytes || new Bytes(); export function fromBase64(value: string): Uint8Array { return bytes.fromBase64(value); diff --git a/ts/context/createNativeThemeListener.ts b/ts/context/createNativeThemeListener.ts index 558a77ae9..526f966db 100644 --- a/ts/context/createNativeThemeListener.ts +++ b/ts/context/createNativeThemeListener.ts @@ -24,7 +24,7 @@ type SystemThemeType = 'dark' | 'light'; export type SystemThemeHolder = { systemTheme: SystemThemeType }; -type NativeThemeType = { +export type NativeThemeType = { getSystemTheme: () => SystemThemeType; subscribe: (fn: Callback) => void; update: () => SystemThemeType; diff --git a/ts/context/index.ts b/ts/context/index.ts deleted file mode 100644 index 943ecbf21..000000000 --- a/ts/context/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -import { Bytes } from './Bytes'; -import { Crypto } from './Crypto'; -import { Timers } from './Timers'; -import { - createNativeThemeListener, - MinimalIPC, -} from './createNativeThemeListener'; - -export class Context { - public readonly bytes = new Bytes(); - - public readonly crypto = new Crypto(); - - public readonly timers = new Timers(); - - public readonly nativeThemeListener; - - constructor(private readonly ipc: MinimalIPC) { - this.nativeThemeListener = createNativeThemeListener(ipc, window); - } - - setIsCallActive(isCallActive: boolean): void { - this.ipc.send('set-is-call-active', isCallActive); - } -} diff --git a/ts/logging/set_up_renderer_logging.ts b/ts/logging/set_up_renderer_logging.ts index c35b91ac9..2716c0515 100644 --- a/ts/logging/set_up_renderer_logging.ts +++ b/ts/logging/set_up_renderer_logging.ts @@ -100,8 +100,8 @@ function logAtLevel(level: LogLevel, ...args: ReadonlyArray): void { log.setLogAtLevel(logAtLevel); -window.SignalWindow = window.SignalWindow || {}; -window.SignalWindow.log = { +window.SignalContext = window.SignalContext || {}; +window.SignalContext.log = { fatal: log.fatal, error: log.error, warn: log.warn, diff --git a/ts/services/notifications.ts b/ts/services/notifications.ts index dac91ee8a..4fa2c17ae 100644 --- a/ts/services/notifications.ts +++ b/ts/services/notifications.ts @@ -322,7 +322,7 @@ class NotificationService extends EventEmitter { } public clear(): void { - window.SignalWindow.log.info('Removing notification'); + window.SignalContext.log.info('Removing notification'); this.notificationData = null; this.update(); } diff --git a/ts/types/Conversation.ts b/ts/types/Conversation.ts index 082517ae7..07695ead5 100644 --- a/ts/types/Conversation.ts +++ b/ts/types/Conversation.ts @@ -41,7 +41,7 @@ function buildAvatarUpdater({ field }: { field: 'avatar' | 'profileAvatar' }) { const { hash, path } = avatar; const exists = await doesAttachmentExist(path); if (!exists) { - window.SignalWindow.log.warn( + window.SignalContext.log.warn( `Conversation.buildAvatarUpdater: attachment ${path} did not exist` ); } diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index efb3bfc79..c4ebde3ec 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -14058,14 +14058,14 @@ { "rule": "DOM-innerHTML", "path": "ts/windows/loading/start.js", - "line": " message.innerHTML = window.SignalWindow.i18n('optimizingApplication');", + "line": " message.innerHTML = window.SignalContext.i18n('optimizingApplication');", "reasonCategory": "usageTrusted", "updated": "2021-09-17T21:02:59.414Z" }, { "rule": "DOM-innerHTML", "path": "ts/windows/loading/start.ts", - "line": " message.innerHTML = window.SignalWindow.i18n('optimizingApplication');", + "line": " message.innerHTML = window.SignalContext.i18n('optimizingApplication');", "reasonCategory": "usageTrusted", "updated": "2021-09-17T21:02:59.414Z" } diff --git a/ts/window.d.ts b/ts/window.d.ts index 254d96e63..dabc427d2 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -10,6 +10,7 @@ import moment from 'moment'; import PQueue from 'p-queue/dist'; import { Ref } from 'react'; import { imageToBlurHash } from './util/imageToBlurHash'; +import type { ParsedUrlQuery } from 'querystring'; import * as Util from './util'; import { ConversationModelCollectionType, @@ -96,7 +97,6 @@ import { MIMEType } from './types/MIME'; import { DownloadedAttachmentType } from './types/Attachment'; import { ElectronLocaleType } from './util/mapToSupportLocale'; import { SignalProtocolStore } from './SignalProtocolStore'; -import { Context as SignalContext } from './context'; import { StartupQueue } from './util/StartupQueue'; import { SocketStatus } from './types/SocketStatus'; import SyncRequest from './textsecure/SyncRequest'; @@ -111,8 +111,7 @@ import { QualifiedAddress } from './types/QualifiedAddress'; import { CI } from './CI'; import { IPCEventsType, IPCEventsValuesType } from './util/createIPCEvents'; import { ConversationView } from './views/conversation_view'; -import { LoggerType } from './types/Logging'; -import { SettingType } from './util/preload'; +import type { SignalContextType } from './windows/context'; export { Long } from 'long'; @@ -462,7 +461,6 @@ declare global { }; challengeHandler: ChallengeHandler; }; - SignalContext: SignalContext; ConversationController: ConversationController; Events: IPCEventsType; @@ -489,21 +487,7 @@ declare global { RETRY_DELAY: boolean; // Context Isolation - SignalWindow: { - Settings: { - themeSetting: SettingType; - waitForChange: () => Promise; - }; - config: string; - context: SignalContext; - getAppInstance: () => string | undefined; - getEnvironment: () => string; - getNodeVersion: () => string; - getVersion: () => string; - i18n: LocalizerType; - log: LoggerType; - renderWindow: () => void; - }; + SignalContext: SignalContextType; } // We want to extend `Error`, so we need an interface. diff --git a/ts/windows/about/preload.ts b/ts/windows/about/preload.ts index d5ab23988..cfa88fd2a 100644 --- a/ts/windows/about/preload.ts +++ b/ts/windows/about/preload.ts @@ -5,18 +5,15 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { contextBridge, ipcRenderer } from 'electron'; -// It is important to call this as early as possible -import '../context'; - -import { SignalWindow } from '../configure'; +import { SignalContext } from '../context'; import { About } from '../../components/About'; -contextBridge.exposeInMainWorld('SignalWindow', { - ...SignalWindow, +contextBridge.exposeInMainWorld('SignalContext', { + ...SignalContext, renderWindow: () => { - const environmentText: Array = [SignalWindow.getEnvironment()]; + const environmentText: Array = [SignalContext.getEnvironment()]; - const appInstance = SignalWindow.getAppInstance(); + const appInstance = SignalContext.getAppInstance(); if (appInstance) { environmentText.push(appInstance); } @@ -25,8 +22,8 @@ contextBridge.exposeInMainWorld('SignalWindow', { React.createElement(About, { closeAbout: () => ipcRenderer.send('close-about'), environment: environmentText.join(' - '), - i18n: SignalWindow.i18n, - version: SignalWindow.getVersion(), + i18n: SignalContext.i18n, + version: SignalContext.getVersion(), }), document.getElementById('app') ); diff --git a/ts/windows/applyTheme.ts b/ts/windows/applyTheme.ts index eef400fed..cb47bb10e 100644 --- a/ts/windows/applyTheme.ts +++ b/ts/windows/applyTheme.ts @@ -2,13 +2,13 @@ // SPDX-License-Identifier: AGPL-3.0-only async function applyTheme() { - const theme = await window.SignalWindow.Settings.themeSetting.getValue(); + const theme = await window.SignalContext.Settings.themeSetting.getValue(); document.body.classList.remove('light-theme'); document.body.classList.remove('dark-theme'); document.body.classList.add( `${ theme === 'system' - ? window.SignalWindow.context.nativeThemeListener.getSystemTheme() + ? window.SignalContext.nativeThemeListener.getSystemTheme() : theme }-theme` ); @@ -18,7 +18,7 @@ async function applyThemeLoop() { // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-await-in-loop - await window.SignalWindow.Settings.waitForChange(); + await window.SignalContext.Settings.waitForChange(); // eslint-disable-next-line no-await-in-loop await applyTheme(); @@ -28,6 +28,6 @@ async function applyThemeLoop() { applyTheme(); applyThemeLoop(); -window.SignalWindow.context.nativeThemeListener.subscribe(() => { +window.SignalContext.nativeThemeListener.subscribe(() => { applyTheme(); }); diff --git a/ts/windows/configure.ts b/ts/windows/configure.ts deleted file mode 100644 index 996d1ea88..000000000 --- a/ts/windows/configure.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018-2021 Signal Messenger, LLC -// SPDX-License-Identifier: AGPL-3.0-only - -import url from 'url'; -import { ipcRenderer } from 'electron'; - -import { setupI18n } from '../util/setupI18n'; -import { - getEnvironment, - parseEnvironment, - setEnvironment, -} from '../environment'; -import { strictAssert } from '../util/assert'; -import { createSetting } from '../util/preload'; -import { initialize as initializeLogging } from '../logging/set_up_renderer_logging'; -import { waitForSettingsChange } from './waitForSettingsChange'; - -const config = url.parse(window.location.toString(), true).query; -const { locale } = config; -strictAssert(locale, 'locale could not be parsed from config'); -strictAssert(typeof locale === 'string', 'locale is not a string'); - -const localeMessages = ipcRenderer.sendSync('locale-data'); -setEnvironment(parseEnvironment(config.environment)); - -strictAssert(Boolean(window.SignalContext), 'context must be defined'); - -initializeLogging(); - -export const SignalWindow = { - Settings: { - themeSetting: createSetting('themeSetting', { setter: false }), - waitForChange: waitForSettingsChange, - }, - config, - context: window.SignalContext, - getAppInstance: (): string | undefined => - config.appInstance ? String(config.appInstance) : undefined, - getEnvironment, - getNodeVersion: (): string => String(config.node_version), - getVersion: (): string => String(config.version), - i18n: setupI18n(locale, localeMessages), - log: window.SignalWindow.log, -}; diff --git a/ts/windows/context.ts b/ts/windows/context.ts index 02a8b3aca..7923d3a6d 100644 --- a/ts/windows/context.ts +++ b/ts/windows/context.ts @@ -1,8 +1,83 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import { ipcRenderer as ipc } from 'electron'; +import { ipcRenderer } from 'electron'; +import url from 'url'; +import type { ParsedUrlQuery } from 'querystring'; +import type { IPCEventsValuesType } from '../util/createIPCEvents'; +import type { LocalizerType } from '../types/Util'; +import type { LoggerType } from '../types/Logging'; +import type { NativeThemeType } from '../context/createNativeThemeListener'; +import type { SettingType } from '../util/preload'; +import { Bytes } from '../context/Bytes'; +import { Crypto } from '../context/Crypto'; +import { Timers } from '../context/Timers'; -import { Context } from '../context'; +import { setupI18n } from '../util/setupI18n'; +import { + getEnvironment, + parseEnvironment, + setEnvironment, +} from '../environment'; +import { strictAssert } from '../util/assert'; +import { createSetting } from '../util/preload'; +import { initialize as initializeLogging } from '../logging/set_up_renderer_logging'; +import { waitForSettingsChange } from './waitForSettingsChange'; +import { createNativeThemeListener } from '../context/createNativeThemeListener'; -window.SignalContext = new Context(ipc); +const config = url.parse(window.location.toString(), true).query; +const { locale } = config; +strictAssert(locale, 'locale could not be parsed from config'); +strictAssert(typeof locale === 'string', 'locale is not a string'); + +const localeMessages = ipcRenderer.sendSync('locale-data'); +setEnvironment(parseEnvironment(config.environment)); + +strictAssert(Boolean(window.SignalContext), 'context must be defined'); + +initializeLogging(); + +export type SignalContextType = { + bytes: Bytes; + crypto: Crypto; + timers: Timers; + nativeThemeListener: NativeThemeType; + setIsCallActive: (isCallActive: boolean) => unknown; + + Settings: { + themeSetting: SettingType; + waitForChange: () => Promise; + }; + config: ParsedUrlQuery; + getAppInstance: () => string | undefined; + getEnvironment: () => string; + getNodeVersion: () => string; + getVersion: () => string; + i18n: LocalizerType; + log: LoggerType; + renderWindow?: () => void; +}; + +export const SignalContext: SignalContextType = { + Settings: { + themeSetting: createSetting('themeSetting', { setter: false }), + waitForChange: waitForSettingsChange, + }, + bytes: new Bytes(), + config, + crypto: new Crypto(), + getAppInstance: (): string | undefined => + config.appInstance ? String(config.appInstance) : undefined, + getEnvironment, + getNodeVersion: (): string => String(config.node_version), + getVersion: (): string => String(config.version), + i18n: setupI18n(locale, localeMessages), + log: window.SignalContext.log, + nativeThemeListener: createNativeThemeListener(ipcRenderer, window), + setIsCallActive(isCallActive: boolean): void { + ipcRenderer.send('set-is-call-active', isCallActive); + }, + timers: new Timers(), +}; + +window.SignalContext = SignalContext; diff --git a/ts/windows/debuglog/preload.ts b/ts/windows/debuglog/preload.ts index 18af8ef25..7e4e8174f 100644 --- a/ts/windows/debuglog/preload.ts +++ b/ts/windows/debuglog/preload.ts @@ -5,19 +5,16 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { contextBridge, ipcRenderer } from 'electron'; -// It is important to call this as early as possible -import '../context'; - -import { SignalWindow } from '../configure'; +import { SignalContext } from '../context'; import { DebugLogWindow } from '../../components/DebugLogWindow'; import * as debugLog from '../../logging/debuglogs'; -contextBridge.exposeInMainWorld('SignalWindow', { - ...SignalWindow, +contextBridge.exposeInMainWorld('SignalContext', { + ...SignalContext, renderWindow: () => { - const environmentText: Array = [SignalWindow.getEnvironment()]; + const environmentText: Array = [SignalContext.getEnvironment()]; - const appInstance = SignalWindow.getAppInstance(); + const appInstance = SignalContext.getAppInstance(); if (appInstance) { environmentText.push(appInstance); } @@ -27,15 +24,15 @@ contextBridge.exposeInMainWorld('SignalWindow', { closeWindow: () => ipcRenderer.send('close-debug-log'), downloadLog: (logText: string) => ipcRenderer.send('show-debug-log-save-dialog', logText), - i18n: SignalWindow.i18n, + i18n: SignalContext.i18n, fetchLogs() { return debugLog.fetch( - SignalWindow.getNodeVersion(), - SignalWindow.getVersion() + SignalContext.getNodeVersion(), + SignalContext.getVersion() ); }, uploadLogs(logs: string) { - return debugLog.upload(logs, SignalWindow.getVersion()); + return debugLog.upload(logs, SignalContext.getVersion()); }, }), document.getElementById('app') diff --git a/ts/windows/init.ts b/ts/windows/init.ts index b4b263970..e8099997e 100644 --- a/ts/windows/init.ts +++ b/ts/windows/init.ts @@ -1,4 +1,8 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -window.SignalWindow.renderWindow(); +if (window.SignalContext.renderWindow) { + window.SignalContext.renderWindow(); +} else { + window.SignalContext.log.error('renderWindow is undefined!'); +} diff --git a/ts/windows/loading/preload.ts b/ts/windows/loading/preload.ts index 66aa95d90..e41b60c5e 100644 --- a/ts/windows/loading/preload.ts +++ b/ts/windows/loading/preload.ts @@ -3,9 +3,6 @@ import { contextBridge } from 'electron'; -// It is important to call this as early as possible -import '../context'; +import { SignalContext } from '../context'; -import { SignalWindow } from '../configure'; - -contextBridge.exposeInMainWorld('SignalWindow', SignalWindow); +contextBridge.exposeInMainWorld('SignalContext', SignalContext); diff --git a/ts/windows/loading/start.ts b/ts/windows/loading/start.ts index 3e1e184e2..4412a41bd 100644 --- a/ts/windows/loading/start.ts +++ b/ts/windows/loading/start.ts @@ -3,5 +3,5 @@ const message = document.getElementById('message'); if (message) { - message.innerHTML = window.SignalWindow.i18n('optimizingApplication'); + message.innerHTML = window.SignalContext.i18n('optimizingApplication'); } diff --git a/ts/windows/permissions/preload.ts b/ts/windows/permissions/preload.ts index 236b67d0a..899e8e0b7 100644 --- a/ts/windows/permissions/preload.ts +++ b/ts/windows/permissions/preload.ts @@ -5,11 +5,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { contextBridge, ipcRenderer } from 'electron'; -// It is important to call this as early as possible -import '../context'; +import { SignalContext } from '../context'; import { createSetting } from '../../util/preload'; -import { SignalWindow } from '../configure'; import { PermissionsPopup } from '../../components/PermissionsPopup'; const mediaCameraPermissions = createSetting('mediaCameraPermissions', { @@ -24,21 +22,21 @@ contextBridge.exposeInMainWorld( window.SignalContext.nativeThemeListener ); -contextBridge.exposeInMainWorld('SignalWindow', { - ...SignalWindow, +contextBridge.exposeInMainWorld('SignalContext', { + ...SignalContext, renderWindow: () => { - const forCalling = SignalWindow.config.forCalling === 'true'; - const forCamera = SignalWindow.config.forCamera === 'true'; + const forCalling = SignalContext.config.forCalling === 'true'; + const forCamera = SignalContext.config.forCamera === 'true'; let message; if (forCalling) { if (forCamera) { - message = SignalWindow.i18n('videoCallingPermissionNeeded'); + message = SignalContext.i18n('videoCallingPermissionNeeded'); } else { - message = SignalWindow.i18n('audioCallingPermissionNeeded'); + message = SignalContext.i18n('audioCallingPermissionNeeded'); } } else { - message = SignalWindow.i18n('audioPermissionNeeded'); + message = SignalContext.i18n('audioPermissionNeeded'); } function onClose() { @@ -47,7 +45,7 @@ contextBridge.exposeInMainWorld('SignalWindow', { ReactDOM.render( React.createElement(PermissionsPopup, { - i18n: SignalWindow.i18n, + i18n: SignalContext.i18n, message, onAccept: () => { if (!forCamera) { diff --git a/ts/windows/screenShare/preload.ts b/ts/windows/screenShare/preload.ts index 905daee04..129edc4e3 100644 --- a/ts/windows/screenShare/preload.ts +++ b/ts/windows/screenShare/preload.ts @@ -5,18 +5,15 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { contextBridge, ipcRenderer } from 'electron'; -// It is important to call this as early as possible -import '../context'; - -import { SignalWindow } from '../configure'; +import { SignalContext } from '../context'; import { CallingScreenSharingController } from '../../components/CallingScreenSharingController'; -contextBridge.exposeInMainWorld('SignalWindow', SignalWindow); +contextBridge.exposeInMainWorld('SignalContext', SignalContext); function renderScreenSharingController(presentedSourceName: string): void { ReactDOM.render( React.createElement(CallingScreenSharingController, { - i18n: SignalWindow.i18n, + i18n: SignalContext.i18n, onCloseController: () => ipcRenderer.send('close-screen-share-controller'), onStopSharing: () => ipcRenderer.send('stop-screen-share'), diff --git a/ts/windows/settings/preload.ts b/ts/windows/settings/preload.ts index cbde26832..49ab5010e 100644 --- a/ts/windows/settings/preload.ts +++ b/ts/windows/settings/preload.ts @@ -5,10 +5,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { contextBridge, ipcRenderer } from 'electron'; -// It is important to call this as early as possible -import '../context'; - -import { SignalWindow } from '../configure'; +import { SignalContext } from '../context'; import * as Settings from '../../types/Settings'; import { Preferences } from '../../components/Preferences'; import { @@ -260,7 +257,7 @@ const renderPreferences = async () => { editCustomColor: ipcEditCustomColor, getConversationsWithCustomColor: ipcGetConversationsWithCustomColor, initialSpellCheckSetting: - SignalWindow.config.appStartInitialSpellcheckSetting === 'true', + SignalContext.config.appStartInitialSpellcheckSetting === 'true', makeSyncRequest: ipcMakeSyncRequest, removeCustomColor: ipcRemoveCustomColor, removeCustomColorOnConversations: ipcRemoveCustomColorOnConversations, @@ -277,7 +274,7 @@ const renderPreferences = async () => { isPhoneNumberSharingSupported, isSyncSupported: !isSyncNotSupported, isSystemTraySupported: Settings.isSystemTraySupported( - SignalWindow.getVersion() + SignalContext.getVersion() ), // Change handlers @@ -343,7 +340,7 @@ const renderPreferences = async () => { // rerender. onZoomFactorChange: settingZoomFactor.setValue, - i18n: SignalWindow.i18n, + i18n: SignalContext.i18n, }; function reRender(f: (value: Value) => Promise) { @@ -361,7 +358,7 @@ const renderPreferences = async () => { ipcRenderer.on('preferences-changed', () => renderPreferences()); -contextBridge.exposeInMainWorld('SignalWindow', { - ...SignalWindow, +contextBridge.exposeInMainWorld('SignalContext', { + ...SignalContext, renderWindow: renderPreferences, });