Skip to content

Commit

Permalink
use windowService.reload() generally; fix double prompt
Browse files Browse the repository at this point in the history
  • Loading branch information
Colin Grant committed Nov 4, 2021
1 parent a32c320 commit 57af267
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 27 deletions.
25 changes: 22 additions & 3 deletions packages/core/src/browser/common-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ import { QuickInputService, QuickPick, QuickPickItem } from './quick-input';
import { AsyncLocalizationProvider } from '../common/i18n/localization';
import { nls } from '../common/nls';
import { confirmExit } from './dialogs';
import { WindowService } from './window/window-service';
import { ConfirmDialog, Dialog } from '.';
import { FrontendApplicationConfigProvider } from './frontend-application-config-provider';

export namespace CommonMenus {

Expand Down Expand Up @@ -349,6 +352,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
@inject(AuthenticationService)
protected readonly authenticationService: AuthenticationService;

@inject(WindowService)
protected readonly windowService: WindowService;

async configure(app: FrontendApplication): Promise<void> {
const configDirUri = await this.environments.getConfigDirUri();
// Global settings
Expand Down Expand Up @@ -1041,10 +1047,11 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
for (const additionalLanguage of ['en', ...availableLanguages]) {
items.push({
label: additionalLanguage,
execute: () => {
if (additionalLanguage !== nls.locale) {
execute: async () => {
if (additionalLanguage !== nls.locale && await this.confirmRestart()) {
this.windowService.setSafeToShutDown();
window.localStorage.setItem(nls.localeId, additionalLanguage);
window.location.reload();
this.windowService.reload();
}
}
});
Expand All @@ -1056,6 +1063,18 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
});
}

protected async confirmRestart(): Promise<boolean> {
const shouldRestart = await new ConfirmDialog({
title: nls.localize('vscode/localizationsActions/relaunchDisplayLanguageMessage', 'A restart is required for the change in display language to take effect.'),
msg: nls.localize(
'vscode/localizationsActions/relaunchDisplayLanguageDetail', 'Press the restart button to restart {0} and change the display language.', FrontendApplicationConfigProvider.get().applicationName
),
ok: nls.localize('vscode/localizationsActions/restart', 'Restart'),
cancel: Dialog.CANCEL,
}).open();
return shouldRestart === true;
}

protected selectIconTheme(): void {
let resetTo: string | undefined = this.iconThemes.current;
const previewTheme = debounce((id: string) => this.iconThemes.current = id, 200);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/browser/shell/shell-layout-restorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export class ShellLayoutRestorer implements CommandContribution {
}

protected async resetLayout(): Promise<void> {
if (await this.windowService.safeToShutDown()) {
if (await this.windowService.isSafeToShutDown()) {
this.logger.info('>>> Resetting layout...');
this.shouldStoreLayout = false;
this.storageService.setData(this.storageKey, undefined);
Expand Down
40 changes: 28 additions & 12 deletions packages/core/src/browser/window/default-window-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { confirmExit } from '../dialogs';
export class DefaultWindowService implements WindowService, FrontendApplicationContribution {

protected frontendApplication: FrontendApplication;
protected allowVetoes = true;

protected onUnloadEmitter = new Emitter<void>();
get onUnload(): Event<void> {
Expand Down Expand Up @@ -54,21 +55,31 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC
this.openNewWindow(DEFAULT_WINDOW_HASH);
}

/**
* Returns a list of actions that {@link FrontendApplicationContribution}s would like to take before shutdown
* It is expected that this will succeed - i.e. return an empty array - at most once per session. If no vetoes are received
* during any cycle, no further checks will be made. In that case, shutdown should proceed unconditionally.
*/
protected collectContributionUnloadVetoes(): OnWillStopAction[] {
const vetoes = [];
const shouldConfirmExit = this.corePreferences['application.confirmExit'];
for (const contribution of this.contributions.getContributions()) {
const veto = contribution.onWillStop?.(this.frontendApplication);
if (veto && shouldConfirmExit !== 'never') { // Ignore vetoes if we should not prompt for exit.
if (OnWillStopAction.is(veto)) {
vetoes.push(veto);
} else {
vetoes.push({ reason: 'No reason given', action: () => false });
if (this.allowVetoes) {
const shouldConfirmExit = this.corePreferences['application.confirmExit'];
for (const contribution of this.contributions.getContributions()) {
const veto = contribution.onWillStop?.(this.frontendApplication);
if (veto && shouldConfirmExit !== 'never') { // Ignore vetoes if we should not prompt for exit or if we have already run vetoes.
if (OnWillStopAction.is(veto)) {
vetoes.push(veto);
} else {
vetoes.push({ reason: 'No reason given', action: () => false });
}
}
}
}
if (vetoes.length === 0 && shouldConfirmExit === 'always') {
vetoes.push({ reason: 'application.confirmExit preference', action: () => confirmExit() });
if (vetoes.length === 0 && shouldConfirmExit === 'always') {
vetoes.push({ reason: 'application.confirmExit preference', action: () => confirmExit() });
}
if (vetoes.length === 0) {
this.allowVetoes = false;
}
}
return vetoes;
}
Expand All @@ -85,7 +96,7 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC
window.addEventListener('unload', () => this.onUnloadEmitter.fire());
}

async safeToShutDown(): Promise<boolean> {
async isSafeToShutDown(): Promise<boolean> {
const vetoes = this.collectContributionUnloadVetoes();
if (vetoes.length === 0) {
return true;
Expand All @@ -94,12 +105,17 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC
const resolvedVetoes = await Promise.allSettled(vetoes.map(({ action }) => action()));
if (resolvedVetoes.every(resolution => resolution.status === 'rejected' || resolution.value === true)) {
console.debug('OnWillStop actions resolved; allowing shutdown');
this.allowVetoes = false;
return true;
} else {
return false;
}
}

setSafeToShutDown(): void {
this.allowVetoes = false;
}

/**
* Called when the `window` is about to `unload` its resources.
* At this point, the `document` is still visible and the [`BeforeUnloadEvent`](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event)
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/browser/window/test/mock-window-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class MockWindowService implements WindowService {
openNewWindow(): undefined { return undefined; }
openNewDefaultWindow(): void { }
reload(): void { }
safeToShutDown(): Promise<boolean> { return Promise.resolve(true); }
isSafeToShutDown(): Promise<boolean> { return Promise.resolve(true); }
setSafeToShutDown(): void { }
get onUnload(): Event<void> { return Event.None; }
}
14 changes: 13 additions & 1 deletion packages/core/src/browser/window/window-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,20 @@ export interface WindowService {
/**
* Checks `FrontendApplicationContribution#willStop` for impediments to shutdown and runs any actions returned.
* Can be used safely in browser and Electron when triggering reload or shutdown programmatically.
* Should _only_ be called before a shutdown - if this returns `true`, `FrontendApplicationContribution#willStop`
* will not be called again in the current session. I.e. if this return `true`, the shutdown should proceed without
* further condition.
*/
safeToShutDown(): Promise<boolean>;
isSafeToShutDown(): Promise<boolean>;

/**
* Will prevent subsequent checks of `FrontendApplicationContribution#willStop`. Should only be used after requesting
* user confirmation.
*
* This is primarily intended programmatic restarts due to e.g. change of display language. It allows for a single confirmation
* of intent, rather than one warning and then several warnings from other contributions.
*/
setSafeToShutDown(): void;

/**
* Reloads the window according to platform.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ import {
} from '../../common';
import {
ApplicationShell, codicon, ConfirmDialog, KeybindingContribution, KeybindingRegistry,
PreferenceScope, Widget, FrontendApplication, FrontendApplicationContribution, CommonMenus, CommonCommands, Dialog
PreferenceScope, Widget, FrontendApplication, FrontendApplicationContribution, CommonMenus, CommonCommands, Dialog,
} from '../../browser';
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
import { FrontendApplicationStateService, FrontendApplicationState } from '../../browser/frontend-application-state';
import { FrontendApplicationConfigProvider } from '../../browser/frontend-application-config-provider';
import { RequestTitleBarStyle, Restart, TitleBarStyleAtStartup, TitleBarStyleChanged } from '../../electron-common/messaging/electron-messages';
import { ZoomLevel } from '../window/electron-window-preferences';
import { BrowserMenuBarContribution } from '../../browser/menu/browser-menu-plugin';
import { WindowService } from '../../browser/window/window-service';

import '../../../src/electron-browser/menu/electron-menu-style.css';

Expand Down Expand Up @@ -84,6 +85,9 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
@inject(FrontendApplicationStateService)
protected readonly stateService: FrontendApplicationStateService;

@inject(WindowService)
protected readonly windowService: WindowService;

protected titleBarStyleChangeFlag = false;
protected titleBarStyle?: string;

Expand Down Expand Up @@ -241,6 +245,7 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
cancel: Dialog.CANCEL
});
if (await dialog.open()) {
this.windowService.setSafeToShutDown();
electron.ipcRenderer.send(Restart);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class ElectronWindowService extends DefaultWindowService {
* after running FrontentApplication `onWillStop` handlers or on the `cancelChannel` if it is not safe to exit.
*/
protected async handleCloseRequestedEvent(event: CloseRequestArguments): Promise<void> {
const safeToClose = await this.safeToShutDown();
const safeToClose = await this.isSafeToShutDown();
if (safeToClose) {
console.debug(`Shutting down because of ${StopReason[event.reason]} request.`);
electron.ipcRenderer.send(event.confirmChannel);
Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/electron-common/messaging/electron-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,18 @@ export const CLOSE_REQUESTED_SIGNAL = 'close-requested';
export const RELOAD_REQUESTED_SIGNAL = 'reload-requested';

export enum StopReason {
/**
* Closing the window with no prospect of restart.
*/
Close,
/**
* Reload without closing the window.
*/
Reload,
/**
* Reload that includes closing the window.
*/
Restart, // eslint-disable-line @typescript-eslint/no-shadow
}

export interface CloseRequestArguments {
Expand Down
13 changes: 7 additions & 6 deletions packages/core/src/electron-main/electron-main-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,14 +457,15 @@ export class ElectronMainApplication {
}

event.preventDefault();
const doExit = () => {
this.closeIsConfirmed.add(electronWindow.id);
electronWindow.close();
};
this.handleStopRequest(electronWindow, doExit, StopReason.Close);
this.handleStopRequest(electronWindow, () => this.doCloseWindow(electronWindow), StopReason.Close);
});
}

protected doCloseWindow(electronWindow: BrowserWindow): void {
this.closeIsConfirmed.add(electronWindow.id);
electronWindow.close();
}

protected async handleStopRequest(electronWindow: BrowserWindow, onSafeCallback: () => unknown, reason: StopReason): Promise<void> {
const safeToClose = await this.checkSafeToStop(electronWindow, reason);
if (safeToClose) {
Expand Down Expand Up @@ -607,7 +608,7 @@ export class ElectronMainApplication {
});
this.restarting = false;
});
window.close();
this.handleStopRequest(window, () => this.doCloseWindow(window), StopReason.Restart);
}

protected async handleReload(event: Electron.IpcMainEvent): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import {
} from '@theia/plugin-ext/lib/main/browser/callhierarchy/callhierarchy-type-converters';
import { CustomEditorOpener } from '@theia/plugin-ext/lib/main/browser/custom-editors/custom-editor-opener';
import { nls } from '@theia/core/lib/common/nls';
import { WindowService } from '@theia/core/lib/browser/window/window-service';

export namespace VscodeCommands {
export const OPEN: Command = {
Expand Down Expand Up @@ -137,6 +138,8 @@ export class PluginVscodeCommandsContribution implements CommandContribution {
protected readonly callHierarchyProvider: CallHierarchyServiceProvider;
@inject(MonacoTextModelService)
protected readonly textModelService: MonacoTextModelService;
@inject(WindowService)
protected readonly windowService: WindowService;

private async openWith(commandId: string, resource: URI, columnOrOptions?: ViewColumn | TextDocumentShowOptions, openerId?: string): Promise<boolean> {
if (!resource) {
Expand Down Expand Up @@ -477,7 +480,7 @@ export class PluginVscodeCommandsContribution implements CommandContribution {

commands.registerCommand({ id: 'workbench.action.reloadWindow' }, {
execute: () => {
window.location.reload();
this.windowService.reload();
}
});

Expand Down

0 comments on commit 57af267

Please sign in to comment.