<commit_before>import { ChildProcess, ExecException, execFile } from 'child_process';
import * as electron from 'electron';
import { app, BrowserWindow, BrowserWindowConstructorOptions, crashReporter, DesktopCapturerSource, globalShortcut, ipcMain } from 'electron';
import * as fs from 'fs';
import * as path from 'path';
import { format, parse } from 'url';

import { apiName, WindowTypes } from '../common/api-interface';
import { isDevEnv, isLinux, isMac, isNodeEnv, isWindowsOS } from '../common/env';
import { i18n, LocaleType } from '../common/i18n';
import { logger } from '../common/logger';
import { getCommandLineArgs, getGuid } from '../common/utils';
import { notification } from '../renderer/notification';
import { AppMenu } from './app-menu';
import { handleChildWindow } from './child-window-handler';
import { CloudConfigDataTypes, config, IConfig, IGlobalConfig } from './config-handler';
import { SpellChecker } from './spell-check-handler';
import { checkIfBuildExpired } from './ttl-handler';
import { versionHandler } from './version-handler';
import { handlePermissionRequests, monitorWindowActions } from './window-actions';
import {
    createComponentWindow,
    didVerifyAndRestoreWindow,
    getBounds, getWindowByName,
    handleCertificateProxyVerification,
    handleDownloadManager,
    injectStyles,
    isSymphonyReachable,
    monitorNetworkInterception,
    preventWindowNavigation,
    reloadWindow,
    windowExists,
} from './window-utils';

interface ICustomBrowserWindowConstructorOpts extends Electron.BrowserWindowConstructorOptions {
    winKey: string;
}

export interface ICustomBrowserWindow extends Electron.BrowserWindow {
    winName: string;
    notificationData?: object;
    origin?: string;
}

// Default window width & height
const DEFAULT_WIDTH: number = 900;
const DEFAULT_HEIGHT: number = 900;

enum ClientVersionTypes {
    CLIENT_1_5 = 'client_1_5',
    CLIENT_MANA_STABLE = 'client_mana_stable',
    CLIENT_MANA_DAILY = 'client_mana_daily',
}

let currentClient = ClientVersionTypes.CLIENT_1_5;

export class WindowHandler {

    /**
     * Verifies if the url is valid and
     * forcefully appends https if not present
     *
     * @param configURL {string}
     */
    private static getValidUrl(configURL: string): string {
        const parsedUrl = parse(configURL);

        if (!parsedUrl.protocol || parsedUrl.protocol !== 'https') {
            parsedUrl.protocol = 'https:';
            parsedUrl.slashes = true;
        }
        return format(parsedUrl);
    }

    public appMenu: AppMenu | null;
    public isAutoReload: boolean;
    public isOnline: boolean;
    public url: string | undefined;
    public willQuitApp: boolean = false;
    public spellchecker: SpellChecker | undefined;
    public isCustomTitleBar: boolean;
    public isWebPageLoading: boolean = true;
    public screenShareIndicatorFrameUtil: string;

    private readonly contextIsolation: boolean;
    private readonly backgroundThrottling: boolean;
    private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
    private readonly globalConfig: IGlobalConfig;
    private readonly config: IConfig;
    // Window reference
    private readonly windows: object;

    private loadFailError: string | undefined;
    private mainWindow: ICustomBrowserWindow | null = null;
    private aboutAppWindow: Electron.BrowserWindow | null = null;
    private screenPickerWindow: Electron.BrowserWindow | null = null;
    private screenSharingIndicatorWindow: Electron.BrowserWindow | null = null;
    private screenSharingFrameWindow: Electron.BrowserWindow | null = null;
    private basicAuthWindow: Electron.BrowserWindow | null = null;
    private notificationSettingsWindow: Electron.BrowserWindow | null = null;

