Files
code-server/lib/vscode/src/vs/workbench/browser/window.ts
Joe Previte eae5d8c807 chore(vscode): update to 1.53.2
These conflicts will be resolved in the following commits. We do it this way so
that PR review is possible.
2021-02-25 11:27:27 -07:00

162 lines
6.3 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { setFullscreen } from 'vs/base/browser/browser';
import { addDisposableListener, addDisposableThrottledListener, detectFullscreen, EventHelper, EventType, windowOpenNoOpener } from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { timeout } from 'vs/base/common/async';
import { Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { isIOS, isMacintosh } from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { localize } from 'vs/nls';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILogService } from 'vs/platform/log/common/log';
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { BrowserLifecycleService } from 'vs/workbench/services/lifecycle/browser/lifecycleService';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
export class BrowserWindow extends Disposable {
constructor(
@IOpenerService private readonly openerService: IOpenerService,
@ILifecycleService private readonly lifecycleService: BrowserLifecycleService,
@IDialogService private readonly dialogService: IDialogService,
@IHostService private readonly hostService: IHostService,
@ILabelService private readonly labelService: ILabelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@ILogService private readonly logService: ILogService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super();
this.registerListeners();
this.create();
}
private registerListeners(): void {
// Lifecycle
this._register(this.lifecycleService.onWillShutdown(() => this.onWillShutdown()));
// Layout
const viewport = isIOS && window.visualViewport ? window.visualViewport /** Visual viewport */ : window /** Layout viewport */;
this._register(addDisposableListener(viewport, EventType.RESIZE, () => this.onWindowResize()));
// Prevent the back/forward gestures in macOS
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.WHEEL, e => e.preventDefault(), { passive: false }));
// Prevent native context menus in web
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.CONTEXT_MENU, e => EventHelper.stop(e, true)));
// Prevent default navigation on drop
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.DROP, e => EventHelper.stop(e, true)));
// Fullscreen (Browser)
[EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE].forEach(event => {
this._register(addDisposableListener(document, event, () => setFullscreen(!!detectFullscreen())));
});
// Fullscreen (Native)
this._register(addDisposableThrottledListener(viewport, EventType.RESIZE, () => {
setFullscreen(!!detectFullscreen());
}, undefined, isMacintosh ? 2000 /* adjust for macOS animation */ : 800 /* can be throttled */));
}
private onWindowResize(): void {
this.logService.trace(`web.main#${isIOS && window.visualViewport ? 'visualViewport' : 'window'}Resize`);
this.layoutService.layout();
}
private onWillShutdown(): void {
// Try to detect some user interaction with the workbench
// when shutdown has happened to not show the dialog e.g.
// when navigation takes a longer time.
Event.toPromise(Event.any(
Event.once(domEvent(document.body, EventType.KEY_DOWN, true)),
Event.once(domEvent(document.body, EventType.MOUSE_DOWN, true))
)).then(async () => {
// Delay the dialog in case the user interacted
// with the page before it transitioned away
await timeout(3000);
// This should normally not happen, but if for some reason
// the workbench was shutdown while the page is still there,
// inform the user that only a reload can bring back a working
// state.
const res = await this.dialogService.show(
Severity.Error,
localize('shutdownError', "An unexpected error occurred that requires a reload of this page."),
[
localize('reload', "Reload")
],
{
detail: localize('shutdownErrorDetail', "The workbench was unexpectedly disposed while running.")
}
);
if (res.choice === 0) {
this.hostService.reload();
}
});
}
private create(): void {
// Driver
if (this.environmentService.options?.driver) {
(async () => this._register(await registerWindowDriver()))();
}
// Handle open calls
this.setupOpenHandlers();
// Label formatting
this.registerLabelFormatters();
}
private setupOpenHandlers(): void {
// We need to ignore the `beforeunload` event while
// we handle external links to open specifically for
// the case of application protocols that e.g. invoke
// vscode itself. We do not want to open these links
// in a new window because that would leave a blank
// window to the user, but using `window.location.href`
// will trigger the `beforeunload`.
this.openerService.setDefaultExternalOpener({
openExternal: async (href: string) => {
if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) {
windowOpenNoOpener(href);
} else {
this.lifecycleService.withExpectedUnload(() => window.location.href = href);
}
return true;
}
});
}
private registerLabelFormatters() {
this.labelService.registerFormatter({
scheme: Schemas.userData,
priority: true,
formatting: {
label: '${scheme}:${path}',
separator: '/',
}
});
}
}