diff --git a/src/vs/platform/lifecycle/browser/lifecycleService.ts b/src/vs/platform/lifecycle/browser/lifecycleService.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0c3014986f3dcdc3c1623a71d8e2b7e2af86dff --- /dev/null +++ b/src/vs/platform/lifecycle/browser/lifecycleService.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ShutdownReason, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; +import { localize } from 'vs/nls'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; + +export class BrowserLifecycleService extends AbstractLifecycleService { + + _serviceBrand: ServiceIdentifier; + + constructor( + @ILogService readonly logService: ILogService + ) { + super(logService); + + this.registerListeners(); + } + + private registerListeners(): void { + window.onbeforeunload = () => this.beforeUnload(); + } + + private beforeUnload(): string | null { + let veto: boolean = false; + + // Before Shutdown + this._onBeforeShutdown.fire({ + veto(value) { + if (value === true) { + veto = true; + } else if (value instanceof Promise && !veto) { + console.warn(new Error('Long running onBeforeShutdown currently not supported')); + } + }, + reason: ShutdownReason.QUIT + }); + + // Veto: signal back to browser by returning a non-falsify return value + if (veto) { + return localize('lifecycleVeto', "Changes that you made may not be saved. Please check press 'Cancel' and try again."); + } + + // No Veto: continue with Will Shutdown + this._onWillShutdown.fire({ + join() { + console.warn(new Error('Long running onWillShutdown currently not supported')); + }, + reason: ShutdownReason.QUIT + }); + + return null; + } +} \ No newline at end of file diff --git a/src/vs/platform/lifecycle/common/lifecycleService.ts b/src/vs/platform/lifecycle/common/lifecycleService.ts index a82dd63da417351073425796fa7bfe9a9c96fafe..b325cde418a947f31ae63175bc0ad001bdb36ab9 100644 --- a/src/vs/platform/lifecycle/common/lifecycleService.ts +++ b/src/vs/platform/lifecycle/common/lifecycleService.ts @@ -9,10 +9,11 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ILifecycleService, BeforeShutdownEvent, WillShutdownEvent, StartupKind, LifecyclePhase, LifecyclePhaseToString } from 'vs/platform/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { mark } from 'vs/base/common/performance'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export abstract class AbstractLifecycleService extends Disposable implements ILifecycleService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; protected readonly _onBeforeShutdown = this._register(new Emitter()); get onBeforeShutdown(): Event { return this._onBeforeShutdown.event; } diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index aa7f6442e0804d44c54bbab6f77cef016678358e..ecef5c0277201b9fa6cb988743c5b73b14e87571 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { ShutdownReason, StartupKind, handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; +import { ShutdownReason, StartupKind, handleVetos, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ipcRenderer as ipc } from 'electron'; import { IWindowService } from 'vs/platform/windows/common/windows'; @@ -12,12 +12,13 @@ import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { onUnexpectedError } from 'vs/base/common/errors'; import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export class LifecycleService extends AbstractLifecycleService { private static readonly LAST_SHUTDOWN_REASON_KEY = 'lifecyle.lastShutdownReason'; - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; private shutdownReason: ShutdownReason; diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts index a56ee98dec6cd853992b1bdec58393b0b4af43e8..058efdc2500f51060acb3ccdb496f6939fbdb600 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts @@ -131,7 +131,7 @@ export const enum LifecycleMainPhase { export class LifecycleService extends Disposable implements ILifecycleService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; private static readonly QUIT_FROM_RESTART_MARKER = 'quit.from.restart'; // use a marker to find out if the session was restarted diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index f41c0327ae351fdf5d799172ec157c033da3a530..83b6403e4f5824724255b56bf49b1e45dd23a1a8 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -75,7 +75,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews })); this._register(lifecycleService.onBeforeShutdown(e => { - e.veto(this._onBeforeShutdown()); + this._onBeforeShutdown(); + e.veto(false); // Don't veto shutdown }, this)); } @@ -217,13 +218,12 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews return `mainThreadWebview-${viewType}`; } - private _onBeforeShutdown(): boolean { + private _onBeforeShutdown(): void { this._webviews.forEach((webview) => { if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) { webview.state.state = webview.webviewState; } }); - return false; // Don't veto shutdown } private createWebviewEventDelegate(handle: WebviewPanelHandle) { diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 3654c72f7ae69cf12eee79c95f2e13b09629f5d4..090913805ad4b76dd57cfbf6da87d3c485577b33 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -21,9 +21,8 @@ import { IPager } from 'vs/base/common/paging'; import { IExtensionManifest, ExtensionType, ExtensionIdentifier, IExtension } from 'vs/platform/extensions/common/extensions'; import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; import { ITelemetryService, ITelemetryData, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; -import { AbstractLifecycleService } from 'vs/platform/lifecycle/common/lifecycleService'; -import { ILogService, LogLevel, ConsoleLogService } from 'vs/platform/log/common/log'; -import { ShutdownReason, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { LogLevel, ConsoleLogService } from 'vs/platform/log/common/log'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IProductService } from 'vs/platform/product/common/product'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IStorageService, IWorkspaceStorageChangeEvent, StorageScope, IWillSaveStateEvent, WillSaveStateReason } from 'vs/platform/storage/common/storage'; @@ -518,55 +517,6 @@ registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true); //#endregion -//#region Lifecycle - -export class SimpleLifecycleService extends AbstractLifecycleService { - - _serviceBrand: any; - - constructor( - @ILogService readonly logService: ILogService - ) { - super(logService); - - this.registerListeners(); - } - - private registerListeners(): void { - window.onbeforeunload = () => this.beforeUnload(); - } - - private beforeUnload(): string { - - // Before Shutdown - this._onBeforeShutdown.fire({ - veto(value) { - if (value === true) { - console.warn(new Error('Preventing onBeforeUnload currently not supported')); - } else if (value instanceof Promise) { - console.warn(new Error('Long running onBeforeShutdown currently not supported')); - } - }, - reason: ShutdownReason.QUIT - }); - - // Will Shutdown - this._onWillShutdown.fire({ - join() { - console.warn(new Error('Long running onWillShutdown currently not supported')); - }, - reason: ShutdownReason.QUIT - }); - - // @ts-ignore - return null; - } -} - -registerSingleton(ILifecycleService, SimpleLifecycleService); - -//#endregion - //#region Log export class SimpleLogService extends ConsoleLogService { } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index d15549e21ccf9210a0376880712850184da03906..8d6531651192e7ca08910201649c55b72f1ff319 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -6,6 +6,7 @@ import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; import { ITextFileService, IResourceEncodings, IResourceEncoding } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; export class BrowserTextFileService extends TextFileService { @@ -14,6 +15,19 @@ export class BrowserTextFileService extends TextFileService { return { encoding: 'utf8', hasBOM: false }; } }; + + protected beforeShutdown(reason: ShutdownReason): boolean | Promise { + const veto = super.beforeShutdown(reason); + + // Web: there is no support for long running unload handlers. As such + // we need to return a direct boolean veto when we detect that there + // are dirty files around. + if (veto instanceof Promise) { + return this.getDirty().length > 0; + } + + return veto; + } } registerSingleton(ITextFileService, BrowserTextFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index b5e83926419799cef4cb2c1c691d8cca55558fad..83e8d9403055f92f50da72e517ef82dab9e70745 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -119,7 +119,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer })); } - private beforeShutdown(reason: ShutdownReason): boolean | Promise { + protected beforeShutdown(reason: ShutdownReason): boolean | Promise { // Dirty files need treatment on shutdown const dirty = this.getDirty(); diff --git a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts index d0c60e71c6a6c4c925ec377468ca3afbeced4d85..905d2a8b2922785c7774d0ff6cf86298c260bc7f 100644 --- a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts @@ -57,14 +57,16 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @ILifecycleService readonly lifecycleService: ILifecycleService, @ILabelService readonly labelService: ILabelService ) { + this.registerListeners(); + } - lifecycleService.onBeforeShutdown(async e => { + private registerListeners(): void { + this.lifecycleService.onBeforeShutdown(async e => { const saveOperation = this.saveUntitedBeforeShutdown(e.reason); if (saveOperation) { e.veto(saveOperation); } }); - } private async saveUntitedBeforeShutdown(reason: ShutdownReason): Promise { diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 3b2f85ac6a35ed4b9b4d57cc9ae61d803fe1bf36..e207ca87c09209684035dc47584f3018b0f35988 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -68,8 +68,8 @@ import { ContextViewService } from 'vs/platform/contextview/browser/contextViewS // import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; // import { IRequestService } from 'vs/platform/request/node/request'; // import { RequestService } from 'vs/platform/request/electron-browser/requestService'; -// import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService'; -// import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { BrowserLifecycleService } from 'vs/platform/lifecycle/browser/lifecycleService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; // import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; // import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; // import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; @@ -152,7 +152,7 @@ registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(IContextViewService, ContextViewService, true); // registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); // registerSingleton(IRequestService, RequestService, true); -// registerSingleton(ILifecycleService, LifecycleService); +registerSingleton(ILifecycleService, BrowserLifecycleService); // registerSingleton(ILocalizationsService, LocalizationsService); // registerSingleton(ISharedProcessService, SharedProcessService, true); // registerSingleton(IProductService, ProductService, true); @@ -238,7 +238,6 @@ import 'vs/workbench/contrib/debug/browser/repl'; import 'vs/workbench/contrib/debug/browser/debugViewlet'; import 'vs/workbench/contrib/debug/browser/debugHelperService'; - // Markers import 'vs/workbench/contrib/markers/browser/markers.contribution'; @@ -288,7 +287,6 @@ import { TaskService } from 'vs/workbench/contrib/tasks/browser/taskService'; import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; registerSingleton(ITaskService, TaskService, true); - // Remote import 'vs/workbench/contrib/remote/common/remote.contribution'; // import 'vs/workbench/contrib/remote/electron-browser/remote.contribution';