    constructor(opts?: Electron.BrowserViewConstructorOptions) {
        // Use these variables only on initial setup
        this.config = config.getConfigFields([ 'isCustomTitleBar', 'mainWinPos', 'minimizeOnClose', 'notificationSettings', 'alwaysOnTop', 'locale', 'customFlags' ]);
        logger.info(`window-handler: main windows initialized with following config data`, this.config);
        this.globalConfig = config.getGlobalConfigFields([ 'url', 'contextIsolation' ]);
        const { disableThrottling } = config.getCloudConfigFields([ 'disableThrottling' ]) as any;
        const { url, contextIsolation }: IGlobalConfig = this.globalConfig;
        const { customFlags } = this.config;

        this.windows = {};
        this.contextIsolation = contextIsolation || false;
        this.backgroundThrottling = (customFlags.disableThrottling !== CloudConfigDataTypes.ENABLED || disableThrottling !== CloudConfigDataTypes.ENABLED);
        this.isCustomTitleBar = isWindowsOS && this.config.isCustomTitleBar === CloudConfigDataTypes.ENABLED;
        this.windowOpts = {
            ...this.getWindowOpts({
                alwaysOnTop: this.config.alwaysOnTop === CloudConfigDataTypes.ENABLED || false,
                frame: !this.isCustomTitleBar,
                minHeight: 300,
                minWidth: 300,
                title: 'Symphony',
            }, {
                preload: path.join(__dirname, '../renderer/_preload-main.js'),
            }), ...opts,
        };
        this.isAutoReload = false;
        this.isOnline = true;

        this.screenShareIndicatorFrameUtil = '';
        if (isWindowsOS) {
            this.screenShareIndicatorFrameUtil = isDevEnv
                ? path.join(__dirname,
                    '../../../node_modules/screen-share-indicator-frame/ScreenShareIndicatorFrame.exe')
                : path.join(path.dirname(app.getPath('exe')), 'ScreenShareIndicatorFrame.exe');
        } else if (isMac) {
            this.screenShareIndicatorFrameUtil = isDevEnv
                ? path.join(__dirname,
                    '../../../node_modules/screen-share-indicator-frame/SymphonyScreenShareIndicator')
                : path.join(path.dirname(app.getPath('exe')), '../node_modules/screen-share-indicator-frame/SymphonyScreenShareIndicator');
        }

        this.appMenu = null;
        const locale: LocaleType = (this.config.locale || app.getLocale()) as LocaleType;
        i18n.setLocale(locale);

        try {
            const extra = {podUrl: url, process: 'main'};
            const defaultOpts = {uploadToServer: false, companyName: 'Symphony', submitURL: ''};
            crashReporter.start({...defaultOpts, extra});
        } catch (e) {
            throw new Error('failed to init crash report');
        }

    }

