Use SignalContext instead of SignalWindow

This commit is contained in:
Josh Perez 2021-10-07 19:28:47 -04:00 committed by GitHub
parent a70a8a88d6
commit 75dab30367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 180 additions and 231 deletions

View File

@ -11,7 +11,7 @@
window.Whisper.Database.nolog = true; window.Whisper.Database.nolog = true;
Whisper.Database.handleDOMException = (prefix, error, reject) => { Whisper.Database.handleDOMException = (prefix, error, reject) => {
window.SignalWindow.log.error( window.SignalContext.log.error(
`${prefix}:`, `${prefix}:`,
error && error.name, error && error.name,
error && error.message, error && error.message,

View File

@ -13,13 +13,13 @@
async function destroyExpiredMessages() { async function destroyExpiredMessages() {
try { try {
window.SignalWindow.log.info( window.SignalContext.log.info(
'destroyExpiredMessages: Loading messages...' 'destroyExpiredMessages: Loading messages...'
); );
const messages = await window.Signal.Data.getExpiredMessages({ const messages = await window.Signal.Data.getExpiredMessages({
MessageCollection: Whisper.MessageCollection, MessageCollection: Whisper.MessageCollection,
}); });
window.SignalWindow.log.info( window.SignalContext.log.info(
`destroyExpiredMessages: found ${messages.length} messages to expire` `destroyExpiredMessages: found ${messages.length} messages to expire`
); );
@ -40,7 +40,7 @@
await Promise.all(messageCleanup); await Promise.all(messageCleanup);
inMemoryMessages.forEach(message => { inMemoryMessages.forEach(message => {
window.SignalWindow.log.info('Message expired', { window.SignalContext.log.info('Message expired', {
sentAt: message.get('sent_at'), sentAt: message.get('sent_at'),
}); });
@ -52,25 +52,25 @@
} }
}); });
} catch (error) { } catch (error) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'destroyExpiredMessages: Error deleting expired messages', 'destroyExpiredMessages: Error deleting expired messages',
error && error.stack ? error.stack : error error && error.stack ? error.stack : error
); );
} }
window.SignalWindow.log.info('destroyExpiredMessages: complete'); window.SignalContext.log.info('destroyExpiredMessages: complete');
checkExpiringMessages(); checkExpiringMessages();
} }
let timeout; let timeout;
async function checkExpiringMessages() { async function checkExpiringMessages() {
window.SignalWindow.log.info( window.SignalContext.log.info(
'checkExpiringMessages: checking for expiring messages' 'checkExpiringMessages: checking for expiring messages'
); );
const soonestExpiry = await window.Signal.Data.getSoonestMessageExpiry(); const soonestExpiry = await window.Signal.Data.getSoonestMessageExpiry();
if (!soonestExpiry) { if (!soonestExpiry) {
window.SignalWindow.log.info( window.SignalContext.log.info(
'checkExpiringMessages: found no messages to expire' 'checkExpiringMessages: found no messages to expire'
); );
return; return;
@ -88,7 +88,7 @@
wait = 2147483647; wait = 2147483647;
} }
window.SignalWindow.log.info( window.SignalContext.log.info(
`checkExpiringMessages: next message expires ${new Date( `checkExpiringMessages: next message expires ${new Date(
soonestExpiry soonestExpiry
).toISOString()}; waiting ${wait} ms before clearing` ).toISOString()}; waiting ${wait} ms before clearing`

View File

@ -13,7 +13,7 @@
async function eraseTapToViewMessages() { async function eraseTapToViewMessages() {
try { try {
window.SignalWindow.log.info( window.SignalContext.log.info(
'eraseTapToViewMessages: Loading messages...' 'eraseTapToViewMessages: Loading messages...'
); );
const messages = await window.Signal.Data.getTapToViewMessagesNeedingErase( const messages = await window.Signal.Data.getTapToViewMessagesNeedingErase(
@ -26,7 +26,7 @@
messages.map(async fromDB => { messages.map(async fromDB => {
const message = MessageController.register(fromDB.id, fromDB); const message = MessageController.register(fromDB.id, fromDB);
window.SignalWindow.log.info( window.SignalContext.log.info(
'eraseTapToViewMessages: message data erased', 'eraseTapToViewMessages: message data erased',
message.idForLogging() message.idForLogging()
); );
@ -35,13 +35,13 @@
}) })
); );
} catch (error) { } catch (error) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'eraseTapToViewMessages: Error erasing messages', 'eraseTapToViewMessages: Error erasing messages',
error && error.stack ? error.stack : error error && error.stack ? error.stack : error
); );
} }
window.SignalWindow.log.info('eraseTapToViewMessages: complete'); window.SignalContext.log.info('eraseTapToViewMessages: complete');
} }
let timeout; let timeout;
@ -59,7 +59,7 @@
const nextCheck = receivedAt + THIRTY_DAYS; const nextCheck = receivedAt + THIRTY_DAYS;
Whisper.TapToViewMessagesListener.nextCheck = nextCheck; Whisper.TapToViewMessagesListener.nextCheck = nextCheck;
window.SignalWindow.log.info( window.SignalContext.log.info(
'checkTapToViewMessages: next check at', 'checkTapToViewMessages: next check at',
new Date(nextCheck).toISOString() new Date(nextCheck).toISOString()
); );

View File

@ -58,7 +58,7 @@ exports.createConversation = async ({
await Promise.all( await Promise.all(
range(0, numMessages).map(async index => { range(0, numMessages).map(async index => {
await sleep(index * 100); 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 }); const message = await createRandomMessage({ conversationId });
return Signal.Data.saveMessage(message); return Signal.Data.saveMessage(message);
}) })
@ -110,7 +110,7 @@ const createRandomMessage = async ({ conversationId } = {}) => {
const message = _createMessage({ commonProperties, conversationId, type }); const message = _createMessage({ commonProperties, conversationId, type });
return Message.initializeSchemaVersion({ return Message.initializeSchemaVersion({
message, message,
logger: window.SignalWindow.log, logger: window.SignalContext.log,
}); });
}; };

View File

@ -16,7 +16,7 @@ class IdleDetector extends EventEmitter {
} }
start() { start() {
window.SignalWindow.log.info('Start idle detector'); window.SignalContext.log.info('Start idle detector');
this._scheduleNextCallback(); this._scheduleNextCallback();
} }
@ -25,7 +25,7 @@ class IdleDetector extends EventEmitter {
return; return;
} }
window.SignalWindow.log.info('Stop idle detector'); window.SignalContext.log.info('Stop idle detector');
this._clearScheduledCallbacks(); this._clearScheduledCallbacks();
} }

