From a074126537ff88d303849e5487260aeb6969cb54 Mon Sep 17 00:00:00 2001 From: Vishwas Shashidhar Date: Tue, 19 Mar 2019 18:00:52 +0530 Subject: [PATCH 1/2] ELECTRON-1138: support opening email composition in new window (#602) * bump up version number * ELECTRON-1138: support opening email composition in new window Currently, due to security reasons, we don't open any url which is not the same as the main window url in a new window, instead, we open it in a new tab of the system default browser. However, this impacts the email app as we'll need new window to be opened to compose an email. This commit fixes that issue by checking if we need to open a sub-domain of the pod url domain. --- js/windowMgr.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/windowMgr.js b/js/windowMgr.js index dd78dcc9..59d71970 100644 --- a/js/windowMgr.js +++ b/js/windowMgr.js @@ -466,7 +466,7 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { // only allow window.open to succeed is if coming from same host, // otherwise open in default browser. - if ((newWinHost === mainWinHost || newWinUrl === emptyUrlString) && dispositionWhitelist.includes(disposition)) { + if ((newWinHost === mainWinHost || newWinUrl === emptyUrlString || (newWinHost.indexOf(mainWinHost) !== -1 && frameName !== "")) && dispositionWhitelist.includes(disposition)) { // handle: window.open if (!frameName) { diff --git a/package.json b/package.json index acd7a6d3..21d74d37 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Symphony", "productName": "Symphony", - "version": "4.4.2", + "version": "4.5.0", "clientVersion": "1.54", "buildNumber": "0", "description": "Symphony desktop app (Foundation ODP)", From c3bbadb955e1afe3d11e17a56bd6068e0513a383 Mon Sep 17 00:00:00 2001 From: Kiran Niranjan Date: Tue, 19 Mar 2019 19:00:40 +0530 Subject: [PATCH 2/2] ELECTRON-967 (Handle network state and implement retry logic) (#600) * ELECTRON-967 - Display Error content when there is no active network connection * ELECTRON-967 - Fix css issue for Windows * ELECTRON-967 - Optimize code --- js/enums/api.js | 4 +- js/mainApiMgr.js | 7 ++ js/networkError/contents.js | 126 ++++++++++++++++++++++++++++++++++++ js/networkError/index.js | 44 +++++++++++++ js/networkError/style.css | 71 ++++++++++++++++++++ js/preload/preloadMain.js | 9 +++ js/windowMgr.js | 102 ++++++++++++++++++++++------- locale/en-US.json | 6 ++ locale/en.json | 6 ++ locale/fr-FR.json | 6 ++ locale/fr.json | 6 ++ locale/ja-JP.json | 6 ++ locale/ja.json | 6 ++ package.json | 1 + 14 files changed, 374 insertions(+), 26 deletions(-) create mode 100644 js/networkError/contents.js create mode 100644 js/networkError/index.js create mode 100644 js/networkError/style.css diff --git a/js/enums/api.js b/js/enums/api.js index 1ecc121c..0376da2f 100644 --- a/js/enums/api.js +++ b/js/enums/api.js @@ -25,7 +25,9 @@ const cmds = keyMirror({ setIsInMeeting: null, setLocale: null, keyPress: null, - openScreenSharingIndicator: null + openScreenSharingIndicator: null, + cancelNetworkStatusCheck: null, + quitWindow: null, }); module.exports = { diff --git a/js/mainApiMgr.js b/js/mainApiMgr.js index 72544cd3..07f666fa 100644 --- a/js/mainApiMgr.js +++ b/js/mainApiMgr.js @@ -5,6 +5,7 @@ * from the renderer process. */ const electron = require('electron'); +const app = electron.app; const windowMgr = require('./windowMgr.js'); const log = require('./log.js'); @@ -190,6 +191,12 @@ electron.ipcMain.on(apiName, (event, arg) => { openScreenSharingIndicator(event.sender, arg.displayId, arg.id); } break; + case apiCmds.cancelNetworkStatusCheck: + windowMgr.cancelNetworkStatusCheck(); + break; + case apiCmds.quitWindow: + app.quit(); + break; default: } diff --git a/js/networkError/contents.js b/js/networkError/contents.js new file mode 100644 index 00000000..d285b28f --- /dev/null +++ b/js/networkError/contents.js @@ -0,0 +1,126 @@ +const errorContent = (data) => { + return (`
+
+
+ + + + Symphony_logo + + + + +
+
+

${data["Problem connecting to Symphony"] || "Problem connecting to Symphony"}

+

+ ${data["Looks like you are not connected to the Internet. We'll try to reconnect automatically."] + || "Looks like you are not connected to the Internet. We'll try to reconnect automatically."} +

+
ERR_INTERNET_DISCONNECTED
+ + +
+
+
`); +}; + + +module.exports = { + errorContent, +}; \ No newline at end of file diff --git a/js/networkError/index.js b/js/networkError/index.js new file mode 100644 index 00000000..e51b6163 --- /dev/null +++ b/js/networkError/index.js @@ -0,0 +1,44 @@ +const { ipcRenderer } = require('electron'); + +const apiEnums = require('../enums/api.js'); +const apiCmds = apiEnums.cmds; +const apiName = apiEnums.apiName; +const htmlContents = require('./contents'); + +class NetworkError { + + constructor() { + this.domParser = new DOMParser(); + } + + showError(data) { + if (!data) { + return; + } + const { message, error } = data; + const errorContent = this.domParser.parseFromString(htmlContents.errorContent(message), 'text/html'); + errorContent.getElementById('error-code').innerText = error || "UNKNOWN_ERROR"; + + // Add event listeners for buttons + const cancelRetryButton = errorContent.getElementById('cancel-retry-button'); + cancelRetryButton.addEventListener('click', () => { + ipcRenderer.send(apiName, { + cmd: apiCmds.cancelNetworkStatusCheck + }); + }); + + const quitButton = errorContent.getElementById('quit-button'); + quitButton.addEventListener('click', () => { + ipcRenderer.send(apiName, { + cmd: apiCmds.quitWindow + }) + }); + + const mainFrame = errorContent.getElementById('main-frame'); + document.body.appendChild(mainFrame); + } +} + +module.exports = { + NetworkError, +}; \ No newline at end of file diff --git a/js/networkError/style.css b/js/networkError/style.css new file mode 100644 index 00000000..e5a4dd40 --- /dev/null +++ b/js/networkError/style.css @@ -0,0 +1,71 @@ +body { + background-color: rgb(247, 247, 247); + color: rgb(100, 100, 100); + margin: 0; + display: flex; +} + +a { + color: rgb(17, 85, 204); + text-decoration: none; +} + +.NetworkError-header { + color: #333; + font-size: 1.6em; + font-weight: normal; + line-height: 1.25em; + margin-bottom: 16px; + margin-top: 0; + word-wrap: break-word; +} + +.NetworkError-icon { + background-repeat: no-repeat; + background-size: 100%; + height: 72px; + margin: 0 0 40px; + width: 72px; + -webkit-user-select: none; + display: inline-block; +} + +.NetworkError-error-code { + color: #646464; + text-transform: uppercase; + display: block; + font-size: .8em; + margin-top: 10px; +} + +.content-wrapper { + box-sizing: border-box; + line-height: 1.6em; + margin: 14vh auto 0; + max-width: 600px; + width: 100%; + -webkit-text-size-adjust: 100%; + font-size: 100%; + font-family: sans-serif, "Segoe UI", "Helvetica Neue", Arial; +} + +.NetworkError-reason { + display: inline; +} + +.NetworkError-button { + font-weight: 600; + margin: 20px 0; + text-transform: uppercase; + transform: translatez(0); + background: rgb(66, 133, 244); + border: 0; + border-radius: 2px; + box-sizing: border-box; + color: #fff; + cursor: pointer; + font-size: .875em; + padding: 10px 24px; + transition: box-shadow 200ms cubic-bezier(0.4, 0, 0.2, 1); + user-select: none; +} \ No newline at end of file diff --git a/js/preload/preloadMain.js b/js/preload/preloadMain.js index dcfbff6c..bf9f8be2 100644 --- a/js/preload/preloadMain.js +++ b/js/preload/preloadMain.js @@ -21,7 +21,9 @@ const getMediaSources = require('../desktopCapturer/getSources'); const getMediaSource = require('../desktopCapturer/getSource'); const showScreenSharingIndicator = require('../screenSharingIndicator/showScreenSharingIndicator'); const { TitleBar } = require('../windowsTitlebar'); +const { NetworkError } = require('../networkError'); const titleBar = new TitleBar(); +const networkError = new NetworkError(); const { buildNumber } = require('../../package.json'); const SnackBar = require('../snackBar').SnackBar; const KeyCodes = { @@ -493,6 +495,13 @@ function createAPI() { } }); + // display network error info + local.ipcRenderer.on('network-error', (event, data) => { + if (data && typeof data === 'object') { + networkError.showError(data); + } + }); + /** * an event triggered by the main process when * the locale is changed diff --git a/js/windowMgr.js b/js/windowMgr.js index 59d71970..48167037 100644 --- a/js/windowMgr.js +++ b/js/windowMgr.js @@ -10,9 +10,9 @@ const path = require('path'); const nodeURL = require('url'); const querystring = require('querystring'); const filesize = require('filesize'); +const fetch = require('electron-fetch').default; const { getTemplate, getMinimizeOnClose, getTitleBarStyle } = require('./menus/menuTemplate.js'); -const loadErrors = require('./dialogs/showLoadError.js'); const isInDisplayBounds = require('./utils/isInDisplayBounds.js'); const getGuid = require('./utils/getGuid.js'); const log = require('./log.js'); @@ -26,6 +26,7 @@ const { isWhitelisted, parseDomain } = require('./utils/whitelistHandler'); const { initCrashReporterMain, initCrashReporterRenderer } = require('./crashReporter.js'); const i18n = require('./translation/i18n'); const getCmdLineArg = require('./utils/getCmdLineArg'); +const config = readConfigFileSync(); const SpellChecker = require('./spellChecker').SpellCheckHelper; const spellchecker = new SpellChecker(); @@ -48,6 +49,8 @@ let isAutoReload = false; let devToolsEnabled = true; let isCustomTitleBarEnabled = true; let titleBarStyles; +let networkStatusCheckIntervalId; +const networkStatusCheckInterval = 5000; const KeyCodes = { Esc: 27, @@ -68,6 +71,12 @@ const MIN_HEIGHT = 300; const DEFAULT_WIDTH = 300; const DEFAULT_HEIGHT = 600; +const IGNORE_ERROR_CODES = [ + 0, + -3, + -111 +]; + // Certificate transparency whitelist let ctWhitelist = []; @@ -174,8 +183,6 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { let url = initialUrl; let key = getGuid(); - const config = readConfigFileSync(); - // condition whether to enable custom Windows 10 title bar isCustomTitleBarEnabled = typeof isCustomTitleBar === 'boolean' && isCustomTitleBar @@ -268,17 +275,6 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { } } - function retry() { - if (!isOnline) { - loadErrors.showNetworkConnectivityError(mainWindow, url, retry); - return; - } - - if (mainWindow.webContents) { - mainWindow.webContents.reload(); - } - } - // Event needed to hide native menu bar on Windows 10 as we use custom menu bar mainWindow.webContents.once('did-start-loading', () => { if ((isCustomTitleBarEnabled || isWindowsOS) && mainWindow && !mainWindow.isDestroyed()) { @@ -320,15 +316,13 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { } if (!isOnline) { - loadErrors.showNetworkConnectivityError(mainWindow, url, retry); - } else { - // 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); - + return; } + // 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); // ELECTRON-540 - needed to automatically // select desktop capture source @@ -345,9 +339,28 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) { } }); - mainWindow.webContents.on('did-fail-load', function (event, errorCode, - errorDesc, validatedURL) { - loadErrors.showLoadFailure(mainWindow, validatedURL, errorDesc, errorCode, retry, false); + mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDesc, validatedURL) => { + // Verify error code and display appropriate error content + if (!IGNORE_ERROR_CODES.includes(parseInt(errorCode, 10))) { + 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: errorDesc }); + isSymphonyReachable(mainWindow, validatedURL); + } + }); + + mainWindow.webContents.on('did-stop-loading', () => { + // Verify if SDA ended up in a blank page + mainWindow.webContents.executeJavaScript('document.location.href').then((href) => { + if (href === 'data:text/html,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: "stop loading"}); + isSymphonyReachable(mainWindow, config.url); + } + }).catch((error) => { + log.send(logLevels.ERROR, `Could not read document.location error: ${error}`); + }); }); // In case a renderer process crashes, provide an @@ -1304,6 +1317,44 @@ const logBrowserWindowEvents = (browserWindow, windowName) => { }; +/** + * Validates the network by fetching the pod url + * every 5sec, 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; + } +}; + module.exports = { createMainWindow: createMainWindow, getMainWindow: getMainWindow, @@ -1322,4 +1373,5 @@ module.exports = { getIsOnline: getIsOnline, getSpellchecker: getSpellchecker, isMisspelled: isMisspelled, + cancelNetworkStatusCheck: cancelNetworkStatusCheck, }; diff --git a/locale/en-US.json b/locale/en-US.json index 3c724296..a8a1dc97 100644 --- a/locale/en-US.json +++ b/locale/en-US.json @@ -68,6 +68,12 @@ "Minimize on Close": "Minimize on Close", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "Network connectivity has been lost. Check your internet connection.", + "NetworkError": { + "Problem connecting to Symphony": "Problem connecting to Symphony", + "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "Looks like you are not connected to the Internet. We'll try to reconnect automatically.", + "Cancel Retry": "Cancel Retry", + "Quit Symphony": "Quit Symphony" + }, "No crashes available to share": "No crashes available to share", "No logs are available to share": "No logs are available to share", "Not Allowed": "Not Allowed", diff --git a/locale/en.json b/locale/en.json index 3c724296..a8a1dc97 100644 --- a/locale/en.json +++ b/locale/en.json @@ -68,6 +68,12 @@ "Minimize on Close": "Minimize on Close", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "Network connectivity has been lost. Check your internet connection.", + "NetworkError": { + "Problem connecting to Symphony": "Problem connecting to Symphony", + "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "Looks like you are not connected to the Internet. We'll try to reconnect automatically.", + "Cancel Retry": "Cancel Retry", + "Quit Symphony": "Quit Symphony" + }, "No crashes available to share": "No crashes available to share", "No logs are available to share": "No logs are available to share", "Not Allowed": "Not Allowed", diff --git a/locale/fr-FR.json b/locale/fr-FR.json index 000e0854..a088e89b 100644 --- a/locale/fr-FR.json +++ b/locale/fr-FR.json @@ -68,6 +68,12 @@ "Minimiser on Close": "Minimiser à la fermeture", "Native": "Originaire", "Network connectivity has been lost. Check your internet connection.": "La connectivité a été perdue. Vérifiez votre connection à l'internet.", + "NetworkError": { + "Problem connecting to Symphony": "Problème de connexion à Symphony", + "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "On dirait que vous n'êtes pas connecté à Internet. Nous allons essayer de vous reconnecter automatiquement.", + "Cancel Retry": "Annuler réessayer", + "Quit Symphony": "Quitter Symphony" + }, "No crashes available to share": "Pas de crash à partager", "No logs are available to share": "Pas de journal à partager", "Not Allowed": "Interdit", diff --git a/locale/fr.json b/locale/fr.json index 000e0854..a088e89b 100644 --- a/locale/fr.json +++ b/locale/fr.json @@ -68,6 +68,12 @@ "Minimiser on Close": "Minimiser à la fermeture", "Native": "Originaire", "Network connectivity has been lost. Check your internet connection.": "La connectivité a été perdue. Vérifiez votre connection à l'internet.", + "NetworkError": { + "Problem connecting to Symphony": "Problème de connexion à Symphony", + "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "On dirait que vous n'êtes pas connecté à Internet. Nous allons essayer de vous reconnecter automatiquement.", + "Cancel Retry": "Annuler réessayer", + "Quit Symphony": "Quitter Symphony" + }, "No crashes available to share": "Pas de crash à partager", "No logs are available to share": "Pas de journal à partager", "Not Allowed": "Interdit", diff --git a/locale/ja-JP.json b/locale/ja-JP.json index 025d2447..3f94c2ad 100644 --- a/locale/ja-JP.json +++ b/locale/ja-JP.json @@ -68,6 +68,12 @@ "Minimize on Close": "閉じるで最小化", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "ネットワーク接続が失われました。インターネット接続を確認してください。", + "NetworkError": { + "Problem connecting to Symphony": "Symphonyへの接続に関する問題", + "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "インターネットに接続していないようです。 自動的に再接続します。", + "Cancel Retry": "再試行をキャンセル", + "Quit Symphony": "Symphonyを終了します" + }, "No crashes available to share": "共有できるクラッシュはありません", "No logs are available to share": "共有できるログはありません", "Not Allowed": "許可されていませ。", diff --git a/locale/ja.json b/locale/ja.json index 025d2447..3f94c2ad 100644 --- a/locale/ja.json +++ b/locale/ja.json @@ -68,6 +68,12 @@ "Minimize on Close": "閉じるで最小化", "Native": "Native", "Network connectivity has been lost. Check your internet connection.": "ネットワーク接続が失われました。インターネット接続を確認してください。", + "NetworkError": { + "Problem connecting to Symphony": "Symphonyへの接続に関する問題", + "Looks like you are not connected to the Internet. We'll try to reconnect automatically.": "インターネットに接続していないようです。 自動的に再接続します。", + "Cancel Retry": "再試行をキャンセル", + "Quit Symphony": "Symphonyを終了します" + }, "No crashes available to share": "共有できるクラッシュはありません", "No logs are available to share": "共有できるログはありません", "Not Allowed": "許可されていませ。", diff --git a/package.json b/package.json index 21d74d37..96e8c6dd 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "async.mapseries": "0.5.2", "auto-launch": "5.0.5", "electron-dl": "1.12.0", + "electron-fetch": "1.3.0", "electron-log": "2.2.17", "electron-spellchecker": "git+https://github.com/symphonyoss/electron-spellchecker.git#v1.1.5", "ffi": "git+https://github.com/symphonyoss/node-ffi.git#v1.2.9",