    /**
     * Starting point of the app
     */
    public createApplication() {

        this.updateVersionInfo();
        this.spellchecker = new SpellChecker();
        logger.info(`window-handler: initialized spellchecker module with locale ${this.spellchecker.locale}`);

        logger.info('window-handler: createApplication mainWinPos: ' + JSON.stringify(this.config.mainWinPos));

        // set window opts with additional config
        this.mainWindow = new BrowserWindow({
            ...this.windowOpts, ...getBounds(this.config.mainWinPos, DEFAULT_WIDTH, DEFAULT_HEIGHT),
        }) as ICustomBrowserWindow;

        if (isWindowsOS) {
            // SDA-1720 when Symphony on secondary screen is wider than main screen, the window on secondary is clapmed to the width of main screen
            // Only happens on windows (BrowserWindow)
            this.mainWindow.setBounds(getBounds(this.config.mainWinPos, DEFAULT_WIDTH, DEFAULT_HEIGHT) as Electron.Rectangle);
        }

        logger.info('window-handler: this.mainWindow.getBounds: ' + JSON.stringify(this.mainWindow.getBounds()));

        this.mainWindow.winName = apiName.mainWindowName;
        const {isFullScreen, isMaximized} = this.config.mainWinPos ? this.config.mainWinPos : {isFullScreen: false, isMaximized: false};
        if (isMaximized) {
            this.mainWindow.maximize();
            logger.info(`window-handler: window is maximized!`);
        }

        if (isFullScreen) {
            logger.info(`window-handler: window is in full screen!`);
            this.mainWindow.setFullScreen(true);
        }

        // Event needed to hide native menu bar on Windows 10 as we use custom menu bar
        this.mainWindow.webContents.once('did-start-loading', () => {
            logger.info(`window-handler: main window web contents started loading!`);
            if ((this.config.isCustomTitleBar === CloudConfigDataTypes.ENABLED && isWindowsOS) && this.mainWindow && windowExists(this.mainWindow)) {
                this.mainWindow.setMenuBarVisibility(false);
            }
            // monitors network connection and
            // displays error banner on failure
            monitorNetworkInterception();
        });

        this.url = WindowHandler.getValidUrl(this.globalConfig.url);
        logger.info(`window-handler: setting url ${this.url} from config file!`);

        // Get url to load from cmd line or from global config file
        const urlFromCmd = getCommandLineArgs(process.argv, '--url=', false);

        if (urlFromCmd) {
            const commandLineUrl = urlFromCmd.substr(6);
            logger.info(`window-handler: trying to set url ${commandLineUrl} from command line.`);
            const { podWhitelist } = config.getConfigFields([ 'podWhitelist' ]);
            logger.info(`window-handler: checking pod whitelist.`);
            if (podWhitelist.length > 0) {
                logger.info(`window-handler: pod whitelist is not empty ${podWhitelist}`);
                if (podWhitelist.includes(commandLineUrl)) {
                    logger.info(`window-handler: url from command line is whitelisted in the config file.`);
                    logger.info(`window-handler: setting ${commandLineUrl} from the command line as the main window url.`);
                    this.url = commandLineUrl;
                } else {
                    logger.info(`window-handler: url ${commandLineUrl} from command line is NOT WHITELISTED in the config file.`);
                }
            } else {
                logger.info(`window-handler: setting ${commandLineUrl} from the command line as the main window url since pod whitelist is empty.`);
                this.url = commandLineUrl;
            }
        }

        // loads the main window with url from config/cmd line
        this.mainWindow.loadURL(this.url);
        // check for build expiry in case of test builds
        this.checkExpiry(this.mainWindow);
        // need this for postMessage origin
        this.mainWindow.origin = this.globalConfig.url;
        this.mainWindow.webContents.on('did-finish-load', async () => {
            logger.info(`window-handler: main window web contents finished loading!`);
            // early exit if the window has already been destroyed
            if (!this.mainWindow || !windowExists(this.mainWindow)) {
                logger.info(`window-handler: main window web contents destroyed already! exiting`);
                return;
            }
            this.url = this.mainWindow.webContents.getURL();

            // Injects custom title bar and snack bar css into the webContents
            await injectStyles(this.mainWindow, this.isCustomTitleBar);

            this.mainWindow.webContents.send('page-load', {
                isWindowsOS,
                locale: i18n.getLocale(),
                resources: i18n.loadedResources,
                enableCustomTitleBar: this.isCustomTitleBar,
                isMainWindow: true,
            });
            this.appMenu = new AppMenu();
            const { permissions } = config.getConfigFields([ 'permissions' ]);
            this.mainWindow.webContents.send('is-screen-share-enabled', permissions.media);
        });

        this.mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDesc, validatedURL) => {
            logger.error(`window-handler: Failed to load ${validatedURL}, with an error: ${errorCode}::${errorDesc}`);
            this.loadFailError = errorDesc;
        });

        this.mainWindow.webContents.on('did-stop-loading', async () => {
            if (this.mainWindow && windowExists(this.mainWindow)) {
                this.mainWindow.webContents.send('page-load-failed', {
                    locale: i18n.getLocale(),
                    resources: i18n.loadedResources,
                });
                const href = await this.mainWindow.webContents.executeJavaScript('document.location.href');
                try {
                    if (href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/') {
                        if (this.mainWindow && windowExists(this.mainWindow)) {
                            this.mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/network-error.css'), 'utf8').toString());
                            this.mainWindow.webContents.send('network-error', {error: this.loadFailError});
                            isSymphonyReachable(this.mainWindow);
                        }
                    }
                } catch (error) {
                    logger.error(`window-handler: Could not read document.location`, error);
                }
            }
        });

        this.mainWindow.webContents.on('crashed', async (_event: Event, killed: boolean) => {
            if (killed) {
                logger.info(`window-handler: main window crashed (killed)!`);
                return;
            }
            logger.info(`window-handler: main window crashed!`);
            const { response } = await electron.dialog.showMessageBox({
                type: 'error',
                title: i18n.t('Renderer Process Crashed')(),
                message: i18n.t('Oops! Looks like we have had a crash. Please reload or close this window.')(),
                buttons: ['Reload', 'Close'],
            });
            if (!this.mainWindow || !windowExists(this.mainWindow)) {
                return;
            }
            response === 0 ? this.mainWindow.reload() : this.mainWindow.close();
        });

        // Handle main window close
        this.mainWindow.on('close', (event) => {
            if (!this.mainWindow || !windowExists(this.mainWindow)) {
                return;
            }

            if (this.willQuitApp) {
                logger.info(`window-handler: app is quitting, destroying all windows!`);
                return this.destroyAllWindows();
            }

            const { minimizeOnClose } = config.getConfigFields([ 'minimizeOnClose' ]);
            if (minimizeOnClose === CloudConfigDataTypes.ENABLED) {
                event.preventDefault();
                this.mainWindow.minimize();
                return;
            }

            if (isMac) {
                event.preventDefault();
                this.mainWindow.hide();
                return;
            }

            app.quit();
        });

        this.mainWindow.once('closed', () => {
            logger.info(`window-handler: main window closed, destroying all windows!`);
            this.destroyAllWindows();
        });

        // Reloads the Symphony<commit_msg>SDA-1927 Remove screen share indicator frame when quit SDA<commit_after>import { ChildProcess, ExecException, execFile } from 'child_process';