View File

@ -12,7 +12,7 @@ module.exports = {
}; };
async function doesDatabaseExist() { async function doesDatabaseExist() {
window.SignalWindow.log.info( window.SignalContext.log.info(
'Checking for the existence of IndexedDB data...' 'Checking for the existence of IndexedDB data...'
); );
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -22,7 +22,7 @@ async function doesDatabaseExist() {
let existed = true; let existed = true;
setTimeout(() => { setTimeout(() => {
window.SignalWindow.log.warn( window.SignalContext.log.warn(
'doesDatabaseExist: Timed out attempting to check IndexedDB status' 'doesDatabaseExist: Timed out attempting to check IndexedDB status'
); );
return resolve(false); return resolve(false);
@ -43,7 +43,7 @@ async function doesDatabaseExist() {
} }
function removeDatabase() { function removeDatabase() {
window.SignalWindow.log.info( window.SignalContext.log.info(
`Deleting IndexedDB database '${Whisper.Database.id}'` `Deleting IndexedDB database '${Whisper.Database.id}'`
); );
window.indexedDB.deleteDatabase(Whisper.Database.id); window.indexedDB.deleteDatabase(Whisper.Database.id);

View File

@ -45,7 +45,7 @@ exports.processNext = async ({
} }
); );
} catch (error) { } catch (error) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'processNext error:', 'processNext error:',
error && error.stack ? error.stack : error error && error.stack ? error.stack : error
); );

View File

@ -66,7 +66,7 @@
a2 = args[1], a2 = args[1],
a3 = args[2]; a3 = args[2];
const logError = function (error) { const logError = function (error) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'Model caught error triggering', 'Model caught error triggering',
name, name,
'event:', 'event:',

View File

@ -21,13 +21,13 @@
} }
async function run() { async function run() {
window.SignalWindow.log.info('Rotating signed prekey...'); window.SignalContext.log.info('Rotating signed prekey...');
try { try {
await getAccountManager().rotateSignedPreKey(); await getAccountManager().rotateSignedPreKey();
scheduleNextRotation(); scheduleNextRotation();
setTimeoutForNextRun(); setTimeoutForNextRun();
} catch (error) { } catch (error) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'rotateSignedPrekey() failed. Trying again in five minutes' 'rotateSignedPrekey() failed. Trying again in five minutes'
); );
setTimeout(setTimeoutForNextRun, 5 * 60 * 1000); setTimeout(setTimeoutForNextRun, 5 * 60 * 1000);
@ -38,7 +38,7 @@
if (navigator.onLine) { if (navigator.onLine) {
run(); run();
} else { } else {
window.SignalWindow.log.info( window.SignalContext.log.info(
'We are offline; keys will be rotated when we are next online' 'We are offline; keys will be rotated when we are next online'
); );
const listener = () => { const listener = () => {
@ -53,7 +53,7 @@
const now = Date.now(); const now = Date.now();
const time = storage.get('nextSignedKeyRotationTime', now); const time = storage.get('nextSignedKeyRotationTime', now);
window.SignalWindow.log.info( window.SignalContext.log.info(
'Next signed key rotation scheduled for', 'Next signed key rotation scheduled for',
new Date(time).toISOString() new Date(time).toISOString()
); );
@ -71,7 +71,7 @@
Whisper.RotateSignedPreKeyListener = { Whisper.RotateSignedPreKeyListener = {
init(events, newVersion) { init(events, newVersion) {
if (initComplete) { if (initComplete) {
window.SignalWindow.log.info( window.SignalContext.log.info(
'Rotate signed prekey listener: Already initialized' 'Rotate signed prekey listener: Already initialized'
); );
return; return;

View File

@ -49,7 +49,7 @@
try { try {
cb(); cb();
} catch (error) { } catch (error) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'ReactWrapperView.update error:', 'ReactWrapperView.update error:',
error && error.stack ? error.stack : error error && error.stack ? error.stack : error
); );

View File

@ -55,12 +55,12 @@ window.Whisper.events = {
before(async () => { before(async () => {
try { try {
window.SignalWindow.log.info('Initializing SQL in renderer'); window.SignalContext.log.info('Initializing SQL in renderer');
const isTesting = true; const isTesting = true;
await window.Signal.Data.startInRenderer(isTesting); await window.Signal.Data.startInRenderer(isTesting);
window.SignalWindow.log.info('SQL initialized in renderer'); window.SignalContext.log.info('SQL initialized in renderer');
} catch (err) { } catch (err) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'SQL failed to initialize', 'SQL failed to initialize',
err && err.stack ? err.stack : err err && err.stack ? err.stack : err
); );

View File

@ -16,14 +16,9 @@ try {
const { parseIntWithFallback } = require('./ts/util/parseIntWithFallback'); const { parseIntWithFallback } = require('./ts/util/parseIntWithFallback');
// It is important to call this as early as possible // It is important to call this as early as possible
require('./ts/windows/context'); const { SignalContext } = require('./ts/windows/context');
const { const { getEnvironment, Environment } = require('./ts/environment');
getEnvironment,
setEnvironment,
parseEnvironment,
Environment,
} = require('./ts/environment');
const ipc = electron.ipcRenderer; const ipc = electron.ipcRenderer;
const { remote } = electron; const { remote } = electron;
@ -31,8 +26,6 @@ try {
const config = require('url').parse(window.location.toString(), true).query; const config = require('url').parse(window.location.toString(), true).query;
setEnvironment(parseEnvironment(config.environment));
const log = require('./ts/logging/log'); const log = require('./ts/logging/log');
let title = config.name; let title = config.name;
@ -107,8 +100,6 @@ try {
} }
}; };
const localeMessages = ipc.sendSync('locale-data');
window.setBadgeCount = count => ipc.send('set-badge-count', count); window.setBadgeCount = count => ipc.send('set-badge-count', count);
let connectStartTime = 0; let connectStartTime = 0;
@ -354,8 +345,6 @@ try {
// We pull these dependencies in now, from here, because they have Node.js dependencies // 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) { if (config.proxyUrl) {
log.info('Using provided proxy url'); log.info('Using provided proxy url');
} }
@ -413,11 +402,10 @@ try {
window.PQueue = require('p-queue').default; window.PQueue = require('p-queue').default;
const Signal = require('./js/modules/signal'); const Signal = require('./js/modules/signal');
const { setupI18n } = require('./ts/util/setupI18n');
const Attachments = require('./app/attachments'); const Attachments = require('./app/attachments');
const { locale } = config; const { locale } = config;
window.i18n = setupI18n(locale, localeMessages); window.i18n = SignalContext.i18n;
window.moment.updateLocale(locale, { window.moment.updateLocale(locale, {
relativeTime: { relativeTime: {
s: window.i18n('timestamp_s'), s: window.i18n('timestamp_s'),

View File

@ -42,7 +42,7 @@ export const UploadStage: React.ComponentType = () => {
actions.setPackMeta(packMeta); actions.setPackMeta(packMeta);
history.push('/share'); history.push('/share');
} catch (e) { } catch (e) {
window.SignalWindow.log.error('Error uploading image:', e); window.SignalContext.log.error('Error uploading image:', e);
actions.addToast({ actions.addToast({
key: 'StickerCreator--Toasts--errorUploading', key: 'StickerCreator--Toasts--errorUploading',
subs: [e.message], subs: [e.message],

View File

@ -58,7 +58,7 @@ const InnerGrid = SortableContainer(
const stickerImage = await processStickerImage(path); const stickerImage = await processStickerImage(path);
actions.addImageData(stickerImage); actions.addImageData(stickerImage);
} catch (e) { } catch (e) {
window.SignalWindow.log.error('Error processing image:', e); window.SignalContext.log.error('Error processing image:', e);
actions.removeSticker(path); actions.removeSticker(path);
actions.addToast({ actions.addToast({
key: key:

View File

@ -66,12 +66,12 @@ before(async () => {
await deleteIndexedDB(); await deleteIndexedDB();
try { try {
window.SignalWindow.log.info('Initializing SQL in renderer'); window.SignalContext.log.info('Initializing SQL in renderer');
const isTesting = true; const isTesting = true;
await window.Signal.Data.startInRenderer(isTesting); await window.Signal.Data.startInRenderer(isTesting);
window.SignalWindow.log.info('SQL initialized in renderer'); window.SignalContext.log.info('SQL initialized in renderer');
} catch (err) { } catch (err) {
window.SignalWindow.log.error( window.SignalContext.log.error(
'SQL failed to initialize', 'SQL failed to initialize',
err && err.stack ? err.stack : err err && err.stack ? err.stack : err
); );

View File

@ -6,8 +6,8 @@
const chai = require('chai'); const chai = require('chai');
const chaiAsPromised = require('chai-as-promised'); const chaiAsPromised = require('chai-as-promised');
const { Crypto } = require('../ts/context/Crypto');
const { setEnvironment, Environment } = require('../ts/environment'); const { setEnvironment, Environment } = require('../ts/environment');
const { Context: SignalContext } = require('../ts/context');
const { isValidGuid } = require('../ts/util/isValidGuid'); const { isValidGuid } = require('../ts/util/isValidGuid');
chai.use(chaiAsPromised); chai.use(chaiAsPromised);
@ -18,8 +18,8 @@ const storageMap = new Map();
// To replicate logic we have on the client side // To replicate logic we have on the client side
global.window = { global.window = {
SignalContext: undefined, SignalContext: {
SignalWindow: { crypto: new Crypto(),
log: { log: {
info: (...args) => console.log(...args), info: (...args) => console.log(...args),
warn: (...args) => console.warn(...args), warn: (...args) => console.warn(...args),
@ -34,21 +34,6 @@ global.window = {
isValidGuid, 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() // For ducks/network.getEmptyState()
global.navigator = {}; global.navigator = {};
global.WebSocket = {}; global.WebSocket = {};

View File

@ -1,7 +1,9 @@
// Copyright 2021 Signal Messenger, LLC // Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // 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 { export function fromBase64(value: string): Uint8Array {
return bytes.fromBase64(value); return bytes.fromBase64(value);

View File

@ -24,7 +24,7 @@ type SystemThemeType = 'dark' | 'light';
export type SystemThemeHolder = { systemTheme: SystemThemeType }; export type SystemThemeHolder = { systemTheme: SystemThemeType };
type NativeThemeType = { export type NativeThemeType = {
getSystemTheme: () => SystemThemeType; getSystemTheme: () => SystemThemeType;
subscribe: (fn: Callback) => void; subscribe: (fn: Callback) => void;
update: () => SystemThemeType; update: () => SystemThemeType;

View File

@ -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);
}
}

View File

@ -100,8 +100,8 @@ function logAtLevel(level: LogLevel, ...args: ReadonlyArray<unknown>): void {
log.setLogAtLevel(logAtLevel); log.setLogAtLevel(logAtLevel);
window.SignalWindow = window.SignalWindow || {}; window.SignalContext = window.SignalContext || {};
window.SignalWindow.log = { window.SignalContext.log = {
fatal: log.fatal, fatal: log.fatal,
error: log.error, error: log.error,
warn: log.warn, warn: log.warn,

View File

@ -322,7 +322,7 @@ class NotificationService extends EventEmitter {
} }
public clear(): void { public clear(): void {
window.SignalWindow.log.info('Removing notification'); window.SignalContext.log.info('Removing notification');
this.notificationData = null; this.notificationData = null;
this.update(); this.update();
} }

View File

@ -41,7 +41,7 @@ function buildAvatarUpdater({ field }: { field: 'avatar' | 'profileAvatar' }) {
const { hash, path } = avatar; const { hash, path } = avatar;
const exists = await doesAttachmentExist(path); const exists = await doesAttachmentExist(path);
if (!exists) { if (!exists) {
window.SignalWindow.log.warn( window.SignalContext.log.warn(
`Conversation.buildAvatarUpdater: attachment ${path} did not exist` `Conversation.buildAvatarUpdater: attachment ${path} did not exist`
); );
} }

View File

@ -14058,14 +14058,14 @@
{ {
"rule": "DOM-innerHTML", "rule": "DOM-innerHTML",
"path": "ts/windows/loading/start.js", "path": "ts/windows/loading/start.js",
"line": " message.innerHTML = window.SignalWindow.i18n('optimizingApplication');", "line": " message.innerHTML = window.SignalContext.i18n('optimizingApplication');",
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2021-09-17T21:02:59.414Z" "updated": "2021-09-17T21:02:59.414Z"
}, },
{ {
"rule": "DOM-innerHTML", "rule": "DOM-innerHTML",
"path": "ts/windows/loading/start.ts", "path": "ts/windows/loading/start.ts",
"line": " message.innerHTML = window.SignalWindow.i18n('optimizingApplication');", "line": " message.innerHTML = window.SignalContext.i18n('optimizingApplication');",
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2021-09-17T21:02:59.414Z" "updated": "2021-09-17T21:02:59.414Z"
} }

22
ts/window.d.ts vendored
View File

@ -10,6 +10,7 @@ import moment from 'moment';
import PQueue from 'p-queue/dist'; import PQueue from 'p-queue/dist';
import { Ref } from 'react'; import { Ref } from 'react';
import { imageToBlurHash } from './util/imageToBlurHash'; import { imageToBlurHash } from './util/imageToBlurHash';
import type { ParsedUrlQuery } from 'querystring';
import * as Util from './util'; import * as Util from './util';
import { import {
ConversationModelCollectionType, ConversationModelCollectionType,
@ -96,7 +97,6 @@ import { MIMEType } from './types/MIME';
import { DownloadedAttachmentType } from './types/Attachment'; import { DownloadedAttachmentType } from './types/Attachment';
import { ElectronLocaleType } from './util/mapToSupportLocale'; import { ElectronLocaleType } from './util/mapToSupportLocale';
import { SignalProtocolStore } from './SignalProtocolStore'; import { SignalProtocolStore } from './SignalProtocolStore';
import { Context as SignalContext } from './context';
import { StartupQueue } from './util/StartupQueue'; import { StartupQueue } from './util/StartupQueue';
import { SocketStatus } from './types/SocketStatus'; import { SocketStatus } from './types/SocketStatus';
import SyncRequest from './textsecure/SyncRequest'; import SyncRequest from './textsecure/SyncRequest';
@ -111,8 +111,7 @@ import { QualifiedAddress } from './types/QualifiedAddress';
import { CI } from './CI'; import { CI } from './CI';
import { IPCEventsType, IPCEventsValuesType } from './util/createIPCEvents'; import { IPCEventsType, IPCEventsValuesType } from './util/createIPCEvents';
import { ConversationView } from './views/conversation_view'; import { ConversationView } from './views/conversation_view';
import { LoggerType } from './types/Logging'; import type { SignalContextType } from './windows/context';
import { SettingType } from './util/preload';
export { Long } from 'long'; export { Long } from 'long';
@ -462,7 +461,6 @@ declare global {
}; };
challengeHandler: ChallengeHandler; challengeHandler: ChallengeHandler;
}; };
SignalContext: SignalContext;
ConversationController: ConversationController; ConversationController: ConversationController;
Events: IPCEventsType; Events: IPCEventsType;
@ -489,21 +487,7 @@ declare global {
RETRY_DELAY: boolean; RETRY_DELAY: boolean;
// Context Isolation // Context Isolation
SignalWindow: { SignalContext: SignalContextType;
Settings: {
themeSetting: SettingType<IPCEventsValuesType['themeSetting']>;
waitForChange: () => Promise<void>;
};
config: string;
context: SignalContext;
getAppInstance: () => string | undefined;
getEnvironment: () => string;
getNodeVersion: () => string;
getVersion: () => string;
i18n: LocalizerType;
log: LoggerType;
renderWindow: () => void;
};
} }
// We want to extend `Error`, so we need an interface. // We want to extend `Error`, so we need an interface.

View File

@ -5,18 +5,15 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { contextBridge, ipcRenderer } from 'electron'; import { contextBridge, ipcRenderer } from 'electron';
// It is important to call this as early as possible import { SignalContext } from '../context';
import '../context';
import { SignalWindow } from '../configure';
import { About } from '../../components/About'; import { About } from '../../components/About';
contextBridge.exposeInMainWorld('SignalWindow', { contextBridge.exposeInMainWorld('SignalContext', {
...SignalWindow, ...SignalContext,
renderWindow: () => { renderWindow: () => {
const environmentText: Array<string> = [SignalWindow.getEnvironment()]; const environmentText: Array<string> = [SignalContext.getEnvironment()];
const appInstance = SignalWindow.getAppInstance(); const appInstance = SignalContext.getAppInstance();
if (appInstance) { if (appInstance) {
environmentText.push(appInstance); environmentText.push(appInstance);
} }
@ -25,8 +22,8 @@ contextBridge.exposeInMainWorld('SignalWindow', {
React.createElement(About, { React.createElement(About, {
closeAbout: () => ipcRenderer.send('close-about'), closeAbout: () => ipcRenderer.send('close-about'),
environment: environmentText.join(' - '), environment: environmentText.join(' - '),
i18n: SignalWindow.i18n, i18n: SignalContext.i18n,
version: SignalWindow.getVersion(), version: SignalContext.getVersion(),
}), }),
document.getElementById('app') document.getElementById('app')
); );

View File

@ -2,13 +2,13 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
async function applyTheme() { 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('light-theme');
document.body.classList.remove('dark-theme'); document.body.classList.remove('dark-theme');
document.body.classList.add( document.body.classList.add(
`${ `${
theme === 'system' theme === 'system'
? window.SignalWindow.context.nativeThemeListener.getSystemTheme() ? window.SignalContext.nativeThemeListener.getSystemTheme()
: theme : theme
}-theme` }-theme`
); );
@ -18,7 +18,7 @@ async function applyThemeLoop() {
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
// eslint-disable-next-line no-await-in-loop // 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 // eslint-disable-next-line no-await-in-loop
await applyTheme(); await applyTheme();
@ -28,6 +28,6 @@ async function applyThemeLoop() {
applyTheme(); applyTheme();
applyThemeLoop(); applyThemeLoop();
window.SignalWindow.context.nativeThemeListener.subscribe(() => { window.SignalContext.nativeThemeListener.subscribe(() => {
applyTheme(); applyTheme();
}); });

View File

@ -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,
};

View File

@ -1,8 +1,83 @@
// Copyright 2021 Signal Messenger, LLC // Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // 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<IPCEventsValuesType['themeSetting']>;
waitForChange: () => Promise<void>;
};
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;

View File

@ -5,19 +5,16 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { contextBridge, ipcRenderer } from 'electron'; import { contextBridge, ipcRenderer } from 'electron';
// It is important to call this as early as possible import { SignalContext } from '../context';
import '../context';
import { SignalWindow } from '../configure';
import { DebugLogWindow } from '../../components/DebugLogWindow'; import { DebugLogWindow } from '../../components/DebugLogWindow';
import * as debugLog from '../../logging/debuglogs'; import * as debugLog from '../../logging/debuglogs';
contextBridge.exposeInMainWorld('SignalWindow', { contextBridge.exposeInMainWorld('SignalContext', {
...SignalWindow, ...SignalContext,
renderWindow: () => { renderWindow: () => {
const environmentText: Array<string> = [SignalWindow.getEnvironment()]; const environmentText: Array<string> = [SignalContext.getEnvironment()];
const appInstance = SignalWindow.getAppInstance(); const appInstance = SignalContext.getAppInstance();
if (appInstance) { if (appInstance) {
environmentText.push(appInstance); environmentText.push(appInstance);
} }
@ -27,15 +24,15 @@ contextBridge.exposeInMainWorld('SignalWindow', {
closeWindow: () => ipcRenderer.send('close-debug-log'), closeWindow: () => ipcRenderer.send('close-debug-log'),
downloadLog: (logText: string) => downloadLog: (logText: string) =>
ipcRenderer.send('show-debug-log-save-dialog', logText), ipcRenderer.send('show-debug-log-save-dialog', logText),
i18n: SignalWindow.i18n, i18n: SignalContext.i18n,
fetchLogs() { fetchLogs() {
return debugLog.fetch( return debugLog.fetch(
SignalWindow.getNodeVersion(), SignalContext.getNodeVersion(),
SignalWindow.getVersion() SignalContext.getVersion()
); );
}, },
uploadLogs(logs: string) { uploadLogs(logs: string) {
return debugLog.upload(logs, SignalWindow.getVersion()); return debugLog.upload(logs, SignalContext.getVersion());
}, },
}), }),
document.getElementById('app') document.getElementById('app')

View File

@ -1,4 +1,8 @@
// Copyright 2021 Signal Messenger, LLC // Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // 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!');
}

View File

@ -3,9 +3,6 @@
import { contextBridge } from 'electron'; import { contextBridge } from 'electron';
// It is important to call this as early as possible import { SignalContext } from '../context';
import '../context';
import { SignalWindow } from '../configure'; contextBridge.exposeInMainWorld('SignalContext', SignalContext);
contextBridge.exposeInMainWorld('SignalWindow', SignalWindow);

View File

@ -3,5 +3,5 @@
const message = document.getElementById('message'); const message = document.getElementById('message');
if (message) { if (message) {
message.innerHTML = window.SignalWindow.i18n('optimizingApplication'); message.innerHTML = window.SignalContext.i18n('optimizingApplication');
} }

View File

@ -5,11 +5,9 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { contextBridge, ipcRenderer } from 'electron'; import { contextBridge, ipcRenderer } from 'electron';
// It is important to call this as early as possible import { SignalContext } from '../context';
import '../context';
import { createSetting } from '../../util/preload'; import { createSetting } from '../../util/preload';
import { SignalWindow } from '../configure';
import { PermissionsPopup } from '../../components/PermissionsPopup'; import { PermissionsPopup } from '../../components/PermissionsPopup';
const mediaCameraPermissions = createSetting('mediaCameraPermissions', { const mediaCameraPermissions = createSetting('mediaCameraPermissions', {
@ -24,21 +22,21 @@ contextBridge.exposeInMainWorld(
window.SignalContext.nativeThemeListener window.SignalContext.nativeThemeListener
); );
contextBridge.exposeInMainWorld('SignalWindow', { contextBridge.exposeInMainWorld('SignalContext', {
...SignalWindow, ...SignalContext,
renderWindow: () => { renderWindow: () => {
const forCalling = SignalWindow.config.forCalling === 'true'; const forCalling = SignalContext.config.forCalling === 'true';
const forCamera = SignalWindow.config.forCamera === 'true'; const forCamera = SignalContext.config.forCamera === 'true';
let message; let message;
if (forCalling) { if (forCalling) {
if (forCamera) { if (forCamera) {
message = SignalWindow.i18n('videoCallingPermissionNeeded'); message = SignalContext.i18n('videoCallingPermissionNeeded');
} else { } else {
message = SignalWindow.i18n('audioCallingPermissionNeeded'); message = SignalContext.i18n('audioCallingPermissionNeeded');
} }
} else { } else {
message = SignalWindow.i18n('audioPermissionNeeded'); message = SignalContext.i18n('audioPermissionNeeded');
} }
function onClose() { function onClose() {
@ -47,7 +45,7 @@ contextBridge.exposeInMainWorld('SignalWindow', {
ReactDOM.render( ReactDOM.render(
React.createElement(PermissionsPopup, { React.createElement(PermissionsPopup, {
i18n: SignalWindow.i18n, i18n: SignalContext.i18n,
message, message,
onAccept: () => { onAccept: () => {
if (!forCamera) { if (!forCamera) {

View File

@ -5,18 +5,15 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { contextBridge, ipcRenderer } from 'electron'; import { contextBridge, ipcRenderer } from 'electron';
// It is important to call this as early as possible import { SignalContext } from '../context';
import '../context';
import { SignalWindow } from '../configure';
import { CallingScreenSharingController } from '../../components/CallingScreenSharingController'; import { CallingScreenSharingController } from '../../components/CallingScreenSharingController';
contextBridge.exposeInMainWorld('SignalWindow', SignalWindow); contextBridge.exposeInMainWorld('SignalContext', SignalContext);
function renderScreenSharingController(presentedSourceName: string): void { function renderScreenSharingController(presentedSourceName: string): void {
ReactDOM.render( ReactDOM.render(
React.createElement(CallingScreenSharingController, { React.createElement(CallingScreenSharingController, {
i18n: SignalWindow.i18n, i18n: SignalContext.i18n,
onCloseController: () => onCloseController: () =>
ipcRenderer.send('close-screen-share-controller'), ipcRenderer.send('close-screen-share-controller'),
onStopSharing: () => ipcRenderer.send('stop-screen-share'), onStopSharing: () => ipcRenderer.send('stop-screen-share'),

View File

@ -5,10 +5,7 @@ import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { contextBridge, ipcRenderer } from 'electron'; import { contextBridge, ipcRenderer } from 'electron';
// It is important to call this as early as possible import { SignalContext } from '../context';
import '../context';
import { SignalWindow } from '../configure';
import * as Settings from '../../types/Settings'; import * as Settings from '../../types/Settings';
import { Preferences } from '../../components/Preferences'; import { Preferences } from '../../components/Preferences';
import { import {
@ -260,7 +257,7 @@ const renderPreferences = async () => {
editCustomColor: ipcEditCustomColor, editCustomColor: ipcEditCustomColor,
getConversationsWithCustomColor: ipcGetConversationsWithCustomColor, getConversationsWithCustomColor: ipcGetConversationsWithCustomColor,
initialSpellCheckSetting: initialSpellCheckSetting:
SignalWindow.config.appStartInitialSpellcheckSetting === 'true', SignalContext.config.appStartInitialSpellcheckSetting === 'true',
makeSyncRequest: ipcMakeSyncRequest, makeSyncRequest: ipcMakeSyncRequest,
removeCustomColor: ipcRemoveCustomColor, removeCustomColor: ipcRemoveCustomColor,
removeCustomColorOnConversations: ipcRemoveCustomColorOnConversations, removeCustomColorOnConversations: ipcRemoveCustomColorOnConversations,
@ -277,7 +274,7 @@ const renderPreferences = async () => {
isPhoneNumberSharingSupported, isPhoneNumberSharingSupported,
isSyncSupported: !isSyncNotSupported, isSyncSupported: !isSyncNotSupported,
isSystemTraySupported: Settings.isSystemTraySupported( isSystemTraySupported: Settings.isSystemTraySupported(
SignalWindow.getVersion() SignalContext.getVersion()
), ),
// Change handlers // Change handlers
@ -343,7 +340,7 @@ const renderPreferences = async () => {
// rerender. // rerender.
onZoomFactorChange: settingZoomFactor.setValue, onZoomFactorChange: settingZoomFactor.setValue,
i18n: SignalWindow.i18n, i18n: SignalContext.i18n,
}; };
function reRender<Value>(f: (value: Value) => Promise<Value>) { function reRender<Value>(f: (value: Value) => Promise<Value>) {
@ -361,7 +358,7 @@ const renderPreferences = async () => {
ipcRenderer.on('preferences-changed', () => renderPreferences()); ipcRenderer.on('preferences-changed', () => renderPreferences());
contextBridge.exposeInMainWorld('SignalWindow', { contextBridge.exposeInMainWorld('SignalContext', {
...SignalWindow, ...SignalContext,
renderWindow: renderPreferences, renderWindow: renderPreferences,
}); });