2017-03-01 16:32:21 -08:00
'use strict' ;
2017-11-10 14:13:41 +05:30
const fs = require ( 'fs' ) ;
2017-03-01 16:32:21 -08:00
const electron = require ( 'electron' ) ;
const app = electron . app ;
2018-04-30 17:32:10 +05:30
const electronSession = electron . session ;
2018-03-29 12:43:54 +05:30
const globalShortcut = electron . globalShortcut ;
2017-05-19 08:01:41 -07:00
const BrowserWindow = electron . BrowserWindow ;
2017-03-01 16:32:21 -08:00
const path = require ( 'path' ) ;
2017-04-18 09:02:25 -07:00
const nodeURL = require ( 'url' ) ;
2017-04-20 11:54:11 -07:00
const querystring = require ( 'querystring' ) ;
2017-06-27 20:38:58 +05:30
const filesize = require ( 'filesize' ) ;
2019-03-19 19:00:40 +05:30
const fetch = require ( 'electron-fetch' ) . default ;
2017-03-01 16:32:21 -08:00
2018-06-06 17:27:10 +05:30
const { getTemplate , getMinimizeOnClose , getTitleBarStyle } = require ( './menus/menuTemplate.js' ) ;
2017-04-20 11:54:11 -07:00
const isInDisplayBounds = require ( './utils/isInDisplayBounds.js' ) ;
2017-03-09 10:12:28 -08:00
const getGuid = require ( './utils/getGuid.js' ) ;
2017-05-09 00:00:45 +05:30
const log = require ( './log.js' ) ;
2017-03-01 16:32:21 -08:00
const logLevels = require ( './enums/logLevels.js' ) ;
2017-04-06 10:07:58 -07:00
const notify = require ( './notify/electron-notify.js' ) ;
2017-06-15 22:41:29 +05:30
const eventEmitter = require ( './eventEmitter' ) ;
2017-04-20 11:54:11 -07:00
const throttle = require ( './utils/throttle.js' ) ;
2018-12-04 12:13:19 -08:00
const { getConfigField , updateConfigField , readConfigFileSync , getMultipleConfigField , readConfigFromFile } = require ( './config.js' ) ;
2018-10-22 22:34:48 +05:30
const { isMac , isWindowsOS , isDevEnv } = require ( './utils/misc' ) ;
2018-05-21 17:04:24 +05:30
const { isWhitelisted , parseDomain } = require ( './utils/whitelistHandler' ) ;
2018-04-11 13:13:59 +00:00
const { initCrashReporterMain , initCrashReporterRenderer } = require ( './crashReporter.js' ) ;
2018-06-19 20:26:04 +05:30
const i18n = require ( './translation/i18n' ) ;
2018-06-25 14:36:20 +05:30
const getCmdLineArg = require ( './utils/getCmdLineArg' ) ;
2019-03-19 19:00:40 +05:30
const config = readConfigFileSync ( ) ;
2017-05-24 19:02:49 +05:30
2019-01-14 17:28:20 +05:30
const SpellChecker = require ( './spellChecker' ) . SpellCheckHelper ;
const spellchecker = new SpellChecker ( ) ;
2017-03-01 16:32:21 -08:00
// show dialog when certificate errors occur
require ( './dialogs/showCertError.js' ) ;
2017-10-18 12:17:09 +05:30
require ( './dialogs/showBasicAuth.js' ) ;
2017-03-01 16:32:21 -08:00
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow ;
let windows = { } ;
let willQuitApp = false ;
let isOnline = true ;
2017-04-20 11:54:11 -07:00
let boundsChangeWindow ;
2017-06-15 22:41:29 +05:30
let alwaysOnTop = false ;
2017-06-17 03:59:56 +05:30
let position = 'lower-right' ;
let display ;
2018-04-11 13:06:46 +00:00
let isAutoReload = false ;
2018-07-20 16:23:08 +05:30
let devToolsEnabled = true ;
2018-08-06 15:31:02 +05:30
let isCustomTitleBarEnabled = true ;
2018-11-29 23:24:23 +05:30
let titleBarStyles ;
2019-03-19 19:00:40 +05:30
let networkStatusCheckIntervalId ;
const networkStatusCheckInterval = 5000 ;
2017-12-13 20:10:18 +05:30
2018-07-09 13:24:35 +05:30
const KeyCodes = {
Esc : 27 ,
2018-08-06 15:31:02 +05:30
Alt : 18 ,
2018-07-09 13:24:35 +05:30
} ;
2018-03-09 16:16:47 +05:30
// Application menu
let menu ;
2018-06-21 18:13:03 +05:30
let lang ;
2018-03-09 16:16:47 +05:30
2017-04-18 09:02:25 -07:00
// note: this file is built using browserify in prebuild step.
2017-03-07 14:44:31 -08:00
const preloadMainScript = path . join ( _ _dirname , 'preload/_preloadMain.js' ) ;
2017-03-01 16:32:21 -08:00
2017-05-11 11:11:42 -07:00
const MIN _WIDTH = 300 ;
2017-10-31 10:20:44 +05:30
const MIN _HEIGHT = 300 ;
// Default window size for pop-out windows
const DEFAULT _WIDTH = 300 ;
const DEFAULT _HEIGHT = 600 ;
2017-05-11 11:11:42 -07:00
2018-05-21 17:04:24 +05:30
// Certificate transparency whitelist
let ctWhitelist = [ ] ;
2019-03-20 20:35:05 +05:30
let lastLoadFailError ;
2018-05-21 17:04:24 +05:30
2017-08-24 16:18:32 +05:30
/ * *
* Adds a window key
* @ param key
* @ param browserWin
* /
2017-03-01 16:32:21 -08:00
function addWindowKey ( key , browserWin ) {
2017-05-09 00:00:45 +05:30
windows [ key ] = browserWin ;
2017-03-01 16:32:21 -08:00
}
2017-08-24 16:18:32 +05:30
/ * *
* Removes a window key
* @ param key
* /
2017-03-01 16:32:21 -08:00
function removeWindowKey ( key ) {
2017-05-09 00:00:45 +05:30
delete windows [ key ] ;
2017-03-01 16:32:21 -08:00
}
2017-08-24 16:18:32 +05:30
/ * *
* Gets the parsed url
2018-04-02 15:51:22 +05:30
* @ returns { Url }
2018-03-29 15:54:42 +05:30
* @ param appUrl
2017-08-24 16:18:32 +05:30
* /
2018-03-29 15:54:42 +05:30
function getParsedUrl ( appUrl ) {
let parsedUrl = nodeURL . parse ( appUrl ) ;
2018-04-02 15:51:22 +05:30
if ( ! parsedUrl . protocol || parsedUrl . protocol !== 'https:' ) {
2018-03-29 15:54:42 +05:30
parsedUrl . protocol = 'https:' ;
parsedUrl . slashes = true
}
let url = nodeURL . format ( parsedUrl ) ;
2018-04-02 15:51:22 +05:30
return nodeURL . parse ( url ) ;
2017-04-18 09:02:25 -07:00
}
2019-01-14 17:28:20 +05:30
/ * *
* Returns the Spellchecker instance
* @ returns { SpellCheckHelper }
* /
function getSpellchecker ( ) {
return spellchecker ;
}
/ * *
* Method that invokes native module that
* verifies missed spelled word
* @ param text { string }
* @ returns { * }
* /
function isMisspelled ( text ) {
if ( ! spellchecker ) {
return false ;
}
return spellchecker . isMisspelled ( text ) ;
}
2017-08-24 16:18:32 +05:30
/ * *
* Creates the main window
* @ param initialUrl
* /
2017-04-18 09:02:25 -07:00
function createMainWindow ( initialUrl ) {
2018-07-20 16:23:08 +05:30
let configParams = [ 'mainWinPos' , 'isCustomTitleBar' , 'locale' , 'devToolsEnabled' ] ;
getMultipleConfigField ( configParams )
2018-06-06 17:27:10 +05:30
. then ( configData => {
2018-07-20 16:23:08 +05:30
lang = configData && configData . locale || app . getLocale ( ) ;
devToolsEnabled = configData && configData . devToolsEnabled ;
2018-06-06 17:27:10 +05:30
doCreateMainWindow ( initialUrl , configData . mainWinPos , configData . isCustomTitleBar ) ;
2018-05-10 18:11:52 +05:30
} )
. catch ( ( ) => {
// failed use default bounds and frame
2018-06-21 18:13:03 +05:30
lang = app . getLocale ( ) ;
2018-06-06 17:27:10 +05:30
doCreateMainWindow ( initialUrl , null , false ) ;
2018-05-10 18:11:52 +05:30
} ) ;
2017-04-20 11:54:11 -07:00
}
2018-12-19 02:13:58 -02:00
/ * *
* ELECTRON - 955 : Always on Top - Toast notification does not show on top ( front ) of the Electron app after it is minimized and maximized again
* Bring to front all notification windows
* /
function bringToFrontNotification ( ) {
if ( mainWindow && ! mainWindow . isDestroyed ( ) ) {
let allBrowserWindows = BrowserWindow . getAllWindows ( ) ;
const notificationWindow = allBrowserWindows . filter ( ( item ) => item . winName === 'notification-window' ) ;
if ( mainWindow . isAlwaysOnTop ( ) ) {
notificationWindow . forEach ( ( item ) => {
if ( item && ! item . isDestroyed ( ) ) {
item . setAlwaysOnTop ( true ) ;
}
} ) ;
}
}
}
2017-08-24 16:18:32 +05:30
/ * *
* Creates the main window with bounds
* @ param initialUrl
* @ param initialBounds
2018-06-06 17:27:10 +05:30
* @ param isCustomTitleBar
2017-08-24 16:18:32 +05:30
* /
2018-06-06 17:27:10 +05:30
function doCreateMainWindow ( initialUrl , initialBounds , isCustomTitleBar ) {
2017-04-18 09:02:25 -07:00
let url = initialUrl ;
2017-03-01 16:32:21 -08:00
let key = getGuid ( ) ;
2018-05-10 18:11:52 +05:30
2018-03-09 16:16:47 +05:30
// condition whether to enable custom Windows 10 title bar
2018-08-06 15:31:02 +05:30
isCustomTitleBarEnabled = typeof isCustomTitleBar === 'boolean'
2018-06-06 17:27:10 +05:30
&& isCustomTitleBar
2018-09-03 16:25:46 +05:30
&& isWindowsOS ;
2018-07-19 18:45:16 +05:30
2018-05-21 17:04:24 +05:30
ctWhitelist = config && config . ctWhitelist ;
2018-07-19 18:45:16 +05:30
2018-05-21 17:04:24 +05:30
log . send ( logLevels . INFO , ` creating main window for ${ url } ` ) ;
2018-07-19 18:45:16 +05:30
2018-11-27 16:56:08 -08:00
if ( config && config . customFlags ) {
2018-07-19 18:45:16 +05:30
2018-04-30 17:32:10 +05:30
if ( config . customFlags . authServerWhitelist && config . customFlags . authServerWhitelist !== "" ) {
electronSession . defaultSession . allowNTLMCredentialsForDomains ( config . customFlags . authServerWhitelist ) ;
}
2018-07-19 18:45:16 +05:30
2018-04-30 17:32:10 +05:30
}
2018-07-19 18:45:16 +05:30
2017-04-18 09:02:25 -07:00
let newWinOpts = {
2017-03-01 16:32:21 -08:00
title : 'Symphony' ,
show : true ,
2017-05-11 11:11:42 -07:00
minWidth : MIN _WIDTH ,
minHeight : MIN _HEIGHT ,
2018-03-09 16:16:47 +05:30
frame : ! isCustomTitleBarEnabled ,
2017-06-15 22:41:29 +05:30
alwaysOnTop : false ,
2017-03-01 16:32:21 -08:00
webPreferences : {
2018-10-22 22:34:48 +05:30
sandbox : true ,
nodeIntegration : false ,
2017-03-07 14:44:31 -08:00
preload : preloadMainScript ,
2019-02-12 09:57:03 +05:30
backgroundThrottling : false
2017-03-01 16:32:21 -08:00
}
2017-04-18 09:02:25 -07:00
} ;
2017-05-13 23:53:44 +05:30
// set size and position
2017-04-20 11:54:11 -07:00
let bounds = initialBounds ;
// if bounds if not fully contained in some display then use default size
// and position.
2018-05-22 14:29:28 +05:30
if ( ! isInDisplayBounds ( bounds ) || initialBounds . isMaximized || initialBounds . isFullScreen ) {
2017-04-20 11:54:11 -07:00
bounds = null ;
}
if ( bounds && bounds . width && bounds . height ) {
newWinOpts . width = bounds . width ;
newWinOpts . height = bounds . height ;
} else {
2017-12-15 11:23:21 +05:30
newWinOpts . width = 900 ;
newWinOpts . height = 900 ;
2017-04-20 11:54:11 -07:00
}
// will center on screen if values not provided
if ( bounds && bounds . x && bounds . y ) {
newWinOpts . x = bounds . x ;
newWinOpts . y = bounds . y ;
}
2017-06-15 22:41:29 +05:30
// will set the main window on top as per the user prefs
2017-12-29 12:48:19 +05:30
if ( alwaysOnTop ) {
2017-06-15 22:41:29 +05:30
newWinOpts . alwaysOnTop = alwaysOnTop ;
}
2017-04-18 09:02:25 -07:00
// note: augmenting with some custom values
newWinOpts . winKey = key ;
2017-05-19 08:01:41 -07:00
mainWindow = new BrowserWindow ( newWinOpts ) ;
2017-04-18 09:02:25 -07:00
mainWindow . winName = 'main' ;
2018-11-27 17:24:15 -08:00
logBrowserWindowEvents ( mainWindow , mainWindow . winName ) ;
2017-03-01 16:32:21 -08:00
2018-05-22 14:29:28 +05:30
let throttledMainWinBoundsChange = throttle ( 1000 , saveMainWinBounds ) ;
2017-04-20 11:54:11 -07:00
mainWindow . on ( 'move' , throttledMainWinBoundsChange ) ;
2017-05-09 00:00:45 +05:30
mainWindow . on ( 'resize' , throttledMainWinBoundsChange ) ;
2018-07-09 13:24:35 +05:30
mainWindow . on ( 'enter-full-screen' , ( ) => {
const snackBarContent = i18n . getMessageFor ( 'SnackBar' ) ;
// event sent to renderer process to show snack bar
mainWindow . webContents . send ( 'window-enter-full-screen' , { snackBar : snackBarContent } ) ;
} ) ;
mainWindow . on ( 'leave-full-screen' , ( ) => {
// event sent to renderer process to remove snack bar
mainWindow . webContents . send ( 'window-leave-full-screen' ) ;
} ) ;
2017-04-20 11:54:11 -07:00
2018-08-01 00:04:45 +07:00
if ( initialBounds ) {
2018-05-22 14:29:28 +05:30
// maximizes the application if previously maximized
if ( initialBounds . isMaximized ) {
mainWindow . maximize ( ) ;
}
// Sets the application to full-screen if previously set to full-screen
if ( isMac && initialBounds . isFullScreen ) {
mainWindow . setFullScreen ( true ) ;
}
}
2018-07-09 13:33:19 +05:30
// Event needed to hide native menu bar on Windows 10 as we use custom menu bar
mainWindow . webContents . once ( 'did-start-loading' , ( ) => {
2018-09-03 16:25:46 +05:30
if ( ( isCustomTitleBarEnabled || isWindowsOS ) && mainWindow && ! mainWindow . isDestroyed ( ) ) {
2018-07-09 13:33:19 +05:30
mainWindow . setMenuBarVisibility ( false ) ;
}
} ) ;
2017-03-01 16:32:21 -08:00
// content can be cached and will still finish load but
2017-05-13 23:53:44 +05:30
// we might not have network connectivity, so warn the user.
2017-05-09 00:00:45 +05:30
mainWindow . webContents . on ( 'did-finish-load' , function ( ) {
2018-04-11 13:13:59 +00:00
// Initialize crash reporter
initCrashReporterMain ( { process : 'main window' } ) ;
initCrashReporterRenderer ( mainWindow , { process : 'render | main window' } ) ;
2017-04-18 09:02:25 -07:00
url = mainWindow . webContents . getURL ( ) ;
2018-07-09 13:24:35 +05:30
mainWindow . webContents . send ( 'on-page-load' ) ;
// initializes and applies styles required for snack bar
mainWindow . webContents . insertCSS ( fs . readFileSync ( path . join ( _ _dirname , '/snackBar/style.css' ) , 'utf8' ) . toString ( ) ) ;
2018-08-06 15:31:02 +05:30
if ( isCustomTitleBarEnabled ) {
2018-11-29 23:24:23 +05:30
if ( ! titleBarStyles ) {
let titleBarStylesPath ;
let stylesFileName = path . join ( 'config' , 'titleBarStyles.css' ) ;
if ( isDevEnv ) {
titleBarStylesPath = path . join ( app . getAppPath ( ) , stylesFileName ) ;
} else {
const execPath = path . dirname ( app . getPath ( 'exe' ) ) ;
titleBarStylesPath = path . join ( execPath , stylesFileName ) ;
}
if ( fs . existsSync ( titleBarStylesPath ) ) {
titleBarStyles = fs . readFileSync ( titleBarStylesPath , 'utf8' ) . toString ( ) ;
} else {
titleBarStyles = fs . readFileSync ( path . join ( _ _dirname , '/windowsTitleBar/style.css' ) , 'utf8' ) . toString ( ) ;
}
}
mainWindow . webContents . insertCSS ( titleBarStyles ) ;
2018-03-09 16:16:47 +05:30
// This is required to initiate Windows title bar only after insertCSS
2018-06-06 17:27:10 +05:30
const titleBarStyle = getTitleBarStyle ( ) ;
mainWindow . webContents . send ( 'initiate-windows-title-bar' , titleBarStyle ) ;
2018-03-09 16:16:47 +05:30
}
2017-04-18 09:02:25 -07:00
2017-03-01 16:32:21 -08:00
if ( ! isOnline ) {
2019-03-19 19:00:40 +05:30
return ;
2017-03-01 16:32:21 -08:00
}
2019-03-19 19:00:40 +05:30
// updates the notify config with user preference
notify . updateConfig ( { position : position , display : display } ) ;
// removes all existing notifications when main window reloads
notify . reset ( ) ;
log . send ( logLevels . INFO , 'loaded main window url: ' + url ) ;
2018-06-25 14:36:20 +05:30
// ELECTRON-540 - needed to automatically
// select desktop capture source
const screenShareArg = getCmdLineArg ( process . argv , '--auto-select-desktop-capture-source' , false ) ;
if ( screenShareArg && typeof screenShareArg === 'string' ) {
mainWindow . webContents . send ( 'screen-share-argv' , screenShareArg ) ;
}
2018-07-17 10:42:33 +05:30
if ( config && config . permissions ) {
const permission = ' screen sharing' ;
2018-12-04 12:12:58 -08:00
const fullMessage = i18n . getMessageFor ( 'Your administrator has disabled ' ) + permission + '. ' + i18n . getMessageFor ( 'Please contact your admin for help' ) ;
2018-07-17 10:42:33 +05:30
const dialogContent = { type : 'error' , title : i18n . getMessageFor ( 'Permission Denied' ) + '!' , message : fullMessage } ;
mainWindow . webContents . send ( 'is-screen-share-enabled' , config . permissions . media , dialogContent ) ;
}
2017-03-01 16:32:21 -08:00
} ) ;
2019-03-19 19:00:40 +05:30
mainWindow . webContents . on ( 'did-fail-load' , ( event , errorCode , errorDesc , validatedURL ) => {
2019-03-20 20:35:05 +05:30
log . send ( logLevels . ERROR , ` Failed to load ${ validatedURL } , with an error: ${ errorCode } :: ${ errorDesc } ` ) ;
lastLoadFailError = errorDesc
2019-03-19 19:00:40 +05:30
} ) ;
mainWindow . webContents . on ( 'did-stop-loading' , ( ) => {
2019-03-20 20:35:05 +05:30
if ( mainWindow && ! mainWindow . isDestroyed ( ) ) {
// Verify if SDA ended up in a blank page
mainWindow . webContents . executeJavaScript ( 'document.location.href' ) . then ( ( href ) => {
if ( href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/' ) {
const message = i18n . getMessageFor ( 'NetworkError' ) ;
mainWindow . webContents . insertCSS ( fs . readFileSync ( path . join ( _ _dirname , '/networkError/style.css' ) , 'utf8' ) . toString ( ) ) ;
mainWindow . webContents . send ( 'network-error' , { message , error : lastLoadFailError || "PAGE_STOPPED_LOADING" } ) ;
isSymphonyReachable ( mainWindow , config . url ) ;
}
} ) . catch ( ( error ) => {
log . send ( logLevels . ERROR , ` Could not read document.location error: ${ error } ` ) ;
} ) ;
}
2017-03-01 16:32:21 -08:00
} ) ;
2017-05-24 19:02:49 +05:30
// In case a renderer process crashes, provide an
// option for the user to either reload or close the window
2019-01-11 11:37:59 +05:30
mainWindow . webContents . on ( 'crashed' , function ( event , killed ) {
log . send ( logLevels . INFO , ` Main Window crashed! Killed? ${ killed } ` ) ;
if ( killed ) {
return ;
}
2017-05-24 19:02:49 +05:30
const options = {
type : 'error' ,
2018-06-19 20:26:04 +05:30
title : i18n . getMessageFor ( 'Renderer Process Crashed' ) ,
message : i18n . getMessageFor ( 'Oops! Looks like we have had a crash. Please reload or close this window.' ) ,
2017-05-24 19:02:49 +05:30
buttons : [ 'Reload' , 'Close' ]
} ;
2017-09-13 16:31:09 +05:30
electron . dialog . showMessageBox ( options , function ( index ) {
2017-05-24 19:02:49 +05:30
if ( index === 0 ) {
mainWindow . reload ( ) ;
}
2017-09-25 14:33:12 +05:30
else {
mainWindow . close ( ) ;
}
2017-05-24 19:02:49 +05:30
} ) ;
} ) ;
2018-07-19 18:45:16 +05:30
2018-03-29 12:43:54 +05:30
registerShortcuts ( ) ;
2018-03-20 17:49:27 +05:30
handlePermissionRequests ( mainWindow . webContents ) ;
2017-05-24 19:02:49 +05:30
2017-03-01 16:32:21 -08:00
addWindowKey ( key , mainWindow ) ;
mainWindow . loadURL ( url ) ;
2018-07-19 18:45:16 +05:30
2018-08-07 18:02:02 +05:30
setLocale ( mainWindow , { language : lang } ) ;
2017-03-01 16:32:21 -08:00
2017-12-29 12:48:19 +05:30
mainWindow . on ( 'close' , function ( e ) {
2017-03-01 16:32:21 -08:00
if ( willQuitApp ) {
2017-04-18 09:02:25 -07:00
destroyAllWindows ( ) ;
2017-03-01 16:32:21 -08:00
return ;
}
2017-05-19 08:01:41 -07:00
if ( getMinimizeOnClose ( ) ) {
2017-03-01 16:32:21 -08:00
e . preventDefault ( ) ;
2017-05-19 08:01:41 -07:00
mainWindow . minimize ( ) ;
2018-06-13 18:09:32 +05:30
} else if ( isMac ) {
e . preventDefault ( ) ;
mainWindow . hide ( ) ;
2017-05-19 08:01:41 -07:00
} else {
2018-06-13 18:09:32 +05:30
app . quit ( ) ;
2017-03-01 16:32:21 -08:00
}
} ) ;
2017-04-18 09:02:25 -07:00
function destroyAllWindows ( ) {
2017-04-18 13:40:26 -07:00
let keys = Object . keys ( windows ) ;
2017-08-24 13:21:02 +05:30
for ( let i = 0 , len = keys . length ; i < len ; i ++ ) {
2017-04-18 09:02:25 -07:00
let winKey = keys [ i ] ;
removeWindowKey ( winKey ) ;
2017-03-03 16:07:48 -08:00
}
2017-04-18 09:02:25 -07:00
mainWindow = null ;
}
2017-03-01 16:32:21 -08:00
2017-04-18 09:02:25 -07:00
mainWindow . on ( 'closed' , destroyAllWindows ) ;
2018-07-19 18:45:16 +05:30
2017-06-27 20:38:58 +05:30
// Manage File Downloads
mainWindow . webContents . session . on ( 'will-download' , ( event , item , webContents ) => {
// When download is in progress, send necessary data to indicate the same
webContents . send ( 'downloadProgress' ) ;
2018-07-19 18:45:16 +05:30
2018-05-01 13:35:57 +05:30
// Send file path when download is complete
2017-07-02 18:23:34 +05:30
item . once ( 'done' , ( e , state ) => {
2017-06-27 20:38:58 +05:30
if ( state === 'completed' ) {
let data = {
_id : getGuid ( ) ,
savedPath : item . getSavePath ( ) ? item . getSavePath ( ) : '' ,
total : filesize ( item . getTotalBytes ( ) ? item . getTotalBytes ( ) : 0 ) ,
2018-05-01 13:35:57 +05:30
fileName : item . getFilename ( ) ? item . getFilename ( ) : 'No name'
2017-06-27 20:38:58 +05:30
} ;
webContents . send ( 'downloadCompleted' , data ) ;
}
} ) ;
} ) ;
2017-05-25 09:48:40 -07:00
// open external links in default browser - a tag with href='_blank' or window.open
2018-08-03 19:31:54 +05:30
const enforceInheritance = ( topWebContents ) => {
const handleNewWindow = ( webContents ) => {
webContents . on ( 'new-window' , ( event , newWinUrl , frameName , disposition , newWinOptions ) => {
2018-11-28 14:48:56 -08:00
log . send ( logLevels . INFO , ` Creating a pop-out window for the url ${ newWinUrl } with frame name ${ frameName } , disposition ${ disposition } and options ${ newWinOptions } ` ) ;
2018-08-03 19:31:54 +05:30
if ( ! newWinOptions . webPreferences ) {
// eslint-disable-next-line no-param-reassign
newWinOptions . webPreferences = { } ;
}
2017-04-20 11:54:11 -07:00
2018-08-03 19:31:54 +05:30
Object . assign ( newWinOptions . webPreferences , topWebContents ) ;
2017-04-18 09:02:25 -07:00
2018-08-03 19:31:54 +05:30
let newWinParsedUrl = getParsedUrl ( newWinUrl ) ;
let mainWinParsedUrl = getParsedUrl ( url ) ;
2018-02-23 17:01:16 +05:30
2018-08-03 19:31:54 +05:30
let newWinHost = newWinParsedUrl && newWinParsedUrl . host ;
let mainWinHost = mainWinParsedUrl && mainWinParsedUrl . host ;
2017-04-18 09:02:25 -07:00
2018-08-03 19:31:54 +05:30
let emptyUrlString = 'about:blank' ;
let dispositionWhitelist = [ 'new-window' , 'foreground-tab' ] ;
2017-04-18 13:40:26 -07:00
2019-03-04 19:04:35 +05:30
let fullMainUrl = ` ${ mainWinParsedUrl . protocol } // ${ mainWinParsedUrl . host } / ` ;
// If the main url and new window url are the same,
// we open that in a browser rather than a separate window
if ( newWinUrl === fullMainUrl ) {
event . preventDefault ( ) ;
openUrlInDefaultBrowser ( newWinUrl ) ;
return ;
}
// only allow window.open to succeed is if coming from same host,
2018-08-03 19:31:54 +05:30
// otherwise open in default browser.
2019-03-19 18:00:52 +05:30
if ( ( newWinHost === mainWinHost || newWinUrl === emptyUrlString || ( newWinHost . indexOf ( mainWinHost ) !== - 1 && frameName !== "" ) ) && dispositionWhitelist . includes ( disposition ) ) {
2018-08-03 19:31:54 +05:30
// handle: window.open
2017-05-31 21:39:08 -07:00
2018-08-03 19:31:54 +05:30
if ( ! frameName ) {
// abort - no frame name provided.
return ;
}
2017-04-20 11:54:11 -07:00
2018-08-03 19:31:54 +05:30
log . send ( logLevels . INFO , 'creating pop-out window url: ' + newWinParsedUrl ) ;
2017-04-20 11:54:11 -07:00
2018-08-03 19:31:54 +05:30
let x = 0 ;
let y = 0 ;
2017-04-20 11:54:11 -07:00
2018-08-03 19:31:54 +05:30
let width = newWinOptions . width || DEFAULT _WIDTH ;
let height = newWinOptions . height || DEFAULT _HEIGHT ;
2017-04-20 11:54:11 -07:00
2018-08-03 19:31:54 +05:30
// try getting x and y position from query parameters
let query = newWinParsedUrl && querystring . parse ( newWinParsedUrl . query ) ;
if ( query && query . x && query . y ) {
let newX = Number . parseInt ( query . x , 10 ) ;
let newY = Number . parseInt ( query . y , 10 ) ;
2017-04-18 09:02:25 -07:00
2018-08-03 19:31:54 +05:30
let newWinRect = { x : newX , y : newY , width , height } ;
2017-04-18 09:02:25 -07:00
2018-08-03 19:31:54 +05:30
// only accept if both are successfully parsed.
if ( Number . isInteger ( newX ) && Number . isInteger ( newY ) &&
isInDisplayBounds ( newWinRect ) ) {
x = newX ;
y = newY ;
} else {
x = 0 ;
y = 0 ;
}
} else {
// create new window at slight offset from main window.
( { x , y } = getWindowSizeAndPosition ( mainWindow ) ) ;
x += 50 ;
y += 50 ;
}
2017-04-18 09:02:25 -07:00
2018-08-03 19:31:54 +05:30
/* eslint-disable no-param-reassign */
newWinOptions . x = x ;
newWinOptions . y = y ;
newWinOptions . width = Math . max ( width , DEFAULT _WIDTH ) ;
newWinOptions . height = Math . max ( height , DEFAULT _HEIGHT ) ;
newWinOptions . minWidth = MIN _WIDTH ;
newWinOptions . minHeight = MIN _HEIGHT ;
newWinOptions . alwaysOnTop = alwaysOnTop ;
newWinOptions . frame = true ;
2019-01-15 11:06:19 -02:00
newWinOptions . parent = null ;
2019-02-12 09:57:03 +05:30
newWinOptions . webPreferences . backgroundThrottling = false ;
2017-04-18 13:40:26 -07:00
2018-08-03 19:31:54 +05:30
let newWinKey = getGuid ( ) ;
2017-04-18 09:02:25 -07:00
2018-08-03 19:31:54 +05:30
newWinOptions . winKey = newWinKey ;
/* eslint-enable no-param-reassign */
2018-07-09 13:33:19 +05:30
2018-08-03 19:31:54 +05:30
let childWebContents = newWinOptions . webContents ;
2017-04-18 09:02:25 -07:00
2018-08-03 19:31:54 +05:30
// Event needed to hide native menu bar
childWebContents . once ( 'did-start-loading' , ( ) => {
let browserWin = BrowserWindow . fromWebContents ( childWebContents ) ;
if ( isWindowsOS && browserWin && ! browserWin . isDestroyed ( ) ) {
browserWin . setMenuBarVisibility ( false ) ;
}
} ) ;
2017-05-31 21:39:08 -07:00
2018-08-03 19:31:54 +05:30
childWebContents . once ( 'did-finish-load' , function ( ) {
let browserWin = BrowserWindow . fromWebContents ( childWebContents ) ;
if ( browserWin ) {
2018-11-27 17:24:15 -08:00
log . send ( logLevels . INFO , ` loaded pop-out window url: ${ JSON . stringify ( newWinParsedUrl ) } ` ) ;
2018-08-03 19:31:54 +05:30
browserWin . webContents . send ( 'on-page-load' ) ;
// applies styles required for snack bar
browserWin . webContents . insertCSS ( fs . readFileSync ( path . join ( _ _dirname , '/snackBar/style.css' ) , 'utf8' ) . toString ( ) ) ;
initCrashReporterMain ( { process : 'pop-out window' } ) ;
initCrashReporterRenderer ( browserWin , { process : 'render | pop-out window' } ) ;
browserWin . winName = frameName ;
browserWin . setAlwaysOnTop ( alwaysOnTop ) ;
2018-11-27 17:24:15 -08:00
logBrowserWindowEvents ( browserWin , browserWin . winName ) ;
2018-08-03 19:31:54 +05:30
2019-01-11 11:37:59 +05:30
let handleChildWindowCrashEvent = ( e , killed ) => {
log . send ( logLevels . INFO , ` Child Window crashed! Killed? ${ killed } ` ) ;
if ( killed ) {
return ;
}
2018-08-03 19:31:54 +05:30
const options = {
type : 'error' ,
title : i18n . getMessageFor ( 'Renderer Process Crashed' ) ,
message : i18n . getMessageFor ( 'Oops! Looks like we have had a crash. Please reload or close this window.' ) ,
buttons : [ 'Reload' , 'Close' ]
} ;
let childBrowserWindow = BrowserWindow . fromWebContents ( e . sender ) ;
if ( childBrowserWindow && ! childBrowserWindow . isDestroyed ( ) ) {
electron . dialog . showMessageBox ( childBrowserWindow , options , function ( index ) {
if ( index === 0 ) {
childBrowserWindow . reload ( ) ;
} else {
childBrowserWindow . close ( ) ;
}
} ) ;
2018-04-11 13:13:59 +00:00
}
2018-08-03 19:31:54 +05:30
} ;
browserWin . webContents . on ( 'crashed' , handleChildWindowCrashEvent ) ;
2018-08-16 11:35:40 +05:30
browserWin . webContents . on ( 'will-navigate' , ( e , navigatedURL ) => {
if ( ! navigatedURL . startsWith ( 'http' || 'https' ) ) {
e . preventDefault ( ) ;
}
} ) ;
2018-08-03 19:31:54 +05:30
// In case we navigate to an external link from inside a pop-out,
// we open that link in an external browser rather than creating
// a new window
if ( browserWin . webContents ) {
handleNewWindow ( browserWin . webContents ) ;
}
addWindowKey ( newWinKey , browserWin ) ;
// Method that sends bound changes as soon
// as a new window is created
// issue https://perzoinc.atlassian.net/browse/ELECTRON-172
sendChildWinBoundsChange ( browserWin ) ;
// throttle full screen
let throttledFullScreen = throttle ( 1000 ,
handleChildWindowFullScreen . bind ( null , browserWin ) ) ;
// throttle leave full screen
let throttledLeaveFullScreen = throttle ( 1000 ,
handleChildWindowLeaveFullScreen . bind ( null , browserWin ) ) ;
// throttle changes so we don't flood client.
let throttledBoundsChange = throttle ( 1000 ,
sendChildWinBoundsChange . bind ( null , browserWin ) ) ;
browserWin . on ( 'move' , throttledBoundsChange ) ;
browserWin . on ( 'resize' , throttledBoundsChange ) ;
browserWin . on ( 'enter-full-screen' , throttledFullScreen ) ;
browserWin . on ( 'leave-full-screen' , throttledLeaveFullScreen ) ;
let handleChildWindowClosed = ( ) => {
removeWindowKey ( newWinKey ) ;
browserWin . removeListener ( 'move' , throttledBoundsChange ) ;
browserWin . removeListener ( 'resize' , throttledBoundsChange ) ;
browserWin . removeListener ( 'enter-full-screen' , throttledFullScreen ) ;
browserWin . removeListener ( 'leave-full-screen' , throttledLeaveFullScreen ) ;
} ;
browserWin . on ( 'close' , ( ) => {
browserWin . webContents . removeListener ( 'crashed' , handleChildWindowCrashEvent ) ;
2018-04-11 13:13:59 +00:00
} ) ;
2018-07-19 18:45:16 +05:30
2018-08-03 19:31:54 +05:30
browserWin . once ( 'closed' , ( ) => {
handleChildWindowClosed ( ) ;
} ) ;
2018-07-19 18:45:16 +05:30
2018-08-03 19:31:54 +05:30
handlePermissionRequests ( browserWin . webContents ) ;
2018-07-19 18:45:16 +05:30
2018-08-03 19:31:54 +05:30
if ( ! isDevEnv ) {
browserWin . webContents . session . setCertificateVerifyProc ( handleCertificateTransparencyChecks ) ;
}
}
2018-07-20 16:23:08 +05:30
} ) ;
2018-08-03 19:31:54 +05:30
} else {
event . preventDefault ( ) ;
openUrlInDefaultBrowser ( newWinUrl ) ;
2017-04-20 11:54:11 -07:00
}
} ) ;
2018-08-03 19:31:54 +05:30
} ;
handleNewWindow ( topWebContents ) ;
} ;
if ( ! isDevEnv ) {
mainWindow . webContents . session . setCertificateVerifyProc ( handleCertificateTransparencyChecks ) ;
}
// enforce main window's webPreferences to child windows
if ( mainWindow . webContents ) {
enforceInheritance ( mainWindow . webContents ) ;
2018-03-15 17:18:06 +05:30
}
2017-04-18 09:02:25 -07:00
2017-12-21 12:53:57 +05:30
// whenever the main window is navigated for ex: window.location.href or url redirect
2017-12-29 12:48:19 +05:30
mainWindow . webContents . on ( 'will-navigate' , function ( event , navigatedURL ) {
2018-08-16 11:35:40 +05:30
if ( ! navigatedURL . startsWith ( 'http' || 'https' ) ) {
event . preventDefault ( ) ;
return ;
}
2017-12-21 13:52:05 +05:30
isWhitelisted ( navigatedURL )
2017-12-21 12:53:57 +05:30
. catch ( ( ) => {
event . preventDefault ( ) ;
electron . dialog . showMessageBox ( mainWindow , {
type : 'warning' ,
2017-12-29 12:48:19 +05:30
buttons : [ 'Ok' ] ,
2018-06-19 20:26:04 +05:30
title : i18n . getMessageFor ( 'Not Allowed' ) ,
message : i18n . getMessageFor ( 'Sorry, you are not allowed to access this website' ) + ' (' + navigatedURL + '), ' + i18n . getMessageFor ( 'please contact your administrator for more details' ) ,
2017-12-21 12:53:57 +05:30
} ) ;
} ) ;
2017-12-12 13:00:32 +05:30
} ) ;
2018-07-19 18:45:16 +05:30
2018-03-29 12:43:54 +05:30
/ * *
* Register shortcuts for the app
* /
function registerShortcuts ( ) {
2018-07-09 13:24:35 +05:30
function devTools ( ) {
const focusedWindow = BrowserWindow . getFocusedWindow ( ) ;
2018-12-04 12:13:19 -08:00
devToolsEnabled = readConfigFromFile ( 'devToolsEnabled' ) ;
2018-03-29 12:43:54 +05:30
if ( focusedWindow && ! focusedWindow . isDestroyed ( ) ) {
2018-09-06 07:56:48 -03:00
if ( devToolsEnabled ) {
focusedWindow . webContents . toggleDevTools ( ) ;
} else {
log . send ( logLevels . INFO , ` dev tools disabled for ${ focusedWindow . winName } window ` ) ;
focusedWindow . webContents . closeDevTools ( ) ;
electron . dialog . showMessageBox ( focusedWindow , {
type : 'warning' ,
buttons : [ 'Ok' ] ,
title : i18n . getMessageFor ( 'Dev Tools disabled' ) ,
message : i18n . getMessageFor ( 'Dev Tools has been disabled! Please contact your system administrator to enable it!' ) ,
} ) ;
}
2018-03-29 12:43:54 +05:30
}
2018-07-09 13:24:35 +05:30
}
2018-07-20 12:20:34 +05:30
// This will initially register the global shortcut
globalShortcut . register ( isMac ? 'Cmd+Alt+I' : 'Ctrl+Shift+I' , devTools ) ;
2018-07-09 13:24:35 +05:30
app . on ( 'browser-window-focus' , function ( ) {
globalShortcut . register ( isMac ? 'Cmd+Alt+I' : 'Ctrl+Shift+I' , devTools ) ;
2018-03-29 12:43:54 +05:30
} ) ;
2018-07-09 13:24:35 +05:30
app . on ( 'browser-window-blur' , function ( ) {
globalShortcut . unregister ( isMac ? 'Cmd+Alt+I' : 'Ctrl+Shift+I' ) ;
} ) ;
2018-03-29 12:43:54 +05:30
}
2018-07-19 18:45:16 +05:30
2018-03-29 12:43:54 +05:30
/ * *
* Sets permission requests for the window
* @ param webContents Web contents of the window
* /
2018-03-20 17:49:27 +05:30
function handlePermissionRequests ( webContents ) {
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
let session = webContents . session ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
getConfigField ( 'permissions' )
. then ( ( permissions ) => {
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
if ( ! permissions ) {
log . send ( logLevels . ERROR , 'permissions configuration is invalid, so, everything will be true by default!' ) ;
return ;
}
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
session . setPermissionRequestHandler ( ( sessionWebContents , permission , callback ) => {
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
function handleSessionPermissions ( userPermission , message , cb ) {
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
log . send ( logLevels . INFO , 'permission is -> ' + userPermission ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
if ( ! userPermission ) {
2018-07-17 10:42:33 +05:30
const fullMessage = ` ${ i18n . getMessageFor ( 'Your administrator has disabled' ) } ${ message } . ${ i18n . getMessageFor ( 'Please contact your admin for help' ) } ` ;
2018-05-21 17:03:46 +05:30
const browserWindow = BrowserWindow . getFocusedWindow ( ) ;
if ( browserWindow && ! browserWindow . isDestroyed ( ) ) {
2018-08-21 15:36:26 +05:30
electron . dialog . showMessageBox ( browserWindow , { type : 'error' , title : ` ${ i18n . getMessageFor ( 'Permission Denied' ) } ! ` , message : fullMessage } ) ;
2018-05-21 17:03:46 +05:30
}
2018-03-20 17:49:27 +05:30
}
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
return cb ( userPermission ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
}
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
let PERMISSION _MEDIA = 'media' ;
let PERMISSION _LOCATION = 'geolocation' ;
let PERMISSION _NOTIFICATIONS = 'notifications' ;
let PERMISSION _MIDI _SYSEX = 'midiSysex' ;
let PERMISSION _POINTER _LOCK = 'pointerLock' ;
let PERMISSION _FULL _SCREEN = 'fullscreen' ;
let PERMISSION _OPEN _EXTERNAL = 'openExternal' ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
switch ( permission ) {
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
case PERMISSION _MEDIA :
return handleSessionPermissions ( permissions . media , 'sharing your camera, microphone, and speakers' , callback ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
case PERMISSION _LOCATION :
return handleSessionPermissions ( permissions . geolocation , 'sharing your location' , callback ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
case PERMISSION _NOTIFICATIONS :
return handleSessionPermissions ( permissions . notifications , 'notifications' , callback ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
case PERMISSION _MIDI _SYSEX :
return handleSessionPermissions ( permissions . midiSysex , 'MIDI Sysex' , callback ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
case PERMISSION _POINTER _LOCK :
return handleSessionPermissions ( permissions . pointerLock , 'Pointer Lock' , callback ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
case PERMISSION _FULL _SCREEN :
return handleSessionPermissions ( permissions . fullscreen , 'Full Screen' , callback ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
case PERMISSION _OPEN _EXTERNAL :
return handleSessionPermissions ( permissions . openExternal , 'Opening External App' , callback ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
default :
return callback ( false ) ;
}
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
} ) ;
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
} ) . catch ( ( error ) => {
log . send ( logLevels . ERROR , 'unable to get permissions configuration, so, everything will be true by default! ' + error ) ;
} )
2018-05-21 17:03:46 +05:30
2018-03-20 17:49:27 +05:30
}
2018-07-19 18:45:16 +05:30
2018-05-21 17:04:24 +05:30
function handleCertificateTransparencyChecks ( request , callback ) {
2018-07-19 18:45:16 +05:30
2018-05-21 17:04:24 +05:30
const { hostname : hostUrl , errorCode } = request ;
2018-07-19 18:45:16 +05:30
2018-05-21 17:04:24 +05:30
if ( errorCode === 0 ) {
return callback ( 0 ) ;
}
2018-07-19 18:45:16 +05:30
2018-05-21 17:04:24 +05:30
let { tld , domain } = parseDomain ( hostUrl ) ;
let host = domain + tld ;
2018-07-19 18:45:16 +05:30
2018-05-21 17:04:24 +05:30
if ( ctWhitelist && Array . isArray ( ctWhitelist ) && ctWhitelist . length > 0 && ctWhitelist . indexOf ( host ) > - 1 ) {
return callback ( 0 ) ;
}
2018-07-19 18:45:16 +05:30
2019-01-29 09:18:49 +05:30
return callback ( - 2 ) ;
2018-05-21 17:04:24 +05:30
}
2017-12-12 13:00:32 +05:30
2017-03-01 16:32:21 -08:00
}
2019-01-15 11:06:19 -02:00
/ * *
* ELECTRON - 956 : App is not minimized upon "Configure Desktop Alert Position" modal when "Always on Top" = True
* /
app . on ( 'browser-window-created' , ( event , window ) => {
const parentWindow = window . getParentWindow ( ) ;
if ( parentWindow && ! parentWindow . isDestroyed ( ) ) {
if ( parentWindow . winName === 'main' ) {
window . setMinimizable ( false ) ;
window . setMaximizable ( false ) ;
}
}
} ) ;
2017-08-24 16:18:32 +05:30
/ * *
* Handles the event before - quit emitted by electron
* /
2017-05-09 00:00:45 +05:30
app . on ( 'before-quit' , function ( ) {
2017-03-01 16:32:21 -08:00
willQuitApp = true ;
} ) ;
2017-08-24 16:18:32 +05:30
/ * *
* Saves the main window bounds
* /
2017-04-20 11:54:11 -07:00
function saveMainWinBounds ( ) {
let newBounds = getWindowSizeAndPosition ( mainWindow ) ;
2018-05-22 14:29:28 +05:30
// set application full-screen and maximized state
if ( mainWindow && ! mainWindow . isDestroyed ( ) ) {
newBounds . isMaximized = mainWindow . isMaximized ( ) ;
newBounds . isFullScreen = mainWindow . isFullScreen ( ) ;
2018-12-19 02:13:58 -02:00
bringToFrontNotification ( ) ;
2018-05-22 14:29:28 +05:30
}
2017-04-20 11:54:11 -07:00
if ( newBounds ) {
updateConfigField ( 'mainWinPos' , newBounds ) ;
}
}
2017-08-24 16:18:32 +05:30
/ * *
* Gets the main window
* @ returns { * }
* /
2017-03-06 21:09:10 -08:00
function getMainWindow ( ) {
return mainWindow ;
}
2018-03-09 16:16:47 +05:30
/ * *
* Gets the application menu
* @ returns { * }
* /
function getMenu ( ) {
return menu ;
}
2017-08-24 16:18:32 +05:30
/ * *
* Gets a window ' s size and position
* @ param window
* @ returns { * }
* /
2017-04-20 11:54:11 -07:00
function getWindowSizeAndPosition ( window ) {
2019-01-29 22:59:44 +05:30
if ( window && ! window . isDestroyed ( ) ) {
2017-05-25 09:48:40 -07:00
let newPos = window . getPosition ( ) ;
let newSize = window . getSize ( ) ;
if ( newPos && newPos . length === 2 &&
newSize && newSize . length === 2 ) {
return {
x : newPos [ 0 ] ,
y : newPos [ 1 ] ,
width : newSize [ 0 ] ,
height : newSize [ 1 ] ,
} ;
}
2017-04-20 11:54:11 -07:00
}
return null ;
}
2017-08-24 16:18:32 +05:30
/ * *
* Shows the main window
* /
2017-03-01 16:32:21 -08:00
function showMainWindow ( ) {
2018-06-12 15:17:02 +05:30
if ( mainWindow ) {
mainWindow . show ( ) ;
}
2017-03-01 16:32:21 -08:00
}
2017-08-24 16:18:32 +05:30
/ * *
* Tells if a window is the main window
* @ param win
* @ returns { boolean }
* /
2017-03-01 16:32:21 -08:00
function isMainWindow ( win ) {
return mainWindow === win ;
}
2017-08-24 16:18:32 +05:30
/ * *
* Checks if the window and a key has a window
* @ param win
* @ param winKey
* @ returns { * }
* /
2017-03-01 16:32:21 -08:00
function hasWindow ( win , winKey ) {
2017-05-19 08:01:41 -07:00
if ( win instanceof BrowserWindow ) {
2017-05-09 00:00:45 +05:30
let browserWin = windows [ winKey ] ;
2017-03-01 16:32:21 -08:00
return browserWin && win === browserWin ;
}
return false ;
}
2017-08-24 16:18:32 +05:30
/ * *
* Sets if a user is online
* @ param status
* /
2017-03-01 16:32:21 -08:00
function setIsOnline ( status ) {
isOnline = status ;
}
2018-08-28 11:47:09 +05:30
/ * *
* Returns user ' s online status
* @ return { boolean }
* /
function getIsOnline ( ) {
return isOnline ;
}
2017-04-20 11:54:11 -07:00
/ * *
2017-10-08 19:32:44 +05:30
* Tries finding a window we have created with given name . If found , then
2017-04-20 11:54:11 -07:00
* brings to front and gives focus .
2018-03-16 12:26:07 +05:30
* @ param { String } windowName Name of target window . Note : main window has
2017-04-20 11:54:11 -07:00
* name 'main' .
2018-03-16 12:26:07 +05:30
* @ param { Boolean } shouldFocus whether to get window to focus or just show
* without giving focus
2017-04-20 11:54:11 -07:00
* /
2018-03-16 12:26:07 +05:30
function activate ( windowName , shouldFocus = true ) {
2018-04-11 13:06:46 +00:00
// don't activate when the app is reloaded programmatically
// Electron-136
if ( isAutoReload ) {
return null ;
}
2019-01-09 13:01:21 +05:30
for ( const key in windows ) {
if ( Object . prototype . hasOwnProperty . call ( windows , key ) ) {
const window = windows [ key ] ;
if ( window && ! window . isDestroyed ( ) && window . winName === windowName ) {
// Bring the window to the top without focusing
// Flash task bar icon in Windows for windows
if ( ! shouldFocus ) {
return isMac ? window . showInactive ( ) : window . flashFrame ( true ) ;
}
2018-03-16 12:26:07 +05:30
2019-01-09 13:01:21 +05:30
// Note: On window just focusing will preserve window snapped state
// Hiding the window and just calling the focus() won't display the window
if ( isWindowsOS ) {
return window . isMinimized ( ) ? window . restore ( ) : window . focus ( ) ;
}
2018-03-16 12:26:07 +05:30
2019-01-09 13:01:21 +05:30
return window . isMinimized ( ) ? window . restore ( ) : window . show ( ) ;
2017-05-08 10:44:00 -07:00
}
2017-04-18 09:02:25 -07:00
}
}
2018-03-16 12:26:07 +05:30
return null ;
2017-04-18 09:02:25 -07:00
}
2018-04-11 13:06:46 +00:00
/ * *
* Sets if is auto reloading the app
* @ param reload
* /
function setIsAutoReload ( reload ) {
if ( typeof reload === 'boolean' ) {
isAutoReload = reload
}
}
2017-04-20 11:54:11 -07:00
/ * *
* name of renderer window to notify when bounds of child window changes .
* @ param { object } window Renderer window to use IPC with to inform about size /
* position change .
* /
function setBoundsChangeWindow ( window ) {
boundsChangeWindow = window ;
}
/ * *
* Called when bounds of child window changes size / position
* @ param { object } window Child window which has changed size / position .
* /
function sendChildWinBoundsChange ( window ) {
let newBounds = getWindowSizeAndPosition ( window ) ;
if ( newBounds && boundsChangeWindow ) {
newBounds . windowName = window . winName ;
2017-04-26 09:46:15 -07:00
// ipc msg back to renderer to inform bounds has changed.
2017-04-20 11:54:11 -07:00
boundsChangeWindow . send ( 'boundsChange' , newBounds ) ;
}
}
2018-07-09 13:24:35 +05:30
/ * *
* Called when the child window is set to full screen
* /
function handleChildWindowFullScreen ( browserWindow ) {
const snackBarContent = i18n . getMessageFor ( 'SnackBar' ) ;
browserWindow . webContents . send ( 'window-enter-full-screen' , { snackBar : snackBarContent } ) ;
}
/ * *
* Called when the child window left full screen
* /
function handleChildWindowLeaveFullScreen ( browserWindow ) {
browserWindow . webContents . send ( 'window-leave-full-screen' ) ;
}
2017-08-24 16:18:32 +05:30
/ * *
* Opens an external url in the system ' s default browser
* @ param urlToOpen
* /
2017-10-06 16:07:13 +05:30
function openUrlInDefaultBrowser ( urlToOpen ) {
2017-12-29 12:48:19 +05:30
if ( urlToOpen ) {
2017-05-25 09:48:40 -07:00
electron . shell . openExternal ( urlToOpen ) ;
}
}
2017-06-15 22:41:29 +05:30
/ * *
* Called when an event is received from menu
2018-05-09 14:49:18 +05:30
* @ param { boolean } boolean whether to enable or disable alwaysOnTop .
* @ param { boolean } shouldActivateMainWindow whether to activate main window
2017-06-15 22:41:29 +05:30
* /
2018-05-09 14:49:18 +05:30
function isAlwaysOnTop ( boolean , shouldActivateMainWindow = true ) {
2017-06-15 22:41:29 +05:30
alwaysOnTop = boolean ;
let browserWins = BrowserWindow . getAllWindows ( ) ;
if ( browserWins . length > 0 ) {
2018-05-07 14:52:02 +05:30
browserWins
. filter ( ( browser ) => typeof browser . notfyObj !== 'object' )
. forEach ( function ( browser ) {
browser . setAlwaysOnTop ( boolean ) ;
} ) ;
2017-11-21 11:40:04 +05:30
// An issue where changing the alwaysOnTop property
// focus the pop-out window
2018-05-09 14:49:18 +05:30
// Issue - Electron-209/470
if ( mainWindow && mainWindow . winName && shouldActivateMainWindow ) {
2017-11-21 11:40:04 +05:30
activate ( mainWindow . winName ) ;
}
2017-06-15 22:41:29 +05:30
}
}
// node event emitter to update always on top
2019-01-11 11:37:59 +05:30
eventEmitter . on ( 'isAlwaysOnTop' , ( params ) => {
2018-05-09 14:49:18 +05:30
isAlwaysOnTop ( params . isAlwaysOnTop , params . shouldActivateMainWindow ) ;
2018-11-28 14:48:56 -08:00
log . send ( logLevels . INFO , ` Updating settings for always on top ${ params } ` ) ;
2017-06-15 22:41:29 +05:30
} ) ;
2017-06-17 03:59:56 +05:30
// node event emitter for notification settings
eventEmitter . on ( 'notificationSettings' , ( notificationSettings ) => {
position = notificationSettings . position ;
display = notificationSettings . display ;
} ) ;
2018-08-06 15:31:02 +05:30
/ * *
* Sets the locale settings
*
* @ param browserWindow { Electron . BrowserWindow }
* @ param opts { Object }
* @ param opts . language { String } - locale string ex : en - US
* /
function setLocale ( browserWindow , opts ) {
2018-06-25 15:13:38 +05:30
const language = opts && opts . language || app . getLocale ( ) ;
2018-08-22 17:34:16 +05:30
const localeContent = { } ;
2018-06-25 15:13:38 +05:30
log . send ( logLevels . INFO , ` language changed to ${ language } . Updating menu and user config ` ) ;
2018-06-19 20:26:04 +05:30
setLanguage ( language ) ;
2018-08-22 17:34:16 +05:30
if ( browserWindow && ! browserWindow . isDestroyed ( ) ) {
if ( isMainWindow ( browserWindow ) ) {
2018-08-06 15:31:02 +05:30
2018-08-22 17:34:16 +05:30
menu = electron . Menu . buildFromTemplate ( getTemplate ( app ) ) ;
electron . Menu . setApplicationMenu ( menu ) ;
2018-08-16 11:34:35 +05:30
2018-09-03 16:25:46 +05:30
if ( isWindowsOS ) {
2018-08-22 17:34:16 +05:30
browserWindow . setMenuBarVisibility ( false ) ;
// update locale for custom title bar
if ( isCustomTitleBarEnabled ) {
localeContent . titleBar = i18n . getMessageFor ( 'TitleBar' ) ;
}
2018-08-16 11:34:35 +05:30
}
2018-08-06 15:31:02 +05:30
}
2018-08-22 17:34:16 +05:30
localeContent . contextMenu = i18n . getMessageFor ( 'ContextMenu' ) ;
2018-08-27 20:28:21 +05:30
localeContent . downloadManager = i18n . getMessageFor ( 'DownloadManager' ) ;
if ( isMac ) {
localeContent . downloadManager [ 'Show in Folder' ] = localeContent . downloadManager [ 'Reveal in Finder' ] ;
}
2018-08-22 17:34:16 +05:30
browserWindow . webContents . send ( 'locale-changed' , localeContent ) ;
2018-08-06 15:31:02 +05:30
}
updateConfigField ( 'locale' , language ) ;
2018-06-19 20:26:04 +05:30
}
2018-08-06 15:31:02 +05:30
/ * *
* Sets language for i18n
* @ param language { String } - locale string ex : en - US
* /
2018-06-25 15:33:11 +05:30
function setLanguage ( language ) {
i18n . setLanguage ( language ) ;
2018-06-19 20:26:04 +05:30
}
2017-08-08 15:20:03 +05:30
/ * *
* Method that gets invoked when an external display
* is removed using electron 'display-removed' event .
* /
function verifyDisplays ( ) {
2018-11-28 14:48:56 -08:00
log . send ( logLevels . INFO , ` Display removed ` ) ;
2017-08-08 15:20:03 +05:30
// This is only for Windows, macOS handles this by itself
2017-12-29 12:48:19 +05:30
if ( ! mainWindow || isMac ) {
2017-08-08 15:20:03 +05:30
return ;
}
const bounds = mainWindow . getBounds ( ) ;
if ( bounds ) {
let isXAxisValid = true ;
let isYAxisValid = true ;
// checks to make sure the x,y are valid pairs
2017-12-29 12:48:19 +05:30
if ( ( bounds . x === undefined && ( bounds . y || bounds . y === 0 ) ) ) {
2017-08-08 15:20:03 +05:30
isXAxisValid = false ;
}
2017-12-29 12:48:19 +05:30
if ( ( bounds . y === undefined && ( bounds . x || bounds . x === 0 ) ) ) {
2017-08-08 15:20:03 +05:30
isYAxisValid = false ;
}
2017-12-29 12:48:19 +05:30
if ( ! isXAxisValid && ! isYAxisValid ) {
2017-08-08 15:20:03 +05:30
return ;
}
let externalDisplay = checkExternalDisplay ( bounds ) ;
// If external window doesn't exists, reposition main window
if ( ! externalDisplay ) {
repositionMainWindow ( ) ;
}
}
}
/ * *
* Method that verifies if wrapper exists in any of the available
* external display by comparing the app bounds with the display bounds
* if not exists returns false otherwise true
* @ param appBounds { Electron . Rectangle } - current electron wrapper bounds
* @ returns { boolean }
* /
function checkExternalDisplay ( appBounds ) {
const x = appBounds . x ;
const y = appBounds . y ;
const width = appBounds . width ;
const height = appBounds . height ;
const factor = 0.2 ;
2018-07-19 18:45:16 +05:30
2017-08-08 15:20:03 +05:30
// Loops through all the available displays and
// verifies if the wrapper exists within the display bounds
// returns false if not exists otherwise true
2018-03-20 17:49:27 +05:30
return ! ! electron . screen . getAllDisplays ( ) . find ( ( { bounds } ) => {
2017-08-08 15:20:03 +05:30
const leftMost = x + ( width * factor ) ;
const topMost = y + ( height * factor ) ;
const rightMost = x + width - ( width * factor ) ;
const bottomMost = y + height - ( height * factor ) ;
if ( leftMost < bounds . x || topMost < bounds . y ) {
return false ;
}
2017-08-24 13:21:02 +05:30
return ! ( rightMost > bounds . x + bounds . width || bottomMost > bounds . y + bounds . height ) ;
2017-08-08 15:20:03 +05:30
} ) ;
}
/ * *
* Method that resets the main window bounds when an external display
* was removed and if the wrapper was contained within that bounds
* /
function repositionMainWindow ( ) {
2018-07-19 18:45:16 +05:30
2018-03-20 17:49:27 +05:30
const { workArea } = electron . screen . getPrimaryDisplay ( ) ;
2017-08-08 15:20:03 +05:30
const bounds = workArea ;
if ( ! bounds ) {
return ;
}
const windowWidth = Math . round ( bounds . width * 0.6 ) ;
const windowHeight = Math . round ( bounds . height * 0.8 ) ;
// Calculating the center of the primary display
// to place the wrapper
const centerX = bounds . x + bounds . width / 2.0 ;
const centerY = bounds . y + bounds . height / 2.0 ;
const x = Math . round ( centerX - ( windowWidth / 2.0 ) ) ;
const y = Math . round ( centerY - ( windowHeight / 2.0 ) ) ;
2017-12-29 12:48:19 +05:30
let rectangle = { x , y , width : windowWidth , height : windowHeight } ;
2017-08-08 15:20:03 +05:30
// resetting the main window bounds
2017-12-29 12:48:19 +05:30
if ( mainWindow ) {
2017-08-08 15:20:03 +05:30
if ( ! mainWindow . isVisible ( ) ) {
mainWindow . show ( ) ;
}
if ( mainWindow . isMinimized ( ) ) {
mainWindow . restore ( ) ;
}
mainWindow . focus ( ) ;
mainWindow . flashFrame ( false ) ;
mainWindow . setBounds ( rectangle , true ) ;
}
}
2018-07-09 13:24:35 +05:30
/ * *
* Method that handles key press
* @ param keyCode { number }
* /
function handleKeyPress ( keyCode ) {
switch ( keyCode ) {
case KeyCodes . Esc : {
const focusedWindow = BrowserWindow . getFocusedWindow ( ) ;
if ( focusedWindow && ! focusedWindow . isDestroyed ( ) && focusedWindow . isFullScreen ( ) ) {
focusedWindow . setFullScreen ( false ) ;
}
break ;
}
2018-08-06 15:31:02 +05:30
case KeyCodes . Alt :
2018-09-03 16:25:46 +05:30
if ( isWindowsOS && ! isCustomTitleBarEnabled ) {
2018-08-06 15:31:02 +05:30
popupMenu ( ) ;
}
break ;
2018-07-09 13:24:35 +05:30
default :
break ;
}
}
2018-08-01 14:51:29 +05:30
/ * *
* Finds all the child window and closes it
* /
function cleanUpChildWindows ( ) {
const browserWindows = BrowserWindow . getAllWindows ( ) ;
notify . resetAnimationQueue ( ) ;
if ( browserWindows && browserWindows . length ) {
browserWindows . forEach ( browserWindow => {
// Closes only child windows
if ( browserWindow && ! browserWindow . isDestroyed ( ) && browserWindow . winName !== 'main' ) {
// clean up notification windows
if ( browserWindow . winName === 'notification-window' ) {
notify . closeAll ( ) ;
} else {
browserWindow . close ( ) ;
}
}
} ) ;
}
}
2018-08-06 15:31:02 +05:30
/ * *
* Method that popup the menu on top of the native title bar
* whenever Alt key is pressed
* /
function popupMenu ( ) {
const focusedWindow = BrowserWindow . getFocusedWindow ( ) ;
if ( mainWindow && ! mainWindow . isDestroyed ( ) && isMainWindow ( focusedWindow ) ) {
2018-10-29 14:25:45 +05:30
const { x , y } = mainWindow . isFullScreen ( ) ? { x : 0 , y : 0 } : { x : 10 , y : - 20 } ;
const popupOpts = { browserWin : mainWindow , x , y } ;
2018-08-06 15:31:02 +05:30
getMenu ( ) . popup ( popupOpts ) ;
}
}
2018-11-27 17:24:15 -08:00
const logBrowserWindowEvents = ( browserWindow , windowName ) => {
const events = [
2018-12-24 19:14:12 +05:30
'page-title-updated' , 'close' , 'session-end' , 'unresponsive' , 'responsive' , 'blur' , 'focus' ,
2018-11-27 17:24:15 -08:00
'show' , 'hide' , 'ready-to-show' , 'maximize' , 'unmaximize' , 'minimize' , 'restore' , 'resize' , 'move' , 'moved' ,
'enter-full-screen' , 'leave-full-screen' , 'enter-html-full-screen' , 'leave-html-full-screen' , 'app-command'
] ;
events . forEach ( ( browserWindowEvent ) => {
browserWindow . on ( browserWindowEvent , ( ) => {
log . send ( logLevels . INFO , ` Browser Window Event Occurred for window ( ${ windowName } ) -> ${ browserWindowEvent } ` ) ;
} ) ;
} ) ;
} ;
2018-07-09 13:24:35 +05:30
2019-03-19 19:00:40 +05:30
/ * *
* Validates the network by fetching the pod url
* every 5 sec , on active reloads the given window
*
* @ param window
* @ param url
* /
const isSymphonyReachable = ( window , url ) => {
if ( networkStatusCheckIntervalId ) {
return ;
}
networkStatusCheckIntervalId = setInterval ( ( ) => {
fetch ( config . url ) . then ( ( rsp ) => {
if ( rsp . status === 200 && isOnline ) {
window . loadURL ( url ) ;
if ( networkStatusCheckIntervalId ) {
clearInterval ( networkStatusCheckIntervalId ) ;
networkStatusCheckIntervalId = null ;
}
} else {
log . send ( logLevels . INFO , ` Symphony down! statusCode: ${ rsp . status } is online: ${ isOnline } ` ) ;
}
} ) . catch ( ( error ) => {
log . send ( logLevels . INFO , ` Network status check: No active network connection ${ error } ` ) ;
} ) ;
} , networkStatusCheckInterval ) ;
} ;
/ * *
* Clears the network status check interval
* /
const cancelNetworkStatusCheck = ( ) => {
if ( networkStatusCheckIntervalId ) {
clearInterval ( networkStatusCheckIntervalId ) ;
networkStatusCheckIntervalId = null ;
}
} ;
2017-03-01 16:32:21 -08:00
module . exports = {
createMainWindow : createMainWindow ,
2017-03-06 21:09:10 -08:00
getMainWindow : getMainWindow ,
2017-03-01 16:32:21 -08:00
showMainWindow : showMainWindow ,
isMainWindow : isMainWindow ,
hasWindow : hasWindow ,
2017-04-18 09:02:25 -07:00
setIsOnline : setIsOnline ,
2017-04-20 11:54:11 -07:00
activate : activate ,
2017-08-08 15:20:03 +05:30
setBoundsChangeWindow : setBoundsChangeWindow ,
2018-03-09 16:16:47 +05:30
verifyDisplays : verifyDisplays ,
2018-04-11 13:06:46 +00:00
getMenu : getMenu ,
2018-07-09 13:24:35 +05:30
setIsAutoReload : setIsAutoReload ,
2018-08-01 14:51:29 +05:30
handleKeyPress : handleKeyPress ,
cleanUpChildWindows : cleanUpChildWindows ,
2018-08-06 15:31:02 +05:30
setLocale : setLocale ,
2018-08-28 11:47:09 +05:30
getIsOnline : getIsOnline ,
2019-01-14 17:28:20 +05:30
getSpellchecker : getSpellchecker ,
isMisspelled : isMisspelled ,
2019-03-19 19:00:40 +05:30
cancelNetworkStatusCheck : cancelNetworkStatusCheck ,
2017-03-01 16:32:21 -08:00
} ;