import * as electron from 'electron';
import { app, BrowserWindow, BrowserWindowConstructorOptions, crashReporter, DesktopCapturerSource, globalShortcut, ipcMain } from 'electron';
import * as fs from 'fs';
import * as path from 'path';
import { format, parse } from 'url';

import { apiName, WindowTypes } from '../common/api-interface';
import { isDevEnv, isLinux, isMac, isNodeEnv, isWindowsOS } from '../common/env';
import { i18n, LocaleType } from '../common/i18n';
import { logger } from '../common/logger';
import { getCommandLineArgs, getGuid } from '../common/utils';
import { notification } from '../renderer/notification';
import { AppMenu } from './app-menu';
import { handleChildWindow } from './child-window-handler';
import { CloudConfigDataTypes, config, IConfig, IGlobalConfig } from './config-handler';
import { SpellChecker } from './spell-check-handler';
import { checkIfBuildExpired } from './ttl-handler';
import { versionHandler } from './version-handler';
import { handlePermissionRequests, monitorWindowActions } from './window-actions';
import {
    createComponentWindow,
    didVerifyAndRestoreWindow,
    getBounds, getWindowByName,
    handleCertificateProxyVerification,
    handleDownloadManager,
    injectStyles,
    isSymphonyReachable,
    monitorNetworkInterception,
    preventWindowNavigation,
    reloadWindow,
    windowExists,
} from './window-utils';

interface ICustomBrowserWindowConstructorOpts extends Electron.BrowserWindowConstructorOptions {
    winKey: string;
}

export interface ICustomBrowserWindow extends Electron.BrowserWindow {
    winName: string;
    notificationData?: object;
    origin?: string;
}

// Default window width & height
const DEFAULT_WIDTH: number = 900;
const DEFAULT_HEIGHT: number = 900;

enum ClientVersionTypes {
    CLIENT_1_5 = 'client_1_5',
    CLIENT_MANA_STABLE = 'client_mana_stable',
    CLIENT_MANA_DAILY = 'client_mana_daily',
}

let currentClient = ClientVersionTypes.CLIENT_1_5;

