diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index eb22ca69c152d..8e53f7d664cfb 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -125,7 +125,7 @@ process.env.LC_NUMERIC = 'C'; const electron = require('electron'); const { join, resolve } = require('path'); const { fork } = require('child_process'); -const { app, shell, BrowserWindow, ipcMain, Menu } = electron; +const { app, dialog, shell, BrowserWindow, ipcMain, Menu } = electron; const applicationName = \`${this.pck.props.frontend.config.applicationName}\`; const isSingleInstance = ${this.pck.props.backend.config.singleInstance === true ? 'true' : 'false'}; @@ -140,36 +140,6 @@ const nativeKeymap = require('native-keymap'); const Storage = require('electron-store'); const electronStore = new Storage(); -let canPreventStop = true; -const windows = []; - -app.on('before-quit', async event => { - if (canPreventStop) { - // Pause the stop. - event.preventDefault(); - let preventStop = false; - // Ask all opened windows whether they want to prevent the \`close\` event or not. - for (const window of windows) { - if (!preventStop) { - window.webContents.send('prevent-stop-request'); - const preventStopPerWindow = await new Promise((resolve) => { - ipcMain.once('prevent-stop-response', (_, arg) => { - if (!!arg && 'preventStop' in arg && typeof arg.preventStop === 'boolean') { - resolve(arg.preventStop); - } - }) - }); - if (preventStopPerWindow) { - preventStop = true; - } - } - } - if (!preventStop) { - canPreventStop = false; - app.quit(); - } - } -}); app.on('ready', () => { const { screen } = electron; @@ -250,13 +220,20 @@ app.on('ready', () => { newWindow.on('close', saveWindowState); newWindow.on('resize', saveWindowStateDelayed); newWindow.on('move', saveWindowStateDelayed); - newWindow.on('closed', () => { - const index = windows.indexOf(newWindow); - if (index !== -1) { - windows.splice(index, 1); - } - if (windows.length === 0) { - app.quit(); + + // Fired when a beforeunload handler tries to prevent the page unloading + newWindow.webContents.on('will-prevent-unload', event => { + const preventStop = 0 !== dialog.showMessageBox(newWindow, { + type: 'question', + buttons: ['Yes', 'No'], + title: 'Confirm', + message: 'Are you sure you want to quit?', + detail: 'Any unsaved changes will not be saved.' + }); + + if (!preventStop) { + // This ignores the beforeunload callback, allowing the page to unload + event.preventDefault(); } }); @@ -274,7 +251,6 @@ app.on('ready', () => { if (!!theUrl) { newWindow.loadURL(theUrl); } - windows.push(newWindow); return newWindow; } diff --git a/packages/core/package.json b/packages/core/package.json index 9eff029440be1..51f1403c96734 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -62,9 +62,6 @@ "frontend": "lib/browser/keyboard/browser-keyboard-module", "frontendElectron": "lib/electron-browser/keyboard/electron-keyboard-module", "backendElectron": "lib/electron-node/keyboard/electron-backend-keyboard-module" - }, - { - "frontendElectron": "lib/electron-browser/shutdown-hook/electron-shutdown-hook-module" } ], "keywords": [ diff --git a/packages/core/src/browser/window/default-window-service.ts b/packages/core/src/browser/window/default-window-service.ts index ad57223cbb741..c03ed64e1fdbc 100644 --- a/packages/core/src/browser/window/default-window-service.ts +++ b/packages/core/src/browser/window/default-window-service.ts @@ -36,9 +36,7 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC this.frontendApplication = app; window.addEventListener('beforeunload', event => { if (!this.canUnload()) { - event.returnValue = ''; - event.preventDefault(); - return ''; + return this.preventUnload(event); } }); } @@ -66,4 +64,14 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC return confirmExit !== 'always'; } + /** + * Ask the user to confirm if they want to unload the window. Prevent it if they do not. + * @param event The beforeunload event + */ + protected preventUnload(event: BeforeUnloadEvent): string | void { + event.returnValue = ''; + event.preventDefault(); + return ''; + } + } diff --git a/packages/core/src/electron-browser/shutdown-hook/electron-shutdown-hook-module.ts b/packages/core/src/electron-browser/shutdown-hook/electron-shutdown-hook-module.ts deleted file mode 100644 index 2203cf0ecc1c5..0000000000000 --- a/packages/core/src/electron-browser/shutdown-hook/electron-shutdown-hook-module.ts +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 TypeFox and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the Eclipse - * Public License v. 2.0 are satisfied: GNU General Public License, version 2 - * with the GNU Classpath Exception which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 - ********************************************************************************/ - -import { ContainerModule } from 'inversify'; -import { FrontendApplicationContribution } from '../../browser/frontend-application'; -import { ElectronShutdownHook } from './electron-shutdown-hook'; - -export default new ContainerModule(bind => { - bind(ElectronShutdownHook).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(ElectronShutdownHook); -}); diff --git a/packages/core/src/electron-browser/shutdown-hook/electron-shutdown-hook.ts b/packages/core/src/electron-browser/shutdown-hook/electron-shutdown-hook.ts deleted file mode 100644 index d24949aac4e47..0000000000000 --- a/packages/core/src/electron-browser/shutdown-hook/electron-shutdown-hook.ts +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************** - * Copyright (C) 2019 TypeFox and others. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * This Source Code may also be made available under the following Secondary - * Licenses when the conditions for such availability set forth in the Eclipse - * Public License v. 2.0 are satisfied: GNU General Public License, version 2 - * with the GNU Classpath Exception which is available at - * https://www.gnu.org/software/classpath/license.html. - * - * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 - ********************************************************************************/ - -import { inject, injectable } from 'inversify'; -import { ipcRenderer, remote, Event } from 'electron'; -import { WindowService } from '../../browser/window/window-service'; -import { FrontendApplicationContribution } from '../../browser/frontend-application'; - -@injectable() -export class ElectronShutdownHook implements FrontendApplicationContribution { - - @inject(WindowService) - protected readonly windowService: WindowService; - - onStart(): void { - ipcRenderer.on(ElectronShutdownHook.PreventStop.Channels.REQUEST, (event: Event) => { - const preventStop = !this.windowService.canUnload() && 0 !== remote.dialog.showMessageBox(remote.getCurrentWindow(), { - type: 'question', - buttons: ['Yes', 'No'], - title: 'Confirm', - message: 'Are you sure you want to quit?', - detail: 'Changes you made may not be saved.' - }); - event.sender.send(ElectronShutdownHook.PreventStop.Channels.RESPONSE, { preventStop }); - }); - } - -} - -export namespace ElectronShutdownHook { - export namespace PreventStop { - export namespace Channels { - export const REQUEST = 'prevent-stop-request'; - export const RESPONSE = 'prevent-stop-response'; - } - export interface Message { - readonly preventStop: boolean; - } - } -} diff --git a/packages/core/src/electron-browser/window/electron-window-service.ts b/packages/core/src/electron-browser/window/electron-window-service.ts index 424632d45bf9a..a8c71e761b705 100644 --- a/packages/core/src/electron-browser/window/electron-window-service.ts +++ b/packages/core/src/electron-browser/window/electron-window-service.ts @@ -17,20 +17,11 @@ import { injectable } from 'inversify'; import { ipcRenderer } from 'electron'; import { NewWindowOptions } from '../../browser/window/window-service'; -import { FrontendApplication } from '../../browser/frontend-application'; import { DefaultWindowService } from '../../browser/window/default-window-service'; @injectable() export class ElectronWindowService extends DefaultWindowService { - onStart(app: FrontendApplication): void { - this.frontendApplication = app; - // We do not want to add a `beforeunload` listener to the `window`. - // Why? Because by the time we get into the unload handler, it is already too late. Our application has quit. - // _Emitted when the `window` is going to be closed. It's emitted before the `beforeunload` and `unload` event of the DOM._ - // https://github.com/electron/electron/blob/master/docs/api/browser-window.md#event-close - } - openNewWindow(url: string, { external }: NewWindowOptions = {}): undefined { if (external) { ipcRenderer.send('open-external', url); @@ -40,4 +31,9 @@ export class ElectronWindowService extends DefaultWindowService { return undefined; } + protected preventUnload(event: BeforeUnloadEvent): string | void { + // The user will be shown a confirmation dialog by the will-prevent-unload handler in the Electron main script + event.returnValue = false; + } + }