export class WindowHandler {

    /**
     * Verifies if the url is valid and
     * forcefully appends https if not present
     *
     * @param configURL {string}
     */
    private static getValidUrl(configURL: string): string {
        const parsedUrl = parse(configURL);

        if (!parsedUrl.protocol || parsedUrl.protocol !== 'https') {
            parsedUrl.protocol = 'https:';
            parsedUrl.slashes = true;
        }
        return format(parsedUrl);
    }

    public appMenu: AppMenu | null;
    public isAutoReload: boolean;
    public isOnline: boolean;
    public url: string | undefined;
    public willQuitApp: boolean = false;
    public spellchecker: SpellChecker | undefined;
    public isCustomTitleBar: boolean;
    public isWebPageLoading: boolean = true;
    public screenShareIndicatorFrameUtil: string;

    private readonly contextIsolation: boolean;
    private readonly backgroundThrottling: boolean;
    private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
    private readonly globalConfig: IGlobalConfig;
    private readonly config: IConfig;
    // Window reference
    private readonly windows: object;

    private loadFailError: string | undefined;
    private mainWindow: ICustomBrowserWindow | null = null;
    private aboutAppWindow: Electron.BrowserWindow | null = null;
    private screenPickerWindow: Electron.BrowserWindow | null = null;
    private screenSharingIndicatorWindow: Electron.BrowserWindow | null = null;
    private screenSharingFrameWindow: Electron.BrowserWindow | null = null;
    private basicAuthWindow: Electron.BrowserWindow | null = null;
    private notificationSettingsWindow: Electron.BrowserWindow | null = null;

    constructor(opts?: Electron.BrowserViewConstructorOptions) {
        // Use these variables only on initial setup
        this.config = config.getConfigFields([ 'isCustomTitleBar', 'mainWinPos', 'minimizeOnClose', 'notificationSettings', 'alwaysOnTop', 'locale', 'customFlags' ]);
        logger.info(`window-handler: main windows initialized with following config data`, this.config);
        this.globalConfig = config.getGlobalConfigFields([ 'url', 'contextIsolation' ]);
        const { disableThrottling } = config.getCloudConfigFields([ 'disableThrottling' ]) as any;
        const { url, contextIsolation }: IGlobalConfig = this.globalConfig;
        const { customFlags } = this.config;

        this.windows = {};
        this.contextIsolation = contextIsolation || false;
        this.backgroundThrottling = (customFlags.disableThrottling !== CloudConfigDataTypes.ENABLED || disableThrottling !== CloudConfigDataTypes.ENABLED);
        this.isCustomTitleBar = isWindowsOS && this.config.isCustomTitleBar === CloudConfigDataTypes.ENABLED;
        this.windowOpts = {
            ...this.getWindowOpts({
                alwaysOnTop: this.config.alwaysOnTop === CloudConfigDataTypes.ENABLED || false,
                frame: !this.isCustomTitleBar,
                minHeight: 300,
                minWidth: 300,
                title: 'Symphony',
            }, {
                preload: path.join(__dirname, '../renderer/_preload-main.js'),
            }), ...opts,
        };
        this.isAutoReload = false;
        this.isOnline = true;

        this.screenShareIndicatorFrameUtil = '';
        if (isWindowsOS) {
            this.screenShareIndicatorFrameUtil = isDevEnv
                ? path.join(__dirname,
                    '../../../node_modules/screen-share-indicator-frame/ScreenShareIndicatorFrame.exe')
                : path.join(path.dirname(app.getPath('exe')), 'ScreenShareIndicatorFrame.exe');
        } else if (isMac) {
            this.screenShareIndicatorFrameUtil = isDevEnv
                ? path.join(__dirname,
                    '../../../node_modules/screen-share-indicator-frame/SymphonyScreenShareIndicator')
                : path.join(path.dirname(app.getPath('exe')), '../node_modules/screen-share-indicator-frame/SymphonyScreenShareIndicator');
        }

        this.appMenu = null;
        const locale: LocaleType = (this.config.locale || app.getLocale()) as LocaleType;
        i18n.setLocale(locale);

        try {
            const extra = {podUrl: url, process: 'main'};
            const defaultOpts = {uploadToServer: false, companyName: 'Symphony', submitURL: ''};
            crashReporter.start({...defaultOpts, extra});
        } catch (e) {
            throw new Error('failed to init crash report');
        }

    }

    /**
     * Starting point of the app
     */
    public createApplication() {

        this.updateVersionInfo();
        this.spellchecker = new SpellChecker();
        logger.info(`window-handler: initialized spellchecker module with locale ${this.spellchecker.locale}`);

        logger.info('window-handler: createApplication mainWinPos: ' + JSON.stringify(this.config.mainWinPos));

        // set window opts with additional config
        this.mainWindow = new BrowserWindow({
            ...this.windowOpts, ...getBounds(this.config.mainWinPos, DEFAULT_WIDTH, DEFAULT_HEIGHT),
        }) as ICustomBrowserWindow;

        if (isWindowsOS) {
            // SDA-1720 when Symphony on secondary screen is wider than main screen, the window on secondary is clapmed to the width of main screen
            // Only happens on windows (BrowserWindow)
            this.mainWindow.setBounds(getBounds(this.config.mainWinPos, DEFAULT_WIDTH, DEFAULT_HEIGHT) as Electron.Rectangle);
        }

        logger.info('window-handler: this.mainWindow.getBounds: ' + JSON.stringify(this.mainWindow.getBounds()));

        this.mainWindow.winName = apiName.mainWindowName;
        const {isFullScreen, isMaximized} = this.config.mainWinPos ? this.config.mainWinPos : {isFullScreen: false, isMaximized: false};
        if (isMaximized) {
            this.mainWindow.maximize();
            logger.info(`window-handler: window is maximized!`);
        }

        if (isFullScreen) {
            logger.info(`window-handler: window is in full screen!`);
            this.mainWindow.setFullScreen(true);
        }

        // Event needed to hide native menu bar on Windows 10 as we use custom menu bar
        this.mainWindow.webContents.once('did-start-loading', () => {
            logger.info(`window-handler: main window web contents started loading!`);
            if ((this.config.isCustomTitleBar === CloudConfigDataTypes.ENABLED && isWindowsOS) && this.mainWindow && windowExists(this.mainWindow)) {
                this.mainWindow.setMenuBarVisibility(false);
            }
            // monitors network connection and
            // displays error banner on failure
            monitorNetworkInterception();
        });

        this.url = WindowHandler.getValidUrl(this.globalConfig.url);
        logger.info(`window-handler: setting url ${this.url} from config file!`);

        // Get url to load from cmd line or from global config file
        const urlFromCmd = getCommandLineArgs(process.argv, '--url=', false);

        if (urlFromCmd) {
            const commandLineUrl = urlFromCmd.substr(6);
            logger.info(`window-handler: trying to set url ${commandLineUrl} from command line.`);
            const { podWhitelist } = config.getConfigFields([ 'podWhitelist' ]);
            logger.info(`window-handler: checking pod whitelist.`);
            if (podWhitelist.length > 0) {
                logger.info(`window-handler: pod whitelist is not empty ${podWhitelist}`);
                if (podWhitelist.includes(commandLineUrl)) {
                    logger.info(`window-handler: url from command line is whitelisted in the config file.`);
                    logger.info(`window-handler: setting ${commandLineUrl} from the command line as the main window url.`);
                    this.url = commandLineUrl;
                } else {
                    logger.info(`window-handler: url ${commandLineUrl} from command line is NOT WHITELISTED in the config file.`);
                }
            } else {
                logger.info(`window-handler: setting ${commandLineUrl} from the command line as the main window url since pod whitelist is empty.`);
                this.url = commandLineUrl;
            }
        }

        // loads the main window with url from config/cmd line
        this.mainWindow.loadURL(this.url);
        // check for build expiry in case of test builds
        this.checkExpiry(this.mainWindow);
        // need this for postMessage origin
        this.mainWindow.origin = this.globalConfig.url;
        this.mainWindow.webContents.on('did-finish-load', async () => {
            logger.info(`window-handler: main window web contents finished loading!`);
            // early exit if the window has already been destroyed
            if (!this.mainWindow || !windowExists(this.mainWindow)) {
                logger.info(`window-handler: main window web contents destroyed already! exiting`);
                return;
            }
            this.url = this.mainWindow.webContents.getURL();

            // Injects custom title bar and snack bar css into the webContents
            await injectStyles(this.mainWindow, this.isCustomTitleBar);

            this.mainWindow.webContents.send('page-load', {
                isWindowsOS,
                locale: i18n.getLocale(),
                resources: i18n.loadedResources,
                enableCustomTitleBar: this.isCustomTitleBar,
                isMainWindow: true,
            });
            this.appMenu = new AppMenu();
            const { permissions } = config.getConfigFields([ 'permissions' ]);
            this.mainWindow.webContents.send('is-screen-share-enabled', permissions.media);
        });

        this.mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDesc, validatedURL) => {
            logger.error(`window-handler: Failed to load ${validatedURL}, with an error: ${errorCode}::${errorDesc}`);
            this.loadFailError = errorDesc;
        });

        this.mainWindow.webContents.on('did-stop-loading', async () => {
            if (this.mainWindow && windowExists(this.mainWindow)) {
                this.mainWindow.webContents.send('page-load-failed', {
                    locale: i18n.getLocale(),
                    resources: i18n.loadedResources,
                });
                const href = await this.mainWindow.webContents.executeJavaScript('document.location.href');
                try {
                    if (href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/') {
                        if (this.mainWindow && windowExists(this.mainWindow)) {
                            this.mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/network-error.css'), 'utf8').toString());
                            this.mainWindow.webContents.send('network-error', {error: this.loadFailError});
                            isSymphonyReachable(this.mainWindow);
                        }
                    }
                } catch (error) {
                    logger.error(`window-handler: Could not read document.location`, error);
                }
            }
        });

        this.mainWindow.webContents.on('crashed', async (_event: Event, killed: boolean) => {
            if (killed) {
                logger.info(`window-handler: main window crashed (killed)!`);
                return;
            }
            logger.info(`window-handler: main window crashed!`);
            const { response } = await electron.dialog.showMessageBox({
                type: 'error',
                title: i18n.t('Renderer Process Crashed')(),
                message: i18n.t('Oops! Looks like we have had a crash. Please reload or close this window.')(),
                buttons: ['Reload', 'Close'],
            });
            if (!this.mainWindow || !windowExists(this.mainWindow)) {
                return;
            }
            response === 0 ? this.mainWindow.reload() : this.mainWindow.close();
        });

        // Handle main window close
        this.mainWindow.on('close', (event) => {
            if (!this.mainWindow || !windowExists(this.mainWindow)) {
                return;
            }

            if (this.willQuitApp) {
                logger.info(`window-handler: app is quitting, destroying all windows!`);
                return this.destroyAllWindows();
            }

            const { minimizeOnClose } = config.getConfigFields([ 'minimizeOnClose' ]);
            if (minimizeOnClose === CloudConfigDataTypes.ENABLED) {
                event.preventDefault();
                this.mainWindow.minimize();
                return;
            }

            if (isMac) {
                event.preventDefault();
                this.mainWindow.hide();
                return;
            }

            app.quit();
        });

        this.mainWindow.once('closed', () => {
            logger.info(`window-handler: main window closed, destroying all windows!`);
            if (isWindowsOS || isMac) {
                this.execCmd(this.screenShareIndicatorFrameUtil, []);
            }
            this.destroyAllWindows();
        });

        // Reloads the Symphony
{'fraction_non_alphanumeric': 0.10974930362116991, 'fraction_numerical': 0.0031459937735539897, 'mean_word_length': 2.418010752688172, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6057536', 'n_tokens_mistral': 8021, 'n_tokens_neox': 7828, 'n_words': 1875}